@latticexyz/store-indexer 2.2.18-8d0ce55e964e646a1c804c401df01c4deb866f30 → 2.2.18-9fa07c8489f1fbf167d0db01cd9aaa645a29c8e2

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 (54) hide show
  1. package/dist/bin/postgres-decoded-indexer.cjs +322 -0
  2. package/dist/bin/postgres-decoded-indexer.cjs.map +1 -0
  3. package/dist/bin/postgres-decoded-indexer.d.cts +1 -0
  4. package/dist/bin/postgres-decoded-indexer.js +92 -1
  5. package/dist/bin/postgres-decoded-indexer.js.map +1 -1
  6. package/dist/bin/postgres-frontend.cjs +567 -0
  7. package/dist/bin/postgres-frontend.cjs.map +1 -0
  8. package/dist/bin/postgres-frontend.d.cts +1 -0
  9. package/dist/bin/postgres-frontend.js +258 -5
  10. package/dist/bin/postgres-frontend.js.map +1 -1
  11. package/dist/bin/postgres-indexer.cjs +368 -0
  12. package/dist/bin/postgres-indexer.cjs.map +1 -0
  13. package/dist/bin/postgres-indexer.d.cts +1 -0
  14. package/dist/bin/postgres-indexer.js +106 -1
  15. package/dist/bin/postgres-indexer.js.map +1 -1
  16. package/dist/bin/sqlite-indexer.cjs +567 -0
  17. package/dist/bin/sqlite-indexer.cjs.map +1 -0
  18. package/dist/bin/sqlite-indexer.d.cts +1 -0
  19. package/dist/bin/sqlite-indexer.js +243 -1
  20. package/dist/bin/sqlite-indexer.js.map +1 -1
  21. package/dist/chunk-66BWQNF7.js +38 -0
  22. package/dist/{chunk-R7HX5BT2.js.map → chunk-66BWQNF7.js.map} +1 -1
  23. package/dist/chunk-CGE4ONKA.js +44 -0
  24. package/dist/{chunk-YQ7E5W26.js.map → chunk-CGE4ONKA.js.map} +1 -1
  25. package/dist/chunk-GDNGJPVT.js +16 -0
  26. package/dist/{chunk-AYPBOJNL.js.map → chunk-GDNGJPVT.js.map} +1 -1
  27. package/dist/chunk-L5CWEDU6.js +53 -0
  28. package/dist/{chunk-O2SDU7EQ.js.map → chunk-L5CWEDU6.js.map} +1 -1
  29. package/dist/chunk-O4XAWAXU.js +72 -0
  30. package/dist/{chunk-ED45N3IT.js.map → chunk-O4XAWAXU.js.map} +1 -1
  31. package/dist/chunk-R7UQFYRA.js +99 -0
  32. package/dist/{chunk-JDWVOODJ.js.map → chunk-R7UQFYRA.js.map} +1 -1
  33. package/dist/chunk-SJLOWI5M.js +31 -0
  34. package/dist/{chunk-7O2ZWWUX.js.map → chunk-SJLOWI5M.js.map} +1 -1
  35. package/dist/healthcheck-2DQWYXPX.js +7 -0
  36. package/dist/helloWorld-6IXGINV6.js +7 -0
  37. package/dist/index.cjs +2 -0
  38. package/dist/index.d.cts +2 -0
  39. package/dist/metrics-HO5SO4EX.js +7 -0
  40. package/dist/metrics-HO5SO4EX.js.map +1 -0
  41. package/package.json +16 -7
  42. package/dist/chunk-7O2ZWWUX.js +0 -2
  43. package/dist/chunk-AYPBOJNL.js +0 -2
  44. package/dist/chunk-ED45N3IT.js +0 -2
  45. package/dist/chunk-JDWVOODJ.js +0 -2
  46. package/dist/chunk-O2SDU7EQ.js +0 -7
  47. package/dist/chunk-R7HX5BT2.js +0 -2
  48. package/dist/chunk-YQ7E5W26.js +0 -2
  49. package/dist/healthcheck-57YETUEX.js +0 -2
  50. package/dist/helloWorld-4VT4FZ7F.js +0 -2
  51. package/dist/metrics-4BMCDEZZ.js +0 -2
  52. /package/dist/{healthcheck-57YETUEX.js.map → healthcheck-2DQWYXPX.js.map} +0 -0
  53. /package/dist/{helloWorld-4VT4FZ7F.js.map → helloWorld-6IXGINV6.js.map} +0 -0
  54. /package/dist/{metrics-4BMCDEZZ.js.map → index.cjs.map} +0 -0
@@ -1,12 +1,173 @@
1
1
  #!/usr/bin/env node
