@agent-native/core 0.128.4 → 0.129.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (343) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +84 -0
  3. package/corpus/core/docs/content/template-design-collaboration-and-full-apps.mdx +3 -1
  4. package/corpus/core/package.json +6 -3
  5. package/corpus/core/src/a2a/client.ts +18 -4
  6. package/corpus/core/src/a2a/server.ts +19 -3
  7. package/corpus/core/src/a2a/types.ts +7 -0
  8. package/corpus/core/src/action.ts +43 -16
  9. package/corpus/core/src/agent/engine/ai-sdk-engine.ts +14 -10
  10. package/corpus/core/src/agent/production-agent.ts +41 -3
  11. package/corpus/core/src/agent/thread-data-builder.ts +5 -0
  12. package/corpus/core/src/chat-threads/migrations.ts +53 -0
  13. package/corpus/core/src/cli/create.ts +4 -0
  14. package/corpus/core/src/cli/skills-content/design-visual-edit-skill.ts +36 -16
  15. package/corpus/core/src/cli/skills.ts +16 -0
  16. package/corpus/core/src/client/AgentPanel.tsx +9 -5
  17. package/corpus/core/src/client/AssistantChat.tsx +138 -46
  18. package/corpus/core/src/client/MultiTabAssistantChat.tsx +16 -10
  19. package/corpus/core/src/client/agent-chat-adapter.ts +21 -3
  20. package/corpus/core/src/client/analytics.ts +159 -102
  21. package/corpus/core/src/client/chat/message-components.tsx +12 -5
  22. package/corpus/core/src/client/chat/runtime.ts +2 -1
  23. package/corpus/core/src/client/chat/tool-call-display.tsx +25 -2
  24. package/corpus/core/src/client/chat-model-groups.ts +3 -0
  25. package/corpus/core/src/client/client-status-requests.ts +161 -0
  26. package/corpus/core/src/client/settings/VoiceTranscriptionSection.tsx +2 -2
  27. package/corpus/core/src/client/sse-event-processor.ts +15 -3
  28. package/corpus/core/src/client/use-agent-engine-configured.ts +54 -50
  29. package/corpus/core/src/client/use-chat-models.ts +16 -11
  30. package/corpus/core/src/client/use-chat-threads.ts +32 -9
  31. package/corpus/core/src/client/use-session.ts +4 -5
  32. package/corpus/core/src/db/client.ts +83 -8
  33. package/corpus/core/src/db/ensure-additive-columns.ts +31 -20
  34. package/corpus/core/src/db/migrations.ts +22 -2
  35. package/corpus/core/src/mcp/approval-store.ts +99 -0
  36. package/corpus/core/src/mcp/build-server.ts +579 -303
  37. package/corpus/core/src/mcp/embed-app.ts +1 -1
  38. package/corpus/core/src/mcp/oauth-client-metadata.ts +380 -0
  39. package/corpus/core/src/mcp/oauth-route.ts +57 -52
  40. package/corpus/core/src/mcp/screen-memory-stdio.ts +400 -378
  41. package/corpus/core/src/mcp/server.ts +29 -145
  42. package/corpus/core/src/mcp/stdio.ts +72 -80
  43. package/corpus/core/src/mcp-client/manager.ts +6 -10
  44. package/corpus/core/src/mcp-client/oauth-client.ts +127 -36
  45. package/corpus/core/src/mcp-client/oauth-routes.ts +16 -6
  46. package/corpus/core/src/mcp-client/routes.ts +6 -5
  47. package/corpus/core/src/server/agent-capabilities.ts +20 -0
  48. package/corpus/core/src/server/agent-chat/action-filters-a2a.ts +27 -0
  49. package/corpus/core/src/server/agent-chat-plugin.ts +18 -0
  50. package/corpus/core/src/server/transcribe-voice.ts +14 -5
  51. package/corpus/core/src/templates/default/.agents/skills/security/SKILL.md +13 -2
  52. package/corpus/core/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +5 -2
  53. package/corpus/core/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +18 -0
  54. package/corpus/core/src/templates/workspace-core/.agents/skills/security/SKILL.md +13 -2
  55. package/corpus/templates/analytics/.agents/skills/security/SKILL.md +13 -2
  56. package/corpus/templates/analytics/app/components/dashboard/DashboardPanelSkeleton.tsx +1 -10
  57. package/corpus/templates/analytics/app/global.css +0 -31
  58. package/corpus/templates/analytics/app/lib/sql-query.ts +46 -20
  59. package/corpus/templates/analytics/changelog/2026-07-28-analytics-dashboard-loading-states-now-use-a-simpler-standar.md +6 -0
  60. package/corpus/templates/analytics/changelog/2026-07-28-analytics-dashboards-now-load-first-party-charts-reliably-in.md +6 -0
  61. package/corpus/templates/analytics/server/lib/first-party-analytics-cache.ts +42 -16
  62. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +4 -2
  63. package/corpus/templates/analytics/shared/sql-query-limits.ts +1 -0
  64. package/corpus/templates/assets/.agents/skills/security/SKILL.md +13 -2
  65. package/corpus/templates/brain/.agents/skills/security/SKILL.md +13 -2
  66. package/corpus/templates/calendar/.agents/skills/security/SKILL.md +13 -2
  67. package/corpus/templates/chat/.agents/skills/security/SKILL.md +13 -2
  68. package/corpus/templates/chat/README.md +0 -2
  69. package/corpus/templates/clips/.agents/skills/security/SKILL.md +13 -2
  70. package/corpus/templates/clips/app/components/player/player-controls.tsx +3 -6
  71. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +6 -2
  72. package/corpus/templates/clips/app/routes/share.$shareId.tsx +3 -1
  73. package/corpus/templates/clips/changelog/2026-07-28-copied-share-links-no-longer-leave-an-unnecessary-confirmati.md +6 -0
  74. package/corpus/templates/clips/changelog/2026-07-28-playback-skip-controls-no-longer-show-overlapping-numbers.md +6 -0
  75. package/corpus/templates/clips/changelog/2026-07-28-public-clips-share-buttons-now-use-a-clean-text-only-label.md +6 -0
  76. package/corpus/templates/clips/desktop/src/app.tsx +14 -17
  77. package/corpus/templates/clips/desktop/src/overlays/live-audio-bars.tsx +3 -1
  78. package/corpus/templates/clips/desktop/src/styles.css +13 -10
  79. package/corpus/templates/clips/desktop/src-tauri/src/recording_indicator.rs +2 -2
  80. package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +36 -0
  81. package/corpus/templates/content/.agents/skills/security/SKILL.md +13 -2
  82. package/corpus/templates/content/actions/_builder-cms-read-client.ts +155 -34
  83. package/corpus/templates/content/actions/_builder-docs-client.ts +151 -30
  84. package/corpus/templates/content/actions/stage-builder-source-bulk-update.ts +5 -2
  85. package/corpus/templates/content/app/root.tsx +35 -14
  86. package/corpus/templates/content/changelog/2026-07-28-builder-backed-files-media-fields-now-accept-direct-file-url.md +6 -0
  87. package/corpus/templates/content/changelog/2026-07-28-builder-content-reads-now-negotiate-the-newest-mcp-protocol-.md +6 -0
  88. package/corpus/templates/content/changelog/2026-07-28-public-content-pages-load-faster-before-the-optional-ask-sid.md +6 -0
  89. package/corpus/templates/design/.agents/skills/security/SKILL.md +13 -2
  90. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +36 -16
  91. package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +42 -3
  92. package/corpus/templates/design/actions/open-visual-edit.ts +2 -1
  93. package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +2 -2
  94. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +14 -15
  95. package/corpus/templates/design/app/components/design/ResponsiveInteractBar.tsx +314 -0
  96. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +88 -3
  97. package/corpus/templates/design/app/components/design/keyboard-shortcuts.ts +1 -1
  98. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +7 -6
  99. package/corpus/templates/design/app/i18n-data.ts +56 -11
  100. package/corpus/templates/design/app/i18n-responsive-interact.ts +183 -0
  101. package/corpus/templates/design/app/lib/agent-chat.ts +60 -0
  102. package/corpus/templates/design/app/pages/design-editor/responsive-interact.ts +89 -0
  103. package/corpus/templates/design/changelog/2026-07-28-design-edits-can-be-applied-through-the-host-coding-agent-fr.md +6 -0
  104. package/corpus/templates/design/changelog/2026-07-28-interact-mode-can-preview-designs-in-responsive-device-sizes.md +6 -0
  105. package/corpus/templates/design/changelog/2026-07-28-interact-mode-now-opens-a-full-screen-responsive-preview-wit.md +6 -0
  106. package/corpus/templates/design/changelog/2026-07-28-show-or-hide-design-editing-chrome-with-cmd-on-apple-devices.md +2 -2
  107. package/corpus/templates/design/changelog/2026-07-28-visual-edit-now-always-shows-the-live-running-app-in-the-can.md +6 -0
  108. package/corpus/templates/dispatch/.agents/skills/security/SKILL.md +13 -2
  109. package/corpus/templates/forms/.agents/skills/security/SKILL.md +13 -2
  110. package/corpus/templates/macros/.agents/skills/security/SKILL.md +13 -2
  111. package/corpus/templates/mail/.agents/skills/security/SKILL.md +13 -2
  112. package/corpus/templates/plan/.agents/skills/security/SKILL.md +13 -2
  113. package/corpus/templates/slides/.agents/skills/security/SKILL.md +13 -2
  114. package/corpus/templates/tasks/.agents/skills/security/SKILL.md +13 -2
  115. package/corpus/toolkit/CHANGELOG.md +13 -0
  116. package/corpus/toolkit/package.json +1 -1
  117. package/corpus/toolkit/src/chat-history.css +1 -2
  118. package/corpus/toolkit/src/composer/TiptapComposer.tsx +45 -0
  119. package/dist/a2a/client.d.ts +7 -0
  120. package/dist/a2a/client.d.ts.map +1 -1
  121. package/dist/a2a/client.js +8 -3
  122. package/dist/a2a/client.js.map +1 -1
  123. package/dist/a2a/server.d.ts.map +1 -1
  124. package/dist/a2a/server.js +18 -3
  125. package/dist/a2a/server.js.map +1 -1
  126. package/dist/a2a/types.d.ts +7 -0
  127. package/dist/a2a/types.d.ts.map +1 -1
  128. package/dist/a2a/types.js.map +1 -1
  129. package/dist/action.d.ts +15 -1
  130. package/dist/action.d.ts.map +1 -1
  131. package/dist/action.js +39 -16
  132. package/dist/action.js.map +1 -1
  133. package/dist/agent/engine/ai-sdk-engine.d.ts.map +1 -1
  134. package/dist/agent/engine/ai-sdk-engine.js +14 -10
  135. package/dist/agent/engine/ai-sdk-engine.js.map +1 -1
  136. package/dist/agent/production-agent.d.ts.map +1 -1
  137. package/dist/agent/production-agent.js +35 -7
  138. package/dist/agent/production-agent.js.map +1 -1
  139. package/dist/agent/thread-data-builder.d.ts +2 -0
  140. package/dist/agent/thread-data-builder.d.ts.map +1 -1
  141. package/dist/agent/thread-data-builder.js +3 -0
  142. package/dist/agent/thread-data-builder.js.map +1 -1
  143. package/dist/chat-threads/migrations.d.ts +21 -0
  144. package/dist/chat-threads/migrations.d.ts.map +1 -0
  145. package/dist/chat-threads/migrations.js +45 -0
  146. package/dist/chat-threads/migrations.js.map +1 -0
  147. package/dist/cli/create.d.ts.map +1 -1
  148. package/dist/cli/create.js +4 -0
  149. package/dist/cli/create.js.map +1 -1
  150. package/dist/cli/skills-content/design-visual-edit-skill.d.ts +1 -1
  151. package/dist/cli/skills-content/design-visual-edit-skill.d.ts.map +1 -1
  152. package/dist/cli/skills-content/design-visual-edit-skill.js +36 -16
  153. package/dist/cli/skills-content/design-visual-edit-skill.js.map +1 -1
  154. package/dist/cli/skills.d.ts.map +1 -1
  155. package/dist/cli/skills.js +15 -0
  156. package/dist/cli/skills.js.map +1 -1
  157. package/dist/client/AgentPanel.d.ts.map +1 -1
  158. package/dist/client/AgentPanel.js +6 -5
  159. package/dist/client/AgentPanel.js.map +1 -1
  160. package/dist/client/AssistantChat.d.ts +37 -0
  161. package/dist/client/AssistantChat.d.ts.map +1 -1
  162. package/dist/client/AssistantChat.js +41 -8
  163. package/dist/client/AssistantChat.js.map +1 -1
  164. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  165. package/dist/client/MultiTabAssistantChat.js +11 -10
  166. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  167. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  168. package/dist/client/agent-chat-adapter.js +19 -3
  169. package/dist/client/agent-chat-adapter.js.map +1 -1
  170. package/dist/client/analytics.d.ts.map +1 -1
  171. package/dist/client/analytics.js +150 -101
  172. package/dist/client/analytics.js.map +1 -1
  173. package/dist/client/chat/message-components.d.ts +2 -1
  174. package/dist/client/chat/message-components.d.ts.map +1 -1
  175. package/dist/client/chat/message-components.js +11 -4
  176. package/dist/client/chat/message-components.js.map +1 -1
  177. package/dist/client/chat/runtime.d.ts.map +1 -1
  178. package/dist/client/chat/runtime.js +2 -1
  179. package/dist/client/chat/runtime.js.map +1 -1
  180. package/dist/client/chat/tool-call-display.d.ts +4 -1
  181. package/dist/client/chat/tool-call-display.d.ts.map +1 -1
  182. package/dist/client/chat/tool-call-display.js +14 -8
  183. package/dist/client/chat/tool-call-display.js.map +1 -1
  184. package/dist/client/chat-model-groups.d.ts.map +1 -1
  185. package/dist/client/chat-model-groups.js +3 -0
  186. package/dist/client/chat-model-groups.js.map +1 -1
  187. package/dist/client/client-status-requests.d.ts +14 -0
  188. package/dist/client/client-status-requests.d.ts.map +1 -0
  189. package/dist/client/client-status-requests.js +126 -0
  190. package/dist/client/client-status-requests.js.map +1 -0
  191. package/dist/client/settings/VoiceTranscriptionSection.js +1 -1
  192. package/dist/client/settings/VoiceTranscriptionSection.js.map +1 -1
  193. package/dist/client/sse-event-processor.d.ts +7 -0
  194. package/dist/client/sse-event-processor.d.ts.map +1 -1
  195. package/dist/client/sse-event-processor.js +8 -3
  196. package/dist/client/sse-event-processor.js.map +1 -1
  197. package/dist/client/use-agent-engine-configured.d.ts.map +1 -1
  198. package/dist/client/use-agent-engine-configured.js +29 -43
  199. package/dist/client/use-agent-engine-configured.js.map +1 -1
  200. package/dist/client/use-chat-models.d.ts.map +1 -1
  201. package/dist/client/use-chat-models.js +11 -11
  202. package/dist/client/use-chat-models.js.map +1 -1
  203. package/dist/client/use-chat-threads.d.ts.map +1 -1
  204. package/dist/client/use-chat-threads.js +23 -7
  205. package/dist/client/use-chat-threads.js.map +1 -1
  206. package/dist/client/use-session.d.ts.map +1 -1
  207. package/dist/client/use-session.js +4 -4
  208. package/dist/client/use-session.js.map +1 -1
  209. package/dist/collab/awareness.d.ts +2 -2
  210. package/dist/collab/awareness.d.ts.map +1 -1
  211. package/dist/collab/struct-routes.d.ts +1 -1
  212. package/dist/db/client.d.ts.map +1 -1
  213. package/dist/db/client.js +48 -4
  214. package/dist/db/client.js.map +1 -1
  215. package/dist/db/ensure-additive-columns.d.ts.map +1 -1
  216. package/dist/db/ensure-additive-columns.js +32 -21
  217. package/dist/db/ensure-additive-columns.js.map +1 -1
  218. package/dist/db/migrations.d.ts +9 -0
  219. package/dist/db/migrations.d.ts.map +1 -1
  220. package/dist/db/migrations.js +14 -3
  221. package/dist/db/migrations.js.map +1 -1
  222. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  223. package/dist/mcp/approval-store.d.ts +16 -0
  224. package/dist/mcp/approval-store.d.ts.map +1 -0
  225. package/dist/mcp/approval-store.js +85 -0
  226. package/dist/mcp/approval-store.js.map +1 -0
  227. package/dist/mcp/build-server.d.ts +1 -34
  228. package/dist/mcp/build-server.d.ts.map +1 -1
  229. package/dist/mcp/build-server.js +220 -23
  230. package/dist/mcp/build-server.js.map +1 -1
  231. package/dist/mcp/embed-app.js +1 -1
  232. package/dist/mcp/embed-app.js.map +1 -1
  233. package/dist/mcp/oauth-client-metadata.d.ts +15 -0
  234. package/dist/mcp/oauth-client-metadata.d.ts.map +1 -0
  235. package/dist/mcp/oauth-client-metadata.js +297 -0
  236. package/dist/mcp/oauth-client-metadata.js.map +1 -0
  237. package/dist/mcp/oauth-route.d.ts.map +1 -1
  238. package/dist/mcp/oauth-route.js +39 -53
  239. package/dist/mcp/oauth-route.js.map +1 -1
  240. package/dist/mcp/screen-memory-stdio.d.ts.map +1 -1
  241. package/dist/mcp/screen-memory-stdio.js +304 -296
  242. package/dist/mcp/screen-memory-stdio.js.map +1 -1
  243. package/dist/mcp/server.d.ts +12 -25
  244. package/dist/mcp/server.d.ts.map +1 -1
  245. package/dist/mcp/server.js +24 -133
  246. package/dist/mcp/server.js.map +1 -1
  247. package/dist/mcp/stdio.d.ts.map +1 -1
  248. package/dist/mcp/stdio.js +36 -35
  249. package/dist/mcp/stdio.js.map +1 -1
  250. package/dist/mcp-client/manager.d.ts.map +1 -1
  251. package/dist/mcp-client/manager.js +6 -6
  252. package/dist/mcp-client/manager.js.map +1 -1
  253. package/dist/mcp-client/oauth-client.d.ts +13 -17
  254. package/dist/mcp-client/oauth-client.d.ts.map +1 -1
  255. package/dist/mcp-client/oauth-client.js +74 -16
  256. package/dist/mcp-client/oauth-client.js.map +1 -1
  257. package/dist/mcp-client/oauth-routes.d.ts +2 -2
  258. package/dist/mcp-client/oauth-routes.d.ts.map +1 -1
  259. package/dist/mcp-client/oauth-routes.js +16 -6
  260. package/dist/mcp-client/oauth-routes.js.map +1 -1
  261. package/dist/mcp-client/routes.js +5 -5
  262. package/dist/mcp-client/routes.js.map +1 -1
  263. package/dist/notifications/routes.d.ts +1 -1
  264. package/dist/observability/routes.d.ts +3 -3
  265. package/dist/progress/routes.d.ts +1 -1
  266. package/dist/provider-api/actions/custom-provider-registration.d.ts +14 -14
  267. package/dist/provider-api/actions/provider-api.d.ts +8 -8
  268. package/dist/resources/handlers.d.ts +1 -1
  269. package/dist/secrets/routes.d.ts +9 -9
  270. package/dist/server/agent-capabilities.d.ts.map +1 -1
  271. package/dist/server/agent-capabilities.js +21 -0
  272. package/dist/server/agent-capabilities.js.map +1 -1
  273. package/dist/server/agent-chat/action-filters-a2a.d.ts +14 -0
  274. package/dist/server/agent-chat/action-filters-a2a.d.ts.map +1 -1
  275. package/dist/server/agent-chat/action-filters-a2a.js +16 -0
  276. package/dist/server/agent-chat/action-filters-a2a.js.map +1 -1
  277. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  278. package/dist/server/agent-chat-plugin.js +11 -1
  279. package/dist/server/agent-chat-plugin.js.map +1 -1
  280. package/dist/server/realtime-token.d.ts +1 -1
  281. package/dist/server/transcribe-voice.d.ts +1 -1
  282. package/dist/server/transcribe-voice.js +12 -6
  283. package/dist/server/transcribe-voice.js.map +1 -1
  284. package/dist/templates/chat/.agents/skills/security/SKILL.md +13 -2
  285. package/dist/templates/chat/README.md +0 -2
  286. package/dist/templates/default/.agents/skills/security/SKILL.md +13 -2
  287. package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +5 -2
  288. package/dist/templates/workspace-core/.agents/skills/external-agents/SKILL.md +18 -0
  289. package/dist/templates/workspace-core/.agents/skills/security/SKILL.md +13 -2
  290. package/docs/content/template-design-collaboration-and-full-apps.mdx +3 -1
  291. package/package.json +7 -4
  292. package/src/a2a/client.ts +18 -4
  293. package/src/a2a/server.ts +19 -3
  294. package/src/a2a/types.ts +7 -0
  295. package/src/action.ts +43 -16
  296. package/src/agent/engine/ai-sdk-engine.ts +14 -10
  297. package/src/agent/production-agent.ts +41 -3
  298. package/src/agent/thread-data-builder.ts +5 -0
  299. package/src/chat-threads/migrations.ts +53 -0
  300. package/src/cli/create.ts +4 -0
  301. package/src/cli/skills-content/design-visual-edit-skill.ts +36 -16
  302. package/src/cli/skills.ts +16 -0
  303. package/src/client/AgentPanel.tsx +9 -5
  304. package/src/client/AssistantChat.tsx +138 -46
  305. package/src/client/MultiTabAssistantChat.tsx +16 -10
  306. package/src/client/agent-chat-adapter.ts +21 -3
  307. package/src/client/analytics.ts +159 -102
  308. package/src/client/chat/message-components.tsx +12 -5
  309. package/src/client/chat/runtime.ts +2 -1
  310. package/src/client/chat/tool-call-display.tsx +25 -2
  311. package/src/client/chat-model-groups.ts +3 -0
  312. package/src/client/client-status-requests.ts +161 -0
  313. package/src/client/settings/VoiceTranscriptionSection.tsx +2 -2
  314. package/src/client/sse-event-processor.ts +15 -3
  315. package/src/client/use-agent-engine-configured.ts +54 -50
  316. package/src/client/use-chat-models.ts +16 -11
  317. package/src/client/use-chat-threads.ts +32 -9
  318. package/src/client/use-session.ts +4 -5
  319. package/src/db/client.ts +83 -8
  320. package/src/db/ensure-additive-columns.ts +31 -20
  321. package/src/db/migrations.ts +22 -2
  322. package/src/mcp/approval-store.ts +99 -0
  323. package/src/mcp/build-server.ts +579 -303
  324. package/src/mcp/embed-app.ts +1 -1
  325. package/src/mcp/oauth-client-metadata.ts +380 -0
  326. package/src/mcp/oauth-route.ts +57 -52
  327. package/src/mcp/screen-memory-stdio.ts +400 -378
  328. package/src/mcp/server.ts +29 -145
  329. package/src/mcp/stdio.ts +72 -80
  330. package/src/mcp-client/manager.ts +6 -10
  331. package/src/mcp-client/oauth-client.ts +127 -36
  332. package/src/mcp-client/oauth-routes.ts +16 -6
  333. package/src/mcp-client/routes.ts +6 -5
  334. package/src/server/agent-capabilities.ts +20 -0
  335. package/src/server/agent-chat/action-filters-a2a.ts +27 -0
  336. package/src/server/agent-chat-plugin.ts +18 -0
  337. package/src/server/transcribe-voice.ts +14 -5
  338. package/src/templates/chat/.agents/skills/security/SKILL.md +13 -2
  339. package/src/templates/chat/README.md +0 -2
  340. package/src/templates/default/.agents/skills/security/SKILL.md +13 -2
  341. package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +5 -2
  342. package/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +18 -0
  343. package/src/templates/workspace-core/.agents/skills/security/SKILL.md +13 -2
