@the-situation/indexer 0.1.0-alpha.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 (206) hide show
  1. package/README.md +236 -0
  2. package/dist/api/app.d.ts +380 -0
  3. package/dist/api/app.d.ts.map +1 -0
  4. package/dist/api/app.js +23 -0
  5. package/dist/api/app.js.map +1 -0
  6. package/dist/api/middleware/admin-auth.d.ts +37 -0
  7. package/dist/api/middleware/admin-auth.d.ts.map +1 -0
  8. package/dist/api/middleware/admin-auth.js +18 -0
  9. package/dist/api/middleware/admin-auth.js.map +1 -0
  10. package/dist/api/middleware/error-handler.d.ts +37 -0
  11. package/dist/api/middleware/error-handler.d.ts.map +1 -0
  12. package/dist/api/middleware/error-handler.js +15 -0
  13. package/dist/api/middleware/error-handler.js.map +1 -0
  14. package/dist/api/routes/admin.d.ts +140 -0
  15. package/dist/api/routes/admin.d.ts.map +1 -0
  16. package/dist/api/routes/admin.js +51 -0
  17. package/dist/api/routes/admin.js.map +1 -0
  18. package/dist/api/routes/health.d.ts +47 -0
  19. package/dist/api/routes/health.d.ts.map +1 -0
  20. package/dist/api/routes/health.js +27 -0
  21. package/dist/api/routes/health.js.map +1 -0
  22. package/dist/api/routes/index.d.ts +11 -0
  23. package/dist/api/routes/index.d.ts.map +1 -0
  24. package/dist/api/routes/index.js +11 -0
  25. package/dist/api/routes/index.js.map +1 -0
  26. package/dist/api/routes/market-events.d.ts +64 -0
  27. package/dist/api/routes/market-events.d.ts.map +1 -0
  28. package/dist/api/routes/market-events.js +41 -0
  29. package/dist/api/routes/market-events.js.map +1 -0
  30. package/dist/api/routes/market-traders.d.ts +64 -0
  31. package/dist/api/routes/market-traders.d.ts.map +1 -0
  32. package/dist/api/routes/market-traders.js +25 -0
  33. package/dist/api/routes/market-traders.js.map +1 -0
  34. package/dist/api/routes/markets.d.ts +78 -0
  35. package/dist/api/routes/markets.d.ts.map +1 -0
  36. package/dist/api/routes/markets.js +57 -0
  37. package/dist/api/routes/markets.js.map +1 -0
  38. package/dist/api/routes/positions.d.ts +62 -0
  39. package/dist/api/routes/positions.d.ts.map +1 -0
  40. package/dist/api/routes/positions.js +25 -0
  41. package/dist/api/routes/positions.js.map +1 -0
  42. package/dist/api/routes/rankings.d.ts +48 -0
  43. package/dist/api/routes/rankings.d.ts.map +1 -0
  44. package/dist/api/routes/rankings.js +9 -0
  45. package/dist/api/routes/rankings.js.map +1 -0
  46. package/dist/api/routes/trader-events.d.ts +64 -0
  47. package/dist/api/routes/trader-events.d.ts.map +1 -0
  48. package/dist/api/routes/trader-events.js +43 -0
  49. package/dist/api/routes/trader-events.js.map +1 -0
  50. package/dist/api/routes/trader-stats.d.ts +63 -0
  51. package/dist/api/routes/trader-stats.d.ts.map +1 -0
  52. package/dist/api/routes/trader-stats.js +8 -0
  53. package/dist/api/routes/trader-stats.js.map +1 -0
  54. package/dist/api/routes/ws.d.ts +44 -0
  55. package/dist/api/routes/ws.d.ts.map +1 -0
  56. package/dist/api/routes/ws.js +27 -0
  57. package/dist/api/routes/ws.js.map +1 -0
  58. package/dist/client/IndexerClient.d.ts +39 -0
  59. package/dist/client/IndexerClient.d.ts.map +1 -0
  60. package/dist/client/IndexerClient.js +11 -0
  61. package/dist/client/IndexerClient.js.map +1 -0
  62. package/dist/client/IndexerClientLive.d.ts +10 -0
  63. package/dist/client/IndexerClientLive.d.ts.map +1 -0
  64. package/dist/client/IndexerClientLive.js +70 -0
  65. package/dist/client/IndexerClientLive.js.map +1 -0
  66. package/dist/client/convenience.d.ts +40 -0
  67. package/dist/client/convenience.d.ts.map +1 -0
  68. package/dist/client/convenience.js +53 -0
  69. package/dist/client/convenience.js.map +1 -0
  70. package/dist/client/index.d.ts +11 -0
  71. package/dist/client/index.d.ts.map +1 -0
  72. package/dist/client/index.js +14 -0
  73. package/dist/client/index.js.map +1 -0
  74. package/dist/client/trader-stats.d.ts +52 -0
  75. package/dist/client/trader-stats.d.ts.map +1 -0
  76. package/dist/client/trader-stats.js +80 -0
  77. package/dist/client/trader-stats.js.map +1 -0
  78. package/dist/config.d.ts +15 -0
  79. package/dist/config.d.ts.map +1 -0
  80. package/dist/config.js +26 -0
  81. package/dist/config.js.map +1 -0
  82. package/dist/db/connection.d.ts +7 -0
  83. package/dist/db/connection.d.ts.map +1 -0
  84. package/dist/db/connection.js +22 -0
  85. package/dist/db/connection.js.map +1 -0
  86. package/dist/db/repositories/cursor.d.ts +12 -0
  87. package/dist/db/repositories/cursor.d.ts.map +1 -0
  88. package/dist/db/repositories/cursor.js +24 -0
  89. package/dist/db/repositories/cursor.js.map +1 -0
  90. package/dist/db/repositories/event.d.ts +30 -0
  91. package/dist/db/repositories/event.d.ts.map +1 -0
  92. package/dist/db/repositories/event.js +106 -0
  93. package/dist/db/repositories/event.js.map +1 -0
  94. package/dist/db/repositories/index.d.ts +7 -0
  95. package/dist/db/repositories/index.d.ts.map +1 -0
  96. package/dist/db/repositories/index.js +7 -0
  97. package/dist/db/repositories/index.js.map +1 -0
  98. package/dist/db/repositories/market-state.d.ts +27 -0
  99. package/dist/db/repositories/market-state.d.ts.map +1 -0
  100. package/dist/db/repositories/market-state.js +35 -0
  101. package/dist/db/repositories/market-state.js.map +1 -0
  102. package/dist/db/repositories/market.d.ts +16 -0
  103. package/dist/db/repositories/market.d.ts.map +1 -0
  104. package/dist/db/repositories/market.js +59 -0
  105. package/dist/db/repositories/market.js.map +1 -0
  106. package/dist/db/repositories/position.d.ts +15 -0
  107. package/dist/db/repositories/position.d.ts.map +1 -0
  108. package/dist/db/repositories/position.js +52 -0
  109. package/dist/db/repositories/position.js.map +1 -0
  110. package/dist/db/repositories/ranking.d.ts +12 -0
  111. package/dist/db/repositories/ranking.d.ts.map +1 -0
  112. package/dist/db/repositories/ranking.js +54 -0
  113. package/dist/db/repositories/ranking.js.map +1 -0
  114. package/dist/db/schema.d.ts +6 -0
  115. package/dist/db/schema.d.ts.map +1 -0
  116. package/dist/db/schema.js +88 -0
  117. package/dist/db/schema.js.map +1 -0
  118. package/dist/etl/decoder.d.ts +11 -0
  119. package/dist/etl/decoder.d.ts.map +1 -0
  120. package/dist/etl/decoder.js +242 -0
  121. package/dist/etl/decoder.js.map +1 -0
  122. package/dist/etl/event-indexer.d.ts +11 -0
  123. package/dist/etl/event-indexer.d.ts.map +1 -0
  124. package/dist/etl/event-indexer.js +102 -0
  125. package/dist/etl/event-indexer.js.map +1 -0
  126. package/dist/etl/position-refresher.d.ts +11 -0
  127. package/dist/etl/position-refresher.d.ts.map +1 -0
  128. package/dist/etl/position-refresher.js +52 -0
  129. package/dist/etl/position-refresher.js.map +1 -0
  130. package/dist/etl/scheduler.d.ts +15 -0
  131. package/dist/etl/scheduler.d.ts.map +1 -0
  132. package/dist/etl/scheduler.js +69 -0
  133. package/dist/etl/scheduler.js.map +1 -0
  134. package/dist/etl/state-refresher.d.ts +11 -0
  135. package/dist/etl/state-refresher.d.ts.map +1 -0
  136. package/dist/etl/state-refresher.js +50 -0
  137. package/dist/etl/state-refresher.js.map +1 -0
  138. package/dist/etl/types.d.ts +20 -0
  139. package/dist/etl/types.d.ts.map +1 -0
  140. package/dist/etl/types.js +5 -0
  141. package/dist/etl/types.js.map +1 -0
  142. package/dist/index.d.ts +2 -0
  143. package/dist/index.d.ts.map +1 -0
  144. package/dist/index.js +193 -0
  145. package/dist/index.js.map +1 -0
  146. package/dist/layers/ChainReaderLive.d.ts +4 -0
  147. package/dist/layers/ChainReaderLive.d.ts.map +1 -0
  148. package/dist/layers/ChainReaderLive.js +87 -0
  149. package/dist/layers/ChainReaderLive.js.map +1 -0
  150. package/dist/layers/DatabaseLive.d.ts +5 -0
  151. package/dist/layers/DatabaseLive.d.ts.map +1 -0
  152. package/dist/layers/DatabaseLive.js +13 -0
  153. package/dist/layers/DatabaseLive.js.map +1 -0
  154. package/dist/layers/EventBusLive.d.ts +4 -0
  155. package/dist/layers/EventBusLive.d.ts.map +1 -0
  156. package/dist/layers/EventBusLive.js +24 -0
  157. package/dist/layers/EventBusLive.js.map +1 -0
  158. package/dist/layers/VoyagerClientLive.d.ts +4 -0
  159. package/dist/layers/VoyagerClientLive.d.ts.map +1 -0
  160. package/dist/layers/VoyagerClientLive.js +30 -0
  161. package/dist/layers/VoyagerClientLive.js.map +1 -0
  162. package/dist/layers/index.d.ts +5 -0
  163. package/dist/layers/index.d.ts.map +1 -0
  164. package/dist/layers/index.js +5 -0
  165. package/dist/layers/index.js.map +1 -0
  166. package/dist/services/ChainReader.d.ts +40 -0
  167. package/dist/services/ChainReader.d.ts.map +1 -0
  168. package/dist/services/ChainReader.js +13 -0
  169. package/dist/services/ChainReader.js.map +1 -0
  170. package/dist/services/Database.d.ts +17 -0
  171. package/dist/services/Database.d.ts.map +1 -0
  172. package/dist/services/Database.js +4 -0
  173. package/dist/services/Database.js.map +1 -0
  174. package/dist/services/EventBus.d.ts +16 -0
  175. package/dist/services/EventBus.d.ts.map +1 -0
  176. package/dist/services/EventBus.js +4 -0
  177. package/dist/services/EventBus.js.map +1 -0
  178. package/dist/services/VoyagerClient.d.ts +20 -0
  179. package/dist/services/VoyagerClient.d.ts.map +1 -0
  180. package/dist/services/VoyagerClient.js +13 -0
  181. package/dist/services/VoyagerClient.js.map +1 -0
  182. package/dist/services/index.d.ts +5 -0
  183. package/dist/services/index.d.ts.map +1 -0
  184. package/dist/services/index.js +5 -0
  185. package/dist/services/index.js.map +1 -0
  186. package/dist/types/api.d.ts +117 -0
  187. package/dist/types/api.d.ts.map +1 -0
  188. package/dist/types/api.js +5 -0
  189. package/dist/types/api.js.map +1 -0
  190. package/dist/types/event.d.ts +88 -0
  191. package/dist/types/event.d.ts.map +1 -0
  192. package/dist/types/event.js +5 -0
  193. package/dist/types/event.js.map +1 -0
  194. package/dist/types/index.d.ts +5 -0
  195. package/dist/types/index.d.ts.map +1 -0
  196. package/dist/types/index.js +2 -0
  197. package/dist/types/index.js.map +1 -0
  198. package/dist/types/market.d.ts +44 -0
  199. package/dist/types/market.d.ts.map +1 -0
  200. package/dist/types/market.js +5 -0
  201. package/dist/types/market.js.map +1 -0
  202. package/dist/types/position.d.ts +39 -0
  203. package/dist/types/position.d.ts.map +1 -0
  204. package/dist/types/position.js +5 -0
  205. package/dist/types/position.js.map +1 -0
  206. package/package.json +42 -0