2
- import{a as D}from"../chunk-R7HX5BT2.js";import{a as b,b as T,c as x}from"../chunk-JDWVOODJ.js";import{a as $,c as L}from"../chunk-O2SDU7EQ.js";import{a as w}from"../chunk-7O2ZWWUX.js";import{a as I}from"../chunk-AYPBOJNL.js";import{a as _}from"../chunk-ED45N3IT.js";import"dotenv/config";import{z as S}from"zod";import ae from"koa";import se from"@koa/cors";import{createKoaMiddleware as ie}from"trpc-koa-adapter";import{createAppRouter as de}from"@latticexyz/store-sync/trpc-indexer";import{drizzle as ce}from"drizzle-orm/postgres-js";import me from"postgres";import{getAddress as E}from"viem";import{isTableRegistrationLog as j,logToTable as Y,schemasTable as U}from"@latticexyz/store-sync";import{decodeKey as V,decodeValueArgs as q}from"@latticexyz/protocol-parser/internal";import{tables as d}from"@latticexyz/store-sync/postgres";import{and as F,asc as H,eq as u,or as W}from"drizzle-orm";import{bigIntMax as J}from"@latticexyz/common/utils";import{decodeDynamicField as z}from"@latticexyz/protocol-parser/internal";function h(e){return{address:e.address,eventName:"Store_SetRecord",args:{tableId:e.tableId,keyTuple:z("bytes32[]",e.keyBytes),staticData:e.staticData??"0x",encodedLengths:e.encodedLengths??"0x",dynamicData:e.dynamicData??"0x"}}}import{createBenchmark as K}from"@latticexyz/common";async function k(e,{chainId:t,address:r,filters:a=[]}){let o=K("drizzleGetLogs"),n=a.length?a.map(s=>F(r!=null?u(d.recordsTable.address,r):void 0,u(d.recordsTable.tableId,s.tableId),s.key0!=null?u(d.recordsTable.key0,s.key0):void 0,s.key1!=null?u(d.recordsTable.key1,s.key1):void 0)):r!=null?[u(d.recordsTable.address,r)]:[];o("parse config");let f=(await e.select().from(d.configTable).where(u(d.configTable.chainId,t)).limit(1).execute().then(s=>s.find(()=>!0)))?.blockNumber??0n;o("query chainState");let l=await e.select().from(d.recordsTable).where(W(...n)).orderBy(H(d.recordsTable.blockNumber));o("query records");let i=l.reduce((s,y)=>J(s,y.blockNumber??0n),f);o("find block number");let R=l.filter(s=>!s.isDeleted).map(h);return o("map records to logs"),{blockNumber:i,logs:R}}import{groupBy as G}from"@latticexyz/common/utils";async function O(e){return{async getLogs(r){return k(e,r)},async findAll(r){let a=r.filters??[],{blockNumber:o,logs:n}=await k(e,{...r,filters:a.length>0?[...a,{tableId:U.tableId}]:[]}),p=n.filter(j).map(Y),f=G(n,i=>`${E(i.address)}:${i.args.tableId}`),l=p.map(i=>{let s=(f.get(`${E(i.address)}:${i.tableId}`)??[]).map(y=>{let A=V(i.keySchema,y.args.keyTuple),N=q(i.valueSchema,y.args);return{key:A,value:N,fields:{...A,...N}}});return{...i,records:s}});return b("findAll: decoded %d logs across %d tables",n.length,p.length),{blockNumber:o,tables:l}}}}import ee from"@koa/router";import re from"koa-compose";import{input as oe}from"@latticexyz/store-sync/indexer-client";import{schemasTable as te}from"@latticexyz/store-sync";import{isNotNull as B}from"@latticexyz/common/utils";import{hexToBytes as g}from"viem";import{transformSchemaName as X}from"@latticexyz/store-sync/postgres";var P=X("mud");function v(e,t){return e`(${t.reduce((r,a)=>e`${r} AND ${a}`)})`}function Z(e,t){return e`(${t.reduce((r,a)=>e`${r} OR ${a}`)})`}function C(e,t){let r=t.filters.length?t.filters.map(o=>v(e,[t.address!=null?e`address = ${g(t.address)}`:null,e`table_id = ${g(o.tableId)}`,o.key0!=null?e`key0 = ${g(o.key0)}`:null,o.key1!=null?e`key1 = ${g(o.key1)}`:null].filter(B))):t.address!=null?[e`address = ${g(t.address)}`]:[],a=e`WHERE ${v(e,[e`is_deleted != true`,r.length?Z(e,r):null].filter(B))}`;return e`
2
+ import {
3
+ compress
4
+ } from "../chunk-66BWQNF7.js";
5
+ import {
6
+ debug,
7
+ error,
8
+ sentry
9
+ } from "../chunk-R7UQFYRA.js";
10
+ import {
11
+ frontendEnvSchema,
12
+ parseEnv
13
+ } from "../chunk-L5CWEDU6.js";
14
+ import {
15
+ healthcheck
16
+ } from "../chunk-SJLOWI5M.js";
17
+ import {
18
+ helloWorld
19
+ } from "../chunk-GDNGJPVT.js";
20
+ import {
21
+ metrics
22
+ } from "../chunk-O4XAWAXU.js";
23
+
24
+ // src/bin/postgres-frontend.ts
25
+ import "dotenv/config";
26
+ import { z } from "zod";
27
+ import Koa from "koa";
28
+ import cors from "@koa/cors";
29
+ import { createKoaMiddleware } from "trpc-koa-adapter";
30
+ import { createAppRouter } from "@latticexyz/store-sync/trpc-indexer";
31
+ import { drizzle } from "drizzle-orm/postgres-js";
32
+ import postgres from "postgres";
33
+
34
+ // src/postgres/deprecated/createQueryAdapter.ts
35
+ import { getAddress } from "viem";
36
+ import { isTableRegistrationLog, logToTable, schemasTable } from "@latticexyz/store-sync";
37
+ import { decodeKey, decodeValueArgs } from "@latticexyz/protocol-parser/internal";
38
+
39
+ // src/postgres/deprecated/getLogs.ts
40
+ import { tables } from "@latticexyz/store-sync/postgres";
41
+ import { and, asc, eq, or } from "drizzle-orm";
42
+ import { bigIntMax } from "@latticexyz/common/utils";
43
+
44
+ // src/postgres/recordToLog.ts
45
+ import { decodeDynamicField } from "@latticexyz/protocol-parser/internal";
46
+ function recordToLog(record) {
47
+ return {
48
+ address: record.address,
49
+ eventName: "Store_SetRecord",
50
+ args: {
51
+ tableId: record.tableId,
52
+ keyTuple: decodeDynamicField("bytes32[]", record.keyBytes),
53
+ staticData: record.staticData ?? "0x",
54
+ encodedLengths: record.encodedLengths ?? "0x",
55
+ dynamicData: record.dynamicData ?? "0x"
56
+ }
57
+ };
58
+ }
59
+
60
+ // src/postgres/deprecated/getLogs.ts
61
+ import { createBenchmark } from "@latticexyz/common";
62
+ async function getLogs(database, {
63
+ chainId,
64
+ address,
65
+ filters = []
66
+ }) {
67
+ const benchmark = createBenchmark("drizzleGetLogs");
68
+ const conditions = filters.length ? filters.map(
69
+ (filter) => and(
70
+ address != null ? eq(tables.recordsTable.address, address) : void 0,
71
+ eq(tables.recordsTable.tableId, filter.tableId),
72
+ filter.key0 != null ? eq(tables.recordsTable.key0, filter.key0) : void 0,
73
+ filter.key1 != null ? eq(tables.recordsTable.key1, filter.key1) : void 0
74
+ )
75
+ ) : address != null ? [eq(tables.recordsTable.address, address)] : [];
76
+ benchmark("parse config");
77
+ const chainState = await database.select().from(tables.configTable).where(eq(tables.configTable.chainId, chainId)).limit(1).execute().then((rows) => rows.find(() => true));
78
+ const indexerBlockNumber = chainState?.blockNumber ?? 0n;
79
+ benchmark("query chainState");
80
+ const records = await database.select().from(tables.recordsTable).where(or(...conditions)).orderBy(
81
+ asc(tables.recordsTable.blockNumber)
82
+ // TODO: add logIndex (https://github.com/latticexyz/mud/issues/1979)
83
+ );
84
+ benchmark("query records");
85
+ const blockNumber = records.reduce((max, record) => bigIntMax(max, record.blockNumber ?? 0n), indexerBlockNumber);
86
+ benchmark("find block number");
87
+ const logs = records.filter((record) => !record.isDeleted).map(recordToLog);
88
+ benchmark("map records to logs");
89
+ return { blockNumber, logs };
90
+ }
91
+
92
+ // src/postgres/deprecated/createQueryAdapter.ts
93
+ import { groupBy } from "@latticexyz/common/utils";
94
+ async function createQueryAdapter(database) {
95
+ const adapter = {
96
+ async getLogs(opts) {
97
+ return getLogs(database, opts);
98
+ },
99
+ async findAll(opts) {
100
+ const filters = opts.filters ?? [];
101
+ const { blockNumber, logs } = await getLogs(database, {
102
+ ...opts,
103
+ // make sure we're always retrieving `store.Tables` table, so we can decode table values
104
+ filters: filters.length > 0 ? [...filters, { tableId: schemasTable.tableId }] : []
105
+ });
106
+ const tables2 = logs.filter(isTableRegistrationLog).map(logToTable);
107
+ const logsByTable = groupBy(logs, (log) => `${getAddress(log.address)}:${log.args.tableId}`);
108
+ const tablesWithRecords = tables2.map((table) => {
109
+ const tableLogs = logsByTable.get(`${getAddress(table.address)}:${table.tableId}`) ?? [];
110
+ const records = tableLogs.map((log) => {
111
+ const key = decodeKey(table.keySchema, log.args.keyTuple);
112
+ const value = decodeValueArgs(table.valueSchema, log.args);
113
+ return { key, value, fields: { ...key, ...value } };
114
+ });
115
+ return {
116
+ ...table,
117
+ records
118
+ };
119
+ });
120
+ debug("findAll: decoded %d logs across %d tables", logs.length, tables2.length);
121
+ return {
122
+ blockNumber,
123
+ tables: tablesWithRecords
124
+ };
125
+ }
126
+ };
127
+ return adapter;
128
+ }
129
+
130
+ // src/postgres/apiRoutes.ts
131
+ import Router from "@koa/router";
132
+ import compose from "koa-compose";
133
+ import { input } from "@latticexyz/store-sync/indexer-client";
134
+ import { schemasTable as schemasTable2 } from "@latticexyz/store-sync";
135
+
136
+ // src/postgres/queryLogs.ts
137
+ import { isNotNull } from "@latticexyz/common/utils";
138
+ import { hexToBytes } from "viem";
139
+ import { transformSchemaName } from "@latticexyz/store-sync/postgres";
140
+ var schemaName = transformSchemaName("mud");
141
+ function and2(sql, conditions) {
142
+ return sql`(${conditions.reduce((query, condition) => sql`${query} AND ${condition}`)})`;
143
+ }
144
+ function or2(sql, conditions) {
145
+ return sql`(${conditions.reduce((query, condition) => sql`${query} OR ${condition}`)})`;
146
+ }
147
+ function queryLogs(sql, opts) {
148
+ const conditions = opts.filters.length ? opts.filters.map(
149
+ (filter) => and2(
150
+ sql,
151
+ [
152
+ opts.address != null ? sql`address = ${hexToBytes(opts.address)}` : null,
153
+ sql`table_id = ${hexToBytes(filter.tableId)}`,
154
+ filter.key0 != null ? sql`key0 = ${hexToBytes(filter.key0)}` : null,
155
+ filter.key1 != null ? sql`key1 = ${hexToBytes(filter.key1)}` : null
156
+ ].filter(isNotNull)
157
+ )
158
+ ) : opts.address != null ? [sql`address = ${hexToBytes(opts.address)}`] : [];
159
+ const where = sql`WHERE ${and2(
160
+ sql,
161
+ [sql`is_deleted != true`, conditions.length ? or2(sql, conditions) : null].filter(isNotNull)
162
+ )}`;
163
+ return sql`
3
164
  WITH
4
165
  config AS (
5
166
  SELECT
6
167
  version AS "indexerVersion",
7
168
  chain_id AS "chainId",
8
169
  block_number AS "chainBlockNumber"
9
- FROM ${e(`${P}.config`)}
170
+ FROM ${sql(`${schemaName}.config`)}
10
171
  LIMIT 1
11
172
  ),
12
173
  records AS (
@@ -19,13 +180,105 @@ import{a as D}from"../chunk-R7HX5BT2.js";import{a as b,b as T,c as x}from"../chu
19
180
  '0x' || encode(dynamic_data, 'hex') AS "dynamicData",
20
181
  block_number AS "recordBlockNumber",
21
182
  log_index AS "logIndex"
22
- FROM ${e(`${P}.records`)}
23
- ${a}
183
+ FROM ${sql(`${schemaName}.records`)}
184
+ ${where}
24
185
  ORDER BY block_number, log_index ASC
25
186
  )
26
187
  SELECT
27
188
  (SELECT COUNT(*) FROM records) AS "totalRows",
28
189
  *
29
190
  FROM config, records
30
- `}import{createBenchmark as ne}from"@latticexyz/common";function M(e){let t=new ee;return t.get("/api/logs",D(),async r=>{let a=ne("postgres:logs"),o;try{o=oe.parse(typeof r.query.input=="string"?JSON.parse(r.query.input):{})}catch(n){r.status=400,r.set("Content-Type","application/json"),r.body=JSON.stringify(n),b(n);return}try{o.filters=o.filters.length>0?[...o.filters,{tableId:te.tableId}]:[];let n=await C(e,o??{}).execute();a("query records");let p=n.map(h);if(a("map records to logs"),n.length===0){r.status=404,r.body="no logs found",T(`no logs found for chainId ${o.chainId}, address ${o.address}, filters ${JSON.stringify(o.filters)}`);return}let f=n[0].chainBlockNumber;r.status=200;let l=60*5,i=4e3*2;r.set("Cache-Control",`public, max-age=${l}, stale-while-revalidate=${i}`),r.set("Content-Type","application/json"),r.body=JSON.stringify({blockNumber:f,logs:p})}catch(n){r.status=500,r.set("Content-Type","application/json"),r.body=JSON.stringify(n),T(n)}}),re([t.routes(),t.allowedMethods()])}var m=L(S.intersection($,S.object({DATABASE_URL:S.string(),SENTRY_DSN:S.string().optional()}))),Q=me(m.DATABASE_URL,{prepare:!1}),c=new ae;m.SENTRY_DSN&&c.use(x(m.SENTRY_DSN));c.use(se());c.use(w());c.use(_({isHealthy:()=>!0,isReady:()=>!0}));c.use(I());c.use(M(Q));c.use(ie({prefix:"/trpc",router:de(),createContext:async()=>({queryAdapter:await O(ce(Q))})}));c.listen({host:m.HOST,port:m.PORT});console.log(`postgres indexer frontend listening on http://${m.HOST}:${m.PORT}`);
191
+ `;
192
+ }
193
+
194
+ // src/postgres/apiRoutes.ts
195
+ import { createBenchmark as createBenchmark2 } from "@latticexyz/common";
196
+ function apiRoutes(database) {
197
+ const router = new Router();
198
+ router.get("/api/logs", compress(), async (ctx) => {
199
+ const benchmark = createBenchmark2("postgres:logs");
200
+ let options;
201
+ try {
202
+ options = input.parse(typeof ctx.query.input === "string" ? JSON.parse(ctx.query.input) : {});
203
+ } catch (e) {
204
+ ctx.status = 400;
205
+ ctx.set("Content-Type", "application/json");
206
+ ctx.body = JSON.stringify(e);
207
+ debug(e);
208
+ return;
209
+ }
210
+ try {
211
+ options.filters = options.filters.length > 0 ? [...options.filters, { tableId: schemasTable2.tableId }] : [];
212
+ const records = await queryLogs(database, options ?? {}).execute();
213
+ benchmark("query records");
214
+ const logs = records.map(recordToLog);
215
+ benchmark("map records to logs");
216
+ if (records.length === 0) {
217
+ ctx.status = 404;
218
+ ctx.body = "no logs found";
219
+ error(
220
+ `no logs found for chainId ${options.chainId}, address ${options.address}, filters ${JSON.stringify(
221
+ options.filters
222
+ )}`
223
+ );
224
+ return;
225
+ }
226
+ const blockNumber = records[0].chainBlockNumber;
227
+ ctx.status = 200;
228
+ const maxAgeSeconds = 60 * 5;
229
+ const staleWhileRevalidateSeconds = 4e3 * 2;
230
+ ctx.set(
231
+ "Cache-Control",
232
+ `public, max-age=${maxAgeSeconds}, stale-while-revalidate=${staleWhileRevalidateSeconds}`
233
+ );
234
+ ctx.set("Content-Type", "application/json");
235
+ ctx.body = JSON.stringify({ blockNumber, logs });
236
+ } catch (e) {
237
+ ctx.status = 500;
238
+ ctx.set("Content-Type", "application/json");
239
+ ctx.body = JSON.stringify(e);
240
+ error(e);
241
+ }
242
+ });
243
+ return compose([router.routes(), router.allowedMethods()]);
244
+ }
245
+
246
+ // src/bin/postgres-frontend.ts
247
+ (async () => {
248
+ const env = parseEnv(
249
+ z.intersection(
250
+ frontendEnvSchema,
251
+ z.object({
252
+ DATABASE_URL: z.string(),
253
+ SENTRY_DSN: z.string().optional()
254
+ })
255
+ )
256
+ );
257
+ const database = postgres(env.DATABASE_URL, { prepare: false });
258
+ const server = new Koa();
259
+ if (env.SENTRY_DSN) {
260
+ server.use(sentry(env.SENTRY_DSN));
261
+ }
262
+ server.use(cors());
263
+ server.use(healthcheck());
264
+ server.use(
265
+ metrics({
266
+ isHealthy: () => true,
267
+ isReady: () => true
268
+ })
269
+ );
270
+ server.use(helloWorld());
271
+ server.use(apiRoutes(database));
272
+ server.use(
273
+ createKoaMiddleware({
274
+ prefix: "/trpc",
275
+ router: createAppRouter(),
276
+ createContext: async () => ({
277
+ queryAdapter: await createQueryAdapter(drizzle(database))
278
+ })
279
+ })
280
+ );
281
+ server.listen({ host: env.HOST, port: env.PORT });
282
+ console.log(`postgres indexer frontend listening on http://${env.HOST}:${env.PORT}`);
283
+ })();
31
284
  //# sourceMappingURL=postgres-frontend.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/bin/postgres-frontend.ts","../../src/postgres/deprecated/createQueryAdapter.ts","../../src/postgres/deprecated/getLogs.ts","../../src/postgres/recordToLog.ts","../../src/postgres/apiRoutes.ts","../../src/postgres/queryLogs.ts"],"sourcesContent":["#!/usr/bin/env node\nimport \"dotenv/config\";\nimport { z } from \"zod\";\nimport Koa from \"koa\";\nimport cors from \"@koa/cors\";\nimport { createKoaMiddleware } from \"trpc-koa-adapter\";\nimport { createAppRouter } from \"@latticexyz/store-sync/trpc-indexer\";\nimport { drizzle } from \"drizzle-orm/postgres-js\";\nimport postgres from \"postgres\";\nimport { frontendEnvSchema, parseEnv } from \"./parseEnv\";\nimport { createQueryAdapter } from \"../postgres/deprecated/createQueryAdapter\";\nimport { apiRoutes } from \"../postgres/apiRoutes\";\nimport { sentry } from \"../koa-middleware/sentry\";\nimport { healthcheck } from \"../koa-middleware/healthcheck\";\nimport { helloWorld } from \"../koa-middleware/helloWorld\";\nimport { metrics } from \"../koa-middleware/metrics\";\n\nconst env = parseEnv(\n z.intersection(\n frontendEnvSchema,\n z.object({\n DATABASE_URL: z.string(),\n SENTRY_DSN: z.string().optional(),\n }),\n ),\n);\n\nconst database = postgres(env.DATABASE_URL, { prepare: false });\n\nconst server = new Koa();\n\nif (env.SENTRY_DSN) {\n server.use(sentry(env.SENTRY_DSN));\n}\n\nserver.use(cors());\nserver.use(healthcheck());\nserver.use(\n metrics({\n isHealthy: () => true,\n isReady: () => true,\n }),\n);\nserver.use(helloWorld());\nserver.use(apiRoutes(database));\n\nserver.use(\n createKoaMiddleware({\n prefix: \"/trpc\",\n router: createAppRouter(),\n createContext: async () => ({\n queryAdapter: await createQueryAdapter(drizzle(database)),\n }),\n }),\n);\n\nserver.listen({ host: env.HOST, port: env.PORT });\nconsole.log(`postgres indexer frontend listening on http://${env.HOST}:${env.PORT}`);\n","import { getAddress } from \"viem\";\nimport { PgDatabase } from \"drizzle-orm/pg-core\";\nimport { TableWithRecords, isTableRegistrationLog, logToTable, schemasTable } from \"@latticexyz/store-sync\";\nimport { KeySchema, decodeKey, decodeValueArgs } from \"@latticexyz/protocol-parser/internal\";\nimport { QueryAdapter } from \"@latticexyz/store-sync/trpc-indexer\";\nimport { debug } from \"../../debug\";\nimport { getLogs } from \"./getLogs\";\nimport { groupBy } from \"@latticexyz/common/utils\";\n\n/**\n * Creates a query adapter for the tRPC server/client to query data from Postgres.\n *\n * @param {PgDatabase<any>} database Postgres database object from Drizzle\n * @returns {Promise<QueryAdapter>} A set of methods used by tRPC endpoints.\n * @deprecated\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport async function createQueryAdapter(database: PgDatabase<any>): Promise<QueryAdapter> {\n const adapter: QueryAdapter = {\n async getLogs(opts) {\n return getLogs(database, opts);\n },\n async findAll(opts) {\n const filters = opts.filters ?? [];\n const { blockNumber, logs } = await getLogs(database, {\n ...opts,\n // make sure we're always retrieving `store.Tables` table, so we can decode table values\n filters: filters.length > 0 ? [...filters, { tableId: schemasTable.tableId }] : [],\n });\n\n const tables = logs.filter(isTableRegistrationLog).map(logToTable);\n\n const logsByTable = groupBy(logs, (log) => `${getAddress(log.address)}:${log.args.tableId}`);\n\n const tablesWithRecords: readonly TableWithRecords[] = tables.map((table) => {\n const tableLogs = logsByTable.get(`${getAddress(table.address)}:${table.tableId}`) ?? [];\n const records = tableLogs.map((log) => {\n const key = decodeKey(table.keySchema as KeySchema, log.args.keyTuple);\n const value = decodeValueArgs(table.valueSchema, log.args);\n return { key, value, fields: { ...key, ...value } };\n });\n\n return {\n ...table,\n records,\n };\n });\n\n debug(\"findAll: decoded %d logs across %d tables\", logs.length, tables.length);\n\n return {\n blockNumber,\n tables: tablesWithRecords,\n };\n },\n };\n return adapter;\n}\n","import { PgDatabase } from \"drizzle-orm/pg-core\";\nimport { Hex } from \"viem\";\nimport { StorageAdapterLog, SyncFilter } from \"@latticexyz/store-sync\";\nimport { tables } from \"@latticexyz/store-sync/postgres\";\nimport { and, asc, eq, or } from \"drizzle-orm\";\nimport { bigIntMax } from \"@latticexyz/common/utils\";\nimport { recordToLog } from \"../recordToLog\";\nimport { createBenchmark } from \"@latticexyz/common\";\n\n/**\n * @deprecated\n */\nexport async function getLogs(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n database: PgDatabase<any>,\n {\n chainId,\n address,\n filters = [],\n }: {\n readonly chainId: number;\n readonly address?: Hex;\n readonly filters?: readonly SyncFilter[];\n },\n): Promise<{ blockNumber: bigint; logs: Extract<StorageAdapterLog, { eventName: \"Store_SetRecord\" }>[] }> {\n const benchmark = createBenchmark(\"drizzleGetLogs\");\n\n const conditions = filters.length\n ? filters.map((filter) =>\n and(\n address != null ? eq(tables.recordsTable.address, address) : undefined,\n eq(tables.recordsTable.tableId, filter.tableId),\n filter.key0 != null ? eq(tables.recordsTable.key0, filter.key0) : undefined,\n filter.key1 != null ? eq(tables.recordsTable.key1, filter.key1) : undefined,\n ),\n )\n : address != null\n ? [eq(tables.recordsTable.address, address)]\n : [];\n benchmark(\"parse config\");\n\n // Query for the block number that the indexer (i.e. chain) is at, in case the\n // indexer is further along in the chain than a given store/table's last updated\n // block number. We'll then take the highest block number between the indexer's\n // chain state and all the records in the query (in case the records updated\n // between these queries). Using just the highest block number from the queries\n // could potentially signal to the client an older-than-necessary block number,\n // for stores/tables that haven't seen recent activity.\n // TODO: move the block number query into the records query for atomicity so we don't have to merge them here\n const chainState = await database\n .select()\n .from(tables.configTable)\n .where(eq(tables.configTable.chainId, chainId))\n .limit(1)\n .execute()\n // Get the first record in a way that returns a possible `undefined`\n // TODO: move this to `.findFirst` after upgrading drizzle or `rows[0]` after enabling `noUncheckedIndexedAccess: true`\n .then((rows) => rows.find(() => true));\n const indexerBlockNumber = chainState?.blockNumber ?? 0n;\n benchmark(\"query chainState\");\n\n const records = await database\n .select()\n .from(tables.recordsTable)\n .where(or(...conditions))\n .orderBy(\n asc(tables.recordsTable.blockNumber),\n // TODO: add logIndex (https://github.com/latticexyz/mud/issues/1979)\n );\n benchmark(\"query records\");\n\n const blockNumber = records.reduce((max, record) => bigIntMax(max, record.blockNumber ?? 0n), indexerBlockNumber);\n benchmark(\"find block number\");\n\n const logs = records\n // TODO: add this to the query, assuming we can optimize with an index\n .filter((record) => !record.isDeleted)\n .map(recordToLog);\n benchmark(\"map records to logs\");\n\n return { blockNumber, logs };\n}\n","import { StorageAdapterLog } from \"@latticexyz/store-sync\";\nimport { decodeDynamicField } from \"@latticexyz/protocol-parser/internal\";\nimport { RecordData } from \"./common\";\n\nexport function recordToLog(\n record: Omit<RecordData, \"recordBlockNumber\">,\n): Extract<StorageAdapterLog, { eventName: \"Store_SetRecord\" }> {\n return {\n address: record.address,\n eventName: \"Store_SetRecord\",\n args: {\n tableId: record.tableId,\n keyTuple: decodeDynamicField(\"bytes32[]\", record.keyBytes),\n staticData: record.staticData ?? \"0x\",\n encodedLengths: record.encodedLengths ?? \"0x\",\n dynamicData: record.dynamicData ?? \"0x\",\n },\n } as const;\n}\n","import { Sql } from \"postgres\";\nimport { Middleware } from \"koa\";\nimport Router from \"@koa/router\";\nimport compose from \"koa-compose\";\nimport { input } from \"@latticexyz/store-sync/indexer-client\";\nimport { schemasTable } from \"@latticexyz/store-sync\";\nimport { queryLogs } from \"./queryLogs\";\nimport { recordToLog } from \"./recordToLog\";\nimport { debug, error } from \"../debug\";\nimport { createBenchmark } from \"@latticexyz/common\";\nimport { compress } from \"../koa-middleware/compress\";\n\nexport function apiRoutes(database: Sql): Middleware {\n const router = new Router();\n\n router.get(\"/api/logs\", compress(), async (ctx) => {\n const benchmark = createBenchmark(\"postgres:logs\");\n let options: ReturnType<typeof input.parse>;\n\n try {\n options = input.parse(typeof ctx.query.input === \"string\" ? JSON.parse(ctx.query.input) : {});\n } catch (e) {\n ctx.status = 400;\n ctx.set(\"Content-Type\", \"application/json\");\n ctx.body = JSON.stringify(e);\n debug(e);\n return;\n }\n\n try {\n options.filters = options.filters.length > 0 ? [...options.filters, { tableId: schemasTable.tableId }] : [];\n const records = await queryLogs(database, options ?? {}).execute();\n benchmark(\"query records\");\n const logs = records.map(recordToLog);\n benchmark(\"map records to logs\");\n\n // Ideally we would immediately return an error if the request is for a Store that the indexer\n // is not configured to index. Since we don't have easy access to this information here,\n // we return an error if there are no logs found for a given Store, since that would never\n // be the case for a Store that is being indexed (since there would at least be records for the\n // Tables table with tables created during Store initialization).\n if (records.length === 0) {\n ctx.status = 404;\n ctx.body = \"no logs found\";\n error(\n `no logs found for chainId ${options.chainId}, address ${options.address}, filters ${JSON.stringify(\n options.filters,\n )}`,\n );\n return;\n }\n\n const blockNumber = records[0].chainBlockNumber;\n ctx.status = 200;\n\n // max age is set to several multiples of the uncached response time (currently ~10s, but using 60s for wiggle room) to ensure only ~one origin request at a time\n // and stale-while-revalidate below means that the cache is refreshed under the hood while still responding fast (cached)\n const maxAgeSeconds = 60 * 5;\n // we set stale-while-revalidate to the time elapsed by the number of blocks we can fetch from the RPC in the same amount of time as an uncached response\n // meaning it would take ~the same about of time to get an uncached response from the origin as it would to catch up from the currently cached response\n // if an uncached response takes ~10 seconds, we have ~10s to catch up, so let's say we can do enough RPC calls to fetch 4000 blocks\n // with a block per 2 seconds, that means we can serve a stale/cached response for 8000 seconds before we should require the response be returned by the origin\n const staleWhileRevalidateSeconds = 4000 * 2;\n\n ctx.set(\n \"Cache-Control\",\n `public, max-age=${maxAgeSeconds}, stale-while-revalidate=${staleWhileRevalidateSeconds}`,\n );\n\n ctx.set(\"Content-Type\", \"application/json\");\n ctx.body = JSON.stringify({ blockNumber, logs });\n } catch (e) {\n ctx.status = 500;\n ctx.set(\"Content-Type\", \"application/json\");\n ctx.body = JSON.stringify(e);\n error(e);\n }\n });\n\n return compose([router.routes(), router.allowedMethods()]) as Middleware;\n}\n","import { isNotNull } from \"@latticexyz/common/utils\";\nimport { PendingQuery, Row, Sql } from \"postgres\";\nimport { hexToBytes } from \"viem\";\nimport { z } from \"zod\";\nimport { input } from \"@latticexyz/store-sync/indexer-client\";\nimport { transformSchemaName } from \"@latticexyz/store-sync/postgres\";\nimport { Record } from \"./common\";\n\nconst schemaName = transformSchemaName(\"mud\");\n\nfunction and(sql: Sql, conditions: PendingQuery<Row[]>[]): PendingQuery<Row[]> {\n return sql`(${conditions.reduce((query, condition) => sql`${query} AND ${condition}`)})`;\n}\n\nfunction or(sql: Sql, conditions: PendingQuery<Row[]>[]): PendingQuery<Row[]> {\n return sql`(${conditions.reduce((query, condition) => sql`${query} OR ${condition}`)})`;\n}\n\nexport function queryLogs(sql: Sql, opts: z.infer<typeof input>): PendingQuery<Record[]> {\n const conditions = opts.filters.length\n ? opts.filters.map((filter) =>\n and(\n sql,\n [\n opts.address != null ? sql`address = ${hexToBytes(opts.address)}` : null,\n sql`table_id = ${hexToBytes(filter.tableId)}`,\n filter.key0 != null ? sql`key0 = ${hexToBytes(filter.key0)}` : null,\n filter.key1 != null ? sql`key1 = ${hexToBytes(filter.key1)}` : null,\n ].filter(isNotNull),\n ),\n )\n : opts.address != null\n ? [sql`address = ${hexToBytes(opts.address)}`]\n : [];\n\n const where = sql`WHERE ${and(\n sql,\n [sql`is_deleted != true`, conditions.length ? or(sql, conditions) : null].filter(isNotNull),\n )}`;\n\n // TODO: implement bytea <> hex columns via custom types: https://github.com/porsager/postgres#custom-types\n return sql<Record[]>`\n WITH\n config AS (\n SELECT\n version AS \"indexerVersion\",\n chain_id AS \"chainId\",\n block_number AS \"chainBlockNumber\"\n FROM ${sql(`${schemaName}.config`)}\n LIMIT 1\n ),\n records AS (\n SELECT\n '0x' || encode(address, 'hex') AS address,\n '0x' || encode(table_id, 'hex') AS \"tableId\",\n '0x' || encode(key_bytes, 'hex') AS \"keyBytes\",\n '0x' || encode(static_data, 'hex') AS \"staticData\",\n '0x' || encode(encoded_lengths, 'hex') AS \"encodedLengths\",\n '0x' || encode(dynamic_data, 'hex') AS \"dynamicData\",\n block_number AS \"recordBlockNumber\",\n log_index AS \"logIndex\"\n FROM ${sql(`${schemaName}.records`)}\n ${where}\n ORDER BY block_number, log_index ASC\n )\n SELECT\n (SELECT COUNT(*) FROM records) AS \"totalRows\",\n *\n FROM config, records\n `;\n}\n"],"mappings":"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","names":["z","Koa","cors","createKoaMiddleware","createAppRouter","drizzle","postgres","getAddress","isTableRegistrationLog","logToTable","schemasTable","decodeKey","decodeValueArgs","tables","and","asc","eq","or","bigIntMax","decodeDynamicField","recordToLog","record","createBenchmark","getLogs","database","chainId","address","filters","benchmark","conditions","filter","and","eq","tables","indexerBlockNumber","rows","records","or","asc","blockNumber","max","record","bigIntMax","logs","recordToLog","groupBy","createQueryAdapter","database","opts","getLogs","filters","blockNumber","logs","schemasTable","tables","isTableRegistrationLog","logToTable","logsByTable","log","getAddress","tablesWithRecords","table","records","key","decodeKey","value","decodeValueArgs","debug","Router","compose","input","schemasTable","isNotNull","hexToBytes","transformSchemaName","schemaName","and","sql","conditions","query","condition","or","queryLogs","opts","filter","where","createBenchmark","apiRoutes","database","router","Router","compress","ctx","benchmark","createBenchmark","options","input","e","debug","schemasTable","records","queryLogs","logs","recordToLog","error","blockNumber","maxAgeSeconds","staleWhileRevalidateSeconds","compose","env","parseEnv","z","frontendEnvSchema","database","postgres","server","Koa","sentry","cors","healthcheck","metrics","helloWorld","apiRoutes","createKoaMiddleware","createAppRouter","createQueryAdapter","drizzle"]}
