ponder 0.16.5 → 0.16.7

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 (39) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/esm/bin/commands/createViews.js +5 -27
  3. package/dist/esm/bin/commands/createViews.js.map +1 -1
  4. package/dist/esm/build/schema.js +0 -7
  5. package/dist/esm/build/schema.js.map +1 -1
  6. package/dist/esm/database/actions.js +7 -55
  7. package/dist/esm/database/actions.js.map +1 -1
  8. package/dist/esm/drizzle/onchain.js +44 -0
  9. package/dist/esm/drizzle/onchain.js.map +1 -1
  10. package/dist/esm/graphql/index.js +33 -3
  11. package/dist/esm/graphql/index.js.map +1 -1
  12. package/dist/esm/indexing-store/cache.js +2 -2
  13. package/dist/esm/indexing-store/cache.js.map +1 -1
  14. package/dist/esm/indexing-store/index.js +1 -1
  15. package/dist/esm/indexing-store/index.js.map +1 -1
  16. package/dist/esm/indexing-store/utils.js +1 -1
  17. package/dist/esm/indexing-store/utils.js.map +1 -1
  18. package/dist/esm/utils/print.js +3 -2
  19. package/dist/esm/utils/print.js.map +1 -1
  20. package/dist/types/bin/commands/createViews.d.ts.map +1 -1
  21. package/dist/types/build/schema.d.ts.map +1 -1
  22. package/dist/types/database/actions.d.ts.map +1 -1
  23. package/dist/types/drizzle/onchain.d.ts +4 -0
  24. package/dist/types/drizzle/onchain.d.ts.map +1 -1
  25. package/dist/types/graphql/index.d.ts.map +1 -1
  26. package/dist/types/indexing-store/index.d.ts.map +1 -1
  27. package/dist/types/indexing-store/utils.d.ts.map +1 -1
  28. package/dist/types/utils/print.d.ts +3 -1
  29. package/dist/types/utils/print.d.ts.map +1 -1
  30. package/package.json +2 -2
  31. package/src/bin/commands/createViews.ts +7 -27
  32. package/src/build/schema.ts +0 -11
  33. package/src/database/actions.ts +9 -55
  34. package/src/drizzle/onchain.ts +48 -0
  35. package/src/graphql/index.ts +42 -2
  36. package/src/indexing-store/cache.ts +2 -2
  37. package/src/indexing-store/index.ts +3 -1
  38. package/src/indexing-store/utils.ts +3 -1
  39. package/src/utils/print.ts +4 -1
