@0xobelisk/graphql-client 1.2.0-pre.99 → 2.0.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/src/client.ts CHANGED
@@ -11,12 +11,98 @@ import {
11
11
  FetchPolicy,
12
12
  OperationVariables
13
13
  } from '@apollo/client';
14
+ import { BatchHttpLink } from '@apollo/client/link/batch-http';
14
15
  import { RetryLink } from '@apollo/client/link/retry';
15
16
  import { GraphQLWsLink } from '@apollo/client/link/subscriptions';
16
17
  import { getMainDefinition } from '@apollo/client/utilities';
17
18
  import { createClient } from 'graphql-ws';
18
19
  import pluralize from 'pluralize';
19
20
 
21
+ /**
22
+ * Static field registry for Dubhe system tables.
23
+ *
24
+ * These tables are exposed via `store_dubhe_*` views in the graphql-server.
25
+ * They are NOT in the DApp's dubhe.config schema, so `convertTableFields`
26
+ * cannot auto-discover their fields. Registering them here means callers
27
+ * can use `getAllTables('dubheDappMarketplaceFees', ...)` without passing
28
+ * `fields` explicitly.
29
+ *
30
+ * Key: the simplified GraphQL field name (after SimpleNamingPlugin strips
31
+ * the `storeDubhe` prefix and removes the `all` prefix).
32
+ * Value: camelCase GraphQL field names matching the PostGraphile schema.
33
+ */
34
+ const SYSTEM_TABLE_FIELDS: Record<string, string[]> = {
35
+ dubheDappMarketplaceFees: [
36
+ 'dappKey',
37
+ 'listingId',
38
+ 'coinType',
39
+ 'totalFee',
40
+ 'treasuryAmount',
41
+ 'dappAmount',
42
+ 'updatedAtCheckpoint',
43
+ 'lastUpdateDigest'
44
+ ],
45
+ dubheDappRuntimeState: [
46
+ 'dappKey',
47
+ 'admin',
48
+ 'paused',
49
+ 'settlementMode',
50
+ 'creditPool',
51
+ 'writeFeeShareBps',
52
+ 'lastRuntimeEvent',
53
+ 'lastRuntimeActor',
54
+ 'lastRuntimeAmount',
55
+ 'updatedAtCheckpoint',
56
+ 'lastUpdateDigest'
57
+ ],
58
+ dubheDappFeeState: [
59
+ 'entityId',
60
+ 'baseFeePerWrite',
61
+ 'bytesFeePerByte',
62
+ 'freeCredit',
63
+ 'creditPool',
64
+ 'totalSettled',
65
+ 'updatedAtTimestampMs',
66
+ 'lastUpdateDigest'
67
+ ],
68
+ dubheDappRevenueState: [
69
+ 'entityId',
70
+ 'dappRevenue',
71
+ 'coinType',
72
+ 'updatedAtTimestampMs',
73
+ 'lastUpdateDigest'
74
+ ],
75
+ dubheMarketplaceListings: [
76
+ 'dappKey',
77
+ 'listingId',
78
+ 'seller',
79
+ 'recordType',
80
+ 'price',
81
+ 'coinType',
82
+ 'isFungible',
83
+ 'listedUntil',
84
+ 'status',
85
+ 'updatedAtCheckpoint',
86
+ 'lastUpdateDigest'
87
+ ],
88
+ dubheSessions: [
89
+ 'dappKey',
90
+ 'canonical',
91
+ 'sessionWallet',
92
+ 'expiresAt',
93
+ 'active',
94
+ 'updatedAtCheckpoint',
95
+ 'lastUpdateDigest'
96
+ ],
97
+ dubheUserStorages: [
98
+ 'dappKey',
99
+ 'canonicalOwner',
100
+ 'userStorageId',
101
+ 'updatedAtCheckpoint',
102
+ 'lastUpdateDigest'
103
+ ]
104
+ };
105
+
20
106
  import {
21
107
  DubheClientConfig,
22
108
  Connection,
@@ -32,7 +118,14 @@ import {
32
118
  MultiTableSubscriptionConfig,
33
119
  MultiTableSubscriptionData,
34
120
  ParsedTableInfo,
35
- DubheMetadata
121
+ DubheMetadata,
122
+ MarketplaceListingRow,
123
+ DubheSessionRow,
124
+ DubheUserStorageRow,
125
+ DubheDappRuntimeStateRow,
126
+ SceneStorageRow,
127
+ SceneStorageFieldRow,
128
+ ObjectStorageRow
36
129
  } from './types';
37
130
 
38
131
  // Convert cache policy type
@@ -53,6 +146,37 @@ function mapCachePolicyToFetchPolicy(cachePolicy: CachePolicy): FetchPolicy {
53
146
  }
54
147
  }
