koota 0.1.12 → 0.2.1

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/README.md CHANGED
@@ -283,6 +283,15 @@ const movedEntities = world.query(Changed(Position));
283
283
  // After running the query, the Changed modifier is reset
284
284
  ```
285
285
 
286
+ ### Query all entities
287
+
288
+ To get al queryable entities you simply query with not paramerters. Note, that not all entities are queryable. Any entity that has `IsExcluded` will not be able to be queried. This is used in Koota to exclude world entities, for example, but maybe used for other system level entities in the future. To get all entities regardless, use `world.entities`.
289
+
290
+ ```js
291
+ // Returns all queryable entities
292
+ const allQueryableEntities = world.query()
293
+ ```
294
+
286
295
  ### Add, remove and change events
287
296
 
288
297
  Koota allows you to subscribe to add, remove, and change events for specific traits.
@@ -309,7 +318,7 @@ entity.set(Position, { x: 10, y: 20 });
309
318
  entity.remove(Position);
310
319
  ```
311
320
 
312
- ### Change detection with `udpateEach`
321
+ ### Change detection with `updateEach`
313
322
 
314
323
  By default, `updateEach` will automatically turn on change detection for traits that are being tracked via `onChange` or the `Changed` modifier. If you want to silence change detection for a loop or force it to always run, you can do so with an options config.
315
324
 
