@hugomrdias/foxer 0.0.1

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 (311) hide show
  1. package/README.md +56 -0
  2. package/dist/src/api/index.d.ts +2 -0
  3. package/dist/src/api/index.d.ts.map +1 -0
  4. package/dist/src/api/index.js +2 -0
  5. package/dist/src/api/index.js.map +1 -0
  6. package/dist/src/api/runner.d.ts +12 -0
  7. package/dist/src/api/runner.d.ts.map +1 -0
  8. package/dist/src/api/runner.js +29 -0
  9. package/dist/src/api/runner.js.map +1 -0
  10. package/dist/src/api/server.d.ts +18 -0
  11. package/dist/src/api/server.d.ts.map +1 -0
  12. package/dist/src/api/server.js +21 -0
  13. package/dist/src/api/server.js.map +1 -0
  14. package/dist/src/api/sql-middleware.d.ts +7 -0
  15. package/dist/src/api/sql-middleware.d.ts.map +1 -0
  16. package/dist/src/api/sql-middleware.js +95 -0
  17. package/dist/src/api/sql-middleware.js.map +1 -0
  18. package/dist/src/api/sql.d.ts +14 -0
  19. package/dist/src/api/sql.d.ts.map +1 -0
  20. package/dist/src/api/sql.js +108 -0
  21. package/dist/src/api/sql.js.map +1 -0
  22. package/dist/src/api/sse.d.ts +3 -0
  23. package/dist/src/api/sse.d.ts.map +1 -0
  24. package/dist/src/api/sse.js +11 -0
  25. package/dist/src/api/sse.js.map +1 -0
  26. package/dist/src/bin/dev.d.ts +3 -0
  27. package/dist/src/bin/dev.d.ts.map +1 -0
  28. package/dist/src/bin/dev.js +76 -0
  29. package/dist/src/bin/dev.js.map +1 -0
  30. package/dist/src/bin/flags.d.ts +32 -0
  31. package/dist/src/bin/flags.d.ts.map +1 -0
  32. package/dist/src/bin/flags.js +55 -0
  33. package/dist/src/bin/flags.js.map +1 -0
  34. package/dist/src/bin/index.d.ts +3 -0
  35. package/dist/src/bin/index.d.ts.map +1 -0
  36. package/dist/src/bin/index.js +22 -0
  37. package/dist/src/bin/index.js.map +1 -0
  38. package/dist/src/bin/utils.d.ts +4 -0
  39. package/dist/src/bin/utils.d.ts.map +1 -0
  40. package/dist/src/bin/utils.js +52 -0
  41. package/dist/src/bin/utils.js.map +1 -0
  42. package/dist/src/client/index.d.ts +18 -0
  43. package/dist/src/client/index.d.ts.map +1 -0
  44. package/dist/src/client/index.js +150 -0
  45. package/dist/src/client/index.js.map +1 -0
  46. package/dist/src/config/config.d.ts +157 -0
  47. package/dist/src/config/config.d.ts.map +1 -0
  48. package/dist/src/config/config.js +65 -0
  49. package/dist/src/config/config.js.map +1 -0
  50. package/dist/src/config/env.d.ts +25 -0
  51. package/dist/src/config/env.d.ts.map +1 -0
  52. package/dist/src/config/env.js +21 -0
  53. package/dist/src/config/env.js.map +1 -0
  54. package/dist/src/contants.d.ts +4 -0
  55. package/dist/src/contants.d.ts.map +1 -0
  56. package/dist/src/contants.js +4 -0
  57. package/dist/src/contants.js.map +1 -0
  58. package/dist/src/db/actions/blocks.d.ts +44 -0
  59. package/dist/src/db/actions/blocks.d.ts.map +1 -0
  60. package/dist/src/db/actions/blocks.js +152 -0
  61. package/dist/src/db/actions/blocks.js.map +1 -0
  62. package/dist/src/db/actions/index.d.ts +2 -0
  63. package/dist/src/db/actions/index.d.ts.map +1 -0
  64. package/dist/src/db/actions/index.js +2 -0
  65. package/dist/src/db/actions/index.js.map +1 -0
  66. package/dist/src/db/actions/transactions.d.ts +11 -0
  67. package/dist/src/db/actions/transactions.d.ts.map +1 -0
  68. package/dist/src/db/actions/transactions.js +22 -0
  69. package/dist/src/db/actions/transactions.js.map +1 -0
  70. package/dist/src/db/client.d.ts +329 -0
  71. package/dist/src/db/client.d.ts.map +1 -0
  72. package/dist/src/db/client.js +108 -0
  73. package/dist/src/db/client.js.map +1 -0
  74. package/dist/src/db/column-types.d.ts +132 -0
  75. package/dist/src/db/column-types.d.ts.map +1 -0
  76. package/dist/src/db/column-types.js +86 -0
  77. package/dist/src/db/column-types.js.map +1 -0
  78. package/dist/src/db/encode.d.ts +10 -0
  79. package/dist/src/db/encode.d.ts.map +1 -0
  80. package/dist/src/db/encode.js +79 -0
  81. package/dist/src/db/encode.js.map +1 -0
  82. package/dist/src/db/migrate.d.ts +31 -0
  83. package/dist/src/db/migrate.d.ts.map +1 -0
  84. package/dist/src/db/migrate.js +147 -0
  85. package/dist/src/db/migrate.js.map +1 -0
  86. package/dist/src/db/schema/blocks.d.ts +369 -0
  87. package/dist/src/db/schema/blocks.d.ts.map +1 -0
  88. package/dist/src/db/schema/blocks.js +24 -0
  89. package/dist/src/db/schema/blocks.js.map +1 -0
  90. package/dist/src/db/schema/index.d.ts +1415 -0
  91. package/dist/src/db/schema/index.d.ts.map +1 -0
  92. package/dist/src/db/schema/index.js +18 -0
  93. package/dist/src/db/schema/index.js.map +1 -0
  94. package/dist/src/db/schema/transactions.d.ts +336 -0
  95. package/dist/src/db/schema/transactions.d.ts.map +1 -0
  96. package/dist/src/db/schema/transactions.js +33 -0
  97. package/dist/src/db/schema/transactions.js.map +1 -0
  98. package/dist/src/db/transaction.d.ts +7 -0
  99. package/dist/src/db/transaction.d.ts.map +1 -0
  100. package/dist/src/db/transaction.js +8 -0
  101. package/dist/src/db/transaction.js.map +1 -0
  102. package/dist/src/hooks/default-hooks.d.ts +2 -0
  103. package/dist/src/hooks/default-hooks.d.ts.map +1 -0
  104. package/dist/src/hooks/default-hooks.js +107 -0
  105. package/dist/src/hooks/default-hooks.js.map +1 -0
  106. package/dist/src/hooks/registry.d.ts +51 -0
  107. package/dist/src/hooks/registry.d.ts.map +1 -0
  108. package/dist/src/hooks/registry.js +32 -0
  109. package/dist/src/hooks/registry.js.map +1 -0
  110. package/dist/src/index.d.ts +10 -0
  111. package/dist/src/index.d.ts.map +1 -0
  112. package/dist/src/index.js +4 -0
  113. package/dist/src/index.js.map +1 -0
  114. package/dist/src/indexer/backfill.d.ts +15 -0
  115. package/dist/src/indexer/backfill.d.ts.map +1 -0
  116. package/dist/src/indexer/backfill.js +95 -0
  117. package/dist/src/indexer/backfill.js.map +1 -0
  118. package/dist/src/indexer/live.d.ts +20 -0
  119. package/dist/src/indexer/live.d.ts.map +1 -0
  120. package/dist/src/indexer/live.js +51 -0
  121. package/dist/src/indexer/live.js.map +1 -0
  122. package/dist/src/indexer/process-block.d.ts +29 -0
  123. package/dist/src/indexer/process-block.d.ts.map +1 -0
  124. package/dist/src/indexer/process-block.js +91 -0
  125. package/dist/src/indexer/process-block.js.map +1 -0
  126. package/dist/src/indexer/queue-block.d.ts +18 -0
  127. package/dist/src/indexer/queue-block.d.ts.map +1 -0
  128. package/dist/src/indexer/queue-block.js +38 -0
  129. package/dist/src/indexer/queue-block.js.map +1 -0
  130. package/dist/src/indexer/reorg.d.ts +24 -0
  131. package/dist/src/indexer/reorg.d.ts.map +1 -0
  132. package/dist/src/indexer/reorg.js +83 -0
  133. package/dist/src/indexer/reorg.js.map +1 -0
  134. package/dist/src/indexer/runner.d.ts +14 -0
  135. package/dist/src/indexer/runner.d.ts.map +1 -0
  136. package/dist/src/indexer/runner.js +22 -0
  137. package/dist/src/indexer/runner.js.map +1 -0
  138. package/dist/src/rpc/client.d.ts +11 -0
  139. package/dist/src/rpc/client.d.ts.map +1 -0
  140. package/dist/src/rpc/client.js +18 -0
  141. package/dist/src/rpc/client.js.map +1 -0
  142. package/dist/src/rpc/get-block.d.ts +16 -0
  143. package/dist/src/rpc/get-block.d.ts.map +1 -0
  144. package/dist/src/rpc/get-block.js +77 -0
  145. package/dist/src/rpc/get-block.js.map +1 -0
  146. package/dist/src/rpc/get-logs.d.ts +11 -0
  147. package/dist/src/rpc/get-logs.d.ts.map +1 -0
  148. package/dist/src/rpc/get-logs.js +23 -0
  149. package/dist/src/rpc/get-logs.js.map +1 -0
  150. package/dist/src/schema.d.ts +3 -0
  151. package/dist/src/schema.d.ts.map +1 -0
  152. package/dist/src/schema.js +9 -0
  153. package/dist/src/schema.js.map +1 -0
  154. package/dist/src/types.d.ts +22 -0
  155. package/dist/src/types.d.ts.map +1 -0
  156. package/dist/src/types.js +1 -0
  157. package/dist/src/types.js.map +1 -0
  158. package/dist/src/utils/bloom.d.ts +6 -0
  159. package/dist/src/utils/bloom.d.ts.map +1 -0
  160. package/dist/src/utils/bloom.js +30 -0
  161. package/dist/src/utils/bloom.js.map +1 -0
  162. package/dist/src/utils/build-conflict-columns.d.ts +4 -0
  163. package/dist/src/utils/build-conflict-columns.d.ts.map +1 -0
  164. package/dist/src/utils/build-conflict-columns.js +14 -0
  165. package/dist/src/utils/build-conflict-columns.js.map +1 -0
  166. package/dist/src/utils/common.d.ts +2 -0
  167. package/dist/src/utils/common.d.ts.map +1 -0
  168. package/dist/src/utils/common.js +4 -0
  169. package/dist/src/utils/common.js.map +1 -0
  170. package/dist/src/utils/cursor.d.ts +5 -0
  171. package/dist/src/utils/cursor.d.ts.map +1 -0
  172. package/dist/src/utils/cursor.js +8 -0
  173. package/dist/src/utils/cursor.js.map +1 -0
  174. package/dist/src/utils/format.d.ts +2 -0
  175. package/dist/src/utils/format.d.ts.map +1 -0
  176. package/dist/src/utils/format.js +16 -0
  177. package/dist/src/utils/format.js.map +1 -0
  178. package/dist/src/utils/hash.d.ts +9 -0
  179. package/dist/src/utils/hash.d.ts.map +1 -0
  180. package/dist/src/utils/hash.js +15 -0
  181. package/dist/src/utils/hash.js.map +1 -0
  182. package/dist/src/utils/json.d.ts +5 -0
  183. package/dist/src/utils/json.d.ts.map +1 -0
  184. package/dist/src/utils/json.js +11 -0
  185. package/dist/src/utils/json.js.map +1 -0
  186. package/dist/src/utils/logger.d.ts +11 -0
  187. package/dist/src/utils/logger.d.ts.map +1 -0
  188. package/dist/src/utils/logger.js +111 -0
  189. package/dist/src/utils/logger.js.map +1 -0
  190. package/dist/src/utils/shutdown.d.ts +9 -0
  191. package/dist/src/utils/shutdown.d.ts.map +1 -0
  192. package/dist/src/utils/shutdown.js +24 -0
  193. package/dist/src/utils/shutdown.js.map +1 -0
  194. package/dist/src/utils/timer.d.ts +6 -0
  195. package/dist/src/utils/timer.d.ts.map +1 -0
  196. package/dist/src/utils/timer.js +9 -0
  197. package/dist/src/utils/timer.js.map +1 -0
  198. package/dist/src/utils/types.d.ts +39 -0
  199. package/dist/src/utils/types.d.ts.map +1 -0
  200. package/dist/src/utils/types.js +1 -0
  201. package/dist/src/utils/types.js.map +1 -0
  202. package/dist/tsconfig.tsbuildinfo +1 -0
  203. package/hello/apps/foc-api/README.md +69 -0
  204. package/hello/apps/foc-api/biome.json +8 -0
  205. package/hello/apps/foc-api/index.html +13 -0
  206. package/hello/apps/foc-api/package.json +39 -0
  207. package/hello/apps/foc-api/public/vite.svg +1 -0
  208. package/hello/apps/foc-api/src/app.css +45 -0
  209. package/hello/apps/foc-api/src/app.tsx +43 -0
  210. package/hello/apps/foc-api/src/assets/Cloudflare_Logo.svg +51 -0
  211. package/hello/apps/foc-api/src/assets/react.svg +1 -0
  212. package/hello/apps/foc-api/src/client.ts +41 -0
  213. package/hello/apps/foc-api/src/components/account.tsx +100 -0
  214. package/hello/apps/foc-api/src/components/wallet-options.tsx +43 -0
  215. package/hello/apps/foc-api/src/index.css +68 -0
  216. package/hello/apps/foc-api/src/main.tsx +38 -0
  217. package/hello/apps/foc-api/src/vite-env.d.ts +1 -0
  218. package/hello/apps/foc-api/tsconfig.app.json +44 -0
  219. package/hello/apps/foc-api/tsconfig.json +17 -0
  220. package/hello/apps/foc-api/tsconfig.node.json +25 -0
  221. package/hello/apps/foc-api/tsconfig.worker.json +8 -0
  222. package/hello/apps/foc-api/vite.config.ts +8 -0
  223. package/hello/apps/foc-api/worker/capabilities.ts +25 -0
  224. package/hello/apps/foc-api/worker/index.ts +64 -0
  225. package/hello/apps/foc-api/worker/router.ts +35 -0
  226. package/hello/apps/foc-api/worker-configuration.d.ts +7357 -0
  227. package/hello/apps/foc-api/wrangler.jsonc +50 -0
  228. package/hello/apps/foc-app/README.md +69 -0
  229. package/hello/apps/foc-app/biome.json +8 -0
  230. package/hello/apps/foc-app/index.html +13 -0
  231. package/hello/apps/foc-app/package.json +39 -0
  232. package/hello/apps/foc-app/public/vite.svg +1 -0
  233. package/hello/apps/foc-app/src/app.css +45 -0
  234. package/hello/apps/foc-app/src/app.tsx +43 -0
  235. package/hello/apps/foc-app/src/assets/Cloudflare_Logo.svg +51 -0
  236. package/hello/apps/foc-app/src/assets/react.svg +1 -0
  237. package/hello/apps/foc-app/src/client.ts +41 -0
  238. package/hello/apps/foc-app/src/components/account.tsx +100 -0
  239. package/hello/apps/foc-app/src/components/wallet-options.tsx +43 -0
  240. package/hello/apps/foc-app/src/index.css +68 -0
  241. package/hello/apps/foc-app/src/main.tsx +38 -0
  242. package/hello/apps/foc-app/src/vite-env.d.ts +1 -0
  243. package/hello/apps/foc-app/tsconfig.app.json +44 -0
  244. package/hello/apps/foc-app/tsconfig.json +17 -0
  245. package/hello/apps/foc-app/tsconfig.node.json +25 -0
  246. package/hello/apps/foc-app/tsconfig.worker.json +8 -0
  247. package/hello/apps/foc-app/vite.config.ts +8 -0
  248. package/hello/apps/foc-app/worker/capabilities.ts +25 -0
  249. package/hello/apps/foc-app/worker/index.ts +64 -0
  250. package/hello/apps/foc-app/worker/router.ts +35 -0
  251. package/hello/apps/foc-app/worker-configuration.d.ts +7357 -0
  252. package/hello/apps/foc-app/wrangler.jsonc +50 -0
  253. package/hello/biome.json +50 -0
  254. package/hello/package.json +22 -0
  255. package/hello/pnpm-workspace.yaml +3 -0
  256. package/hello/tsconfig.json +37 -0
  257. package/package.json +78 -0
  258. package/src/api/index.ts +1 -0
  259. package/src/api/runner.ts +43 -0
  260. package/src/api/server.ts +38 -0
  261. package/src/api/sql-middleware.ts +131 -0
  262. package/src/api/sql.ts +149 -0
  263. package/src/api/sse.ts +12 -0
  264. package/src/bin/create.ts +199 -0
  265. package/src/bin/dev.ts +91 -0
  266. package/src/bin/flags.ts +65 -0
  267. package/src/bin/index.ts +28 -0
  268. package/src/bin/utils.ts +55 -0
  269. package/src/config/config.ts +221 -0
  270. package/src/config/env.ts +28 -0
  271. package/src/contants.ts +3 -0
  272. package/src/db/actions/blocks.ts +209 -0
  273. package/src/db/actions/index.ts +1 -0
  274. package/src/db/actions/transactions.ts +32 -0
  275. package/src/db/client.ts +186 -0
  276. package/src/db/column-types.ts +105 -0
  277. package/src/db/encode.ts +99 -0
  278. package/src/db/migrate.ts +222 -0
  279. package/src/db/schema/blocks.ts +24 -0
  280. package/src/db/schema/index.ts +21 -0
  281. package/src/db/schema/transactions.ts +39 -0
  282. package/src/db/transaction.ts +20 -0
  283. package/src/hooks/registry.ts +107 -0
  284. package/src/index.ts +9 -0
  285. package/src/indexer/backfill.ts +133 -0
  286. package/src/indexer/live.ts +76 -0
  287. package/src/indexer/process-block.ts +142 -0
  288. package/src/indexer/queue-block.ts +74 -0
  289. package/src/indexer/reorg.ts +120 -0
  290. package/src/indexer/runner.ts +35 -0
  291. package/src/rpc/client.ts +27 -0
  292. package/src/rpc/get-block.ts +100 -0
  293. package/src/rpc/get-logs.ts +38 -0
  294. package/src/schema.ts +10 -0
  295. package/src/types.ts +32 -0
  296. package/src/utils/bloom.ts +41 -0
  297. package/src/utils/build-conflict-columns.ts +26 -0
  298. package/src/utils/common.ts +3 -0
  299. package/src/utils/cursor.ts +7 -0
  300. package/src/utils/format.ts +18 -0
  301. package/src/utils/hash.ts +17 -0
  302. package/src/utils/json.ts +11 -0
  303. package/src/utils/logger.ts +149 -0
  304. package/src/utils/shutdown.ts +36 -0
  305. package/src/utils/timer.ts +8 -0
  306. package/src/utils/types.ts +87 -0
  307. package/template/biome.json +50 -0
  308. package/template/package.json +22 -0
  309. package/template/pnpm-workspace.yaml +3 -0
  310. package/template/tsconfig.json +37 -0
  311. package/tsconfig.json +8 -0