55
148
 
149
+ /**
150
+ * Build the HTTP transport link.
151
+ *
152
+ * When `config.batchRequests` is true, returns a `BatchHttpLink` that collects
153
+ * queries fired within `batchInterval` ms (default 10 ms) and sends them as a
154
+ * single POST request. The PostGraphile server already has `enableQueryBatching`
155
+ * enabled, so no server-side changes are needed.
156
+ *
157
+ * Falls back to the standard `createHttpLink` when batching is disabled.
158
+ */
159
+ function buildHttpLink(config: DubheClientConfig): ApolloLink {
160
+ const fetchFn = (input: RequestInfo | URL, init?: RequestInit) =>
161
+ fetch(input, { ...config.fetchOptions, ...(init ?? {}) });
162
+
163
+ if (config.batchRequests) {
164
+ return new BatchHttpLink({
165
+ uri: config.endpoint,
166
+ headers: config.headers,
167
+ fetch: fetchFn,
168
+ batchInterval: config.batchInterval ?? 10,
169
+ batchMax: config.batchMax ?? 20
170
+ });
171
+ }
172
+
173
+ return createHttpLink({
174
+ uri: config.endpoint,
175
+ headers: config.headers,
176
+ fetch: fetchFn
177
+ });
178
+ }
179
+
56
180
  export class DubheGraphqlClient {
57
181
  private apolloClient: ApolloClient<NormalizedCacheObject>;
58
182
  private subscriptionClient?: any;
@@ -73,12 +197,8 @@ export class DubheGraphqlClient {
73
197
  this.parseTableInfoFromConfig();
74
198
  }
75
199
 
76
- // Create HTTP Link
77
- const httpLink = createHttpLink({
78
- uri: config.endpoint,
79
- headers: config.headers,
80
- fetch: (input, init) => fetch(input, { ...config.fetchOptions, ...init })
81
- });
200
+ // Create HTTP link (batched or standard depending on config.batchRequests)
201
+ const httpLink = buildHttpLink(config);
82
202
 
83
203
  // Create retry link
84
204
  const retryLink = new RetryLink({
@@ -244,12 +364,8 @@ export class DubheGraphqlClient {
244
364
  this.subscriptionClient = undefined;
245
365
  }
246
366
 
247
- // Recreate HTTP Link
248
- const httpLink = createHttpLink({
249
- uri: this.currentConfig.endpoint,
250
- headers: this.currentConfig.headers,
251
- fetch: (input, init) => fetch(input, { ...this.currentConfig.fetchOptions, ...init })
252
- });
367
+ // Recreate HTTP link (batched or standard depending on config)
368
+ const httpLink = buildHttpLink(this.currentConfig);
253
369
 
254
370
  // Recreate retry link with current config
255
371
  const retryLink = new RetryLink({
@@ -883,6 +999,480 @@ export class DubheGraphqlClient {
883
999
  });
884
1000
  }
885
1001
 
1002
+ /**
1003
+ * Query marketplace listings indexed by the Dubhe indexer.
1004
+ * Uses the marketplace_listings PostgreSQL table (exposed as dubheMarketplaceListings in GraphQL).
1005
+ * The record_type field is pre-decoded text ("wheat", "corn", …) and status tracks
1006
+ * active/sold/cancelled directly — no extra RPC calls needed.
1007
+ */
1008
+ async getMarketplaceListings(options?: {
1009
+ dappKey?: string;
1010
+ status?: 'listed' | 'sold' | 'cancelled' | 'expired';
1011
+ recordType?: string;
1012
+ seller?: string;
1013
+ first?: number;
1014
+ after?: string;
1015
+ }): Promise<Connection<MarketplaceListingRow>> {
1016
+ const filter: Record<string, any> = {};
1017
+ if (options?.dappKey) filter.dappKey = { equalTo: options.dappKey };
1018
+ if (options?.status !== undefined) filter.status = { equalTo: options.status };
1019
+ if (options?.recordType) filter.recordType = { equalTo: options.recordType };
1020
+ if (options?.seller) filter.seller = { equalTo: options.seller };
1021
+
1022
+ const query = gql`
1023
+ query GetMarketplaceListings(
1024
+ $first: Int
1025
+ $after: Cursor
1026
+ $filter: StoreDubheMarketplaceListingFilter
1027
+ $orderBy: [StoreDubheMarketplaceListingsOrderBy!]
1028
+ ) {
1029
+ dubheMarketplaceListings(first: $first, after: $after, filter: $filter, orderBy: $orderBy) {
1030
+ totalCount
1031
+ pageInfo {
1032
+ hasNextPage
1033
+ hasPreviousPage
1034
+ startCursor
1035
+ endCursor
1036
+ }
1037
+ edges {
1038
+ cursor
1039
+ node {
1040
+ listingId
1041
+ dappKey
1042
+ seller
1043
+ recordType
1044
+ recordDataRaw
1045
+ price
1046
+ coinType
1047
+ isFungible
1048
+ status
1049
+ buyer
1050
+ listedUntil
1051
+ createdAtCheckpoint
1052
+ updatedAtCheckpoint
1053
+ lastUpdateDigest
1054
+ }
1055
+ }
1056
+ }
1057
+ }
1058
+ `;
1059
+
1060
+ const result = await this.apolloClient.query({
1061
+ query,
1062
+ variables: {
1063
+ first: options?.first ?? 100,
1064
+ after: options?.after,
1065
+ filter: Object.keys(filter).length > 0 ? filter : undefined,
1066
+ orderBy: ['CREATED_AT_CHECKPOINT_DESC']
1067
+ },
1068
+ fetchPolicy: 'network-only'
1069
+ });
1070
+
1071
+ if (result.error) throw result.error;
1072
+ return (
1073
+ (result.data as any)?.dubheMarketplaceListings ?? {
1074
+ edges: [],
1075
+ pageInfo: { hasNextPage: false, hasPreviousPage: false },
1076
+ totalCount: 0
1077
+ }
1078
+ );
1079
+ }
1080
+
1081
+ /**
1082
+ * Query session keys indexed by the Dubhe indexer (dubheSessions in GraphQL).
1083
+ */
1084
+ async getDubheSessions(options?: {
1085
+ dappKey?: string;
1086
+ canonical?: string;
1087
+ active?: boolean;
1088
+ first?: number;
1089
+ after?: string;
1090
+ }): Promise<Connection<DubheSessionRow>> {
1091
+ const filter: Record<string, any> = {};
1092
+ if (options?.dappKey) filter.dappKey = { equalTo: options.dappKey };
1093
+ if (options?.canonical) filter.canonical = { equalTo: options.canonical };
1094
+ if (options?.active !== undefined) filter.active = { equalTo: options.active };
1095
+
1096
+ const query = gql`
1097
+ query GetDubheSessions(
1098
+ $first: Int
1099
+ $after: Cursor
1100
+ $filter: StoreDubheSessionFilter
1101
+ $orderBy: [StoreDubheSessionsOrderBy!]
1102
+ ) {
1103
+ dubheSessions(first: $first, after: $after, filter: $filter, orderBy: $orderBy) {
1104
+ totalCount
1105
+ pageInfo {
1106
+ hasNextPage
1107
+ hasPreviousPage
1108
+ startCursor
1109
+ endCursor
1110
+ }
1111
+ edges {
1112
+ cursor
1113
+ node {
1114
+ dappKey
1115
+ canonical
1116
+ sessionWallet
1117
+ expiresAt
1118
+ active
1119
+ updatedAtCheckpoint
1120
+ lastUpdateDigest
1121
+ lastEventSeq
1122
+ }
1123
+ }
1124
+ }
1125
+ }
1126
+ `;
1127
+
1128
+ const result = await this.apolloClient.query({
1129
+ query,
1130
+ variables: {
1131
+ first: options?.first ?? 100,
1132
+ after: options?.after,
1133
+ filter: Object.keys(filter).length > 0 ? filter : undefined,
1134
+ orderBy: ['UPDATED_AT_CHECKPOINT_DESC']
1135
+ },
1136
+ fetchPolicy: 'network-only'
1137
+ });
1138
+
1139
+ if (result.error) throw result.error;
1140
+ return (
1141
+ (result.data as any)?.dubheSessions ?? {
1142
+ edges: [],
1143
+ pageInfo: { hasNextPage: false, hasPreviousPage: false },
1144
+ totalCount: 0
1145
+ }
1146
+ );
1147
+ }
1148
+
1149
+ /**
1150
+ * Query user storage registrations indexed by the Dubhe indexer (dubheUserStorages in GraphQL).
1151
+ */
1152
+ async getDubheUserStorages(options?: {
1153
+ dappKey?: string;
1154
+ canonicalOwner?: string;
1155
+ first?: number;
1156
+ after?: string;
1157
+ }): Promise<Connection<DubheUserStorageRow>> {
1158
+ const filter: Record<string, any> = {};
1159
+ if (options?.dappKey) filter.dappKey = { equalTo: options.dappKey };
1160
+ if (options?.canonicalOwner) filter.canonicalOwner = { equalTo: options.canonicalOwner };
1161
+
1162
+ const query = gql`
1163
+ query GetDubheUserStorages(
1164
+ $first: Int
1165
+ $after: Cursor
1166
+ $filter: StoreDubheUserStorageFilter
1167
+ $orderBy: [StoreDubheUserStoragesOrderBy!]
1168
+ ) {
1169
+ dubheUserStorages(first: $first, after: $after, filter: $filter, orderBy: $orderBy) {
1170
+ totalCount
1171
+ pageInfo {
1172
+ hasNextPage
1173
+ hasPreviousPage
1174
+ startCursor
1175
+ endCursor
1176
+ }
1177
+ edges {
1178
+ cursor
1179
+ node {
1180
+ dappKey
1181
+ canonicalOwner
1182
+ userStorageId
1183
+ createdAtCheckpoint
1184
+ updatedAtCheckpoint
1185
+ lastUpdateDigest
1186
+ lastEventSeq
1187
+ }
1188
+ }
1189
+ }
1190
+ }
1191
+ `;
1192
+
1193
+ const result = await this.apolloClient.query({
1194
+ query,
1195
+ variables: {
1196
+ first: options?.first ?? 100,
1197
+ after: options?.after,
1198
+ filter: Object.keys(filter).length > 0 ? filter : undefined,
1199
+ orderBy: ['CREATED_AT_CHECKPOINT_DESC']
1200
+ },
1201
+ fetchPolicy: 'network-only'
1202
+ });
1203
+
1204
+ if (result.error) throw result.error;
1205
+ return (
1206
+ (result.data as any)?.dubheUserStorages ?? {
1207
+ edges: [],
1208
+ pageInfo: { hasNextPage: false, hasPreviousPage: false },
1209
+ totalCount: 0
1210
+ }
1211
+ );
1212
+ }
1213
+
1214
+ /**
1215
+ * Query SceneStorage system rows indexed by the Dubhe indexer
1216
+ * (scene_storages table, exposed as dubheSceneStorages in GraphQL).
1217
+ * Field values live in the companion scene_storage_fields table —
1218
+ * see getSceneStorageFields.
1219
+ */
1220
+ async getSceneStorages(options?: {
1221
+ dappKey?: string;
1222
+ sceneType?: string;
1223
+ sceneId?: string;
1224
+ isDestroyed?: boolean;
1225
+ first?: number;
1226
+ after?: string;
1227
+ }): Promise<Connection<SceneStorageRow>> {
1228
+ const filter: Record<string, any> = {};
1229
+ if (options?.dappKey) filter.dappKey = { equalTo: options.dappKey };
1230
+ if (options?.sceneType) filter.sceneType = { equalTo: options.sceneType };
1231
+ if (options?.sceneId) filter.sceneId = { equalTo: options.sceneId };
1232
+ if (options?.isDestroyed !== undefined) filter.isDestroyed = { equalTo: options.isDestroyed };
1233
+
1234
+ const query = gql`
1235
+ query GetSceneStorages(
1236
+ $first: Int
1237
+ $after: Cursor
1238
+ $filter: StoreDubheSceneStorageFilter
1239
+ $orderBy: [StoreDubheSceneStoragesOrderBy!]
1240
+ ) {
1241
+ dubheSceneStorages(first: $first, after: $after, filter: $filter, orderBy: $orderBy) {
1242
+ totalCount
1243
+ pageInfo {
1244
+ hasNextPage
1245
+ hasPreviousPage
1246
+ startCursor
1247
+ endCursor
1248
+ }
1249
+ edges {
1250
+ cursor
1251
+ node {
1252
+ sceneId
1253
+ dappKey
1254
+ sceneType
1255
+ authorizationKind
1256
+ authorizedPermitId
1257
+ isDestroyed
1258
+ createdAtCheckpoint
1259
+ updatedAtCheckpoint
1260
+ lastUpdateDigest
1261
+ lastEventSeq
1262
+ }
1263
+ }
1264
+ }
1265
+ }
1266
+ `;
1267
+
1268
+ const result = await this.apolloClient.query({
1269
+ query,
1270
+ variables: {
1271
+ first: options?.first ?? 100,
1272
+ after: options?.after,
1273
+ filter: Object.keys(filter).length > 0 ? filter : undefined,
1274
+ orderBy: ['UPDATED_AT_CHECKPOINT_DESC']
1275
+ },
1276
+ fetchPolicy: 'network-only'
1277
+ });
1278
+
1279
+ if (result.error) throw result.error;
1280
+ return (
1281
+ (result.data as any)?.dubheSceneStorages ?? {
1282
+ edges: [],
1283
+ pageInfo: { hasNextPage: false, hasPreviousPage: false },
1284
+ totalCount: 0
1285
+ }
1286
+ );
1287
+ }
1288
+
1289
+ /**
1290
+ * Query raw field rows of SceneStorages (scene_storage_fields table,
1291
+ * exposed as dubheSceneStorageFields in GraphQL). Values are hex-encoded
1292
+ * BCS — decode with the decoders exported from this package.
1293
+ */
1294
+ async getSceneStorageFields(options?: {
1295
+ dappKey?: string;
1296
+ sceneIds?: string[];
1297
+ sceneId?: string;
1298
+ fieldName?: string;
1299
+ isDeleted?: boolean;
1300
+ first?: number;
1301
+ after?: string;
1302
+ }): Promise<Connection<SceneStorageFieldRow>> {
1303
+ const filter: Record<string, any> = {};
1304
+ if (options?.dappKey) filter.dappKey = { equalTo: options.dappKey };
1305
+ if (options?.sceneIds && options.sceneIds.length > 0) filter.sceneId = { in: options.sceneIds };
1306
+ if (options?.sceneId) filter.sceneId = { equalTo: options.sceneId };
1307
+ if (options?.fieldName) filter.fieldName = { equalTo: options.fieldName };
1308
+ if (options?.isDeleted !== undefined) filter.isDeleted = { equalTo: options.isDeleted };
1309
+
1310
+ const query = gql`
1311
+ query GetSceneStorageFields(
1312
+ $first: Int
1313
+ $after: Cursor
1314
+ $filter: StoreDubheSceneStorageFieldFilter
1315
+ $orderBy: [StoreDubheSceneStorageFieldsOrderBy!]
1316
+ ) {
1317
+ dubheSceneStorageFields(first: $first, after: $after, filter: $filter, orderBy: $orderBy) {
1318
+ totalCount
1319
+ pageInfo {
1320
+ hasNextPage
1321
+ hasPreviousPage
1322
+ startCursor
1323
+ endCursor
1324
+ }
1325
+ edges {
1326
+ cursor
1327
+ node {
1328
+ sceneId
1329
+ dappKey
1330
+ sceneType
1331
+ fieldName
1332
+ fieldValueRaw
1333
+ isDeleted
1334
+ updatedAtCheckpoint
1335
+ lastUpdateDigest
1336
+ lastEventSeq
1337
+ }
1338
+ }
1339
+ }
1340
+ }
1341
+ `;
1342
+
1343
+ const result = await this.apolloClient.query({
1344
+ query,
1345
+ variables: {
1346
+ first: options?.first ?? 1000,
1347
+ after: options?.after,
1348
+ filter: Object.keys(filter).length > 0 ? filter : undefined,
1349
+ orderBy: ['UPDATED_AT_CHECKPOINT_DESC']
1350
+ },
1351
+ fetchPolicy: 'network-only'
1352
+ });
1353
+
1354
+ if (result.error) throw result.error;
1355
+ return (
1356
+ (result.data as any)?.dubheSceneStorageFields ?? {
1357
+ edges: [],
1358
+ pageInfo: { hasNextPage: false, hasPreviousPage: false },
1359
+ totalCount: 0
1360
+ }
1361
+ );
1362
+ }
1363
+
1364
+ /**
1365
+ * Query ObjectStorage system rows indexed by the Dubhe indexer
1366
+ * (object_storages table, exposed as dubheObjectStorages in GraphQL).
1367
+ */
1368
+ async getObjectStorages(options?: {
1369
+ dappKey?: string;
1370
+ objectType?: string;
1371
+ objectId?: string;
1372
+ isDestroyed?: boolean;
1373
+ first?: number;
1374
+ after?: string;
1375
+ }): Promise<Connection<ObjectStorageRow>> {
1376
+ const filter: Record<string, any> = {};
1377
+ if (options?.dappKey) filter.dappKey = { equalTo: options.dappKey };
1378
+ if (options?.objectType) filter.objectType = { equalTo: options.objectType };
1379
+ if (options?.objectId) filter.objectId = { equalTo: options.objectId };
1380
+ if (options?.isDestroyed !== undefined) filter.isDestroyed = { equalTo: options.isDestroyed };
1381
+
1382
+ const query = gql`
1383
+ query GetObjectStorages(
1384
+ $first: Int
1385
+ $after: Cursor
1386
+ $filter: StoreDubheObjectStorageFilter
1387
+ $orderBy: [StoreDubheObjectStoragesOrderBy!]
1388
+ ) {
1389
+ dubheObjectStorages(first: $first, after: $after, filter: $filter, orderBy: $orderBy) {
1390
+ totalCount
1391
+ pageInfo {
1392
+ hasNextPage
1393
+ hasPreviousPage
1394
+ startCursor
1395
+ endCursor
1396
+ }
1397
+ edges {
1398
+ cursor
1399
+ node {
1400
+ objectId
1401
+ dappKey
1402
+ objectType
1403
+ entityIdRaw
1404
+ isDestroyed
1405
+ createdAtCheckpoint
1406
+ updatedAtCheckpoint
1407
+ lastUpdateDigest
1408
+ lastEventSeq
1409
+ }
1410
+ }
1411
+ }
1412
+ }
1413
+ `;
1414
+
1415
+ const result = await this.apolloClient.query({
1416
+ query,
1417
+ variables: {
1418
+ first: options?.first ?? 100,
1419
+ after: options?.after,
1420
+ filter: Object.keys(filter).length > 0 ? filter : undefined,
1421
+ orderBy: ['UPDATED_AT_CHECKPOINT_DESC']
1422
+ },
1423
+ fetchPolicy: 'network-only'
1424
+ });
1425
+
1426
+ if (result.error) throw result.error;
1427
+ return (
1428
+ (result.data as any)?.dubheObjectStorages ?? {
1429
+ edges: [],
1430
+ pageInfo: { hasNextPage: false, hasPreviousPage: false },
1431
+ totalCount: 0
1432
+ }
1433
+ );
1434
+ }
1435
+
1436
+ /**
1437
+ * Query DApp runtime state (credit pool, admin, package version, etc.).
1438
+ * Exposed as dubheDappRuntimeStates in GraphQL.
1439
+ */
1440
+ async getDubheDappRuntimeState(dappKey: string): Promise<DubheDappRuntimeStateRow | null> {
1441
+ const query = gql`
1442
+ query GetDubheDappRuntimeState($filter: StoreDubheDappRuntimeStateFilter) {
1443
+ dubheDappRuntimeStates(first: 1, filter: $filter) {
1444
+ edges {
1445
+ node {
1446
+ dappKey
1447
+ admin
1448
+ dappStorageId
1449
+ packageId
1450
+ version
1451
+ creditPool
1452
+ paused
1453
+ settlementMode
1454
+ createdAt
1455
+ createdAtCheckpoint
1456
+ updatedAtCheckpoint
1457
+ lastUpdateDigest
1458
+ lastEventSeq
1459
+ }
1460
+ }
1461
+ }
1462
+ }
1463
+ `;
1464
+
1465
+ const result = await this.apolloClient.query({
1466
+ query,
1467
+ variables: { filter: { dappKey: { equalTo: dappKey } } },
1468
+ fetchPolicy: 'network-only'
1469
+ });
1470
+
1471
+ if (result.error) throw result.error;
1472
+ const edges = (result.data as any)?.dubheDappRuntimeStates?.edges ?? [];
1473
+ return edges[0]?.node ?? null;
1474
+ }
1475
+
886
1476
  // Improved table name handling methods
887
1477
  private getFilterTypeName(tableName: string): string {
888
1478
  // Convert to singular form and apply PascalCase conversion
@@ -1235,12 +1825,24 @@ export class DubheGraphqlClient {
1235
1825
  if (customFields && customFields.length > 0) {
1236
1826
  fields = customFields;
1237
1827
  } else {
1238
- // Try to get fields from dubhe configuration
1239
- const autoFields = this.getTableFields(tableName);
1240
- if (autoFields.length > 0) {
1241
- fields = autoFields;
1828
+ // 1. Check system table registry first
1829
+ const systemFields = SYSTEM_TABLE_FIELDS[tableName];
1830
+ if (systemFields) {
1831
+ fields = systemFields;
1242
1832
  } else {
1243
- fields = ['createdAtTimestampMs', 'updatedAtTimestampMs', 'isDeleted', 'lastUpdateDigest'];
1833
+ // 2. Try to get fields from dubhe configuration (DApp store tables)
1834
+ const autoFields = this.getTableFields(tableName);
1835
+ if (autoFields.length > 0) {
1836
+ fields = autoFields;
1837
+ } else {
1838
+ // 3. Generic fallback for store_* tables
1839
+ fields = [
1840
+ 'createdAtTimestampMs',
1841
+ 'updatedAtTimestampMs',
1842
+ 'isDeleted',
1843
+ 'lastUpdateDigest'
1844
+ ];
1845
+ }
1244
1846
  }
1245
1847
  }
1246
1848
 
@@ -1248,6 +1850,74 @@ export class DubheGraphqlClient {
1248
1850
 
1249
1851
  return fields.join('\n ');
1250
1852
  }
1853
+
1854
+ // ── Typed system-table query methods ─────────────────────────────────────
1855
+
1856
+ /** Latest DApp fee state snapshot (credit_pool, total_settled, fee rates). */
1857
+ async getDappFeeState(): Promise<{
1858
+ entityId: string;
1859
+ baseFeePerWrite: string;
1860
+ bytesFeePerByte: string;
1861
+ freeCredit: string;
1862
+ creditPool: string;
1863
+ totalSettled: string;
1864
+ updatedAtTimestampMs: string;
1865
+ } | null> {
1866
+ const result = await this.getAllTables<any>('dubheDappFeeState', { first: 1 }).catch(
1867
+ () => null
1868
+ );
1869
+ return result?.edges?.[0]?.node ?? null;
1870
+ }
1871
+
1872
+ /** Latest DApp revenue balance (USER_PAYS mode collected revenue). */
1873
+ async getDappRevenueState(): Promise<{
1874
+ entityId: string;
1875
+ dappRevenue: string;
1876
+ coinType: string;
1877
+ updatedAtTimestampMs: string;
1878
+ } | null> {
1879
+ const result = await this.getAllTables<any>('dubheDappRevenueState', { first: 1 }).catch(
1880
+ () => null
1881
+ );
1882
+ return result?.edges?.[0]?.node ?? null;
1883
+ }
1884
+
1885
+ /** Latest DApp runtime state (admin, settlement mode, last event). */
1886
+ async getDappRuntimeState(): Promise<{
1887
+ dappKey: string;
1888
+ admin: string;
1889
+ paused: boolean;
1890
+ settlementMode: number;
1891
+ creditPool: string;
1892
+ writeFeeShareBps: number;
1893
+ lastRuntimeEvent: string;
1894
+ lastRuntimeActor: string;
1895
+ lastRuntimeAmount: string;
1896
+ } | null> {
1897
+ const result = await this.getAllTables<any>('dubheDappRuntimeState', { first: 1 }).catch(
1898
+ () => null
1899
+ );
1900
+ return result?.edges?.[0]?.node ?? null;
1901
+ }
1902
+
1903
+ /** Marketplace fee records (one row per listing sold). */
1904
+ async getDappMarketplaceFees(options?: { first?: number; after?: string }): Promise<
1905
+ Connection<{
1906
+ dappKey: string;
1907
+ listingId: string;
1908
+ coinType: string;
1909
+ totalFee: string;
1910
+ treasuryAmount: string;
1911
+ dappAmount: string;
1912
+ updatedAtCheckpoint: string;
1913
+ }>
1914
+ > {
1915
+ return this.getAllTables<any>('dubheDappMarketplaceFees', {
1916
+ first: options?.first ?? 20,
1917
+ after: options?.after,
1918
+ orderBy: [{ field: 'updatedAtCheckpoint', direction: 'DESC' }]
1919
+ });
1920
+ }
1251
1921
  }
1252
1922
 
1253
1923
  // Export convenience function