@ponder/react 0.14.12 → 0.15.0

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.
package/dist/esm/utils.js CHANGED
@@ -4,7 +4,7 @@ export function getPonderQueryOptions(client, queryFn) {
4
4
  const queryPromise = queryFn(client.db);
5
5
  // @ts-expect-error
6
6
  if ("getSQL" in queryPromise === false) {
7
- throw new Error('"queryFn" must return SQL');
7
+ throw new Error('"queryFn" must return SQL. You may have to remove `.execute()` from your query.');
8
8
  }
9
9
  const query = compileQuery(queryPromise);
10
10
  const queryKey = ["__ponder_react", query.sql, stringify(query.params)];
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE3D,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAKtC,MAAM,UAAU,qBAAqB,CACnC,MAA8B,EAC9B,OAAgD;IAKhD,MAAM,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAExC,mBAAmB;IACnB,IAAI,QAAQ,IAAI,YAAY,KAAK,KAAK,EAAE;QACtC,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;KAC9C;IAED,MAAM,KAAK,GAAG,YAAY,CAAC,YAAqC,CAAC,CAAC;IAClE,MAAM,QAAQ,GAAG,CAAC,gBAAgB,EAAE,KAAK,CAAC,GAAG,EAAE,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IAExE,OAAO;QACL,QAAQ;QACR,OAAO,EAAE,GAAG,EAAE,CAAC,YAAY;KAC5B,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE3D,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAKtC,MAAM,UAAU,qBAAqB,CACnC,MAA8B,EAC9B,OAAgD;IAKhD,MAAM,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAExC,mBAAmB;IACnB,IAAI,QAAQ,IAAI,YAAY,KAAK,KAAK,EAAE;QACtC,MAAM,IAAI,KAAK,CACb,iFAAiF,CAClF,CAAC;KACH;IAED,MAAM,KAAK,GAAG,YAAY,CAAC,YAAqC,CAAC,CAAC;IAClE,MAAM,QAAQ,GAAG,CAAC,gBAAgB,EAAE,KAAK,CAAC,GAAG,EAAE,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IAExE,OAAO;QACL,QAAQ;QACR,OAAO,EAAE,GAAG,EAAE,CAAC,YAAY;KAC5B,CAAC;AACJ,CAAC"}
@@ -0,0 +1,10 @@
1
+ export type Checkpoint = {
2
+ blockTimestamp: bigint;
3
+ chainId: bigint;
4
+ blockNumber: bigint;
5
+ transactionIndex: bigint;
6
+ eventType: number;
7
+ eventIndex: bigint;
8
+ };
9
+ export declare const decodeCheckpoint: (checkpoint: string) => Checkpoint;
10
+ //# sourceMappingURL=checkpoint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkpoint.d.ts","sourceRoot":"","sources":["../../src/checkpoint.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAgBF,eAAO,MAAM,gBAAgB,eAAgB,MAAM,KAAG,UAqCrD,CAAC"}
@@ -3,11 +3,18 @@ import { type DefaultError, type QueryKey, type UseQueryOptions, type UseQueryRe
3
3
  import type { ResolvedSchema } from "./index.js";
4
4
  export declare function usePonderQuery<queryFnData = unknown, error = DefaultError, data = queryFnData>(params: {
5
5
  queryFn: (db: Client<ResolvedSchema>["db"]) => Promise<queryFnData>;
6
+ live?: boolean;
6
7
  } & Omit<UseQueryOptions<queryFnData, error, data>, "queryFn" | "queryKey">): UseQueryResult<data, error>;
7
8
  export declare function usePonderClient(): Client<ResolvedSchema>;
8
9
  export declare function usePonderQueryOptions<T>(queryFn: (db: Client<ResolvedSchema>["db"]) => T): {
9
10
  queryKey: QueryKey;
10
11
  queryFn: () => T;
11
12
  };
12
- export declare function usePonderStatus(params: Omit<UseQueryOptions<Status>, "queryFn" | "queryKey">): UseQueryResult<Status>;
13
+ export declare function usePonderStatus<error = DefaultError>(params?: {
14
+ live?: boolean;
15
+ } & Omit<UseQueryOptions<{
16
+ chain_name: string;
17
+ chain_id: number;
18
+ latest_checkpoint: string;
19
+ }[], error, Status>, "queryFn" | "queryKey" | "select">): UseQueryResult<Status, error>;
13
20
  //# sourceMappingURL=hook.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"hook.d.ts","sourceRoot":"","sources":["../../src/hook.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,QAAQ,EACb,KAAK,eAAe,EACpB,KAAK,cAAc,EAGpB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAGjD,wBAAgB,cAAc,CAC5B,WAAW,GAAG,OAAO,EACrB,KAAK,GAAG,YAAY,EACpB,IAAI,GAAG,WAAW,EAElB,MAAM,EAAE;IACN,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;CACrE,GAAG,IAAI,CAAC,eAAe,CAAC,WAAW,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC,GAC1E,cAAc,CAAC,IAAI,EAAE,KAAK,CAAC,CAyB7B;AAED,wBAAgB,eAAe,IAAI,MAAM,CAAC,cAAc,CAAC,CAMxD;AAED,wBAAgB,qBAAqB,CAAC,CAAC,EACrC,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAC/C;IACD,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC,CAAC;CAClB,CAGA;AAID,wBAAgB,eAAe,CAC7B,MAAM,EAAE,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC,GAC5D,cAAc,CAAC,MAAM,CAAC,CAsBxB"}
1
+ {"version":3,"file":"hook.d.ts","sourceRoot":"","sources":["../../src/hook.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,QAAQ,EACb,KAAK,eAAe,EACpB,KAAK,cAAc,EAGpB,MAAM,uBAAuB,CAAC;AAI/B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAGjD,wBAAgB,cAAc,CAC5B,WAAW,GAAG,OAAO,EACrB,KAAK,GAAG,YAAY,EACpB,IAAI,GAAG,WAAW,EAElB,MAAM,EAAE;IACN,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;IACpE,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB,GAAG,IAAI,CAAC,eAAe,CAAC,WAAW,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC,GAC1E,cAAc,CAAC,IAAI,EAAE,KAAK,CAAC,CA8B7B;AAED,wBAAgB,eAAe,IAAI,MAAM,CAAC,cAAc,CAAC,CAMxD;AAED,wBAAgB,qBAAqB,CAAC,CAAC,EACrC,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAC/C;IACD,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC,CAAC;CAClB,CAGA;AAED,wBAAgB,eAAe,CAAC,KAAK,GAAG,YAAY,EAClD,MAAM,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CAChC,eAAe,CACb;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAA;CAAE,EAAE,EACrE,KAAK,EACL,MAAM,CACP,EACD,SAAS,GAAG,UAAU,GAAG,QAAQ,CAClC,GACA,cAAc,CAAC,MAAM,EAAE,KAAK,CAAC,CA6B/B"}
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC3D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;AAE7E,wBAAgB,qBAAqB,CAAC,CAAC,EACrC,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC,EAC9B,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAC/C;IACD,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC,CAAC;CAClB,CAeA"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC3D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;AAE7E,wBAAgB,qBAAqB,CAAC,CAAC,EACrC,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC,EAC9B,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAC/C;IACD,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC,CAAC;CAClB,CAiBA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ponder/react",
3
- "version": "0.14.12",
3
+ "version": "0.15.0",
4
4
  "description": "React hooks for Ponder",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -31,7 +31,7 @@
31
31
  "superjson": "^2.2.2"
32
32
  },
33
33
  "peerDependencies": {
34
- "@ponder/client": ">=0.14.12",
34
+ "@ponder/client": ">=0.15.0",
35
35
  "@tanstack/react-query": ">=5.0.0",
36
36
  "react": ">=18",
37
37
  "typescript": ">=5.0.4"
@@ -52,7 +52,7 @@
52
52
  "rimraf": "^5.0.5",
53
53
  "tsx": "^4.19.2",
54
54
  "vitest": "^1.0.2",
55
- "@ponder/client": "0.14.12"
55
+ "@ponder/client": "0.15.0"
56
56
  },
57
57
  "scripts": {
58
58
  "build": "tsx build.ts",
@@ -0,0 +1,61 @@
1
+ export type Checkpoint = {
2
+ blockTimestamp: bigint;
3
+ chainId: bigint;
4
+ blockNumber: bigint;
5
+ transactionIndex: bigint;
6
+ eventType: number;
7
+ eventIndex: bigint;
8
+ };
9
+
10
+ // 10 digits for unix timestamp gets us to the year 2277.
11
+ const BLOCK_TIMESTAMP_DIGITS = 10;
12
+ // Chain IDs are uint256. As of writing the largest Chain ID on https://chainlist.org
13
+ // is 13 digits. 16 digits should be enough (JavaScript's max safe integer).
14
+ const CHAIN_ID_DIGITS = 16;
15
+ // Same logic as chain ID.
16
+ const BLOCK_NUMBER_DIGITS = 16;
17
+ // Same logic as chain ID.
18
+ const TRANSACTION_INDEX_DIGITS = 16;
19
+ // At time of writing, we only have 2 event types planned, so one digit (10 types) is enough.
20
+ const EVENT_TYPE_DIGITS = 1;
21
+ // This could contain log index, trace index, etc. 16 digits should be enough.
22
+ const EVENT_INDEX_DIGITS = 16;
23
+
24
+ export const decodeCheckpoint = (checkpoint: string): Checkpoint => {
25
+ let offset = 0;
26
+
27
+ const blockTimestamp = BigInt(
28
+ checkpoint.slice(offset, offset + BLOCK_TIMESTAMP_DIGITS),
29
+ );
30
+ offset += BLOCK_TIMESTAMP_DIGITS;
31
+
32
+ const chainId = BigInt(checkpoint.slice(offset, offset + CHAIN_ID_DIGITS));
33
+ offset += CHAIN_ID_DIGITS;
34
+
35
+ const blockNumber = BigInt(
36
+ checkpoint.slice(offset, offset + BLOCK_NUMBER_DIGITS),
37
+ );
38
+ offset += BLOCK_NUMBER_DIGITS;
39
+
40
+ const transactionIndex = BigInt(
41
+ checkpoint.slice(offset, offset + TRANSACTION_INDEX_DIGITS),
42
+ );
43
+ offset += TRANSACTION_INDEX_DIGITS;
44
+
45
+ const eventType = +checkpoint.slice(offset, offset + EVENT_TYPE_DIGITS);
46
+ offset += EVENT_TYPE_DIGITS;
47
+
48
+ const eventIndex = BigInt(
49
+ checkpoint.slice(offset, offset + EVENT_INDEX_DIGITS),
50
+ );
51
+ offset += EVENT_INDEX_DIGITS;
52
+
53
+ return {
54
+ blockTimestamp,
55
+ chainId,
56
+ blockNumber,
57
+ transactionIndex,
58
+ eventType,
59
+ eventIndex,
60
+ };
61
+ };
package/src/hook.ts CHANGED
@@ -10,6 +10,7 @@ import {
10
10
  useQueryClient,
11
11
  } from "@tanstack/react-query";
12
12
  import { useContext, useEffect, useMemo } from "react";
13
+ import { decodeCheckpoint } from "./checkpoint.js";
13
14
  import { PonderContext } from "./context.js";
14
15
  import type { ResolvedSchema } from "./index.js";
15
16
  import { getPonderQueryOptions } from "./utils.js";
@@ -21,8 +22,10 @@ export function usePonderQuery<
21
22
  >(
22
23
  params: {
23
24
  queryFn: (db: Client<ResolvedSchema>["db"]) => Promise<queryFnData>;
25
+ live?: boolean;
24
26
  } & Omit<UseQueryOptions<queryFnData, error, data>, "queryFn" | "queryKey">,
25
27
  ): UseQueryResult<data, error> {
28
+ const live = params.live ?? true;
26
29
  const queryClient = useQueryClient();
27
30
 
28
31
  const client = usePonderClient();
@@ -35,17 +38,21 @@ export function usePonderQuery<
35
38
 
36
39
  // biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
37
40
  useEffect(() => {
38
- const { unsubscribe } = client.live(
39
- () => Promise.resolve(),
40
- () => queryClient.invalidateQueries({ queryKey: queryOptions.queryKey }),
41
- );
41
+ if (live === false) return;
42
+
43
+ const { unsubscribe } = client.live(queryOptions.queryFn, (data) => {
44
+ queryClient.setQueryData(queryOptions.queryKey, data);
45
+ });
42
46
  return unsubscribe;
43
- }, queryOptions.queryKey);
47
+ }, [...queryOptions.queryKey, live]);
44
48
 
45
49
  return useQuery({
46
50
  ...params,
47
51
  queryKey: queryOptions.queryKey,
48
52
  queryFn: queryOptions.queryFn,
53
+ staleTime: live
54
+ ? (params.staleTime ?? Number.POSITIVE_INFINITY)
55
+ : params.staleTime,
49
56
  });
50
57
  }
51
58
 
@@ -67,30 +74,42 @@ export function usePonderQueryOptions<T>(
67
74
  return getPonderQueryOptions(client, queryFn);
68
75
  }
69
76
 
70
- const statusQueryKey = ["status"];
71
-
72
- export function usePonderStatus(
73
- params: Omit<UseQueryOptions<Status>, "queryFn" | "queryKey">,
74
- ): UseQueryResult<Status> {
75
- const queryClient = useQueryClient();
76
-
77
- const client = useContext(PonderContext);
78
- if (client === undefined) {
79
- throw new Error("PonderProvider not found");
80
- }
81
-
82
- // biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
83
- useEffect(() => {
84
- const { unsubscribe } = client.live(
85
- () => Promise.resolve(),
86
- () => queryClient.invalidateQueries({ queryKey: statusQueryKey }),
87
- );
88
- return unsubscribe;
89
- }, []);
90
-
91
- return useQuery({
77
+ export function usePonderStatus<error = DefaultError>(
78
+ params?: { live?: boolean } & Omit<
79
+ UseQueryOptions<
80
+ { chain_name: string; chain_id: number; latest_checkpoint: string }[],
81
+ error,
82
+ Status
83
+ >,
84
+ "queryFn" | "queryKey" | "select"
85
+ >,
86
+ ): UseQueryResult<Status, error> {
87
+ return usePonderQuery<
88
+ { chain_name: string; chain_id: number; latest_checkpoint: string }[],
89
+ error,
90
+ Status
91
+ >({
92
92
  ...params,
93
- queryKey: statusQueryKey,
94
- queryFn: () => client.getStatus(),
93
+ queryFn: (db) => db.execute("SELECT * FROM _ponder_checkpoint"),
94
+ select(checkpoints) {
95
+ const status: Status = {};
96
+ for (const {
97
+ chain_name,
98
+ chain_id,
99
+ latest_checkpoint,
100
+ } of checkpoints.sort((a, b) => (a.chain_id > b.chain_id ? 1 : -1))) {
101
+ status[chain_name] = {
102
+ id: chain_id,
103
+ block: {
104
+ number: Number(decodeCheckpoint(latest_checkpoint).blockNumber),
105
+ timestamp: Number(
106
+ decodeCheckpoint(latest_checkpoint).blockTimestamp,
107
+ ),
108
+ },
109
+ };
110
+ }
111
+
112
+ return status;
113
+ },
95
114
  });
96
115
  }
package/src/utils.ts CHANGED
@@ -16,7 +16,9 @@ export function getPonderQueryOptions<T>(
16
16
 
17
17
  // @ts-expect-error
18
18
  if ("getSQL" in queryPromise === false) {
19
- throw new Error('"queryFn" must return SQL');
19
+ throw new Error(
20
+ '"queryFn" must return SQL. You may have to remove `.execute()` from your query.',
21
+ );
20
22
  }
21
23
 
22
24
  const query = compileQuery(queryPromise as unknown as SQLWrapper);