@codehz/ecs 0.1.8 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/archetype.d.ts CHANGED
@@ -1,5 +1,9 @@
1
1
  import type { EntityId, WildcardRelationId } from "./entity";
2
- import type { ComponentTuple } from "./types";
2
+ import { type ComponentTuple, type ComponentType } from "./types";
3
+ /**
4
+ * Special value to represent missing component data
5
+ */
6
+ export declare const MISSING_COMPONENT: unique symbol;
3
7
  /**
4
8
  * Archetype class for ECS architecture
5
9
  * Represents a group of entities that share the same set of components
@@ -49,6 +53,20 @@ export declare class Archetype {
49
53
  * @param componentData Map of component type to component data
50
54
  */
51
55
  addEntity(entityId: EntityId, componentData: Map<EntityId<any>, any>): void;
56
+ /**
57
+ * Get all component data for a specific entity
58
+ * @param entityId The entity to get data for
59
+ * @returns Map of component type to component data
60
+ */
61
+ getEntity(entityId: EntityId): Map<EntityId<any>, any> | undefined;
62
+ /**
63
+ * Dump all entities and their component data in this archetype
64
+ * @returns Array of objects with entity and component data
65
+ */
66
+ dump(): Array<{
67
+ entity: EntityId;
68
+ components: Map<EntityId<any>, any>;
69
+ }>;
52
70
  /**
53
71
  * Remove an entity from this archetype
54
72
  * @param entityId The entity to remove
@@ -89,13 +107,19 @@ export declare class Archetype {
89
107
  * @param componentType The component type
90
108
  */
91
109
  getComponentData<T>(componentType: EntityId<T>): T[];
110
+ /**
111
+ * Get optional component data for all entities of a specific component type
112
+ * @param componentType The component type
113
+ * @returns An array of component data or undefined if not present
114
+ */
115
+ getOptionalComponentData<T>(componentType: EntityId<T>): T[] | undefined;
92
116
  /**
93
117
  * Get entities with their component data for specified component types
94
118
  * Optimized for bulk component access with pre-computed indices
95
119
  * @param componentTypes Array of component types to retrieve
96
120
  * @returns Array of objects with entity and component data
97
121
  */
98
- getEntitiesWithComponents<const T extends readonly EntityId<any>[]>(componentTypes: T): Array<{
122
+ getEntitiesWithComponents<const T extends readonly ComponentType<any>[]>(componentTypes: T): Array<{
99
123
  entity: EntityId;
100
124
  components: ComponentTuple<T>;
101
125
  }>;
@@ -105,7 +129,7 @@ export declare class Archetype {
105
129
  * @param componentTypes Array of component types to retrieve
106
130
  * @param callback Function called for each entity with its components
107
131
  */
108
- forEachWithComponents<const T extends readonly EntityId<any>[]>(componentTypes: T, callback: (entity: EntityId, ...components: ComponentTuple<T>) => void): void;
132
+ forEachWithComponents<const T extends readonly ComponentType<any>[]>(componentTypes: T, callback: (entity: EntityId, ...components: ComponentTuple<T>) => void): void;
109
133
  /**
110
134
  * Iterate over all entities with their component data
111
135
  * @param callback Function called for each entity with its component data
package/changeset.d.ts CHANGED
@@ -29,4 +29,10 @@ export declare class ComponentChangeset {
29
29
  * Apply the changeset to existing components and return the final state
30
30
  */
31
31
  applyTo(existingComponents: Map<EntityId<any>, any>): Map<EntityId<any>, any>;
32
+ /**
33
+ * Get the final component types after applying the changeset
34
+ * @param existingComponentTypes - The current component types on the entity
35
+ * @returns The final component types or undefined if no changes
36
+ */
37
+ getFinalComponentTypes(existingComponentTypes: EntityId<any>[]): EntityId<any>[] | undefined;
32
38
  }
package/entity.d.ts CHANGED
@@ -167,7 +167,22 @@ export declare class ComponentIdAllocator {
167
167
  hasAvailableIds(): boolean;
168
168
  }
169
169
  /**
170
- * Allocate a new component ID from the global allocator
170
+ * Allocate a new component ID from the global allocator.
171
+ * Optionally register a name for the component.
172
+ * The name is only for serialization/debugging and does not affect base functionality.
173
+ * @param name Optional name for the component
174
+ * @returns The allocated component ID
171
175
  */
172
- export declare function component<T = void>(): ComponentId<T>;
176
+ export declare function component<T = void>(name?: string): ComponentId<T>;
177
+ /**
178
+ * Get a component ID by its registered name
179
+ * @param name The component name
180
+ * @returns The component ID if found, undefined otherwise
181
+ */
182
+ export declare function getComponentIdByName(name: string): ComponentId<any> | undefined;
183
+ /** Get a component name by its ID
184
+ * @param id The component ID
185
+ * @returns The component name if found, undefined otherwise
186
+ */
187
+ export declare function getComponentNameById(id: ComponentId<any>): string | undefined;
173
188
  export {};
package/index.js CHANGED
@@ -214,9 +214,30 @@ class ComponentIdAllocator {
214
214
  }
215
215
  }
