enpilink 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (477) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +289 -0
  3. package/bin/run.js +5 -0
  4. package/dist/cli/build-helpers.d.ts +8 -0
  5. package/dist/cli/build-helpers.js +105 -0
  6. package/dist/cli/build-helpers.js.map +1 -0
  7. package/dist/cli/build-helpers.test.d.ts +1 -0
  8. package/dist/cli/build-helpers.test.js +100 -0
  9. package/dist/cli/build-helpers.test.js.map +1 -0
  10. package/dist/cli/detect-port.d.ts +18 -0
  11. package/dist/cli/detect-port.js +50 -0
  12. package/dist/cli/detect-port.js.map +1 -0
  13. package/dist/cli/ensure-ssh-key.d.ts +17 -0
  14. package/dist/cli/ensure-ssh-key.js +45 -0
  15. package/dist/cli/ensure-ssh-key.js.map +1 -0
  16. package/dist/cli/ensure-ssh-key.test.d.ts +1 -0
  17. package/dist/cli/ensure-ssh-key.test.js +68 -0
  18. package/dist/cli/ensure-ssh-key.test.js.map +1 -0
  19. package/dist/cli/header.d.ts +4 -0
  20. package/dist/cli/header.js +6 -0
  21. package/dist/cli/header.js.map +1 -0
  22. package/dist/cli/resolve-views-dir.d.ts +1 -0
  23. package/dist/cli/resolve-views-dir.js +17 -0
  24. package/dist/cli/resolve-views-dir.js.map +1 -0
  25. package/dist/cli/run-command.d.ts +2 -0
  26. package/dist/cli/run-command.js +43 -0
  27. package/dist/cli/run-command.js.map +1 -0
  28. package/dist/cli/telemetry.d.ts +14 -0
  29. package/dist/cli/telemetry.js +24 -0
  30. package/dist/cli/telemetry.js.map +1 -0
  31. package/dist/cli/tunnel-control-server.d.ts +11 -0
  32. package/dist/cli/tunnel-control-server.js +35 -0
  33. package/dist/cli/tunnel-control-server.js.map +1 -0
  34. package/dist/cli/tunnel-control-server.test.d.ts +1 -0
  35. package/dist/cli/tunnel-control-server.test.js +39 -0
  36. package/dist/cli/tunnel-control-server.test.js.map +1 -0
  37. package/dist/cli/tunnel-handler.d.ts +3 -0
  38. package/dist/cli/tunnel-handler.js +48 -0
  39. package/dist/cli/tunnel-handler.js.map +1 -0
  40. package/dist/cli/tunnel-handler.test.d.ts +1 -0
  41. package/dist/cli/tunnel-handler.test.js +107 -0
  42. package/dist/cli/tunnel-handler.test.js.map +1 -0
  43. package/dist/cli/tunnel-providers/index.d.ts +5 -0
  44. package/dist/cli/tunnel-providers/index.js +5 -0
  45. package/dist/cli/tunnel-providers/index.js.map +1 -0
  46. package/dist/cli/tunnel-providers/srv-us.d.ts +18 -0
  47. package/dist/cli/tunnel-providers/srv-us.js +66 -0
  48. package/dist/cli/tunnel-providers/srv-us.js.map +1 -0
  49. package/dist/cli/tunnel-providers/srv-us.test.d.ts +1 -0
  50. package/dist/cli/tunnel-providers/srv-us.test.js +74 -0
  51. package/dist/cli/tunnel-providers/srv-us.test.js.map +1 -0
  52. package/dist/cli/tunnel-providers/types.d.ts +49 -0
  53. package/dist/cli/tunnel-providers/types.js +2 -0
  54. package/dist/cli/tunnel-providers/types.js.map +1 -0
  55. package/dist/cli/tunnel.d.ts +75 -0
  56. package/dist/cli/tunnel.js +254 -0
  57. package/dist/cli/tunnel.js.map +1 -0
  58. package/dist/cli/tunnel.test.d.ts +1 -0
  59. package/dist/cli/tunnel.test.js +255 -0
  60. package/dist/cli/tunnel.test.js.map +1 -0
  61. package/dist/cli/types.d.ts +5 -0
  62. package/dist/cli/types.js +2 -0
  63. package/dist/cli/types.js.map +1 -0
  64. package/dist/cli/use-execute-steps.d.ts +11 -0
  65. package/dist/cli/use-execute-steps.js +36 -0
  66. package/dist/cli/use-execute-steps.js.map +1 -0
  67. package/dist/cli/use-messages.d.ts +3 -0
  68. package/dist/cli/use-messages.js +11 -0
  69. package/dist/cli/use-messages.js.map +1 -0
  70. package/dist/cli/use-nodemon.d.ts +2 -0
  71. package/dist/cli/use-nodemon.js +73 -0
  72. package/dist/cli/use-nodemon.js.map +1 -0
  73. package/dist/cli/use-open-browser.d.ts +1 -0
  74. package/dist/cli/use-open-browser.js +44 -0
  75. package/dist/cli/use-open-browser.js.map +1 -0
  76. package/dist/cli/use-open-tunnel-browser.d.ts +6 -0
  77. package/dist/cli/use-open-tunnel-browser.js +19 -0
  78. package/dist/cli/use-open-tunnel-browser.js.map +1 -0
  79. package/dist/cli/use-tunnel.d.ts +17 -0
  80. package/dist/cli/use-tunnel.js +131 -0
  81. package/dist/cli/use-tunnel.js.map +1 -0
  82. package/dist/cli/use-typescript-check.d.ts +9 -0
  83. package/dist/cli/use-typescript-check.js +94 -0
  84. package/dist/cli/use-typescript-check.js.map +1 -0
  85. package/dist/commands/build.d.ts +8 -0
  86. package/dist/commands/build.js +97 -0
  87. package/dist/commands/build.js.map +1 -0
  88. package/dist/commands/create.d.ts +9 -0
  89. package/dist/commands/create.js +30 -0
  90. package/dist/commands/create.js.map +1 -0
  91. package/dist/commands/dev.d.ts +13 -0
  92. package/dist/commands/dev.js +112 -0
  93. package/dist/commands/dev.js.map +1 -0
  94. package/dist/commands/start.d.ts +10 -0
  95. package/dist/commands/start.js +76 -0
  96. package/dist/commands/start.js.map +1 -0
  97. package/dist/commands/telemetry/disable.d.ts +5 -0
  98. package/dist/commands/telemetry/disable.js +12 -0
  99. package/dist/commands/telemetry/disable.js.map +1 -0
  100. package/dist/commands/telemetry/enable.d.ts +5 -0
  101. package/dist/commands/telemetry/enable.js +12 -0
  102. package/dist/commands/telemetry/enable.js.map +1 -0
  103. package/dist/commands/telemetry/status.d.ts +5 -0
  104. package/dist/commands/telemetry/status.js +12 -0
  105. package/dist/commands/telemetry/status.js.map +1 -0
  106. package/dist/server/admin.d.ts +79 -0
  107. package/dist/server/admin.js +239 -0
  108. package/dist/server/admin.js.map +1 -0
  109. package/dist/server/admin.test.d.ts +1 -0
  110. package/dist/server/admin.test.js +226 -0
  111. package/dist/server/admin.test.js.map +1 -0
  112. package/dist/server/analytics.d.ts +60 -0
  113. package/dist/server/analytics.js +168 -0
  114. package/dist/server/analytics.js.map +1 -0
  115. package/dist/server/analytics.test.d.ts +1 -0
  116. package/dist/server/analytics.test.js +179 -0
  117. package/dist/server/analytics.test.js.map +1 -0
  118. package/dist/server/asset-base-url-transform-plugin.d.ts +11 -0
  119. package/dist/server/asset-base-url-transform-plugin.js +48 -0
  120. package/dist/server/asset-base-url-transform-plugin.js.map +1 -0
  121. package/dist/server/asset-base-url-transform-plugin.test.d.ts +1 -0
  122. package/dist/server/asset-base-url-transform-plugin.test.js +134 -0
  123. package/dist/server/asset-base-url-transform-plugin.test.js.map +1 -0
  124. package/dist/server/auth.d.ts +20 -0
  125. package/dist/server/auth.js +28 -0
  126. package/dist/server/auth.js.map +1 -0
  127. package/dist/server/build-manifest.test.d.ts +1 -0
  128. package/dist/server/build-manifest.test.js +27 -0
  129. package/dist/server/build-manifest.test.js.map +1 -0
  130. package/dist/server/config/config.test.d.ts +1 -0
  131. package/dist/server/config/config.test.js +214 -0
  132. package/dist/server/config/config.test.js.map +1 -0
  133. package/dist/server/config/index.d.ts +3 -0
  134. package/dist/server/config/index.js +4 -0
  135. package/dist/server/config/index.js.map +1 -0
  136. package/dist/server/config/resolve.d.ts +73 -0
  137. package/dist/server/config/resolve.js +167 -0
  138. package/dist/server/config/resolve.js.map +1 -0
  139. package/dist/server/config/router.d.ts +23 -0
  140. package/dist/server/config/router.js +119 -0
  141. package/dist/server/config/router.js.map +1 -0
  142. package/dist/server/config/schema.d.ts +78 -0
  143. package/dist/server/config/schema.js +158 -0
  144. package/dist/server/config/schema.js.map +1 -0
  145. package/dist/server/content-helpers.d.ts +67 -0
  146. package/dist/server/content-helpers.js +79 -0
  147. package/dist/server/content-helpers.js.map +1 -0
  148. package/dist/server/content-helpers.test.d.ts +1 -0
  149. package/dist/server/content-helpers.test.js +70 -0
  150. package/dist/server/content-helpers.test.js.map +1 -0
  151. package/dist/server/express.d.ts +11 -0
  152. package/dist/server/express.js +129 -0
  153. package/dist/server/express.js.map +1 -0
  154. package/dist/server/express.test.d.ts +1 -0
  155. package/dist/server/express.test.js +464 -0
  156. package/dist/server/express.test.js.map +1 -0
  157. package/dist/server/file-ref.d.ts +28 -0
  158. package/dist/server/file-ref.js +27 -0
  159. package/dist/server/file-ref.js.map +1 -0
  160. package/dist/server/index.d.ts +17 -0
  161. package/dist/server/index.js +14 -0
  162. package/dist/server/index.js.map +1 -0
  163. package/dist/server/inferUtilityTypes.d.ts +64 -0
  164. package/dist/server/inferUtilityTypes.js +2 -0
  165. package/dist/server/inferUtilityTypes.js.map +1 -0
  166. package/dist/server/log-sink.d.ts +16 -0
  167. package/dist/server/log-sink.js +66 -0
  168. package/dist/server/log-sink.js.map +1 -0
  169. package/dist/server/metric.d.ts +12 -0
  170. package/dist/server/metric.js +13 -0
  171. package/dist/server/metric.js.map +1 -0
  172. package/dist/server/middleware.d.ts +137 -0
  173. package/dist/server/middleware.js +93 -0
  174. package/dist/server/middleware.js.map +1 -0
  175. package/dist/server/middleware.test-d.d.ts +1 -0
  176. package/dist/server/middleware.test-d.js +75 -0
  177. package/dist/server/middleware.test-d.js.map +1 -0
  178. package/dist/server/middleware.test.d.ts +1 -0
  179. package/dist/server/middleware.test.js +493 -0
  180. package/dist/server/middleware.test.js.map +1 -0
  181. package/dist/server/mock-seed.d.ts +62 -0
  182. package/dist/server/mock-seed.js +251 -0
  183. package/dist/server/mock-seed.js.map +1 -0
  184. package/dist/server/mock-seed.test.d.ts +1 -0
  185. package/dist/server/mock-seed.test.js +122 -0
  186. package/dist/server/mock-seed.test.js.map +1 -0
  187. package/dist/server/observability.d.ts +149 -0
  188. package/dist/server/observability.js +340 -0
  189. package/dist/server/observability.js.map +1 -0
  190. package/dist/server/observability.test.d.ts +1 -0
  191. package/dist/server/observability.test.js +251 -0
  192. package/dist/server/observability.test.js.map +1 -0
  193. package/dist/server/otel.d.ts +45 -0
  194. package/dist/server/otel.js +117 -0
  195. package/dist/server/otel.js.map +1 -0
  196. package/dist/server/otel.test.d.ts +1 -0
  197. package/dist/server/otel.test.js +122 -0
  198. package/dist/server/otel.test.js.map +1 -0
  199. package/dist/server/server.d.ts +422 -0
  200. package/dist/server/server.js +684 -0
  201. package/dist/server/server.js.map +1 -0
  202. package/dist/server/storage/index.d.ts +23 -0
  203. package/dist/server/storage/index.js +46 -0
  204. package/dist/server/storage/index.js.map +1 -0
  205. package/dist/server/storage/memory.d.ts +30 -0
  206. package/dist/server/storage/memory.js +98 -0
  207. package/dist/server/storage/memory.js.map +1 -0
  208. package/dist/server/storage/memory.test.d.ts +1 -0
  209. package/dist/server/storage/memory.test.js +81 -0
  210. package/dist/server/storage/memory.test.js.map +1 -0
  211. package/dist/server/storage/postgres.d.ts +65 -0
  212. package/dist/server/storage/postgres.js +242 -0
  213. package/dist/server/storage/postgres.js.map +1 -0
  214. package/dist/server/storage/postgres.test.d.ts +1 -0
  215. package/dist/server/storage/postgres.test.js +182 -0
  216. package/dist/server/storage/postgres.test.js.map +1 -0
  217. package/dist/server/storage/sqlite.d.ts +33 -0
  218. package/dist/server/storage/sqlite.js +250 -0
  219. package/dist/server/storage/sqlite.js.map +1 -0
  220. package/dist/server/storage/sqlite.test.d.ts +1 -0
  221. package/dist/server/storage/sqlite.test.js +133 -0
  222. package/dist/server/storage/sqlite.test.js.map +1 -0
  223. package/dist/server/storage/types.d.ts +119 -0
  224. package/dist/server/storage/types.js +11 -0
  225. package/dist/server/storage/types.js.map +1 -0
  226. package/dist/server/templateHelper.d.ts +16 -0
  227. package/dist/server/templateHelper.js +11 -0
  228. package/dist/server/templateHelper.js.map +1 -0
  229. package/dist/server/templates.generated.d.ts +4 -0
  230. package/dist/server/templates.generated.js +47 -0
  231. package/dist/server/templates.generated.js.map +1 -0
  232. package/dist/server/tunnel-proxy-router.d.ts +7 -0
  233. package/dist/server/tunnel-proxy-router.js +110 -0
  234. package/dist/server/tunnel-proxy-router.js.map +1 -0
  235. package/dist/server/tunnel-proxy-router.test.d.ts +1 -0
  236. package/dist/server/tunnel-proxy-router.test.js +229 -0
  237. package/dist/server/tunnel-proxy-router.test.js.map +1 -0
  238. package/dist/server/viewsDevServer.d.ts +14 -0
  239. package/dist/server/viewsDevServer.js +45 -0
  240. package/dist/server/viewsDevServer.js.map +1 -0
  241. package/dist/test/utils.d.ts +127 -0
  242. package/dist/test/utils.js +247 -0
  243. package/dist/test/utils.js.map +1 -0
  244. package/dist/test/view.test.d.ts +1 -0
  245. package/dist/test/view.test.js +568 -0
  246. package/dist/test/view.test.js.map +1 -0
  247. package/dist/version.d.ts +1 -0
  248. package/dist/version.js +3 -0
  249. package/dist/version.js.map +1 -0
  250. package/dist/web/bridges/apps-sdk/adaptor.d.ts +54 -0
  251. package/dist/web/bridges/apps-sdk/adaptor.js +164 -0
  252. package/dist/web/bridges/apps-sdk/adaptor.js.map +1 -0
  253. package/dist/web/bridges/apps-sdk/bridge.d.ts +11 -0
  254. package/dist/web/bridges/apps-sdk/bridge.js +47 -0
  255. package/dist/web/bridges/apps-sdk/bridge.js.map +1 -0
  256. package/dist/web/bridges/apps-sdk/index.d.ts +5 -0
  257. package/dist/web/bridges/apps-sdk/index.js +5 -0
  258. package/dist/web/bridges/apps-sdk/index.js.map +1 -0
  259. package/dist/web/bridges/apps-sdk/types.d.ts +147 -0
  260. package/dist/web/bridges/apps-sdk/types.js +10 -0
  261. package/dist/web/bridges/apps-sdk/types.js.map +1 -0
  262. package/dist/web/bridges/apps-sdk/use-apps-sdk-context.d.ts +13 -0
  263. package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js +18 -0
  264. package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js.map +1 -0
  265. package/dist/web/bridges/get-adaptor.d.ts +9 -0
  266. package/dist/web/bridges/get-adaptor.js +15 -0
  267. package/dist/web/bridges/get-adaptor.js.map +1 -0
  268. package/dist/web/bridges/index.d.ts +5 -0
  269. package/dist/web/bridges/index.js +6 -0
  270. package/dist/web/bridges/index.js.map +1 -0
  271. package/dist/web/bridges/mcp-app/adaptor.d.ts +81 -0
  272. package/dist/web/bridges/mcp-app/adaptor.js +346 -0
  273. package/dist/web/bridges/mcp-app/adaptor.js.map +1 -0
  274. package/dist/web/bridges/mcp-app/bridge.d.ts +28 -0
  275. package/dist/web/bridges/mcp-app/bridge.js +124 -0
  276. package/dist/web/bridges/mcp-app/bridge.js.map +1 -0
  277. package/dist/web/bridges/mcp-app/index.d.ts +4 -0
  278. package/dist/web/bridges/mcp-app/index.js +4 -0
  279. package/dist/web/bridges/mcp-app/index.js.map +1 -0
  280. package/dist/web/bridges/mcp-app/types.d.ts +8 -0
  281. package/dist/web/bridges/mcp-app/types.js +2 -0
  282. package/dist/web/bridges/mcp-app/types.js.map +1 -0
  283. package/dist/web/bridges/mcp-app/use-mcp-app-context.d.ts +19 -0
  284. package/dist/web/bridges/mcp-app/use-mcp-app-context.js +19 -0
  285. package/dist/web/bridges/mcp-app/use-mcp-app-context.js.map +1 -0
  286. package/dist/web/bridges/mcp-app/use-mcp-app-context.test.d.ts +1 -0
  287. package/dist/web/bridges/mcp-app/use-mcp-app-context.test.js +26 -0
  288. package/dist/web/bridges/mcp-app/use-mcp-app-context.test.js.map +1 -0
  289. package/dist/web/bridges/mcp-app/view-tools.test.d.ts +1 -0
  290. package/dist/web/bridges/mcp-app/view-tools.test.js +144 -0
  291. package/dist/web/bridges/mcp-app/view-tools.test.js.map +1 -0
  292. package/dist/web/bridges/types.d.ts +243 -0
  293. package/dist/web/bridges/types.js +2 -0
  294. package/dist/web/bridges/types.js.map +1 -0
  295. package/dist/web/bridges/use-host-context.d.ts +7 -0
  296. package/dist/web/bridges/use-host-context.js +13 -0
  297. package/dist/web/bridges/use-host-context.js.map +1 -0
  298. package/dist/web/components/modal-provider.d.ts +4 -0
  299. package/dist/web/components/modal-provider.js +45 -0
  300. package/dist/web/components/modal-provider.js.map +1 -0
  301. package/dist/web/create-store.d.ts +29 -0
  302. package/dist/web/create-store.js +64 -0
  303. package/dist/web/create-store.js.map +1 -0
  304. package/dist/web/create-store.test.d.ts +1 -0
  305. package/dist/web/create-store.test.js +129 -0
  306. package/dist/web/create-store.test.js.map +1 -0
  307. package/dist/web/data-llm.d.ts +47 -0
  308. package/dist/web/data-llm.js +100 -0
  309. package/dist/web/data-llm.js.map +1 -0
  310. package/dist/web/data-llm.test.d.ts +1 -0
  311. package/dist/web/data-llm.test.js +142 -0
  312. package/dist/web/data-llm.test.js.map +1 -0
  313. package/dist/web/generate-helpers.d.ts +120 -0
  314. package/dist/web/generate-helpers.js +115 -0
  315. package/dist/web/generate-helpers.js.map +1 -0
  316. package/dist/web/generate-helpers.test-d.d.ts +1 -0
  317. package/dist/web/generate-helpers.test-d.js +211 -0
  318. package/dist/web/generate-helpers.test-d.js.map +1 -0
  319. package/dist/web/generate-helpers.test.d.ts +1 -0
  320. package/dist/web/generate-helpers.test.js +17 -0
  321. package/dist/web/generate-helpers.test.js.map +1 -0
  322. package/dist/web/helpers/state.d.ts +7 -0
  323. package/dist/web/helpers/state.js +45 -0
  324. package/dist/web/helpers/state.js.map +1 -0
  325. package/dist/web/helpers/state.test.d.ts +1 -0
  326. package/dist/web/helpers/state.test.js +53 -0
  327. package/dist/web/helpers/state.test.js.map +1 -0
  328. package/dist/web/hooks/index.d.ts +17 -0
  329. package/dist/web/hooks/index.js +18 -0
  330. package/dist/web/hooks/index.js.map +1 -0
  331. package/dist/web/hooks/test/utils.d.ts +20 -0
  332. package/dist/web/hooks/test/utils.js +75 -0
  333. package/dist/web/hooks/test/utils.js.map +1 -0
  334. package/dist/web/hooks/use-call-tool.d.ts +146 -0
  335. package/dist/web/hooks/use-call-tool.js +96 -0
  336. package/dist/web/hooks/use-call-tool.js.map +1 -0
  337. package/dist/web/hooks/use-call-tool.test-d.d.ts +1 -0
  338. package/dist/web/hooks/use-call-tool.test-d.js +104 -0
  339. package/dist/web/hooks/use-call-tool.test-d.js.map +1 -0
  340. package/dist/web/hooks/use-call-tool.test.d.ts +1 -0
  341. package/dist/web/hooks/use-call-tool.test.js +211 -0
  342. package/dist/web/hooks/use-call-tool.test.js.map +1 -0
  343. package/dist/web/hooks/use-display-mode.d.ts +24 -0
  344. package/dist/web/hooks/use-display-mode.js +29 -0
  345. package/dist/web/hooks/use-display-mode.js.map +1 -0
  346. package/dist/web/hooks/use-display-mode.test-d.d.ts +1 -0
  347. package/dist/web/hooks/use-display-mode.test-d.js +8 -0
  348. package/dist/web/hooks/use-display-mode.test-d.js.map +1 -0
  349. package/dist/web/hooks/use-display-mode.test.d.ts +1 -0
  350. package/dist/web/hooks/use-display-mode.test.js +41 -0
  351. package/dist/web/hooks/use-display-mode.test.js.map +1 -0
  352. package/dist/web/hooks/use-download.d.ts +5 -0
  353. package/dist/web/hooks/use-download.js +8 -0
  354. package/dist/web/hooks/use-download.js.map +1 -0
  355. package/dist/web/hooks/use-download.test.d.ts +1 -0
  356. package/dist/web/hooks/use-download.test.js +95 -0
  357. package/dist/web/hooks/use-download.test.js.map +1 -0
  358. package/dist/web/hooks/use-files.d.ts +39 -0
  359. package/dist/web/hooks/use-files.js +42 -0
  360. package/dist/web/hooks/use-files.js.map +1 -0
  361. package/dist/web/hooks/use-files.test.d.ts +1 -0
  362. package/dist/web/hooks/use-files.test.js +54 -0
  363. package/dist/web/hooks/use-files.test.js.map +1 -0
  364. package/dist/web/hooks/use-intent.d.ts +30 -0
  365. package/dist/web/hooks/use-intent.js +34 -0
  366. package/dist/web/hooks/use-intent.js.map +1 -0
  367. package/dist/web/hooks/use-intent.test.d.ts +1 -0
  368. package/dist/web/hooks/use-intent.test.js +85 -0
  369. package/dist/web/hooks/use-intent.test.js.map +1 -0
  370. package/dist/web/hooks/use-layout.d.ts +24 -0
  371. package/dist/web/hooks/use-layout.js +25 -0
  372. package/dist/web/hooks/use-layout.js.map +1 -0
  373. package/dist/web/hooks/use-layout.test.d.ts +1 -0
  374. package/dist/web/hooks/use-layout.test.js +96 -0
  375. package/dist/web/hooks/use-layout.test.js.map +1 -0
  376. package/dist/web/hooks/use-notify.d.ts +29 -0
  377. package/dist/web/hooks/use-notify.js +33 -0
  378. package/dist/web/hooks/use-notify.js.map +1 -0
  379. package/dist/web/hooks/use-notify.test.d.ts +1 -0
  380. package/dist/web/hooks/use-notify.test.js +105 -0
  381. package/dist/web/hooks/use-notify.test.js.map +1 -0
  382. package/dist/web/hooks/use-open-external.d.ts +20 -0
  383. package/dist/web/hooks/use-open-external.js +24 -0
  384. package/dist/web/hooks/use-open-external.js.map +1 -0
  385. package/dist/web/hooks/use-open-external.test.d.ts +1 -0
  386. package/dist/web/hooks/use-open-external.test.js +65 -0
  387. package/dist/web/hooks/use-open-external.test.js.map +1 -0
  388. package/dist/web/hooks/use-register-view-tool.d.ts +38 -0
  389. package/dist/web/hooks/use-register-view-tool.js +50 -0
  390. package/dist/web/hooks/use-register-view-tool.js.map +1 -0
  391. package/dist/web/hooks/use-request-close.d.ts +16 -0
  392. package/dist/web/hooks/use-request-close.js +21 -0
  393. package/dist/web/hooks/use-request-close.js.map +1 -0
  394. package/dist/web/hooks/use-request-close.test.d.ts +1 -0
  395. package/dist/web/hooks/use-request-close.test.js +52 -0
  396. package/dist/web/hooks/use-request-close.test.js.map +1 -0
  397. package/dist/web/hooks/use-request-modal.d.ts +24 -0
  398. package/dist/web/hooks/use-request-modal.js +31 -0
  399. package/dist/web/hooks/use-request-modal.js.map +1 -0
  400. package/dist/web/hooks/use-request-modal.test.d.ts +1 -0
  401. package/dist/web/hooks/use-request-modal.test.js +61 -0
  402. package/dist/web/hooks/use-request-modal.test.js.map +1 -0
  403. package/dist/web/hooks/use-request-size.d.ts +20 -0
  404. package/dist/web/hooks/use-request-size.js +24 -0
  405. package/dist/web/hooks/use-request-size.js.map +1 -0
  406. package/dist/web/hooks/use-request-size.test.d.ts +1 -0
  407. package/dist/web/hooks/use-request-size.test.js +65 -0
  408. package/dist/web/hooks/use-request-size.test.js.map +1 -0
  409. package/dist/web/hooks/use-send-follow-up-message.d.ts +19 -0
  410. package/dist/web/hooks/use-send-follow-up-message.js +25 -0
  411. package/dist/web/hooks/use-send-follow-up-message.js.map +1 -0
  412. package/dist/web/hooks/use-set-open-in-app-url.d.ts +18 -0
  413. package/dist/web/hooks/use-set-open-in-app-url.js +25 -0
  414. package/dist/web/hooks/use-set-open-in-app-url.js.map +1 -0
  415. package/dist/web/hooks/use-set-open-in-app-url.test.d.ts +1 -0
  416. package/dist/web/hooks/use-set-open-in-app-url.test.js +43 -0
  417. package/dist/web/hooks/use-set-open-in-app-url.test.js.map +1 -0
  418. package/dist/web/hooks/use-tool-info.d.ts +87 -0
  419. package/dist/web/hooks/use-tool-info.js +49 -0
  420. package/dist/web/hooks/use-tool-info.js.map +1 -0
  421. package/dist/web/hooks/use-tool-info.test-d.d.ts +1 -0
  422. package/dist/web/hooks/use-tool-info.test-d.js +91 -0
  423. package/dist/web/hooks/use-tool-info.test-d.js.map +1 -0
  424. package/dist/web/hooks/use-tool-info.test.d.ts +1 -0
  425. package/dist/web/hooks/use-tool-info.test.js +130 -0
  426. package/dist/web/hooks/use-tool-info.test.js.map +1 -0
  427. package/dist/web/hooks/use-user.d.ts +20 -0
  428. package/dist/web/hooks/use-user.js +37 -0
  429. package/dist/web/hooks/use-user.js.map +1 -0
  430. package/dist/web/hooks/use-user.test.d.ts +1 -0
  431. package/dist/web/hooks/use-user.test.js +122 -0
  432. package/dist/web/hooks/use-user.test.js.map +1 -0
  433. package/dist/web/hooks/use-view-state.d.ts +25 -0
  434. package/dist/web/hooks/use-view-state.js +32 -0
  435. package/dist/web/hooks/use-view-state.js.map +1 -0
  436. package/dist/web/hooks/use-view-state.test.d.ts +1 -0
  437. package/dist/web/hooks/use-view-state.test.js +177 -0
  438. package/dist/web/hooks/use-view-state.test.js.map +1 -0
  439. package/dist/web/index.d.ts +7 -0
  440. package/dist/web/index.js +8 -0
  441. package/dist/web/index.js.map +1 -0
  442. package/dist/web/mount-view.d.ts +20 -0
  443. package/dist/web/mount-view.js +46 -0
  444. package/dist/web/mount-view.js.map +1 -0
  445. package/dist/web/plugin/data-llm.test.d.ts +1 -0
  446. package/dist/web/plugin/data-llm.test.js +81 -0
  447. package/dist/web/plugin/data-llm.test.js.map +1 -0
  448. package/dist/web/plugin/plugin.d.ts +33 -0
  449. package/dist/web/plugin/plugin.js +189 -0
  450. package/dist/web/plugin/plugin.js.map +1 -0
  451. package/dist/web/plugin/scan-views.d.ts +16 -0
  452. package/dist/web/plugin/scan-views.js +88 -0
  453. package/dist/web/plugin/scan-views.js.map +1 -0
  454. package/dist/web/plugin/scan-views.test.d.ts +1 -0
  455. package/dist/web/plugin/scan-views.test.js +99 -0
  456. package/dist/web/plugin/scan-views.test.js.map +1 -0
  457. package/dist/web/plugin/transform-data-llm.d.ts +12 -0
  458. package/dist/web/plugin/transform-data-llm.js +96 -0
  459. package/dist/web/plugin/transform-data-llm.js.map +1 -0
  460. package/dist/web/plugin/transform-data-llm.test.d.ts +1 -0
  461. package/dist/web/plugin/transform-data-llm.test.js +81 -0
  462. package/dist/web/plugin/transform-data-llm.test.js.map +1 -0
  463. package/dist/web/plugin/validate-view.d.ts +1 -0
  464. package/dist/web/plugin/validate-view.js +9 -0
  465. package/dist/web/plugin/validate-view.js.map +1 -0
  466. package/dist/web/plugin/validate-view.test.d.ts +1 -0
  467. package/dist/web/plugin/validate-view.test.js +24 -0
  468. package/dist/web/plugin/validate-view.test.js.map +1 -0
  469. package/dist/web/proxy.d.ts +1 -0
  470. package/dist/web/proxy.js +52 -0
  471. package/dist/web/proxy.js.map +1 -0
  472. package/dist/web/types.d.ts +20 -0
  473. package/dist/web/types.js +2 -0
  474. package/dist/web/types.js.map +1 -0
  475. package/package.json +125 -0
  476. package/scripts/postinstall.mjs +45 -0
  477. package/tsconfig.base.json +36 -0
