@latticexyz/store-indexer 2.0.0-main-f61b4bc0 → 2.0.0-main-0a3b9b1c

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.
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
- import{a as c,b as $}from"../chunk-ET56KTBQ.js";import{a as R,c as L}from"../chunk-2E5MDUA2.js";import"dotenv/config";import{z as T}from"zod";import Z from"koa";import ee from"@koa/cors";import re from"@koa/router";import{createKoaMiddleware as oe}from"trpc-koa-adapter";import{createAppRouter as te}from"@latticexyz/store-sync/trpc-indexer";import{drizzle as ne}from"drizzle-orm/postgres-js";import ae from"postgres";import{getAddress as x}from"viem";import{isTableRegistrationLog as C,logToTable as F,storeTables as H}from"@latticexyz/store-sync";import{decodeKey as W,decodeValueArgs as J}from"@latticexyz/protocol-parser";import{tables as i}from"@latticexyz/store-sync/postgres";import{and as P,asc as M,eq as m,or as v}from"drizzle-orm";import{bigIntMax as Q}from"@latticexyz/common/utils";import{decodeDynamicField as E}from"@latticexyz/protocol-parser";function S(e){return{address:e.address,eventName:"Store_SetRecord",args:{tableId:e.tableId,keyTuple:E("bytes32[]",e.keyBytes),staticData:e.staticData??"0x",encodedLengths:e.encodedLengths??"0x",dynamicData:e.dynamicData??"0x"}}}import{createBenchmark as z}from"@latticexyz/common";async function k(e,{chainId:t,address:r,filters:a=[]}){let o=z("drizzleGetLogs"),n=a.length?a.map(s=>P(r!=null?m(i.recordsTable.address,r):void 0,m(i.recordsTable.tableId,s.tableId),s.key0!=null?m(i.recordsTable.key0,s.key0):void 0,s.key1!=null?m(i.recordsTable.key1,s.key1):void 0)):r!=null?[m(i.recordsTable.address,r)]:[];o("parse config");let p=(await e.select().from(i.configTable).where(m(i.configTable.chainId,t)).limit(1).execute().then(s=>s.find(()=>!0)))?.blockNumber??0n;o("query chainState");let y=await e.select().from(i.recordsTable).where(v(...n)).orderBy(M(i.recordsTable.blockNumber));o("query records");let d=y.reduce((s,h)=>Q(s,h.blockNumber??0n),p);o("find block number");let A=y.filter(s=>!s.isDeleted).map(S);return o("map records to logs"),{blockNumber:d,logs:A}}import{groupBy as K}from"@latticexyz/common/utils";async function N(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:H.Tables.tableId}]:[]}),u=n.filter(C).map(F),p=K(n,d=>`${x(d.address)}:${d.args.tableId}`),y=u.map(d=>{let s=(p.get(`${x(d.address)}:${d.tableId}`)??[]).map(h=>({key:W(d.keySchema,h.args.keyTuple),value:J(d.valueSchema,h.args)}));return{...d,records:s}});return c("findAll: decoded %d logs across %d tables",n.length,u.length),{blockNumber:o,tables:y}}}}import j from"@koa/router";import q from"koa-compose";import{input as G}from"@latticexyz/store-sync/indexer-client";import{storeTables as Y}from"@latticexyz/store-sync";import{isNotNull as w}from"@latticexyz/common/utils";import{hexToBytes as g}from"viem";import{transformSchemaName as U}from"@latticexyz/store-sync/postgres";var I=U("mud");function D(e,t){return e`(${t.reduce((r,a)=>e`${r} AND ${a}`)})`}function V(e,t){return e`(${t.reduce((r,a)=>e`${r} OR ${a}`)})`}function _(e,t){let r=t.filters.length?t.filters.map(o=>D(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(w))):t.address!=null?[e`address = ${g(t.address)}`]:[],a=e`WHERE ${D(e,[e`is_deleted != true`,r.length?V(e,r):null].filter(w))}`;return e`
2
+ import{a as h,b as k,c as x}from"../chunk-ACVTNAB2.js";import{a as L,c as $}from"../chunk-2E5MDUA2.js";import"dotenv/config";import{z as A}from"zod";import ee from"koa";import re from"@koa/cors";import oe from"@koa/router";import{createKoaMiddleware as te}from"trpc-koa-adapter";import{createAppRouter as ne}from"@latticexyz/store-sync/trpc-indexer";import{drizzle as ae}from"drizzle-orm/postgres-js";import se from"postgres";import{getAddress as N}from"viem";import{isTableRegistrationLog as F,logToTable as H,storeTables as W}from"@latticexyz/store-sync";import{decodeKey as J,decodeValueArgs as K}from"@latticexyz/protocol-parser";import{tables as i}from"@latticexyz/store-sync/postgres";import{and as M,asc as v,eq as c,or as Q}from"drizzle-orm";import{bigIntMax as z}from"@latticexyz/common/utils";import{decodeDynamicField as P}from"@latticexyz/protocol-parser";function S(e){return{address:e.address,eventName:"Store_SetRecord",args:{tableId:e.tableId,keyTuple:P("bytes32[]",e.keyBytes),staticData:e.staticData??"0x",encodedLengths:e.encodedLengths??"0x",dynamicData:e.dynamicData??"0x"}}}import{createBenchmark as C}from"@latticexyz/common";async function T(e,{chainId:t,address:r,filters:a=[]}){let o=C("drizzleGetLogs"),n=a.length?a.map(s=>M(r!=null?c(i.recordsTable.address,r):void 0,c(i.recordsTable.tableId,s.tableId),s.key0!=null?c(i.recordsTable.key0,s.key0):void 0,s.key1!=null?c(i.recordsTable.key1,s.key1):void 0)):r!=null?[c(i.recordsTable.address,r)]:[];o("parse config");let u=(await e.select().from(i.configTable).where(c(i.configTable.chainId,t)).limit(1).execute().then(s=>s.find(()=>!0)))?.blockNumber??0n;o("query chainState");let b=await e.select().from(i.recordsTable).where(Q(...n)).orderBy(v(i.recordsTable.blockNumber));o("query records");let d=b.reduce((s,y)=>z(s,y.blockNumber??0n),u);o("find block number");let R=b.filter(s=>!s.isDeleted).map(S);return o("map records to logs"),{blockNumber:d,logs:R}}import{groupBy as U}from"@latticexyz/common/utils";async function w(e){return{async getLogs(r){return T(e,r)},async findAll(r){let a=r.filters??[],{blockNumber:o,logs:n}=await T(e,{...r,filters:a.length>0?[...a,{tableId:W.Tables.tableId}]:[]}),l=n.filter(F).map(H),u=U(n,d=>`${N(d.address)}:${d.args.tableId}`),b=l.map(d=>{let s=(u.get(`${N(d.address)}:${d.tableId}`)??[]).map(y=>({key:J(d.keySchema,y.args.keyTuple),value:K(d.valueSchema,y.args)}));return{...d,records:s}});return h("findAll: decoded %d logs across %d tables",n.length,l.length),{blockNumber:o,tables:b}}}}import q from"@koa/router";import G from"koa-compose";import{input as Y}from"@latticexyz/store-sync/indexer-client";import{storeTables as X}from"@latticexyz/store-sync";import{isNotNull as I}from"@latticexyz/common/utils";import{hexToBytes as p}from"viem";import{transformSchemaName as V}from"@latticexyz/store-sync/postgres";var D=V("mud");function _(e,t){return e`(${t.reduce((r,a)=>e`${r} AND ${a}`)})`}function j(e,t){return e`(${t.reduce((r,a)=>e`${r} OR ${a}`)})`}function B(e,t){let r=t.filters.length?t.filters.map(o=>_(e,[t.address!=null?e`address = ${p(t.address)}`:null,e`table_id = ${p(o.tableId)}`,o.key0!=null?e`key0 = ${p(o.key0)}`:null,o.key1!=null?e`key1 = ${p(o.key1)}`:null].filter(I))):t.address!=null?[e`address = ${p(t.address)}`]:[],a=e`WHERE ${_(e,[e`is_deleted != true`,r.length?j(e,r):null].filter(I))}`;return e`
3
3
  WITH
4
4
  config AS (
5
5
  SELECT
6
6
  version AS "indexerVersion",
7
7
  chain_id AS "chainId",
8
8
  block_number AS "chainBlockNumber"
9
- FROM ${e(`${I}.config`)}
9
+ FROM ${e(`${D}.config`)}
10
10
  LIMIT 1
11
11
  ),
12
12
  records AS (
@@ -19,7 +19,7 @@ import{a as c,b as $}from"../chunk-ET56KTBQ.js";import{a as R,c as L}from"../chu
19
19
  '0x' || encode(dynamic_data, 'hex') AS "dynamicData",
20
20
  block_number AS "recordBlockNumber",
21
21
  log_index AS "logIndex"
22
- FROM ${e(`${I}.records`)}
22
+ FROM ${e(`${D}.records`)}
23
23
  ${a}
24
24
  ORDER BY block_number, log_index ASC
25
25
  )
@@ -27,5 +27,5 @@ import{a as c,b as $}from"../chunk-ET56KTBQ.js";import{a as R,c as L}from"../chu
27
27
  (SELECT COUNT(*) FROM records) AS "totalRows",
28
28
  *
29
29
  FROM config, records
30
- `}import{createBenchmark as X}from"@latticexyz/common";function B(e){let t=new j;return t.get("/api/logs",$(),async r=>{let a=X("postgres:logs"),o;try{o=G.parse(typeof r.query.input=="string"?JSON.parse(r.query.input):{})}catch(n){r.status=400,r.body=JSON.stringify(n),c(n);return}try{o.filters=o.filters.length>0?[...o.filters,{tableId:Y.Tables.tableId}]:[];let n=await _(e,o??{}).execute();a("query records");let u=n.map(S);if(a("map records to logs"),n.length===0){r.status=404,r.body="no logs found",c(`no logs found for chainId ${o.chainId}, address ${o.address}, filters ${o.filters}`);return}let p=n[0].chainBlockNumber;r.body=JSON.stringify({blockNumber:p,logs:u}),r.status=200}catch(n){r.status=500,r.body=JSON.stringify(n),c(n)}}),q([t.routes(),t.allowedMethods()])}var f=L(T.intersection(R,T.object({DATABASE_URL:T.string()}))),O=ae(f.DATABASE_URL),l=new Z;l.use(ee());l.use(B(O));var b=new re;b.get("/",e=>{e.body="emit HelloWorld();"});b.get("/healthz",e=>{e.status=200});b.get("/readyz",e=>{e.status=200});l.use(b.routes());l.use(b.allowedMethods());l.use(oe({prefix:"/trpc",router:te(),createContext:async()=>({queryAdapter:await N(ne(O))})}));l.listen({host:f.HOST,port:f.PORT});console.log(`postgres indexer frontend listening on http://${f.HOST}:${f.PORT}`);
30
+ `}import{createBenchmark as Z}from"@latticexyz/common";function O(e){let t=new q;return t.get("/api/logs",x(),async r=>{let a=Z("postgres:logs"),o;try{o=Y.parse(typeof r.query.input=="string"?JSON.parse(r.query.input):{})}catch(n){r.status=400,r.body=JSON.stringify(n),h(n);return}try{o.filters=o.filters.length>0?[...o.filters,{tableId:X.Tables.tableId}]:[];let n=await B(e,o??{}).execute();a("query records");let l=n.map(S);if(a("map records to logs"),n.length===0){r.status=404,r.body="no logs found",k(`no logs found for chainId ${o.chainId}, address ${o.address}, filters ${o.filters}`);return}let u=n[0].chainBlockNumber;r.body=JSON.stringify({blockNumber:u,logs:l}),r.status=200}catch(n){r.status=500,r.body=JSON.stringify(n),k(n)}}),G([t.routes(),t.allowedMethods()])}var g=$(A.intersection(L,A.object({DATABASE_URL:A.string()}))),E=se(g.DATABASE_URL),m=new ee;m.use(re());m.use(O(E));var f=new oe;f.get("/",e=>{e.body="emit HelloWorld();"});f.get("/healthz",e=>{e.status=200});f.get("/readyz",e=>{e.status=200});m.use(f.routes());m.use(f.allowedMethods());m.use(te({prefix:"/trpc",router:ne(),createContext:async()=>({queryAdapter:await w(ae(E))})}));m.listen({host:g.HOST,port:g.PORT});console.log(`postgres indexer frontend listening on http://${g.HOST}:${g.PORT}`);
31
31
  //# sourceMappingURL=postgres-frontend.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../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 Router from \"@koa/router\";\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 \"../src/postgres/deprecated/createQueryAdapter\";\nimport { apiRoutes } from \"../src/postgres/apiRoutes\";\n\nconst env = parseEnv(\n z.intersection(\n frontendEnvSchema,\n z.object({\n DATABASE_URL: z.string(),\n })\n )\n);\n\nconst database = postgres(env.DATABASE_URL);\n\nconst server = new Koa();\nserver.use(cors());\nserver.use(apiRoutes(database));\n\nconst router = new Router();\n\nrouter.get(\"/\", (ctx) => {\n ctx.body = \"emit HelloWorld();\";\n});\n\n// k8s healthchecks\nrouter.get(\"/healthz\", (ctx) => {\n ctx.status = 200;\n});\nrouter.get(\"/readyz\", (ctx) => {\n ctx.status = 200;\n});\n\nserver.use(router.routes());\nserver.use(router.allowedMethods());\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, storeTables } from \"@latticexyz/store-sync\";\nimport { decodeKey, decodeValueArgs } from \"@latticexyz/protocol-parser\";\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 */\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: storeTables.Tables.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: TableWithRecords[] = tables.map((table) => {\n const tableLogs = logsByTable.get(`${getAddress(table.address)}:${table.tableId}`) ?? [];\n const records = tableLogs.map((log) => ({\n key: decodeKey(table.keySchema, log.args.keyTuple),\n value: decodeValueArgs(table.valueSchema, log.args),\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 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: (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\";\nimport { RecordData } from \"./common\";\n\nexport function recordToLog(\n record: Omit<RecordData, \"recordBlockNumber\">\n): 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 { storeTables } from \"@latticexyz/store-sync\";\nimport { queryLogs } from \"./queryLogs\";\nimport { recordToLog } from \"./recordToLog\";\nimport { debug } from \"../debug\";\nimport { createBenchmark } from \"@latticexyz/common\";\nimport { compress } from \"../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 (error) {\n ctx.status = 400;\n ctx.body = JSON.stringify(error);\n debug(error);\n return;\n }\n\n try {\n options.filters = options.filters.length > 0 ? [...options.filters, { tableId: storeTables.Tables.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 if (records.length === 0) {\n ctx.status = 404;\n ctx.body = \"no logs found\";\n debug(`no logs found for chainId ${options.chainId}, address ${options.address}, filters ${options.filters}`);\n return;\n }\n\n const blockNumber = records[0].chainBlockNumber;\n ctx.body = JSON.stringify({ blockNumber, logs });\n ctx.status = 200;\n } catch (error) {\n ctx.status = 500;\n ctx.body = JSON.stringify(error);\n debug(error);\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 // TODO: sort by logIndex (https://github.com/latticexyz/mud/issues/1979)\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","Router","createKoaMiddleware","createAppRouter","drizzle","postgres","getAddress","isTableRegistrationLog","logToTable","storeTables","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","storeTables","tables","isTableRegistrationLog","logToTable","logsByTable","log","getAddress","tablesWithRecords","table","records","decodeKey","decodeValueArgs","debug","Router","compose","input","storeTables","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","error","debug","storeTables","records","queryLogs","logs","recordToLog","blockNumber","compose","env","parseEnv","z","frontendEnvSchema","database","postgres","server","Koa","cors","apiRoutes","router","Router","ctx","createKoaMiddleware","createAppRouter","createQueryAdapter","drizzle"]}
1
+ {"version":3,"sources":["../../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 Router from \"@koa/router\";\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 \"../src/postgres/deprecated/createQueryAdapter\";\nimport { apiRoutes } from \"../src/postgres/apiRoutes\";\n\nconst env = parseEnv(\n z.intersection(\n frontendEnvSchema,\n z.object({\n DATABASE_URL: z.string(),\n })\n )\n);\n\nconst database = postgres(env.DATABASE_URL);\n\nconst server = new Koa();\nserver.use(cors());\nserver.use(apiRoutes(database));\n\nconst router = new Router();\n\nrouter.get(\"/\", (ctx) => {\n ctx.body = \"emit HelloWorld();\";\n});\n\n// k8s healthchecks\nrouter.get(\"/healthz\", (ctx) => {\n ctx.status = 200;\n});\nrouter.get(\"/readyz\", (ctx) => {\n ctx.status = 200;\n});\n\nserver.use(router.routes());\nserver.use(router.allowedMethods());\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, storeTables } from \"@latticexyz/store-sync\";\nimport { decodeKey, decodeValueArgs } from \"@latticexyz/protocol-parser\";\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 */\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: storeTables.Tables.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: TableWithRecords[] = tables.map((table) => {\n const tableLogs = logsByTable.get(`${getAddress(table.address)}:${table.tableId}`) ?? [];\n const records = tableLogs.map((log) => ({\n key: decodeKey(table.keySchema, log.args.keyTuple),\n value: decodeValueArgs(table.valueSchema, log.args),\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 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: (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\";\nimport { RecordData } from \"./common\";\n\nexport function recordToLog(\n record: Omit<RecordData, \"recordBlockNumber\">\n): 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 { storeTables } 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 \"../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.body = JSON.stringify(e);\n debug(e);\n return;\n }\n\n try {\n options.filters = options.filters.length > 0 ? [...options.filters, { tableId: storeTables.Tables.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 if (records.length === 0) {\n ctx.status = 404;\n ctx.body = \"no logs found\";\n error(`no logs found for chainId ${options.chainId}, address ${options.address}, filters ${options.filters}`);\n return;\n }\n\n const blockNumber = records[0].chainBlockNumber;\n ctx.body = JSON.stringify({ blockNumber, logs });\n ctx.status = 200;\n } catch (e) {\n ctx.status = 500;\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 // TODO: sort by logIndex (https://github.com/latticexyz/mud/issues/1979)\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","Router","createKoaMiddleware","createAppRouter","drizzle","postgres","getAddress","isTableRegistrationLog","logToTable","storeTables","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","storeTables","tables","isTableRegistrationLog","logToTable","logsByTable","log","getAddress","tablesWithRecords","table","records","decodeKey","decodeValueArgs","debug","Router","compose","input","storeTables","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","storeTables","records","queryLogs","logs","recordToLog","error","blockNumber","compose","env","parseEnv","z","frontendEnvSchema","database","postgres","server","Koa","cors","apiRoutes","router","Router","ctx","createKoaMiddleware","createAppRouter","createQueryAdapter","drizzle"]}
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import{a as g,b as N}from"../chunk-ET56KTBQ.js";import{a as R,b as k,c as L}from"../chunk-2E5MDUA2.js";import"dotenv/config";import V from"node:fs";import{z as b}from"zod";import{eq as $}from"drizzle-orm";import{drizzle as G}from"drizzle-orm/better-sqlite3";import x from"better-sqlite3";import{createPublicClient as X,fallback as Y,webSocket as Z,http as ee}from"viem";import te from"koa";import re from"@koa/cors";import oe from"@koa/router";import{createKoaMiddleware as se}from"trpc-koa-adapter";import{createAppRouter as ae}from"@latticexyz/store-sync/trpc-indexer";import{chainState as E,schemaVersion as O,syncToSqlite as ne}from"@latticexyz/store-sync/sqlite";import{asc as P,eq as _}from"drizzle-orm";import{buildTable as W,chainState as I,getTables as q}from"@latticexyz/store-sync/sqlite";import{getAddress as w}from"viem";import{decodeDynamicField as M}from"@latticexyz/protocol-parser";function d(e,{chainId:t,address:r,filters:n=[]}){let i=e.select().from(I).where(_(I.chainId,t)).limit(1).all().find(()=>!0),s=Array.from(new Set(n.map(a=>a.tableId))),y=q(e).filter(a=>r==null||w(r)===w(a.address)).filter(a=>!s.length||s.includes(a.tableId)).map(a=>{let h=W(a),T=e.select().from(h).where(_(h.__isDeleted,!1)).orderBy(P(h.__lastUpdatedBlockNumber)).all(),v=n.length?T.filter(u=>{let l=M("bytes32[]",u.__key);return n.some(m=>m.tableId===a.tableId&&(m.key0==null||m.key0===l[0])&&(m.key1==null||m.key1===l[1]))}):T;return{...a,records:v.map(u=>({key:Object.fromEntries(Object.entries(a.keySchema).map(([l])=>[l,u[l]])),value:Object.fromEntries(Object.entries(a.valueSchema).map(([l])=>[l,u[l]]))}))}});return{blockNumber:i?.lastUpdatedBlockNumber??null,tables:y}}import{tablesWithRecordsToLogs as U}from"@latticexyz/store-sync";async function B(e){return{async getLogs(r){let{blockNumber:n,tables:i}=d(e,r),s=U(i);return{blockNumber:n??0n,logs:s}},async findAll(r){return d(e,r)}}}import{isDefined as ie}from"@latticexyz/common/utils";import{combineLatest as le,filter as ce,first as me}from"rxjs";import H from"@koa/router";import F from"koa-compose";import{input as j}from"@latticexyz/store-sync/indexer-client";import{storeTables as z,tablesWithRecordsToLogs as J}from"@latticexyz/store-sync";import{createBenchmark as K}from"@latticexyz/common";function A(e){let t=new H;return t.get("/api/logs",N(),async r=>{let n=K("sqlite:logs"),i;try{i=j.parse(typeof r.query.input=="string"?JSON.parse(r.query.input):{})}catch(s){r.status=400,r.body=JSON.stringify(s),g(s);return}try{i.filters=i.filters.length>0?[...i.filters,{tableId:z.Tables.tableId}]:[],n("parse config");let{blockNumber:s,tables:S}=d(e,i);n("query tables with records");let y=J(S);n("convert records to logs"),r.body=JSON.stringify({blockNumber:s?.toString()??"-1",logs:y}),r.status=200}catch(s){r.status=500,r.body=JSON.stringify(s),g(s)}}),F([t.routes(),t.allowedMethods()])}var o=L(b.intersection(b.intersection(k,R),b.object({SQLITE_FILENAME:b.string().default("indexer.db")}))),de=[o.RPC_WS_URL?Z(o.RPC_WS_URL):void 0,o.RPC_HTTP_URL?ee(o.RPC_HTTP_URL):void 0].filter(ie),C=X({transport:Y(de),pollingInterval:o.POLLING_INTERVAL}),pe=await C.getChainId(),f=G(new x(o.SQLITE_FILENAME)),Q=o.START_BLOCK;try{let t=f.select().from(E).where($(E.chainId,pe)).all()[0];t!=null&&(t.schemaVersion!=O?(console.log("schema version changed from",t.schemaVersion,"to",O,"recreating database"),V.truncateSync(o.SQLITE_FILENAME)):t.lastUpdatedBlockNumber!=null&&(console.log("resuming from block number",t.lastUpdatedBlockNumber+1n),Q=t.lastUpdatedBlockNumber+1n))}catch{}var{latestBlockNumber$:ue,storedBlockLogs$:be}=await ne({database:f,publicClient:C,startBlock:Q,maxBlockRange:o.MAX_BLOCK_RANGE,address:o.STORE_ADDRESS}),D=!1;le([ue,be]).pipe(ce(([e,{blockNumber:t}])=>e===t),me()).subscribe(()=>{D=!0,console.log("all caught up")});var c=new te;c.use(re());c.use(A(f));var p=new oe;p.get("/",e=>{e.body="emit HelloWorld();"});p.get("/healthz",e=>{e.status=200});p.get("/readyz",e=>{D?(e.status=200,e.body="ready"):(e.status=424,e.body="backfilling")});c.use(p.routes());c.use(p.allowedMethods());c.use(se({prefix:"/trpc",router:ae(),createContext:async()=>({queryAdapter:await B(f)})}));c.listen({host:o.HOST,port:o.PORT});console.log(`sqlite indexer frontend listening on http://${o.HOST}:${o.PORT}`);
2
+ import{a as g,c as N}from"../chunk-ACVTNAB2.js";import{a as R,b as k,c as L}from"../chunk-2E5MDUA2.js";import"dotenv/config";import V from"node:fs";import{z as b}from"zod";import{eq as $}from"drizzle-orm";import{drizzle as G}from"drizzle-orm/better-sqlite3";import x from"better-sqlite3";import{createPublicClient as X,fallback as Y,webSocket as Z,http as ee}from"viem";import te from"koa";import re from"@koa/cors";import oe from"@koa/router";import{createKoaMiddleware as se}from"trpc-koa-adapter";import{createAppRouter as ae}from"@latticexyz/store-sync/trpc-indexer";import{chainState as E,schemaVersion as O,syncToSqlite as ne}from"@latticexyz/store-sync/sqlite";import{asc as P,eq as _}from"drizzle-orm";import{buildTable as W,chainState as I,getTables as q}from"@latticexyz/store-sync/sqlite";import{getAddress as w}from"viem";import{decodeDynamicField as M}from"@latticexyz/protocol-parser";function d(e,{chainId:t,address:r,filters:n=[]}){let i=e.select().from(I).where(_(I.chainId,t)).limit(1).all().find(()=>!0),s=Array.from(new Set(n.map(a=>a.tableId))),y=q(e).filter(a=>r==null||w(r)===w(a.address)).filter(a=>!s.length||s.includes(a.tableId)).map(a=>{let h=W(a),T=e.select().from(h).where(_(h.__isDeleted,!1)).orderBy(P(h.__lastUpdatedBlockNumber)).all(),v=n.length?T.filter(u=>{let l=M("bytes32[]",u.__key);return n.some(m=>m.tableId===a.tableId&&(m.key0==null||m.key0===l[0])&&(m.key1==null||m.key1===l[1]))}):T;return{...a,records:v.map(u=>({key:Object.fromEntries(Object.entries(a.keySchema).map(([l])=>[l,u[l]])),value:Object.fromEntries(Object.entries(a.valueSchema).map(([l])=>[l,u[l]]))}))}});return{blockNumber:i?.lastUpdatedBlockNumber??null,tables:y}}import{tablesWithRecordsToLogs as U}from"@latticexyz/store-sync";async function B(e){return{async getLogs(r){let{blockNumber:n,tables:i}=d(e,r),s=U(i);return{blockNumber:n??0n,logs:s}},async findAll(r){return d(e,r)}}}import{isDefined as ie}from"@latticexyz/common/utils";import{combineLatest as le,filter as ce,first as me}from"rxjs";import H from"@koa/router";import F from"koa-compose";import{input as j}from"@latticexyz/store-sync/indexer-client";import{storeTables as z,tablesWithRecordsToLogs as J}from"@latticexyz/store-sync";import{createBenchmark as K}from"@latticexyz/common";function A(e){let t=new H;return t.get("/api/logs",N(),async r=>{let n=K("sqlite:logs"),i;try{i=j.parse(typeof r.query.input=="string"?JSON.parse(r.query.input):{})}catch(s){r.status=400,r.body=JSON.stringify(s),g(s);return}try{i.filters=i.filters.length>0?[...i.filters,{tableId:z.Tables.tableId}]:[],n("parse config");let{blockNumber:s,tables:S}=d(e,i);n("query tables with records");let y=J(S);n("convert records to logs"),r.body=JSON.stringify({blockNumber:s?.toString()??"-1",logs:y}),r.status=200}catch(s){r.status=500,r.body=JSON.stringify(s),g(s)}}),F([t.routes(),t.allowedMethods()])}var o=L(b.intersection(b.intersection(k,R),b.object({SQLITE_FILENAME:b.string().default("indexer.db")}))),de=[o.RPC_WS_URL?Z(o.RPC_WS_URL):void 0,o.RPC_HTTP_URL?ee(o.RPC_HTTP_URL):void 0].filter(ie),C=X({transport:Y(de),pollingInterval:o.POLLING_INTERVAL}),pe=await C.getChainId(),f=G(new x(o.SQLITE_FILENAME)),Q=o.START_BLOCK;try{let t=f.select().from(E).where($(E.chainId,pe)).all()[0];t!=null&&(t.schemaVersion!=O?(console.log("schema version changed from",t.schemaVersion,"to",O,"recreating database"),V.truncateSync(o.SQLITE_FILENAME)):t.lastUpdatedBlockNumber!=null&&(console.log("resuming from block number",t.lastUpdatedBlockNumber+1n),Q=t.lastUpdatedBlockNumber+1n))}catch{}var{latestBlockNumber$:ue,storedBlockLogs$:be}=await ne({database:f,publicClient:C,startBlock:Q,maxBlockRange:o.MAX_BLOCK_RANGE,address:o.STORE_ADDRESS}),D=!1;le([ue,be]).pipe(ce(([e,{blockNumber:t}])=>e===t),me()).subscribe(()=>{D=!0,console.log("all caught up")});var c=new te;c.use(re());c.use(A(f));var p=new oe;p.get("/",e=>{e.body="emit HelloWorld();"});p.get("/healthz",e=>{e.status=200});p.get("/readyz",e=>{D?(e.status=200,e.body="ready"):(e.status=424,e.body="backfilling")});c.use(p.routes());c.use(p.allowedMethods());c.use(se({prefix:"/trpc",router:ae(),createContext:async()=>({queryAdapter:await B(f)})}));c.listen({host:o.HOST,port:o.PORT});console.log(`sqlite indexer frontend listening on http://${o.HOST}:${o.PORT}`);
3
3
  //# sourceMappingURL=sqlite-indexer.js.map
@@ -0,0 +1,2 @@
1
+ import i from"debug";var a=i("mud:store-indexer"),m=i("mud:store-indexer");a.log=console.debug.bind(console);m.log=console.error.bind(console);import{Stream as p}from"node:stream";import l from"accepts";import{createBrotliCompress as f,createDeflate as b,createGzip as u}from"node:zlib";import{includes as g}from"@latticexyz/common/utils";var c={br:f,gzip:u,deflate:b},d=Object.keys(c);function y(o,s){let e=0;return o.on("data",r=>{e+=r.length,e>s&&(e=0,o.flush())}),o}function z({flushThreshold:o=1024*4}={}){return async function(e,r){e.vary("Accept-Encoding"),await r();let n=l(e.req).encoding(d);if(!g(d,n))return;let t=y(c[n](),o);e.set("Content-Encoding",n),e.body=e.body instanceof p?e.body.pipe(t):t.end(e.body)}}export{a,m as b,z as c};
2
+ //# sourceMappingURL=chunk-ACVTNAB2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/debug.ts","../src/compress.ts"],"sourcesContent":["import createDebug from \"debug\";\n\nexport const debug = createDebug(\"mud:store-indexer\");\nexport const error = createDebug(\"mud:store-indexer\");\n\n// Pipe debug output to stdout instead of stderr\ndebug.log = console.debug.bind(console);\n\n// Pipe error output to stderr\nerror.log = console.error.bind(console);\n","import { Middleware } from \"koa\";\nimport { Readable, Stream } from \"node:stream\";\nimport accepts from \"accepts\";\nimport { Zlib, createBrotliCompress, createDeflate, createGzip } from \"node:zlib\";\nimport { includes } from \"@latticexyz/common/utils\";\n\n// Loosely based on https://github.com/holic/koa-compress/blob/master/lib/index.js\n// with better handling of streams better with occasional flushing\n\nconst encodings = {\n br: createBrotliCompress,\n gzip: createGzip,\n deflate: createDeflate,\n} as const;\n\nconst encodingNames = Object.keys(encodings) as (keyof typeof encodings)[];\n\nfunction flushEvery<stream extends Zlib & Readable>(stream: stream, bytesThreshold: number): stream {\n let bytesSinceFlush = 0;\n stream.on(\"data\", (data) => {\n bytesSinceFlush += data.length;\n if (bytesSinceFlush > bytesThreshold) {\n bytesSinceFlush = 0;\n stream.flush();\n }\n });\n return stream;\n}\n\ntype CompressOptions = {\n flushThreshold?: number;\n};\n\nexport function compress({ flushThreshold = 1024 * 4 }: CompressOptions = {}): Middleware {\n return async function compressMiddleware(ctx, next) {\n ctx.vary(\"Accept-Encoding\");\n\n await next();\n\n const encoding = accepts(ctx.req).encoding(encodingNames);\n if (!includes(encodingNames, encoding)) return;\n\n const compressed = flushEvery(encodings[encoding](), flushThreshold);\n\n ctx.set(\"Content-Encoding\", encoding);\n ctx.body = ctx.body instanceof Stream ? ctx.body.pipe(compressed) : compressed.end(ctx.body);\n };\n}\n"],"mappings":"AAAA,OAAOA,MAAiB,QAEjB,IAAMC,EAAQD,EAAY,mBAAmB,EACvCE,EAAQF,EAAY,mBAAmB,EAGpDC,EAAM,IAAM,QAAQ,MAAM,KAAK,OAAO,EAGtCC,EAAM,IAAM,QAAQ,MAAM,KAAK,OAAO,ECRtC,OAAmB,UAAAC,MAAc,cACjC,OAAOC,MAAa,UACpB,OAAe,wBAAAC,EAAsB,iBAAAC,EAAe,cAAAC,MAAkB,YACtE,OAAS,YAAAC,MAAgB,2BAKzB,IAAMC,EAAY,CAChB,GAAIJ,EACJ,KAAME,EACN,QAASD,CACX,EAEMI,EAAgB,OAAO,KAAKD,CAAS,EAE3C,SAASE,EAA2CC,EAAgBC,EAAgC,CAClG,IAAIC,EAAkB,EACtB,OAAAF,EAAO,GAAG,OAASG,GAAS,CAC1BD,GAAmBC,EAAK,OACpBD,EAAkBD,IACpBC,EAAkB,EAClBF,EAAO,MAAM,EAEjB,CAAC,EACMA,CACT,CAMO,SAASI,EAAS,CAAE,eAAAC,EAAiB,KAAO,CAAE,EAAqB,CAAC,EAAe,CACxF,OAAO,eAAkCC,EAAKC,EAAM,CAClDD,EAAI,KAAK,iBAAiB,EAE1B,MAAMC,EAAK,EAEX,IAAMC,EAAWhB,EAAQc,EAAI,GAAG,EAAE,SAASR,CAAa,EACxD,GAAI,CAACF,EAASE,EAAeU,CAAQ,EAAG,OAExC,IAAMC,EAAaV,EAAWF,EAAUW,CAAQ,EAAE,EAAGH,CAAc,EAEnEC,EAAI,IAAI,mBAAoBE,CAAQ,EACpCF,EAAI,KAAOA,EAAI,gBAAgBf,EAASe,EAAI,KAAK,KAAKG,CAAU,EAAIA,EAAW,IAAIH,EAAI,IAAI,CAC7F,CACF","names":["createDebug","debug","error","Stream","accepts","createBrotliCompress","createDeflate","createGzip","includes","encodings","encodingNames","flushEvery","stream","bytesThreshold","bytesSinceFlush","data","compress","flushThreshold","ctx","next","encoding","compressed"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@latticexyz/store-indexer",
3
- "version": "2.0.0-main-f61b4bc0",
3
+ "version": "2.0.0-main-0a3b9b1c",
4
4
  "description": "Minimal Typescript indexer for Store",
5
5
  "repository": {
6
6
  "type": "git",
@@ -36,11 +36,11 @@
36
36
  "trpc-koa-adapter": "^1.1.3",
37
37
  "viem": "1.14.0",
38
38
  "zod": "^3.21.4",
39
- "@latticexyz/protocol-parser": "2.0.0-main-f61b4bc0",
40
- "@latticexyz/block-logs-stream": "2.0.0-main-f61b4bc0",
41
- "@latticexyz/common": "2.0.0-main-f61b4bc0",
42
- "@latticexyz/store": "2.0.0-main-f61b4bc0",
43
- "@latticexyz/store-sync": "2.0.0-main-f61b4bc0"
39
+ "@latticexyz/block-logs-stream": "2.0.0-main-0a3b9b1c",
40
+ "@latticexyz/common": "2.0.0-main-0a3b9b1c",
41
+ "@latticexyz/protocol-parser": "2.0.0-main-0a3b9b1c",
42
+ "@latticexyz/store": "2.0.0-main-0a3b9b1c",
43
+ "@latticexyz/store-sync": "2.0.0-main-0a3b9b1c"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@types/accepts": "^1.3.7",
package/src/debug.ts CHANGED
@@ -1,3 +1,10 @@
1
1
  import createDebug from "debug";
2
2
 
3
3
  export const debug = createDebug("mud:store-indexer");
4
+ export const error = createDebug("mud:store-indexer");
5
+
6
+ // Pipe debug output to stdout instead of stderr
7
+ debug.log = console.debug.bind(console);
8
+
9
+ // Pipe error output to stderr
10
+ error.log = console.error.bind(console);
@@ -6,7 +6,7 @@ import { input } from "@latticexyz/store-sync/indexer-client";
6
6
  import { storeTables } from "@latticexyz/store-sync";
7
7
  import { queryLogs } from "./queryLogs";
8
8
  import { recordToLog } from "./recordToLog";
9
- import { debug } from "../debug";
9
+ import { debug, error } from "../debug";
10
10
  import { createBenchmark } from "@latticexyz/common";
11
11
  import { compress } from "../compress";
12
12
 
@@ -19,10 +19,10 @@ export function apiRoutes(database: Sql): Middleware {
19
19
 
20
20
  try {
21
21
  options = input.parse(typeof ctx.query.input === "string" ? JSON.parse(ctx.query.input) : {});
22
- } catch (error) {
22
+ } catch (e) {
23
23
  ctx.status = 400;
24
- ctx.body = JSON.stringify(error);
25
- debug(error);
24
+ ctx.body = JSON.stringify(e);
25
+ debug(e);
26
26
  return;
27
27
  }
28
28
 
@@ -36,17 +36,17 @@ export function apiRoutes(database: Sql): Middleware {
36
36
  if (records.length === 0) {
37
37
  ctx.status = 404;
38
38
  ctx.body = "no logs found";
39
- debug(`no logs found for chainId ${options.chainId}, address ${options.address}, filters ${options.filters}`);
39
+ error(`no logs found for chainId ${options.chainId}, address ${options.address}, filters ${options.filters}`);
40
40
  return;
41
41
  }
42
42
 
43
43
  const blockNumber = records[0].chainBlockNumber;
44
44
  ctx.body = JSON.stringify({ blockNumber, logs });
45
45
  ctx.status = 200;
46
- } catch (error) {
46
+ } catch (e) {
47
47
  ctx.status = 500;
48
- ctx.body = JSON.stringify(error);
49
- debug(error);
48
+ ctx.body = JSON.stringify(e);
49
+ error(e);
50
50
  }
51
51
  });
52
52
 
@@ -1,2 +0,0 @@
1
- import a from"debug";var g=a("mud:store-indexer");import{Stream as c}from"node:stream";import m from"accepts";import{createBrotliCompress as p,createDeflate as f,createGzip as l}from"node:zlib";import{includes as u}from"@latticexyz/common/utils";var d={br:p,gzip:l,deflate:f},i=Object.keys(d);function b(o,t){let e=0;return o.on("data",n=>{e+=n.length,e>t&&(e=0,o.flush())}),o}function v({flushThreshold:o=1024*4}={}){return async function(e,n){e.vary("Accept-Encoding"),await n();let r=m(e.req).encoding(i);if(!u(i,r))return;let s=b(d[r](),o);e.set("Content-Encoding",r),e.body=e.body instanceof c?e.body.pipe(s):s.end(e.body)}}export{g as a,v as b};
2
- //# sourceMappingURL=chunk-ET56KTBQ.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/debug.ts","../src/compress.ts"],"sourcesContent":["import createDebug from \"debug\";\n\nexport const debug = createDebug(\"mud:store-indexer\");\n","import { Middleware } from \"koa\";\nimport { Readable, Stream } from \"node:stream\";\nimport accepts from \"accepts\";\nimport { Zlib, createBrotliCompress, createDeflate, createGzip } from \"node:zlib\";\nimport { includes } from \"@latticexyz/common/utils\";\n\n// Loosely based on https://github.com/holic/koa-compress/blob/master/lib/index.js\n// with better handling of streams better with occasional flushing\n\nconst encodings = {\n br: createBrotliCompress,\n gzip: createGzip,\n deflate: createDeflate,\n} as const;\n\nconst encodingNames = Object.keys(encodings) as (keyof typeof encodings)[];\n\nfunction flushEvery<stream extends Zlib & Readable>(stream: stream, bytesThreshold: number): stream {\n let bytesSinceFlush = 0;\n stream.on(\"data\", (data) => {\n bytesSinceFlush += data.length;\n if (bytesSinceFlush > bytesThreshold) {\n bytesSinceFlush = 0;\n stream.flush();\n }\n });\n return stream;\n}\n\ntype CompressOptions = {\n flushThreshold?: number;\n};\n\nexport function compress({ flushThreshold = 1024 * 4 }: CompressOptions = {}): Middleware {\n return async function compressMiddleware(ctx, next) {\n ctx.vary(\"Accept-Encoding\");\n\n await next();\n\n const encoding = accepts(ctx.req).encoding(encodingNames);\n if (!includes(encodingNames, encoding)) return;\n\n const compressed = flushEvery(encodings[encoding](), flushThreshold);\n\n ctx.set(\"Content-Encoding\", encoding);\n ctx.body = ctx.body instanceof Stream ? ctx.body.pipe(compressed) : compressed.end(ctx.body);\n };\n}\n"],"mappings":"AAAA,OAAOA,MAAiB,QAEjB,IAAMC,EAAQD,EAAY,mBAAmB,ECDpD,OAAmB,UAAAE,MAAc,cACjC,OAAOC,MAAa,UACpB,OAAe,wBAAAC,EAAsB,iBAAAC,EAAe,cAAAC,MAAkB,YACtE,OAAS,YAAAC,MAAgB,2BAKzB,IAAMC,EAAY,CAChB,GAAIJ,EACJ,KAAME,EACN,QAASD,CACX,EAEMI,EAAgB,OAAO,KAAKD,CAAS,EAE3C,SAASE,EAA2CC,EAAgBC,EAAgC,CAClG,IAAIC,EAAkB,EACtB,OAAAF,EAAO,GAAG,OAASG,GAAS,CAC1BD,GAAmBC,EAAK,OACpBD,EAAkBD,IACpBC,EAAkB,EAClBF,EAAO,MAAM,EAEjB,CAAC,EACMA,CACT,CAMO,SAASI,EAAS,CAAE,eAAAC,EAAiB,KAAO,CAAE,EAAqB,CAAC,EAAe,CACxF,OAAO,eAAkCC,EAAKC,EAAM,CAClDD,EAAI,KAAK,iBAAiB,EAE1B,MAAMC,EAAK,EAEX,IAAMC,EAAWhB,EAAQc,EAAI,GAAG,EAAE,SAASR,CAAa,EACxD,GAAI,CAACF,EAASE,EAAeU,CAAQ,EAAG,OAExC,IAAMC,EAAaV,EAAWF,EAAUW,CAAQ,EAAE,EAAGH,CAAc,EAEnEC,EAAI,IAAI,mBAAoBE,CAAQ,EACpCF,EAAI,KAAOA,EAAI,gBAAgBf,EAASe,EAAI,KAAK,KAAKG,CAAU,EAAIA,EAAW,IAAIH,EAAI,IAAI,CAC7F,CACF","names":["createDebug","debug","Stream","accepts","createBrotliCompress","createDeflate","createGzip","includes","encodings","encodingNames","flushEvery","stream","bytesThreshold","bytesSinceFlush","data","compress","flushThreshold","ctx","next","encoding","compressed"]}