1
+ {"version":3,"sources":["../../src/bin/postgres-frontend.ts","../../src/postgres/deprecated/createQueryAdapter.ts","../../src/postgres/deprecated/getLogs.ts","../../src/postgres/recordToLog.ts","../../src/postgres/apiRoutes.ts","../../src/postgres/queryLogs.ts"],"sourcesContent":["#!/usr/bin/env node\nimport \"dotenv/config\";\nimport { z } from \"zod\";\nimport Koa from \"koa\";\nimport cors from \"@koa/cors\";\nimport { createKoaMiddleware } from \"trpc-koa-adapter\";\nimport { createAppRouter } from \"@latticexyz/store-sync/trpc-indexer\";\nimport { drizzle } from \"drizzle-orm/postgres-js\";\nimport postgres from \"postgres\";\nimport { frontendEnvSchema, parseEnv } from \"./parseEnv\";\nimport { createQueryAdapter } from \"../postgres/deprecated/createQueryAdapter\";\nimport { apiRoutes } from \"../postgres/apiRoutes\";\nimport { sentry } from \"../koa-middleware/sentry\";\nimport { healthcheck } from \"../koa-middleware/healthcheck\";\nimport { helloWorld } from \"../koa-middleware/helloWorld\";\nimport { metrics } from \"../koa-middleware/metrics\";\n\n// Workaround for:\n// Top-level await is currently not supported with the \"cjs\" output format\n(async (): Promise<void> => {\n const env = parseEnv(\n z.intersection(\n frontendEnvSchema,\n z.object({\n DATABASE_URL: z.string(),\n SENTRY_DSN: z.string().optional(),\n }),\n ),\n );\n\n const database = postgres(env.DATABASE_URL, { prepare: false });\n\n const server = new Koa();\n\n if (env.SENTRY_DSN) {\n server.use(sentry(env.SENTRY_DSN));\n }\n\n server.use(cors());\n server.use(healthcheck());\n server.use(\n metrics({\n isHealthy: () => true,\n isReady: () => true,\n }),\n );\n server.use(helloWorld());\n server.use(apiRoutes(database));\n\n server.use(\n createKoaMiddleware({\n prefix: \"/trpc\",\n router: createAppRouter(),\n createContext: async () => ({\n queryAdapter: await createQueryAdapter(drizzle(database)),\n }),\n }),\n );\n\n server.listen({ host: env.HOST, port: env.PORT });\n console.log(`postgres indexer frontend listening on http://${env.HOST}:${env.PORT}`);\n})();\n","import { getAddress } from \"viem\";\nimport { PgDatabase } from \"drizzle-orm/pg-core\";\nimport { TableWithRecords, isTableRegistrationLog, logToTable, schemasTable } from \"@latticexyz/store-sync\";\nimport { KeySchema, decodeKey, decodeValueArgs } from \"@latticexyz/protocol-parser/internal\";\nimport { QueryAdapter } from \"@latticexyz/store-sync/trpc-indexer\";\nimport { debug } from \"../../debug\";\nimport { getLogs } from \"./getLogs\";\nimport { groupBy } from \"@latticexyz/common/utils\";\n\n/**\n * Creates a query adapter for the tRPC server/client to query data from Postgres.\n *\n * @param {PgDatabase<any>} database Postgres database object from Drizzle\n * @returns {Promise<QueryAdapter>} A set of methods used by tRPC endpoints.\n * @deprecated\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport async function createQueryAdapter(database: PgDatabase<any>): Promise<QueryAdapter> {\n const adapter: QueryAdapter = {\n async getLogs(opts) {\n return getLogs(database, opts);\n },\n async findAll(opts) {\n const filters = opts.filters ?? [];\n const { blockNumber, logs } = await getLogs(database, {\n ...opts,\n // make sure we're always retrieving `store.Tables` table, so we can decode table values\n filters: filters.length > 0 ? [...filters, { tableId: schemasTable.tableId }] : [],\n });\n\n const tables = logs.filter(isTableRegistrationLog).map(logToTable);\n\n const logsByTable = groupBy(logs, (log) => `${getAddress(log.address)}:${log.args.tableId}`);\n\n const tablesWithRecords: readonly TableWithRecords[] = tables.map((table) => {\n const tableLogs = logsByTable.get(`${getAddress(table.address)}:${table.tableId}`) ?? [];\n const records = tableLogs.map((log) => {\n const key = decodeKey(table.keySchema as KeySchema, log.args.keyTuple);\n const value = decodeValueArgs(table.valueSchema, log.args);\n return { key, value, fields: { ...key, ...value } };\n });\n\n return {\n ...table,\n records,\n };\n });\n\n debug(\"findAll: decoded %d logs across %d tables\", logs.length, tables.length);\n\n return {\n blockNumber,\n tables: tablesWithRecords,\n };\n },\n };\n return adapter;\n}\n","import { PgDatabase } from \"drizzle-orm/pg-core\";\nimport { Hex } from \"viem\";\nimport { StorageAdapterLog, SyncFilter } from \"@latticexyz/store-sync\";\nimport { tables } from \"@latticexyz/store-sync/postgres\";\nimport { and, asc, eq, or } from \"drizzle-orm\";\nimport { bigIntMax } from \"@latticexyz/common/utils\";\nimport { recordToLog } from \"../recordToLog\";\nimport { createBenchmark } from \"@latticexyz/common\";\n\n/**\n * @deprecated\n */\nexport async function getLogs(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n database: PgDatabase<any>,\n {\n chainId,\n address,\n filters = [],\n }: {\n readonly chainId: number;\n readonly address?: Hex;\n readonly filters?: readonly SyncFilter[];\n },\n): Promise<{ blockNumber: bigint; logs: Extract<StorageAdapterLog, { eventName: \"Store_SetRecord\" }>[] }> {\n const benchmark = createBenchmark(\"drizzleGetLogs\");\n\n const conditions = filters.length\n ? filters.map((filter) =>\n and(\n address != null ? eq(tables.recordsTable.address, address) : undefined,\n eq(tables.recordsTable.tableId, filter.tableId),\n filter.key0 != null ? eq(tables.recordsTable.key0, filter.key0) : undefined,\n filter.key1 != null ? eq(tables.recordsTable.key1, filter.key1) : undefined,\n ),\n )\n : address != null\n ? [eq(tables.recordsTable.address, address)]\n : [];\n benchmark(\"parse config\");\n\n // Query for the block number that the indexer (i.e. chain) is at, in case the\n // indexer is further along in the chain than a given store/table's last updated\n // block number. We'll then take the highest block number between the indexer's\n // chain state and all the records in the query (in case the records updated\n // between these queries). Using just the highest block number from the queries\n // could potentially signal to the client an older-than-necessary block number,\n // for stores/tables that haven't seen recent activity.\n // TODO: move the block number query into the records query for atomicity so we don't have to merge them here\n const chainState = await database\n .select()\n .from(tables.configTable)\n .where(eq(tables.configTable.chainId, chainId))\n .limit(1)\n .execute()\n // Get the first record in a way that returns a possible `undefined`\n // TODO: move this to `.findFirst` after upgrading drizzle or `rows[0]` after enabling `noUncheckedIndexedAccess: true`\n .then((rows) => rows.find(() => true));\n const indexerBlockNumber = chainState?.blockNumber ?? 0n;\n benchmark(\"query chainState\");\n\n const records = await database\n .select()\n .from(tables.recordsTable)\n .where(or(...conditions))\n .orderBy(\n asc(tables.recordsTable.blockNumber),\n // TODO: add logIndex (https://github.com/latticexyz/mud/issues/1979)\n );\n benchmark(\"query records\");\n\n const blockNumber = records.reduce((max, record) => bigIntMax(max, record.blockNumber ?? 0n), indexerBlockNumber);\n benchmark(\"find block number\");\n\n const logs = records\n // TODO: add this to the query, assuming we can optimize with an index\n .filter((record) => !record.isDeleted)\n .map(recordToLog);\n benchmark(\"map records to logs\");\n\n return { blockNumber, logs };\n}\n","import { StorageAdapterLog } from \"@latticexyz/store-sync\";\nimport { decodeDynamicField } from \"@latticexyz/protocol-parser/internal\";\nimport { RecordData } from \"./common\";\n\nexport function recordToLog(\n record: Omit<RecordData, \"recordBlockNumber\">,\n): Extract<StorageAdapterLog, { eventName: \"Store_SetRecord\" }> {\n return {\n address: record.address,\n eventName: \"Store_SetRecord\",\n args: {\n tableId: record.tableId,\n keyTuple: decodeDynamicField(\"bytes32[]\", record.keyBytes),\n staticData: record.staticData ?? \"0x\",\n encodedLengths: record.encodedLengths ?? \"0x\",\n dynamicData: record.dynamicData ?? \"0x\",\n },\n } as const;\n}\n","import { Sql } from \"postgres\";\nimport { Middleware } from \"koa\";\nimport Router from \"@koa/router\";\nimport compose from \"koa-compose\";\nimport { input } from \"@latticexyz/store-sync/indexer-client\";\nimport { schemasTable } from \"@latticexyz/store-sync\";\nimport { queryLogs } from \"./queryLogs\";\nimport { recordToLog } from \"./recordToLog\";\nimport { debug, error } from \"../debug\";\nimport { createBenchmark } from \"@latticexyz/common\";\nimport { compress } from \"../koa-middleware/compress\";\n\nexport function apiRoutes(database: Sql): Middleware {\n const router = new Router();\n\n router.get(\"/api/logs\", compress(), async (ctx) => {\n const benchmark = createBenchmark(\"postgres:logs\");\n let options: ReturnType<typeof input.parse>;\n\n try {\n options = input.parse(typeof ctx.query.input === \"string\" ? JSON.parse(ctx.query.input) : {});\n } catch (e) {\n ctx.status = 400;\n ctx.set(\"Content-Type\", \"application/json\");\n ctx.body = JSON.stringify(e);\n debug(e);\n return;\n }\n\n try {\n options.filters = options.filters.length > 0 ? [...options.filters, { tableId: schemasTable.tableId }] : [];\n const records = await queryLogs(database, options ?? {}).execute();\n benchmark(\"query records\");\n const logs = records.map(recordToLog);\n benchmark(\"map records to logs\");\n\n // Ideally we would immediately return an error if the request is for a Store that the indexer\n // is not configured to index. Since we don't have easy access to this information here,\n // we return an error if there are no logs found for a given Store, since that would never\n // be the case for a Store that is being indexed (since there would at least be records for the\n // Tables table with tables created during Store initialization).\n if (records.length === 0) {\n ctx.status = 404;\n ctx.body = \"no logs found\";\n error(\n `no logs found for chainId ${options.chainId}, address ${options.address}, filters ${JSON.stringify(\n options.filters,\n )}`,\n );\n return;\n }\n\n const blockNumber = records[0].chainBlockNumber;\n ctx.status = 200;\n\n // max age is set to several multiples of the uncached response time (currently ~10s, but using 60s for wiggle room) to ensure only ~one origin request at a time\n // and stale-while-revalidate below means that the cache is refreshed under the hood while still responding fast (cached)\n const maxAgeSeconds = 60 * 5;\n // we set stale-while-revalidate to the time elapsed by the number of blocks we can fetch from the RPC in the same amount of time as an uncached response\n // meaning it would take ~the same about of time to get an uncached response from the origin as it would to catch up from the currently cached response\n // if an uncached response takes ~10 seconds, we have ~10s to catch up, so let's say we can do enough RPC calls to fetch 4000 blocks\n // with a block per 2 seconds, that means we can serve a stale/cached response for 8000 seconds before we should require the response be returned by the origin\n const staleWhileRevalidateSeconds = 4000 * 2;\n\n ctx.set(\n \"Cache-Control\",\n `public, max-age=${maxAgeSeconds}, stale-while-revalidate=${staleWhileRevalidateSeconds}`,\n );\n\n ctx.set(\"Content-Type\", \"application/json\");\n ctx.body = JSON.stringify({ blockNumber, logs });\n } catch (e) {\n ctx.status = 500;\n ctx.set(\"Content-Type\", \"application/json\");\n ctx.body = JSON.stringify(e);\n error(e);\n }\n });\n\n return compose([router.routes(), router.allowedMethods()]) as Middleware;\n}\n","import { isNotNull } from \"@latticexyz/common/utils\";\nimport { PendingQuery, Row, Sql } from \"postgres\";\nimport { hexToBytes } from \"viem\";\nimport { z } from \"zod\";\nimport { input } from \"@latticexyz/store-sync/indexer-client\";\nimport { transformSchemaName } from \"@latticexyz/store-sync/postgres\";\nimport { Record } from \"./common\";\n\nconst schemaName = transformSchemaName(\"mud\");\n\nfunction and(sql: Sql, conditions: PendingQuery<Row[]>[]): PendingQuery<Row[]> {\n return sql`(${conditions.reduce((query, condition) => sql`${query} AND ${condition}`)})`;\n}\n\nfunction or(sql: Sql, conditions: PendingQuery<Row[]>[]): PendingQuery<Row[]> {\n return sql`(${conditions.reduce((query, condition) => sql`${query} OR ${condition}`)})`;\n}\n\nexport function queryLogs(sql: Sql, opts: z.infer<typeof input>): PendingQuery<Record[]> {\n const conditions = opts.filters.length\n ? opts.filters.map((filter) =>\n and(\n sql,\n [\n opts.address != null ? sql`address = ${hexToBytes(opts.address)}` : null,\n sql`table_id = ${hexToBytes(filter.tableId)}`,\n filter.key0 != null ? sql`key0 = ${hexToBytes(filter.key0)}` : null,\n filter.key1 != null ? sql`key1 = ${hexToBytes(filter.key1)}` : null,\n ].filter(isNotNull),\n ),\n )\n : opts.address != null\n ? [sql`address = ${hexToBytes(opts.address)}`]\n : [];\n\n const where = sql`WHERE ${and(\n sql,\n [sql`is_deleted != true`, conditions.length ? or(sql, conditions) : null].filter(isNotNull),\n )}`;\n\n // TODO: implement bytea <> hex columns via custom types: https://github.com/porsager/postgres#custom-types\n return sql<Record[]>`\n WITH\n config AS (\n SELECT\n version AS \"indexerVersion\",\n chain_id AS \"chainId\",\n block_number AS \"chainBlockNumber\"\n FROM ${sql(`${schemaName}.config`)}\n LIMIT 1\n ),\n records AS (\n SELECT\n '0x' || encode(address, 'hex') AS address,\n '0x' || encode(table_id, 'hex') AS \"tableId\",\n '0x' || encode(key_bytes, 'hex') AS \"keyBytes\",\n '0x' || encode(static_data, 'hex') AS \"staticData\",\n '0x' || encode(encoded_lengths, 'hex') AS \"encodedLengths\",\n '0x' || encode(dynamic_data, 'hex') AS \"dynamicData\",\n block_number AS \"recordBlockNumber\",\n log_index AS \"logIndex\"\n FROM ${sql(`${schemaName}.records`)}\n ${where}\n ORDER BY block_number, log_index ASC\n )\n SELECT\n (SELECT COUNT(*) FROM records) AS \"totalRows\",\n *\n FROM config, records\n `;\n}\n"],"mappings":"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","names":["tables","schemasTable","and","or","createBenchmark","createBenchmark","schemasTable"]}