forgeos 0.1.0-alpha.4 → 0.1.0-alpha.41

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 (416) hide show
  1. package/.npmignore +10 -0
  2. package/AGENTS.md +142 -90
  3. package/CHANGELOG.md +433 -0
  4. package/LICENSE +21 -0
  5. package/README.md +67 -8
  6. package/adapters/go/README.md +23 -0
  7. package/adapters/go/go.mod +3 -0
  8. package/adapters/go/http.go +149 -0
  9. package/adapters/go/registry.go +234 -0
  10. package/adapters/go/types.go +136 -0
  11. package/adapters/java/README.md +68 -0
  12. package/adapters/java/pom.xml +34 -0
  13. package/adapters/java/src/main/java/dev/forgeos/adapter/Auth.java +20 -0
  14. package/adapters/java/src/main/java/dev/forgeos/adapter/Diagnostic.java +16 -0
  15. package/adapters/java/src/main/java/dev/forgeos/adapter/Entry.java +38 -0
  16. package/adapters/java/src/main/java/dev/forgeos/adapter/EntryKind.java +16 -0
  17. package/adapters/java/src/main/java/dev/forgeos/adapter/ErrorInfo.java +4 -0
  18. package/adapters/java/src/main/java/dev/forgeos/adapter/Forge.java +94 -0
  19. package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeCall.java +12 -0
  20. package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeContext.java +11 -0
  21. package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeHandler.java +8 -0
  22. package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeHttpHandler.java +179 -0
  23. package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeRegistry.java +121 -0
  24. package/adapters/java/src/main/java/dev/forgeos/adapter/Json.java +14 -0
  25. package/adapters/java/src/main/java/dev/forgeos/adapter/Manifest.java +14 -0
  26. package/adapters/java/src/main/java/dev/forgeos/adapter/RequestEnvelope.java +6 -0
  27. package/adapters/java/src/main/java/dev/forgeos/adapter/ResponseEnvelope.java +25 -0
  28. package/adapters/java/src/main/java/dev/forgeos/adapter/Risk.java +18 -0
  29. package/adapters/java/src/main/java/dev/forgeos/adapter/Schemas.java +36 -0
  30. package/adapters/java/src/main/java/dev/forgeos/adapter/Service.java +65 -0
  31. package/adapters/java/src/main/java/dev/forgeos/adapter/TransactionMode.java +18 -0
  32. package/adapters/java/src/main/java/dev/forgeos/adapter/TypedForgeHandler.java +6 -0
  33. package/adapters/java-spring-boot-starter/README.md +32 -0
  34. package/adapters/java-spring-boot-starter/pom.xml +36 -0
  35. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeCommand.java +22 -0
  36. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeExternalService.java +15 -0
  37. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeQuery.java +16 -0
  38. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeServiceBeanCondition.java +18 -0
  39. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeSpringAutoConfiguration.java +16 -0
  40. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeSpringRuntime.java +104 -0
  41. package/adapters/java-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +1 -0
  42. package/bin/forge.mjs +18 -0
  43. package/docs/cair-protocol.md +103 -0
  44. package/docs/changelog.md +548 -0
  45. package/docs/forge-protocol.md +189 -0
  46. package/examples/go-billing/go.mod +7 -0
  47. package/examples/go-billing/main.go +120 -0
  48. package/examples/java-billing/pom.xml +52 -0
  49. package/examples/java-billing/src/main/java/dev/forgeos/examples/billing/CreateInvoiceInput.java +4 -0
  50. package/examples/java-billing/src/main/java/dev/forgeos/examples/billing/Invoice.java +11 -0
  51. package/examples/java-billing/src/main/java/dev/forgeos/examples/billing/Main.java +127 -0
  52. package/package.json +31 -3
  53. package/schemas/forge-manifest.schema.json +57 -0
  54. package/src/forge/_generated/releaseManifest.json +1 -2
  55. package/src/forge/_generated/releaseManifest.ts +3 -3
  56. package/src/forge/agent-adapters/index.ts +1584 -125
  57. package/src/forge/agent-adapters/types.ts +224 -1
  58. package/src/forge/agent-memory/bridge.ts +1333 -0
  59. package/src/forge/agent-memory/context-pack.ts +277 -0
  60. package/src/forge/agent-memory/hook-runner.ts +312 -0
  61. package/src/forge/agent-memory/mcp.ts +224 -0
  62. package/src/forge/agent-memory/normalize.ts +498 -0
  63. package/src/forge/agent-memory/redaction.ts +103 -0
  64. package/src/forge/agent-memory/sources/claude-code.ts +51 -0
  65. package/src/forge/agent-memory/sources/codex-hook-runner.mjs +273 -0
  66. package/src/forge/agent-memory/sources/codex.ts +119 -0
  67. package/src/forge/agent-memory/sources/cursor.ts +35 -0
  68. package/src/forge/agent-memory/types.ts +204 -0
  69. package/src/forge/bench.ts +248 -0
  70. package/src/forge/brownfield-import/index.ts +801 -0
  71. package/src/forge/brownfield-import/types.ts +127 -0
  72. package/src/forge/cair/action-journal.ts +61 -0
  73. package/src/forge/cair/action-parser.ts +314 -0
  74. package/src/forge/cair/action-validator.ts +40 -0
  75. package/src/forge/cair/actions.ts +1818 -0
  76. package/src/forge/cair/format.ts +77 -0
  77. package/src/forge/cair/index.ts +106 -0
  78. package/src/forge/cair/query.ts +478 -0
  79. package/src/forge/cair/snapshot.ts +315 -0
  80. package/src/forge/cair/types.ts +248 -0
  81. package/src/forge/cli/ai.ts +339 -21
  82. package/src/forge/cli/auth.ts +348 -10
  83. package/src/forge/cli/authmd.ts +441 -0
  84. package/src/forge/cli/baseline.ts +112 -0
  85. package/src/forge/cli/build.ts +20 -4
  86. package/src/forge/cli/changed.ts +437 -0
  87. package/src/forge/cli/codex-app-server.ts +877 -0
  88. package/src/forge/cli/commands.ts +1620 -20
  89. package/src/forge/cli/db.ts +209 -2
  90. package/src/forge/cli/deploy.ts +887 -0
  91. package/src/forge/cli/deps.ts +82 -13
  92. package/src/forge/cli/dev.ts +961 -46
  93. package/src/forge/cli/docs.ts +265 -0
  94. package/src/forge/cli/doctor.ts +297 -0
  95. package/src/forge/cli/field-test.ts +329 -0
  96. package/src/forge/cli/handoff.ts +336 -0
  97. package/src/forge/cli/index.ts +1 -0
  98. package/src/forge/cli/last-run.ts +84 -0
  99. package/src/forge/cli/main.ts +107 -3
  100. package/src/forge/cli/new.ts +152 -13
  101. package/src/forge/cli/next-actions.ts +23 -0
  102. package/src/forge/cli/output.ts +389 -6
  103. package/src/forge/cli/parse.ts +1050 -34
  104. package/src/forge/cli/progress.ts +51 -0
  105. package/src/forge/cli/query.ts +32 -0
  106. package/src/forge/cli/release.ts +35 -11
  107. package/src/forge/cli/rls.ts +43 -4
  108. package/src/forge/cli/run.ts +41 -0
  109. package/src/forge/cli/security.ts +112 -0
  110. package/src/forge/cli/self-host.ts +56 -14
  111. package/src/forge/cli/studio.ts +2218 -0
  112. package/src/forge/cli/verify.ts +1455 -32
  113. package/src/forge/cli/windows.ts +23 -0
  114. package/src/forge/cli/workos.ts +1020 -0
  115. package/src/forge/compiler/agent-contract/build.ts +670 -64
  116. package/src/forge/compiler/agent-contract/types.ts +45 -2
  117. package/src/forge/compiler/ai-registry/parse.ts +71 -2
  118. package/src/forge/compiler/api-surface/build.ts +47 -0
  119. package/src/forge/compiler/app-graph/build.ts +112 -9
  120. package/src/forge/compiler/app-graph/extract.ts +107 -0
  121. package/src/forge/compiler/app-graph/module-graph.ts +73 -78
  122. package/src/forge/compiler/app-graph/parser.ts +24 -24
  123. package/src/forge/compiler/app-graph/profile.ts +26 -0
  124. package/src/forge/compiler/app-graph/versions.ts +1 -1
  125. package/src/forge/compiler/classifier/capabilities.ts +3 -2
  126. package/src/forge/compiler/classifier/classify.ts +32 -8
  127. package/src/forge/compiler/classifier/secrets.ts +3 -2
  128. package/src/forge/compiler/classifier/signals.ts +91 -1
  129. package/src/forge/compiler/client-sdk/build-manifest.ts +59 -0
  130. package/src/forge/compiler/client-sdk/render-client.ts +295 -13
  131. package/src/forge/compiler/data-graph/parse.ts +20 -4
  132. package/src/forge/compiler/data-graph/sql/ddl.ts +144 -31
  133. package/src/forge/compiler/data-graph/sql/serialize.ts +4 -0
  134. package/src/forge/compiler/data-graph/sql/types.ts +1 -0
  135. package/src/forge/compiler/diagnostics/codes.ts +30 -0
  136. package/src/forge/compiler/diagnostics/create.ts +21 -2
  137. package/src/forge/compiler/diagnostics/index.ts +2 -0
  138. package/src/forge/compiler/emitter/barrel.ts +3 -0
  139. package/src/forge/compiler/emitter/render.ts +9 -0
  140. package/src/forge/compiler/external-manifest/registry.ts +205 -0
  141. package/src/forge/compiler/external-manifest/types.ts +91 -0
  142. package/src/forge/compiler/external-manifest/validate.ts +373 -0
  143. package/src/forge/compiler/frontend-graph/build.ts +142 -14
  144. package/src/forge/compiler/integration/add.ts +618 -24
  145. package/src/forge/compiler/integration/plan.ts +43 -12
  146. package/src/forge/compiler/integration/render.ts +29 -0
  147. package/src/forge/compiler/integration/snapshot.ts +2 -0
  148. package/src/forge/compiler/integration/templates/convex.ts +70 -0
  149. package/src/forge/compiler/integration/templates/index.ts +1 -0
  150. package/src/forge/compiler/integration/templates/render.ts +38 -0
  151. package/src/forge/compiler/integration/templates/types.ts +3 -0
  152. package/src/forge/compiler/integration/templates/workos.ts +1219 -0
  153. package/src/forge/compiler/make-registry/build.ts +8 -9
  154. package/src/forge/compiler/orchestrator/fast-check.ts +4 -0
  155. package/src/forge/compiler/orchestrator/generate-lock.ts +14 -3
  156. package/src/forge/compiler/orchestrator/manifest.ts +1 -1
  157. package/src/forge/compiler/orchestrator/plan-profile.ts +23 -0
  158. package/src/forge/compiler/orchestrator/plan.ts +247 -10
  159. package/src/forge/compiler/orchestrator/profile.ts +65 -0
  160. package/src/forge/compiler/orchestrator/run.ts +97 -31
  161. package/src/forge/compiler/orchestrator/serialize.ts +33 -8
  162. package/src/forge/compiler/orchestrator/types.ts +1 -1
  163. package/src/forge/compiler/package-graph/compiler.ts +3 -1
  164. package/src/forge/compiler/package-graph/constants.ts +1 -1
  165. package/src/forge/compiler/package-manager/adapter.ts +4 -1
  166. package/src/forge/compiler/package-manager/commands.ts +4 -0
  167. package/src/forge/compiler/package-manager/executor.ts +30 -1
  168. package/src/forge/compiler/package-manager/version.ts +36 -12
  169. package/src/forge/compiler/package-upgrades/planner.ts +155 -18
  170. package/src/forge/compiler/package-upgrades/types.ts +2 -0
  171. package/src/forge/compiler/policy-registry/build.ts +47 -2
  172. package/src/forge/compiler/policy-registry/parse.ts +32 -2
  173. package/src/forge/compiler/recipes/definitions.ts +63 -0
  174. package/src/forge/compiler/recipes/index.ts +2 -0
  175. package/src/forge/compiler/recipes/registry.ts +6 -0
  176. package/src/forge/compiler/test-graph/build.ts +11 -3
  177. package/src/forge/compiler/types/app-graph.ts +8 -2
  178. package/src/forge/compiler/types/cli.ts +75 -1
  179. package/src/forge/compiler/types/dev-manifest.ts +4 -0
  180. package/src/forge/compiler/types/emit.ts +2 -0
  181. package/src/forge/compiler/types/frontend-graph.ts +2 -2
  182. package/src/forge/compiler/types/integration.ts +1 -0
  183. package/src/forge/compiler/types/package-graph.ts +6 -0
  184. package/src/forge/compiler/types/policy-registry.ts +3 -1
  185. package/src/forge/delta/classifier.ts +52 -0
  186. package/src/forge/delta/explain.ts +238 -0
  187. package/src/forge/delta/git-observer.ts +43 -0
  188. package/src/forge/delta/ids.ts +44 -0
  189. package/src/forge/delta/index.ts +25 -0
  190. package/src/forge/delta/recorder.ts +540 -0
  191. package/src/forge/delta/redaction.ts +50 -0
  192. package/src/forge/delta/schema.ts +240 -0
  193. package/src/forge/delta/session.ts +142 -0
  194. package/src/forge/delta/status.ts +1202 -0
  195. package/src/forge/delta/store.ts +3284 -0
  196. package/src/forge/delta/timeline.ts +178 -0
  197. package/src/forge/dev/server.ts +808 -17
  198. package/src/forge/dev/types.ts +15 -1
  199. package/src/forge/dev/watch.ts +17 -7
  200. package/src/forge/dev-console/cycle.ts +280 -33
  201. package/src/forge/dev-console/types.ts +47 -1
  202. package/src/forge/impact/index.ts +204 -9
  203. package/src/forge/impact/types.ts +33 -1
  204. package/src/forge/intent/index.ts +35 -16
  205. package/src/forge/make/fields.ts +26 -0
  206. package/src/forge/make/index.ts +40 -6
  207. package/src/forge/make/templates.ts +155 -2
  208. package/src/forge/make/types.ts +2 -1
  209. package/src/forge/policy.ts +1 -1
  210. package/src/forge/react/index.ts +160 -2
  211. package/src/forge/refactor/index.ts +1 -0
  212. package/src/forge/repair/rules/index.ts +2 -2
  213. package/src/forge/review/index.ts +158 -12
  214. package/src/forge/review/types.ts +15 -0
  215. package/src/forge/runtime/auth/config.ts +17 -0
  216. package/src/forge/runtime/auth/evaluate.ts +15 -2
  217. package/src/forge/runtime/auth/resolve.ts +61 -6
  218. package/src/forge/runtime/auth/types.ts +1 -0
  219. package/src/forge/runtime/db/factory.ts +1 -3
  220. package/src/forge/runtime/db/generated-client.ts +13 -2
  221. package/src/forge/runtime/db/memory-adapter.ts +139 -32
  222. package/src/forge/runtime/db/pglite-adapter.ts +263 -2
  223. package/src/forge/runtime/db/postgres-adapter.ts +6 -3
  224. package/src/forge/runtime/executor.ts +129 -16
  225. package/src/forge/runtime/external/bridge.ts +649 -0
  226. package/src/forge/runtime/runner/run-entry.ts +16 -7
  227. package/src/forge/runtime/telemetry/buffer.ts +4 -1
  228. package/src/forge/runtime/telemetry/scrubber.ts +35 -5
  229. package/src/forge/runtime/webhooks/security.ts +12 -7
  230. package/src/forge/server.ts +17 -3
  231. package/src/forge/ui/index.ts +209 -0
  232. package/src/forge/ui/types.ts +1 -0
  233. package/src/forge/version.ts +1 -1
  234. package/src/forge/vue/index.ts +422 -0
  235. package/src/forge/workspace/baseline.ts +112 -0
  236. package/src/forge/workspace/change-summary.ts +250 -0
  237. package/src/forge/workspace/forge-cli.ts +42 -0
  238. package/src/forge/workspace/git-summary.ts +391 -0
  239. package/templates/agent-workroom/AGENTS.md +29 -0
  240. package/templates/agent-workroom/README.md +34 -0
  241. package/templates/agent-workroom/forge.config.ts +3 -0
  242. package/templates/agent-workroom/package.json +33 -0
  243. package/templates/agent-workroom/src/actions/indexAgentSignal.ts +10 -0
  244. package/templates/agent-workroom/src/commands/openWorkroom.ts +61 -0
  245. package/templates/agent-workroom/src/commands/recordAgentSignal.ts +119 -0
  246. package/templates/agent-workroom/src/commands/recordCheckRun.ts +52 -0
  247. package/templates/agent-workroom/src/forge/schema.ts +54 -0
  248. package/templates/agent-workroom/src/policies.ts +6 -0
  249. package/templates/agent-workroom/src/queries/listWorkrooms.ts +11 -0
  250. package/templates/agent-workroom/src/queries/liveWorkroom.ts +63 -0
  251. package/templates/agent-workroom/tsconfig.json +16 -0
  252. package/templates/agent-workroom/web/index.html +12 -0
  253. package/templates/agent-workroom/web/package.json +21 -0
  254. package/templates/agent-workroom/web/src/App.tsx +345 -0
  255. package/templates/agent-workroom/web/src/lib/forge.ts +13 -0
  256. package/templates/agent-workroom/web/src/main.tsx +13 -0
  257. package/templates/agent-workroom/web/src/styles.css +545 -0
  258. package/templates/agent-workroom/web/tsconfig.json +27 -0
  259. package/templates/b2b-support-web/package.json +1 -0
  260. package/templates/b2b-support-web/web/package.json +1 -1
  261. package/templates/minimal-web/package.json +1 -1
  262. package/templates/minimal-web/web/package.json +2 -2
  263. package/templates/nuxt-web/.vscode/settings.json +14 -0
  264. package/templates/nuxt-web/README.md +30 -0
  265. package/templates/nuxt-web/forge.config.ts +3 -0
  266. package/templates/nuxt-web/package.json +33 -0
  267. package/templates/nuxt-web/src/actions/logNoteCreated.ts +11 -0
  268. package/templates/nuxt-web/src/commands/createNote.ts +26 -0
  269. package/templates/nuxt-web/src/forge/schema.ts +12 -0
  270. package/templates/nuxt-web/src/policies.ts +6 -0
  271. package/templates/nuxt-web/src/queries/listNotes.ts +8 -0
  272. package/templates/nuxt-web/src/queries/liveNotes.ts +8 -0
  273. package/templates/nuxt-web/tsconfig.json +17 -0
  274. package/templates/nuxt-web/web/app.vue +67 -0
  275. package/templates/nuxt-web/web/components/LiveNotes.vue +89 -0
  276. package/templates/nuxt-web/web/components/NoteComposer.vue +100 -0
  277. package/templates/nuxt-web/web/composables/forge.ts +13 -0
  278. package/templates/nuxt-web/web/composables/useNotes.ts +24 -0
  279. package/templates/nuxt-web/web/nuxt.config.ts +11 -0
  280. package/templates/nuxt-web/web/package.json +18 -0
  281. package/templates/nuxt-web/web/plugins/forge.client.ts +10 -0
  282. package/templates/nuxt-web/web/plugins/forge.server.ts +10 -0
  283. package/templates/nuxt-web/web/server/api/forge-health.get.ts +7 -0
  284. package/templates/nuxt-web/web/tsconfig.json +6 -0
  285. package/src/forge/_generated/actionSubscriptions.json +0 -2
  286. package/src/forge/_generated/actionSubscriptions.ts +0 -10
  287. package/src/forge/_generated/agentAdapterManifest.json +0 -2
  288. package/src/forge/_generated/agentAdapterManifest.ts +0 -73
  289. package/src/forge/_generated/agentContract.json +0 -2
  290. package/src/forge/_generated/agentContract.ts +0 -7829
  291. package/src/forge/_generated/agentQuickstart.md +0 -34
  292. package/src/forge/_generated/agentTools.json +0 -2
  293. package/src/forge/_generated/agentTools.md +0 -16
  294. package/src/forge/_generated/agentTools.ts +0 -12
  295. package/src/forge/_generated/aiContext.ts +0 -125
  296. package/src/forge/_generated/aiModels.json +0 -2
  297. package/src/forge/_generated/aiModels.ts +0 -51
  298. package/src/forge/_generated/aiProviders.json +0 -2
  299. package/src/forge/_generated/aiProviders.ts +0 -23
  300. package/src/forge/_generated/aiRegistry.json +0 -2
  301. package/src/forge/_generated/aiRegistry.ts +0 -31
  302. package/src/forge/_generated/api.json +0 -2
  303. package/src/forge/_generated/api.ts +0 -8
  304. package/src/forge/_generated/appGraph.json +0 -2
  305. package/src/forge/_generated/appGraph.ts +0 -14919
  306. package/src/forge/_generated/appMap.md +0 -55
  307. package/src/forge/_generated/artifactManifest.json +0 -2
  308. package/src/forge/_generated/artifactManifest.ts +0 -7
  309. package/src/forge/_generated/authClaims.json +0 -2
  310. package/src/forge/_generated/authClaims.ts +0 -13
  311. package/src/forge/_generated/authConfig.json +0 -2
  312. package/src/forge/_generated/authConfig.ts +0 -17
  313. package/src/forge/_generated/authContext.ts +0 -23
  314. package/src/forge/_generated/authRegistry.json +0 -2
  315. package/src/forge/_generated/authRegistry.ts +0 -25
  316. package/src/forge/_generated/buildInfo.json +0 -2
  317. package/src/forge/_generated/buildInfo.ts +0 -9
  318. package/src/forge/_generated/capabilityMap.json +0 -2
  319. package/src/forge/_generated/capabilityMap.md +0 -15
  320. package/src/forge/_generated/capabilityMap.ts +0 -17
  321. package/src/forge/_generated/client.ts +0 -282
  322. package/src/forge/_generated/clientApi.ts +0 -9
  323. package/src/forge/_generated/clientManifest.json +0 -2
  324. package/src/forge/_generated/clientManifest.ts +0 -39
  325. package/src/forge/_generated/clientTypes.ts +0 -78
  326. package/src/forge/_generated/configRegistry.json +0 -2
  327. package/src/forge/_generated/configRegistry.ts +0 -4
  328. package/src/forge/_generated/dataGraph.json +0 -2
  329. package/src/forge/_generated/dataGraph.ts +0 -8
  330. package/src/forge/_generated/db.json +0 -2
  331. package/src/forge/_generated/db.ts +0 -2
  332. package/src/forge/_generated/dbSecurityManifest.json +0 -2
  333. package/src/forge/_generated/dbSecurityManifest.ts +0 -15
  334. package/src/forge/_generated/dbSessionContext.json +0 -2
  335. package/src/forge/_generated/dbSessionContext.ts +0 -39
  336. package/src/forge/_generated/deployManifest.json +0 -2
  337. package/src/forge/_generated/deployManifest.ts +0 -14
  338. package/src/forge/_generated/devManifest.json +0 -2
  339. package/src/forge/_generated/devManifest.ts +0 -62
  340. package/src/forge/_generated/envSchema.json +0 -2
  341. package/src/forge/_generated/envSchema.ts +0 -59
  342. package/src/forge/_generated/frontendGraph.json +0 -2
  343. package/src/forge/_generated/frontendGraph.ts +0 -27
  344. package/src/forge/_generated/importGuards.json +0 -2
  345. package/src/forge/_generated/importGuards.ts +0 -686
  346. package/src/forge/_generated/index.ts +0 -68
  347. package/src/forge/_generated/liveProductionManifest.json +0 -2
  348. package/src/forge/_generated/liveProductionManifest.ts +0 -23
  349. package/src/forge/_generated/liveProtocol.json +0 -2
  350. package/src/forge/_generated/liveProtocol.ts +0 -21
  351. package/src/forge/_generated/liveQueryRegistry.json +0 -2
  352. package/src/forge/_generated/liveQueryRegistry.ts +0 -9
  353. package/src/forge/_generated/liveTransportConfig.json +0 -2
  354. package/src/forge/_generated/liveTransportConfig.ts +0 -19
  355. package/src/forge/_generated/makeRegistry.json +0 -2
  356. package/src/forge/_generated/makeRegistry.ts +0 -177
  357. package/src/forge/_generated/makeTemplates.json +0 -2
  358. package/src/forge/_generated/makeTemplates.ts +0 -66
  359. package/src/forge/_generated/mockMap.json +0 -2
  360. package/src/forge/_generated/mockMap.ts +0 -7
  361. package/src/forge/_generated/operationPlaybooks.md +0 -167
  362. package/src/forge/_generated/packageGraph.json +0 -2
  363. package/src/forge/_generated/packageGraph.ts +0 -249334
  364. package/src/forge/_generated/packageUpgradeRegistry.json +0 -2
  365. package/src/forge/_generated/packageUpgradeRegistry.ts +0 -15
  366. package/src/forge/_generated/permissionMatrix.json +0 -2
  367. package/src/forge/_generated/permissionMatrix.ts +0 -7
  368. package/src/forge/_generated/policyRegistry.json +0 -2
  369. package/src/forge/_generated/policyRegistry.ts +0 -11
  370. package/src/forge/_generated/queryRegistry.json +0 -2
  371. package/src/forge/_generated/queryRegistry.ts +0 -9
  372. package/src/forge/_generated/react.d.ts +0 -22
  373. package/src/forge/_generated/react.ts +0 -29
  374. package/src/forge/_generated/reactManifest.json +0 -2
  375. package/src/forge/_generated/reactManifest.ts +0 -19
  376. package/src/forge/_generated/rlsPolicies.json +0 -2
  377. package/src/forge/_generated/rlsPolicies.sql +0 -34
  378. package/src/forge/_generated/rlsPolicies.ts +0 -6
  379. package/src/forge/_generated/runtimeGraph.json +0 -2
  380. package/src/forge/_generated/runtimeGraph.ts +0 -8
  381. package/src/forge/_generated/runtimeMatrix.json +0 -2
  382. package/src/forge/_generated/runtimeMatrix.ts +0 -334130
  383. package/src/forge/_generated/runtimeRegistry.ts +0 -2
  384. package/src/forge/_generated/runtimeRules.md +0 -91
  385. package/src/forge/_generated/secretRegistry.json +0 -2
  386. package/src/forge/_generated/secretRegistry.ts +0 -50
  387. package/src/forge/_generated/secretsContext.ts +0 -11
  388. package/src/forge/_generated/serverApi.ts +0 -10
  389. package/src/forge/_generated/sourceMapManifest.json +0 -2
  390. package/src/forge/_generated/sourceMapManifest.ts +0 -7
  391. package/src/forge/_generated/sqlPlan.json +0 -2
  392. package/src/forge/_generated/sqlPlan.ts +0 -88
  393. package/src/forge/_generated/subscriptionManifest.json +0 -2
  394. package/src/forge/_generated/subscriptionManifest.ts +0 -7
  395. package/src/forge/_generated/symbolicationManifest.json +0 -2
  396. package/src/forge/_generated/symbolicationManifest.ts +0 -17
  397. package/src/forge/_generated/telemetryRegistry.json +0 -2
  398. package/src/forge/_generated/telemetryRegistry.ts +0 -9
  399. package/src/forge/_generated/telemetrySinks.json +0 -2
  400. package/src/forge/_generated/telemetrySinks.ts +0 -11
  401. package/src/forge/_generated/tenantScope.json +0 -2
  402. package/src/forge/_generated/tenantScope.ts +0 -8
  403. package/src/forge/_generated/testGraph.json +0 -2
  404. package/src/forge/_generated/testGraph.ts +0 -3430
  405. package/src/forge/_generated/testPlanRegistry.json +0 -2
  406. package/src/forge/_generated/testPlanRegistry.ts +0 -33
  407. package/src/forge/_generated/uiRoutes.json +0 -2
  408. package/src/forge/_generated/uiRoutes.ts +0 -16
  409. package/src/forge/_generated/uiScenarios.json +0 -2
  410. package/src/forge/_generated/uiScenarios.ts +0 -30
  411. package/src/forge/_generated/uiTestManifest.json +0 -2
  412. package/src/forge/_generated/uiTestManifest.ts +0 -27
  413. package/src/forge/_generated/workflowRegistry.json +0 -2
  414. package/src/forge/_generated/workflowRegistry.ts +0 -9
  415. package/src/forge/_generated/workflowSubscriptions.json +0 -2
  416. package/src/forge/_generated/workflowSubscriptions.ts +0 -10