@@ -0,0 +1,242 @@
1
+ /**
2
+ * Resolve the connection string from the environment. Returns `undefined` when
3
+ * none of the enpilink/standard URL vars are set — in that case `pg` falls back
4
+ * to its own `PG*` env-var handling. NEVER returns a hardcoded default.
5
+ */
6
+ export function resolvePostgresConnectionString() {
7
+ // Treat blank/whitespace-only values as unset so a blank var never shadows a
8
+ // real one (and never becomes a hardcoded-looking default).
9
+ const enpilink = process.env.ENPILINK_DB_URL?.trim();
10
+ if (enpilink) {
11
+ return enpilink;
12
+ }
13
+ const standard = process.env.DATABASE_URL?.trim();
14
+ return standard ? standard : undefined;
15
+ }
16
+ export class PostgresStorageAdapter {
17
+ injectedPool;
18
+ connectionString;
19
+ pool = null;
20
+ constructor(opts) {
21
+ this.injectedPool = opts?.pool;
22
+ this.connectionString =
23
+ opts?.connectionString ?? resolvePostgresConnectionString();
24
+ }
25
+ async init() {
26
+ if (this.pool) {
27
+ return;
28
+ }
29
+ if (this.injectedPool) {
30
+ this.pool = this.injectedPool;
31
+ }
32
+ else {
33
+ // Dynamic import keeps `pg` out of the load path until a postgres adapter
34
+ // is actually instantiated (memory/sqlite users pay nothing).
35
+ const mod = await import("pg");
36
+ const Pool = (mod.default?.Pool ??
37
+ mod
38
+ .Pool);
39
+ // When no connectionString is resolved, pass none so `pg` reads PG* vars.
40
+ this.pool = this.connectionString
41
+ ? new Pool({ connectionString: this.connectionString })
42
+ : new Pool();
43
+ }
44
+ await this.pool.query(SCHEMA);
45
+ }
46
+ require() {
47
+ if (!this.pool) {
48
+ throw new Error("PostgresStorageAdapter: call init() before use");
49
+ }
50
+ return this.pool;
51
+ }
52
+ async recordEvent(e) {
53
+ const pool = this.require();
54
+ await pool.query("INSERT INTO events (ts, type, tool, method, ms, ok, error, meta) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)", [
55
+ e.ts,
56
+ e.type,
57
+ e.tool ?? null,
58
+ e.method ?? null,
59
+ e.ms ?? null,
60
+ e.ok === undefined ? null : e.ok,
61
+ e.error ?? null,
62
+ e.meta === undefined ? null : JSON.stringify(e.meta),
63
+ ]);
64
+ }
65
+ async queryEvents(f = {}) {
66
+ const pool = this.require();
67
+ const where = [];
68
+ const params = [];
69
+ if (f.since !== undefined) {
70
+ params.push(f.since);
71
+ where.push(`ts >= $${params.length}`);
72
+ }
73
+ if (f.type !== undefined) {
74
+ params.push(f.type);
75
+ where.push(`type = $${params.length}`);
76
+ }
77
+ if (f.tool !== undefined) {
78
+ params.push(f.tool);
79
+ where.push(`tool = $${params.length}`);
80
+ }
81
+ const clause = where.length ? `WHERE ${where.join(" AND ")}` : "";
82
+ const limit = limitClause(f.limit, params);
83
+ const { rows } = await pool.query(`SELECT ts, type, tool, method, ms, ok, error, meta FROM events ${clause} ORDER BY id DESC ${limit}`, params);
84
+ return rows.map(rowToEvent);
85
+ }
86
+ async appendLog(l) {
87
+ const pool = this.require();
88
+ await pool.query("INSERT INTO logs (ts, level, msg, data) VALUES ($1, $2, $3, $4)", [
89
+ l.ts,
90
+ l.level,
91
+ l.msg,
92
+ l.data === undefined ? null : JSON.stringify(l.data),
93
+ ]);
94
+ }
95
+ async queryLogs(f = {}) {
96
+ const pool = this.require();
97
+ const where = [];
98
+ const params = [];
99
+ if (f.since !== undefined) {
100
+ params.push(f.since);
101
+ where.push(`ts >= $${params.length}`);
102
+ }
103
+ if (f.level !== undefined) {
104
+ params.push(f.level);
105
+ where.push(`level = $${params.length}`);
106
+ }
107
+ const clause = where.length ? `WHERE ${where.join(" AND ")}` : "";
108
+ const limit = limitClause(f.limit, params);
109
+ const { rows } = await pool.query(`SELECT ts, level, msg, data FROM logs ${clause} ORDER BY id DESC ${limit}`, params);
110
+ return rows.map(rowToLog);
111
+ }
112
+ async getConfig(key) {
113
+ const pool = this.require();
114
+ const { rows } = await pool.query("SELECT value FROM config WHERE key = $1", [key]);
115
+ const row = rows[0];
116
+ return row === undefined ? undefined : JSON.parse(row.value);
117
+ }
118
+ async setConfig(key, value, actor) {
119
+ const pool = this.require();
120
+ const serialized = JSON.stringify(value);
121
+ // Read-old → upsert-new → write-audit. pg-mem does not implement
122
+ // transactions identically across versions, so we read-then-write without a
123
+ // BEGIN/COMMIT wrapper; the audit row records the prior value either way.
124
+ const { rows } = await pool.query("SELECT value FROM config WHERE key = $1", [key]);
125
+ const oldSerialized = rows[0]?.value ?? null;
126
+ await pool.query("INSERT INTO config (key, value) VALUES ($1, $2) ON CONFLICT (key) DO UPDATE SET value = $2", [key, serialized]);
127
+ await pool.query("INSERT INTO config_audit (ts, key, old_value, new_value, actor) VALUES ($1, $2, $3, $4, $5)", [Date.now(), key, oldSerialized, serialized, actor ?? "system"]);
128
+ }
129
+ async allConfig() {
130
+ const pool = this.require();
131
+ const { rows } = await pool.query("SELECT key, value FROM config");
132
+ const out = {};
133
+ for (const r of rows) {
134
+ out[r.key] = JSON.parse(r.value);
135
+ }
136
+ return out;
137
+ }
138
+ async getConfigAudit() {
139
+ const pool = this.require();
140
+ const { rows } = await pool.query("SELECT ts, key, old_value, new_value, actor FROM config_audit ORDER BY id DESC");
141
+ return rows.map((r) => ({
142
+ ts: Number(r.ts),
143
+ key: r.key,
144
+ oldValue: r.old_value === null ? undefined : JSON.parse(r.old_value),
145
+ newValue: JSON.parse(r.new_value),
146
+ actor: r.actor,
147
+ }));
148
+ }
149
+ async close() {
150
+ if (this.pool) {
151
+ await this.pool.end();
152
+ this.pool = null;
153
+ }
154
+ }
155
+ }
156
+ function limitClause(limit, params) {
157
+ if (limit === undefined || limit < 0) {
158
+ return "";
159
+ }
160
+ params.push(limit);
161
+ return `LIMIT $${params.length}`;
162
+ }
163
+ function rowToEvent(r) {
164
+ const e = { ts: Number(r.ts), type: r.type };
165
+ if (r.tool !== null) {
166
+ e.tool = r.tool;
167
+ }
168
+ if (r.method !== null) {
169
+ e.method = r.method;
170
+ }
171
+ if (r.ms !== null) {
172
+ e.ms = Number(r.ms);
173
+ }
174
+ if (r.ok !== null) {
175
+ e.ok = r.ok;
176
+ }
177
+ if (r.error !== null) {
178
+ e.error = r.error;
179
+ }
180
+ if (r.meta !== null) {
181
+ e.meta = JSON.parse(r.meta);
182
+ }
183
+ return e;
184
+ }
185
+ function rowToLog(r) {
186
+ const l = {
187
+ ts: Number(r.ts),
188
+ level: r.level,
189
+ msg: r.msg,
190
+ };
191
+ if (r.data !== null) {
192
+ l.data = JSON.parse(r.data);
193
+ }
194
+ return l;
195
+ }
196
+ /**
197
+ * Schema. `BIGINT` for epoch-ms timestamps; `BIGSERIAL` ids give a stable
198
+ * insertion order for most-recent-first queries (`ORDER BY id DESC`). `meta` /
199
+ * `data` / config values are opaque JSON text.
200
+ */
201
+ const SCHEMA = `
202
+ CREATE TABLE IF NOT EXISTS events (
203
+ id BIGSERIAL PRIMARY KEY,
204
+ ts BIGINT NOT NULL,
205
+ type TEXT NOT NULL,
206
+ tool TEXT,
207
+ method TEXT,
208
+ ms BIGINT,
209
+ ok BOOLEAN,
210
+ error TEXT,
211
+ meta TEXT
212
+ );
213
+ CREATE INDEX IF NOT EXISTS idx_events_ts ON events (ts);
214
+ CREATE INDEX IF NOT EXISTS idx_events_type ON events (type);
215
+ CREATE INDEX IF NOT EXISTS idx_events_tool ON events (tool);
216
+
217
+ CREATE TABLE IF NOT EXISTS logs (
218
+ id BIGSERIAL PRIMARY KEY,
219
+ ts BIGINT NOT NULL,
220
+ level TEXT NOT NULL,
221
+ msg TEXT NOT NULL,
222
+ data TEXT
223
+ );
224
+ CREATE INDEX IF NOT EXISTS idx_logs_ts ON logs (ts);
225
+ CREATE INDEX IF NOT EXISTS idx_logs_level ON logs (level);
226
+
227
+ CREATE TABLE IF NOT EXISTS config (
228
+ key TEXT PRIMARY KEY,
229
+ value TEXT NOT NULL
230
+ );
231
+
232
+ CREATE TABLE IF NOT EXISTS config_audit (
233
+ id BIGSERIAL PRIMARY KEY,
234
+ ts BIGINT NOT NULL,
235
+ key TEXT NOT NULL,
236
+ old_value TEXT,
237
+ new_value TEXT NOT NULL,
238
+ actor TEXT NOT NULL
239
+ );
240
+ CREATE INDEX IF NOT EXISTS idx_audit_key ON config_audit (key);
241
+ `;
242
+ //# sourceMappingURL=postgres.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgres.js","sourceRoot":"","sources":["../../../src/server/storage/postgres.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n AnalyticsEvent,\n ConfigAuditEntry,\n EventQuery,\n LogEntry,\n LogQuery,\n StorageAdapter,\n StorageAdapterOptions,\n} from \"./types.js\";\n\n/**\n * PostgreSQL {@link StorageAdapter}, backed by `pg` (node-postgres).\n *\n * `pg` is pure-JS (no native build), so it installs cleanly in CI on both\n * macOS-arm64 and linux-x64. The connection is configured ENTIRELY from the\n * environment — never hardcoded:\n *\n * - `ENPILINK_DB_URL` (enpilink-specific) takes precedence, else\n * - `DATABASE_URL` (the de-facto standard), else\n * - the standard `PG*` vars (`PGHOST`/`PGPORT`/`PGUSER`/`PGPASSWORD`/`PGDATABASE`)\n * which `pg` reads on its own when no `connectionString` is given.\n *\n * Tables `events`, `logs`, `config`, `config_audit` are created-if-not-exists in\n * {@link init}. Config values are stored as opaque JSON text — no special-casing\n * of keys. Writes use prepared/parameterized statements; `recordEvent` /\n * `appendLog` are single cheap inserts so the ~600-write mock seed stays fast.\n */\n\n/** Minimal structural type for a `pg` query result (avoids a hard type dep). */\ninterface PgQueryResult<R = Record<string, unknown>> {\n rows: R[];\n}\n\n/** Minimal structural type for a `pg` Pool — enough for this adapter. */\nexport interface PgPoolLike {\n query<R = Record<string, unknown>>(\n text: string,\n values?: unknown[],\n ): Promise<PgQueryResult<R>>;\n end(): Promise<void>;\n}\n\n/** Options specific to the postgres adapter. */\nexport interface PostgresStorageOptions extends StorageAdapterOptions {\n /**\n * Inject a `pg`-compatible Pool (used by tests with `pg-mem`). When omitted,\n * a real `pg.Pool` is constructed lazily in {@link init} from the environment.\n */\n pool?: PgPoolLike;\n /**\n * Explicit connection string. Normally left undefined so the connection is\n * resolved from `ENPILINK_DB_URL` / `DATABASE_URL` / `PG*` env vars. Never\n * hardcode a connection string in source.\n */\n connectionString?: string;\n}\n\n/**\n * Resolve the connection string from the environment. Returns `undefined` when\n * none of the enpilink/standard URL vars are set — in that case `pg` falls back\n * to its own `PG*` env-var handling. NEVER returns a hardcoded default.\n */\nexport function resolvePostgresConnectionString(): string | undefined {\n // Treat blank/whitespace-only values as unset so a blank var never shadows a\n // real one (and never becomes a hardcoded-looking default).\n const enpilink = process.env.ENPILINK_DB_URL?.trim();\n if (enpilink) {\n return enpilink;\n }\n const standard = process.env.DATABASE_URL?.trim();\n return standard ? standard : undefined;\n}\n\nexport class PostgresStorageAdapter implements StorageAdapter {\n private readonly injectedPool?: PgPoolLike;\n private readonly connectionString?: string;\n private pool: PgPoolLike | null = null;\n\n constructor(opts?: PostgresStorageOptions) {\n this.injectedPool = opts?.pool;\n this.connectionString =\n opts?.connectionString ?? resolvePostgresConnectionString();\n }\n\n async init(): Promise<void> {\n if (this.pool) {\n return;\n }\n if (this.injectedPool) {\n this.pool = this.injectedPool;\n } else {\n // Dynamic import keeps `pg` out of the load path until a postgres adapter\n // is actually instantiated (memory/sqlite users pay nothing).\n const mod = await import(\"pg\");\n const Pool = (mod.default?.Pool ??\n (mod as unknown as { Pool: new (cfg?: unknown) => PgPoolLike })\n .Pool) as new (\n cfg?: unknown,\n ) => PgPoolLike;\n // When no connectionString is resolved, pass none so `pg` reads PG* vars.\n this.pool = this.connectionString\n ? new Pool({ connectionString: this.connectionString })\n : new Pool();\n }\n await this.pool.query(SCHEMA);\n }\n\n private require(): PgPoolLike {\n if (!this.pool) {\n throw new Error(\"PostgresStorageAdapter: call init() before use\");\n }\n return this.pool;\n }\n\n async recordEvent(e: AnalyticsEvent): Promise<void> {\n const pool = this.require();\n await pool.query(\n \"INSERT INTO events (ts, type, tool, method, ms, ok, error, meta) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)\",\n [\n e.ts,\n e.type,\n e.tool ?? null,\n e.method ?? null,\n e.ms ?? null,\n e.ok === undefined ? null : e.ok,\n e.error ?? null,\n e.meta === undefined ? null : JSON.stringify(e.meta),\n ],\n );\n }\n\n async queryEvents(f: EventQuery = {}): Promise<AnalyticsEvent[]> {\n const pool = this.require();\n const where: string[] = [];\n const params: unknown[] = [];\n if (f.since !== undefined) {\n params.push(f.since);\n where.push(`ts >= $${params.length}`);\n }\n if (f.type !== undefined) {\n params.push(f.type);\n where.push(`type = $${params.length}`);\n }\n if (f.tool !== undefined) {\n params.push(f.tool);\n where.push(`tool = $${params.length}`);\n }\n const clause = where.length ? `WHERE ${where.join(\" AND \")}` : \"\";\n const limit = limitClause(f.limit, params);\n const { rows } = await pool.query<EventRow>(\n `SELECT ts, type, tool, method, ms, ok, error, meta FROM events ${clause} ORDER BY id DESC ${limit}`,\n params,\n );\n return rows.map(rowToEvent);\n }\n\n async appendLog(l: LogEntry): Promise<void> {\n const pool = this.require();\n await pool.query(\n \"INSERT INTO logs (ts, level, msg, data) VALUES ($1, $2, $3, $4)\",\n [\n l.ts,\n l.level,\n l.msg,\n l.data === undefined ? null : JSON.stringify(l.data),\n ],\n );\n }\n\n async queryLogs(f: LogQuery = {}): Promise<LogEntry[]> {\n const pool = this.require();\n const where: string[] = [];\n const params: unknown[] = [];\n if (f.since !== undefined) {\n params.push(f.since);\n where.push(`ts >= $${params.length}`);\n }\n if (f.level !== undefined) {\n params.push(f.level);\n where.push(`level = $${params.length}`);\n }\n const clause = where.length ? `WHERE ${where.join(\" AND \")}` : \"\";\n const limit = limitClause(f.limit, params);\n const { rows } = await pool.query<LogRow>(\n `SELECT ts, level, msg, data FROM logs ${clause} ORDER BY id DESC ${limit}`,\n params,\n );\n return rows.map(rowToLog);\n }\n\n async getConfig(key: string): Promise<unknown> {\n const pool = this.require();\n const { rows } = await pool.query<{ value: string }>(\n \"SELECT value FROM config WHERE key = $1\",\n [key],\n );\n const row = rows[0];\n return row === undefined ? undefined : JSON.parse(row.value);\n }\n\n async setConfig(key: string, value: unknown, actor?: string): Promise<void> {\n const pool = this.require();\n const serialized = JSON.stringify(value);\n // Read-old → upsert-new → write-audit. pg-mem does not implement\n // transactions identically across versions, so we read-then-write without a\n // BEGIN/COMMIT wrapper; the audit row records the prior value either way.\n const { rows } = await pool.query<{ value: string }>(\n \"SELECT value FROM config WHERE key = $1\",\n [key],\n );\n const oldSerialized = rows[0]?.value ?? null;\n await pool.query(\n \"INSERT INTO config (key, value) VALUES ($1, $2) ON CONFLICT (key) DO UPDATE SET value = $2\",\n [key, serialized],\n );\n await pool.query(\n \"INSERT INTO config_audit (ts, key, old_value, new_value, actor) VALUES ($1, $2, $3, $4, $5)\",\n [Date.now(), key, oldSerialized, serialized, actor ?? \"system\"],\n );\n }\n\n async allConfig(): Promise<Record<string, unknown>> {\n const pool = this.require();\n const { rows } = await pool.query<{ key: string; value: string }>(\n \"SELECT key, value FROM config\",\n );\n const out: Record<string, unknown> = {};\n for (const r of rows) {\n out[r.key] = JSON.parse(r.value);\n }\n return out;\n }\n\n async getConfigAudit(): Promise<ConfigAuditEntry[]> {\n const pool = this.require();\n const { rows } = await pool.query<AuditRow>(\n \"SELECT ts, key, old_value, new_value, actor FROM config_audit ORDER BY id DESC\",\n );\n return rows.map((r) => ({\n ts: Number(r.ts),\n key: r.key,\n oldValue: r.old_value === null ? undefined : JSON.parse(r.old_value),\n newValue: JSON.parse(r.new_value),\n actor: r.actor,\n }));\n }\n\n async close(): Promise<void> {\n if (this.pool) {\n await this.pool.end();\n this.pool = null;\n }\n }\n}\n\nfunction limitClause(limit: number | undefined, params: unknown[]): string {\n if (limit === undefined || limit < 0) {\n return \"\";\n }\n params.push(limit);\n return `LIMIT $${params.length}`;\n}\n\ninterface EventRow {\n ts: number | string;\n type: string;\n tool: string | null;\n method: string | null;\n ms: number | string | null;\n ok: boolean | null;\n error: string | null;\n meta: string | null;\n}\n\ninterface LogRow {\n ts: number | string;\n level: string;\n msg: string;\n data: string | null;\n}\n\ninterface AuditRow {\n ts: number | string;\n key: string;\n old_value: string | null;\n new_value: string;\n actor: string;\n}\n\nfunction rowToEvent(r: EventRow): AnalyticsEvent {\n const e: AnalyticsEvent = { ts: Number(r.ts), type: r.type };\n if (r.tool !== null) {\n e.tool = r.tool;\n }\n if (r.method !== null) {\n e.method = r.method;\n }\n if (r.ms !== null) {\n e.ms = Number(r.ms);\n }\n if (r.ok !== null) {\n e.ok = r.ok;\n }\n if (r.error !== null) {\n e.error = r.error;\n }\n if (r.meta !== null) {\n e.meta = JSON.parse(r.meta);\n }\n return e;\n}\n\nfunction rowToLog(r: LogRow): LogEntry {\n const l: LogEntry = {\n ts: Number(r.ts),\n level: r.level as LogEntry[\"level\"],\n msg: r.msg,\n };\n if (r.data !== null) {\n l.data = JSON.parse(r.data);\n }\n return l;\n}\n\n/**\n * Schema. `BIGINT` for epoch-ms timestamps; `BIGSERIAL` ids give a stable\n * insertion order for most-recent-first queries (`ORDER BY id DESC`). `meta` /\n * `data` / config values are opaque JSON text.\n */\nconst SCHEMA = `\nCREATE TABLE IF NOT EXISTS events (\n id BIGSERIAL PRIMARY KEY,\n ts BIGINT NOT NULL,\n type TEXT NOT NULL,\n tool TEXT,\n method TEXT,\n ms BIGINT,\n ok BOOLEAN,\n error TEXT,\n meta TEXT\n);\nCREATE INDEX IF NOT EXISTS idx_events_ts ON events (ts);\nCREATE INDEX IF NOT EXISTS idx_events_type ON events (type);\nCREATE INDEX IF NOT EXISTS idx_events_tool ON events (tool);\n\nCREATE TABLE IF NOT EXISTS logs (\n id BIGSERIAL PRIMARY KEY,\n ts BIGINT NOT NULL,\n level TEXT NOT NULL,\n msg TEXT NOT NULL,\n data TEXT\n);\nCREATE INDEX IF NOT EXISTS idx_logs_ts ON logs (ts);\nCREATE INDEX IF NOT EXISTS idx_logs_level ON logs (level);\n\nCREATE TABLE IF NOT EXISTS config (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n);\n\nCREATE TABLE IF NOT EXISTS config_audit (\n id BIGSERIAL PRIMARY KEY,\n ts BIGINT NOT NULL,\n key TEXT NOT NULL,\n old_value TEXT,\n new_value TEXT NOT NULL,\n actor TEXT NOT NULL\n);\nCREATE INDEX IF NOT EXISTS idx_audit_key ON config_audit (key);\n`;\n"]}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,182 @@
1
+ import { newDb } from "pg-mem";
2
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
3
+ import { PostgresStorageAdapter, resolvePostgresConnectionString, } from "./postgres.js";
4
+ /**
5
+ * Tests run against `pg-mem` (pure-JS in-memory Postgres). pg-mem's
6
+ * `createPg().Pool` is a drop-in `pg.Pool`, so the adapter exercises the SAME
7
+ * code paths it would against a real Postgres — only the engine differs. We
8
+ * inject the pool via `opts.pool` so no real connection / env is touched.
9
+ *
10
+ * Each test gets a fresh `newDb()` so state is isolated and deterministic.
11
+ */
12
+ function makePool() {
13
+ const db = newDb();
14
+ const { Pool } = db.adapters.createPg();
15
+ return new Pool();
16
+ }
17
+ describe("PostgresStorageAdapter (pg-mem)", () => {
18
+ let store;
19
+ beforeEach(async () => {
20
+ store = new PostgresStorageAdapter({ pool: makePool() });
21
+ await store.init();
22
+ });
23
+ afterEach(async () => {
24
+ await store.close();
25
+ });
26
+ describe("events", () => {
27
+ it("records and queries events with since/tool/type/limit filters", async () => {
28
+ await store.recordEvent({
29
+ ts: 1,
30
+ type: "tool_call",
31
+ tool: "a",
32
+ ms: 5,
33
+ ok: true,
34
+ });
35
+ await store.recordEvent({
36
+ ts: 5,
37
+ type: "tool_call",
38
+ tool: "a",
39
+ ms: 9,
40
+ ok: false,
41
+ error: "boom",
42
+ });
43
+ await store.recordEvent({ ts: 10, type: "ping", tool: "b" });
44
+ const all = await store.queryEvents({});
45
+ expect(all.map((e) => e.ts)).toEqual([10, 5, 1]);
46
+ expect(await store.queryEvents({ since: 5 })).toHaveLength(2);
47
+ expect(await store.queryEvents({ tool: "a" })).toHaveLength(2);
48
+ expect(await store.queryEvents({ type: "ping" })).toHaveLength(1);
49
+ expect(await store.queryEvents({ limit: 1 })).toHaveLength(1);
50
+ const errored = (await store.queryEvents({ since: 5, tool: "a" }))[0];
51
+ expect(errored).toMatchObject({ ok: false, error: "boom", ms: 9 });
52
+ });
53
+ it("round-trips the meta object", async () => {
54
+ await store.recordEvent({
55
+ ts: 1,
56
+ type: "tool_call",
57
+ meta: { user: "x", nested: { n: 1 } },
58
+ });
59
+ const [e] = await store.queryEvents({});
60
+ expect(e?.meta).toEqual({ user: "x", nested: { n: 1 } });
61
+ });
62
+ });
63
+ describe("logs", () => {
64
+ it("appends and queries logs with level/limit/since", async () => {
65
+ await store.appendLog({ ts: 1, level: "info", msg: "one" });
66
+ await store.appendLog({
67
+ ts: 2,
68
+ level: "error",
69
+ msg: "two",
70
+ data: { code: 42 },
71
+ });
72
+ await store.appendLog({ ts: 3, level: "info", msg: "three" });
73
+ const all = await store.queryLogs({});
74
+ expect(all.map((l) => l.msg)).toEqual(["three", "two", "one"]);
75
+ expect(await store.queryLogs({ level: "error" })).toHaveLength(1);
76
+ expect(await store.queryLogs({ since: 2 })).toHaveLength(2);
77
+ expect(await store.queryLogs({ limit: 1 })).toHaveLength(1);
78
+ expect((await store.queryLogs({ level: "error" }))[0]?.data).toEqual({
79
+ code: 42,
80
+ });
81
+ });
82
+ });
83
+ describe("config + audit", () => {
84
+ it("get/set/all round-trips opaque values", async () => {
85
+ expect(await store.getConfig("missing")).toBeUndefined();
86
+ await store.setConfig("analytics.enabled", true);
87
+ await store.setConfig("retention", { days: 7 });
88
+ expect(await store.getConfig("analytics.enabled")).toBe(true);
89
+ expect(await store.getConfig("retention")).toEqual({ days: 7 });
90
+ expect(await store.allConfig()).toEqual({
91
+ "analytics.enabled": true,
92
+ retention: { days: 7 },
93
+ });
94
+ });
95
+ it("upserts on repeated setConfig (no duplicate key)", async () => {
96
+ await store.setConfig("k", "v1");
97
+ await store.setConfig("k", "v2");
98
+ expect(await store.getConfig("k")).toBe("v2");
99
+ expect(Object.keys(await store.allConfig())).toEqual(["k"]);
100
+ });
101
+ it("writes a config_audit row on setConfig with old → new + actor (most recent first)", async () => {
102
+ await store.setConfig("k", "v1");
103
+ await store.setConfig("k", "v2", "alice");
104
+ const audit = await store.getConfigAudit();
105
+ expect(audit).toHaveLength(2);
106
+ expect(audit[0]).toMatchObject({
107
+ key: "k",
108
+ oldValue: "v1",
109
+ newValue: "v2",
110
+ actor: "alice",
111
+ });
112
+ expect(audit[1]).toMatchObject({
113
+ key: "k",
114
+ oldValue: undefined,
115
+ newValue: "v1",
116
+ actor: "system",
117
+ });
118
+ });
119
+ });
120
+ describe("persistence within a session", () => {
121
+ it("retains writes across queries on the same pool", async () => {
122
+ await store.recordEvent({ ts: 1, type: "tool_call", tool: "persisted" });
123
+ await store.appendLog({ ts: 1, level: "warning", msg: "stays" });
124
+ await store.setConfig("flag", { on: true }, "bob");
125
+ const events = await store.queryEvents({});
126
+ expect(events).toHaveLength(1);
127
+ expect(events[0]?.tool).toBe("persisted");
128
+ const logs = await store.queryLogs({});
129
+ expect(logs[0]?.msg).toBe("stays");
130
+ expect(await store.getConfig("flag")).toEqual({ on: true });
131
+ const audit = await store.getConfigAudit();
132
+ expect(audit).toHaveLength(1);
133
+ expect(audit[0]).toMatchObject({ actor: "bob" });
134
+ });
135
+ });
136
+ describe("init is idempotent", () => {
137
+ it("can be called twice without error", async () => {
138
+ await store.init();
139
+ await store.recordEvent({ ts: 1, type: "tool_call" });
140
+ expect(await store.queryEvents({})).toHaveLength(1);
141
+ });
142
+ });
143
+ describe("use-before-init guard", () => {
144
+ it("throws when querying before init", async () => {
145
+ const s = new PostgresStorageAdapter({ pool: makePool() });
146
+ await expect(s.queryEvents({})).rejects.toThrow(/init\(\) before use/);
147
+ });
148
+ });
149
+ });
150
+ describe("resolvePostgresConnectionString", () => {
151
+ const saved = {
152
+ url: process.env.ENPILINK_DB_URL,
153
+ db: process.env.DATABASE_URL,
154
+ };
155
+ afterEach(() => {
156
+ if (saved.url === undefined) {
157
+ delete process.env.ENPILINK_DB_URL;
158
+ }
159
+ else {
160
+ process.env.ENPILINK_DB_URL = saved.url;
161
+ }
162
+ if (saved.db === undefined) {
163
+ delete process.env.DATABASE_URL;
164
+ }
165
+ else {
166
+ process.env.DATABASE_URL = saved.db;
167
+ }
168
+ });
169
+ it("prefers ENPILINK_DB_URL, falls back to DATABASE_URL, else undefined", () => {
170
+ delete process.env.ENPILINK_DB_URL;
171
+ delete process.env.DATABASE_URL;
172
+ expect(resolvePostgresConnectionString()).toBeUndefined();
173
+ process.env.DATABASE_URL = "postgres://db/standard";
174
+ expect(resolvePostgresConnectionString()).toBe("postgres://db/standard");
175
+ process.env.ENPILINK_DB_URL = "postgres://db/enpilink";
176
+ expect(resolvePostgresConnectionString()).toBe("postgres://db/enpilink");
177
+ // Whitespace-only is treated as unset (no hardcoded default ever).
178
+ process.env.ENPILINK_DB_URL = " ";
179
+ expect(resolvePostgresConnectionString()).toBe("postgres://db/standard");
180
+ });
181
+ });
182
+ //# sourceMappingURL=postgres.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgres.test.js","sourceRoot":"","sources":["../../../src/server/storage/postgres.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAC/B,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACrE,OAAO,EAEL,sBAAsB,EACtB,+BAA+B,GAChC,MAAM,eAAe,CAAC;AAEvB;;;;;;;GAOG;AACH,SAAS,QAAQ;IACf,MAAM,EAAE,GAAG,KAAK,EAAE,CAAC;IACnB,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC;IACxC,OAAO,IAAI,IAAI,EAA2B,CAAC;AAC7C,CAAC;AAED,QAAQ,CAAC,iCAAiC,EAAE,GAAG,EAAE;IAC/C,IAAI,KAA6B,CAAC;IAElC,UAAU,CAAC,KAAK,IAAI,EAAE;QACpB,KAAK,GAAG,IAAI,sBAAsB,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;QACzD,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC;IACrB,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,KAAK,IAAI,EAAE;QACnB,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC;IACtB,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE;QACtB,EAAE,CAAC,+DAA+D,EAAE,KAAK,IAAI,EAAE;YAC7E,MAAM,KAAK,CAAC,WAAW,CAAC;gBACtB,EAAE,EAAE,CAAC;gBACL,IAAI,EAAE,WAAW;gBACjB,IAAI,EAAE,GAAG;gBACT,EAAE,EAAE,CAAC;gBACL,EAAE,EAAE,IAAI;aACT,CAAC,CAAC;YACH,MAAM,KAAK,CAAC,WAAW,CAAC;gBACtB,EAAE,EAAE,CAAC;gBACL,IAAI,EAAE,WAAW;gBACjB,IAAI,EAAE,GAAG;gBACT,EAAE,EAAE,CAAC;gBACL,EAAE,EAAE,KAAK;gBACT,KAAK,EAAE,MAAM;aACd,CAAC,CAAC;YACH,MAAM,KAAK,CAAC,WAAW,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;YAE7D,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;YACxC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YACjD,MAAM,CAAC,MAAM,KAAK,CAAC,WAAW,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAC9D,MAAM,CAAC,MAAM,KAAK,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAC/D,MAAM,CAAC,MAAM,KAAK,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAClE,MAAM,CAAC,MAAM,KAAK,CAAC,WAAW,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAE9D,MAAM,OAAO,GAAG,CAAC,MAAM,KAAK,CAAC,WAAW,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACtE,MAAM,CAAC,OAAO,CAAC,CAAC,aAAa,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;QACrE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,6BAA6B,EAAE,KAAK,IAAI,EAAE;YAC3C,MAAM,KAAK,CAAC,WAAW,CAAC;gBACtB,EAAE,EAAE,CAAC;gBACL,IAAI,EAAE,WAAW;gBACjB,IAAI,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE;aACtC,CAAC,CAAC;YACH,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,KAAK,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;YACxC,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QAC3D,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,MAAM,EAAE,GAAG,EAAE;QACpB,EAAE,CAAC,iDAAiD,EAAE,KAAK,IAAI,EAAE;YAC/D,MAAM,KAAK,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;YAC5D,MAAM,KAAK,CAAC,SAAS,CAAC;gBACpB,EAAE,EAAE,CAAC;gBACL,KAAK,EAAE,OAAO;gBACd,GAAG,EAAE,KAAK;gBACV,IAAI,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;aACnB,CAAC,CAAC;YACH,MAAM,KAAK,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;YAE9D,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;YACtC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;YAC/D,MAAM,CAAC,MAAM,KAAK,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAClE,MAAM,CAAC,MAAM,KAAK,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAC5D,MAAM,CAAC,MAAM,KAAK,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAC5D,MAAM,CAAC,CAAC,MAAM,KAAK,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC;gBACnE,IAAI,EAAE,EAAE;aACT,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;QAC9B,EAAE,CAAC,uCAAuC,EAAE,KAAK,IAAI,EAAE;YACrD,MAAM,CAAC,MAAM,KAAK,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;YACzD,MAAM,KAAK,CAAC,SAAS,CAAC,mBAAmB,EAAE,IAAI,CAAC,CAAC;YACjD,MAAM,KAAK,CAAC,SAAS,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;YAChD,MAAM,CAAC,MAAM,KAAK,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC9D,MAAM,CAAC,MAAM,KAAK,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;YAChE,MAAM,CAAC,MAAM,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC,OAAO,CAAC;gBACtC,mBAAmB,EAAE,IAAI;gBACzB,SAAS,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE;aACvB,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,kDAAkD,EAAE,KAAK,IAAI,EAAE;YAChE,MAAM,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;YACjC,MAAM,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;YACjC,MAAM,CAAC,MAAM,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC9C,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAC9D,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,mFAAmF,EAAE,KAAK,IAAI,EAAE;YACjG,MAAM,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;YACjC,MAAM,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;YAC1C,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,cAAc,EAAE,CAAC;YAC3C,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAC9B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC;gBAC7B,GAAG,EAAE,GAAG;gBACR,QAAQ,EAAE,IAAI;gBACd,QAAQ,EAAE,IAAI;gBACd,KAAK,EAAE,OAAO;aACf,CAAC,CAAC;YACH,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC;gBAC7B,GAAG,EAAE,GAAG;gBACR,QAAQ,EAAE,SAAS;gBACnB,QAAQ,EAAE,IAAI;gBACd,KAAK,EAAE,QAAQ;aAChB,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,8BAA8B,EAAE,GAAG,EAAE;QAC5C,EAAE,CAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;YAC9D,MAAM,KAAK,CAAC,WAAW,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC;YACzE,MAAM,KAAK,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;YACjE,MAAM,KAAK,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,KAAK,CAAC,CAAC;YAEnD,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;YAC3C,MAAM,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAC/B,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YAE1C,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;YACvC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAEnC,MAAM,CAAC,MAAM,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;YAC5D,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,cAAc,EAAE,CAAC;YAC3C,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAC9B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;QACnD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;QAClC,EAAE,CAAC,mCAAmC,EAAE,KAAK,IAAI,EAAE;YACjD,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC;YACnB,MAAM,KAAK,CAAC,WAAW,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC;YACtD,MAAM,CAAC,MAAM,KAAK,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACtD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;QACrC,EAAE,CAAC,kCAAkC,EAAE,KAAK,IAAI,EAAE;YAChD,MAAM,CAAC,GAAG,IAAI,sBAAsB,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;YAC3D,MAAM,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC;QACzE,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,iCAAiC,EAAE,GAAG,EAAE;IAC/C,MAAM,KAAK,GAAG;QACZ,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe;QAChC,EAAE,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY;KAC7B,CAAC;IACF,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;YAC5B,OAAO,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;QACrC,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,eAAe,GAAG,KAAK,CAAC,GAAG,CAAC;QAC1C,CAAC;QACD,IAAI,KAAK,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;YAC3B,OAAO,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;QAClC,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,YAAY,GAAG,KAAK,CAAC,EAAE,CAAC;QACtC,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qEAAqE,EAAE,GAAG,EAAE;QAC7E,OAAO,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;QACnC,OAAO,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;QAChC,MAAM,CAAC,+BAA+B,EAAE,CAAC,CAAC,aAAa,EAAE,CAAC;QAE1D,OAAO,CAAC,GAAG,CAAC,YAAY,GAAG,wBAAwB,CAAC;QACpD,MAAM,CAAC,+BAA+B,EAAE,CAAC,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;QAEzE,OAAO,CAAC,GAAG,CAAC,eAAe,GAAG,wBAAwB,CAAC;QACvD,MAAM,CAAC,+BAA+B,EAAE,CAAC,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;QAEzE,mEAAmE;QACnE,OAAO,CAAC,GAAG,CAAC,eAAe,GAAG,KAAK,CAAC;QACpC,MAAM,CAAC,+BAA+B,EAAE,CAAC,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;IAC3E,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { newDb } from \"pg-mem\";\nimport { afterEach, beforeEach, describe, expect, it } from \"vitest\";\nimport {\n type PgPoolLike,\n PostgresStorageAdapter,\n resolvePostgresConnectionString,\n} from \"./postgres.js\";\n\n/**\n * Tests run against `pg-mem` (pure-JS in-memory Postgres). pg-mem's\n * `createPg().Pool` is a drop-in `pg.Pool`, so the adapter exercises the SAME\n * code paths it would against a real Postgres — only the engine differs. We\n * inject the pool via `opts.pool` so no real connection / env is touched.\n *\n * Each test gets a fresh `newDb()` so state is isolated and deterministic.\n */\nfunction makePool(): PgPoolLike {\n const db = newDb();\n const { Pool } = db.adapters.createPg();\n return new Pool() as unknown as PgPoolLike;\n}\n\ndescribe(\"PostgresStorageAdapter (pg-mem)\", () => {\n let store: PostgresStorageAdapter;\n\n beforeEach(async () => {\n store = new PostgresStorageAdapter({ pool: makePool() });\n await store.init();\n });\n\n afterEach(async () => {\n await store.close();\n });\n\n describe(\"events\", () => {\n it(\"records and queries events with since/tool/type/limit filters\", async () => {\n await store.recordEvent({\n ts: 1,\n type: \"tool_call\",\n tool: \"a\",\n ms: 5,\n ok: true,\n });\n await store.recordEvent({\n ts: 5,\n type: \"tool_call\",\n tool: \"a\",\n ms: 9,\n ok: false,\n error: \"boom\",\n });\n await store.recordEvent({ ts: 10, type: \"ping\", tool: \"b\" });\n\n const all = await store.queryEvents({});\n expect(all.map((e) => e.ts)).toEqual([10, 5, 1]);\n expect(await store.queryEvents({ since: 5 })).toHaveLength(2);\n expect(await store.queryEvents({ tool: \"a\" })).toHaveLength(2);\n expect(await store.queryEvents({ type: \"ping\" })).toHaveLength(1);\n expect(await store.queryEvents({ limit: 1 })).toHaveLength(1);\n\n const errored = (await store.queryEvents({ since: 5, tool: \"a\" }))[0];\n expect(errored).toMatchObject({ ok: false, error: \"boom\", ms: 9 });\n });\n\n it(\"round-trips the meta object\", async () => {\n await store.recordEvent({\n ts: 1,\n type: \"tool_call\",\n meta: { user: \"x\", nested: { n: 1 } },\n });\n const [e] = await store.queryEvents({});\n expect(e?.meta).toEqual({ user: \"x\", nested: { n: 1 } });\n });\n });\n\n describe(\"logs\", () => {\n it(\"appends and queries logs with level/limit/since\", async () => {\n await store.appendLog({ ts: 1, level: \"info\", msg: \"one\" });\n await store.appendLog({\n ts: 2,\n level: \"error\",\n msg: \"two\",\n data: { code: 42 },\n });\n await store.appendLog({ ts: 3, level: \"info\", msg: \"three\" });\n\n const all = await store.queryLogs({});\n expect(all.map((l) => l.msg)).toEqual([\"three\", \"two\", \"one\"]);\n expect(await store.queryLogs({ level: \"error\" })).toHaveLength(1);\n expect(await store.queryLogs({ since: 2 })).toHaveLength(2);\n expect(await store.queryLogs({ limit: 1 })).toHaveLength(1);\n expect((await store.queryLogs({ level: \"error\" }))[0]?.data).toEqual({\n code: 42,\n });\n });\n });\n\n describe(\"config + audit\", () => {\n it(\"get/set/all round-trips opaque values\", async () => {\n expect(await store.getConfig(\"missing\")).toBeUndefined();\n await store.setConfig(\"analytics.enabled\", true);\n await store.setConfig(\"retention\", { days: 7 });\n expect(await store.getConfig(\"analytics.enabled\")).toBe(true);\n expect(await store.getConfig(\"retention\")).toEqual({ days: 7 });\n expect(await store.allConfig()).toEqual({\n \"analytics.enabled\": true,\n retention: { days: 7 },\n });\n });\n\n it(\"upserts on repeated setConfig (no duplicate key)\", async () => {\n await store.setConfig(\"k\", \"v1\");\n await store.setConfig(\"k\", \"v2\");\n expect(await store.getConfig(\"k\")).toBe(\"v2\");\n expect(Object.keys(await store.allConfig())).toEqual([\"k\"]);\n });\n\n it(\"writes a config_audit row on setConfig with old → new + actor (most recent first)\", async () => {\n await store.setConfig(\"k\", \"v1\");\n await store.setConfig(\"k\", \"v2\", \"alice\");\n const audit = await store.getConfigAudit();\n expect(audit).toHaveLength(2);\n expect(audit[0]).toMatchObject({\n key: \"k\",\n oldValue: \"v1\",\n newValue: \"v2\",\n actor: \"alice\",\n });\n expect(audit[1]).toMatchObject({\n key: \"k\",\n oldValue: undefined,\n newValue: \"v1\",\n actor: \"system\",\n });\n });\n });\n\n describe(\"persistence within a session\", () => {\n it(\"retains writes across queries on the same pool\", async () => {\n await store.recordEvent({ ts: 1, type: \"tool_call\", tool: \"persisted\" });\n await store.appendLog({ ts: 1, level: \"warning\", msg: \"stays\" });\n await store.setConfig(\"flag\", { on: true }, \"bob\");\n\n const events = await store.queryEvents({});\n expect(events).toHaveLength(1);\n expect(events[0]?.tool).toBe(\"persisted\");\n\n const logs = await store.queryLogs({});\n expect(logs[0]?.msg).toBe(\"stays\");\n\n expect(await store.getConfig(\"flag\")).toEqual({ on: true });\n const audit = await store.getConfigAudit();\n expect(audit).toHaveLength(1);\n expect(audit[0]).toMatchObject({ actor: \"bob\" });\n });\n });\n\n describe(\"init is idempotent\", () => {\n it(\"can be called twice without error\", async () => {\n await store.init();\n await store.recordEvent({ ts: 1, type: \"tool_call\" });\n expect(await store.queryEvents({})).toHaveLength(1);\n });\n });\n\n describe(\"use-before-init guard\", () => {\n it(\"throws when querying before init\", async () => {\n const s = new PostgresStorageAdapter({ pool: makePool() });\n await expect(s.queryEvents({})).rejects.toThrow(/init\\(\\) before use/);\n });\n });\n});\n\ndescribe(\"resolvePostgresConnectionString\", () => {\n const saved = {\n url: process.env.ENPILINK_DB_URL,\n db: process.env.DATABASE_URL,\n };\n afterEach(() => {\n if (saved.url === undefined) {\n delete process.env.ENPILINK_DB_URL;\n } else {\n process.env.ENPILINK_DB_URL = saved.url;\n }\n if (saved.db === undefined) {\n delete process.env.DATABASE_URL;\n } else {\n process.env.DATABASE_URL = saved.db;\n }\n });\n\n it(\"prefers ENPILINK_DB_URL, falls back to DATABASE_URL, else undefined\", () => {\n delete process.env.ENPILINK_DB_URL;\n delete process.env.DATABASE_URL;\n expect(resolvePostgresConnectionString()).toBeUndefined();\n\n process.env.DATABASE_URL = \"postgres://db/standard\";\n expect(resolvePostgresConnectionString()).toBe(\"postgres://db/standard\");\n\n process.env.ENPILINK_DB_URL = \"postgres://db/enpilink\";\n expect(resolvePostgresConnectionString()).toBe(\"postgres://db/enpilink\");\n\n // Whitespace-only is treated as unset (no hardcoded default ever).\n process.env.ENPILINK_DB_URL = \" \";\n expect(resolvePostgresConnectionString()).toBe(\"postgres://db/standard\");\n });\n});\n"]}
@@ -0,0 +1,33 @@
1
+ import type { AnalyticsEvent, ConfigAuditEntry, EventQuery, LogEntry, LogQuery, StorageAdapter, StorageAdapterOptions } from "./types.js";
2
+ /** Default on-disk database path. Overridable via `ENPILINK_DB_PATH`. */
3
+ export declare const DEFAULT_DB_PATH = "./enpilink.db";
4
+ /**
5
+ * Embedded-SQLite {@link StorageAdapter}, backed by `better-sqlite3`.
6
+ *
7
+ * `better-sqlite3` was chosen over `node:sqlite` (experimental, warns on every
8
+ * load) and `@libsql/client` (async, remote-oriented, heavier): it is mature,
9
+ * synchronous (simple adapter code), and ships prebuilt binaries for both
10
+ * macOS-arm64 and linux-x64, so `pnpm install` succeeds without node-gyp.
11
+ *
12
+ * Tables: `events`, `logs`, `config`, `config_audit`. Config values are stored
13
+ * as opaque JSON — no special-casing of keys. The DB path is gitignored.
14
+ */
15
+ export declare class SqliteStorageAdapter implements StorageAdapter {
16
+ private readonly path;
17
+ private db;
18
+ private stmts;
19
+ constructor(opts?: StorageAdapterOptions);
20
+ init(): Promise<void>;
21
+ private require;
22
+ recordEvent(e: AnalyticsEvent): Promise<void>;
23
+ queryEvents(f?: EventQuery): Promise<AnalyticsEvent[]>;
24
+ appendLog(l: LogEntry): Promise<void>;
25
+ queryLogs(f?: LogQuery): Promise<LogEntry[]>;
26
+ getConfig(key: string): Promise<unknown>;
27
+ setConfig(key: string, value: unknown, actor?: string): Promise<void>;
28
+ allConfig(): Promise<Record<string, unknown>>;
29
+ getConfigAudit(): Promise<ConfigAuditEntry[]>;
30
+ close(): Promise<void>;
31
+ /** Audit trail of config writes (most recent first). Synchronous helper. */
32
+ getAuditLog(): ConfigAuditEntry[];
33
+ }