@@ -320,7 +329,7 @@ world.query(Position, Velocity).updateEach(([position, velocity]) => {
320
329
 
321
330
  // Setting changeDetection to 'always' will ignore selective tracking and always emit change events for all traits that are mutated
322
331
  world.query(Position, Velocity).updateEach(([position, velocity]) => {
323
- }, { changeDetection: 'never' });
332
+ }, { changeDetection: 'always' });
324
333
  ```
325
334
 
326
335
  ### World traits
@@ -427,7 +436,7 @@ const unsub = world.onAdd([Position], (entity) => {})
427
436
  const unsub = world.onRemove([Position], (entity) => {})
428
437
  const unsub = world.onChange([Position], (entity) => {})
429
438
 
430
- // An array of all entities alive in the world
439
+ // An array of all entities alive in the world, including non-queryable entities
431
440
  // This is a copy so editing it won't do anything!
432
441
  // Entity[]
433
442
  world.entities
@@ -754,4 +763,4 @@ useEffect(() => {
754
763
  spawnPlayer()
755
764
  return () => destroyAllPlayers()
756
765
  }, [])
757
- ```
766
+ ```
@@ -330,7 +330,12 @@ function defineRelation(definition) {
330
330
  function relationFn(target) {
331
331
  if (target === void 0) throw Error("Relation target is undefined");
332
332
  if (target === "*") target = Wildcard;
333
- return getRelationTrait(relationFn, traitFactory, pairsMap, target);
333
+ return getRelationTrait(
334
+ relationFn,
335
+ traitFactory,
336
+ pairsMap,
337
+ target
338
+ );
334
339
  }
335
340
  return Object.assign(relationFn, {
336
341
  [$internal]: {
@@ -570,9 +575,7 @@ Number.prototype.set = function(trait2, value, triggerChanged = true) {
570
575
  const index = this & ENTITY_ID_MASK;
571
576
  const worldId = this >>> WORLD_ID_SHIFT;
572
577
  const store = ctx.stores[worldId];
573
- if (value instanceof Function) {
574
- value = value(ctx.get(index, store));
575
- }
578
+ value instanceof Function && (value = value(ctx.get(index, store)));
576
579
  ctx.set(index, store, value);
577
580
  triggerChanged && setChanged(universe.worlds[worldId], this, trait2);
578
581
  };
@@ -791,6 +794,7 @@ var createQueryHash = (parameters) => {
791
794
  var IsExcluded = trait();
792
795
  var Query = class {
793
796
  constructor(world, parameters = []) {
797
+ __publicField(this, "version", 0);
794
798
  __publicField(this, "world");
795
799
  __publicField(this, "parameters");
796
800
  __publicField(this, "hash");
@@ -876,6 +880,7 @@ var Query = class {
876
880
  this.traits.push(trait2);
877
881
  }
878
882
  }
883
+ this.traitData.forbidden.push(ctx.traitData.get(IsExcluded));
879
884
  this.traitData.all = [
880
885
  ...this.traitData.required,
881
886
  ...this.traitData.forbidden,
@@ -957,7 +962,6 @@ var Query = class {
957
962
  const entities = ctx.entityIndex.dense;
958
963
  for (let i = 0; i < entities.length; i++) {
959
964
  const entity = entities[i];
960
- if (entity.has(IsExcluded)) continue;
961
965
  const match = this.check(world, entity);
962
966
  if (match) this.add(entity);
963
967
  }
@@ -976,6 +980,7 @@ var Query = class {
976
980
  for (const sub of this.addSubscriptions) {
977
981
  sub(entity);
978
982
  }
983
+ this.version++;
979
984
  }
980
985
  remove(world, entity) {
981
986
  if (!this.entities.has(entity) || this.toRemove.has(entity)) return;
package/dist/index.cjs CHANGED
@@ -35,8 +35,8 @@ var __privateWrapper = (obj, member, setter, getter) => ({
35
35
  });
36
36
 
37
37
  // src/index.ts
38
- var src_exports = {};
39
- __export(src_exports, {
38
+ var index_exports = {};
39
+ __export(index_exports, {
40
40
  $internal: () => $internal,
41
41
  Not: () => Not,
42
42
  Or: () => Or,
@@ -52,7 +52,7 @@ __export(src_exports, {
52
52
  trait: () => trait,
53
53
  universe: () => universe
54
54
  });
55
- module.exports = __toCommonJS(src_exports);
55
+ module.exports = __toCommonJS(index_exports);
56
56
 
57
57
  // ../core/src/common.ts
58
58
  var $internal = Symbol("internal");
@@ -82,7 +82,12 @@ function defineRelation(definition) {
82
82
  function relationFn(target) {
83
83
  if (target === void 0) throw Error("Relation target is undefined");
84
84
  if (target === "*") target = Wildcard;
85
- return getRelationTrait(relationFn, traitFactory, pairsMap, target);
85
+ return getRelationTrait(
86
+ relationFn,
87
+ traitFactory,
88
+ pairsMap,
89
+ target
90
+ );
86
91
  }
87
92
  return Object.assign(relationFn, {
88
93
  [$internal]: {
@@ -607,9 +612,7 @@ Number.prototype.set = function(trait2, value, triggerChanged = true) {
607
612
  const index = this & ENTITY_ID_MASK;
608
613
  const worldId = this >>> WORLD_ID_SHIFT;
609
614
  const store = ctx.stores[worldId];
610
- if (value instanceof Function) {
611
- value = value(ctx.get(index, store));
612
- }
615
+ value instanceof Function && (value = value(ctx.get(index, store)));
613
616
  ctx.set(index, store, value);
614
617
  triggerChanged && setChanged(universe.worlds[worldId], this, trait2);
615
618
  };
@@ -769,6 +772,7 @@ var createQueryHash = (parameters) => {
769
772
  var IsExcluded = trait();
770
773
  var Query = class {
771
774
  constructor(world, parameters = []) {
775
+ __publicField(this, "version", 0);
772
776
  __publicField(this, "world");
773
777
  __publicField(this, "parameters");
774
778
  __publicField(this, "hash");
@@ -854,6 +858,7 @@ var Query = class {
854
858
  this.traits.push(trait2);
855
859
  }
856
860
  }
861
+ this.traitData.forbidden.push(ctx.traitData.get(IsExcluded));
857
862
  this.traitData.all = [
858
863
  ...this.traitData.required,
859
864
  ...this.traitData.forbidden,
@@ -935,7 +940,6 @@ var Query = class {
935
940
  const entities = ctx.entityIndex.dense;
936
941
  for (let i = 0; i < entities.length; i++) {
937
942
  const entity = entities[i];
938
- if (entity.has(IsExcluded)) continue;
939
943
  const match = this.check(world, entity);
940
944
  if (match) this.add(entity);
941
945
  }
@@ -954,6 +958,7 @@ var Query = class {
954
958
  for (const sub of this.addSubscriptions) {
955
959
  sub(entity);
956
960
  }
961
+ this.version++;
957
962
  }
958
963
  remove(world, entity) {
959
964
  if (!this.entities.has(entity) || this.toRemove.has(entity)) return;
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { S as Schema, T as Trait, N as Norm, M as ModifierData, W as World, Q as QueryParameter, R as Relation, a as RelationTarget, b as WildcardRelation } from './world-CIrtrsKj.cjs';
2
- export { $ as $internal, A as AoSFactory, C as ConfigurableTrait, l as Entity, j as ExtractIsTag, E as ExtractSchema, i as ExtractStore, k as ExtractStores, r as InstancesFromParameters, s as IsNotModifier, I as IsTag, m as QueryModifier, p as QueryResult, o as QueryResultOptions, n as QuerySubscriber, h as Store, q as StoresFromParameters, g as TraitInstance, f as TraitTuple, d as TraitType, e as TraitValue, c as createWorld } from './world-CIrtrsKj.cjs';
1
+ import { S as Schema, T as Trait, N as Norm, M as ModifierData, W as World, Q as QueryParameter, R as Relation, a as RelationTarget, b as WildcardRelation } from './world-DQXl4lI8.cjs';
2
+ export { $ as $internal, A as AoSFactory, C as ConfigurableTrait, k as Entity, j as ExtractIsTag, E as ExtractSchema, i as ExtractStore, q as InstancesFromParameters, r as IsNotModifier, I as IsTag, l as QueryModifier, o as QueryResult, n as QueryResultOptions, m as QuerySubscriber, h as Store, p as StoresFromParameters, g as TraitInstance, f as TraitTuple, d as TraitType, e as TraitValue, c as createWorld } from './world-DQXl4lI8.cjs';
3
3
 
4
4
  declare function defineTrait<S extends Schema = {}>(schema?: S): Trait<Norm<S>>;
5
5
  declare const trait: typeof defineTrait;
@@ -29,11 +29,11 @@ declare const universe: {
29
29
 
30
30
  declare function cacheQuery(...parameters: QueryParameter[]): string;
31
31
 
32
- declare function defineRelation<S extends Schema = any, T extends Trait = Trait<Schema>>(definition?: {
32
+ declare function defineRelation<S extends Schema = any>(definition?: {
33
33
  exclusive?: boolean;
34
34
  autoRemoveTarget?: boolean;
35
35
  store?: S;
36
- }): Relation<T>;
36
+ }): Relation<Trait<S>>;
37
37
  declare const relation: typeof defineRelation;
38
38
  declare const Pair: <T extends Trait>(relation: Relation<T>, target: RelationTarget) => T;
39
39
  declare const Wildcard: WildcardRelation;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { S as Schema, T as Trait, N as Norm, M as ModifierData, W as World, Q as QueryParameter, R as Relation, a as RelationTarget, b as WildcardRelation } from './world-CIrtrsKj.js';
2
- export { $ as $internal, A as AoSFactory, C as ConfigurableTrait, l as Entity, j as ExtractIsTag, E as ExtractSchema, i as ExtractStore, k as ExtractStores, r as InstancesFromParameters, s as IsNotModifier, I as IsTag, m as QueryModifier, p as QueryResult, o as QueryResultOptions, n as QuerySubscriber, h as Store, q as StoresFromParameters, g as TraitInstance, f as TraitTuple, d as TraitType, e as TraitValue, c as createWorld } from './world-CIrtrsKj.js';
1
+ import { S as Schema, T as Trait, N as Norm, M as ModifierData, W as World, Q as QueryParameter, R as Relation, a as RelationTarget, b as WildcardRelation } from './world-DQXl4lI8.js';
2
+ export { $ as $internal, A as AoSFactory, C as ConfigurableTrait, k as Entity, j as ExtractIsTag, E as ExtractSchema, i as ExtractStore, q as InstancesFromParameters, r as IsNotModifier, I as IsTag, l as QueryModifier, o as QueryResult, n as QueryResultOptions, m as QuerySubscriber, h as Store, p as StoresFromParameters, g as TraitInstance, f as TraitTuple, d as TraitType, e as TraitValue, c as createWorld } from './world-DQXl4lI8.js';
3
3
 
4
4
  declare function defineTrait<S extends Schema = {}>(schema?: S): Trait<Norm<S>>;
5
5
  declare const trait: typeof defineTrait;
@@ -29,11 +29,11 @@ declare const universe: {
29
29
 
30
30
  declare function cacheQuery(...parameters: QueryParameter[]): string;
31
31
 
32
- declare function defineRelation<S extends Schema = any, T extends Trait = Trait<Schema>>(definition?: {
32
+ declare function defineRelation<S extends Schema = any>(definition?: {
33
33
  exclusive?: boolean;
34
34
  autoRemoveTarget?: boolean;
35
35
  store?: S;
36
- }): Relation<T>;
36
+ }): Relation<Trait<S>>;
37
37
  declare const relation: typeof defineRelation;
38
38
  declare const Pair: <T extends Trait>(relation: Relation<T>, target: RelationTarget) => T;
39
39
  declare const Wildcard: WildcardRelation;
package/dist/index.js CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  relation,
14
14
  trait,
15
15
  universe
16
- } from "./chunk-FAWXK4QV.js";
16
+ } from "./chunk-4QEK6BHY.js";
17
17
  export {
18
18
  $internal,
19
19
  Not,
package/dist/react.cjs CHANGED
@@ -48,16 +48,6 @@ __export(react_exports, {
48
48
  });
49
49
  module.exports = __toCommonJS(react_exports);
50
50
 
51
- // ../react/src/hooks/use-query.ts
52
- var import_react3 = require("react");
53
-
54
- // ../react/src/world/use-world.ts
55
- var import_react2 = require("react");
56
-
57
- // ../react/src/world/world-context.ts
58
- var import_react = require("react");
59
- var WorldContext = (0, import_react.createContext)(null);
60
-
61
51
  // ../core/src/common.ts
62
52
  var $internal = Symbol("internal");
63
53
 
@@ -86,7 +76,12 @@ function defineRelation(definition) {
86
76
  function relationFn(target) {
87
77
  if (target === void 0) throw Error("Relation target is undefined");
88
78
  if (target === "*") target = Wildcard;
89
- return getRelationTrait(relationFn, traitFactory, pairsMap, target);
79
+ return getRelationTrait(
80
+ relationFn,
81
+ traitFactory,
82
+ pairsMap,
83
+ target
84
+ );
90
85
  }
91
86
  return Object.assign(relationFn, {
92
87
  [$internal]: {
@@ -599,9 +594,7 @@ Number.prototype.set = function(trait2, value, triggerChanged = true) {
599
594
  const index = this & ENTITY_ID_MASK;
600
595
  const worldId = this >>> WORLD_ID_SHIFT;
601
596
  const store = ctx.stores[worldId];
602
- if (value instanceof Function) {
603
- value = value(ctx.get(index, store));
604
- }
597
+ value instanceof Function && (value = value(ctx.get(index, store)));
605
598
  ctx.set(index, store, value);
606
599
  triggerChanged && setChanged(universe.worlds[worldId], this, trait2);
607
600
  };
@@ -761,6 +754,7 @@ var createQueryHash = (parameters) => {
761
754
  var IsExcluded = trait();
762
755
  var Query = class {
763
756
  constructor(world, parameters = []) {
757
+ __publicField(this, "version", 0);
764
758
  __publicField(this, "world");
765
759
  __publicField(this, "parameters");
766
760
  __publicField(this, "hash");
@@ -846,6 +840,7 @@ var Query = class {
846
840
  this.traits.push(trait2);
847
841
  }
848
842
  }
843
+ this.traitData.forbidden.push(ctx.traitData.get(IsExcluded));
849
844
  this.traitData.all = [
850
845
  ...this.traitData.required,
851
846
  ...this.traitData.forbidden,
@@ -927,7 +922,6 @@ var Query = class {
927
922
  const entities = ctx.entityIndex.dense;
928
923
  for (let i = 0; i < entities.length; i++) {
929
924
  const entity = entities[i];
930
- if (entity.has(IsExcluded)) continue;
931
925
  const match = this.check(world, entity);
932
926
  if (match) this.add(entity);
933
927
  }
@@ -946,6 +940,7 @@ var Query = class {
946
940
  for (const sub of this.addSubscriptions) {
947
941
  sub(entity);
948
942
  }
943
+ this.version++;
949
944
  }
950
945
  remove(world, entity) {
951
946
  if (!this.entities.has(entity) || this.toRemove.has(entity)) return;
@@ -1362,6 +1357,31 @@ function createWorld(...traits) {
1362
1357
  return new World(...traits);
1363
1358
  }
1364
1359
 
1360
+ // ../core/src/query/utils/cache-query.ts
1361
+ function cacheQuery(...parameters) {
1362
+ const hash = createQueryHash(parameters);
1363
+ for (const world of universe.worlds) {
1364
+ if (!world) continue;
1365
+ const ctx = world[$internal];
1366
+ if (!ctx.queriesHashMap.has(hash)) {
1367
+ const query = new Query(world, parameters);
1368
+ ctx.queriesHashMap.set(hash, query);
1369
+ }
1370
+ }
1371
+ universe.cachedQueries.set(hash, parameters);
1372
+ return hash;
1373
+ }
1374
+
1375
+ // ../react/src/hooks/use-query.ts
1376
+ var import_react3 = require("react");
1377
+
1378
+ // ../react/src/world/use-world.ts
1379
+ var import_react2 = require("react");
1380
+
1381
+ // ../react/src/world/world-context.ts
1382
+ var import_react = require("react");
1383
+ var WorldContext = (0, import_react.createContext)(null);
1384
+
1365
1385
  // ../react/src/world/default-world.ts
1366
1386
  var defaultWorld = createWorld();
1367
1387
  var getDefaultWorld = () => defaultWorld;
@@ -1374,34 +1394,29 @@ function useWorld() {
1374
1394
 
1375
1395
  // ../react/src/hooks/use-query.ts
1376
1396
  function useQuery(...parameters) {
1377
- const memoizedParameters = (0, import_react3.useMemo)(() => parameters, [parameters]);
1378
1397
  const world = useWorld();
1379
- const entities = (0, import_react3.useMemo)(() => world.query(...memoizedParameters), [world, memoizedParameters]);
1380
- const [, forceUpdate] = (0, import_react3.useReducer)((v) => v + 1, 0);
1381
- (0, import_react3.useEffect)(() => {
1382
- const mutableEntities = entities;
1383
- mutableEntities.length = 0;
1384
- mutableEntities.push(...world.query(...memoizedParameters));
1385
- forceUpdate();
1386
- }, [world]);
1398
+ const [hash, initialVersion] = (0, import_react3.useMemo)(() => {
1399
+ const hash2 = cacheQuery(...parameters);
1400
+ const query = world[$internal].queriesHashMap.get(hash2);
1401
+ return [hash2, query.version];
1402
+ }, [parameters]);
1403
+ const [entities, setEntities] = (0, import_react3.useState)(() => world.query(hash));
1387
1404
  (0, import_react3.useEffect)(() => {
1388
- const unsubAdd = world.onAdd(memoizedParameters, (entity) => {
1389
- const mutableEntities = entities;
1390
- mutableEntities.push(entity);
1391
- forceUpdate();
1405
+ const unsubAdd = world.onAdd(parameters, () => {
1406
+ setEntities(world.query(hash));
1392
1407
  });
1393
- const unsubRemove = world.onRemove(memoizedParameters, (entity) => {
1394
- const mutableEntities = entities;
1395
- const index = mutableEntities.indexOf(entity);
1396
- mutableEntities[index] = mutableEntities[mutableEntities.length - 1];
1397
- mutableEntities.pop();
1398
- forceUpdate();
1408
+ const unsubRemove = world.onRemove(parameters, () => {
1409
+ setEntities(world.query(hash));
1399
1410
  });
1411
+ const query = world[$internal].queriesHashMap.get(hash);
1412
+ if (query.version !== initialVersion) {
1413
+ setEntities(world.query(hash));
1414
+ }
1400
1415
  return () => {
1401
1416
  unsubAdd();
1402
1417
  unsubRemove();
1403
1418
  };
1404
- }, [world]);
1419
+ }, [world, hash]);
1405
1420
  return entities;
1406
1421
  }
1407
1422
 
package/dist/react.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { Q as QueryParameter, p as QueryResult, W as World, T as Trait, l as Entity, g as TraitInstance } from './world-CIrtrsKj.cjs';
1
+ import { Q as QueryParameter, o as QueryResult, W as World, T as Trait, k as Entity, g as TraitInstance } from './world-DQXl4lI8.cjs';
2
2
  import * as react_jsx_runtime from 'react/jsx-runtime';
3
3
 
4
4
  declare function useQuery<T extends QueryParameter[]>(...parameters: T): QueryResult<T>;
package/dist/react.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Q as QueryParameter, p as QueryResult, W as World, T as Trait, l as Entity, g as TraitInstance } from './world-CIrtrsKj.js';
1
+ import { Q as QueryParameter, o as QueryResult, W as World, T as Trait, k as Entity, g as TraitInstance } from './world-DQXl4lI8.js';
2
2
  import * as react_jsx_runtime from 'react/jsx-runtime';
3
3
 
4
4
  declare function useQuery<T extends QueryParameter[]>(...parameters: T): QueryResult<T>;
package/dist/react.js CHANGED
@@ -1,10 +1,11 @@
1
1
  import {
2
2
  $internal,
3
+ cacheQuery,
3
4
  createWorld
4
- } from "./chunk-FAWXK4QV.js";
5
+ } from "./chunk-4QEK6BHY.js";
5
6
 
6
7
  // ../react/src/hooks/use-query.ts
7
- import { useEffect, useMemo, useReducer } from "react";
8
+ import { useEffect, useMemo, useState } from "react";
8
9
 
9
10
  // ../react/src/world/use-world.ts
10
11
  import { useContext } from "react";
@@ -25,34 +26,29 @@ function useWorld() {
25
26
 
26
27
  // ../react/src/hooks/use-query.ts
27
28
  function useQuery(...parameters) {
28
- const memoizedParameters = useMemo(() => parameters, [parameters]);
29
29
  const world = useWorld();
30
- const entities = useMemo(() => world.query(...memoizedParameters), [world, memoizedParameters]);
31
- const [, forceUpdate] = useReducer((v) => v + 1, 0);
30
+ const [hash, initialVersion] = useMemo(() => {
31
+ const hash2 = cacheQuery(...parameters);
32
+ const query = world[$internal].queriesHashMap.get(hash2);
33
+ return [hash2, query.version];
34
+ }, [parameters]);
35
+ const [entities, setEntities] = useState(() => world.query(hash));
32
36
  useEffect(() => {
33
- const mutableEntities = entities;
34
- mutableEntities.length = 0;
35
- mutableEntities.push(...world.query(...memoizedParameters));
36
- forceUpdate();
37
- }, [world]);
38
- useEffect(() => {
39
- const unsubAdd = world.onAdd(memoizedParameters, (entity) => {
40
- const mutableEntities = entities;
41
- mutableEntities.push(entity);
42
- forceUpdate();
37
+ const unsubAdd = world.onAdd(parameters, () => {
38
+ setEntities(world.query(hash));
43
39
  });
44
- const unsubRemove = world.onRemove(memoizedParameters, (entity) => {
45
- const mutableEntities = entities;
46
- const index = mutableEntities.indexOf(entity);
47
- mutableEntities[index] = mutableEntities[mutableEntities.length - 1];
48
- mutableEntities.pop();
49
- forceUpdate();
40
+ const unsubRemove = world.onRemove(parameters, () => {
41
+ setEntities(world.query(hash));
50
42
  });
43
+ const query = world[$internal].queriesHashMap.get(hash);
44
+ if (query.version !== initialVersion) {
45
+ setEntities(world.query(hash));
46
+ }
51
47
  return () => {
52
48
  unsubAdd();
53
49
  unsubRemove();
54
50
  };
55
- }, [world]);
51
+ }, [world, hash]);
56
52
  return entities;
57
53
  }
58
54
 
@@ -69,7 +65,7 @@ function useActions(actions) {
69
65
  }
70
66
 
71
67
  // ../react/src/hooks/use-trait.ts
72
- import { useEffect as useEffect2, useMemo as useMemo2, useState } from "react";
68
+ import { useEffect as useEffect2, useMemo as useMemo2, useState as useState2 } from "react";
73
69
 
74
70
  // ../react/src/utils/is-world.ts
75
71
  function isWorld(target) {
@@ -83,7 +79,7 @@ function useTrait(target, trait) {
83
79
  () => target ? createSubscriptions(target, trait, contextWorld) : void 0,
84
80
  [target, trait, contextWorld]
85
81
  );
86
- const [value, setValue] = useState(() => {
82
+ const [value, setValue] = useState2(() => {
87
83
  return memo?.entity.has(trait) ? memo?.entity.get(trait) : void 0;
88
84
  });
89
85
  useEffect2(() => {
@@ -43,12 +43,9 @@ type Store<T extends Schema = any> = T extends AoSFactory ? ReturnType<T>[] : {
43
43
  type Norm<T extends Schema> = T extends AoSFactory ? () => ReturnType<T> extends number ? number : ReturnType<T> extends boolean ? boolean : ReturnType<T> extends string ? string : ReturnType<T> : {
44
44
  [K in keyof T]: T[K] extends boolean ? boolean : T[K];
45
45
  };
46
- type ExtractSchema<T extends Trait> = T extends Trait<infer S, any> ? S : never;
46
+ type ExtractSchema<T extends Trait | Relation<Trait>> = T extends Relation<infer R> ? ExtractSchema<R> : T extends Trait<infer S> ? S : never;
47
47
  type ExtractStore<T extends Trait> = T extends Trait<any, infer S> ? S : never;
48
48
  type ExtractIsTag<T extends Trait> = T extends Trait<any, any, infer Tag> ? Tag : false;
49
- type ExtractStores<T extends [Trait, ...Trait[]]> = T extends [infer C] ? C extends Trait<any, Store<any>> ? ExtractStore<C> : never : {
50
- [K in keyof T]: ExtractStore<T[K]>;
51
- };
52
49
  type IsTag<T extends Trait> = T extends Trait<any, any, infer Tag> ? Tag : false;
53
50
 
54
51
  type RelationTarget = Entity | '*' | WildcardRelation;
@@ -79,7 +76,7 @@ type Entity = number & {
79
76
  destroy: () => void;
80
77
  changed: (trait: Trait) => void;
81
78
  set: <T extends Trait>(trait: T, value: TraitValue<ExtractSchema<T>> | ((prev: TraitInstance<ExtractSchema<T>>) => TraitValue<ExtractSchema<T>>), flagChanged?: boolean) => void;
82
- get: <T extends Trait>(trait: T) => TraitInstance<ExtractSchema<T>> | undefined;
79
+ get: <T extends Trait | Relation<Trait>>(trait: T) => TraitInstance<ExtractSchema<T>> | undefined;
83
80
  targetFor: <T extends Trait>(relation: Relation<T>) => Entity | undefined;
84
81
  targetsFor: <T extends Trait>(relation: Relation<T>) => Entity[];
85
82
  id: () => number;
@@ -156,6 +153,7 @@ type InstancesFromParameters<T extends QueryParameter[]> = T extends [
156
153
  type IsNotModifier<T> = T extends ModifierData<any, infer TType> ? TType extends 'not' ? true : false : false;
157
154
 
158
155
  declare class Query {
156
+ version: number;
159
157
  world: World;
160
158
  parameters: QueryParameter[];
161
159
  hash: string;
@@ -244,4 +242,4 @@ declare class World {
244
242
  }
245
243
  declare function createWorld(...traits: ConfigurableTrait[]): World;
246
244
 
247
- export { $internal as $, type AoSFactory as A, type ConfigurableTrait as C, type ExtractSchema as E, type IsTag as I, ModifierData as M, type Norm as N, type QueryParameter as Q, type Relation as R, type Schema as S, type Trait as T, World as W, type RelationTarget as a, type WildcardRelation as b, createWorld as c, type TraitType as d, type TraitValue as e, type TraitTuple as f, type TraitInstance as g, type Store as h, type ExtractStore as i, type ExtractIsTag as j, type ExtractStores as k, type Entity as l, type QueryModifier as m, type QuerySubscriber as n, type QueryResultOptions as o, type QueryResult as p, type StoresFromParameters as q, type InstancesFromParameters as r, type IsNotModifier as s };
245
+ export { $internal as $, type AoSFactory as A, type ConfigurableTrait as C, type ExtractSchema as E, type IsTag as I, ModifierData as M, type Norm as N, type QueryParameter as Q, type Relation as R, type Schema as S, type Trait as T, World as W, type RelationTarget as a, type WildcardRelation as b, createWorld as c, type TraitType as d, type TraitValue as e, type TraitTuple as f, type TraitInstance as g, type Store as h, type ExtractStore as i, type ExtractIsTag as j, type Entity as k, type QueryModifier as l, type QuerySubscriber as m, type QueryResultOptions as n, type QueryResult as o, type StoresFromParameters as p, type InstancesFromParameters as q, type IsNotModifier as r };
@@ -43,12 +43,9 @@ type Store<T extends Schema = any> = T extends AoSFactory ? ReturnType<T>[] : {
43
43
  type Norm<T extends Schema> = T extends AoSFactory ? () => ReturnType<T> extends number ? number : ReturnType<T> extends boolean ? boolean : ReturnType<T> extends string ? string : ReturnType<T> : {
44
44
  [K in keyof T]: T[K] extends boolean ? boolean : T[K];
45
45
  };
46
- type ExtractSchema<T extends Trait> = T extends Trait<infer S, any> ? S : never;
46
+ type ExtractSchema<T extends Trait | Relation<Trait>> = T extends Relation<infer R> ? ExtractSchema<R> : T extends Trait<infer S> ? S : never;
47
47
  type ExtractStore<T extends Trait> = T extends Trait<any, infer S> ? S : never;
48
48
  type ExtractIsTag<T extends Trait> = T extends Trait<any, any, infer Tag> ? Tag : false;
49
- type ExtractStores<T extends [Trait, ...Trait[]]> = T extends [infer C] ? C extends Trait<any, Store<any>> ? ExtractStore<C> : never : {
50
- [K in keyof T]: ExtractStore<T[K]>;
51
- };
52
49
  type IsTag<T extends Trait> = T extends Trait<any, any, infer Tag> ? Tag : false;
53
50
 
54
51
  type RelationTarget = Entity | '*' | WildcardRelation;
@@ -79,7 +76,7 @@ type Entity = number & {
79
76
  destroy: () => void;
80
77
  changed: (trait: Trait) => void;
81
78
  set: <T extends Trait>(trait: T, value: TraitValue<ExtractSchema<T>> | ((prev: TraitInstance<ExtractSchema<T>>) => TraitValue<ExtractSchema<T>>), flagChanged?: boolean) => void;
82
- get: <T extends Trait>(trait: T) => TraitInstance<ExtractSchema<T>> | undefined;
79
+ get: <T extends Trait | Relation<Trait>>(trait: T) => TraitInstance<ExtractSchema<T>> | undefined;
83
80
  targetFor: <T extends Trait>(relation: Relation<T>) => Entity | undefined;
84
81
  targetsFor: <T extends Trait>(relation: Relation<T>) => Entity[];
85
82
  id: () => number;
@@ -156,6 +153,7 @@ type InstancesFromParameters<T extends QueryParameter[]> = T extends [
156
153
  type IsNotModifier<T> = T extends ModifierData<any, infer TType> ? TType extends 'not' ? true : false : false;
157
154
 
158
155
  declare class Query {
156
+ version: number;
159
157
  world: World;
160
158
  parameters: QueryParameter[];
161
159
  hash: string;
@@ -244,4 +242,4 @@ declare class World {
244
242
  }
245
243
  declare function createWorld(...traits: ConfigurableTrait[]): World;
246
244
 
247
- export { $internal as $, type AoSFactory as A, type ConfigurableTrait as C, type ExtractSchema as E, type IsTag as I, ModifierData as M, type Norm as N, type QueryParameter as Q, type Relation as R, type Schema as S, type Trait as T, World as W, type RelationTarget as a, type WildcardRelation as b, createWorld as c, type TraitType as d, type TraitValue as e, type TraitTuple as f, type TraitInstance as g, type Store as h, type ExtractStore as i, type ExtractIsTag as j, type ExtractStores as k, type Entity as l, type QueryModifier as m, type QuerySubscriber as n, type QueryResultOptions as o, type QueryResult as p, type StoresFromParameters as q, type InstancesFromParameters as r, type IsNotModifier as s };
245
+ export { $internal as $, type AoSFactory as A, type ConfigurableTrait as C, type ExtractSchema as E, type IsTag as I, ModifierData as M, type Norm as N, type QueryParameter as Q, type Relation as R, type Schema as S, type Trait as T, World as W, type RelationTarget as a, type WildcardRelation as b, createWorld as c, type TraitType as d, type TraitValue as e, type TraitTuple as f, type TraitInstance as g, type Store as h, type ExtractStore as i, type ExtractIsTag as j, type Entity as k, type QueryModifier as l, type QuerySubscriber as m, type QueryResultOptions as n, type QueryResult as o, type StoresFromParameters as p, type InstancesFromParameters as q, type IsNotModifier as r };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "koota",
3
- "version": "0.1.12",
3
+ "version": "0.2.1",
4
4
  "description": "🌎 Performant real-time state management for React and TypeScript",
5
5
  "license": "ISC",
6
6
  "type": "module",
@@ -32,36 +32,35 @@
32
32
  "LICENSE"
33
33
  ],
34
34
  "devDependencies": {
35
- "@types/react": "^18.3.3",
36
- "@types/react-dom": "^18.3.0",
37
- "@types/three": "^0.162.0",
38
- "react": "^18.3.1",
39
- "react-dom": "^18.3.1",
40
- "rimraf": "^6.0.1",
35
+ "react": "^18.0.0 || ^19.0.0",
36
+ "react-dom": "^18.0.0 || ^19.0.0",
41
37
  "tsup": "^8.3.0",
42
38
  "@koota/core": "0.0.1",
43
- "@koota/react": "0.0.1",
44
- "tsconfig": "0.1.0"
39
+ "tsconfig": "0.1.0",
40
+ "@koota/react": "0.0.1"
45
41
  },
46
42
  "peerDependencies": {
47
- "@react-three/fiber": "^8.17.10",
48
- "react": "^18.3.1",
49
- "react-dom": "^18.3.1",
50
- "three": ">=0.159"
43
+ "@types/react": "^18.0.0 || ^19.0.0",
44
+ "@types/react-dom": "^18.0.0 || ^19.0.0",
45
+ "react": "^18.0.0 || ^19.0.0",
46
+ "react-dom": "^18.0.0 || ^19.0.0"
51
47
  },
52
48
  "peerDependenciesMeta": {
53
- "@react-three/fiber": {
49
+ "@types/react": {
54
50
  "optional": true
55
51
  },
56
- "three": {
52
+ "@types/react-dom": {
57
53
  "optional": true
58
54
  },
59
55
  "react-dom": {
60
56
  "optional": true
57
+ },
58
+ "react": {
59
+ "optional": true
61
60
  }
62
61
  },
63
62
  "scripts": {
64
- "build": "rimraf dist react && tsup src/index.ts src/react.ts --format esm,cjs --dts --dts-resolve && tsx scripts/copy-readme.ts && tsx scripts/copy-react-files.ts",
63
+ "build": "tsup && tsx scripts/copy-readme.ts && tsx scripts/copy-react-files.ts",
65
64
  "test": "vitest --environment=jsdom"
66
65
  }
67
66
  }
package/react/index.cjs CHANGED
@@ -48,16 +48,6 @@ __export(react_exports, {
48
48
  });
49
49
  module.exports = __toCommonJS(react_exports);
50
50
 
51
- // ../react/src/hooks/use-query.ts
52
- var import_react3 = require("react");
53
-
54
- // ../react/src/world/use-world.ts
55
- var import_react2 = require("react");
56
-
57
- // ../react/src/world/world-context.ts
58
- var import_react = require("react");
59
- var WorldContext = (0, import_react.createContext)(null);
60
-
61
51
  // ../core/src/common.ts
62
52
  var $internal = Symbol("internal");
63
53
 
@@ -86,7 +76,12 @@ function defineRelation(definition) {
86
76
  function relationFn(target) {
87
77
  if (target === void 0) throw Error("Relation target is undefined");
88
78
  if (target === "*") target = Wildcard;
89
- return getRelationTrait(relationFn, traitFactory, pairsMap, target);
79
+ return getRelationTrait(
80
+ relationFn,
81
+ traitFactory,
82
+ pairsMap,
83
+ target
84
+ );
90
85
  }
91
86
  return Object.assign(relationFn, {
92
87
  [$internal]: {
@@ -599,9 +594,7 @@ Number.prototype.set = function(trait2, value, triggerChanged = true) {
599
594
  const index = this & ENTITY_ID_MASK;
600
595
  const worldId = this >>> WORLD_ID_SHIFT;
601
596
  const store = ctx.stores[worldId];
602
- if (value instanceof Function) {
603
- value = value(ctx.get(index, store));
604
- }
597
+ value instanceof Function && (value = value(ctx.get(index, store)));
605
598
  ctx.set(index, store, value);
606
599
  triggerChanged && setChanged(universe.worlds[worldId], this, trait2);
607
600
  };
@@ -761,6 +754,7 @@ var createQueryHash = (parameters) => {
761
754
  var IsExcluded = trait();
762
755
  var Query = class {
763
756
  constructor(world, parameters = []) {
757
+ __publicField(this, "version", 0);
764
758
  __publicField(this, "world");
765
759
  __publicField(this, "parameters");
766
760
  __publicField(this, "hash");
@@ -846,6 +840,7 @@ var Query = class {
846
840
  this.traits.push(trait2);
847
841
  }
848
842
  }
843
+ this.traitData.forbidden.push(ctx.traitData.get(IsExcluded));
849
844
  this.traitData.all = [
850
845
  ...this.traitData.required,
851
846
  ...this.traitData.forbidden,
@@ -927,7 +922,6 @@ var Query = class {
927
922
  const entities = ctx.entityIndex.dense;
928
923
  for (let i = 0; i < entities.length; i++) {
929
924
  const entity = entities[i];
930
- if (entity.has(IsExcluded)) continue;
931
925
  const match = this.check(world, entity);
932
926
  if (match) this.add(entity);
933
927
  }
@@ -946,6 +940,7 @@ var Query = class {
946
940
  for (const sub of this.addSubscriptions) {
947
941
  sub(entity);
948
942
  }
943
+ this.version++;
949
944
  }
950
945
  remove(world, entity) {
951
946
  if (!this.entities.has(entity) || this.toRemove.has(entity)) return;
@@ -1362,6 +1357,31 @@ function createWorld(...traits) {
1362
1357
  return new World(...traits);
1363
1358
  }
1364
1359
 
1360
+ // ../core/src/query/utils/cache-query.ts
1361
+ function cacheQuery(...parameters) {
1362
+ const hash = createQueryHash(parameters);
1363
+ for (const world of universe.worlds) {
1364
+ if (!world) continue;
1365
+ const ctx = world[$internal];
1366
+ if (!ctx.queriesHashMap.has(hash)) {
1367
+ const query = new Query(world, parameters);
1368
+ ctx.queriesHashMap.set(hash, query);
1369
+ }
1370
+ }
1371
+ universe.cachedQueries.set(hash, parameters);
1372
+ return hash;
1373
+ }
1374
+
1375
+ // ../react/src/hooks/use-query.ts
1376
+ var import_react3 = require("react");
1377
+
1378
+ // ../react/src/world/use-world.ts
1379
+ var import_react2 = require("react");
1380
+
1381
+ // ../react/src/world/world-context.ts
1382
+ var import_react = require("react");
1383
+ var WorldContext = (0, import_react.createContext)(null);
1384
+
1365
1385
  // ../react/src/world/default-world.ts
1366
1386
  var defaultWorld = createWorld();
1367
1387
  var getDefaultWorld = () => defaultWorld;
@@ -1374,34 +1394,29 @@ function useWorld() {
1374
1394
 
1375
1395
  // ../react/src/hooks/use-query.ts
1376
1396
  function useQuery(...parameters) {
1377
- const memoizedParameters = (0, import_react3.useMemo)(() => parameters, [parameters]);
1378
1397
  const world = useWorld();
1379
- const entities = (0, import_react3.useMemo)(() => world.query(...memoizedParameters), [world, memoizedParameters]);
1380
- const [, forceUpdate] = (0, import_react3.useReducer)((v) => v + 1, 0);
1381
- (0, import_react3.useEffect)(() => {
1382
- const mutableEntities = entities;
1383
- mutableEntities.length = 0;
1384
- mutableEntities.push(...world.query(...memoizedParameters));
1385
- forceUpdate();
1386
- }, [world]);
1398
+ const [hash, initialVersion] = (0, import_react3.useMemo)(() => {
1399
+ const hash2 = cacheQuery(...parameters);
1400
+ const query = world[$internal].queriesHashMap.get(hash2);
1401
+ return [hash2, query.version];
1402
+ }, [parameters]);
1403
+ const [entities, setEntities] = (0, import_react3.useState)(() => world.query(hash));
1387
1404
  (0, import_react3.useEffect)(() => {
1388
- const unsubAdd = world.onAdd(memoizedParameters, (entity) => {
1389
- const mutableEntities = entities;
1390
- mutableEntities.push(entity);
1391
- forceUpdate();
1405
+ const unsubAdd = world.onAdd(parameters, () => {
1406
+ setEntities(world.query(hash));
1392
1407
  });
1393
- const unsubRemove = world.onRemove(memoizedParameters, (entity) => {
1394
- const mutableEntities = entities;
1395
- const index = mutableEntities.indexOf(entity);
1396
- mutableEntities[index] = mutableEntities[mutableEntities.length - 1];
1397
- mutableEntities.pop();
1398
- forceUpdate();
1408
+ const unsubRemove = world.onRemove(parameters, () => {
1409
+ setEntities(world.query(hash));
1399
1410
  });
1411
+ const query = world[$internal].queriesHashMap.get(hash);
1412
+ if (query.version !== initialVersion) {
1413
+ setEntities(world.query(hash));
1414
+ }
1400
1415
  return () => {
1401
1416
  unsubAdd();
1402
1417
  unsubRemove();
1403
1418
  };
1404
- }, [world]);
1419
+ }, [world, hash]);
1405
1420
  return entities;
1406
1421
  }
1407
1422
 
package/react/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { Q as QueryParameter, p as QueryResult, W as World, T as Trait, l as Entity, g as TraitInstance } from '../dist/world-CIrtrsKj.cjs';
1
+ import { Q as QueryParameter, o as QueryResult, W as World, T as Trait, k as Entity, g as TraitInstance } from '../dist/world-DQXl4lI8.cjs';
2
2
  import * as react_jsx_runtime from 'react/jsx-runtime';
3
3
 
4
4
  declare function useQuery<T extends QueryParameter[]>(...parameters: T): QueryResult<T>;
package/react/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Q as QueryParameter, p as QueryResult, W as World, T as Trait, l as Entity, g as TraitInstance } from '../dist/world-CIrtrsKj.js';
1
+ import { Q as QueryParameter, o as QueryResult, W as World, T as Trait, k as Entity, g as TraitInstance } from '../dist/world-DQXl4lI8.js';
2
2
  import * as react_jsx_runtime from 'react/jsx-runtime';
3
3
 
4
4
  declare function useQuery<T extends QueryParameter[]>(...parameters: T): QueryResult<T>;
package/react/index.js CHANGED
@@ -1,10 +1,11 @@
1
1
  import {
2
2
  $internal,
3
+ cacheQuery,
3
4
  createWorld
4
- } from "../dist/chunk-FAWXK4QV.js";
5
+ } from "../dist/chunk-4QEK6BHY.js";
5
6
 
6
7
  // ../react/src/hooks/use-query.ts
7
- import { useEffect, useMemo, useReducer } from "react";
8
+ import { useEffect, useMemo, useState } from "react";
8
9
 
9
10
  // ../react/src/world/use-world.ts
10
11
  import { useContext } from "react";
@@ -25,34 +26,29 @@ function useWorld() {
25
26
 
26
27
  // ../react/src/hooks/use-query.ts
27
28
  function useQuery(...parameters) {
28
- const memoizedParameters = useMemo(() => parameters, [parameters]);
29
29
  const world = useWorld();
30
- const entities = useMemo(() => world.query(...memoizedParameters), [world, memoizedParameters]);
31
- const [, forceUpdate] = useReducer((v) => v + 1, 0);
30
+ const [hash, initialVersion] = useMemo(() => {
31
+ const hash2 = cacheQuery(...parameters);
32
+ const query = world[$internal].queriesHashMap.get(hash2);
33
+ return [hash2, query.version];
34
+ }, [parameters]);
35
+ const [entities, setEntities] = useState(() => world.query(hash));
32
36
  useEffect(() => {
33
- const mutableEntities = entities;
34
- mutableEntities.length = 0;
35
- mutableEntities.push(...world.query(...memoizedParameters));
36
- forceUpdate();
37
- }, [world]);
38
- useEffect(() => {
39
- const unsubAdd = world.onAdd(memoizedParameters, (entity) => {
40
- const mutableEntities = entities;
41
- mutableEntities.push(entity);
42
- forceUpdate();
37
+ const unsubAdd = world.onAdd(parameters, () => {
38
+ setEntities(world.query(hash));
43
39
  });
44
- const unsubRemove = world.onRemove(memoizedParameters, (entity) => {
45
- const mutableEntities = entities;
46
- const index = mutableEntities.indexOf(entity);
47
- mutableEntities[index] = mutableEntities[mutableEntities.length - 1];
48
- mutableEntities.pop();
49
- forceUpdate();
40
+ const unsubRemove = world.onRemove(parameters, () => {
41
+ setEntities(world.query(hash));
50
42
  });
43
+ const query = world[$internal].queriesHashMap.get(hash);
44
+ if (query.version !== initialVersion) {
45
+ setEntities(world.query(hash));
46
+ }
51
47
  return () => {
52
48
  unsubAdd();
53
49
  unsubRemove();
54
50
  };
55
- }, [world]);
51
+ }, [world, hash]);
56
52
  return entities;
57
53
  }
58
54
 
@@ -69,7 +65,7 @@ function useActions(actions) {
69
65
  }
70
66
 
71
67
  // ../react/src/hooks/use-trait.ts
72
- import { useEffect as useEffect2, useMemo as useMemo2, useState } from "react";
68
+ import { useEffect as useEffect2, useMemo as useMemo2, useState as useState2 } from "react";
73
69
 
74
70
  // ../react/src/utils/is-world.ts
75
71
  function isWorld(target) {
@@ -83,7 +79,7 @@ function useTrait(target, trait) {
83
79
  () => target ? createSubscriptions(target, trait, contextWorld) : void 0,
84
80
  [target, trait, contextWorld]
85
81
  );
86
- const [value, setValue] = useState(() => {
82
+ const [value, setValue] = useState2(() => {
87
83
  return memo?.entity.has(trait) ? memo?.entity.get(trait) : void 0;
88
84
  });
89
85
  useEffect2(() => {