216
216
  var globalComponentIdAllocator = new ComponentIdAllocator;
217
- function component() {
218
- return globalComponentIdAllocator.allocate();
217
+ var ComponentNames = new Map;
218
+ var ComponentIdForNames = new Map;
219
+ function component(name) {
220
+ const id = globalComponentIdAllocator.allocate();
221
+ if (name) {
222
+ if (ComponentIdForNames.has(name)) {
223
+ throw new Error(`Component name "${name}" is already registered`);
224
+ }
225
+ ComponentNames.set(id, name);
226
+ ComponentIdForNames.set(name, id);
227
+ }
228
+ return id;
229
+ }
230
+ function getComponentIdByName(name) {
231
+ return ComponentIdForNames.get(name);
219
232
  }
233
+ function getComponentNameById(id) {
234
+ return ComponentNames.get(id);
235
+ }
236
+ // src/types.ts
237
+ function isOptionalEntityId(type) {
238
+ return typeof type === "object" && type !== null && "optional" in type;
239
+ }
240
+
220
241
  // src/utils.ts
221
242
  function getOrComputeCache(cache, key, compute) {
222
243
  let value = cache.get(key);
@@ -272,6 +293,33 @@ class Archetype {
272
293
  this.getComponentData(componentType).push(data === undefined ? MISSING_COMPONENT : data);
273
294
  }
274
295
  }
296
+ getEntity(entityId) {
297
+ const index = this.entityToIndex.get(entityId);
298
+ if (index === undefined) {
299
+ return;
300
+ }
301
+ const entityData = new Map;
302
+ for (const componentType of this.componentTypes) {
303
+ const dataArray = this.getComponentData(componentType);
304
+ const data = dataArray[index];
305
+ entityData.set(componentType, data === MISSING_COMPONENT ? undefined : data);
306
+ }
307
+ return entityData;
308
+ }
309
+ dump() {
310
+ const result = [];
311
+ for (let i = 0;i < this.entities.length; i++) {
312
+ const entity = this.entities[i];
313
+ const components = new Map;
314
+ for (const componentType of this.componentTypes) {
315
+ const dataArray = this.getComponentData(componentType);
316
+ const data = dataArray[i];
317
+ components.set(componentType, data === MISSING_COMPONENT ? undefined : data);
318
+ }
319
+ result.push({ entity, components });
320
+ }
321
+ return result;
322
+ }
275
323
  removeEntity(entityId) {
276
324
  const index = this.entityToIndex.get(entityId);
277
325
  if (index === undefined) {
@@ -348,6 +396,9 @@ class Archetype {
348
396
  }
349
397
  return data;
350
398
  }
399
+ getOptionalComponentData(componentType) {
400
+ return this.componentData.get(componentType);
401
+ }
351
402
  getEntitiesWithComponents(componentTypes) {
352
403
  const result = [];
353
404
  this.forEachWithComponents(componentTypes, (entity, ...components) => {
@@ -356,9 +407,14 @@ class Archetype {
356
407
  return result;
357
408
  }
358
409
  forEachWithComponents(componentTypes, callback) {
359
- const cacheKey = componentTypes.map((id) => id.toString()).join(",");
410
+ const cacheKey = componentTypes.map((id) => isOptionalEntityId(id) ? `opt(${id.optional})` : `${id}`).join(",");
360
411
  const componentDataSources = getOrComputeCache(this.componentDataSourcesCache, cacheKey, () => {
361
412
  return componentTypes.map((compType) => {
413
+ let optional = false;
414
+ if (isOptionalEntityId(compType)) {
415
+ compType = compType.optional;
416
+ optional = true;
417
+ }
362
418
  const detailedType = getDetailedIdType(compType);
363
419
  if (detailedType.type === "wildcard-relation") {
364
420
  const componentId = detailedType.componentId;
@@ -368,17 +424,29 @@ class Archetype {
368
424
  return false;
369
425
  return detailedCt.componentId === componentId;
370
426
  });
371
- return matchingRelations;
427
+ return optional ? matchingRelations.length > 0 ? matchingRelations : undefined : matchingRelations;
372
428
  } else {
373
- return this.getComponentData(compType);
429
+ return optional ? this.getOptionalComponentData(compType) : this.getComponentData(compType);
374
430
  }
375
431
  });
376
432
  });
377
433
  for (let entityIndex = 0;entityIndex < this.entities.length; entityIndex++) {
378
434
  const entity = this.entities[entityIndex];
379
435
  const components = componentDataSources.map((dataSource, i) => {
380
- const compType = componentTypes[i];
436
+ let compType = componentTypes[i];
437
+ let optional = false;
438
+ if (isOptionalEntityId(compType)) {
439
+ compType = compType.optional;
440
+ optional = true;
441
+ }
381
442
  if (getIdType(compType) === "wildcard-relation") {
443
+ if (dataSource === undefined) {
444
+ if (optional) {
445
+ return;
446
+ } else {
447
+ throw new Error(`No matching relations found for mandatory wildcard relation component type`);
448
+ }
449
+ }
382
450
  const matchingRelations = dataSource;
383
451
  const relations = [];
384
452
  for (const relType of matchingRelations) {
@@ -387,11 +455,19 @@ class Archetype {
387
455
  const decodedRel = decodeRelationId(relType);
388
456
  relations.push([decodedRel.targetId, data === MISSING_COMPONENT ? undefined : data]);
389
457
  }
390
- return relations;
458
+ return optional ? { value: relations } : relations;
391
459
  } else {
460
+ if (dataSource === undefined) {
461
+ if (optional) {
462
+ return;
463
+ } else {
464
+ throw new Error(`No matching relations found for mandatory wildcard relation component type`);
465
+ }
466
+ }
392
467
  const dataArray = dataSource;
393
- const data = dataArray ? dataArray[entityIndex] : undefined;
394
- return data === MISSING_COMPONENT ? undefined : data;
468
+ const data = dataArray[entityIndex];
469
+ const result = data === MISSING_COMPONENT ? undefined : data;
470
+ return optional ? { value: result } : result;
395
471
  }
396
472
  });
397
473
  callback(entity, ...components);
@@ -447,6 +523,25 @@ class ComponentChangeset {
447
523
  }
448
524
  return existingComponents;
449
525
  }
526
+ getFinalComponentTypes(existingComponentTypes) {
527
+ const finalComponentTypes = new Set(existingComponentTypes);
528
+ let changed = false;
529
+ for (const componentType of this.removes) {
530
+ if (!finalComponentTypes.has(componentType)) {
531
+ continue;
532
+ }
533
+ changed = true;
534
+ finalComponentTypes.delete(componentType);
535
+ }
536
+ for (const componentType of this.adds.keys()) {
537
+ if (finalComponentTypes.has(componentType)) {
538
+ continue;
539
+ }
540
+ changed = true;
541
+ finalComponentTypes.add(componentType);
542
+ }
543
+ return changed ? Array.from(finalComponentTypes) : undefined;
544
+ }
450
545
  }
451
546
 
452
547
  // src/command-buffer.ts
@@ -531,17 +626,15 @@ function matchesFilter(archetype, filter) {
531
626
 
532
627
  // src/query.ts
533
628
  class Query {
534
- key;
535
629
  world;
536
630
  componentTypes;
537
631
  filter;
538
632
  cachedArchetypes = [];
539
633
  isDisposed = false;
540
- constructor(world, componentTypes, filter = {}, key) {
634
+ constructor(world, componentTypes, filter = {}) {
541
635
  this.world = world;
542
636
  this.componentTypes = [...componentTypes].sort((a, b) => a - b);
543
637
  this.filter = filter;
544
- this.key = key ?? `${this.componentTypes.join(",")}|`;
545
638
  this.updateCache();
546
639
  world._registerQuery(this);
547
640
  }
@@ -711,8 +804,27 @@ class World {
711
804
  const componentMap = new Map;
712
805
  const componentTypes = [];
713
806
  for (const componentEntry of componentsArray) {
714
- componentMap.set(componentEntry.type, componentEntry.value);
715
- componentTypes.push(componentEntry.type);
807
+ const componentTypeRaw = componentEntry.type;
808
+ let componentType;
809
+ if (typeof componentTypeRaw === "number") {
810
+ componentType = componentTypeRaw;
811
+ } else if (typeof componentTypeRaw === "string") {
812
+ const compId = getComponentIdByName(componentTypeRaw);
813
+ if (compId === undefined) {
814
+ throw new Error(`Unknown component name in snapshot: ${componentTypeRaw}`);
815
+ }
816
+ componentType = compId;
817
+ } else if (typeof componentTypeRaw === "object" && componentTypeRaw !== null && typeof componentTypeRaw.component === "string" && typeof componentTypeRaw.target === "number") {
818
+ const compId = getComponentIdByName(componentTypeRaw.component);
819
+ if (compId === undefined) {
820
+ throw new Error(`Unknown component name in snapshot: ${componentTypeRaw.component}`);
821
+ }
822
+ componentType = relation(compId, componentTypeRaw.target);
823
+ } else {
824
+ throw new Error(`Invalid component type in snapshot: ${JSON.stringify(componentTypeRaw)}`);
825
+ }
826
+ componentMap.set(componentType, componentEntry.value);
827
+ componentTypes.push(componentType);
716
828
  }
717
829
  const archetype = this.ensureArchetype(componentTypes);
718
830
  archetype.addEntity(entityId, componentMap);
@@ -862,7 +974,7 @@ class World {
862
974
  cached.refCount++;
863
975
  return cached.query;
864
976
  }
865
- const query = new Query(this, sortedTypes, filter, key);
977
+ const query = new Query(this, sortedTypes, filter);
866
978
  this.queryCache.set(key, { query, refCount: 1 });
867
979
  return query;
868
980
  }
@@ -876,27 +988,17 @@ class World {
876
988
  }
877
989
  }
878
990
  releaseQuery(query) {
879
- const key = query.key;
880
991
  for (const [k, v] of this.queryCache.entries()) {
881
992
  if (v.query === query) {
882
993
  v.refCount--;
883
994
  if (v.refCount <= 0) {
884
995
  this.queryCache.delete(k);
996
+ this._unregisterQuery(query);
885
997
  v.query._disposeInternal();
886
998
  }
887
999
  return;
888
1000
  }
889
1001
  }
890
- const entry = this.queryCache.get(key);
891
- if (!entry) {
892
- this._unregisterQuery(query);
893
- return;
894
- }
895
- entry.refCount--;
896
- if (entry.refCount <= 0) {
897
- this.queryCache.delete(key);
898
- entry.query._disposeInternal();
899
- }
900
1002
  }
901
1003
  getMatchingArchetypes(componentTypes) {
902
1004
  if (componentTypes.length === 0) {
@@ -977,11 +1079,6 @@ class World {
977
1079
  if (!currentArchetype) {
978
1080
  return changeset;
979
1081
  }
980
- const currentComponents = new Map;
981
- for (const componentType of currentArchetype.componentTypes) {
982
- const componentData = currentArchetype.get(entityId, componentType);
983
- currentComponents.set(componentType, componentData);
984
- }
985
1082
  for (const command of commands) {
986
1083
  switch (command.type) {
987
1084
  case "set":
@@ -1018,13 +1115,11 @@ class World {
1018
1115
  break;
1019
1116
  }
1020
1117
  }
1021
- const finalComponents = changeset.applyTo(currentComponents);
1022
- const currentComponentTypes = currentArchetype.componentTypes;
1023
- const needsArchetypeChange = finalComponents.size !== currentComponentTypes.length || !currentComponentTypes.every((type) => finalComponents.has(type));
1024
- if (needsArchetypeChange) {
1025
- const newArchetype = this.ensureArchetype(finalComponents.keys());
1026
- currentArchetype.removeEntity(entityId);
1027
- newArchetype.addEntity(entityId, finalComponents);
1118
+ const finalComponentTypes = changeset.getFinalComponentTypes(currentArchetype.componentTypes);
1119
+ if (finalComponentTypes) {
1120
+ const newArchetype = this.ensureArchetype(finalComponentTypes);
1121
+ const currentComponents = currentArchetype.removeEntity(entityId);
1122
+ newArchetype.addEntity(entityId, changeset.applyTo(currentComponents));
1028
1123
  this.entityToArchetype.set(entityId, newArchetype);
1029
1124
  } else {
1030
1125
  for (const [componentType, component2] of changeset.adds) {
@@ -1163,13 +1258,31 @@ class World {
1163
1258
  }
1164
1259
  serialize() {
1165
1260
  const entities = [];
1166
- for (const [entityId, archetype] of this.entityToArchetype.entries()) {
1167
- const compEntries = [];
1168
- for (const compType of archetype.componentTypes) {
1169
- const value = archetype.get(entityId, compType);
1170
- compEntries.push({ type: compType, value });
1261
+ for (const archetype of this.archetypes) {
1262
+ const dumpedEntities = archetype.dump();
1263
+ for (const { entity, components } of dumpedEntities) {
1264
+ entities.push({
1265
+ id: entity,
1266
+ components: Array.from(components.entries()).map(([rawType, value]) => {
1267
+ const detailedType = getDetailedIdType(rawType);
1268
+ let type = rawType;
1269
+ let componentName;
1270
+ switch (detailedType.type) {
1271
+ case "component":
1272
+ type = getComponentNameById(rawType) || rawType;
1273
+ break;
1274
+ case "entity-relation":
1275
+ case "component-relation":
1276
+ componentName = getComponentNameById(detailedType.componentId);
1277
+ if (componentName) {
1278
+ type = { component: componentName, target: detailedType.targetId };
1279
+ }
1280
+ break;
1281
+ }
1282
+ return { type, value: value === MISSING_COMPONENT ? undefined : value };
1283
+ })
1284
+ });
1171
1285
  }
1172
- entities.push({ id: entityId, components: compEntries });
1173
1286
  }
1174
1287
  return {
1175
1288
  version: 1,
@@ -1183,11 +1296,14 @@ export {
1183
1296
  relation,
1184
1297
  isWildcardRelationId,
1185
1298
  isRelationId,
1299
+ isOptionalEntityId,
1186
1300
  isEntityId,
1187
1301
  isComponentId,
1188
1302
  inspectEntityId,
1189
1303
  getIdType,
1190
1304
  getDetailedIdType,
1305
+ getComponentNameById,
1306
+ getComponentIdByName,
1191
1307
  decodeRelationId,
1192
1308
  createEntityId,
1193
1309
  createComponentId,
@@ -1197,6 +1313,7 @@ export {
1197
1313
  SystemScheduler,
1198
1314
  RELATION_SHIFT,
1199
1315
  Query,
1316
+ MISSING_COMPONENT,
1200
1317
  INVALID_COMPONENT_ID,
1201
1318
  EntityIdManager,
1202
1319
  ENTITY_ID_START,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codehz/ecs",
3
- "version": "0.1.8",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "main": "./index.js",
6
6
  "types": "./index.d.ts",
package/query.d.ts CHANGED
@@ -1,19 +1,18 @@
1
1
  import { Archetype } from "./archetype";
2
2
  import type { EntityId } from "./entity";
3
3
  import { type QueryFilter } from "./query-filter";
4
- import type { ComponentTuple } from "./types";
4
+ import type { ComponentTuple, ComponentType } from "./types";
5
5
  import type { World } from "./world";
6
6
  /**
7
7
  * Query class for efficient entity queries with cached archetypes
8
8
  */
9
9
  export declare class Query {
10
- readonly key: string;
11
10
  private world;
12
11
  private componentTypes;
13
12
  private filter;
14
13
  private cachedArchetypes;
15
14
  private isDisposed;
16
- constructor(world: World<any[]>, componentTypes: EntityId<any>[], filter?: QueryFilter, key?: string);
15
+ constructor(world: World<any[]>, componentTypes: EntityId<any>[], filter?: QueryFilter);
17
16
  /**
18
17
  * Get all entities matching the query
19
18
  */
@@ -23,7 +22,7 @@ export declare class Query {
23
22
  * @param componentTypes Array of component types to retrieve
24
23
  * @returns Array of objects with entity and component data
25
24
  */
26
- getEntitiesWithComponents<const T extends readonly EntityId<any>[]>(componentTypes: T): Array<{
25
+ getEntitiesWithComponents<const T extends readonly ComponentType<any>[]>(componentTypes: T): Array<{
27
26
  entity: EntityId;
28
27
  components: ComponentTuple<T>;
29
28
  }>;
@@ -32,7 +31,7 @@ export declare class Query {
32
31
  * @param componentTypes Array of component types to retrieve
33
32
  * @param callback Function called for each entity with its components
34
33
  */
35
- forEach<const T extends readonly EntityId<any>[]>(componentTypes: T, callback: (entity: EntityId, ...components: ComponentTuple<T>) => void): void;
34
+ forEach<const T extends readonly ComponentType<any>[]>(componentTypes: T, callback: (entity: EntityId, ...components: ComponentTuple<T>) => void): void;
36
35
  /**
37
36
  * Get component data arrays for all matching entities
38
37
  * @param componentType The component type to retrieve
package/types.d.ts CHANGED
@@ -12,9 +12,19 @@ export interface LifecycleHook<T = unknown> {
12
12
  */
13
13
  onRemoved?: (entityId: EntityId, componentType: EntityId<T>) => void;
14
14
  }
15
+ export type ComponentType<T> = EntityId<T> | OptionalEntityId<T>;
16
+ export type OptionalEntityId<T> = {
17
+ optional: EntityId<T>;
18
+ };
19
+ export declare function isOptionalEntityId<T>(type: ComponentType<T>): type is OptionalEntityId<T>;
20
+ export type ComponentTypeToData<T> = T extends {
21
+ optional: infer U;
22
+ } ? {
23
+ value: ComponentTypeToData<U>;
24
+ } | undefined : T extends WildcardRelationId<infer U> ? [EntityId<unknown>, U][] : T extends EntityId<infer U> ? U : never;
15
25
  /**
16
26
  * Type helper for component tuples extracted from EntityId array
17
27
  */
18
- export type ComponentTuple<T extends readonly EntityId<any>[]> = {
19
- readonly [K in keyof T]: T[K] extends WildcardRelationId<infer U> ? [EntityId<unknown>, U][] : T[K] extends EntityId<infer U> ? U : never;
28
+ export type ComponentTuple<T extends readonly ComponentType<any>[]> = {
29
+ readonly [K in keyof T]: ComponentTypeToData<T[K]>;
20
30
  };
package/world.d.ts CHANGED
@@ -40,7 +40,7 @@ export declare class World<UpdateParams extends any[] = []> {
40
40
  * If an optional snapshot object is provided (previously produced by `world.serialize()`),
41
41
  * the world will be restored from that snapshot. The snapshot may contain non-JSON values.
42
42
  */
43
- constructor(snapshot?: any);
43
+ constructor(snapshot?: SerializedWorld);
44
44
  /**
45
45
  * Generate a signature string for component types array
46
46
  * @returns A string signature for the component types
@@ -192,5 +192,22 @@ export declare class World<UpdateParams extends any[] = []> {
192
192
  * This returns an in-memory structure and does not perform JSON stringification.
193
193
  * Component values are stored as-is (they may be non-JSON-serializable).
194
194
  */
195
- serialize(): any;
195
+ serialize(): SerializedWorld;
196
196
  }
197
+ export type SerializedWorld = {
198
+ version: number;
199
+ entityManager: any;
200
+ exclusiveComponents: EntityId[];
201
+ entities: SerializedEntity[];
202
+ };
203
+ export type SerializedEntity = {
204
+ id: number;
205
+ components: SerializedComponent[];
206
+ };
207
+ export type SerializedComponent = {
208
+ type: number | string | {
209
+ component: string;
210
+ target: number;
211
+ };
212
+ value: any;
213
+ };