@perplexdotgg/mecs 0.4.2 → 0.5.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
@@ -16,7 +16,7 @@ MECS focuses on the developer's experience in their IDE, especially around auto-
16
16
 
17
17
  <details>
18
18
  <summary>Side note: Why not use SoA design?</summary>
19
- While SoA can achieve better performance in the best cases, it tends to be unwieldy, especially for nested fields, and generally ECS implementations lack auto-complete for components and properties. For larger game projecting, remembering all component names and their properties is quite difficult. Using JS getters with SoA (to achieve auto complete) gives much worse overall performance than AoS.
19
+ While SoA can achieve better performance in the best cases, it tends to be unwieldy, especially for nested fields, and generally ECS implementations lack auto-complete for components and properties. For larger game projects, remembering all component names and their properties is quite difficult. Using JS getters with SoA (to achieve auto complete) gives much worse overall performance than AoS.
20
20
  </details>
21
21
 
22
22
  ---
@@ -142,6 +142,9 @@ const queries = {
142
142
  without: ['localTransform'],
143
143
 
144
144
  // listeners can added now, or later (see the Queries docs below)
145
+ // note that in this context, the entity type doesn't have Entity methods that you've added
146
+ // if you need those, either ts-ignore when calling them here, or add listeners later in your system code using:
147
+ // Entity.queries().yourQueryKey.afterEntityAdded.addListener((entity) => { /* your code here will get the right entity type */ }))
145
148
  afterEntityAdded: (entity) => {
146
149
  // this is called when an entity newly matches this query
147
150
  },
@@ -154,7 +157,7 @@ const queries = {
154
157
  with: ['localTransform', 'globalTransform'],
155
158
  without: [],
156
159
  },
157
- } as const satisfies QueryMap<Entity, typeof components>;
160
+ } as const satisfies QueryMap<typeof components>;
158
161
 
159
162
  // additional Entity class options, see "Entity Class Options" below
160
163
  const extraOptions = {};
package/build/index.d.ts CHANGED
@@ -4,10 +4,18 @@ import { MonomorphInstance } from 'monomorph';
4
4
  import { NumberArray } from 'monomorph';
5
5
  import { PartialRecursive } from 'monomorph';
6
6
  import { PoolClass } from 'monomorph';
7
+ import { PropertyDefinitionClassProperties } from 'monomorph';
8
+ import { PropertyDefinitionReference } from 'monomorph';
9
+ import { PropertyDefinitionReferenceList } from 'monomorph';
10
+ import { ReferenceListClass } from 'monomorph';
7
11
  import { WithPool } from 'monomorph';
8
12
 