@@ -1 +1 @@
1
- {"version":3,"file":"ensure-additive-columns.js","sourceRoot":"","sources":["../../src/db/ensure-additive-columns.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Boot-time convergence for columns Drizzle's schema declares but a\n * pre-existing table is missing.\n *\n * Lives in its own module (like `./widen-columns.js`) so stores/plugins can\n * import it without every `vi.mock(\"../db/client.js\")` test needing to stub\n * it: the helper resolves `isPostgres()` / `getDbExec()` through `client.js`,\n * so a test that mocks the client to SQLite makes the Postgres introspection\n * path a no-op automatically.\n *\n * ## Why\n *\n * `CREATE TABLE IF NOT EXISTS` only runs the CREATE the first time a table is\n * created — once it exists, adding a column to the Drizzle schema (e.g.\n * `schema.ts`) does nothing to already-existing rows unless a hand-written\n * `ALTER TABLE ... ADD COLUMN` migration ships alongside it. Fresh dev\n * databases mask this because the CREATE always includes every declared\n * column; only long-lived, pre-existing production tables are missing the\n * new column. Forgetting the migration turns every query that references the\n * column into a Postgres `42703 (undefined_column)` — e.g.\n * `session_recordings.network_error_count`, which 500'd every\n * `list-session-recordings` call.\n *\n * `ensureAdditiveColumns()` is belt-and-braces for that failure mode: after\n * the authoritative hand-written migrations run, diff each Drizzle table's\n * declared columns against the live table and additively patch any gap. It\n * is NOT a replacement for migrations — it never touches indexes, data\n * transforms, or existing columns, and a hand-written migration should still\n * ship for every schema change. This is a safety net for the case where one\n * doesn't.\n *\n * ## Safety rules (hard)\n *\n * - Additive only: never drops, renames, retypes, or otherwise touches a\n * column that already exists on the live table.\n * - A `NOT NULL` column is only ever added when it has a renderable default\n * (so existing rows get a valid value in the same statement). If it has no\n * renderable default, it is added as nullable when the Drizzle declaration\n * allows null, else the column is SKIPPED entirely with a loud log line\n * naming the column and the reason.\n * - Only simple literal defaults are rendered: numbers, strings, booleans,\n * and `sql` template defaults that stringify to one of a small allow-listed\n * set of safe constants (see `renderDefaultLiteral`). Any other `sql`\n * default is NOT rendered — the column is added without a `DEFAULT` (and\n * without `NOT NULL`, per the rule above) rather than risk interpolating\n * unsafe SQL.\n * - All identifiers (table/column names) are validated against a strict\n * `[A-Za-z_][A-Za-z0-9_]*` pattern and double-quoted; no value from a row or\n * from user data is ever interpolated into the generated SQL.\n * - If the table itself does not exist yet, this is a no-op — table creation\n * owns bringing every declared column into existence for a brand-new table.\n * - Idempotent and best-effort: a failure on one column is logged and does\n * not abort the rest, and a total failure (e.g. `information_schema`\n * unreadable) must never crash boot — the caller decides whether/how to\n * surface `errors` in the returned summary.\n */\n\nimport { isPostgres, type DbExec } from \"./client.js\";\n\nconst PLAIN_IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;\n\nexport interface EnsureAdditiveColumnsLogger {\n info: (message: string) => void;\n warn: (message: string) => void;\n error: (message: string) => void;\n}\n\nconst defaultLogger: EnsureAdditiveColumnsLogger = {\n info: (message) => console.log(message),\n warn: (message) => console.warn(message),\n error: (message) => console.error(message),\n};\n\n/**\n * Minimal shape this module needs from a Drizzle column — matches both\n * `drizzle-orm/pg-core` and `drizzle-orm/sqlite-core` column instances (and\n * the dialect-agnostic wrappers in `./schema.js`, which delegate to one or\n * the other at runtime).\n */\ninterface DeclaredColumnLike {\n name: string;\n notNull: boolean;\n hasDefault: boolean;\n default: unknown;\n getSQLType(): string;\n}\n\ninterface DeclaredTableLike {\n columns: DeclaredColumnLike[];\n name: string;\n schema?: string;\n}\n\nexport interface EnsureAdditiveColumnsOptions {\n db: DbExec;\n /** Drizzle table objects (from `pgTable`/`sqliteTable`, or the dialect-agnostic `table()` helper). */\n tables: unknown[];\n logger?: EnsureAdditiveColumnsLogger;\n}\n\nexport interface EnsureAdditiveColumnsResult {\n /** `\"table.column\"` entries that were successfully added. */\n applied: string[];\n /** Declared columns that were intentionally left unpatched, with why. */\n skipped: Array<{ column: string; reason: string }>;\n /** `\"table.column\"` entries whose ALTER failed unexpectedly (logged, non-fatal). */\n errors: Array<{ column: string; error: string }>;\n}\n\nfunction emptyResult(): EnsureAdditiveColumnsResult {\n return { applied: [], skipped: [], errors: [] };\n}\n\n/**\n * Resolve `getTableConfig` for the dialect currently in effect. Both\n * `drizzle-orm/pg-core` and `drizzle-orm/sqlite-core` export a function with\n * this name and a compatible-enough shape (`{ columns, name, schema? }`), but\n * each only understands its own table class — calling the wrong one throws.\n * `table()` from `./schema.js` builds a `pgTable` on Postgres and a\n * `sqliteTable` everywhere else, so the same dialect switch used there\n * decides which `getTableConfig` to load here.\n */\nasync function loadGetTableConfig(): Promise<\n (table: unknown) => DeclaredTableLike\n> {\n if (isPostgres()) {\n const { getTableConfig } = await import(\"drizzle-orm/pg-core\");\n return getTableConfig as unknown as (table: unknown) => DeclaredTableLike;\n }\n const { getTableConfig } = await import(\"drizzle-orm/sqlite-core\");\n return getTableConfig as unknown as (table: unknown) => DeclaredTableLike;\n}\n\n/**\n * Load every requested Postgres table's live columns in one round trip.\n * Calling information_schema separately for each table adds dozens of\n * serialized network requests to every serverless cold start.\n */\nasync function introspectPostgresColumns(\n db: DbExec,\n tables: DeclaredTableLike[],\n): Promise<Map<string, Set<string>> | null> {\n if (tables.length === 0) return new Map();\n const pairs = [\n ...new Map(\n tables.map((table) => {\n const schema = table.schema || \"public\";\n return [`${schema}.${table.name}`, { schema, table: table.name }];\n }),\n ).values(),\n ];\n const args: unknown[] = [];\n const predicates = pairs.map(({ schema, table }) => {\n args.push(schema, table);\n return `(table_schema = ? AND table_name = ?)`;\n });\n try {\n const { rows } = await db.execute({\n sql: `SELECT table_schema, table_name, column_name\n FROM information_schema.columns\n WHERE ${predicates.join(\" OR \")}`,\n args,\n });\n const columns = new Map<string, Set<string>>();\n for (const row of rows) {\n const key = `${String(row.table_schema)}.${String(row.table_name)}`;\n const tableColumns = columns.get(key) ?? new Set<string>();\n tableColumns.add(String(row.column_name));\n columns.set(key, tableColumns);\n }\n return columns;\n } catch {\n return null;\n }\n}\n\n/**\n * Live SQLite columns already present on `table`, keyed by column name.\n * Returns `null` when the table does not exist or introspection fails.\n */\nasync function introspectSqliteLiveColumns(\n db: DbExec,\n table: string,\n): Promise<Set<string> | null> {\n // SQLite: PRAGMA table_info returns zero rows for a non-existent table\n // (no error), so we can't distinguish \"missing table\" from \"no columns\"\n // that way — probe sqlite_master first.\n try {\n const { rows: existsRows } = await db.execute({\n sql: `SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ? LIMIT 1`,\n args: [table],\n });\n if (existsRows.length === 0) return null;\n // PRAGMA doesn't support bound parameters; the table name is already\n // validated against PLAIN_IDENTIFIER by the caller before we get here.\n const { rows } = await db.execute(`PRAGMA table_info(\"${table}\")`);\n return new Set(rows.map((r) => String(r.name)));\n } catch {\n return null;\n }\n}\n\n/** Quote a validated identifier for use in generated SQL. */\nfunction quoteIdent(name: string): string {\n return `\"${name}\"`;\n}\n\n/**\n * Render a column's declared default as a SQL literal, or `undefined` if it\n * can't be rendered safely. Only numbers, strings, booleans, and a small\n * allow-list of `sql` template defaults that stringify to a known-safe\n * constant are supported — anything else is deliberately left unrendered so\n * we never interpolate arbitrary SQL.\n */\nfunction renderDefaultLiteral(column: DeclaredColumnLike): string | undefined {\n if (!column.hasDefault) return undefined;\n const value = column.default;\n if (value == null) return undefined;\n\n if (typeof value === \"number\" && Number.isFinite(value)) {\n return String(value);\n }\n if (typeof value === \"boolean\") {\n return isPostgres() ? (value ? \"true\" : \"false\") : value ? \"1\" : \"0\";\n }\n if (typeof value === \"string\") {\n // Escape single quotes for a plain string literal.\n return `'${value.replace(/'/g, \"''\")}'`;\n }\n\n // Drizzle `sql` template defaults (e.g. `now()`, `(datetime('now'))`) carry\n // a `queryChunks`/`toQuery`-style object rather than a plain scalar. Only\n // render the handful of known-safe constants used by this codebase's\n // `now()` helper (see ./schema.ts) — never stringify arbitrary SQL.\n const raw = sqlDefaultText(value);\n if (raw == null) return undefined;\n const normalized = raw.trim().toLowerCase();\n const SAFE_SQL_DEFAULTS = new Set([\n \"now()\",\n \"(datetime('now'))\",\n \"current_timestamp\",\n \"current_date\",\n \"current_time\",\n ]);\n if (SAFE_SQL_DEFAULTS.has(normalized)) {\n return raw.trim();\n }\n return undefined;\n}\n\n/**\n * Best-effort extraction of the raw SQL text from a drizzle `SQL` default\n * object (e.g. the value produced by `` sql`now()` ``). Drizzle's `SQL`\n * class publicly exposes `readonly queryChunks: SQLChunk[]`, where a plain\n * literal chunk is a `StringChunk` with a `.value: string[]`. Only accept the\n * text when EVERY chunk is a plain string literal — if the default embeds any\n * dynamic piece (a bound param, column reference, nested table, etc.) we\n * refuse to stringify it, since that could contain unsafe or unbounded SQL.\n */\nfunction sqlDefaultText(value: unknown): string | undefined {\n if (typeof value === \"string\") return value;\n const chunks = (value as { queryChunks?: unknown } | null)?.queryChunks;\n if (!Array.isArray(chunks)) return undefined;\n const parts: string[] = [];\n for (const chunk of chunks) {\n const stringValues = (chunk as { value?: unknown } | null)?.value;\n if (\n !Array.isArray(stringValues) ||\n !stringValues.every((v) => typeof v === \"string\")\n ) {\n return undefined;\n }\n parts.push(...(stringValues as string[]));\n }\n const text = parts.join(\"\");\n return text || undefined;\n}\n\n/** True when an ALTER ... ADD COLUMN failure indicates the column already exists (race with a concurrent boot). */\nfunction isDuplicateColumnError(err: unknown): boolean {\n const msg = (err as { message?: string } | undefined)?.message ?? \"\";\n return (\n /duplicate column name/i.test(msg) ||\n /column .* already exists/i.test(msg) ||\n (err as { code?: string } | undefined)?.code === \"42701\"\n );\n}\n\n/** Annotate a schema-drift-shaped Postgres error (42703/42P01) so logs read plainly. */\nfunction describeSchemaDriftError(err: unknown): string {\n const code = (err as { code?: string } | undefined)?.code;\n const message =\n (err as { message?: string } | undefined)?.message ?? String(err);\n if (code === \"42703\") {\n return `schema drift (42703 undefined_column): ${message}`;\n }\n if (code === \"42P01\") {\n return `schema drift (42P01 undefined_table): ${message}`;\n }\n return message;\n}\n\n/**\n * Diff each declared Drizzle table's columns against the live database and\n * additively `ALTER TABLE ... ADD COLUMN` any that are missing. See the\n * module docstring for the full safety-rule contract.\n *\n * Call this once at boot, immediately after the authoritative hand-written\n * migrations have run. Never throws — every failure path is captured in the\n * returned summary instead so a boot-time caller can log-and-continue.\n */\nexport async function ensureAdditiveColumns(\n options: EnsureAdditiveColumnsOptions,\n): Promise<EnsureAdditiveColumnsResult> {\n const { db, tables, logger = defaultLogger } = options;\n const result = emptyResult();\n\n let getTableConfig: (table: unknown) => DeclaredTableLike;\n try {\n getTableConfig = await loadGetTableConfig();\n } catch (err) {\n result.errors.push({\n column: \"*\",\n error: `failed to load drizzle-orm table config helper: ${err instanceof Error ? err.message : String(err)}`,\n });\n return result;\n }\n\n const declaredTables: DeclaredTableLike[] = [];\n for (const tableObj of tables) {\n try {\n const config = getTableConfig(tableObj);\n if (config.name && PLAIN_IDENTIFIER.test(config.name)) {\n declaredTables.push(config);\n }\n } catch (err) {\n // Not a table this dialect's getTableConfig understands (e.g. a stray\n // export that isn't a Drizzle table) — skip quietly rather than\n // erroring the whole run over one bad entry.\n logger.warn(\n `[ensure-additive-columns] skipping non-table export: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n\n const postgresColumns = isPostgres()\n ? await introspectPostgresColumns(db, declaredTables)\n : null;\n\n for (const config of declaredTables) {\n const tableName = config.name;\n let liveColumns: Set<string> | null;\n try {\n liveColumns = isPostgres()\n ? (postgresColumns?.get(`${config.schema || \"public\"}.${tableName}`) ??\n null)\n : await introspectSqliteLiveColumns(db, tableName);\n } catch (err) {\n result.errors.push({\n column: `${tableName}.*`,\n error: describeSchemaDriftError(err),\n });\n continue;\n }\n\n // Table doesn't exist yet — the creation path (CREATE TABLE IF NOT\n // EXISTS / a real migration) owns bringing every declared column into\n // existence. Nothing to do here.\n if (liveColumns == null) continue;\n\n for (const column of config.columns) {\n const columnName = column.name;\n if (!columnName || !PLAIN_IDENTIFIER.test(columnName)) continue;\n if (liveColumns.has(columnName)) continue; // untouched — already present\n\n const label = `${tableName}.${columnName}`;\n const sqlType = safeSQLType(column, label, logger);\n if (!sqlType) {\n result.skipped.push({\n column: label,\n reason: \"could not determine a safe SQL type for the column\",\n });\n continue;\n }\n\n const defaultLiteral = renderDefaultLiteral(column);\n let notNullClause = \"\";\n if (column.notNull) {\n if (defaultLiteral != null) {\n notNullClause = \" NOT NULL\";\n } else {\n // Can't safely backfill existing rows — adding NOT NULL without a\n // default would fail immediately on any existing row. Skip the\n // whole column rather than add a nullable column that silently\n // disagrees with the Drizzle declaration's NOT NULL contract in\n // a way that could surprise a future safe-default addition.\n result.skipped.push({\n column: label,\n reason:\n \"declared NOT NULL with no renderable default — cannot backfill existing rows safely\",\n });\n logger.warn(\n `[ensure-additive-columns] SKIPPING ${label}: declared NOT NULL with no renderable default (existing rows have no safe backfill value)`,\n );\n continue;\n }\n }\n\n const defaultClause =\n defaultLiteral != null ? ` DEFAULT ${defaultLiteral}` : \"\";\n const ddl = `ALTER TABLE ${quoteIdent(tableName)} ADD COLUMN ${quoteIdent(columnName)} ${sqlType}${defaultClause}${notNullClause}`;\n\n try {\n await db.execute(ddl);\n result.applied.push(label);\n logger.info(`[ensure-additive-columns] added ${label}`);\n } catch (err) {\n if (isDuplicateColumnError(err)) {\n // A concurrent boot already added it — treat as success.\n result.applied.push(label);\n continue;\n }\n const message = describeSchemaDriftError(err);\n result.errors.push({ column: label, error: message });\n logger.error(\n `[ensure-additive-columns] failed to add ${label}: ${message}`,\n );\n // Continue with the remaining columns/tables — one failure must not\n // abort the rest.\n }\n }\n }\n\n return result;\n}\n\n/**\n * Resolve the SQL type to use for a new column. Prefers the column's own\n * `getSQLType()` (the exact type Drizzle would have used in a fresh CREATE\n * TABLE); falls back to skipping (returns `undefined`) if it throws or\n * returns something empty.\n */\nfunction safeSQLType(\n column: DeclaredColumnLike,\n label: string,\n logger: EnsureAdditiveColumnsLogger,\n): string | undefined {\n try {\n const sqlType = column.getSQLType();\n if (typeof sqlType === \"string\" && sqlType.trim()) return sqlType.trim();\n } catch (err) {\n logger.warn(\n `[ensure-additive-columns] getSQLType() threw for ${label}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n return undefined;\n}\n"]}
1
+ {"version":3,"file":"ensure-additive-columns.js","sourceRoot":"","sources":["../../src/db/ensure-additive-columns.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Boot-time convergence for columns Drizzle's schema declares but a\n * pre-existing table is missing.\n *\n * Lives in its own module (like `./widen-columns.js`) so stores/plugins can\n * import it without every `vi.mock(\"../db/client.js\")` test needing to stub\n * it: the helper resolves `isPostgres()` / `getDbExec()` through `client.js`,\n * so a test that mocks the client to SQLite makes the Postgres introspection\n * path a no-op automatically.\n *\n * ## Why\n *\n * `CREATE TABLE IF NOT EXISTS` only runs the CREATE the first time a table is\n * created — once it exists, adding a column to the Drizzle schema (e.g.\n * `schema.ts`) does nothing to already-existing rows unless a hand-written\n * `ALTER TABLE ... ADD COLUMN` migration ships alongside it. Fresh dev\n * databases mask this because the CREATE always includes every declared\n * column; only long-lived, pre-existing production tables are missing the\n * new column. Forgetting the migration turns every query that references the\n * column into a Postgres `42703 (undefined_column)` — e.g.\n * `session_recordings.network_error_count`, which 500'd every\n * `list-session-recordings` call.\n *\n * `ensureAdditiveColumns()` is belt-and-braces for that failure mode: after\n * the authoritative hand-written migrations run, diff each Drizzle table's\n * declared columns against the live table and additively patch any gap. It\n * is NOT a replacement for migrations — it never touches indexes, data\n * transforms, or existing columns, and a hand-written migration should still\n * ship for every schema change. This is a safety net for the case where one\n * doesn't.\n *\n * ## Safety rules (hard)\n *\n * - Additive only: never drops, renames, retypes, or otherwise touches a\n * column that already exists on the live table.\n * - A `NOT NULL` column is only ever added when it has a renderable default\n * (so existing rows get a valid value in the same statement). If it has no\n * renderable default, it is added as nullable when the Drizzle declaration\n * allows null, else the column is SKIPPED entirely with a loud log line\n * naming the column and the reason.\n * - Only simple literal defaults are rendered: numbers, strings, booleans,\n * and `sql` template defaults that stringify to one of a small allow-listed\n * set of safe constants (see `renderDefaultLiteral`). Any other `sql`\n * default is NOT rendered — the column is added without a `DEFAULT` (and\n * without `NOT NULL`, per the rule above) rather than risk interpolating\n * unsafe SQL.\n * - All identifiers (table/column names) are validated against a strict\n * `[A-Za-z_][A-Za-z0-9_]*` pattern and double-quoted; no value from a row or\n * from user data is ever interpolated into the generated SQL.\n * - If the table itself does not exist yet, this is a no-op — table creation\n * owns bringing every declared column into existence for a brand-new table.\n * - Idempotent and best-effort: a failure on one column is logged and does\n * not abort the rest, and a total failure (e.g. `information_schema`\n * unreadable) must never crash boot — the caller decides whether/how to\n * surface `errors` in the returned summary.\n */\n\nimport { isPostgres, type DbExec } from \"./client.js\";\n\nconst PLAIN_IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;\n\nexport interface EnsureAdditiveColumnsLogger {\n info: (message: string) => void;\n warn: (message: string) => void;\n error: (message: string) => void;\n}\n\nconst defaultLogger: EnsureAdditiveColumnsLogger = {\n info: (message) => console.log(message),\n warn: (message) => console.warn(message),\n error: (message) => console.error(message),\n};\n\n/**\n * Minimal shape this module needs from a Drizzle column — matches both\n * `drizzle-orm/pg-core` and `drizzle-orm/sqlite-core` column instances (and\n * the dialect-agnostic wrappers in `./schema.js`, which delegate to one or\n * the other at runtime).\n */\ninterface DeclaredColumnLike {\n name: string;\n notNull: boolean;\n hasDefault: boolean;\n default: unknown;\n getSQLType(): string;\n}\n\ninterface DeclaredTableLike {\n columns: DeclaredColumnLike[];\n name: string;\n schema?: string;\n}\n\nexport interface EnsureAdditiveColumnsOptions {\n db: DbExec;\n /** Drizzle table objects (from `pgTable`/`sqliteTable`, or the dialect-agnostic `table()` helper). */\n tables: unknown[];\n logger?: EnsureAdditiveColumnsLogger;\n}\n\nexport interface EnsureAdditiveColumnsResult {\n /** `\"table.column\"` entries that were successfully added. */\n applied: string[];\n /** Declared columns that were intentionally left unpatched, with why. */\n skipped: Array<{ column: string; reason: string }>;\n /** `\"table.column\"` entries whose ALTER failed unexpectedly (logged, non-fatal). */\n errors: Array<{ column: string; error: string }>;\n}\n\nfunction emptyResult(): EnsureAdditiveColumnsResult {\n return { applied: [], skipped: [], errors: [] };\n}\n\n/**\n * Resolve `getTableConfig` for the dialect currently in effect. Both\n * `drizzle-orm/pg-core` and `drizzle-orm/sqlite-core` export a function with\n * this name and a compatible-enough shape (`{ columns, name, schema? }`), but\n * each only understands its own table class — calling the wrong one throws.\n * `table()` from `./schema.js` builds a `pgTable` on Postgres and a\n * `sqliteTable` everywhere else, so the same dialect switch used there\n * decides which `getTableConfig` to load here.\n */\nasync function loadGetTableConfig(): Promise<\n (table: unknown) => DeclaredTableLike\n> {\n if (isPostgres()) {\n const { getTableConfig } = await import(\"drizzle-orm/pg-core\");\n return getTableConfig as unknown as (table: unknown) => DeclaredTableLike;\n }\n const { getTableConfig } = await import(\"drizzle-orm/sqlite-core\");\n return getTableConfig as unknown as (table: unknown) => DeclaredTableLike;\n}\n\n/**\n * Load every requested Postgres table's live columns in one round trip.\n * Calling information_schema separately for each table adds dozens of\n * serialized network requests to every serverless cold start.\n */\nasync function introspectPostgresColumns(\n db: DbExec,\n tables: DeclaredTableLike[],\n): Promise<Map<string, Set<string>> | null> {\n if (tables.length === 0) return new Map();\n const pairs = [\n ...new Map(\n tables.map((table) => {\n const schema = table.schema || \"public\";\n return [`${schema}.${table.name}`, { schema, table: table.name }];\n }),\n ).values(),\n ];\n const args: unknown[] = [];\n const predicates = pairs.map(({ schema, table }) => {\n args.push(schema, table);\n return `(table_schema = ? AND table_name = ?)`;\n });\n // Deliberately not caught: this one query now covers every table, so\n // swallowing a failure here would skip the entire safety net while\n // reporting a clean result. The caller records it as an error instead.\n const { rows } = await db.execute({\n sql: `SELECT table_schema, table_name, column_name\n FROM information_schema.columns\n WHERE ${predicates.join(\" OR \")}`,\n args,\n });\n const columns = new Map<string, Set<string>>();\n for (const row of rows) {\n const key = `${String(row.table_schema)}.${String(row.table_name)}`;\n const tableColumns = columns.get(key) ?? new Set<string>();\n tableColumns.add(String(row.column_name));\n columns.set(key, tableColumns);\n }\n return columns;\n}\n\n/**\n * Live SQLite columns already present on `table`, keyed by column name.\n * Returns `null` when the table does not exist or introspection fails.\n */\nasync function introspectSqliteLiveColumns(\n db: DbExec,\n table: string,\n): Promise<Set<string> | null> {\n // SQLite: PRAGMA table_info returns zero rows for a non-existent table\n // (no error), so we can't distinguish \"missing table\" from \"no columns\"\n // that way — probe sqlite_master first.\n try {\n const { rows: existsRows } = await db.execute({\n sql: `SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ? LIMIT 1`,\n args: [table],\n });\n if (existsRows.length === 0) return null;\n // PRAGMA doesn't support bound parameters; the table name is already\n // validated against PLAIN_IDENTIFIER by the caller before we get here.\n const { rows } = await db.execute(`PRAGMA table_info(\"${table}\")`);\n return new Set(rows.map((r) => String(r.name)));\n } catch {\n return null;\n }\n}\n\n/** Quote a validated identifier for use in generated SQL. */\nfunction quoteIdent(name: string): string {\n return `\"${name}\"`;\n}\n\n/**\n * Render a column's declared default as a SQL literal, or `undefined` if it\n * can't be rendered safely. Only numbers, strings, booleans, and a small\n * allow-list of `sql` template defaults that stringify to a known-safe\n * constant are supported — anything else is deliberately left unrendered so\n * we never interpolate arbitrary SQL.\n */\nfunction renderDefaultLiteral(column: DeclaredColumnLike): string | undefined {\n if (!column.hasDefault) return undefined;\n const value = column.default;\n if (value == null) return undefined;\n\n if (typeof value === \"number\" && Number.isFinite(value)) {\n return String(value);\n }\n if (typeof value === \"boolean\") {\n return isPostgres() ? (value ? \"true\" : \"false\") : value ? \"1\" : \"0\";\n }\n if (typeof value === \"string\") {\n // Escape single quotes for a plain string literal.\n return `'${value.replace(/'/g, \"''\")}'`;\n }\n\n // Drizzle `sql` template defaults (e.g. `now()`, `(datetime('now'))`) carry\n // a `queryChunks`/`toQuery`-style object rather than a plain scalar. Only\n // render the handful of known-safe constants used by this codebase's\n // `now()` helper (see ./schema.ts) — never stringify arbitrary SQL.\n const raw = sqlDefaultText(value);\n if (raw == null) return undefined;\n const normalized = raw.trim().toLowerCase();\n const SAFE_SQL_DEFAULTS = new Set([\n \"now()\",\n \"(datetime('now'))\",\n \"current_timestamp\",\n \"current_date\",\n \"current_time\",\n ]);\n if (SAFE_SQL_DEFAULTS.has(normalized)) {\n return raw.trim();\n }\n return undefined;\n}\n\n/**\n * Best-effort extraction of the raw SQL text from a drizzle `SQL` default\n * object (e.g. the value produced by `` sql`now()` ``). Drizzle's `SQL`\n * class publicly exposes `readonly queryChunks: SQLChunk[]`, where a plain\n * literal chunk is a `StringChunk` with a `.value: string[]`. Only accept the\n * text when EVERY chunk is a plain string literal — if the default embeds any\n * dynamic piece (a bound param, column reference, nested table, etc.) we\n * refuse to stringify it, since that could contain unsafe or unbounded SQL.\n */\nfunction sqlDefaultText(value: unknown): string | undefined {\n if (typeof value === \"string\") return value;\n const chunks = (value as { queryChunks?: unknown } | null)?.queryChunks;\n if (!Array.isArray(chunks)) return undefined;\n const parts: string[] = [];\n for (const chunk of chunks) {\n const stringValues = (chunk as { value?: unknown } | null)?.value;\n if (\n !Array.isArray(stringValues) ||\n !stringValues.every((v) => typeof v === \"string\")\n ) {\n return undefined;\n }\n parts.push(...(stringValues as string[]));\n }\n const text = parts.join(\"\");\n return text || undefined;\n}\n\n/** True when an ALTER ... ADD COLUMN failure indicates the column already exists (race with a concurrent boot). */\nfunction isDuplicateColumnError(err: unknown): boolean {\n const msg = (err as { message?: string } | undefined)?.message ?? \"\";\n return (\n /duplicate column name/i.test(msg) ||\n /column .* already exists/i.test(msg) ||\n (err as { code?: string } | undefined)?.code === \"42701\"\n );\n}\n\n/** Annotate a schema-drift-shaped Postgres error (42703/42P01) so logs read plainly. */\nfunction describeSchemaDriftError(err: unknown): string {\n const code = (err as { code?: string } | undefined)?.code;\n const message =\n (err as { message?: string } | undefined)?.message ?? String(err);\n if (code === \"42703\") {\n return `schema drift (42703 undefined_column): ${message}`;\n }\n if (code === \"42P01\") {\n return `schema drift (42P01 undefined_table): ${message}`;\n }\n return message;\n}\n\n/**\n * Diff each declared Drizzle table's columns against the live database and\n * additively `ALTER TABLE ... ADD COLUMN` any that are missing. See the\n * module docstring for the full safety-rule contract.\n *\n * Call this once at boot, immediately after the authoritative hand-written\n * migrations have run. Never throws — every failure path is captured in the\n * returned summary instead so a boot-time caller can log-and-continue.\n */\nexport async function ensureAdditiveColumns(\n options: EnsureAdditiveColumnsOptions,\n): Promise<EnsureAdditiveColumnsResult> {\n const { db, tables, logger = defaultLogger } = options;\n const result = emptyResult();\n\n let getTableConfig: (table: unknown) => DeclaredTableLike;\n try {\n getTableConfig = await loadGetTableConfig();\n } catch (err) {\n result.errors.push({\n column: \"*\",\n error: `failed to load drizzle-orm table config helper: ${err instanceof Error ? err.message : String(err)}`,\n });\n return result;\n }\n\n const declaredTables: DeclaredTableLike[] = [];\n for (const tableObj of tables) {\n try {\n const config = getTableConfig(tableObj);\n if (config.name && PLAIN_IDENTIFIER.test(config.name)) {\n declaredTables.push(config);\n }\n } catch (err) {\n // Not a table this dialect's getTableConfig understands (e.g. a stray\n // export that isn't a Drizzle table) — skip quietly rather than\n // erroring the whole run over one bad entry.\n logger.warn(\n `[ensure-additive-columns] skipping non-table export: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n\n let postgresColumns: Map<string, Set<string>> | null = null;\n if (isPostgres()) {\n try {\n postgresColumns = await introspectPostgresColumns(db, declaredTables);\n } catch (err) {\n // One batch probe now covers every table, so its failure means nothing\n // was checked. Returning an empty-but-clean result here would be\n // indistinguishable from \"schema already correct\".\n result.errors.push({\n column: \"*\",\n error: describeSchemaDriftError(err),\n });\n return result;\n }\n }\n\n for (const config of declaredTables) {\n const tableName = config.name;\n let liveColumns: Set<string> | null;\n try {\n liveColumns = isPostgres()\n ? (postgresColumns?.get(`${config.schema || \"public\"}.${tableName}`) ??\n null)\n : await introspectSqliteLiveColumns(db, tableName);\n } catch (err) {\n result.errors.push({\n column: `${tableName}.*`,\n error: describeSchemaDriftError(err),\n });\n continue;\n }\n\n // Table doesn't exist yet — the creation path (CREATE TABLE IF NOT\n // EXISTS / a real migration) owns bringing every declared column into\n // existence. Nothing to do here.\n if (liveColumns == null) continue;\n\n for (const column of config.columns) {\n const columnName = column.name;\n if (!columnName || !PLAIN_IDENTIFIER.test(columnName)) continue;\n if (liveColumns.has(columnName)) continue; // untouched — already present\n\n const label = `${tableName}.${columnName}`;\n const sqlType = safeSQLType(column, label, logger);\n if (!sqlType) {\n result.skipped.push({\n column: label,\n reason: \"could not determine a safe SQL type for the column\",\n });\n continue;\n }\n\n const defaultLiteral = renderDefaultLiteral(column);\n let notNullClause = \"\";\n if (column.notNull) {\n if (defaultLiteral != null) {\n notNullClause = \" NOT NULL\";\n } else {\n // Can't safely backfill existing rows — adding NOT NULL without a\n // default would fail immediately on any existing row. Skip the\n // whole column rather than add a nullable column that silently\n // disagrees with the Drizzle declaration's NOT NULL contract in\n // a way that could surprise a future safe-default addition.\n result.skipped.push({\n column: label,\n reason:\n \"declared NOT NULL with no renderable default — cannot backfill existing rows safely\",\n });\n logger.warn(\n `[ensure-additive-columns] SKIPPING ${label}: declared NOT NULL with no renderable default (existing rows have no safe backfill value)`,\n );\n continue;\n }\n }\n\n const defaultClause =\n defaultLiteral != null ? ` DEFAULT ${defaultLiteral}` : \"\";\n const ddl = `ALTER TABLE ${quoteIdent(tableName)} ADD COLUMN ${quoteIdent(columnName)} ${sqlType}${defaultClause}${notNullClause}`;\n\n try {\n await db.execute(ddl);\n result.applied.push(label);\n logger.info(`[ensure-additive-columns] added ${label}`);\n } catch (err) {\n if (isDuplicateColumnError(err)) {\n // A concurrent boot already added it — treat as success.\n result.applied.push(label);\n continue;\n }\n const message = describeSchemaDriftError(err);\n result.errors.push({ column: label, error: message });\n logger.error(\n `[ensure-additive-columns] failed to add ${label}: ${message}`,\n );\n // Continue with the remaining columns/tables — one failure must not\n // abort the rest.\n }\n }\n }\n\n return result;\n}\n\n/**\n * Resolve the SQL type to use for a new column. Prefers the column's own\n * `getSQLType()` (the exact type Drizzle would have used in a fresh CREATE\n * TABLE); falls back to skipping (returns `undefined`) if it throws or\n * returns something empty.\n */\nfunction safeSQLType(\n column: DeclaredColumnLike,\n label: string,\n logger: EnsureAdditiveColumnsLogger,\n): string | undefined {\n try {\n const sqlType = column.getSQLType();\n if (typeof sqlType === \"string\" && sqlType.trim()) return sqlType.trim();\n } catch (err) {\n logger.warn(\n `[ensure-additive-columns] getSQLType() threw for ${label}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n return undefined;\n}\n"]}
@@ -64,6 +64,15 @@ export interface MigrationEntry {
64
64
  * throws at startup (programmer error, not a runtime data problem).
65
65
  */
66
66
  name?: string;
67
+ /**
68
+ * JS step for a backfill SQL cannot express — e.g. a repair whose value comes
69
+ * from application-level parsing of a stored blob. Runs BEFORE this entry's
70
+ * SQL and before the bookkeeping row is written, so a throw leaves the
71
+ * migration unrecorded and it retries on the next boot. Pass `sql: {}` for a
72
+ * run-only entry. Give run-only entries a `name`: the legacy version gate
73
+ * would otherwise mark them applied via any later migration's MAX advance.
74
+ */
75
+ run?: () => Promise<void>;
67
76
  }