@@ -16,7 +16,7 @@ import {
16
16
  import {
17
17
  getLiveQueryChannelName,
18
18
  getLiveQueryNotifyProcedureName,
19
- getLiveQueryTempTableName,
19
+ getLiveQueryNotifyProcedureSql,
20
20
  getViewsLiveQueryNotifyTriggerName,
21
21
  } from "@/drizzle/onchain.js";
22
22
  import { sql } from "@/index.js";
@@ -273,32 +273,12 @@ export async function createViews({
273
273
  const channel = getLiveQueryChannelName(cliOptions.viewsSchema);
274
274
 
275
275
  await database.adminQB.wrap((db) =>
276
- db.execute(`
277
- CREATE OR REPLACE FUNCTION "${cliOptions.viewsSchema}".${notifyProcedure}
278
- RETURNS TRIGGER LANGUAGE plpgsql
279
- AS $$
280
- DECLARE
281
- table_names json;
282
- table_exists boolean := false;
283
- BEGIN
284
- SELECT EXISTS (
285
- SELECT 1
286
- FROM information_schema.tables
287
- WHERE table_name = '${getLiveQueryTempTableName()}'
288
- AND table_type = 'LOCAL TEMPORARY'
289
- ) INTO table_exists;
290
-
291
- IF table_exists THEN
292
- SELECT json_agg(table_name) INTO table_names
293
- FROM ${getLiveQueryTempTableName()};
294
-
295
- table_names := COALESCE(table_names, '[]'::json);
296
- PERFORM pg_notify('${channel}', table_names::text);
297
- END IF;
298
-
299
- RETURN NULL;
300
- END;
301
- $$;`),
276
+ db.execute(
277
+ getLiveQueryNotifyProcedureSql({
278
+ schema: cliOptions.viewsSchema!,
279
+ channel,
280
+ }),
281
+ ),
302
282
  );
303
283
 
304
284
  const trigger = getViewsLiveQueryNotifyTriggerName(cliOptions.viewsSchema);
@@ -27,11 +27,6 @@ import {
27
27
  getViewConfig,
28
28
  } from "drizzle-orm/pg-core";
29
29
 
30
- /**
31
- * @dev The maximum notify message size is 8KB (8000 / 63 > 100).
32
- */
33
- const TABLE_LIMIT = 100;
34
-
35
30
  export const buildSchema = ({
36
31
  schema,
37
32
  preBuild,
@@ -330,12 +325,6 @@ export const buildSchema = ({
330
325
  }
331
326
  }
332
327
 
333
- if (tableNames.size > TABLE_LIMIT) {
334
- throw new Error(
335
- `Schema validation failed: the maximum number of tables is ${TABLE_LIMIT}.`,
336
- );
337
- }
338
-
339
328
  return { statements };
340
329
  };
341
330
 
@@ -3,6 +3,7 @@ import { getColumnCasing, getReorgTable } from "@/drizzle/kit/index.js";
3
3
  import {
4
4
  getLiveQueryChannelName,
5
5
  getLiveQueryNotifyProcedureName,
6
+ getLiveQueryNotifyProcedureSql,
6
7
  getLiveQueryNotifyTriggerName,
7
8
  getLiveQueryProcedureName,
8
9
  getLiveQueryTempTableName,
@@ -246,37 +247,10 @@ $$;`,
246
247
  context,
247
248
  );
248
249
 
249
- const notifyProcedure = getLiveQueryNotifyProcedureName();
250
250
  const channel = getLiveQueryChannelName(namespaceBuild.schema);
251
251
 
252
252
  await tx.wrap(
253
- (tx) =>
254
- tx.execute(`
255
- CREATE OR REPLACE FUNCTION "${schema}".${notifyProcedure}
256
- RETURNS TRIGGER LANGUAGE plpgsql
257
- AS $$
258
- DECLARE
259
- table_names json;
260
- table_exists boolean := false;
261
- BEGIN
262
- SELECT EXISTS (
263
- SELECT 1
264
- FROM information_schema.tables
265
- WHERE table_name = '${getLiveQueryTempTableName()}'
266
- AND table_type = 'LOCAL TEMPORARY'
267
- ) INTO table_exists;
268
-
269
- IF table_exists THEN
270
- SELECT json_agg(table_name) INTO table_names
271
- FROM ${getLiveQueryTempTableName()};
272
-
273
- table_names := COALESCE(table_names, '[]'::json);
274
- PERFORM pg_notify('${channel}', table_names::text);
275
- END IF;
276
-
277
- RETURN NULL;
278
- END;
279
- $$;`),
253
+ (tx) => tx.execute(getLiveQueryNotifyProcedureSql({ schema, channel })),
280
254
  context,
281
255
  );
282
256
  },
@@ -358,41 +332,21 @@ export const createViews = async (
358
332
  ),
359
333
  );
360
334
 
361
- const notifyProcedure = getLiveQueryNotifyProcedureName();
362
335
  const channel = getLiveQueryChannelName(namespaceBuild.viewsSchema!);
363
336
 
364
337
  await tx.wrap((tx) =>
365
- tx.execute(`
366
- CREATE OR REPLACE FUNCTION "${namespaceBuild.viewsSchema}".${notifyProcedure}
367
- RETURNS TRIGGER LANGUAGE plpgsql
368
- AS $$
369
- DECLARE
370
- table_names json;
371
- table_exists boolean := false;
372
- BEGIN
373
- SELECT EXISTS (
374
- SELECT 1
375
- FROM information_schema.tables
376
- WHERE table_name = '${getLiveQueryTempTableName()}'
377
- AND table_type = 'LOCAL TEMPORARY'
378
- ) INTO table_exists;
379
-
380
- IF table_exists THEN
381
- SELECT json_agg(table_name) INTO table_names
382
- FROM ${getLiveQueryTempTableName()};
383
-
384
- table_names := COALESCE(table_names, '[]'::json);
385
- PERFORM pg_notify('${channel}', table_names::text);
386
- END IF;
387
-
388
- RETURN NULL;
389
- END;
390
- $$;`),
338
+ tx.execute(
339
+ getLiveQueryNotifyProcedureSql({
340
+ schema: namespaceBuild.viewsSchema!,
341
+ channel,
342
+ }),
343
+ ),
391
344
  );
392
345
 
393
346
  const trigger = getViewsLiveQueryNotifyTriggerName(
394
347
  namespaceBuild.viewsSchema!,
395
348
  );
349
+ const notifyProcedure = getLiveQueryNotifyProcedureName();
396
350
 
397
351
  await tx.wrap((tx) =>
398
352
  tx.execute(
@@ -70,6 +70,54 @@ export const getLiveQueryNotifyProcedureName = () => {
70
70
  export const getLiveQueryTempTableName = () => {
71
71
  return "live_query_tables";
72
72
  };
73
+ export const getLiveQueryNotifyProcedureSql = ({
74
+ schema,
75
+ channel,
76
+ }: { schema: string; channel: string }) => {
77
+ const tempTableName = getLiveQueryTempTableName();
78
+
79
+ return `
80
+ CREATE OR REPLACE FUNCTION "${schema}".${getLiveQueryNotifyProcedureName()}
81
+ RETURNS TRIGGER LANGUAGE plpgsql
82
+ AS $$
83
+ DECLARE
84
+ current_payload text := '[';
85
+ separator text := '';
86
+ entry text;
87
+ entry_json text;
88
+ BEGIN
89
+ IF NOT EXISTS (
90
+ SELECT 1
91
+ FROM information_schema.tables
92
+ WHERE table_name = '${tempTableName}'
93
+ AND table_type = 'LOCAL TEMPORARY'
94
+ ) THEN
95
+ RETURN NULL;
96
+ END IF;
97
+
98
+ FOR entry IN SELECT table_name FROM ${tempTableName} ORDER BY table_name LOOP
99
+ entry_json := to_json(entry)::text;
100
+
101
+ IF current_payload <> '[' AND octet_length(current_payload) + octet_length(separator) + octet_length(entry_json) + 1 >= 7900 THEN
102
+ PERFORM pg_notify('${channel}', current_payload || ']');
103
+ current_payload := '[' || entry_json;
104
+ separator := ',';
105
+ ELSE
106
+ current_payload := current_payload || separator || entry_json;
107
+ separator := ',';
108
+ END IF;
109
+ END LOOP;
110
+
111
+ IF current_payload = '[' THEN
112
+ PERFORM pg_notify('${channel}', '[]');
113
+ ELSE
114
+ PERFORM pg_notify('${channel}', current_payload || ']');
115
+ END IF;
116
+
117
+ RETURN NULL;
118
+ END;
119
+ $$;`;
120
+ };
73
121
  export const getPartitionName = (table: string | PgTable, chainId: number) => {
74
122
  return `${typeof table === "string" ? table : getTableName(table)}_${chainId}`;
75
123
  };
@@ -23,6 +23,7 @@ import {
23
23
  getTableColumns,
24
24
  gt,
25
25
  gte,
26
+ ilike,
26
27
  inArray,
27
28
  is,
28
29
  isNotNull,
@@ -32,6 +33,7 @@ import {
32
33
  lte,
33
34
  ne,
34
35
  not,
36
+ notIlike,
35
37
  notInArray,
36
38
  notLike,
37
39
  or,
@@ -171,7 +173,14 @@ export function buildGraphQLSchema({
171
173
  });
172
174
 
173
175
  if (["String", "ID"].includes(type.name)) {
174
- conditionSuffixes.string.forEach((suffix) => {
176
+ const stringConditionSuffixes =
177
+ column.columnType === "PgHex"
178
+ ? conditionSuffixes.string.filter(
179
+ (suffix) => suffix.endsWith("_nocase") === false,
180
+ )
181
+ : conditionSuffixes.string;
182
+
183
+ stringConditionSuffixes.forEach((suffix) => {
175
184
  filterFields[`${columnName}${suffix}`] = {
176
185
  type: type,
177
186
  };
@@ -251,7 +260,14 @@ export function buildGraphQLSchema({
251
260
  });
252
261
 
253
262
  if (["String", "ID"].includes(type.name)) {
254
- conditionSuffixes.string.forEach((suffix) => {
263
+ const stringConditionSuffixes =
264
+ (column as PgColumn).columnType === "PgHex"
265
+ ? conditionSuffixes.string.filter(
266
+ (suffix) => suffix.endsWith("_nocase") === false,
267
+ )
268
+ : conditionSuffixes.string;
269
+
270
+ stringConditionSuffixes.forEach((suffix) => {
255
271
  filterFields[`${columnName}${suffix}`] = {
256
272
  type: type,
257
273
  };
@@ -1012,6 +1028,12 @@ const conditionSuffixes = {
1012
1028
  "_ends_with",
1013
1029
  "_not_starts_with",
1014
1030
  "_not_ends_with",
1031
+ "_contains_nocase",
1032
+ "_not_contains_nocase",
1033
+ "_starts_with_nocase",
1034
+ "_ends_with_nocase",
1035
+ "_not_starts_with_nocase",
1036
+ "_not_ends_with_nocase",
1015
1037
  ],
1016
1038
  } as const;
1017
1039
 
@@ -1152,6 +1174,24 @@ function buildWhereConditions(
1152
1174
  case "_not_ends_with":
1153
1175
  conditions.push(notLike(column, `%${rawValue}`));
1154
1176
  break;
1177
+ case "_contains_nocase":
1178
+ conditions.push(ilike(column, `%${rawValue}%`));
1179
+ break;
1180
+ case "_not_contains_nocase":
1181
+ conditions.push(notIlike(column, `%${rawValue}%`));
1182
+ break;
1183
+ case "_starts_with_nocase":
1184
+ conditions.push(ilike(column, `${rawValue}%`));
1185
+ break;
1186
+ case "_ends_with_nocase":
1187
+ conditions.push(ilike(column, `%${rawValue}`));
1188
+ break;
1189
+ case "_not_starts_with_nocase":
1190
+ conditions.push(notIlike(column, `${rawValue}%`));
1191
+ break;
1192
+ case "_not_ends_with_nocase":
1193
+ conditions.push(notIlike(column, `%${rawValue}`));
1194
+ break;
1155
1195
  default:
1156
1196
  never(conditionSuffix);
1157
1197
  }
@@ -762,7 +762,7 @@ export const createIndexingCache = ({
762
762
 
763
763
  addErrorMeta(
764
764
  error,
765
- `db.insert arguments:\n${prettyPrint(result.value.row)}`,
765
+ `db.insert arguments:\n${prettyPrint(result.value.row, { truncate: false })}`,
766
766
  );
767
767
 
768
768
  if (result.value.metadata.event) {
@@ -834,7 +834,7 @@ export const createIndexingCache = ({
834
834
 
835
835
  addErrorMeta(
836
836
  error,
837
- `db.update arguments:\n${prettyPrint(result.value.row)}`,
837
+ `db.update arguments:\n${prettyPrint(result.value.row, { truncate: false })}`,
838
838
  );
839
839
 
840
840
  if (result.value.metadata.event) {
@@ -497,7 +497,9 @@ export const createIndexingStore = ({
497
497
  const error = new RecordNotFoundError(
498
498
  `No existing record found in table '${getTableName(table)}'`,
499
499
  );
500
- error.meta.push(`db.update arguments:\n${prettyPrint(key)}`);
500
+ error.meta.push(
501
+ `db.update arguments:\n${prettyPrint(key, { truncate: false })}`,
502
+ );
501
503
  throw error;
502
504
  }
503
505
 
@@ -115,7 +115,9 @@ export const normalizeRow = (
115
115
  table,
116
116
  )}.${columnName}' violates not-null constraint.`,
117
117
  );
118
- error.meta.push(`db.insert arguments:\n${prettyPrint(row)}`);
118
+ error.meta.push(
119
+ `db.insert arguments:\n${prettyPrint(row, { truncate: false })}`,
120
+ );
119
121
  throw error;
120
122
  }
121
123
 
@@ -2,15 +2,18 @@
2
2
  // https://github.com/wagmi-dev/viem/blob/021ce8e5a3fb02db6139564345a91fc77cba08a6/src/errors/transaction.ts#L6-L19
3
3
  export function prettyPrint(
4
4
  args?: Record<string, bigint | number | string | undefined | false | unknown>,
5
+ options?: { truncate?: boolean },
5
6
  ) {
6
7
  if (args === undefined) return "(undefined)";
7
8
 
9
+ const shouldTruncate = options?.truncate !== false;
10
+
8
11
  const entries = Object.entries(args)
9
12
  .map(([key, value]) => {
10
13
  if (value === undefined) return null;
11
14
 
12
15
  const trimmedValue =
13
- typeof value === "string" && value.length > 80
16
+ typeof value === "string" && value.length > 80 && shouldTruncate
14
17
  ? value.slice(0, 80).concat("...")
15
18
  : value;
16
19