13
+ declare type BivariantCallback<T> = {
14
+ bivarianceHack(arg: T): void;
15
+ }["bivarianceHack"];
16
+
9
17
  export declare type ComponentConfig<ComponentType, ComponentInputType extends any = InputType<ComponentType> extends never ? ComponentType : InputType<ComponentType>> = {
10
- monomorphClass?: ComponentType extends MonomorphClass<any, any, any> ? ComponentType : ComponentType extends MonomorphInstance<infer P, infer I> ? MonomorphClass<P, I, ComponentType> : never;
18
+ monomorphClass?: ComponentType extends LazyWrapper<infer MC extends MonomorphClass<any, any, any>> ? LazyWrapper<MC> : ComponentType extends MonomorphClass<any, any, any> ? ComponentType : ComponentType extends MonomorphInstance<infer P, infer I> ? MonomorphClass<P, I, ComponentType> : never;
11
19
  afterComponentAdded?: (componentInstance: ComponentType extends abstract new (...args: any) => any ? InstanceType<ComponentType> : ComponentType, entity: any, componentKey: string) => void;
12
20
  afterComponentAddedCode?: string | ComponentConfigFunction<ComponentType, ComponentInputType>;
13
21
  beforeComponentRemoved?: (componentInstance: ComponentType extends abstract new (...args: any) => any ? InstanceType<ComponentType> : ComponentType, entity: any, componentKey: string) => void;
@@ -40,37 +48,59 @@ export declare type ComponentConfigFunction<ComponentType, ComponentInputType> =
40
48
 
41
49
  export declare type ComponentInput<X> = X extends MonomorphClass<infer P, infer I, infer M> ? M extends MonomorphInstance<infer iP, infer iI> ? InputType<M> : never : X extends ComponentConfig<infer CT, infer I> ? I : X;
42
50
 
51
+ declare type ComponentInputResolved<CC, CM extends ComponentMap> = ResolveEntityInputListItems<ComponentInput<ExtractMonomorphClass<CC>>, CM>;
52
+
43
53
  export declare type ComponentMap = Record<string, ComponentConfig<any, any> | MonomorphClass<any, any, any> | LazyWrapper<MonomorphClass<any, any, any>> | ComponentConfig<MonomorphClass<any, any, any>, any> | ComponentConfig<LazyWrapper<MonomorphClass<any, any, any>>, any>>;
44
54
 
45
- declare type ComponentMapClassProperties<CM> = {
46
- [K in keyof CM as LowercaseFirstLetter<K>]?: ExtractMonomorphClass<CM[K]> extends MonomorphClass<infer P, infer I, infer M> ? M : ExtractMonomorphClass<CM[K]>;
55
+ declare type ComponentMapClassProperties<CM extends ComponentMap> = {
56
+ [K in keyof CM as LowercaseFirstLetter<K>]?: ExtractMonomorphClass<CM[K]> extends MonomorphClass<infer P, infer I, infer M> ? ResolveEntityRefs<M, CM> : ExtractMonomorphClass<CM[K]>;
47
57
  };
48
58
 
49
59
  export declare type ComponentMapInput<CM extends ComponentMap> = Partial<{
50
- [K in keyof CM as LowercaseFirstLetter<K>]: ComponentInput<ExtractMonomorphClass<CM[K]>>;
60
+ [K in keyof CM as LowercaseFirstLetter<K>]: ResolveEntityInput<ComponentInput<ExtractMonomorphClass<CM[K]>>, CM>;
61
+ }>;
62
+
63
+ declare type ComponentMapInputListItems<CM extends ComponentMap> = Partial<{
64
+ [K in keyof CM as LowercaseFirstLetter<K>]: ResolveEntityInputListItems<ComponentInput<ExtractMonomorphClass<CM[K]>>, CM>;
51
65
  }>;
52
66
 
53
- export declare function createEntityClass<C>(options?: EntityCodeGenerationOptions): <CM extends ComponentMap, QM extends QueryMap<C, CM>, I extends ComponentMapInput<CM> = ComponentMapInput<CM>>(componentMap: CM, queries?: QM) => EntityClassWithStatics<C, CM, QM, I>;
67
+ declare type ComponentsWithEntities<E, CM extends ComponentMap> = {
68
+ [K in keyof CM as ExtractMonomorphClass<CM[K]> extends MonomorphClass<infer P, infer I, infer M> ? LowercaseFirstLetter<K> : never]: {
69
+ [Symbol.iterator](): IterableIterator<[WithPool<InstanceType<ExtractMonomorphClass<CM[K]>>>, E]>;
70
+ };
71
+ };
72
+
73
+ declare type ComponentsWithEntitiesCallable<E, CM extends ComponentMap> = ComponentsWithEntities<E, CM> & {
74
+ <Self extends abstract new (...args: any) => any>(this: Self): ComponentsWithEntities<InstanceType<Self>, CM>;
75
+ };
54
76
 
55
- export declare type CreateEntityFunction<C, CM extends ComponentMap, I extends ComponentMapInput<CM>> = (data?: I, updateQueryMemberships?: boolean, pool?: EntityPoolClass<C>) => EntityInstanceWithPool<CM, I, C>;
77
+ declare type ComponentTypeFromMonomorphClass<T> = T extends MonomorphClass<infer P, any, any> ? PropertyDefinitionClassProperties<P> : never;
56
78
 
57
- declare type ElementOfArray<T> = T extends (infer E)[] ? E : never;
79
+ export declare function createEntityClass<C>(options?: EntityCodeGenerationOptions): <CM extends ComponentMap, QM extends QueryMap<CM>, I extends ComponentMapInput<CM> = ComponentMapInput<CM>>(componentMap: CM, queries?: QM) => EntityClassWithStatics<C, CM, QM, I>;
80
+
81
+ export declare function createEntityClass(options?: EntityCodeGenerationOptions): <CM extends ComponentMap, QM extends QueryMap<CM>, I extends ComponentMapInput<CM> = ComponentMapInput<CM>>(componentMap: CM, queries?: QM) => EntityClassWithStatics<EntityInstance<CM, I>, CM, QM, I>;
82
+
83
+ export declare type CreateEntityFunction<C, CM extends ComponentMap, I extends ComponentMapInput<CM>> = <T extends EntityConstructor<any, any, I, CM>>(this: T, data?: I, updateQueryMemberships?: boolean, pool?: EntityPoolClass<InstanceType<T>>) => EntityInstanceWithPool<CM, I, InstanceType<T>>;
84
+
85
+ declare type ElementOfReadonlyArray<T> = T extends ReadonlyArray<infer E> ? E : never;
58
86
 
59
87
  declare interface EntityBaseProperties<CM extends ComponentMap, I extends any = ComponentMapInput<CM>> {
60
88
  componentMap: CM;
61
89
  __typescriptOnlyInputType: I;
62
- addComponent: <CCK extends LowercaseFirstLetter<keyof CM>, CC extends CM[OriginalComponentKey<CCK, CM>] = CM[OriginalComponentKey<CCK, CM>]>(...a: (undefined extends ComponentInput<ExtractMonomorphClass<CC>> ? [key: CCK, data?: ComponentInput<ExtractMonomorphClass<CC>>, updateQueryMemberships?: boolean] : [key: CCK, data: ComponentInput<ExtractMonomorphClass<CC>>, updateQueryMemberships?: boolean])) => void;
90
+ addComponent: <CCK extends LowercaseFirstLetter<keyof CM>, CC extends CM[OriginalComponentKey<CCK, CM>] = CM[OriginalComponentKey<CCK, CM>]>(...a: (undefined extends NoInfer<ComponentInputResolved<CC, CM>> ? [key: CCK, data?: NoInfer<ComponentInputResolved<CC, CM>>, updateQueryMemberships?: boolean] : [key: CCK, data: NoInfer<ComponentInputResolved<CC, CM>>, updateQueryMemberships?: boolean])) => void;
63
91
  removeComponent: <CCK extends LowercaseFirstLetter<keyof CM>>(key: CCK, updateQueryMemberships?: boolean) => void;
64
92
  hasComponent: <CCK extends LowercaseFirstLetter<keyof CM>>(key: CCK) => boolean;
65
93
  clone(): this;
66
94
  reset(data?: PartialRecursive<I>, updateQueryMemberships?: boolean, pool?: EntityPoolClass<this>): void;
67
95
  destroy(updateQueryMemberships?: boolean): void;
68
96
  isDestroyed(): boolean;
97
+ poolIndex: number;
98
+ pool: EntityPoolClass<this>;
69
99
  }
70
100
 
71
- export declare type EntityClass<CM extends ComponentMap, I extends ComponentMapInput<CM> = ComponentMapInput<CM>, QM extends QueryMap<any, CM> = QueryMap<any, CM>, E extends EntityInstance<CM, I> = EntityInstance<CM, I>> = EntityConstructor<QM, E, I, CM>;
101
+ export declare type EntityClass<CM extends ComponentMap, I extends ComponentMapInput<CM> = ComponentMapInput<CM>, QM extends QueryMap<CM> = QueryMap<CM>, E extends EntityInstance<CM, I> = EntityInstance<CM, I>> = EntityConstructor<QM, E, I, CM>;
72
102
 
73
- export declare type EntityClassWithStatics<C, CM extends ComponentMap, QM extends QueryMap<C, CM>, I extends ComponentMapInput<CM> = ComponentMapInput<CM>> = EntityClass<CM, I, QM> & {
103
+ export declare type EntityClassWithStatics<C, CM extends ComponentMap, QM extends QueryMap<CM>, I extends ComponentMapInput<CM> = ComponentMapInput<CM>> = EntityClass<CM, I, QM> & {
74
104
  create: CreateEntityFunction<C, CM, I>;
75
105
  Pool: NoInfer<EntityPoolClass<C>>;
76
106
  };
@@ -96,37 +126,28 @@ declare type EntityCodeGenerationOptions = {
96
126
  skipSafetyChecks?: boolean;
97
127
  };
98
128
 
99
- export declare interface EntityConstructor<QM extends QueryMap<any, CM>, E extends EntityInstance<CM, I>, I extends any, CM extends ComponentMap> {
100
- new (data: I, index?: number, pool?: EntityPoolClass<E>, updateQueryMemberships?: boolean): E;
129
+ export declare interface EntityConstructor<QM extends QueryMap<CM>, E extends EntityInstance<CM, I>, I extends any, CM extends ComponentMap> {
130
+ new (data: I, poolIndex?: number, pool?: EntityPoolClass<E>, updateQueryMemberships?: boolean): E;
101
131
  componentMap: CM;
132
+ __lazyReferenceTarget?: E;
102
133
  __typescriptOnlyInputType: I;
103
- pool: EntityPoolClass<E>;
104
- componentsWithEntities: {
105
- [K in keyof CM as ExtractMonomorphClass<CM[K]> extends MonomorphClass<infer P, infer I, infer M> ? LowercaseFirstLetter<K> : never]: {
106
- [Symbol.iterator](): IterableIterator<[WithPool<InstanceType<ExtractMonomorphClass<CM[K]>>>, E]>;
107
- };
108
- };
134
+ pool: PoolCallable<E>;
135
+ queries: QueriesCallable<E, QM, CM>;
136
+ componentsWithEntities: ComponentsWithEntitiesCallable<E, CM>;
109
137
  components: {
110
138
  [K in keyof CM as ExtractMonomorphClass<CM[K]> extends MonomorphClass<infer P, infer I, infer M> ? LowercaseFirstLetter<K> : never]: PoolClass<ExtractMonomorphClass<CM[K]>>;
111
139
  };
112
- queries: {
113
- [K in keyof QM]: {
114
- afterEntityAdded: {
115
- addListener(listener: (entity: E) => void): void;
116
- removeListener(listener: (entity: E) => void): void;
117
- };
118
- beforeEntityRemoved: {
119
- addListener(listener: (entity: E) => void): void;
120
- removeListener(listener: (entity: E) => void): void;
121
- };
122
- [Symbol.iterator](): IterableIterator<WithComponent<E, ElementOfArray<QM[K]['with']> extends LowercaseFirstLetter<keyof E["componentMap"]> ? ElementOfArray<QM[K]['with']> : never>>;
123
- };
124
- };
125
140
  }
126
141
 
142
+ declare type EntityConstructorLike = abstract new (...args: any[]) => any;
143
+
144
+ declare type EntityFor<CM extends ComponentMap> = EntityInstance<CM, any>;
145
+
127
146
  export declare type EntityInput<X extends ComponentMap | EntityClass<any, any, any, any> | EntityInstanceWithPool<any, any, any> | EntityInstance<any, any>> = Writeable<X extends ComponentMap ? ComponentMapInput<X> : X extends EntityConstructor<infer QM, infer E, infer I, infer CM> ? I : X extends EntityInstanceWithPool<infer CM, infer I, infer E> ? I : X extends EntityInstance<infer CM, infer I> ? I : never>;
128
147
 
129
- export declare type EntityInstance<CM extends ComponentMap, I extends any = ComponentMapInput<CM>> = ComponentMapClassProperties<CM> & EntityBaseProperties<CM, I>;
148
+ declare type EntityInputListItems<E> = E extends ComponentMap ? ComponentMapInputListItems<E> : E extends EntityConstructor<any, any, any, infer CM> ? ComponentMapInputListItems<CM> : E extends EntityInstance<infer CM, any> ? ComponentMapInputListItems<CM> : E extends EntityInstanceWithPool<infer CM, any, any> ? ComponentMapInputListItems<CM> : EntityInput<NoInfer<E>>;
149
+
150
+ export declare type EntityInstance<CM extends ComponentMap, I extends any = ComponentMapInput<CM>> = ComponentMapClassProperties<NoInfer<CM>> & EntityBaseProperties<CM, I>;
130
151
 
131
152
  export declare type EntityInstanceWithPool<CM extends ComponentMap, I extends ComponentMapInput<CM>, E> = NoInfer<E & {
132
153
  pool: EntityPoolClass<E> | null;
@@ -159,6 +180,65 @@ export declare interface EntityPoolClass<M> {
159
180
  fromArrayOnlyReferences(array: NumberArray, references?: any, startOffset?: number, classConstructor?: new (...args: any[]) => M): number;
160
181
  }
161
182
 
183
+ declare type EntityPoolInput = {
184
+ array: unknown[];
185
+ freeIndices: number[];
186
+ maxLength: number;
187
+ length: number;
188
+ [Symbol.iterator](): IterableIterator<any>;
189
+ create?(data?: any): any;
190
+ };
191
+
192
+ declare type EntityQueries<E, QM extends QueryMap<CM>, CM extends ComponentMap> = E extends {
193
+ componentMap: any;
194
+ } ? ({
195
+ [K in keyof QM]: {
196
+ afterEntityAdded: {
197
+ addListener(listener: BivariantCallback<WithComponent<E, ElementOfReadonlyArray<QM[K]['with']> extends LowercaseFirstLetter<keyof E["componentMap"]> ? ElementOfReadonlyArray<QM[K]['with']> : never>>): void;
198
+ removeListener(listener: BivariantCallback<E>): void;
199
+ };
200
+ beforeEntityRemoved: {
201
+ addListener(listener: BivariantCallback<WithComponent<E, ElementOfReadonlyArray<QM[K]['with']> extends LowercaseFirstLetter<keyof E["componentMap"]> ? ElementOfReadonlyArray<QM[K]['with']> : never>>): void;
202
+ removeListener(listener: BivariantCallback<E>): void;
203
+ };
204
+ [Symbol.iterator](): IterableIterator<WithComponent<E, ElementOfReadonlyArray<QM[K]['with']> extends LowercaseFirstLetter<keyof E["componentMap"]> ? ElementOfReadonlyArray<QM[K]['with']> : never>>;
205
+ };
206
+ } & {
207
+ [key: string]: any;
208
+ }) : never;
209
+
210
+ declare const EntityRefBrand: unique symbol;
211
+
212
+ export declare function EntityReference(fn: () => any): PropertyDefinitionReference<EntitySelfRef | null, true>;
213
+
214
+ export declare function EntityReference<E>(fn: () => any): PropertyDefinitionReference<LazyEntityReferenceTarget<E> | null, true>;
215
+
216
+ export declare function EntityReference<C extends EntityConstructorLike>(fn: () => C): PropertyDefinitionReference<LazyEntityReferenceTarget<C> | null, true>;
217
+
218
+ export declare function EntityReferenceList(fn: () => any): PropertyDefinitionReferenceList<EntitySelfRef, EntitySelfRefListInput>;
219
+
220
+ export declare function EntityReferenceList<E>(fn: () => any): PropertyDefinitionReferenceList<LazyEntityReferenceTarget<E>, EntitySelfRefListInput>;
221
+
222
+ export declare function EntityReferenceList<C extends EntityConstructorLike>(fn: () => C): PropertyDefinitionReferenceList<LazyEntityReferenceTarget<C>, EntitySelfRefListInput>;
223
+
224
+ export declare type EntityReferenceListInput<E> = EntityReferenceListInputBase<EntityInputListItems<NoInfer<E>>>;
225
+
226
+ declare type EntityReferenceListInputBase<TItems = unknown> = {
227
+ pool: EntityPoolInput;
228
+ items?: TItems[];
229
+ maxLength?: number;
230
+ };
231
+
232
+ declare const EntityRefListBrand: unique symbol;
233
+
234
+ declare type EntitySelfRef = {
235
+ [EntityRefBrand]?: true;
236
+ };
237
+
238
+ declare type EntitySelfRefListInput = {
239
+ [EntityRefListBrand]?: true;
240
+ };
241
+
162
242
  export declare type ExtractMonomorphClass<CC> = CC extends {
163
243
  monomorphClass?: infer MC extends MonomorphClass<any, any, any>;
164
244
  } ? MC : CC extends ({
@@ -169,9 +249,20 @@ export declare type ExtractMonomorphClass<CC> = CC extends {
169
249
  fnInArray: [() => infer MC extends MonomorphClass<any, any, any>];
170
250
  } ? MC : CC;
171
251
 
172
- export declare function getEntityClassCode<C, CM extends ComponentMap>(componentMap: CM, queries?: QueryMap<C, CM>, options?: EntityCodeGenerationOptions): string;
252
+ export declare function getEntityClassCode<C, CM extends ComponentMap>(componentMap: CM, queries?: QueryMap<CM>, options?: EntityCodeGenerationOptions): string;
253
+
254
+ export declare function LazyComponent<CT = undefined, MC extends MonomorphClass<any, any, any> = MonomorphClass<any, any, any>>(componentFn: () => MC): LazyComponentWrapper<MC, CT>;
255
+
256
+ declare type LazyComponentResolvedType<MC extends MonomorphClass<any, any, any>, CT> = CT extends undefined ? ComponentTypeFromMonomorphClass<MC> : CT;
173
257
 
174
- export declare function LazyComponent<F extends () => T, T>(componentFn: F): LazyWrapper<ReturnType<F>>;
258
+ declare type LazyComponentWrapper<MC extends MonomorphClass<any, any, any>, CT> = {
259
+ fnInArray: [() => MC];
260
+ componentType: Writeable<LazyComponentResolvedType<MC, CT>>;
261
+ };
262
+
263
+ export declare type LazyEntityReferenceTarget<T> = {
264
+ __lazyReferenceTarget?: NoInfer<T>;
265
+ };
175
266
 
176
267
  export declare type LazyMonomorphComponentConfig<MC extends MonomorphClass<any, any, any>> = ComponentConfig<LazyWrapper<MC>, InputType<InstanceType<MC>>>;
177
268
 
@@ -185,21 +276,53 @@ export declare type MonomorphComponentConfig<MC extends MonomorphClass<any, any,
185
276
 
186
277
  declare type OriginalComponentKey<CCK extends LowercaseFirstLetter<keyof CM>, CM extends ComponentMap> = CCK extends keyof CM ? CCK : UppercaseFirstLetter<CCK>;
187
278
 
188
- export declare type QueryConfig<C, CM extends ComponentMap> = {
189
- with?: LowercaseFirstLetter<keyof CM>[];
190
- without?: LowercaseFirstLetter<keyof CM>[];
191
- afterEntityAdded?: (entity: C) => void;
192
- beforeEntityRemoved?: (entity: C) => void;
279
+ declare type PoolCallable<E> = EntityPoolClass<E> & {
280
+ <Self extends abstract new (...args: any) => any>(this: Self): EntityPoolClass<InstanceType<Self>>;
281
+ };
282
+
283
+ declare type QueriesCallable<E, QM extends QueryMap<CM>, CM extends ComponentMap> = EntityQueries<E, QM, CM> & {
284
+ <Self extends abstract new (...args: any) => any>(this: Self): EntityQueries<InstanceType<Self>, QM, CM>;
285
+ };
286
+
287
+ export declare type QueryConfig<CM extends ComponentMap> = {
288
+ with?: ReadonlyArray<LowercaseFirstLetter<keyof CM>>;
289
+ without?: ReadonlyArray<LowercaseFirstLetter<keyof CM>>;
290
+ afterEntityAdded?: (entity: EntityInstance<CM>) => void;
291
+ beforeEntityRemoved?: (entity: EntityInstance<CM>) => void;
193
292
  };
194
293
 
195
- export declare type QueryMap<C, CM extends ComponentMap> = Record<string, QueryConfig<C, CM>>;
294
+ export declare type QueryMap<CM extends ComponentMap> = Record<string, QueryConfig<CM>>;
295
+
296
+ declare type ResolveEntityInput<T, CM extends ComponentMap> = T extends object ? {
297
+ [K in keyof T]: ResolveEntityInputValue<T[K], CM>;
298
+ } : ResolveEntityInputValue<T, CM>;
299
+
300
+ declare type ResolveEntityInputListItems<T, CM extends ComponentMap> = T extends object ? {
301
+ [K in keyof T]: ResolveEntityInputValueListItems<T[K], CM>;
302
+ } : ResolveEntityInputValueListItems<T, CM>;
303
+
304
+ declare type ResolveEntityInputValue<T, CM extends ComponentMap> = T extends any ? (UnwrapLazyEntityReferenceTarget<T> extends EntitySelfRef ? EntityFor<CM> : UnwrapLazyEntityReferenceTarget<T> extends EntitySelfRefListInput ? EntityReferenceListInput<EntityFor<CM>> : UnwrapEntityTarget<T> extends ReferenceListClass<infer M> ? EntityReferenceListInput<UnwrapEntityTarget<M>> : UnwrapEntityTarget<T>) : never;
305
+
306
+ declare type ResolveEntityInputValueListItems<T, CM extends ComponentMap> = T extends any ? (UnwrapLazyEntityReferenceTarget<T> extends EntitySelfRef ? EntityFor<CM> : UnwrapLazyEntityReferenceTarget<T> extends EntitySelfRefListInput ? EntityReferenceListInputBase : T extends EntityReferenceListInputBase<any> ? EntityReferenceListInputBase : UnwrapEntityTarget<T> extends ReferenceListClass<any> ? EntityReferenceListInputBase : UnwrapEntityTarget<T>) : never;
307
+
308
+ declare type ResolveEntityRefs<T, CM extends ComponentMap> = T extends object ? {
309
+ [K in keyof T]: ResolveEntityRefValue<T[K], CM>;
310
+ } : ResolveEntityRefValue<T, CM>;
311
+
312
+ declare type ResolveEntityRefValue<T, CM extends ComponentMap> = [
313
+ T
314
+ ] extends [any] ? (UnwrapLazyEntityReferenceTarget<T> extends EntitySelfRef ? EntityFor<CM> : T extends ReferenceListClass<infer M> ? (UnwrapLazyEntityReferenceTarget<M> extends EntitySelfRef ? ReferenceListClass<EntityFor<CM>> : ReferenceListClass<UnwrapEntityTarget<M>>) : UnwrapEntityTarget<T>) : never;
315
+
316
+ declare type UnwrapEntityTarget<T> = UnwrapLazyEntityReferenceTarget<T> extends EntityConstructor<any, infer E, any, any> ? E : UnwrapLazyEntityReferenceTarget<T> extends EntityConstructor<any, any, any, any> ? InstanceType<UnwrapLazyEntityReferenceTarget<T>> : UnwrapLazyEntityReferenceTarget<T>;
317
+
318
+ declare type UnwrapLazyEntityReferenceTarget<T> = T extends LazyEntityReferenceTarget<infer U> ? U : T;
196
319
 
197
320
  declare type UppercaseFirstLetter<S> = S extends `${infer FirstLetter}${infer Rest}` ? `${Uppercase<FirstLetter>}${Rest}` : S;
198
321
 
199
322
  export declare type WithComponent<C extends {
200
323
  componentMap: any;
201
- }, KCM extends LowercaseFirstLetter<keyof C['componentMap']>> = Writeable<Omit<C, KCM> & {
202
- [K in KCM as LowercaseFirstLetter<K>]-?: Exclude<C[K extends keyof C ? K : never], undefined | null>;
324
+ }, KCM extends LowercaseFirstLetter<keyof C['componentMap']>> = Writeable<C & {
325
+ [K in KCM]-?: NonNullable<C[K extends keyof C ? K : never]>;
203
326
  }>;
204
327
 
205
328
  declare type Writeable<T> = {
package/build/mecs.js CHANGED
@@ -1,5 +1,43 @@
1
+ // @__NO_SIDE_EFFECTS__
2
+ function LazyReferenceType(fn, readOnly) {
3
+ return {
4
+ monomorph: {
5
+ serializedSize: 2,
6
+ fnInArray: [fn],
7
+ theClass: null
8
+ },
9
+ propertyType: 4,
10
+ optional: true,
11
+ defaultValue: null,
12
+ readOnly
13
+ };
14
+ }
15
+ // @__NO_SIDE_EFFECTS__
16
+ function LazyReferenceListType(fn, readOnly) {
17
+ return {
18
+ monomorph: {
19
+ serializedSize: 2,
20
+ // @ts-ignore
21
+ fnInArray: [fn],
22
+ theClass: null
23
+ },
24
+ propertyType: 7,
25
+ optional: true,
26
+ defaultValue: void 0,
27
+ readOnly
28
+ };
29
+ }
1
30
  function LazyComponent(componentFn) {
2
- return { fnInArray: [componentFn] };
31
+ return {
32
+ fnInArray: [componentFn],
33
+ componentType: void 0
34
+ };
35
+ }
36
+ function EntityReference(fn) {
37
+ return /* @__PURE__ */ LazyReferenceType(fn ?? (() => null));
38
+ }
39
+ function EntityReferenceList(fn) {
40
+ return /* @__PURE__ */ LazyReferenceListType(fn ?? (() => null));
3
41
  }
4
42
  function lowercaseFirstLetter(str) {
5
43
  return str.charAt(0).toLowerCase() + str.slice(1);
@@ -182,7 +220,7 @@ function getEntityClassCode(componentMap, queries, options) {
182
220
  return {
183
221
  next() {
184
222
  ${""}
185
- while (index < (${componentKey}Pool.length + ${componentKey}Pool.freeIndices.length) && (!${componentKey}PoolArray[index] || (${componentKey}PoolArray[index].version & 1) === 1)) {
223
+ while (index < (${componentKey}Pool.length + ${componentKey}Pool.freeIndices.length) && (!${componentKey}PoolArray[index] || (${componentKey}PoolArray[index].poolVersion & 1) === 1)) {
186
224
  index++;
187
225
  }
188
226
 
@@ -208,7 +246,7 @@ function getEntityClassCode(componentMap, queries, options) {
208
246
  (data, entity, componentKey, updateQueryMemberships) => {
209
247
  entity.componentFlags |= ${componentFlagValue}n;
210
248
  entity.${componentKey} = ${resolvedMonomorphClass}.create(${processedDataCode}, ${componentKey}Pool);
211
- ${componentKey}ComponentToEntity[entity.${componentKey}.index] = entity;
249
+ ${componentKey}ComponentToEntity[entity.${componentKey}.poolIndex] = entity;
212
250
 
213
251
  ${afterComponentAddedCode}
214
252
 
@@ -225,7 +263,7 @@ function getEntityClassCode(componentMap, queries, options) {
225
263
 
226
264
  ${beforeComponentRemovedCode}
227
265
 
228
- ${componentKey}ComponentToEntity[instance.index] = null;
266
+ ${componentKey}ComponentToEntity[instance.poolIndex] = null;
229
267
  instance.destroy();
230
268
 
231
269
  ${""}
@@ -323,12 +361,8 @@ function getEntityClassCode(componentMap, queries, options) {
323
361
  }
324
362
  }
325
363
  queryListenerCode += `
326
- const queryEntityAddedListeners${queryIndex} = [
327
- ${queryConfig.afterEntityAdded ? "queryMap." + queryName + ".afterEntityAdded" : ""}
328
- ];
329
- const queryEntityRemovedListeners${queryIndex} = [
330
- ${queryConfig.beforeEntityRemoved ? "queryMap." + queryName + ".beforeEntityRemoved" : ""}
331
- ];
364
+ const queryEntityAddedListeners${queryIndex} = [];
365
+ const queryEntityRemovedListeners${queryIndex} = [];
332
366
 
333
367
  function afterEntityAdded${queryIndex} (entity) {
334
368
  for (const listener of queryEntityAddedListeners${queryIndex}) {
@@ -499,7 +533,7 @@ function getEntityClassCode(componentMap, queries, options) {
499
533
  return {
500
534
  next() {
501
535
  ${""}
502
- while (index < (pool.length + pool.freeIndices.length) && (!array[index] || (array[index].version & 1) === 1)) {
536
+ while (index < (pool.length + pool.freeIndices.length) && (!array[index] || (array[index].poolVersion & 1) === 1)) {
503
537
  index++;
504
538
  }
505
539
 
@@ -544,7 +578,7 @@ function getEntityClassCode(componentMap, queries, options) {
544
578
  poolItem = poolArray[arrayOfIndices[index]];
545
579
 
546
580
  ${""}
547
- while (index < arrayOfIndices.length && (((poolItem.version & 1) === 1) || poolItem.version !== arrayOfVersions[index])) {
581
+ while (index < arrayOfIndices.length && (((poolItem.poolVersion & 1) === 1) || poolItem.poolVersion !== arrayOfVersions[index])) {
548
582
  if (fixDestroyed) {
549
583
  if (index < arrayOfIndices.length - 1) {
550
584
  arrayOfIndices[index] = arrayOfIndices[arrayOfIndices.length - 1];
@@ -576,9 +610,9 @@ function getEntityClassCode(componentMap, queries, options) {
576
610
  }
577
611
 
578
612
  const theClass = class {
579
- constructor(data, index = 0, updateQueryMemberships = true, pool = this.constructor.pool) {
580
- this.index = index;
581
- this.version = 0; ${""}
613
+ constructor(data, poolIndex = 0, updateQueryMemberships = true, pool = this.constructor.pool) {
614
+ this.poolIndex = poolIndex;
615
+ this.poolVersion = 0; ${""}
582
616
  this.pool = pool;
583
617
  this.queryIndices = new Array(${queryIndex}).fill(-1);
584
618
  this.componentFlags = 0n; ${""}
@@ -637,14 +671,14 @@ function getEntityClassCode(componentMap, queries, options) {
637
671
 
638
672
  destroy(updateQueryMemberships = true) {
639
673
  ${destroyCode}
640
- if (this.version & 1 === 1) {
674
+ if (this.poolVersion & 1 === 1) {
641
675
  ${""}
642
676
  return;
643
677
  }
644
- this.version++; // becomes odd, meaning deleted
678
+ this.poolVersion++; // becomes odd, meaning deleted
645
679
  if (this.pool) {
646
680
  this.pool.length--;
647
- this.pool.freeIndices.push(this.index);
681
+ this.pool.freeIndices.push(this.poolIndex);
648
682
  }
649
683
  }
650
684
 
@@ -691,8 +725,8 @@ function getEntityClassCode(componentMap, queries, options) {
691
725
  throw new Error("Could not add instance to reference list, instance does not belong to the same pool as the reference list");
692
726
  }
693
727
  const length = this.length;
694
- this.arrayOfIndices[length] = instance.index;
695
- this.arrayOfVersions[length] = instance.version;
728
+ this.arrayOfIndices[length] = instance.poolIndex;
729
+ this.arrayOfVersions[length] = instance.poolVersion;
696
730
  return length;
697
731
  }
698
732
 
@@ -703,7 +737,7 @@ function getEntityClassCode(componentMap, queries, options) {
703
737
  let poolItem = this.pool.array[this.arrayOfIndices[index]];
704
738
 
705
739
  if (fixDestroyed) {
706
- while (index < this.arrayOfIndices.length && ((poolItem.version & 1) === 1 || poolItem.version !== this.arrayOfVersions[index])) {
740
+ while (index < this.arrayOfIndices.length && ((poolItem.poolVersion & 1) === 1 || poolItem.poolVersion !== this.arrayOfVersions[index])) {
707
741
  if (index < this.arrayOfIndices.length - 1) {
708
742
  this.arrayOfIndices[index] = this.arrayOfIndices[this.arrayOfIndices.length - 1];
709
743
  this.arrayOfVersions[index] = this.arrayOfVersions[this.arrayOfVersions.length - 1];
@@ -717,7 +751,7 @@ function getEntityClassCode(componentMap, queries, options) {
717
751
  return null;
718
752
  }
719
753
  }
720
- } else if ((poolItem.version & 1) === 1 || poolItem.version !== this.arrayOfVersions[index]) {
754
+ } else if ((poolItem.poolVersion & 1) === 1 || poolItem.poolVersion !== this.arrayOfVersions[index]) {
721
755
  return null;
722
756
  }
723
757
 
@@ -726,7 +760,7 @@ function getEntityClassCode(componentMap, queries, options) {
726
760
 
727
761
  remove(instance) {
728
762
  for (let i = 0; i < this.arrayOfIndices.length; i++) {
729
- if (this.arrayOfIndices[i] === instance.index && this.arrayOfVersions[i] === instance.version) {
763
+ if (this.arrayOfIndices[i] === instance.poolIndex && this.arrayOfVersions[i] === instance.poolVersion) {
730
764
  if (i < this.arrayOfIndices.length - 1) {
731
765
  this.arrayOfIndices[i] = this.arrayOfIndices[this.arrayOfIndices.length - 1];
732
766
  this.arrayOfVersions[i] = this.arrayOfVersions[this.arrayOfVersions.length - 1];
@@ -757,8 +791,8 @@ function getEntityClassCode(componentMap, queries, options) {
757
791
  create(data) {
758
792
  const instance = this.classConstructor.create(data, this.pool);
759
793
  const length = this.length;
760
- this.arrayOfIndices[length] = instance.index;
761
- this.arrayOfVersions[length] = instance.version;
794
+ this.arrayOfIndices[length] = instance.poolIndex;
795
+ this.arrayOfVersions[length] = instance.poolVersion;
762
796
  return instance;
763
797
  }
764
798
 
@@ -784,7 +818,23 @@ function getEntityClassCode(componentMap, queries, options) {
784
818
  if (!this.dynamicPoolInstance) {
785
819
  this.dynamicPoolInstance = new this.Pool();
786
820
  }
787
- return this.dynamicPoolInstance;
821
+ if (!this.dynamicPoolCallable) {
822
+ const poolInstance = this.dynamicPoolInstance;
823
+ const callable = new Proxy(function () { return poolInstance; }, {
824
+ get(_target, prop) {
825
+ if (prop in poolInstance) {
826
+ return poolInstance[prop];
827
+ }
828
+ return undefined;
829
+ },
830
+ set(_target, prop, value) {
831
+ poolInstance[prop] = value;
832
+ return true;
833
+ },
834
+ });
835
+ this.dynamicPoolCallable = callable;
836
+ }
837
+ return this.dynamicPoolCallable;
788
838
  }
789
839
 
790
840
  static get Pool() {
@@ -829,8 +879,8 @@ function getEntityClassCode(componentMap, queries, options) {
829
879
  array[offset++] = -1;
830
880
  continue;
831
881
  }
832
- array[offset++] = instance.version;
833
- if (instance.version & 1) {${""}
882
+ array[offset++] = instance.poolVersion;
883
+ if (instance.poolVersion & 1) {${""}
834
884
  continue;
835
885
  }
836
886
  offset = instance.toArray(array, offset, skipPoolReferences);
@@ -857,11 +907,11 @@ function getEntityClassCode(componentMap, queries, options) {
857
907
 
858
908
 
859
909
  for (let i = 0; i < usedLength; i++) {
860
- const version = arrayOfData[offset++];
910
+ const poolVersion = arrayOfData[offset++];
861
911
 
862
- if (version & 1) {
912
+ if (poolVersion & 1) {
863
913
  if (this.array[i]) {
864
- this.array[i].version = version;
914
+ this.array[i].poolVersion = poolVersion;
865
915
  }
866
916
  continue;
867
917
  }
@@ -872,7 +922,7 @@ function getEntityClassCode(componentMap, queries, options) {
872
922
  this.array[i] = new classConstructor(undefined, i, this);
873
923
  }
874
924
 
875
- this.array[i].version = version;
925
+ this.array[i].poolVersion = poolVersion;
876
926
 
877
927
  offset = this.array[i].fromArray(arrayOfData, references, offset);
878
928
  }
@@ -897,11 +947,11 @@ function getEntityClassCode(componentMap, queries, options) {
897
947
  }
898
948
 
899
949
  for (let i = 0; i < usedLength; i++) {
900
- const version = arrayOfData[offset++];
950
+ const poolVersion = arrayOfData[offset++];
901
951
 
902
- if (version & 1) {
952
+ if (poolVersion & 1) {
903
953
  if (this.array[i]) {
904
- this.array[i].version = version;
954
+ this.array[i].poolVersion = poolVersion;
905
955
  }
906
956
  continue;
907
957
  }
@@ -912,7 +962,7 @@ function getEntityClassCode(componentMap, queries, options) {
912
962
  this.array[i] = new classConstructor(undefined, i, this);
913
963
  }
914
964
 
915
- this.array[i].version = version;
965
+ this.array[i].poolVersion = poolVersion;
916
966
 
917
967
  offset = this.array[i].fromArrayNoReferences(arrayOfData, offset);
918
968
  }
@@ -937,11 +987,11 @@ function getEntityClassCode(componentMap, queries, options) {
937
987
  }
938
988
 
939
989
  for (let i = 0; i < usedLength; i++) {
940
- const version = arrayOfData[offset++];
990
+ const poolVersion = arrayOfData[offset++];
941
991
 
942
- if (version & 1) {
992
+ if (poolVersion & 1) {
943
993
  if (this.array[i]) {
944
- this.array[i].version = version;
994
+ this.array[i].poolVersion = poolVersion;
945
995
  }
946
996
  continue;
947
997
  }
@@ -952,7 +1002,7 @@ function getEntityClassCode(componentMap, queries, options) {
952
1002
  this.array[i] = new classConstructor(undefined, i, this);
953
1003
  }
954
1004
 
955
- this.array[i].version = version;
1005
+ this.array[i].poolVersion = poolVersion;
956
1006
 
957
1007
  offset = this.array[i].fromArrayOnlyReferences(arrayOfData, references, offset);
958
1008
  }
@@ -967,20 +1017,46 @@ function getEntityClassCode(componentMap, queries, options) {
967
1017
 
968
1018
  }
969
1019
 
970
- theClass.queries = queries;
1020
+ const queriesCallable = new Proxy(function () { return queriesCallable; }, {
1021
+ get(_target, prop) {
1022
+ if (prop in queries) {
1023
+ return queries[prop];
1024
+ }
1025
+ return undefined;
1026
+ },
1027
+ set(_target, prop, value) {
1028
+ queries[prop] = value;
1029
+ return true;
1030
+ },
1031
+ });
1032
+ theClass.queries = queriesCallable;
971
1033
  theClass.componentToEntity = componentToEntity;
972
1034
  theClass.componentIndices = componentIndices;
973
1035
  theClass.components = componentPools;
974
- theClass.componentsWithEntities = {
1036
+
1037
+ const componentsWithEntities = {
975
1038
  ${componentWithEntityIteratorFunctionsCode}
976
1039
  };
1040
+ const componentsWithEntityCallable = new Proxy(function () { return componentsWithEntityCallable; }, {
1041
+ get(_target, prop) {
1042
+ if (prop in componentsWithEntities) {
1043
+ return componentsWithEntities[prop];
1044
+ }
1045
+ return undefined;
1046
+ },
1047
+ set(_target, prop, value) {
1048
+ componentsWithEntities[prop] = value;
1049
+ return true;
1050
+ },
1051
+ });
1052
+ theClass.componentsWithEntities = componentsWithEntityCallable;
977
1053
 
978
1054
  theClass.create = function(data, updateQueryMemberships = true, pool = this.pool) {
979
1055
  if (pool.freeIndices.length) {
980
1056
  const index = pool.freeIndices.pop();
981
1057
  pool.length++;
982
1058
  const instance = pool.array[index];
983
- instance.version++; ${""}
1059
+ instance.poolVersion++; ${""}
984
1060
  instance.reset(data, updateQueryMemberships, pool);
985
1061
  return instance;
986
1062
  } else {
@@ -993,12 +1069,12 @@ function getEntityClassCode(componentMap, queries, options) {
993
1069
 
994
1070
  theClass.Reference = class {
995
1071
  constructor(reference) {
996
- if (reference && !(reference.version & 1)) {
1072
+ if (reference && !(reference.poolVersion & 1)) {
997
1073
  this.reference = reference;
998
- this.version = reference.version;
1074
+ this.poolVersion = reference.poolVersion;
999
1075
  } else {
1000
1076
  this.reference = null;
1001
- this.version = -1;
1077
+ this.poolVersion = -1;
1002
1078
  }
1003
1079
  }
1004
1080
  }
@@ -1014,6 +1090,8 @@ function createEntityClass(options) {
1014
1090
  return (componentMap, queries) => new Function("componentMap", "queryMap", getEntityClassCode(componentMap, queries, options))(componentMap, queries);
1015
1091
  }
1016
1092
  export {
1093
+ EntityReference,
1094
+ EntityReferenceList,
1017
1095
  LazyComponent,
1018
1096
  createEntityClass,
1019
1097
  getEntityClassCode
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@perplexdotgg/mecs",
3
- "version": "0.4.2",
3
+ "version": "0.5.1",
4
4
  "description": "MECS - Monomorph ECS - A high-performance Entity Component System for TypeScript and JavaScript projects, designed for games and simulations.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -28,7 +28,7 @@
28
28
  "bench": "vitest bench"
29
29
  },
30
30
  "peerDependencies": {
31
- "monomorph": "^1.5.6"
31
+ "monomorph": "^2.1.0"
32
32
  },
33
33
  "peerDependenciesMeta": {
34
34
  "monomorph": {
@@ -37,7 +37,7 @@
37
37
  },
38
38
  "devDependencies": {
39
39
  "@types/node": "^25.0.1",
40
- "monomorph": "^1.5.6",
40
+ "monomorph": "^2.1.0",
41
41
  "ts-node": "^10.9.2",
42
42
  "tslib": "^2.8.1",
43
43
  "typescript": "^5.9.3",