@0xobelisk/ecs 1.2.0-pre.44 → 1.2.0-pre.46

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/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- export type { EntityId, ComponentType, Unsubscribe, ComponentCallback, ComponentChangeCallback, EntityCallback, QueryChange, QueryChangeCallback, QueryWatcher, PagedResult, BatchQueryResult, ComponentChangeEvent, EntityChangeEvent, QueryOptions, SubscriptionOptions, ComponentMetadata, ComponentField, ComponentDiscoveryResult, ResourceMetadata, ResourceDiscoveryResult, ECSWorldConfig, DubheMetadata, } from './types';
1
+ export type { EntityId, ComponentType, Unsubscribe, ComponentCallback, ComponentChangeCallback, EntityCallback, QueryChange, QueryChangeCallback, QueryWatcher, PagedResult, PagedQueryResult, BatchQueryResult, ComponentChangeEvent, EntityChangeEvent, QueryOptions, SubscriptionOptions, ComponentMetadata, ComponentField, ComponentDiscoveryResult, ResourceMetadata, ResourceDiscoveryResult, ECSWorldConfig, DubheMetadata, } from './types';
2
2
  export { DubheECSWorld } from './world';
3
3
  export { ComponentDiscoverer, ResourceDiscoverer } from './world';
4
4
  export { ECSQuery } from './query';
5
5
  export { ECSSubscription } from './subscription';
6
6
  export { createECSWorld } from './world';
7
- export { extractEntityIds, calculateDelta, findEntityIntersection, findEntityUnion, extractIntersectionFromBatchResult, extractUnionFromBatchResult, debounce, normalizeComponentType, createCacheKey, isValidEntityId, isValidComponentType, deepEqual, safeJsonParse, formatError, createTimestamp, limitArray, paginateArray, } from './utils';
7
+ export { extractEntityIds, extractPagedQueryResult, calculateDelta, findEntityIntersection, findEntityUnion, extractIntersectionFromBatchResult, extractUnionFromBatchResult, debounce, normalizeComponentType, createCacheKey, isValidEntityId, isValidComponentType, deepEqual, safeJsonParse, formatError, createTimestamp, limitArray, paginateArray, } from './utils';
8
8
  export { DubheECSWorld as default } from './world';
package/dist/index.js CHANGED
@@ -44,6 +44,7 @@ __export(src_exports, {
44
44
  default: () => DubheECSWorld,
45
45
  extractEntityIds: () => extractEntityIds,
46
46
  extractIntersectionFromBatchResult: () => extractIntersectionFromBatchResult,
47
+ extractPagedQueryResult: () => extractPagedQueryResult,
47
48
  extractUnionFromBatchResult: () => extractUnionFromBatchResult,
48
49
  findEntityIntersection: () => findEntityIntersection,
49
50
  findEntityUnion: () => findEntityUnion,
@@ -75,6 +76,21 @@ function extractEntityIds(connection, options) {
75
76
  }
76
77
  }).filter(Boolean);
77
78
  }