@@ -7,6 +7,7 @@ interface MemoryRow {
7
7
  interface MemoryTable {
8
8
  rows: MemoryRow[];
9
9
  nextSerial: number;
10
+ columnTypes: Map<string, string>;
10
11
  }
11
12
 
12
13
  function normalizeRows(rows: MemoryRow[]): DbQueryResult {
@@ -16,8 +17,8 @@ function normalizeRows(rows: MemoryRow[]): DbQueryResult {
16
17
  };
17
18
  }
18
19
 
19
- function parseNow(): string {
20
- return new Date().toISOString();
20
+ function parseNow(): Date {
21
+ return new Date();
21
22
  }
22
23
 
23
24
  function compareValue(left: unknown, right: unknown): boolean {
@@ -52,6 +53,42 @@ function parseTableName(sql: string): string | null {
52
53
  return null;
53
54
  }
54
55
 
56
+ function parseColumnTypes(sql: string): Map<string, string> {
57
+ if (!parseTableName(sql)) {
58
+ return new Map();
59
+ }
60
+ const tableNameMatch =
61
+ sql.match(/CREATE TABLE IF NOT EXISTS\s+"?([^"\s(]+)"?/i) ??
62
+ sql.match(/CREATE TABLE\s+"?([^"\s(]+)"?/i);
63
+ if (!tableNameMatch?.[0]) {
64
+ return new Map();
65
+ }
66
+
67
+ const start = sql.indexOf("(", tableNameMatch.index! + tableNameMatch[0].length);
68
+ const end = sql.lastIndexOf(")");
69
+ if (start < 0 || end <= start) {
70
+ return new Map();
71
+ }
72
+
73
+ const types = new Map<string, string>();
74
+ for (const definition of splitSqlList(sql.slice(start + 1, end))) {
75
+ const trimmed = definition.trim();
76
+ if (/^(CONSTRAINT|PRIMARY|UNIQUE|FOREIGN|CHECK)\b/i.test(trimmed)) {
77
+ continue;
78
+ }
79
+ const match = trimmed.match(/^"?(\w+)"?\s+([a-zA-Z][a-zA-Z0-9_]*(?:\s+precision)?)/);
80
+ if (match?.[1] && match?.[2]) {
81
+ types.set(match[1], match[2].toLowerCase());
82
+ }
83
+ }
84
+ return types;
85
+ }
86
+
87
+ function isTimestampType(sqlType: string | undefined): boolean {
88
+ if (!sqlType) return false;
89
+ return ["timestamp", "timestamptz", "timestamp with time zone", "timestamp without time zone"].includes(sqlType);
90
+ }
91
+
55
92
  function splitSqlList(value: string): string[] {
56
93
  const parts: string[] = [];
57
94
  let current = "";
@@ -111,10 +148,68 @@ function parseSqlLiteral(token: string, params: unknown[]): unknown {
111
148
  return Number.isFinite(numeric) ? numeric : token;
112
149
  }
113
150
 
151
+ function parseUpdateExpression(
152
+ row: MemoryRow,
153
+ column: string,
154
+ expression: string,
155
+ params: unknown[],
156
+ ): unknown {
157
+ const paramMatch = expression.match(/\$(\d+)/);
158
+ const normalizedExpression = expression.replace(/"/g, "").replace(/\s+/g, " ");
159
+
160
+ if (normalizedExpression.toLowerCase() === `${column.toLowerCase()} + 1`) {
161
+ return Number(row[column] ?? 0) + 1;
162
+ }
163
+
164
+ const coalesceMatch = expression.match(/^COALESCE\(\s*"?(\w+)"?\s*,\s*(.+)\)$/i);
165
+ if (coalesceMatch?.[1] && coalesceMatch[2]) {
166
+ const existing = row[coalesceMatch[1]];
167
+ return existing ?? parseUpdateExpression(row, column, coalesceMatch[2].trim(), params);
168
+ }
169
+
170
+ if (paramMatch?.[1]) {
171
+ return params[Number(paramMatch[1]) - 1];
172
+ }
173
+
174
+ return parseSqlLiteral(expression, params);
175
+ }
176
+
114
177
  function deterministicUuid(serial: number): string {
115
178
  return `00000000-0000-0000-0000-${String(serial).padStart(12, "0")}`;
116
179
  }
117
180
 
181
+ function coerceColumnValue(tableName: string, table: MemoryTable, column: string, value: unknown): unknown {
182
+ if (!isTimestampType(table.columnTypes.get(column))) {
183
+ return value;
184
+ }
185
+ if (value === null || value === undefined) {
186
+ return value;
187
+ }
188
+ if (value === "") {
189
+ throw new Error(`FORGE_DB_INVALID_TIMESTAMP: empty timestamp value for ${tableName}.${column}`);
190
+ }
191
+ if (value instanceof Date) {
192
+ if (Number.isNaN(value.getTime())) {
193
+ throw new Error(`FORGE_DB_INVALID_TIMESTAMP: invalid timestamp value for ${tableName}.${column}`);
194
+ }
195
+ return value;
196
+ }
197
+ if (typeof value === "string" || typeof value === "number") {
198
+ const date = new Date(value);
199
+ if (Number.isNaN(date.getTime())) {
200
+ throw new Error(`FORGE_DB_INVALID_TIMESTAMP: invalid timestamp value for ${tableName}.${column}`);
201
+ }
202
+ return date;
203
+ }
204
+ throw new Error(`FORGE_DB_INVALID_TIMESTAMP: unsupported timestamp value for ${tableName}.${column}`);
205
+ }
206
+
207
+ function coerceRowValues(tableName: string, table: MemoryTable, row: MemoryRow): void {
208
+ for (const column of Object.keys(row)) {
209
+ row[column] = coerceColumnValue(tableName, table, column, row[column]);
210
+ }
211
+ }
212
+
118
213
  function applySystemDefaults(tableName: string, row: MemoryRow): void {
119
214
  const now = parseNow();
120
215
 
@@ -203,6 +298,16 @@ function projectRows(sql: string, rows: MemoryRow[]): MemoryRow[] {
203
298
  });
204
299
  }
205
300
 
301
+ function extractWhereClause(sql: string): string | null {
302
+ const whereIndex = sql.search(/\bWHERE\b/i);
303
+ if (whereIndex < 0) {
304
+ return null;
305
+ }
306
+ const afterWhere = sql.slice(whereIndex);
307
+ const endMatch = afterWhere.match(/\b(ORDER\s+BY|GROUP\s+BY|LIMIT|RETURNING)\b/i);
308
+ return endMatch?.index === undefined ? afterWhere : afterWhere.slice(0, endMatch.index);
309
+ }
310
+
206
311
  export class MemoryAdapter implements DbAdapter {
207
312
  readonly kind = "memory" as const;
208
313
  private tables = new Map<string, MemoryTable>();
@@ -215,8 +320,11 @@ export class MemoryAdapter implements DbAdapter {
215
320
  const match =
216
321
  trimmed.match(/CREATE TABLE IF NOT EXISTS "([^"]+)"/i) ??
217
322
  trimmed.match(/CREATE TABLE IF NOT EXISTS ([a-z_][a-z0-9_]*)/i);
218
- if (match?.[1] && !this.tables.has(match[1])) {
219
- this.tables.set(match[1], { rows: [], nextSerial: 1 });
323
+ if (match?.[1]) {
324
+ const table = this.ensureTable(match[1]);
325
+ for (const [column, sqlType] of parseColumnTypes(trimmed)) {
326
+ table.columnTypes.set(column, sqlType);
327
+ }
220
328
  }
221
329
  return { rows: [], rowCount: 0 };
222
330
  }
@@ -331,6 +439,7 @@ export class MemoryAdapter implements DbAdapter {
331
439
  }
332
440
 
333
441
  applySystemDefaults(tableName, row);
442
+ coerceRowValues(tableName, table, row);
334
443
 
335
444
  table.rows.push(row);
336
445
 
@@ -474,31 +583,33 @@ export class MemoryAdapter implements DbAdapter {
474
583
  }
475
584
 
476
585
  private filterRows(rows: MemoryRow[], sql: string, params: unknown[]): MemoryRow[] {
477
- if (/status\s*=\s*'pending'/i.test(sql) && /next_attempt_at\s*<=\s*now\(\)/i.test(sql)) {
586
+ const whereSql = extractWhereClause(sql) ?? sql;
587
+
588
+ if (/status\s*=\s*'pending'/i.test(whereSql) && /next_attempt_at\s*<=\s*now\(\)/i.test(whereSql)) {
478
589
  return rows.filter((row) => row.status === "pending");
479
590
  }
480
591
 
481
- if (/trace_id\s*=\s*\$\d+/i.test(sql)) {
592
+ if (/trace_id\s*=\s*\$\d+/i.test(whereSql)) {
482
593
  return rows.filter((row) => row.trace_id === params[0]);
483
594
  }
484
595
 
485
- if (/status\s+IN\s*\(\s*'pending'\s*,\s*'running'\s*,\s*'failed'\s*\)/i.test(sql)) {
596
+ if (/status\s+IN\s*\(\s*'pending'\s*,\s*'running'\s*,\s*'failed'\s*\)/i.test(whereSql)) {
486
597
  return rows.filter((row) =>
487
598
  ["pending", "running", "failed"].includes(String(row.status)),
488
599
  );
489
600
  }
490
601
 
491
- if (/status\s+NOT\s+IN\s*\(\s*'completed'\s*,\s*'skipped'\s*\)/i.test(sql)) {
602
+ if (/status\s+NOT\s+IN\s*\(\s*'completed'\s*,\s*'skipped'\s*\)/i.test(whereSql)) {
492
603
  return rows.filter(
493
604
  (row) => !["completed", "skipped"].includes(String(row.status)),
494
605
  );
495
606
  }
496
607
 
497
- if (/status\s*=\s*'dead'/i.test(sql) && /COUNT/i.test(sql)) {
608
+ if (/status\s*=\s*'dead'/i.test(whereSql) && /COUNT/i.test(sql)) {
498
609
  return rows.filter((row) => row.status === "dead");
499
610
  }
500
611
 
501
- if (/step_index\s*<\s*\$\d+/i.test(sql) && /status\s*!=\s*'completed'/i.test(sql)) {
612
+ if (/step_index\s*<\s*\$\d+/i.test(whereSql) && /status\s*!=\s*'completed'/i.test(whereSql)) {
502
613
  const threshold = Number(params[1]);
503
614
  const runId = params[0];
504
615
  return rows.filter(
@@ -509,16 +620,16 @@ export class MemoryAdapter implements DbAdapter {
509
620
  );
510
621
  }
511
622
 
512
- if (/idempotency_key\s*=\s*\$\d+/i.test(sql)) {
623
+ if (/idempotency_key\s*=\s*\$\d+/i.test(whereSql)) {
513
624
  return rows.filter((row) => row.idempotency_key === params[0]);
514
625
  }
515
626
 
516
- if (/status\s*=\s*'dead'/i.test(sql)) {
627
+ if (/status\s*=\s*'dead'/i.test(whereSql)) {
517
628
  return rows.filter((row) => row.status === "dead");
518
629
  }
519
630
 
520
631
  const conditions = [
521
- ...sql.matchAll(
632
+ ...whereSql.matchAll(
522
633
  /(?:^|\s|AND\s)(?:\w+\.)?"?(\w+)"?\s*(=|<=|>=|>|<|!=)\s*(\$\d+|now\(\)|'[^']*'|"[^"]*"|\d+)/gi,
523
634
  ),
524
635
  ];
@@ -569,14 +680,12 @@ export class MemoryAdapter implements DbAdapter {
569
680
  }
570
681
 
571
682
  const setMatch = sql.match(/SET (.+?) WHERE/i);
572
- const assignments = setMatch?.[1]?.split(",") ?? [];
573
- const whereMatch = sql.match(/WHERE\s+"?(\w+)"?\s*=\s*\$\d+/i);
574
- const whereColumn = whereMatch?.[1] ?? "id";
575
- const whereValue = params[params.length - 1];
683
+ const assignments = setMatch?.[1] ? splitSqlList(setMatch[1]) : [];
684
+ const matchingRows = /WHERE/i.test(sql) ? new Set(this.filterRows(table.rows, sql, params)) : new Set(table.rows);
576
685
 
577
686
  let updated = 0;
578
687
  for (const row of table.rows) {
579
- if (row[whereColumn] !== whereValue) {
688
+ if (!matchingRows.has(row)) {
580
689
  continue;
581
690
  }
582
691
 
@@ -608,23 +717,20 @@ export class MemoryAdapter implements DbAdapter {
608
717
  }
609
718
 
610
719
  for (const assignment of assignments) {
611
- const [rawColumn, rawExpression] = assignment.split("=");
612
- const column = rawColumn?.trim().replace(/"/g, "");
720
+ const equalsIndex = assignment.indexOf("=");
721
+ if (equalsIndex < 0) {
722
+ continue;
723
+ }
724
+ const rawColumn = assignment.slice(0, equalsIndex);
725
+ const rawExpression = assignment.slice(equalsIndex + 1);
726
+ const column = rawColumn.trim().replace(/"/g, "");
613
727
  if (!column) {
614
728
  continue;
615
729
  }
616
730
 
617
- const expression = rawExpression?.trim() ?? "";
618
- const paramMatch = expression.match(/\$(\d+)/);
619
- const normalizedExpression = expression.replace(/"/g, "").replace(/\s+/g, " ");
620
-
621
- if (normalizedExpression.toLowerCase() === `${column.toLowerCase()} + 1`) {
622
- row[column] = Number(row[column] ?? 0) + 1;
623
- } else if (paramMatch?.[1]) {
624
- row[column] = params[Number(paramMatch[1]) - 1];
625
- } else {
626
- row[column] = parseSqlLiteral(expression, params);
627
- }
731
+ const expression = rawExpression.trim();
732
+ row[column] = parseUpdateExpression(row, column, expression, params);
733
+ row[column] = coerceColumnValue(tableName, table, column, row[column]);
628
734
  }
629
735
 
630
736
  updated += 1;
@@ -668,6 +774,7 @@ export class MemoryAdapter implements DbAdapter {
668
774
  snapshot.set(name, {
669
775
  rows: table.rows.map((row) => ({ ...row })),
670
776
  nextSerial: table.nextSerial,
777
+ columnTypes: new Map(table.columnTypes),
671
778
  });
672
779
  }
673
780
  const sequenceSnapshot = new Map(this.sequences);
@@ -695,7 +802,7 @@ export class MemoryAdapter implements DbAdapter {
695
802
  if (existing) {
696
803
  return existing;
697
804
  }
698
- const created = { rows: [], nextSerial: 1 };
805
+ const created = { rows: [], nextSerial: 1, columnTypes: new Map<string, string>() };
699
806
  this.tables.set(name, created);
700
807
  return created;
701
808
  }
@@ -1,6 +1,40 @@
1
1
  import { PGlite } from "@electric-sql/pglite";
2
+ import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, unlinkSync } from "node:fs";
3
+ import { basename, dirname, join } from "node:path";
2
4
  import type { DbAdapter, DbQueryResult, DbTransaction } from "./adapter.ts";
3
5
 
6
+ export const DEFAULT_PGLITE_DIR = ".forge/pglite";
7
+
8
+ export type PgliteStoreState =
9
+ | "missing"
10
+ | "healthy"
11
+ | "active"
12
+ | "aborted"
13
+ | "unhealthy";
14
+
15
+ export interface PgliteStoreInspection {
16
+ dataDir: string;
17
+ exists: boolean;
18
+ state: PgliteStoreState;
19
+ openable: boolean;
20
+ postmasterPid?: number;
21
+ postmasterAlive?: boolean;
22
+ lockFiles: string[];
23
+ error?: string;
24
+ nextActions: string[];
25
+ }
26
+
27
+ export interface PgliteStoreRepairResult {
28
+ ok: boolean;
29
+ repaired: boolean;
30
+ dataDir: string;
31
+ backupPath: string | null;
32
+ before: PgliteStoreInspection;
33
+ after: PgliteStoreInspection;
34
+ message: string;
35
+ nextActions: string[];
36
+ }
37
+
4
38
  function toQueryResult(result: {
5
39
  rows: Record<string, unknown>[];
6
40
  affectedRows?: number;
@@ -45,7 +79,234 @@ export class PgliteAdapter implements DbAdapter {
45
79
  }
46
80
 
47
81
  export async function createPgliteAdapter(dataDir: string): Promise<DbAdapter> {
82
+ mkdirSync(dataDir, { recursive: true });
83
+ repairStalePgliteStore(dataDir);
48
84
  const adapter = new PgliteAdapter(dataDir);
49
- await adapter.query("SELECT 1");
50
- return adapter;
85
+ try {
86
+ await adapter.query("SELECT 1");
87
+ return adapter;
88
+ } catch (error) {
89
+ await adapter.close().catch(() => undefined);
90
+ throw error;
91
+ }
92
+ }
93
+
94
+ export async function inspectPgliteStore(dataDir: string): Promise<PgliteStoreInspection> {
95
+ const exists = existsSync(dataDir);
96
+ const lockFiles = [
97
+ "postmaster.pid",
98
+ ".s.PGSQL.5432.lock",
99
+ ".s.PGSQL.5432.lock.out",
100
+ ].filter((file) => existsSync(join(dataDir, file)));
101
+
102
+ if (!exists) {
103
+ return {
104
+ dataDir,
105
+ exists: false,
106
+ state: "missing",
107
+ openable: true,
108
+ lockFiles,
109
+ nextActions: ["forge dev --db pglite --json"],
110
+ };
111
+ }
112
+
113
+ const pid = readPglitePostmasterPid(dataDir);
114
+ const postmasterAlive = Number.isInteger(pid) && pid > 0 ? isProcessAlive(pid) : undefined;
115
+ if (postmasterAlive) {
116
+ return {
117
+ dataDir,
118
+ exists: true,
119
+ state: "active",
120
+ openable: false,
121
+ postmasterPid: pid,
122
+ postmasterAlive,
123
+ lockFiles,
124
+ error: `PGlite store is currently owned by live pid ${pid}`,
125
+ nextActions: [
126
+ "stop the running forge dev process that owns .forge/pglite",
127
+ "forge doctor pglite --json",
128
+ ],
129
+ };
130
+ }
131
+
132
+ let adapter: DbAdapter | null = null;
133
+ const previousExitCode = process.exitCode;
134
+ let restoreExitCode = false;
135
+ try {
136
+ adapter = new PgliteAdapter(dataDir);
137
+ await adapter.query("SELECT 1");
138
+ return {
139
+ dataDir,
140
+ exists: true,
141
+ state: "healthy",
142
+ openable: true,
143
+ ...(Number.isInteger(pid) && pid > 0 ? { postmasterPid: pid, postmasterAlive: false } : {}),
144
+ lockFiles,
145
+ nextActions: ["forge dev --db pglite --json"],
146
+ };
147
+ } catch (error) {
148
+ const message = error instanceof Error ? error.message : String(error);
149
+ const aborted = isPgliteAbortMessage(message);
150
+ if (aborted) {
151
+ restoreExitCode = true;
152
+ process.exitCode = previousExitCode;
153
+ }
154
+ return {
155
+ dataDir,
156
+ exists: true,
157
+ state: aborted ? "aborted" : "unhealthy",
158
+ openable: false,
159
+ ...(Number.isInteger(pid) && pid > 0 ? { postmasterPid: pid, postmasterAlive: false } : {}),
160
+ lockFiles,
161
+ error: message,
162
+ nextActions: [
163
+ "forge db repair --local --adapter pglite --json",
164
+ "forge dev --db memory --json",
165
+ ],
166
+ };
167
+ } finally {
168
+ await adapter?.close().catch(() => undefined);
169
+ if (restoreExitCode) {
170
+ process.exitCode = previousExitCode;
171
+ }
172
+ }
173
+ }
174
+
175
+ export async function repairLocalPgliteStore(dataDir: string): Promise<PgliteStoreRepairResult> {
176
+ const before = await inspectPgliteStore(dataDir);
177
+ if (before.state === "active") {
178
+ return {
179
+ ok: false,
180
+ repaired: false,
181
+ dataDir,
182
+ backupPath: null,
183
+ before,
184
+ after: before,
185
+ message: "PGlite store is active; stop the owning forge dev process before repair.",
186
+ nextActions: before.nextActions,
187
+ };
188
+ }
189
+
190
+ if (before.state === "missing") {
191
+ return {
192
+ ok: true,
193
+ repaired: false,
194
+ dataDir,
195
+ backupPath: null,
196
+ before,
197
+ after: before,
198
+ message: "No local PGlite store exists; nothing to repair.",
199
+ nextActions: before.nextActions,
200
+ };
201
+ }
202
+
203
+ if (before.state === "healthy") {
204
+ return {
205
+ ok: true,
206
+ repaired: false,
207
+ dataDir,
208
+ backupPath: null,
209
+ before,
210
+ after: before,
211
+ message: "Local PGlite store is healthy; no repair needed.",
212
+ nextActions: before.nextActions,
213
+ };
214
+ }
215
+
216
+ const backupPath = archivePgliteStore(dataDir, before.state);
217
+ const after = await inspectPgliteStore(dataDir);
218
+ const ok = after.state === "missing" || after.state === "healthy";
219
+ return {
220
+ ok,
221
+ repaired: true,
222
+ dataDir,
223
+ backupPath,
224
+ before,
225
+ after,
226
+ message: backupPath
227
+ ? `Archived local PGlite store to ${backupPath}.`
228
+ : "Removed stale local PGlite lock files; retry forge dev.",
229
+ nextActions: ok
230
+ ? ["forge dev --db pglite --json"]
231
+ : ["forge dev --db memory --json", "forge doctor pglite --json"],
232
+ };
233
+ }
234
+
235
+ export function isPgliteAbortMessage(message: string): boolean {
236
+ return /Aborted\(\)\. Build with -sASSERTIONS/i.test(message) ||
237
+ (/pglite/i.test(message) && /abort/i.test(message));
238
+ }
239
+
240
+ function repairStalePgliteStore(dataDir: string): void {
241
+ const pidPath = join(dataDir, "postmaster.pid");
242
+ if (!existsSync(pidPath)) {
243
+ return;
244
+ }
245
+
246
+ const pid = readPglitePostmasterPid(dataDir);
247
+ const stale =
248
+ !Number.isInteger(pid) ||
249
+ pid <= 0 ||
250
+ !isProcessAlive(pid);
251
+
252
+ if (!stale) {
253
+ return;
254
+ }
255
+
256
+ removeStalePgliteLocks(dataDir);
257
+ }
258
+
259
+ function readPglitePostmasterPid(dataDir: string): number {
260
+ try {
261
+ const rawPid = readFileSync(join(dataDir, "postmaster.pid"), "utf8").split(/\r?\n/, 1)[0]?.trim();
262
+ return rawPid ? Number(rawPid) : NaN;
263
+ } catch {
264
+ return NaN;
265
+ }
266
+ }
267
+
268
+ function removeStalePgliteLocks(dataDir: string): void {
269
+ for (const file of [
270
+ "postmaster.pid",
271
+ ".s.PGSQL.5432.lock",
272
+ ".s.PGSQL.5432.lock.out",
273
+ ]) {
274
+ try {
275
+ unlinkSync(join(dataDir, file));
276
+ } catch {
277
+ // Best effort. If PGlite still cannot open, the caller reports the real open error.
278
+ }
279
+ }
280
+ }
281
+
282
+ export function archivePgliteStore(dataDir: string, reason = "repair"): string | null {
283
+ if (!existsSync(dataDir)) {
284
+ return null;
285
+ }
286
+
287
+ const parent = dirname(dataDir);
288
+ const backupsDir = join(parent, `${basename(dataDir)}.backups`);
289
+ mkdirSync(backupsDir, { recursive: true });
290
+ const stamp = new Date().toISOString().replace(/[:.]/g, "-");
291
+ const backupPath = join(backupsDir, `${basename(dataDir)}.${reason}.${stamp}`);
292
+
293
+ try {
294
+ renameSync(dataDir, backupPath);
295
+ mkdirSync(dataDir, { recursive: true });
296
+ return backupPath;
297
+ } catch {
298
+ rmSync(join(dataDir, "postmaster.pid"), { force: true });
299
+ rmSync(join(dataDir, ".s.PGSQL.5432.lock"), { force: true });
300
+ rmSync(join(dataDir, ".s.PGSQL.5432.lock.out"), { force: true });
301
+ return null;
302
+ }
303
+ }
304
+
305
+ function isProcessAlive(pid: number): boolean {
306
+ try {
307
+ process.kill(pid, 0);
308
+ return true;
309
+ } catch {
310
+ return false;
311
+ }
51
312
  }
@@ -12,7 +12,7 @@ function loadBunSql(databaseUrl: string): PostgresClient | null {
12
12
  try {
13
13
  const bunGlobal = globalThis as {
14
14
  Bun?: {
15
- SQL?: new (url: string) => {
15
+ SQL?: new (options: string | { url: string; max?: number }) => {
16
16
  unsafe: (sql: string, params?: unknown[]) => Promise<Record<string, unknown>[]>;
17
17
  close: () => Promise<void>;
18
18
  };
@@ -23,7 +23,7 @@ function loadBunSql(databaseUrl: string): PostgresClient | null {
23
23
  return null;
24
24
  }
25
25
 
26
- const client = new bunGlobal.Bun.SQL(databaseUrl);
26
+ const client = new bunGlobal.Bun.SQL({ url: databaseUrl, max: 1 });
27
27
  return {
28
28
  query: async (sql, params = []) => {
29
29
  const rows = await client.unsafe(sql, params);
@@ -42,7 +42,10 @@ async function loadPostgresJs(databaseUrl: string): Promise<PostgresClient | nul
42
42
  try {
43
43
  const module = await import("postgres");
44
44
  const createSql = module.default;
45
- const sql = createSql(databaseUrl);
45
+ const sql = createSql(databaseUrl, {
46
+ max: 1,
47
+ onnotice: () => undefined,
48
+ });
46
49
  return {
47
50
  query: async (statement, params = []) => {
48
51
  const rows = await sql.unsafe(statement, params as never[]);