68
77
  /**
69
78
  * Runs a list of migrations against the configured database, gated by a
@@ -1 +1 @@
1
- {"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../../src/db/migrations.ts"],"names":[],"mappings":"AA2EA,KAAK,cAAc,GAAG,CAAC,QAAQ,EAAE,GAAG,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AA4B9D;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAK5D;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CASvD;AAmDD,MAAM,WAAW,oBAAoB;IACnC;;;;;;;;;;;;OAYG;IACH,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3E,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,YAAY,CAAC;IAClB;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAQD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AACH,wBAAgB,aAAa,CAC3B,UAAU,EAAE,KAAK,CAAC,cAAc,CAAC,EACjC,OAAO,EAAE,oBAAoB,GAC5B,cAAc,CAuYhB"}
1
+ {"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../../src/db/migrations.ts"],"names":[],"mappings":"AA2EA,KAAK,cAAc,GAAG,CAAC,QAAQ,EAAE,GAAG,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AA4B9D;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAK5D;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CASvD;AAmDD,MAAM,WAAW,oBAAoB;IACnC;;;;;;;;;;;;OAYG;IACH,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3E,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,YAAY,CAAC;IAClB;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;;;OAOG;IACH,GAAG,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAQD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AACH,wBAAgB,aAAa,CAC3B,UAAU,EAAE,KAAK,CAAC,cAAc,CAAC,EACjC,OAAO,EAAE,oBAAoB,GAC5B,cAAc,CAkZhB"}
@@ -267,6 +267,8 @@ export function runMigrations(migrations, options) {
267
267
  try {
268
268
  // D1 is SQLite-compatible
269
269
  const raw = resolveMigrationSql(m.sql, false);
270
+ if (m.run)
271
+ await m.run();
270
272
  const recordStatements = [
271
273
  m.name
272
274
  ? d1
@@ -281,9 +283,13 @@ export function runMigrations(migrations, options) {
281
283
  ].filter((s) => s != null);
282
284
  if (raw == null) {
283
285
  // Dialect-gated migration with no SQL for this dialect; still
284
- // record it so we don't retry forever.
285
- for (const stmt of recordStatements)
286
- await stmt.run();
286
+ // record it so we don't retry forever. Keep the name + legacy
287
+ // version rows atomic: if an isolate is interrupted between
288
+ // separate writes, the name row would suppress every retry even
289
+ // though the legacy bookkeeping row never landed.
290
+ if (recordStatements.length > 0) {
291
+ await d1.batch(recordStatements);
292
+ }
287
293
  continue;
288
294
  }
289
295
  const originalStatements = splitSqlStatements(raw);
@@ -448,6 +454,11 @@ export function runMigrations(migrations, options) {
448
454
  if (m.version > current) {
449
455
  recordSql.push({ sql: insertVersionSql, args: [m.version] });
450
456
  }
457
+ // A throw here escapes to the outer handler with nothing recorded,
458
+ // so the entry is retried on the next boot rather than being marked
459
+ // applied against work that never happened.
460
+ if (m.run)
461
+ await m.run();
451
462
  if (raw == null) {
452
463
  // Dialect-gated migration with no SQL for this dialect; still mark
453
464
  // as applied so we don't retry forever.
@@ -1 +1 @@
1
- {"version":3,"file":"migrations.js","sourceRoot":"","sources":["../../src/db/migrations.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n getDbExec,\n createDbExec,\n isPostgres,\n getDialect,\n getMigrationDatabaseUrl,\n getCloudflareD1Binding,\n retrySqliteBusy,\n type DbExec,\n} from \"./client.js\";\n\ninterface D1PreparedStatementLike {\n bind(...values: unknown[]): D1PreparedStatementLike;\n all(): Promise<{ results: Record<string, unknown>[] }>;\n first<T = Record<string, unknown>>(): Promise<T | null>;\n run(): Promise<unknown>;\n}\n\ninterface D1DatabaseLike {\n prepare(query: string): D1PreparedStatementLike;\n batch(statements: D1PreparedStatementLike[]): Promise<unknown>;\n}\n\n// ---------------------------------------------------------------------------\n// Shared direct-endpoint exec across concurrent migration runners per boot.\n//\n// On each serverless cold start THREE plugins call runMigrations (core,\n// org, context-xray). Without coordination each would open an independent\n// direct-endpoint Neon connection AND leave it idle for ~10 s after\n// migrations finish. We avoid that by sharing a single direct exec for\n// the duration of the migration window: the first caller opens it,\n// subsequent callers reuse the same promise, and the last caller closes it\n// via reference-counting. The exec is nulled out after the window so a\n// second cold start (e.g. after a Lambda thaw) gets a fresh connection.\n// ---------------------------------------------------------------------------\n\nlet _migrationExecPromise: Promise<DbExec> | null = null;\nlet _migrationExecRefCount = 0;\n\nasync function acquireMigrationExec(): Promise<DbExec> {\n if (!_migrationExecPromise) {\n const opened = createDbExec({ url: getMigrationDatabaseUrl() });\n _migrationExecPromise = opened;\n opened.catch(() => {\n // Opening the connection failed. Reset the shared state so the next\n // caller retries with a fresh connection instead of awaiting this\n // permanently rejected promise — and zero the ref count, because none\n // of the awaiters that failed here will ever call release.\n if (_migrationExecPromise === opened) {\n _migrationExecPromise = null;\n _migrationExecRefCount = 0;\n }\n });\n }\n _migrationExecRefCount++;\n return _migrationExecPromise;\n}\n\nasync function releaseMigrationExec(): Promise<void> {\n _migrationExecRefCount--;\n if (_migrationExecRefCount > 0) return;\n // Last caller — close and reset so next boot gets a fresh connection.\n const execPromise = _migrationExecPromise;\n _migrationExecPromise = null;\n _migrationExecRefCount = 0;\n if (!execPromise) return;\n try {\n const exec = await execPromise;\n await exec.close?.();\n } catch {\n // Swallow close errors — the migrations themselves already succeeded or\n // failed; a cleanup error should not surface as a boot crash.\n }\n}\n\ntype NitroPluginDef = (nitroApp: any) => void | Promise<void>;\n\n/**\n * Rewrite SQLite-specific SQL to Postgres-compatible equivalents.\n * Handles: datetime('now') → CURRENT_TIMESTAMP, AUTOINCREMENT → GENERATED, etc.\n */\nfunction adaptSqlForPostgres(sql: string): string {\n return sql\n .replace(/datetime\\s*\\(\\s*'now'\\s*\\)/gi, \"CURRENT_TIMESTAMP\")\n .replace(/\\bAUTOINCREMENT\\b/gi, \"\")\n .replace(/\\bINTEGER\\b/gi, \"BIGINT\");\n}\n\nconst IF_NOT_EXISTS_ADD_COLUMN_RE = /ADD\\s+COLUMN\\s+IF\\s+NOT\\s+EXISTS/i;\n\n/**\n * Strip Postgres-only syntax that SQLite doesn't support.\n * Handles: ALTER TABLE ... ADD COLUMN IF NOT EXISTS → ADD COLUMN\n *\n * Note: SQLite does not have a native equivalent, so the idempotent\n * semantic is emulated at the executor level by swallowing the\n * \"duplicate column name\" error for statements that originally carried\n * the clause. See `hadIfNotExists` tracking in the run loop.\n */\nfunction adaptSqlForSqlite(sql: string): string {\n return sql.replace(/ADD\\s+COLUMN\\s+IF\\s+NOT\\s+EXISTS/gi, \"ADD COLUMN\");\n}\n\n/**\n * True when an error from `ALTER TABLE ... ADD COLUMN` indicates the\n * column already existed. Recognizes both SQLite (\"duplicate column\n * name\") and Postgres (\"column ... already exists\" — exact text varies\n * by error code 42701, but the substring is stable). Exported so other\n * idempotent column-upgrade loops in the codebase don't reinvent this\n * regex with subtly different shapes.\n */\nexport function isDuplicateColumnError(err: unknown): boolean {\n const msg = (err as Error | undefined)?.message ?? \"\";\n return (\n /duplicate column name/i.test(msg) || /column .* already exists/i.test(msg)\n );\n}\n\n/**\n * True when a migration statement failed because the connected DB ROLE lacks\n * privilege — e.g. a permission-limited dev/replica role that doesn't own the\n * table. Postgres raises SQLSTATE 42501 (\"insufficient_privilege\", routine\n * aclcheck_error, message \"must be owner of table …\"). We treat these as\n * NON-FATAL so a perms-limited database can't crash-loop the whole server: the\n * migration is skipped (left unrecorded) and a properly-privileged role applies\n * it later. Production, where the role owns its tables, never hits this path.\n */\nexport function isPermissionError(err: unknown): boolean {\n const e = err as { code?: string; message?: string } | undefined;\n if (e?.code === \"42501\") return true;\n const msg = e?.message ?? \"\";\n return (\n /must be owner of/i.test(msg) ||\n /permission denied/i.test(msg) ||\n /insufficient privilege/i.test(msg)\n );\n}\n\n/**\n * Split a multi-statement SQL blob into individual statements.\n *\n * libsql's `execute(sql)` only runs the first statement in a multi-statement\n * string. This splitter is intentionally simple: it respects single-quoted\n * string literals (with `''` escaping) and `--` line comments, and splits on\n * top-level `;`. It does NOT attempt to parse `$$`-quoted Postgres function\n * bodies — migrations that define functions/triggers with `;` inside bodies\n * should pass a single-statement migration per entry instead.\n */\nfunction splitSqlStatements(sql: string): string[] {\n const out: string[] = [];\n let buf = \"\";\n let i = 0;\n let inSingle = false;\n while (i < sql.length) {\n const ch = sql[i];\n const next = sql[i + 1];\n if (!inSingle && ch === \"-\" && next === \"-\") {\n // Skip to end of line\n while (i < sql.length && sql[i] !== \"\\n\") i++;\n continue;\n }\n if (ch === \"'\") {\n buf += ch;\n if (inSingle && next === \"'\") {\n buf += next;\n i += 2;\n continue;\n }\n inSingle = !inSingle;\n i++;\n continue;\n }\n if (ch === \";\" && !inSingle) {\n const trimmed = buf.trim();\n if (trimmed) out.push(trimmed);\n buf = \"\";\n i++;\n continue;\n }\n buf += ch;\n i++;\n }\n const tail = buf.trim();\n if (tail) out.push(tail);\n return out;\n}\n\nexport interface RunMigrationsOptions {\n /**\n * Name of the migrations bookkeeping table. REQUIRED — there is intentionally\n * no default. Two templates that share a database (e.g. via the same Neon URL)\n * each have their own version space starting at v1, and a single shared\n * `_migrations` table will silently skip the second template's migrations if\n * the first has already advanced past those version numbers. This caused the\n * design template's migrations to be skipped entirely on a Neon DB that\n * slides had already populated up to v15 (PR #320 era).\n *\n * Use one bookkeeping table per template, e.g. `slides_migrations`. Core\n * feature plugins (e.g. the org module) follow the same convention with\n * their own prefix, e.g. `_org_migrations`.\n */\n table: string;\n}\n\n/**\n * A single migration entry.\n *\n * `sql` can be a string (runs on every dialect) or an object with dialect\n * keys for dialect-gated SQL. Useful when Postgres needs an ALTER that\n * SQLite can't parse.\n *\n * { version: 14, sql: { postgres: \"ALTER TABLE …\" } } // no-op on sqlite\n * { version: 15, sql: { sqlite: \"…\", postgres: \"…\" } } // both dialects\n *\n * `name` is an optional stable, unique slug that opts a migration into\n * **name-based tracking** instead of the legacy version-number gate. See the\n * \"Name-based tracking\" section on `runMigrations` for why this exists and\n * exactly how the two gating strategies interact.\n */\nexport type MigrationSql = string | { postgres?: string; sqlite?: string };\n\nexport interface MigrationEntry {\n version: number;\n sql: MigrationSql;\n /**\n * Stable, unique slug for this migration (e.g. `\"analytics-alert-rules-table\"`).\n * When present, this migration is tracked by NAME instead of by version\n * number — see the `runMigrations` doc comment for the full rationale and\n * gating rules. Must be unique across the migration list; a duplicate name\n * throws at startup (programmer error, not a runtime data problem).\n */\n name?: string;\n}\n\nfunction resolveMigrationSql(sql: MigrationSql, pg: boolean): string | null {\n if (typeof sql === \"string\") return sql;\n const raw = pg ? sql.postgres : sql.sqlite;\n return raw ?? null;\n}\n\n/**\n * Runs a list of migrations against the configured database, gated by a\n * per-table bookkeeping row (`options.table`) so repeated boots only apply\n * new migrations.\n *\n * ## Name-based tracking (why it exists)\n *\n * The legacy gate is purely numeric: `SELECT MAX(version)` from the\n * bookkeeping table, then apply every entry whose `version` is greater. That\n * scheme silently breaks down when **two independent branches each extend the\n * same migration list with different DDL under the same version numbers** —\n * exactly what happened to the analytics template's v75-v83 range. Branch A\n * shipped alert-rule tables as v75-v78; branch B shipped unrelated DDL as its\n * own v75-v83. Whichever branch deployed first recorded \"v75..v83 applied\"\n * in the bookkeeping table. When the other branch merged and deployed, its\n * migrations at those same version numbers were never applied — `MAX(version)`\n * was already ≥ their version, so the gate treated them as done even though\n * their actual DDL had never run. The result: `analytics_migrations` showed\n * rows for 1..83 with no gaps, yet `analytics_alert_rules`,\n * `analytics_alert_incidents`, and `session_recordings.network_error_count`\n * did not exist in production. Version numbers are not a stable identity —\n * they're just sequence position, and sequence position collides across\n * branches.\n *\n * Name-based tracking fixes this by keying application on a stable, unique\n * **string slug** instead of a position in a shared integer sequence:\n *\n * - Entries with a `name` are recorded in a companion table,\n * `${table}_named` (`name TEXT PRIMARY KEY`), and APPLY IFF their name is\n * absent from that table — completely independent of version numbers or\n * the legacy `MAX(version)` gate. Two branches can both ship a migration\n * named `\"analytics-alert-rules-table\"` at version 75, or one at version 75\n * and the other at version 90 after a rebase — either way it applies\n * exactly once per database, keyed on the name.\n * - Entries WITHOUT a `name` keep the exact legacy behavior\n * (`version > MAX(recorded version)`), so nothing changes for existing\n * unnamed migrations.\n * - Named migrations still execute in list order, interleaved with unnamed\n * ones exactly as written.\n * - When a named migration's DDL runs, we record BOTH the named row (always)\n * and — if its `version` is greater than the current legacy max — the\n * legacy version row too, in the SAME atomic batch/transaction as the DDL.\n * This keeps the legacy `MAX(version)` gate monotonically advancing for\n * any unnamed migrations that come after it in the list, while ensuring a\n * named migration is never \"double recorded\" in a way that would let it\n * re-apply.\n * - A duplicate `name` across the migration list throws at startup — that's\n * a programmer error (copy-paste or merge mistake), not a runtime data\n * problem, and failing loud beats silently tracking the wrong row.\n *\n * New migrations should always set a `name`. Legacy unnamed migrations don't\n * need to be renamed retroactively — the two gating strategies coexist in the\n * same list — but if a table is EVER at risk of being shared across parallel\n * branches (any template's own migrations qualify, since branches routinely\n * extend the same list concurrently), giving new entries a name is what makes\n * them immune to the collision class described above.\n */\nexport function runMigrations(\n migrations: Array<MigrationEntry>,\n options: RunMigrationsOptions,\n): NitroPluginDef {\n const table = options?.table;\n if (\n !table ||\n typeof table !== \"string\" ||\n !/^[A-Za-z_][A-Za-z0-9_]*$/.test(table)\n ) {\n throw new Error(\n \"runMigrations: `table` option is required and must be a valid SQL identifier \" +\n '(e.g. `{ table: \"slides_migrations\" }`). See packages/core/src/db/migrations.ts ' +\n \"for why this is required (shared-DB version-collision bug).\",\n );\n }\n\n // Duplicate-name detection — programmer error, fail loud at startup rather\n // than silently tracking the wrong migration's applied state.\n {\n const seenNames = new Set<string>();\n for (const m of migrations) {\n if (!m.name) continue;\n if (seenNames.has(m.name)) {\n throw new Error(\n `runMigrations: duplicate migration name \"${m.name}\" in the migration list for table \"${table}\". ` +\n \"Migration names must be unique — pick a different stable slug.\",\n );\n }\n seenNames.add(m.name);\n }\n }\n\n const namedTable = `${table}_named`;\n\n return async () => {\n try {\n // Check for Cloudflare D1 binding (only if DATABASE_URL not set)\n const d1 =\n getDialect() === \"d1\"\n ? (getCloudflareD1Binding() as D1DatabaseLike | undefined)\n : null;\n if (d1) {\n await d1\n .prepare(\n `CREATE TABLE IF NOT EXISTS ${table} (version INTEGER PRIMARY KEY)`,\n )\n .run();\n await d1\n .prepare(\n `CREATE TABLE IF NOT EXISTS ${namedTable} (name TEXT PRIMARY KEY, version INTEGER, applied_at TEXT NOT NULL DEFAULT (datetime('now')))`,\n )\n .run();\n const firstRow = await d1\n .prepare(`SELECT MAX(version) as v FROM ${table}`)\n .first<{ v?: number }>();\n const current = (firstRow?.v as number) ?? 0;\n\n const appliedNamesRows = await d1\n .prepare(`SELECT name FROM ${namedTable}`)\n .all();\n const appliedNames = new Set(\n (appliedNamesRows?.results ?? []).map((r) => String(r.name)),\n );\n\n const pending = migrations.filter((m) =>\n m.name ? !appliedNames.has(m.name) : m.version > current,\n );\n\n for (const m of pending) {\n try {\n // D1 is SQLite-compatible\n const raw = resolveMigrationSql(m.sql, false);\n const recordStatements = [\n m.name\n ? d1\n .prepare(\n `INSERT OR IGNORE INTO ${namedTable} (name, version) VALUES (?, ?)`,\n )\n .bind(m.name, m.version)\n : null,\n m.version > current\n ? d1\n .prepare(`INSERT OR IGNORE INTO ${table} VALUES (?)`)\n .bind(m.version)\n : null,\n ].filter((s): s is NonNullable<typeof s> => s != null);\n\n if (raw == null) {\n // Dialect-gated migration with no SQL for this dialect; still\n // record it so we don't retry forever.\n for (const stmt of recordStatements) await stmt.run();\n continue;\n }\n const originalStatements = splitSqlStatements(raw);\n const statements = originalStatements.map((orig) => ({\n sql: adaptSqlForSqlite(orig),\n hadIfNotExists: IF_NOT_EXISTS_ADD_COLUMN_RE.test(orig),\n }));\n const hasIfNotExists = statements.some((s) => s.hadIfNotExists);\n if (hasIfNotExists) {\n // Per-statement path: we need to swallow \"duplicate column\"\n // errors for statements that originally carried\n // `ADD COLUMN IF NOT EXISTS`, which a batch() can't express.\n // Loses atomicity, but the idempotent-ADD-COLUMN semantic\n // means a partial re-run resolves cleanly on retry.\n for (const { sql: stmt, hadIfNotExists } of statements) {\n try {\n await d1.prepare(stmt).run();\n } catch (err) {\n if (hadIfNotExists && isDuplicateColumnError(err)) continue;\n throw err;\n }\n }\n for (const stmt of recordStatements) await stmt.run();\n } else {\n // Atomic batch: all statements + bookkeeping inserts land in\n // the same transaction. A failing statement rolls the whole\n // migration back, so we never record a half-applied migration.\n await d1.batch([\n ...statements.map((s) => d1.prepare(s.sql)),\n ...recordStatements,\n ]);\n }\n console.log(\n `[db] Applied migration ${m.name ? `\"${m.name}\" ` : \"\"}v${m.version} (${statements.length} statement${statements.length === 1 ? \"\" : \"s\"})`,\n );\n } catch (err) {\n console.error(\n `[db] Migration ${m.name ? `\"${m.name}\" ` : \"\"}v${m.version} FAILED:`,\n (err as Error).message,\n \"\\nSQL:\",\n JSON.stringify(m.sql),\n );\n throw err;\n }\n }\n return;\n }\n\n // Generic path — works for libsql and Postgres\n const pg = isPostgres();\n\n // ---------------------------------------------------------------------------\n // Fast-path: read migration state through the regular pooled singleton before\n // opening the direct-endpoint connection.\n //\n // On Postgres every cold start previously opened a fresh direct-endpoint Neon\n // connection (bypassing PgBouncer, which is needed only for DDL), ran\n // CREATE TABLE IF NOT EXISTS + SELECT MAX(version) even with zero pending\n // migrations, and never closed the pool — it idled for ~10 s. Three runners\n // (core, org, context-xray) did this independently per boot.\n //\n // Now: use the pooled singleton (getDbExec) for the bookkeeping SELECT. If\n // the migrations table does not yet exist we treat it as \"all migrations\n // pending\" (current = -1). Only when there are pending migrations do we open\n // the direct-endpoint exec (DDL is the only thing Neon's PgBouncer blocks —\n // documented at getMigrationDatabaseUrl). The direct exec is shared across\n // concurrent runners via acquireMigrationExec() and closed after the last\n // caller via releaseMigrationExec().\n // ---------------------------------------------------------------------------\n\n let current = -1; // sentinel: \"table missing\" → treat all as pending\n let namedRowsMissing = false; // sentinel: \"named table missing\" → no names applied yet\n\n // Any migration with a `name` is a candidate regardless of version, so\n // the fast-path \"anything pending?\" check must also account for names.\n const hasNamedMigrations = migrations.some((m) => m.name);\n let appliedNames = new Set<string>();\n\n if (pg) {\n try {\n const { rows } = await getDbExec().execute(\n `SELECT MAX(version) as v FROM ${table}`,\n );\n current = (rows[0]?.v as number) ?? 0;\n } catch {\n // Table doesn't exist yet — leave current = -1 so all migrations apply.\n }\n if (hasNamedMigrations) {\n try {\n const { rows } = await getDbExec().execute(\n `SELECT name FROM ${namedTable}`,\n );\n appliedNames = new Set(rows.map((r) => String(r.name)));\n } catch {\n // Named table doesn't exist yet — leave appliedNames empty so all\n // named migrations apply.\n namedRowsMissing = true;\n }\n }\n }\n\n // For SQLite we still use getDbExec() as exec throughout (no pooler concern).\n // For Postgres we only open the direct exec when there are pending migrations.\n const pendingFast = pg\n ? migrations.filter((m) =>\n m.name ? !appliedNames.has(m.name) : m.version > current,\n )\n : null; // SQLite: compute after table creation below\n\n // Short-circuit: Postgres with nothing to do — skip the direct connection entirely.\n if (pg && pendingFast !== null && pendingFast.length === 0) {\n return;\n }\n\n // Acquire the exec appropriate for the dialect.\n // For Postgres: the shared direct-endpoint exec (DDL-safe, closed on release).\n // For SQLite/libsql: the singleton pooled exec (no pooler concern).\n const exec = pg ? await acquireMigrationExec() : getDbExec();\n\n try {\n // Retry initial table creation — SQLITE_BUSY_RECOVERY can occur on HMR\n // restarts when WAL files from the previous process haven't been released yet.\n await retrySqliteBusy(\n () =>\n exec.execute(\n `CREATE TABLE IF NOT EXISTS ${table} (version INTEGER PRIMARY KEY)`,\n ),\n { maxAttempts: 6, baseDelayMs: 1000, rethrow: true },\n );\n // Companion name-keyed bookkeeping table — never alters the existing\n // `${table}`'s PRIMARY KEY, so legacy version rows keep working exactly\n // as before. See the `runMigrations` doc comment for why this exists.\n await retrySqliteBusy(\n () =>\n exec.execute(\n `CREATE TABLE IF NOT EXISTS ${namedTable} (name TEXT PRIMARY KEY, version INTEGER, applied_at ${pg ? \"TIMESTAMP NOT NULL DEFAULT now()\" : \"TEXT NOT NULL DEFAULT (datetime('now'))\"})`,\n ),\n { maxAttempts: 6, baseDelayMs: 1000, rethrow: true },\n );\n\n // For Postgres, current was already set by the fast-path SELECT above.\n // For SQLite we run the SELECT now (via the same exec, which is the singleton).\n if (!pg) {\n const { rows } = await exec.execute(\n `SELECT MAX(version) as v FROM ${table}`,\n );\n current = (rows[0]?.v as number) ?? 0;\n if (hasNamedMigrations) {\n const { rows: nameRows } = await exec.execute(\n `SELECT name FROM ${namedTable}`,\n );\n appliedNames = new Set(nameRows.map((r) => String(r.name)));\n }\n } else if (current === -1 || (hasNamedMigrations && namedRowsMissing)) {\n // Fast-path read failed (table was absent on the pooler): re-read via the\n // direct exec now that CREATE TABLE IF NOT EXISTS has ensured it exists.\n if (current === -1) {\n const { rows } = await exec.execute(\n `SELECT MAX(version) as v FROM ${table}`,\n );\n current = (rows[0]?.v as number) ?? 0;\n }\n if (hasNamedMigrations && namedRowsMissing) {\n const { rows: nameRows } = await exec.execute(\n `SELECT name FROM ${namedTable}`,\n );\n appliedNames = new Set(nameRows.map((r) => String(r.name)));\n }\n }\n\n const insertVersionSql = pg\n ? `INSERT INTO ${table} VALUES (?) ON CONFLICT DO NOTHING`\n : `INSERT OR IGNORE INTO ${table} VALUES (?)`;\n const insertNamedSql = pg\n ? `INSERT INTO ${namedTable} (name, version) VALUES (?, ?) ON CONFLICT DO NOTHING`\n : `INSERT OR IGNORE INTO ${namedTable} (name, version) VALUES (?, ?)`;\n\n const pending = migrations.filter((m) =>\n m.name ? !appliedNames.has(m.name) : m.version > current,\n );\n if (pending.length > 0) {\n console.log(\n `[db] Applying ${pending.length} migration(s) on ${pg ? \"Postgres\" : \"SQLite/libsql\"}…`,\n );\n }\n\n for (const m of pending) {\n const raw = resolveMigrationSql(m.sql, pg);\n const label = m.name\n ? `\"${m.name}\" (v${m.version})`\n : `v${m.version}`;\n\n // Record BOTH the named row (always, when named) and the legacy\n // version row (only if this migration actually advances the legacy\n // MAX) — atomically with the DDL below. This keeps the legacy gate\n // monotonic for any unnamed migrations later in the list, while a\n // named migration is tracked by name regardless of version.\n const recordSql: Array<{ sql: string; args: unknown[] }> = [];\n if (m.name) {\n recordSql.push({\n sql: insertNamedSql,\n args: [m.name, m.version],\n });\n }\n if (m.version > current) {\n recordSql.push({ sql: insertVersionSql, args: [m.version] });\n }\n\n if (raw == null) {\n // Dialect-gated migration with no SQL for this dialect; still mark\n // as applied so we don't retry forever.\n for (const stmt of recordSql) await exec.execute(stmt);\n if (m.version > current) current = m.version;\n if (m.name) appliedNames.add(m.name);\n continue;\n }\n // Split BEFORE adapting so we can remember which original statements\n // carried `ADD COLUMN IF NOT EXISTS` — SQLite drops the clause, so we\n // emulate the idempotent semantic by swallowing duplicate-column\n // errors only for those statements.\n const originalStatements = splitSqlStatements(raw);\n const statements = originalStatements.map((orig) => ({\n sql: pg ? adaptSqlForPostgres(orig) : adaptSqlForSqlite(orig),\n hadIfNotExists: IF_NOT_EXISTS_ADD_COLUMN_RE.test(orig),\n }));\n let currentStmt = \"\";\n try {\n for (const { sql: stmt, hadIfNotExists } of statements) {\n currentStmt = stmt;\n try {\n await exec.execute(stmt);\n } catch (err) {\n if (!pg && hadIfNotExists && isDuplicateColumnError(err)) {\n // IF NOT EXISTS semantic: column already present, skip.\n continue;\n }\n throw err;\n }\n }\n for (const stmt of recordSql) await exec.execute(stmt);\n if (m.version > current) current = m.version;\n if (m.name) appliedNames.add(m.name);\n console.log(\n `[db] Applied migration ${label} (${statements.length} statement${statements.length === 1 ? \"\" : \"s\"})`,\n );\n } catch (err) {\n if (pg && isPermissionError(err)) {\n // The connected role lacks privilege for this migration (e.g. a\n // permission-limited dev/replica role that doesn't own the table).\n // Don't crash-loop the whole server over it — warn and STOP here.\n // We must NOT continue to later migrations: unnamed pending work is\n // computed as `version > MAX(recorded version)`, so applying a later\n // unnamed migration would advance MAX past this unrecorded one and\n // orphan it forever. Stopping leaves MAX at the last recorded\n // version, so a properly-privileged role resumes from this exact\n // migration, in order. (A named migration skipped here simply\n // isn't recorded by name either, so it's retried next boot same as\n // the legacy gate — no orphaning risk from name-based tracking.)\n console.warn(\n `[db] Migration ${label} skipped — insufficient privilege: ${(err as Error).message}. ` +\n `Apply it with a DB role that owns the table. ` +\n `Halting further migrations so this one isn't orphaned. ` +\n `Set <APP_NAME>_DATABASE_URL (e.g. PLAN_DATABASE_URL) to a database this app owns — a file: URL uses local SQLite.`,\n \"\\nStatement:\",\n currentStmt,\n );\n break;\n }\n console.error(\n `[db] Migration ${label} FAILED:`,\n (err as Error).message,\n \"\\nStatement:\",\n currentStmt,\n );\n throw err;\n }\n }\n } finally {\n // Release the direct-endpoint exec (Postgres only). For SQLite getDbExec()\n // returns the process-lifetime singleton, so releaseMigrationExec is a no-op\n // (refCount never incremented for SQLite path, guard in releaseMigrationExec).\n if (pg) await releaseMigrationExec();\n }\n } catch (err) {\n console.error(\"[db] Migration failed:\", (err as Error).message);\n // In local dev, hard-fail so the developer catches errors immediately.\n // On serverless runtimes (Netlify Functions, Vercel, CF Workers) we\n // keep the process alive — the app will return 500s for routes that\n // depend on the missing tables, but at least other routes still work.\n // Note: Node.js 21+ defines globalThis.navigator, so we check for\n // serverless env vars instead of navigator presence.\n const isServerless =\n !!globalThis.process?.env?.NETLIFY ||\n !!globalThis.process?.env?.AWS_LAMBDA_FUNCTION_NAME ||\n !!globalThis.process?.env?.VERCEL ||\n \"__cf_env\" in globalThis ||\n \"__env__\" in globalThis;\n if (typeof globalThis.process?.exit === \"function\" && !isServerless) {\n process.exit(1);\n }\n }\n };\n}\n"]}
1
+ {"version":3,"file":"migrations.js","sourceRoot":"","sources":["../../src/db/migrations.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n getDbExec,\n createDbExec,\n isPostgres,\n getDialect,\n getMigrationDatabaseUrl,\n getCloudflareD1Binding,\n retrySqliteBusy,\n type DbExec,\n} from \"./client.js\";\n\ninterface D1PreparedStatementLike {\n bind(...values: unknown[]): D1PreparedStatementLike;\n all(): Promise<{ results: Record<string, unknown>[] }>;\n first<T = Record<string, unknown>>(): Promise<T | null>;\n run(): Promise<unknown>;\n}\n\ninterface D1DatabaseLike {\n prepare(query: string): D1PreparedStatementLike;\n batch(statements: D1PreparedStatementLike[]): Promise<unknown>;\n}\n\n// ---------------------------------------------------------------------------\n// Shared direct-endpoint exec across concurrent migration runners per boot.\n//\n// On each serverless cold start THREE plugins call runMigrations (core,\n// org, context-xray). Without coordination each would open an independent\n// direct-endpoint Neon connection AND leave it idle for ~10 s after\n// migrations finish. We avoid that by sharing a single direct exec for\n// the duration of the migration window: the first caller opens it,\n// subsequent callers reuse the same promise, and the last caller closes it\n// via reference-counting. The exec is nulled out after the window so a\n// second cold start (e.g. after a Lambda thaw) gets a fresh connection.\n// ---------------------------------------------------------------------------\n\nlet _migrationExecPromise: Promise<DbExec> | null = null;\nlet _migrationExecRefCount = 0;\n\nasync function acquireMigrationExec(): Promise<DbExec> {\n if (!_migrationExecPromise) {\n const opened = createDbExec({ url: getMigrationDatabaseUrl() });\n _migrationExecPromise = opened;\n opened.catch(() => {\n // Opening the connection failed. Reset the shared state so the next\n // caller retries with a fresh connection instead of awaiting this\n // permanently rejected promise — and zero the ref count, because none\n // of the awaiters that failed here will ever call release.\n if (_migrationExecPromise === opened) {\n _migrationExecPromise = null;\n _migrationExecRefCount = 0;\n }\n });\n }\n _migrationExecRefCount++;\n return _migrationExecPromise;\n}\n\nasync function releaseMigrationExec(): Promise<void> {\n _migrationExecRefCount--;\n if (_migrationExecRefCount > 0) return;\n // Last caller — close and reset so next boot gets a fresh connection.\n const execPromise = _migrationExecPromise;\n _migrationExecPromise = null;\n _migrationExecRefCount = 0;\n if (!execPromise) return;\n try {\n const exec = await execPromise;\n await exec.close?.();\n } catch {\n // Swallow close errors — the migrations themselves already succeeded or\n // failed; a cleanup error should not surface as a boot crash.\n }\n}\n\ntype NitroPluginDef = (nitroApp: any) => void | Promise<void>;\n\n/**\n * Rewrite SQLite-specific SQL to Postgres-compatible equivalents.\n * Handles: datetime('now') → CURRENT_TIMESTAMP, AUTOINCREMENT → GENERATED, etc.\n */\nfunction adaptSqlForPostgres(sql: string): string {\n return sql\n .replace(/datetime\\s*\\(\\s*'now'\\s*\\)/gi, \"CURRENT_TIMESTAMP\")\n .replace(/\\bAUTOINCREMENT\\b/gi, \"\")\n .replace(/\\bINTEGER\\b/gi, \"BIGINT\");\n}\n\nconst IF_NOT_EXISTS_ADD_COLUMN_RE = /ADD\\s+COLUMN\\s+IF\\s+NOT\\s+EXISTS/i;\n\n/**\n * Strip Postgres-only syntax that SQLite doesn't support.\n * Handles: ALTER TABLE ... ADD COLUMN IF NOT EXISTS → ADD COLUMN\n *\n * Note: SQLite does not have a native equivalent, so the idempotent\n * semantic is emulated at the executor level by swallowing the\n * \"duplicate column name\" error for statements that originally carried\n * the clause. See `hadIfNotExists` tracking in the run loop.\n */\nfunction adaptSqlForSqlite(sql: string): string {\n return sql.replace(/ADD\\s+COLUMN\\s+IF\\s+NOT\\s+EXISTS/gi, \"ADD COLUMN\");\n}\n\n/**\n * True when an error from `ALTER TABLE ... ADD COLUMN` indicates the\n * column already existed. Recognizes both SQLite (\"duplicate column\n * name\") and Postgres (\"column ... already exists\" — exact text varies\n * by error code 42701, but the substring is stable). Exported so other\n * idempotent column-upgrade loops in the codebase don't reinvent this\n * regex with subtly different shapes.\n */\nexport function isDuplicateColumnError(err: unknown): boolean {\n const msg = (err as Error | undefined)?.message ?? \"\";\n return (\n /duplicate column name/i.test(msg) || /column .* already exists/i.test(msg)\n );\n}\n\n/**\n * True when a migration statement failed because the connected DB ROLE lacks\n * privilege — e.g. a permission-limited dev/replica role that doesn't own the\n * table. Postgres raises SQLSTATE 42501 (\"insufficient_privilege\", routine\n * aclcheck_error, message \"must be owner of table …\"). We treat these as\n * NON-FATAL so a perms-limited database can't crash-loop the whole server: the\n * migration is skipped (left unrecorded) and a properly-privileged role applies\n * it later. Production, where the role owns its tables, never hits this path.\n */\nexport function isPermissionError(err: unknown): boolean {\n const e = err as { code?: string; message?: string } | undefined;\n if (e?.code === \"42501\") return true;\n const msg = e?.message ?? \"\";\n return (\n /must be owner of/i.test(msg) ||\n /permission denied/i.test(msg) ||\n /insufficient privilege/i.test(msg)\n );\n}\n\n/**\n * Split a multi-statement SQL blob into individual statements.\n *\n * libsql's `execute(sql)` only runs the first statement in a multi-statement\n * string. This splitter is intentionally simple: it respects single-quoted\n * string literals (with `''` escaping) and `--` line comments, and splits on\n * top-level `;`. It does NOT attempt to parse `$$`-quoted Postgres function\n * bodies — migrations that define functions/triggers with `;` inside bodies\n * should pass a single-statement migration per entry instead.\n */\nfunction splitSqlStatements(sql: string): string[] {\n const out: string[] = [];\n let buf = \"\";\n let i = 0;\n let inSingle = false;\n while (i < sql.length) {\n const ch = sql[i];\n const next = sql[i + 1];\n if (!inSingle && ch === \"-\" && next === \"-\") {\n // Skip to end of line\n while (i < sql.length && sql[i] !== \"\\n\") i++;\n continue;\n }\n if (ch === \"'\") {\n buf += ch;\n if (inSingle && next === \"'\") {\n buf += next;\n i += 2;\n continue;\n }\n inSingle = !inSingle;\n i++;\n continue;\n }\n if (ch === \";\" && !inSingle) {\n const trimmed = buf.trim();\n if (trimmed) out.push(trimmed);\n buf = \"\";\n i++;\n continue;\n }\n buf += ch;\n i++;\n }\n const tail = buf.trim();\n if (tail) out.push(tail);\n return out;\n}\n\nexport interface RunMigrationsOptions {\n /**\n * Name of the migrations bookkeeping table. REQUIRED — there is intentionally\n * no default. Two templates that share a database (e.g. via the same Neon URL)\n * each have their own version space starting at v1, and a single shared\n * `_migrations` table will silently skip the second template's migrations if\n * the first has already advanced past those version numbers. This caused the\n * design template's migrations to be skipped entirely on a Neon DB that\n * slides had already populated up to v15 (PR #320 era).\n *\n * Use one bookkeeping table per template, e.g. `slides_migrations`. Core\n * feature plugins (e.g. the org module) follow the same convention with\n * their own prefix, e.g. `_org_migrations`.\n */\n table: string;\n}\n\n/**\n * A single migration entry.\n *\n * `sql` can be a string (runs on every dialect) or an object with dialect\n * keys for dialect-gated SQL. Useful when Postgres needs an ALTER that\n * SQLite can't parse.\n *\n * { version: 14, sql: { postgres: \"ALTER TABLE …\" } } // no-op on sqlite\n * { version: 15, sql: { sqlite: \"…\", postgres: \"…\" } } // both dialects\n *\n * `name` is an optional stable, unique slug that opts a migration into\n * **name-based tracking** instead of the legacy version-number gate. See the\n * \"Name-based tracking\" section on `runMigrations` for why this exists and\n * exactly how the two gating strategies interact.\n */\nexport type MigrationSql = string | { postgres?: string; sqlite?: string };\n\nexport interface MigrationEntry {\n version: number;\n sql: MigrationSql;\n /**\n * Stable, unique slug for this migration (e.g. `\"analytics-alert-rules-table\"`).\n * When present, this migration is tracked by NAME instead of by version\n * number — see the `runMigrations` doc comment for the full rationale and\n * gating rules. Must be unique across the migration list; a duplicate name\n * throws at startup (programmer error, not a runtime data problem).\n */\n name?: string;\n /**\n * JS step for a backfill SQL cannot express — e.g. a repair whose value comes\n * from application-level parsing of a stored blob. Runs BEFORE this entry's\n * SQL and before the bookkeeping row is written, so a throw leaves the\n * migration unrecorded and it retries on the next boot. Pass `sql: {}` for a\n * run-only entry. Give run-only entries a `name`: the legacy version gate\n * would otherwise mark them applied via any later migration's MAX advance.\n */\n run?: () => Promise<void>;\n}\n\nfunction resolveMigrationSql(sql: MigrationSql, pg: boolean): string | null {\n if (typeof sql === \"string\") return sql;\n const raw = pg ? sql.postgres : sql.sqlite;\n return raw ?? null;\n}\n\n/**\n * Runs a list of migrations against the configured database, gated by a\n * per-table bookkeeping row (`options.table`) so repeated boots only apply\n * new migrations.\n *\n * ## Name-based tracking (why it exists)\n *\n * The legacy gate is purely numeric: `SELECT MAX(version)` from the\n * bookkeeping table, then apply every entry whose `version` is greater. That\n * scheme silently breaks down when **two independent branches each extend the\n * same migration list with different DDL under the same version numbers** —\n * exactly what happened to the analytics template's v75-v83 range. Branch A\n * shipped alert-rule tables as v75-v78; branch B shipped unrelated DDL as its\n * own v75-v83. Whichever branch deployed first recorded \"v75..v83 applied\"\n * in the bookkeeping table. When the other branch merged and deployed, its\n * migrations at those same version numbers were never applied — `MAX(version)`\n * was already ≥ their version, so the gate treated them as done even though\n * their actual DDL had never run. The result: `analytics_migrations` showed\n * rows for 1..83 with no gaps, yet `analytics_alert_rules`,\n * `analytics_alert_incidents`, and `session_recordings.network_error_count`\n * did not exist in production. Version numbers are not a stable identity —\n * they're just sequence position, and sequence position collides across\n * branches.\n *\n * Name-based tracking fixes this by keying application on a stable, unique\n * **string slug** instead of a position in a shared integer sequence:\n *\n * - Entries with a `name` are recorded in a companion table,\n * `${table}_named` (`name TEXT PRIMARY KEY`), and APPLY IFF their name is\n * absent from that table — completely independent of version numbers or\n * the legacy `MAX(version)` gate. Two branches can both ship a migration\n * named `\"analytics-alert-rules-table\"` at version 75, or one at version 75\n * and the other at version 90 after a rebase — either way it applies\n * exactly once per database, keyed on the name.\n * - Entries WITHOUT a `name` keep the exact legacy behavior\n * (`version > MAX(recorded version)`), so nothing changes for existing\n * unnamed migrations.\n * - Named migrations still execute in list order, interleaved with unnamed\n * ones exactly as written.\n * - When a named migration's DDL runs, we record BOTH the named row (always)\n * and — if its `version` is greater than the current legacy max — the\n * legacy version row too, in the SAME atomic batch/transaction as the DDL.\n * This keeps the legacy `MAX(version)` gate monotonically advancing for\n * any unnamed migrations that come after it in the list, while ensuring a\n * named migration is never \"double recorded\" in a way that would let it\n * re-apply.\n * - A duplicate `name` across the migration list throws at startup — that's\n * a programmer error (copy-paste or merge mistake), not a runtime data\n * problem, and failing loud beats silently tracking the wrong row.\n *\n * New migrations should always set a `name`. Legacy unnamed migrations don't\n * need to be renamed retroactively — the two gating strategies coexist in the\n * same list — but if a table is EVER at risk of being shared across parallel\n * branches (any template's own migrations qualify, since branches routinely\n * extend the same list concurrently), giving new entries a name is what makes\n * them immune to the collision class described above.\n */\nexport function runMigrations(\n migrations: Array<MigrationEntry>,\n options: RunMigrationsOptions,\n): NitroPluginDef {\n const table = options?.table;\n if (\n !table ||\n typeof table !== \"string\" ||\n !/^[A-Za-z_][A-Za-z0-9_]*$/.test(table)\n ) {\n throw new Error(\n \"runMigrations: `table` option is required and must be a valid SQL identifier \" +\n '(e.g. `{ table: \"slides_migrations\" }`). See packages/core/src/db/migrations.ts ' +\n \"for why this is required (shared-DB version-collision bug).\",\n );\n }\n\n // Duplicate-name detection — programmer error, fail loud at startup rather\n // than silently tracking the wrong migration's applied state.\n {\n const seenNames = new Set<string>();\n for (const m of migrations) {\n if (!m.name) continue;\n if (seenNames.has(m.name)) {\n throw new Error(\n `runMigrations: duplicate migration name \"${m.name}\" in the migration list for table \"${table}\". ` +\n \"Migration names must be unique — pick a different stable slug.\",\n );\n }\n seenNames.add(m.name);\n }\n }\n\n const namedTable = `${table}_named`;\n\n return async () => {\n try {\n // Check for Cloudflare D1 binding (only if DATABASE_URL not set)\n const d1 =\n getDialect() === \"d1\"\n ? (getCloudflareD1Binding() as D1DatabaseLike | undefined)\n : null;\n if (d1) {\n await d1\n .prepare(\n `CREATE TABLE IF NOT EXISTS ${table} (version INTEGER PRIMARY KEY)`,\n )\n .run();\n await d1\n .prepare(\n `CREATE TABLE IF NOT EXISTS ${namedTable} (name TEXT PRIMARY KEY, version INTEGER, applied_at TEXT NOT NULL DEFAULT (datetime('now')))`,\n )\n .run();\n const firstRow = await d1\n .prepare(`SELECT MAX(version) as v FROM ${table}`)\n .first<{ v?: number }>();\n const current = (firstRow?.v as number) ?? 0;\n\n const appliedNamesRows = await d1\n .prepare(`SELECT name FROM ${namedTable}`)\n .all();\n const appliedNames = new Set(\n (appliedNamesRows?.results ?? []).map((r) => String(r.name)),\n );\n\n const pending = migrations.filter((m) =>\n m.name ? !appliedNames.has(m.name) : m.version > current,\n );\n\n for (const m of pending) {\n try {\n // D1 is SQLite-compatible\n const raw = resolveMigrationSql(m.sql, false);\n if (m.run) await m.run();\n const recordStatements = [\n m.name\n ? d1\n .prepare(\n `INSERT OR IGNORE INTO ${namedTable} (name, version) VALUES (?, ?)`,\n )\n .bind(m.name, m.version)\n : null,\n m.version > current\n ? d1\n .prepare(`INSERT OR IGNORE INTO ${table} VALUES (?)`)\n .bind(m.version)\n : null,\n ].filter((s): s is NonNullable<typeof s> => s != null);\n\n if (raw == null) {\n // Dialect-gated migration with no SQL for this dialect; still\n // record it so we don't retry forever. Keep the name + legacy\n // version rows atomic: if an isolate is interrupted between\n // separate writes, the name row would suppress every retry even\n // though the legacy bookkeeping row never landed.\n if (recordStatements.length > 0) {\n await d1.batch(recordStatements);\n }\n continue;\n }\n const originalStatements = splitSqlStatements(raw);\n const statements = originalStatements.map((orig) => ({\n sql: adaptSqlForSqlite(orig),\n hadIfNotExists: IF_NOT_EXISTS_ADD_COLUMN_RE.test(orig),\n }));\n const hasIfNotExists = statements.some((s) => s.hadIfNotExists);\n if (hasIfNotExists) {\n // Per-statement path: we need to swallow \"duplicate column\"\n // errors for statements that originally carried\n // `ADD COLUMN IF NOT EXISTS`, which a batch() can't express.\n // Loses atomicity, but the idempotent-ADD-COLUMN semantic\n // means a partial re-run resolves cleanly on retry.\n for (const { sql: stmt, hadIfNotExists } of statements) {\n try {\n await d1.prepare(stmt).run();\n } catch (err) {\n if (hadIfNotExists && isDuplicateColumnError(err)) continue;\n throw err;\n }\n }\n for (const stmt of recordStatements) await stmt.run();\n } else {\n // Atomic batch: all statements + bookkeeping inserts land in\n // the same transaction. A failing statement rolls the whole\n // migration back, so we never record a half-applied migration.\n await d1.batch([\n ...statements.map((s) => d1.prepare(s.sql)),\n ...recordStatements,\n ]);\n }\n console.log(\n `[db] Applied migration ${m.name ? `\"${m.name}\" ` : \"\"}v${m.version} (${statements.length} statement${statements.length === 1 ? \"\" : \"s\"})`,\n );\n } catch (err) {\n console.error(\n `[db] Migration ${m.name ? `\"${m.name}\" ` : \"\"}v${m.version} FAILED:`,\n (err as Error).message,\n \"\\nSQL:\",\n JSON.stringify(m.sql),\n );\n throw err;\n }\n }\n return;\n }\n\n // Generic path — works for libsql and Postgres\n const pg = isPostgres();\n\n // ---------------------------------------------------------------------------\n // Fast-path: read migration state through the regular pooled singleton before\n // opening the direct-endpoint connection.\n //\n // On Postgres every cold start previously opened a fresh direct-endpoint Neon\n // connection (bypassing PgBouncer, which is needed only for DDL), ran\n // CREATE TABLE IF NOT EXISTS + SELECT MAX(version) even with zero pending\n // migrations, and never closed the pool — it idled for ~10 s. Three runners\n // (core, org, context-xray) did this independently per boot.\n //\n // Now: use the pooled singleton (getDbExec) for the bookkeeping SELECT. If\n // the migrations table does not yet exist we treat it as \"all migrations\n // pending\" (current = -1). Only when there are pending migrations do we open\n // the direct-endpoint exec (DDL is the only thing Neon's PgBouncer blocks —\n // documented at getMigrationDatabaseUrl). The direct exec is shared across\n // concurrent runners via acquireMigrationExec() and closed after the last\n // caller via releaseMigrationExec().\n // ---------------------------------------------------------------------------\n\n let current = -1; // sentinel: \"table missing\" → treat all as pending\n let namedRowsMissing = false; // sentinel: \"named table missing\" → no names applied yet\n\n // Any migration with a `name` is a candidate regardless of version, so\n // the fast-path \"anything pending?\" check must also account for names.\n const hasNamedMigrations = migrations.some((m) => m.name);\n let appliedNames = new Set<string>();\n\n if (pg) {\n try {\n const { rows } = await getDbExec().execute(\n `SELECT MAX(version) as v FROM ${table}`,\n );\n current = (rows[0]?.v as number) ?? 0;\n } catch {\n // Table doesn't exist yet — leave current = -1 so all migrations apply.\n }\n if (hasNamedMigrations) {\n try {\n const { rows } = await getDbExec().execute(\n `SELECT name FROM ${namedTable}`,\n );\n appliedNames = new Set(rows.map((r) => String(r.name)));\n } catch {\n // Named table doesn't exist yet — leave appliedNames empty so all\n // named migrations apply.\n namedRowsMissing = true;\n }\n }\n }\n\n // For SQLite we still use getDbExec() as exec throughout (no pooler concern).\n // For Postgres we only open the direct exec when there are pending migrations.\n const pendingFast = pg\n ? migrations.filter((m) =>\n m.name ? !appliedNames.has(m.name) : m.version > current,\n )\n : null; // SQLite: compute after table creation below\n\n // Short-circuit: Postgres with nothing to do — skip the direct connection entirely.\n if (pg && pendingFast !== null && pendingFast.length === 0) {\n return;\n }\n\n // Acquire the exec appropriate for the dialect.\n // For Postgres: the shared direct-endpoint exec (DDL-safe, closed on release).\n // For SQLite/libsql: the singleton pooled exec (no pooler concern).\n const exec = pg ? await acquireMigrationExec() : getDbExec();\n\n try {\n // Retry initial table creation — SQLITE_BUSY_RECOVERY can occur on HMR\n // restarts when WAL files from the previous process haven't been released yet.\n await retrySqliteBusy(\n () =>\n exec.execute(\n `CREATE TABLE IF NOT EXISTS ${table} (version INTEGER PRIMARY KEY)`,\n ),\n { maxAttempts: 6, baseDelayMs: 1000, rethrow: true },\n );\n // Companion name-keyed bookkeeping table — never alters the existing\n // `${table}`'s PRIMARY KEY, so legacy version rows keep working exactly\n // as before. See the `runMigrations` doc comment for why this exists.\n await retrySqliteBusy(\n () =>\n exec.execute(\n `CREATE TABLE IF NOT EXISTS ${namedTable} (name TEXT PRIMARY KEY, version INTEGER, applied_at ${pg ? \"TIMESTAMP NOT NULL DEFAULT now()\" : \"TEXT NOT NULL DEFAULT (datetime('now'))\"})`,\n ),\n { maxAttempts: 6, baseDelayMs: 1000, rethrow: true },\n );\n\n // For Postgres, current was already set by the fast-path SELECT above.\n // For SQLite we run the SELECT now (via the same exec, which is the singleton).\n if (!pg) {\n const { rows } = await exec.execute(\n `SELECT MAX(version) as v FROM ${table}`,\n );\n current = (rows[0]?.v as number) ?? 0;\n if (hasNamedMigrations) {\n const { rows: nameRows } = await exec.execute(\n `SELECT name FROM ${namedTable}`,\n );\n appliedNames = new Set(nameRows.map((r) => String(r.name)));\n }\n } else if (current === -1 || (hasNamedMigrations && namedRowsMissing)) {\n // Fast-path read failed (table was absent on the pooler): re-read via the\n // direct exec now that CREATE TABLE IF NOT EXISTS has ensured it exists.\n if (current === -1) {\n const { rows } = await exec.execute(\n `SELECT MAX(version) as v FROM ${table}`,\n );\n current = (rows[0]?.v as number) ?? 0;\n }\n if (hasNamedMigrations && namedRowsMissing) {\n const { rows: nameRows } = await exec.execute(\n `SELECT name FROM ${namedTable}`,\n );\n appliedNames = new Set(nameRows.map((r) => String(r.name)));\n }\n }\n\n const insertVersionSql = pg\n ? `INSERT INTO ${table} VALUES (?) ON CONFLICT DO NOTHING`\n : `INSERT OR IGNORE INTO ${table} VALUES (?)`;\n const insertNamedSql = pg\n ? `INSERT INTO ${namedTable} (name, version) VALUES (?, ?) ON CONFLICT DO NOTHING`\n : `INSERT OR IGNORE INTO ${namedTable} (name, version) VALUES (?, ?)`;\n\n const pending = migrations.filter((m) =>\n m.name ? !appliedNames.has(m.name) : m.version > current,\n );\n if (pending.length > 0) {\n console.log(\n `[db] Applying ${pending.length} migration(s) on ${pg ? \"Postgres\" : \"SQLite/libsql\"}…`,\n );\n }\n\n for (const m of pending) {\n const raw = resolveMigrationSql(m.sql, pg);\n const label = m.name\n ? `\"${m.name}\" (v${m.version})`\n : `v${m.version}`;\n\n // Record BOTH the named row (always, when named) and the legacy\n // version row (only if this migration actually advances the legacy\n // MAX) — atomically with the DDL below. This keeps the legacy gate\n // monotonic for any unnamed migrations later in the list, while a\n // named migration is tracked by name regardless of version.\n const recordSql: Array<{ sql: string; args: unknown[] }> = [];\n if (m.name) {\n recordSql.push({\n sql: insertNamedSql,\n args: [m.name, m.version],\n });\n }\n if (m.version > current) {\n recordSql.push({ sql: insertVersionSql, args: [m.version] });\n }\n\n // A throw here escapes to the outer handler with nothing recorded,\n // so the entry is retried on the next boot rather than being marked\n // applied against work that never happened.\n if (m.run) await m.run();\n\n if (raw == null) {\n // Dialect-gated migration with no SQL for this dialect; still mark\n // as applied so we don't retry forever.\n for (const stmt of recordSql) await exec.execute(stmt);\n if (m.version > current) current = m.version;\n if (m.name) appliedNames.add(m.name);\n continue;\n }\n // Split BEFORE adapting so we can remember which original statements\n // carried `ADD COLUMN IF NOT EXISTS` — SQLite drops the clause, so we\n // emulate the idempotent semantic by swallowing duplicate-column\n // errors only for those statements.\n const originalStatements = splitSqlStatements(raw);\n const statements = originalStatements.map((orig) => ({\n sql: pg ? adaptSqlForPostgres(orig) : adaptSqlForSqlite(orig),\n hadIfNotExists: IF_NOT_EXISTS_ADD_COLUMN_RE.test(orig),\n }));\n let currentStmt = \"\";\n try {\n for (const { sql: stmt, hadIfNotExists } of statements) {\n currentStmt = stmt;\n try {\n await exec.execute(stmt);\n } catch (err) {\n if (!pg && hadIfNotExists && isDuplicateColumnError(err)) {\n // IF NOT EXISTS semantic: column already present, skip.\n continue;\n }\n throw err;\n }\n }\n for (const stmt of recordSql) await exec.execute(stmt);\n if (m.version > current) current = m.version;\n if (m.name) appliedNames.add(m.name);\n console.log(\n `[db] Applied migration ${label} (${statements.length} statement${statements.length === 1 ? \"\" : \"s\"})`,\n );\n } catch (err) {\n if (pg && isPermissionError(err)) {\n // The connected role lacks privilege for this migration (e.g. a\n // permission-limited dev/replica role that doesn't own the table).\n // Don't crash-loop the whole server over it — warn and STOP here.\n // We must NOT continue to later migrations: unnamed pending work is\n // computed as `version > MAX(recorded version)`, so applying a later\n // unnamed migration would advance MAX past this unrecorded one and\n // orphan it forever. Stopping leaves MAX at the last recorded\n // version, so a properly-privileged role resumes from this exact\n // migration, in order. (A named migration skipped here simply\n // isn't recorded by name either, so it's retried next boot same as\n // the legacy gate — no orphaning risk from name-based tracking.)\n console.warn(\n `[db] Migration ${label} skipped — insufficient privilege: ${(err as Error).message}. ` +\n `Apply it with a DB role that owns the table. ` +\n `Halting further migrations so this one isn't orphaned. ` +\n `Set <APP_NAME>_DATABASE_URL (e.g. PLAN_DATABASE_URL) to a database this app owns — a file: URL uses local SQLite.`,\n \"\\nStatement:\",\n currentStmt,\n );\n break;\n }\n console.error(\n `[db] Migration ${label} FAILED:`,\n (err as Error).message,\n \"\\nStatement:\",\n currentStmt,\n );\n throw err;\n }\n }\n } finally {\n // Release the direct-endpoint exec (Postgres only). For SQLite getDbExec()\n // returns the process-lifetime singleton, so releaseMigrationExec is a no-op\n // (refCount never incremented for SQLite path, guard in releaseMigrationExec).\n if (pg) await releaseMigrationExec();\n }\n } catch (err) {\n console.error(\"[db] Migration failed:\", (err as Error).message);\n // In local dev, hard-fail so the developer catches errors immediately.\n // On serverless runtimes (Netlify Functions, Vercel, CF Workers) we\n // keep the process alive — the app will return 500s for routes that\n // depend on the missing tables, but at least other routes still work.\n // Note: Node.js 21+ defines globalThis.navigator, so we check for\n // serverless env vars instead of navigator presence.\n const isServerless =\n !!globalThis.process?.env?.NETLIFY ||\n !!globalThis.process?.env?.AWS_LAMBDA_FUNCTION_NAME ||\n !!globalThis.process?.env?.VERCEL ||\n \"__cf_env\" in globalThis ||\n \"__env__\" in globalThis;\n if (typeof globalThis.process?.exit === \"function\" && !isServerless) {\n process.exit(1);\n }\n }\n };\n}\n"]}
@@ -17,12 +17,12 @@ declare const _default: import("../../action.js").ActionDefinition<{
17
17
  id?: undefined;
18
18
  provider?: undefined;
19
19
  } | {
20
+ error?: undefined;
20
21
  configured?: undefined;
21
22
  connectPath?: undefined;
22
23
  url: string;
23
24
  id: string;
24
25
  provider: string;
25
- error?: undefined;
26
26
  }>;