79
+ function extractPagedQueryResult(connection, options) {
80
+ const entityIds = extractEntityIds(connection, options);
81
+ const items = connection.edges.map((edge) => edge.node);
82
+ return {
83
+ entityIds,
84
+ items,
85
+ pageInfo: {
86
+ hasNextPage: connection.pageInfo.hasNextPage,
87
+ hasPreviousPage: connection.pageInfo.hasPreviousPage,
88
+ startCursor: connection.pageInfo.startCursor,
89
+ endCursor: connection.pageInfo.endCursor
90
+ },
91
+ totalCount: connection.totalCount || 0
92
+ };
93
+ }
78
94
  function calculateDelta(oldResults, newResults) {
79
95
  const oldSet = new Set(oldResults);
80
96
  const newSet = new Set(newResults);
@@ -243,6 +259,29 @@ var ECSQuery = class {
243
259
  setComponentDiscoverer(discoverer) {
244
260
  this.componentDiscoverer = discoverer;
245
261
  }
262
+ buildPaginationParams(options) {
263
+ const params = {};
264
+ if (options?.first !== void 0) {
265
+ params.first = options.first;
266
+ } else if (options?.limit !== void 0) {
267
+ params.first = options.limit;
268
+ if (options?.offset !== void 0) {
269
+ console.warn(
270
+ "ECS Query: offset parameter is not supported with GraphQL cursor-based pagination. Use after/before instead."
271
+ );
272
+ }
273
+ }
274
+ if (options?.last !== void 0) {
275
+ params.last = options.last;
276
+ }
277
+ if (options?.after !== void 0) {
278
+ params.after = options.after;
279
+ }
280
+ if (options?.before !== void 0) {
281
+ params.before = options.before;
282
+ }
283
+ return params;
284
+ }
246
285
  /**
247
286
  * Get component field information
248
287
  */
@@ -453,47 +492,60 @@ var ECSQuery = class {
453
492
  return validComponents;
454
493
  }
455
494
  /**
456
- * Query all entities that have a specific component
495
+ * Query all entities that have a specific component with full pagination info
457
496
  */
458
- async queryWith(componentType, options) {
497
+ async queryWithFullPagination(componentType, options) {
498
+ const emptyResult = {
499
+ entityIds: [],
500
+ items: [],
501
+ pageInfo: {
502
+ hasNextPage: false,
503
+ hasPreviousPage: false
504
+ },
505
+ totalCount: 0
506
+ };
459
507
  if (!isValidComponentType(componentType))
460
- return [];
508
+ return emptyResult;
461
509
  if (!this.isECSComponent(componentType)) {
462
- return [];
510
+ return emptyResult;
463
511
  }
464
- const cacheKey = createCacheKey("queryWith", [componentType], options);
465
- const cached = this.getCachedResult(cacheKey);
466
- if (cached && options?.cache !== false)
467
- return cached;
468
512
  try {
469
513
  const queryFields = await this.getQueryFields(
470
514
  componentType,
471
515
  options?.fields
472
516
  );
473
517
  const primaryKeys = await this.getComponentPrimaryKeys(componentType);
518
+ const paginationParams = this.buildPaginationParams(options);
474
519
  const connection = await this.graphqlClient.getAllTables(componentType, {
475
- first: options?.limit,
520
+ ...paginationParams,
476
521
  fields: queryFields,
477
522
  orderBy: options?.orderBy
478
523
  });
479
- const result = extractEntityIds(connection, {
524
+ return extractPagedQueryResult(connection, {
480
525
  idFields: options?.idFields || primaryKeys,
481
526
  composite: options?.compositeId
482
527
  });
483
- this.setCachedResult(cacheKey, result);
484
- return result;
485
528
  } catch (error) {
486
- return [];
529
+ return emptyResult;
487
530
  }
488
531
  }
532
+ /**
533
+ * Query all entities that have a specific component
534
+ * Returns complete pagination information including entity IDs, actual data, and page info
535
+ */
536
+ async queryWith(componentType, options) {
537
+ return this.queryWithFullPagination(componentType, options);
538
+ }
489
539
  /**
490
540
  * Query entities that have all specified components (intersection)
491
541
  */
492
542
  async queryWithAll(componentTypes, options) {
493
543
  if (componentTypes.length === 0)
494
544
  return [];
495
- if (componentTypes.length === 1)
496
- return this.queryWith(componentTypes[0], options);
545
+ if (componentTypes.length === 1) {
546
+ const result = await this.queryWith(componentTypes[0], options);
547
+ return result.entityIds;
548
+ }
497
549
  const validTypes = this.filterValidECSComponents(componentTypes);
498
550
  if (validTypes.length === 0)
499
551
  return [];
@@ -502,6 +554,7 @@ var ECSQuery = class {
502
554
  if (cached && options?.cache !== false)
503
555
  return cached;
504
556
  try {
557
+ const paginationParams = this.buildPaginationParams(options);
505
558
  const queries = await Promise.all(
506
559
  validTypes.map(async (type) => {
507
560
  const queryFields = await this.getQueryFields(type, options?.fields);
@@ -510,7 +563,7 @@ var ECSQuery = class {
510
563
  tableName: type,
511
564
  params: {
512
565
  fields: queryFields,
513
- first: options?.limit,
566
+ ...paginationParams,
514
567
  orderBy: options?.orderBy
515
568
  }
516
569
  };
@@ -544,8 +597,10 @@ var ECSQuery = class {
544
597
  async queryWithAny(componentTypes, options) {
545
598
  if (componentTypes.length === 0)
546
599
  return [];
547
- if (componentTypes.length === 1)
548
- return this.queryWith(componentTypes[0], options);
600
+ if (componentTypes.length === 1) {
601
+ const result = await this.queryWith(componentTypes[0], options);
602
+ return result.entityIds;
603
+ }
549
604
  const validTypes = this.filterValidECSComponents(componentTypes);
550
605
  if (validTypes.length === 0)
551
606
  return [];
@@ -554,6 +609,7 @@ var ECSQuery = class {
554
609
  if (cached && options?.cache !== false)
555
610
  return cached;
556
611
  try {
612
+ const paginationParams = this.buildPaginationParams(options);
557
613
  const queries = await Promise.all(
558
614
  validTypes.map(async (type) => {
559
615
  const queryFields = await this.getQueryFields(type, options?.fields);
@@ -562,7 +618,7 @@ var ECSQuery = class {
562
618
  tableName: type,
563
619
  params: {
564
620
  fields: queryFields,
565
- first: options?.limit,
621
+ ...paginationParams,
566
622
  orderBy: options?.orderBy
567
623
  }
568
624
  };
@@ -611,13 +667,22 @@ var ECSQuery = class {
611
667
  }
612
668
  }
613
669
  /**
614
- * Query components based on conditions
670
+ * Query components based on conditions with full pagination info
615
671
  */
616
- async queryWhere(componentType, predicate, options) {
672
+ async queryWhereFullPagination(componentType, predicate, options) {
673
+ const emptyResult = {
674
+ entityIds: [],
675
+ items: [],
676
+ pageInfo: {
677
+ hasNextPage: false,
678
+ hasPreviousPage: false
679
+ },
680
+ totalCount: 0
681
+ };
617
682
  if (!isValidComponentType(componentType))
618
- return [];
683
+ return emptyResult;
619
684
  if (!this.isECSComponent(componentType)) {
620
- return [];
685
+ return emptyResult;
621
686
  }
622
687
  try {
623
688
  const queryFields = await this.getQueryFields(
@@ -625,20 +690,32 @@ var ECSQuery = class {
625
690
  options?.fields
626
691
  );
627
692
  const primaryKeys = await this.getComponentPrimaryKeys(componentType);
693
+ const paginationParams = this.buildPaginationParams(options);
628
694
  const connection = await this.graphqlClient.getAllTables(componentType, {
629
695
  filter: predicate,
630
- first: options?.limit,
696
+ ...paginationParams,
631
697
  fields: queryFields,
632
698
  orderBy: options?.orderBy
633
699
  });
634
- return extractEntityIds(connection, {
700
+ return extractPagedQueryResult(connection, {
635
701
  idFields: options?.idFields || primaryKeys,
636
702
  composite: options?.compositeId
637
703
  });
638
704
  } catch (error) {
639
- return [];
705
+ return emptyResult;
640
706
  }
641
707
  }
708
+ /**
709
+ * Query components based on conditions
710
+ */
711
+ async queryWhere(componentType, predicate, options) {
712
+ const result = await this.queryWhereFullPagination(
713
+ componentType,
714
+ predicate,
715
+ options
716
+ );
717
+ return result.entityIds;
718
+ }
642
719
  /**
643
720
  * Range query
644
721
  */
@@ -661,7 +738,7 @@ var ECSQuery = class {
661
738
  */
662
739
  async queryPaged(componentTypes, page, pageSize) {
663
740
  try {
664
- const allResults = componentTypes.length === 1 ? await this.queryWith(componentTypes[0]) : await this.queryWithAll(componentTypes);
741
+ const allResults = componentTypes.length === 1 ? (await this.queryWith(componentTypes[0])).entityIds : await this.queryWithAll(componentTypes);
665
742
  return paginateArray(allResults, page, pageSize);
666
743
  } catch (error) {
667
744
  return {
@@ -1989,11 +2066,27 @@ var DubheECSWorld = class {
1989
2066
  return this.querySystem.queryRange(componentType, field, min, max, options);
1990
2067
  }
1991
2068
  /**
1992
- * Paginated query
2069
+ * Paginated query (legacy version using page/pageSize)
1993
2070
  */
1994
2071
  async queryPaged(componentTypes, page, pageSize) {
1995
2072
  return this.querySystem.queryPaged(componentTypes, page, pageSize);
1996
2073
  }
2074
+ /**
2075
+ * Query with complete pagination info using GraphQL cursor-based pagination
2076
+ */
2077
+ async queryWithPagination(componentType, options) {
2078
+ return this.querySystem.queryWith(componentType, options);
2079
+ }
2080
+ /**
2081
+ * Query components with conditions and complete pagination info
2082
+ */
2083
+ async queryWherePagination(componentType, predicate, options) {
2084
+ return this.querySystem.queryWhereFullPagination(
2085
+ componentType,
2086
+ predicate,
2087
+ options
2088
+ );
2089
+ }
1997
2090
  // ============ Query Builder ============
1998
2091
  /**
1999
2092
  * Create query builder
@@ -2057,9 +2150,9 @@ var DubheECSWorld = class {
2057
2150
  */
2058
2151
  async queryWithComponentData(componentType, options) {
2059
2152
  try {
2060
- const entityIds = await this.queryWith(componentType, options);
2153
+ const data = await this.queryWith(componentType, options);
2061
2154
  const results = [];
2062
- for (const entityId of entityIds) {
2155
+ for (const entityId of data.entityIds) {
2063
2156
  const componentData = await this.getComponent(
2064
2157
  entityId,
2065
2158
  componentType
@@ -2129,7 +2222,7 @@ var DubheECSWorld = class {
2129
2222
  componentTypes.map(async (componentType) => {
2130
2223
  try {
2131
2224
  const entities = await this.queryWith(componentType);
2132
- stats[componentType] = entities.length;
2225
+ stats[componentType] = entities.totalCount;
2133
2226
  } catch (error) {
2134
2227
  stats[componentType] = 0;
2135
2228
  }
@@ -2224,11 +2317,17 @@ var DubheECSWorld = class {
2224
2317
  for (const [key, value] of Object.entries(keyValues)) {
2225
2318
  whereConditions[key] = { equalTo: value };
2226
2319
  }
2320
+ const paginationParams = {
2321
+ first: options?.first ?? options?.limit ?? 1,
2322
+ last: options?.last,
2323
+ after: options?.after,
2324
+ before: options?.before
2325
+ };
2227
2326
  const result = await this.graphqlClient.getAllTables(resourceType, {
2228
- first: 1,
2327
+ ...paginationParams,
2229
2328
  filter: whereConditions,
2230
2329
  fields: options?.fields || resourceMetadata.fields.map((f) => f.name),
2231
- ...options
2330
+ orderBy: options?.orderBy
2232
2331
  });
2233
2332
  const record = result.edges[0]?.node;
2234
2333
  return record ? record : null;
@@ -2237,17 +2336,26 @@ var DubheECSWorld = class {
2237
2336
  }
2238
2337
  }
2239
2338
  /**
2240
- * Query multiple resources
2339
+ * Query multiple resources with complete pagination info
2241
2340
  */
2242
- async getResources(resourceType, filters, options) {
2341
+ async getResources(resourceType, options) {
2243
2342
  try {
2244
2343
  const resourceMetadata = this.resourceDiscoverer.getResourceMetadata(resourceType);
2245
2344
  if (!resourceMetadata) {
2246
- return [];
2345
+ return {
2346
+ entityIds: [],
2347
+ // Resources don't have entityIds, but include for compatibility
2348
+ items: [],
2349
+ pageInfo: {
2350
+ hasNextPage: false,
2351
+ hasPreviousPage: false
2352
+ },
2353
+ totalCount: 0
2354
+ };
2247
2355
  }
2248
2356
  const whereConditions = {};
2249
- if (filters) {
2250
- for (const [key, value] of Object.entries(filters)) {
2357
+ if (options?.filters) {
2358
+ for (const [key, value] of Object.entries(options.filters)) {
2251
2359
  if (typeof value === "object" && value !== null) {
2252
2360
  whereConditions[key] = value;
2253
2361
  } else {
@@ -2255,23 +2363,44 @@ var DubheECSWorld = class {
2255
2363
  }
2256
2364
  }
2257
2365
  }
2366
+ const paginationParams = {
2367
+ first: options?.first ?? options?.limit,
2368
+ last: options?.last,
2369
+ after: options?.after,
2370
+ before: options?.before
2371
+ };
2258
2372
  const result = await this.graphqlClient.getAllTables(resourceType, {
2373
+ ...paginationParams,
2259
2374
  filter: Object.keys(whereConditions).length > 0 ? whereConditions : void 0,
2260
2375
  fields: options?.fields || resourceMetadata.fields.map((f) => f.name),
2261
- ...options
2376
+ orderBy: options?.orderBy
2262
2377
  });
2263
- const records = result.edges.map((edge) => edge.node);
2264
- return records;
2378
+ const items = result.edges.map((edge) => edge.node);
2379
+ return {
2380
+ entityIds: [],
2381
+ // Resources don't have entityIds, but include for compatibility
2382
+ items,
2383
+ pageInfo: {
2384
+ hasNextPage: result.pageInfo.hasNextPage,
2385
+ hasPreviousPage: result.pageInfo.hasPreviousPage,
2386
+ startCursor: result.pageInfo.startCursor,
2387
+ endCursor: result.pageInfo.endCursor
2388
+ },
2389
+ totalCount: result.totalCount || 0
2390
+ };
2265
2391
  } catch (error) {
2266
- return [];
2392
+ return {
2393
+ entityIds: [],
2394
+ // Resources don't have entityIds, but include for compatibility
2395
+ items: [],
2396
+ pageInfo: {
2397
+ hasNextPage: false,
2398
+ hasPreviousPage: false
2399
+ },
2400
+ totalCount: 0
2401
+ };
2267
2402
  }
2268
2403
  }
2269
- /**
2270
- * Get all resources of a specific type
2271
- */
2272
- async getAllResources(resourceType, options) {
2273
- return this.getResources(resourceType, void 0, options);
2274
- }
2275
2404
  /**
2276
2405
  * Check if a resource exists
2277
2406
  */
@@ -2332,6 +2461,7 @@ function createECSWorld(graphqlClient, config) {
2332
2461
  deepEqual,
2333
2462
  extractEntityIds,
2334
2463
  extractIntersectionFromBatchResult,
2464
+ extractPagedQueryResult,
2335
2465
  extractUnionFromBatchResult,
2336
2466
  findEntityIntersection,
2337
2467
  findEntityUnion,