@@ -0,0 +1,50 @@
1
+ /**
2
+ * For more details on how to configure Wrangler, refer to:
3
+ * https://developers.cloudflare.com/workers/wrangler/configuration/
4
+ */
5
+ {
6
+ "$schema": "node_modules/wrangler/config-schema.json",
7
+ "name": "ucan-store",
8
+ "main": "worker/index.ts",
9
+ "compatibility_date": "2025-07-19",
10
+ "assets": {
11
+ "not_found_handling": "single-page-application"
12
+ },
13
+ "observability": {
14
+ "enabled": true
15
+ }
16
+ /**
17
+ * Smart Placement
18
+ * Docs: https://developers.cloudflare.com/workers/configuration/smart-placement/#smart-placement
19
+ */
20
+ // "placement": { "mode": "smart" },
21
+
22
+ /**
23
+ * Bindings
24
+ * Bindings allow your Worker to interact with resources on the Cloudflare Developer Platform, including
25
+ * databases, object storage, AI inference, real-time communication and more.
26
+ * https://developers.cloudflare.com/workers/runtime-apis/bindings/
27
+ */
28
+
29
+ /**
30
+ * Environment Variables
31
+ * https://developers.cloudflare.com/workers/wrangler/configuration/#environment-variables
32
+ */
33
+ // "vars": { "MY_VARIABLE": "production_value" },
34
+ /**
35
+ * Note: Use secrets to store sensitive data.
36
+ * https://developers.cloudflare.com/workers/configuration/secrets/
37
+ */
38
+
39
+ /**
40
+ * Static Assets
41
+ * https://developers.cloudflare.com/workers/static-assets/binding/
42
+ */
43
+ // "assets": { "directory": "./public/", "binding": "ASSETS" },
44
+
45
+ /**
46
+ * Service Bindings (communicate between multiple Workers)
47
+ * https://developers.cloudflare.com/workers/wrangler/configuration/#service-bindings
48
+ */
49
+ // "services": [{ "binding": "MY_SERVICE", "service": "my-service" }]
50
+ }
@@ -0,0 +1,50 @@
1
+ {
2
+ "$schema": "https://biomejs.dev/schemas/latest/schema.json",
3
+ "vcs": {
4
+ "enabled": false,
5
+ "clientKind": "git",
6
+ "useIgnoreFile": false
7
+ },
8
+ "files": {
9
+ "ignoreUnknown": false
10
+ },
11
+ "formatter": {
12
+ "enabled": true,
13
+ "formatWithErrors": true,
14
+ "indentStyle": "space",
15
+ "indentWidth": 2,
16
+ "lineEnding": "lf",
17
+ "lineWidth": 80,
18
+ "attributePosition": "auto",
19
+ "includes": ["**"]
20
+ },
21
+ "linter": {
22
+ "enabled": true,
23
+ "rules": {
24
+ "recommended": true
25
+ }
26
+ },
27
+ "javascript": {
28
+ "formatter": {
29
+ "jsxQuoteStyle": "double",
30
+ "quoteProperties": "asNeeded",
31
+ "trailingCommas": "es5",
32
+ "semicolons": "asNeeded",
33
+ "arrowParentheses": "always",
34
+ "bracketSpacing": true,
35
+ "bracketSameLine": false,
36
+ "quoteStyle": "single",
37
+ "attributePosition": "auto"
38
+ },
39
+ "globals": ["document", "navigator", "window"]
40
+ },
41
+ "assist": {
42
+ "enabled": true,
43
+ "actions": {
44
+ "source": {
45
+ "organizeImports": "on",
46
+ "useSortedAttributes": "on"
47
+ }
48
+ }
49
+ }
50
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "hello",
3
+ "version": "0.0.0",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "private": true,
7
+ "workspaces": [
8
+ "apps/*",
9
+ "packages/*"
10
+ ],
11
+ "scripts": {
12
+ "test": "echo \"Error: no test specified\" && exit 1",
13
+ "lint": "pnpm -r --if-present run lint",
14
+ "build": "pnpm -r --if-present run build",
15
+ "lint:fix": "biome check --write ."
16
+ },
17
+ "keywords": [],
18
+ "license": "MIT",
19
+ "devDependencies": {
20
+ "@biomejs/biome": "^2.4.5"
21
+ }
22
+ }
@@ -0,0 +1,3 @@
1
+ packages:
2
+ - 'apps/*'
3
+ - 'packages/*'
@@ -0,0 +1,37 @@
1
+ {
2
+ "compilerOptions": {
3
+ /* Projects */
4
+ "incremental": true,
5
+ "composite": true,
6
+ // Emit
7
+
8
+ "declaration": true,
9
+ "declarationMap": true,
10
+ "sourceMap": true,
11
+ "removeComments": true,
12
+
13
+ // Language and Environment
14
+ "lib": ["ESNext", "DOM"],
15
+ "target": "ESNext",
16
+ "moduleDetection": "force",
17
+
18
+ // Modules
19
+ "module": "NodeNext",
20
+ "moduleResolution": "NodeNext",
21
+ "resolveJsonModule": true,
22
+ "rewriteRelativeImportExtensions": true,
23
+
24
+ // Interop Constraints
25
+ "isolatedModules": true,
26
+ "verbatimModuleSyntax": true,
27
+ "erasableSyntaxOnly": true,
28
+ "esModuleInterop": true,
29
+
30
+ // Type Checking
31
+ "strict": true,
32
+ "noImplicitOverride": true,
33
+
34
+ // Completeness
35
+ "skipLibCheck": true
36
+ }
37
+ }
package/package.json ADDED
@@ -0,0 +1,78 @@
1
+ {
2
+ "name": "@hugomrdias/foxer",
3
+ "description": "Foxer is a all-in-one application server for Filecoin.",
4
+ "version": "0.0.1",
5
+ "author": "Hugo Dias <hugomrdias@gmail.com>",
6
+ "license": "Apache-2.0 OR MIT",
7
+ "type": "module",
8
+ "sideEffects": false,
9
+ "bin": {
10
+ "foxer": "./dist/src/bin/index.js"
11
+ },
12
+ "main": "dist/src/index.js",
13
+ "module": "dist/src/index.js",
14
+ "types": "dist/src/index.d.ts",
15
+ "exports": {
16
+ ".": {
17
+ "types": "./dist/src/index.d.ts",
18
+ "default": "./dist/src/index.js"
19
+ },
20
+ "./api": {
21
+ "types": "./dist/src/api/index.d.ts",
22
+ "default": "./dist/src/api/index.js"
23
+ },
24
+ "./schema": {
25
+ "types": "./dist/src/schema.d.ts",
26
+ "default": "./dist/src/schema.js"
27
+ }
28
+ },
29
+ "typesVersions": {
30
+ "*": {
31
+ "api": [
32
+ "./src/api/index"
33
+ ],
34
+ "schema": [
35
+ "./src/schema"
36
+ ]
37
+ }
38
+ },
39
+ "scripts": {
40
+ "build": "tsc --build",
41
+ "lint": "tsc --build && biome check ."
42
+ },
43
+ "dependencies": {
44
+ "@bluwy/giget-core": "^0.1.6",
45
+ "@clack/prompts": "^1.1.0",
46
+ "@electric-sql/pglite": "^0.3.8",
47
+ "@hono/node-server": "^1.19.5",
48
+ "@libpg-query/parser": "^17.6.3",
49
+ "@pgsql/traverse": "^17.2.4",
50
+ "abitype": "^1.2.3",
51
+ "cleye": "^2.2.1",
52
+ "dotenv": "^17.2.3",
53
+ "drizzle-orm": "1.0.0-beta.15-859cf75",
54
+ "exit-hook": "^5.1.0",
55
+ "hono": "^4.10.6",
56
+ "hono-pino": "^0.10.3",
57
+ "http-shutdown": "^1.2.2",
58
+ "lilconfig": "^3.1.3",
59
+ "p-queue": "^9.1.0",
60
+ "pg": "^8.19.0",
61
+ "picocolors": "^1.1.1",
62
+ "pino": "^10.3.1",
63
+ "postgres": "^3.4.8",
64
+ "scule": "^1.3.0",
65
+ "zod": "^4.1.11"
66
+ },
67
+ "devDependencies": {
68
+ "@pgsql/types": "^17.6.2",
69
+ "@types/node": "^25.3.3",
70
+ "@types/pg": "^8.18.0",
71
+ "type-fest": "^5.4.4",
72
+ "typescript": "^5.9.2"
73
+ },
74
+ "peerDependencies": {
75
+ "hono": ">=4.5",
76
+ "viem": ">=2"
77
+ }
78
+ }
@@ -0,0 +1 @@
1
+ export { sqlMiddleware } from './sql-middleware.ts'
@@ -0,0 +1,43 @@
1
+ import { serve } from '@hono/node-server'
2
+ import shutdown from 'http-shutdown'
3
+ import type { InternalConfig } from '../config/config.ts'
4
+ import type { Database } from '../db/client.ts'
5
+ import type { Logger } from '../utils/logger.ts'
6
+ import { createApiServer } from './server.ts'
7
+
8
+ export function bootstrapApiServer(options: {
9
+ db: Database
10
+ config: InternalConfig
11
+ logger: Logger
12
+ port: number
13
+ }): { stop: () => void } {
14
+ const app = createApiServer({
15
+ logger: options.logger,
16
+ db: options.db,
17
+ config: options.config,
18
+ })
19
+
20
+ const server = serve(
21
+ {
22
+ fetch: app.fetch,
23
+ port: options.port,
24
+ },
25
+ () => {
26
+ options.logger.info({ port: options.port }, 'api server listening')
27
+ }
28
+ )
29
+
30
+ const _server = shutdown(server)
31
+
32
+ return {
33
+ stop: () => {
34
+ _server.shutdown((error) => {
35
+ if (error) {
36
+ options.logger.error({ error }, 'api server shutdown failed')
37
+ return
38
+ }
39
+ options.logger.info('api server shutdown complete')
40
+ })
41
+ },
42
+ }
43
+ }
@@ -0,0 +1,38 @@
1
+ import { Hono } from 'hono'
2
+ import { type PinoLogger, pinoLogger } from 'hono-pino'
3
+ import type { InternalConfig } from '../config/config.ts'
4
+ import type { Database } from '../db/client.ts'
5
+ import type { Logger } from '../utils/logger.ts'
6
+
7
+ /**
8
+ * Builds the Hono API server with health and sql endpoints.
9
+ */
10
+ export function createApiServer({
11
+ db,
12
+ config,
13
+ logger,
14
+ }: {
15
+ db: Database
16
+ config: InternalConfig
17
+ logger: Logger
18
+ }) {
19
+ const app = new Hono<{ Variables: { logger: PinoLogger } }>()
20
+
21
+ app.use(
22
+ pinoLogger({
23
+ pino: logger,
24
+ })
25
+ )
26
+
27
+ app.get('/health', async (c) => {
28
+ const latest =
29
+ (await db.$prepared.getLatestBlock.execute())[0]?.number ?? null
30
+ return c.json({
31
+ ok: true,
32
+ latestIndexedBlock: latest?.toString() ?? null,
33
+ })
34
+ })
35
+
36
+ app.route('/', config.hono({ logger, db }))
37
+ return app
38
+ }
@@ -0,0 +1,131 @@
1
+ import type { QueryWithTypings } from 'drizzle-orm'
2
+ import { createMiddleware } from 'hono/factory'
3
+ import { streamSSE } from 'hono/streaming'
4
+ import postgres from 'postgres'
5
+ import { PUBLICATION_NAME } from '../contants.ts'
6
+ import type { Database } from '../db/client.ts'
7
+ import type { Logger } from '../utils/logger.ts'
8
+ import { executeSql, validateSql } from './sql.ts'
9
+ import { sseError } from './sse.ts'
10
+
11
+ const MAX_LIVE_QUERIES = 1000
12
+
13
+ export function sqlMiddleware({
14
+ db,
15
+ logger,
16
+ }: {
17
+ db: Database
18
+ logger: Logger
19
+ }) {
20
+ let pg: postgres.Sql | undefined
21
+ const databaseUrl = process.env.DATABASE_URL
22
+
23
+ if (databaseUrl && typeof databaseUrl === 'string') {
24
+ // TODO: kill gracefully
25
+ pg = postgres(databaseUrl, {
26
+ publications: PUBLICATION_NAME,
27
+ })
28
+ }
29
+
30
+ let liveQueryCount = 0
31
+
32
+ return createMiddleware(async (c, next) => {
33
+ // SQL over HTTP endpoint
34
+ if (c.req.path === '/sql/db') {
35
+ const queryString = c.req.query('sql')
36
+ if (!queryString) {
37
+ return c.json({ error: 'sql query is required' }, 400)
38
+ }
39
+ const query = JSON.parse(queryString) as QueryWithTypings
40
+
41
+ const result = await validateSql(query)
42
+ if (result.error) {
43
+ return c.json({ error: result.error.message }, 400)
44
+ }
45
+
46
+ const dbResult = await executeSql({ db, query })
47
+
48
+ if (dbResult.error) {
49
+ return c.json({ error: dbResult.error.message }, 500)
50
+ }
51
+ return c.json(dbResult.result, 200)
52
+ }
53
+
54
+ // Live SQL over SSE endpoint
55
+ if (c.req.path === '/sql/live') {
56
+ if (liveQueryCount >= MAX_LIVE_QUERIES) {
57
+ return sseError(c, 'Too many live queries')
58
+ }
59
+
60
+ const queryString = c.req.query('sql')
61
+ if (!queryString) {
62
+ return sseError(c, 'SQL query is required')
63
+ }
64
+ const query = JSON.parse(queryString) as QueryWithTypings
65
+
66
+ const result = await validateSql(query)
67
+ if (result.error) {
68
+ return sseError(c, result.error.message)
69
+ }
70
+
71
+ const tables = result.result
72
+
73
+ const dbResult = await executeSql({ db, query })
74
+
75
+ if (dbResult.error) {
76
+ return sseError(c, dbResult.error.message)
77
+ }
78
+
79
+ if (!pg) {
80
+ return sseError(
81
+ c,
82
+ 'Database connection not established. Please check your DATABASE_URL environment variable.'
83
+ )
84
+ }
85
+
86
+ const subscriptions: (() => void)[] = []
87
+ liveQueryCount++
88
+
89
+ return streamSSE(
90
+ c,
91
+ async (stream) => {
92
+ stream.onAbort(() => {
93
+ liveQueryCount--
94
+ logger.trace('stream aborted')
95
+ subscriptions.forEach((unsubscribe) => {
96
+ unsubscribe()
97
+ })
98
+ })
99
+
100
+ stream.writeSSE({
101
+ data: JSON.stringify(dbResult.result),
102
+ })
103
+
104
+ for (const table of tables) {
105
+ const sub = await pg.subscribe(`*:${table}`, async () => {
106
+ const dbResult = await executeSql({ db, query })
107
+ stream.writeSSE({
108
+ data: JSON.stringify(dbResult.result),
109
+ })
110
+ })
111
+ subscriptions.push(sub.unsubscribe)
112
+ }
113
+
114
+ while (stream.closed === false && stream.aborted === false) {
115
+ // keep the stream alive
116
+ await stream.sleep(1000)
117
+ }
118
+ },
119
+ (error) => {
120
+ logger.error({ error }, 'stream error')
121
+ subscriptions.forEach((unsubscribe) => {
122
+ unsubscribe()
123
+ })
124
+ return Promise.resolve(undefined)
125
+ }
126
+ )
127
+ }
128
+
129
+ return next()
130
+ })
131
+ }
package/src/api/sql.ts ADDED
@@ -0,0 +1,149 @@
1
+ import { PGlite } from '@electric-sql/pglite'
2
+ import {
3
+ type A_Const,
4
+ type Node,
5
+ parse,
6
+ type RawStmt,
7
+ } from '@libpg-query/parser'
8
+ import type { Visitor } from '@pgsql/traverse'
9
+ import { walk } from '@pgsql/traverse'
10
+ import type { QueryWithTypings } from 'drizzle-orm'
11
+ import { Pool } from 'pg'
12
+ import type { Database } from '../db/client'
13
+ import type { MaybeResult } from '../types'
14
+
15
+ // biome-ignore lint/style/noNonNullAssertion: we know the node is not null
16
+ const getNodeType = (node: Node) => Object.keys(node)[0]!
17
+
18
+ export async function parseSql(
19
+ sql: string
20
+ ): Promise<MaybeResult<{ node: Node; tables: string[] }>> {
21
+ let result: { stmts: RawStmt[] } | undefined
22
+ const tables: string[] = []
23
+
24
+ // Using a visitor object (recommended for multiple node types)
25
+ const visitor: Visitor = {
26
+ RangeVar: (path) => {
27
+ tables.push(path.node.relname)
28
+ },
29
+ }
30
+
31
+ try {
32
+ result = (await parse(sql)) as { stmts: RawStmt[] }
33
+ walk(result, visitor)
34
+ } catch (error) {
35
+ return { error: new Error('Failed to parse SQL', { cause: error }) }
36
+ }
37
+
38
+ if (result?.stmts.length === 0) {
39
+ return { error: new Error('No statement found') }
40
+ }
41
+
42
+ if (result?.stmts?.length && result.stmts.length > 1) {
43
+ return { error: new Error('Only one statement is allowed') }
44
+ }
45
+
46
+ const stmt = result?.stmts[0]
47
+ if (stmt?.stmt == null) {
48
+ return { error: new Error('Invalid statement') }
49
+ }
50
+
51
+ const node = stmt.stmt
52
+ return { result: { node, tables } }
53
+ }
54
+
55
+ export async function validateSql(
56
+ query: QueryWithTypings
57
+ ): Promise<MaybeResult<string[]>> {
58
+ const { sql, params } = query
59
+ const result = await parseSql(sql)
60
+ if (result.error) {
61
+ return result
62
+ }
63
+
64
+ const { node, tables } = result.result
65
+
66
+ const nodeType = getNodeType(node)
67
+
68
+ if (nodeType !== 'SelectStmt') {
69
+ return { error: new Error('Only select statements are allowed') }
70
+ }
71
+
72
+ if (!('SelectStmt' in node)) {
73
+ return { error: new Error('Invalid statement') }
74
+ }
75
+ const selectStmt = node.SelectStmt
76
+
77
+ if (selectStmt.lockingClause || selectStmt.intoClause) {
78
+ return { error: new Error('Locking or into clauses are not allowed') }
79
+ }
80
+ if (selectStmt.withClause?.recursive) {
81
+ return { error: new Error('Recursive with clauses are not allowed') }
82
+ }
83
+
84
+ if (!selectStmt.limitCount) {
85
+ return { error: new Error('Limit is required') }
86
+ }
87
+
88
+ let limit: number | undefined
89
+
90
+ if ('ParamRef' in selectStmt.limitCount) {
91
+ const limitIndex = selectStmt.limitCount.ParamRef.number as number
92
+ const _limit = params[limitIndex - 1]
93
+ if (typeof _limit === 'number') {
94
+ limit = _limit
95
+ }
96
+ }
97
+
98
+ if ('A_Const' in selectStmt.limitCount) {
99
+ const aConst = selectStmt.limitCount.A_Const as A_Const
100
+ limit = aConst.ival?.ival ?? 0
101
+ }
102
+ if (typeof limit !== 'number' || limit < 1) {
103
+ return {
104
+ error: new Error('Limit is required and must be a number greater than 0'),
105
+ }
106
+ }
107
+
108
+ if (limit > 100) {
109
+ return { error: new Error('Limit is too large (max 100)') }
110
+ }
111
+
112
+ return { result: tables }
113
+ }
114
+
115
+ export async function executeSql({
116
+ db,
117
+ query,
118
+ }: {
119
+ db: Database
120
+ query: QueryWithTypings
121
+ }): Promise<MaybeResult<unknown>> {
122
+ try {
123
+ let dbResult: unknown | undefined
124
+ if (db.$client instanceof PGlite) {
125
+ dbResult = await db._.session
126
+ .prepareQuery(query, undefined, undefined, false)
127
+ .execute()
128
+ }
129
+ if (db.$client instanceof Pool) {
130
+ dbResult = await db.transaction(
131
+ (tx) => {
132
+ return tx._.session
133
+ .prepareQuery(query, undefined, undefined, false)
134
+ .execute()
135
+ },
136
+ { accessMode: 'read only' }
137
+ )
138
+ }
139
+
140
+ if (dbResult == null) {
141
+ return { error: new Error('Empty result') }
142
+ }
143
+ return { result: dbResult }
144
+ } catch (error) {
145
+ return {
146
+ error: error instanceof Error ? error : new Error('Unknown error'),
147
+ }
148
+ }
149
+ }
package/src/api/sse.ts ADDED
@@ -0,0 +1,12 @@
1
+ import type { Context } from 'hono'
2
+ import { streamSSE } from 'hono/streaming'
3
+
4
+ export function sseError(c: Context, message: string) {
5
+ return streamSSE(c, async (stream) => {
6
+ await stream.writeSSE({
7
+ event: 'error',
8
+ data: message,
9
+ })
10
+ return stream.close()
11
+ })
12
+ }