27
27
  export default _default;
28
28
  //# sourceMappingURL=upload-image.d.ts.map
@@ -0,0 +1,16 @@
1
+ export interface McpApprovalGrant {
2
+ nonce: string;
3
+ callerKey: string;
4
+ actionName: string;
5
+ argumentsHash: string;
6
+ expiresAt: number;
7
+ }
8
+ export declare function createMcpApprovalGrant(grant: McpApprovalGrant): Promise<void>;
9
+ /**
10
+ * Atomically consume an exact grant. The UPDATE predicate is the security
11
+ * boundary: only one hosted instance can move a matching, unexpired row from
12
+ * pending to consumed, so an accepted response is at-most-once even when the
13
+ * same signed requestState is replayed concurrently.
14
+ */
15
+ export declare function consumeMcpApprovalGrant(grant: McpApprovalGrant): Promise<boolean>;
16
+ //# sourceMappingURL=approval-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approval-store.d.ts","sourceRoot":"","sources":["../../src/mcp/approval-store.ts"],"names":[],"mappings":"AA+BA,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,gBAAgB,GACtB,OAAO,CAAC,IAAI,CAAC,CAyBf;AAED;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,gBAAgB,GACtB,OAAO,CAAC,OAAO,CAAC,CAsBlB"}
@@ -0,0 +1,85 @@
1
+ import { getDbExec, intType, isPostgres } from "../db/client.js";
2
+ import { ensureTableExists } from "../db/ddl-guard.js";
3
+ let initPromise;
4
+ async function ensureApprovalTable() {
5
+ if (!initPromise) {
6
+ initPromise = (async () => {
7
+ const createSql = `
8
+ CREATE TABLE IF NOT EXISTS mcp_action_approvals (
9
+ nonce TEXT PRIMARY KEY,
10
+ caller_key TEXT NOT NULL,
11
+ action_name TEXT NOT NULL,
12
+ arguments_hash TEXT NOT NULL,
13
+ expires_at ${intType()} NOT NULL,
14
+ consumed_at ${intType()}
15
+ )
16
+ `;
17
+ if (isPostgres()) {
18
+ await ensureTableExists("mcp_action_approvals", createSql);
19
+ }
20
+ else {
21
+ await getDbExec().execute(createSql);
22
+ }
23
+ })().catch((error) => {
24
+ initPromise = undefined;
25
+ throw error;
26
+ });
27
+ }
28
+ return initPromise;
29
+ }
30
+ export async function createMcpApprovalGrant(grant) {
31
+ await ensureApprovalTable();
32
+ const client = getDbExec();
33
+ await client.execute({
34
+ sql: `INSERT INTO mcp_action_approvals (nonce, caller_key, action_name, arguments_hash, expires_at, consumed_at) VALUES (?, ?, ?, ?, ?, NULL)`,
35
+ args: [
36
+ grant.nonce,
37
+ grant.callerKey,
38
+ grant.actionName,
39
+ grant.argumentsHash,
40
+ grant.expiresAt,
41
+ ],
42
+ });
43
+ // This is storage hygiene only. A failed cleanup must never turn a failed
44
+ // grant insert/consume into success, and expired rows remain unusable because
45
+ // consumeMcpApprovalGrant checks expires_at in the atomic update below.
46
+ try {
47
+ await client.execute({
48
+ sql: `DELETE FROM mcp_action_approvals WHERE expires_at < ?`,
49
+ args: [Date.now() - 24 * 60 * 60_000],
50
+ });
51
+ }
52
+ catch (error) {
53
+ console.warn("[mcp] Could not clean expired action approval grants", error);
54
+ }
55
+ }
56
+ /**
57
+ * Atomically consume an exact grant. The UPDATE predicate is the security
58
+ * boundary: only one hosted instance can move a matching, unexpired row from
59
+ * pending to consumed, so an accepted response is at-most-once even when the
60
+ * same signed requestState is replayed concurrently.
61
+ */
62
+ export async function consumeMcpApprovalGrant(grant) {
63
+ await ensureApprovalTable();
64
+ const now = Date.now();
65
+ const result = await getDbExec().execute({
66
+ sql: `UPDATE mcp_action_approvals
67
+ SET consumed_at = ?
68
+ WHERE nonce = ?
69
+ AND caller_key = ?
70
+ AND action_name = ?
71
+ AND arguments_hash = ?
72
+ AND consumed_at IS NULL
73
+ AND expires_at >= ?`,
74
+ args: [
75
+ now,
76
+ grant.nonce,
77
+ grant.callerKey,
78
+ grant.actionName,
79
+ grant.argumentsHash,
80
+ now,
81
+ ],
82
+ });
83
+ return result.rowsAffected === 1;
84
+ }
85
+ //# sourceMappingURL=approval-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approval-store.js","sourceRoot":"","sources":["../../src/mcp/approval-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAEvD,IAAI,WAAsC,CAAC;AAE3C,KAAK,UAAU,mBAAmB;IAChC,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,WAAW,GAAG,CAAC,KAAK,IAAI,EAAE;YACxB,MAAM,SAAS,GAAG;;;;;;uBAMD,OAAO,EAAE;wBACR,OAAO,EAAE;;OAE1B,CAAC;YACF,IAAI,UAAU,EAAE,EAAE,CAAC;gBACjB,MAAM,iBAAiB,CAAC,sBAAsB,EAAE,SAAS,CAAC,CAAC;YAC7D,CAAC;iBAAM,CAAC;gBACN,MAAM,SAAS,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YACvC,CAAC;QACH,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;YACnB,WAAW,GAAG,SAAS,CAAC;YACxB,MAAM,KAAK,CAAC;QACd,CAAC,CAAC,CAAC;IACL,CAAC;IACD,OAAO,WAAW,CAAC;AACrB,CAAC;AAUD,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,KAAuB;IAEvB,MAAM,mBAAmB,EAAE,CAAC;IAC5B,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,MAAM,CAAC,OAAO,CAAC;QACnB,GAAG,EAAE,yIAAyI;QAC9I,IAAI,EAAE;YACJ,KAAK,CAAC,KAAK;YACX,KAAK,CAAC,SAAS;YACf,KAAK,CAAC,UAAU;YAChB,KAAK,CAAC,aAAa;YACnB,KAAK,CAAC,SAAS;SAChB;KACF,CAAC,CAAC;IAEH,0EAA0E;IAC1E,8EAA8E;IAC9E,wEAAwE;IACxE,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,OAAO,CAAC;YACnB,GAAG,EAAE,uDAAuD;YAC5D,IAAI,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,MAAM,CAAC;SACtC,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,CAAC,sDAAsD,EAAE,KAAK,CAAC,CAAC;IAC9E,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,KAAuB;IAEvB,MAAM,mBAAmB,EAAE,CAAC;IAC5B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC,OAAO,CAAC;QACvC,GAAG,EAAE;;;;;;;4BAOmB;QACxB,IAAI,EAAE;YACJ,GAAG;YACH,KAAK,CAAC,KAAK;YACX,KAAK,CAAC,SAAS;YACf,KAAK,CAAC,UAAU;YAChB,KAAK,CAAC,aAAa;YACnB,GAAG;SACJ;KACF,CAAC,CAAC;IACH,OAAO,MAAM,CAAC,YAAY,KAAK,CAAC,CAAC;AACnC,CAAC","sourcesContent":["import { getDbExec, intType, isPostgres } from \"../db/client.js\";\nimport { ensureTableExists } from \"../db/ddl-guard.js\";\n\nlet initPromise: Promise<void> | undefined;\n\nasync function ensureApprovalTable(): Promise<void> {\n if (!initPromise) {\n initPromise = (async () => {\n const createSql = `\n CREATE TABLE IF NOT EXISTS mcp_action_approvals (\n nonce TEXT PRIMARY KEY,\n caller_key TEXT NOT NULL,\n action_name TEXT NOT NULL,\n arguments_hash TEXT NOT NULL,\n expires_at ${intType()} NOT NULL,\n consumed_at ${intType()}\n )\n `;\n if (isPostgres()) {\n await ensureTableExists(\"mcp_action_approvals\", createSql);\n } else {\n await getDbExec().execute(createSql);\n }\n })().catch((error) => {\n initPromise = undefined;\n throw error;\n });\n }\n return initPromise;\n}\n\nexport interface McpApprovalGrant {\n nonce: string;\n callerKey: string;\n actionName: string;\n argumentsHash: string;\n expiresAt: number;\n}\n\nexport async function createMcpApprovalGrant(\n grant: McpApprovalGrant,\n): Promise<void> {\n await ensureApprovalTable();\n const client = getDbExec();\n await client.execute({\n sql: `INSERT INTO mcp_action_approvals (nonce, caller_key, action_name, arguments_hash, expires_at, consumed_at) VALUES (?, ?, ?, ?, ?, NULL)`,\n args: [\n grant.nonce,\n grant.callerKey,\n grant.actionName,\n grant.argumentsHash,\n grant.expiresAt,\n ],\n });\n\n // This is storage hygiene only. A failed cleanup must never turn a failed\n // grant insert/consume into success, and expired rows remain unusable because\n // consumeMcpApprovalGrant checks expires_at in the atomic update below.\n try {\n await client.execute({\n sql: `DELETE FROM mcp_action_approvals WHERE expires_at < ?`,\n args: [Date.now() - 24 * 60 * 60_000],\n });\n } catch (error) {\n console.warn(\"[mcp] Could not clean expired action approval grants\", error);\n }\n}\n\n/**\n * Atomically consume an exact grant. The UPDATE predicate is the security\n * boundary: only one hosted instance can move a matching, unexpired row from\n * pending to consumed, so an accepted response is at-most-once even when the\n * same signed requestState is replayed concurrently.\n */\nexport async function consumeMcpApprovalGrant(\n grant: McpApprovalGrant,\n): Promise<boolean> {\n await ensureApprovalTable();\n const now = Date.now();\n const result = await getDbExec().execute({\n sql: `UPDATE mcp_action_approvals\n SET consumed_at = ?\n WHERE nonce = ?\n AND caller_key = ?\n AND action_name = ?\n AND arguments_hash = ?\n AND consumed_at IS NULL\n AND expires_at >= ?`,\n args: [\n now,\n grant.nonce,\n grant.callerKey,\n grant.actionName,\n grant.argumentsHash,\n now,\n ],\n });\n return result.rowsAffected === 1;\n}\n"]}
@@ -196,40 +196,7 @@ export declare function buildLinkArtifacts(entry: ActionEntry, args: Record<stri
196
196
  * `requestMeta`; the stdio standalone path passes the resolved local app
197
197
  * origin so deep links still become absolute URLs.
198
198
  */
199
- export declare function createMCPServerForRequest(config: MCPConfig, identity: MCPCallerIdentity | undefined, requestMeta?: MCPRequestMeta): Promise<import("@modelcontextprotocol/sdk/server").Server<{
200
- method: string;
201
- params?: {
202
- [x: string]: unknown;
203
- _meta?: {
204
- [x: string]: unknown;
205
- progressToken?: string | number;
206
- "io.modelcontextprotocol/related-task"?: {
207
- taskId: string;
208
- };
209
- };
210
- };
211
- }, {
212
- method: string;
213
- params?: {
214
- [x: string]: unknown;
215
- _meta?: {
216
- [x: string]: unknown;
217
- progressToken?: string | number;
218
- "io.modelcontextprotocol/related-task"?: {
219
- taskId: string;
220
- };
221
- };
222
- };
223
- }, {
224
- [x: string]: unknown;
225
- _meta?: {
226
- [x: string]: unknown;
227
- progressToken?: string | number;
228
- "io.modelcontextprotocol/related-task"?: {
229
- taskId: string;
230
- };
231
- };
232
- }>>;
199
+ export declare function createMCPServerForRequest(config: MCPConfig, identity: MCPCallerIdentity | undefined, requestMeta?: MCPRequestMeta): Promise<import("@modelcontextprotocol/server").Server>;
233
200
  export declare function getAccessTokens(): string[];
234
201
  export declare function getBearerToken(authHeader: string | undefined): string | undefined;
235
202
  /**