package/README.md ADDED
@@ -0,0 +1,236 @@
1
+ # @the-situation/indexer
2
+
3
+ ETL indexer and REST/WebSocket API for The Situation prediction markets. Polls the [Voyager](https://voyager.online) explorer API for on-chain events, decodes them, stores everything in SQLite, and serves it over HTTP and WebSocket.
4
+
5
+ ## Quick Start
6
+
7
+ ```bash
8
+ # From the repo root
9
+ cp packages/indexer/.env.example packages/indexer/.env
10
+ # Fill in VOYAGER_API_KEY and ADMIN_API_KEY
11
+
12
+ bun run --filter '@the-situation/indexer' dev
13
+ ```
14
+
15
+ The server starts at `http://localhost:3000`. Register a market to begin indexing:
16
+
17
+ ```bash
18
+ curl -X POST http://localhost:3000/admin/markets \
19
+ -H "Authorization: Bearer <ADMIN_API_KEY>" \
20
+ -H "Content-Type: application/json" \
21
+ -d '{"address": "0x02de...", "title": "My Market", "category": "crypto"}'
22
+ ```
23
+
24
+ Events will begin appearing within ~15 seconds.
25
+
26
+ ## Environment Variables
27
+
28
+ | Variable | Required | Default | Description |
29
+ |----------|----------|---------|-------------|
30
+ | `VOYAGER_API_KEY` | Yes | — | Voyager explorer API key |
31
+ | `ADMIN_API_KEY` | Yes | — | Bearer token for `/admin/*` endpoints |
32
+ | `DB_PATH` | No | `./data/indexer.db` | SQLite database file path |
33
+ | `PORT` | No | `3000` | HTTP server port |
34
+ | `STARKNET_RPC_URL` | No | `https://api.cartridge.gg/x/starknet/sepolia` | Starknet RPC endpoint |
35
+ | `EVENT_POLL_INTERVAL_MS` | No | `15000` | Event polling interval (ms) |
36
+ | `STATE_POLL_INTERVAL_MS` | No | `30000` | Market state refresh interval (ms) |
37
+ | `POSITION_POLL_INTERVAL_MS` | No | `60000` | Position refresh interval (ms) |
38
+
39
+ ## API Reference
40
+
41
+ ### Public Endpoints
42
+
43
+ #### `GET /health`
44
+
45
+ Returns service health and basic stats.
46
+
47
+ ```json
48
+ {
49
+ "status": "ok",
50
+ "uptime": 3600,
51
+ "dbStatus": "connected",
52
+ "marketsCount": 1,
53
+ "eventsCount": 42,
54
+ "lastPollAt": null
55
+ }
56
+ ```
57
+
58
+ #### `GET /api/markets`
59
+
60
+ List all registered markets with their latest on-chain state.
61
+
62
+ #### `GET /api/markets/:address`
63
+
64
+ Get a single market by contract address. Returns `404` if not found.
65
+
66
+ #### `GET /api/markets/:address/events`
67
+
68
+ Paginated event time series for a market.
69
+
70
+ | Query Param | Default | Description |
71
+ |-------------|---------|-------------|
72
+ | `page` | `1` | Page number |
73
+ | `pageSize` | `100` | Items per page (max 500) |
74
+ | `from` | — | Unix timestamp lower bound |
75
+ | `to` | — | Unix timestamp upper bound |
76
+
77
+ ```json
78
+ {
79
+ "data": [
80
+ {
81
+ "id": 1,
82
+ "txHash": "0x...",
83
+ "blockNumber": 6290091,
84
+ "timestamp": 1770487426,
85
+ "eventType": "trade_executed",
86
+ "trader": "0x...",
87
+ "mean": 13,
88
+ "stdDev": 3,
89
+ "lowerBound": 10,
90
+ "upperBound": 16,
91
+ "oldMean": 12,
92
+ "oldStdDev": 3,
93
+ "collateralPosted": "91208000000000000"
94
+ }
95
+ ],
96
+ "total": 42,
97
+ "page": 1,
98
+ "pageSize": 100
99
+ }
100
+ ```
101
+
102
+ #### `GET /api/markets/:address/traders`
103
+
104
+ List all discovered traders and their positions for a market.
105
+
106
+ #### `GET /api/positions/:trader`
107
+
108
+ All positions held by a trader across markets.
109
+
110
+ #### `GET /api/rankings`
111
+
112
+ Global leaderboard sorted by PnL.
113
+
114
+ | Query Param | Default | Description |
115
+ |-------------|---------|-------------|
116
+ | `limit` | `50` | Max entries (max 200) |
117
+
118
+ #### `WS /ws`
119
+
120
+ WebSocket endpoint for real-time event streaming. Receives JSON messages when new events are indexed:
121
+
122
+ ```json
123
+ {
124
+ "type": "new_events",
125
+ "marketAddress": "0x...",
126
+ "data": { "count": 3, "latestBlock": 6395140 }
127
+ }
128
+ ```
129
+
130
+ ### Admin Endpoints
131
+
132
+ All admin endpoints require `Authorization: Bearer <ADMIN_API_KEY>`.
133
+
134
+ #### `POST /admin/markets`
135
+
136
+ Register a new market for indexing.
137
+
138
+ ```bash
139
+ curl -X POST http://localhost:3000/admin/markets \
140
+ -H "Authorization: Bearer $ADMIN_API_KEY" \
141
+ -H "Content-Type: application/json" \
142
+ -d '{
143
+ "address": "0x02de...",
144
+ "title": "BTC Price Market",
145
+ "description": "Predict the price of BTC",
146
+ "category": "crypto",
147
+ "topics": ["btc", "price"]
148
+ }'
149
+ ```
150
+
151
+ #### `PUT /admin/markets/:address`
152
+
153
+ Update market metadata (title, description, category, topics, is_active).
154
+
155
+ #### `DELETE /admin/markets/:address`
156
+
157
+ Soft-delete a market (stops indexing, keeps historical data).
158
+
159
+ ## ETL Pipeline
160
+
161
+ The indexer runs three polling loops:
162
+
163
+ | Loop | Interval | Description |
164
+ |------|----------|-------------|
165
+ | **Event indexer** | 15s | Fetches events from Voyager, decodes, stores in SQLite, broadcasts to WebSocket |
166
+ | **State refresher** | 30s | Reads current market state from Starknet RPC |
167
+ | **Position refresher** | 60s | Reads positions for discovered traders from Starknet RPC |
168
+
169
+ Supported event types:
170
+
171
+ | Event | Encoding | Description |
172
+ |-------|----------|-------------|
173
+ | `TradeExecuted` | SQ128x128 (4-limb) | Current-generation trade events |
174
+ | `MarketInitialized` | SQ128x128 | Market bootstrap event |
175
+ | `TradeMoved` | Q96 | Legacy trade events |
176
+ | `Bootstrapped` | Q96 | Legacy bootstrap events |
177
+
178
+ Events are deduplicated by `tx_hash` (UNIQUE constraint). The indexer maintains a per-market cursor tracking the last indexed block number.
179
+
180
+ ## Building
181
+
182
+ ```bash
183
+ # Type-check only
184
+ bun run --filter '@the-situation/indexer' typecheck
185
+
186
+ # Full build (compiles to dist/)
187
+ bun run --filter '@the-situation/indexer' build
188
+
189
+ # Run tests
190
+ bun run --filter '@the-situation/indexer' test
191
+ ```
192
+
193
+ ## Deploying to Fly.io
194
+
195
+ The indexer is configured for [Fly.io](https://fly.io) with a persistent volume for SQLite.
196
+
197
+ ```bash
198
+ cd packages/indexer
199
+
200
+ # Create the app (first time only)
201
+ fly apps create situation-indexer
202
+
203
+ # Create a persistent volume for the database
204
+ fly volumes create indexer_data --region iad --size 1
205
+
206
+ # Set secrets
207
+ fly secrets set VOYAGER_API_KEY=your-key ADMIN_API_KEY=your-key
208
+
209
+ # Deploy (uses Dockerfile.indexer at repo root)
210
+ fly deploy
211
+
212
+ # Verify
213
+ curl https://situation-indexer.fly.dev/health
214
+ ```
215
+
216
+ The `fly.toml` is pre-configured with:
217
+ - `shared-cpu-1x` / 512MB RAM
218
+ - Persistent volume mounted at `/data`
219
+ - `auto_stop_machines = false` (always-on for polling)
220
+ - HTTPS enforced
221
+
222
+ ### Useful Fly commands
223
+
224
+ ```bash
225
+ # View logs
226
+ fly logs
227
+
228
+ # SSH into the machine
229
+ fly ssh console
230
+
231
+ # Restart the app
232
+ fly machines restart
233
+
234
+ # Check volume
235
+ fly volumes list
236
+ ```
@@ -0,0 +1,380 @@
1
+ /**
2
+ * Elysia app factory — assembles all middleware and routes.
3
+ */
4
+ import { Elysia } from 'elysia';
5
+ import type { DatabaseService } from '../services/Database';
6
+ import type { EventBusService } from '../services/EventBus';
7
+ export interface AppConfig {
8
+ readonly db: DatabaseService;
9
+ readonly eventBus: EventBusService;
10
+ readonly adminApiKey: string;
11
+ }
12
+ export declare function createApp(config: AppConfig): Elysia<"", {
13
+ decorator: {};
14
+ store: {};
15
+ derive: {};
16
+ resolve: {};
17
+ }, {
18
+ typebox: {};
19
+ error: {};
20
+ } & {
21
+ typebox: {};
22
+ error: {};
23
+ }, {
24
+ schema: {};
25
+ standaloneSchema: {};
26
+ macro: {};
27
+ macroFn: {};
28
+ parser: {};
29
+ response: {};
30
+ } & {
31
+ schema: {};
32
+ standaloneSchema: {};
33
+ macro: {};
34
+ macroFn: {};
35
+ parser: {};
36
+ response: {};
37
+ }, {
38
+ health: {
39
+ get: {
40
+ body: unknown;
41
+ params: {};
42
+ query: unknown;
43
+ headers: unknown;
44
+ response: {
45
+ 200: import("../types").HealthResponse;
46
+ };
47
+ };
48
+ };
49
+ } & {
50
+ api: {
51
+ markets: {
52
+ get: {
53
+ body: unknown;
54
+ params: {};
55
+ query: unknown;
56
+ headers: unknown;
57
+ response: {
58
+ 200: import("../types").MarketResponse[];
59
+ };
60
+ };
61
+ };
62
+ };
63
+ } & {
64
+ api: {
65
+ markets: {
66
+ ":address": {
67
+ get: {
68
+ body: unknown;
69
+ params: {
70
+ address: string;
71
+ } & {};
72
+ query: unknown;
73
+ headers: unknown;
74
+ response: {
75
+ 200: import("../types").MarketResponse | {
76
+ error: string;
77
+ };
78
+ 422: {
79
+ type: "validation";
80
+ on: string;
81
+ summary?: string;
82
+ message?: string;
83
+ found?: unknown;
84
+ property?: string;
85
+ expected?: string;
86
+ };
87
+ };
88
+ };
89
+ };
90
+ };
91
+ };
92
+ } & {
93
+ api: {
94
+ markets: {
95
+ ":address": {
96
+ events: {
97
+ get: {
98
+ body: unknown;
99
+ params: {
100
+ address: string;
101
+ } & {};
102
+ query: unknown;
103
+ headers: unknown;
104
+ response: {
105
+ 200: import("../types").PaginatedResponse<import("../types").EventResponse>;
106
+ 422: {
107
+ type: "validation";
108
+ on: string;
109
+ summary?: string;
110
+ message?: string;
111
+ found?: unknown;
112
+ property?: string;
113
+ expected?: string;
114
+ };
115
+ };
116
+ };
117
+ };
118
+ };
119
+ };
120
+ };
121
+ } & {
122
+ api: {
123
+ markets: {
124
+ ":address": {
125
+ traders: {
126
+ get: {
127
+ body: unknown;
128
+ params: {
129
+ address: string;
130
+ } & {};
131
+ query: unknown;
132
+ headers: unknown;
133
+ response: {
134
+ 200: import("../types").TraderPositionResponse[];
135
+ 422: {
136
+ type: "validation";
137
+ on: string;
138
+ summary?: string;
139
+ message?: string;
140
+ found?: unknown;
141
+ property?: string;
142
+ expected?: string;
143
+ };
144
+ };
145
+ };
146
+ };
147
+ };
148
+ };
149
+ };
150
+ } & {
151
+ api: {
152
+ positions: {
153
+ ":trader": {
154
+ get: {
155
+ body: unknown;
156
+ params: {
157
+ trader: string;
158
+ } & {};
159
+ query: unknown;
160
+ headers: unknown;
161
+ response: {
162
+ 200: import("../types").TraderPositionResponse[];
163
+ 422: {
164
+ type: "validation";
165
+ on: string;
166
+ summary?: string;
167
+ message?: string;
168
+ found?: unknown;
169
+ property?: string;
170
+ expected?: string;
171
+ };
172
+ };
173
+ };
174
+ };
175
+ };
176
+ };
177
+ } & {
178
+ api: {
179
+ positions: {
180
+ ":trader": {
181
+ events: {
182
+ get: {
183
+ body: unknown;
184
+ params: {
185
+ trader: string;
186
+ } & {};
187
+ query: unknown;
188
+ headers: unknown;
189
+ response: {
190
+ 200: import("../types").PaginatedResponse<import("../types").TraderEventResponse>;
191
+ 422: {
192
+ type: "validation";
193
+ on: string;
194
+ summary?: string;
195
+ message?: string;
196
+ found?: unknown;
197
+ property?: string;
198
+ expected?: string;
199
+ };
200
+ };
201
+ };
202
+ };
203
+ };
204
+ };
205
+ };
206
+ } & {
207
+ api: {
208
+ positions: {
209
+ ":trader": {
210
+ stats: {
211
+ get: {
212
+ body: unknown;
213
+ params: {
214
+ trader: string;
215
+ } & {};
216
+ query: unknown;
217
+ headers: unknown;
218
+ response: {
219
+ 200: import("../types").TraderStatsResponse;
220
+ 422: {
221
+ type: "validation";
222
+ on: string;
223
+ summary?: string;
224
+ message?: string;
225
+ found?: unknown;
226
+ property?: string;
227
+ expected?: string;
228
+ };
229
+ };
230
+ };
231
+ };
232
+ };
233
+ };
234
+ };
235
+ } & {
236
+ api: {
237
+ rankings: {
238
+ get: {
239
+ body: unknown;
240
+ params: {};
241
+ query: unknown;
242
+ headers: unknown;
243
+ response: {
244
+ 200: import("../types").RankingEntry[];
245
+ };
246
+ };
247
+ };
248
+ };
249
+ } & {
250
+ admin: {
251
+ markets: {
252
+ post: {
253
+ body: unknown;
254
+ params: {};
255
+ query: unknown;
256
+ headers: unknown;
257
+ response: {
258
+ 200: {
259
+ error: string;
260
+ } | {
261
+ error: string;
262
+ success?: never;
263
+ market?: never;
264
+ } | {
265
+ success: boolean;
266
+ market: import("../types").MarketRow;
267
+ error?: never;
268
+ };
269
+ };
270
+ };
271
+ };
272
+ };
273
+ } & {
274
+ admin: {
275
+ markets: {
276
+ ":address": {
277
+ put: {
278
+ body: unknown;
279
+ params: {
280
+ address: string;
281
+ } & {};
282
+ query: unknown;
283
+ headers: unknown;
284
+ response: {
285
+ 200: {
286
+ error: string;
287
+ } | {
288
+ error: string;
289
+ success?: never;
290
+ market?: never;
291
+ } | {
292
+ success: boolean;
293
+ market: import("../types").MarketRow;
294
+ error?: never;
295
+ };
296
+ 422: {
297
+ type: "validation";
298
+ on: string;
299
+ summary?: string;
300
+ message?: string;
301
+ found?: unknown;
302
+ property?: string;
303
+ expected?: string;
304
+ };
305
+ };
306
+ };
307
+ };
308
+ };
309
+ };
310
+ } & {
311
+ admin: {
312
+ markets: {
313
+ ":address": {
314
+ delete: {
315
+ body: unknown;
316
+ params: {
317
+ address: string;
318
+ } & {};
319
+ query: unknown;
320
+ headers: unknown;
321
+ response: {
322
+ 200: {
323
+ error: string;
324
+ } | {
325
+ error: string;
326
+ success?: never;
327
+ } | {
328
+ success: boolean;
329
+ error?: never;
330
+ };
331
+ 422: {
332
+ type: "validation";
333
+ on: string;
334
+ summary?: string;
335
+ message?: string;
336
+ found?: unknown;
337
+ property?: string;
338
+ expected?: string;
339
+ };
340
+ };
341
+ };
342
+ };
343
+ };
344
+ };
345
+ } & {
346
+ ws: {
347
+ subscribe: {
348
+ body: unknown;
349
+ params: {};
350
+ query: unknown;
351
+ headers: unknown;
352
+ response: {};
353
+ };
354
+ };
355
+ }, {
356
+ derive: {};
357
+ resolve: {};
358
+ schema: {};
359
+ standaloneSchema: {};
360
+ response: {};
361
+ }, {
362
+ derive: {};
363
+ resolve: {};
364
+ schema: {};
365
+ standaloneSchema: {};
366
+ response: {};
367
+ } & {
368
+ derive: {};
369
+ resolve: {};
370
+ schema: {};
371
+ standaloneSchema: {};
372
+ response: {};
373
+ } & {
374
+ derive: {};
375
+ resolve: {};
376
+ schema: {};
377
+ standaloneSchema: {};
378
+ response: {};
379
+ }>;
380
+ //# sourceMappingURL=app.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../src/api/app.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAe5D,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,EAAE,EAAE,eAAe,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IACnC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAc1C"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Elysia app factory — assembles all middleware and routes.
3
+ */
4
+ import { Elysia } from 'elysia';
5
+ import { cors } from '@elysiajs/cors';
6
+ import { errorHandler } from './middleware/error-handler';
7
+ import { healthRoutes, marketRoutes, marketEventsRoutes, marketTradersRoutes, positionRoutes, traderEventsRoutes, traderStatsRoutes, rankingRoutes, adminRoutes, wsRoutes, } from './routes';
8
+ export function createApp(config) {
9
+ return new Elysia()
10
+ .use(cors())
11
+ .use(errorHandler)
12
+ .use(healthRoutes(config.db))
13
+ .use(marketRoutes(config.db))
14
+ .use(marketEventsRoutes(config.db))
15
+ .use(marketTradersRoutes(config.db))
16
+ .use(positionRoutes(config.db))
17
+ .use(traderEventsRoutes(config.db))
18
+ .use(traderStatsRoutes(config.db))
19
+ .use(rankingRoutes(config.db))
20
+ .use(adminRoutes(config.db, config.adminApiKey))
21
+ .use(wsRoutes(config.eventBus));
22
+ }
23
+ //# sourceMappingURL=app.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.js","sourceRoot":"","sources":["../../src/api/app.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAGtC,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,kBAAkB,EAClB,mBAAmB,EACnB,cAAc,EACd,kBAAkB,EAClB,iBAAiB,EACjB,aAAa,EACb,WAAW,EACX,QAAQ,GACT,MAAM,UAAU,CAAC;AAQlB,MAAM,UAAU,SAAS,CAAC,MAAiB;IACzC,OAAO,IAAI,MAAM,EAAE;SAChB,GAAG,CAAC,IAAI,EAAE,CAAC;SACX,GAAG,CAAC,YAAY,CAAC;SACjB,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;SAC5B,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;SAC5B,GAAG,CAAC,kBAAkB,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;SAClC,GAAG,CAAC,mBAAmB,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;SACnC,GAAG,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;SAC9B,GAAG,CAAC,kBAAkB,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;SAClC,GAAG,CAAC,iBAAiB,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;SACjC,GAAG,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;SAC7B,GAAG,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;SAC/C,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;AACpC,CAAC"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Bearer token check for admin endpoints.
3
+ */
4
+ import { Elysia } from 'elysia';
5
+ export declare const adminAuth: (adminApiKey: string) => Elysia<"", {
6
+ decorator: {};
7
+ store: {};
8
+ derive: {};
9
+ resolve: {};
10
+ }, {
11
+ typebox: {};
12
+ error: {};
13
+ }, {
14
+ schema: {};
15
+ standaloneSchema: {};
16
+ macro: {};
17
+ macroFn: {};
18
+ parser: {};
19
+ response: {};
20
+ }, {}, {
21
+ derive: {};
22
+ resolve: {};
23
+ schema: {};
24
+ standaloneSchema: {};
25
+ response: {
26
+ 200: {
27
+ error: string;
28
+ };
29
+ };
30
+ }, {
31
+ derive: {};
32
+ resolve: {};
33
+ schema: {};
34
+ standaloneSchema: {};
35
+ response: {};
36
+ }>;
37
+ //# sourceMappingURL=admin-auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-auth.d.ts","sourceRoot":"","sources":["../../../src/api/middleware/admin-auth.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,eAAO,MAAM,SAAS,GAAI,aAAa,MAAM;;;;;;;;;;;;;;;;;;;;;;mBAGZ,MAAM;;;;;;;;;EAapC,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Bearer token check for admin endpoints.
3
+ */
4
+ import { Elysia } from 'elysia';
5
+ export const adminAuth = (adminApiKey) => new Elysia({ name: 'admin-auth' }).onBeforeHandle({ as: 'scoped' }, ({ headers, set }) => {
6
+ const auth = headers['authorization'];
7
+ if (!auth || !auth.startsWith('Bearer ')) {
8
+ set.status = 401;
9
+ return { error: 'Missing or invalid Authorization header' };
10
+ }
11
+ const token = auth.slice(7);
12
+ if (token !== adminApiKey) {
13
+ set.status = 403;
14
+ return { error: 'Invalid API key' };
15
+ }
16
+ return undefined;
17
+ });
18
+ //# sourceMappingURL=admin-auth.js.map