@vived/core 2.0.0 → 2.0.2

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.
@@ -72,12 +72,11 @@ export function makeExampleRepo(appObject) {
72
72
  class ExampleRepoImp extends ExampleRepo {
73
73
  /**
74
74
  * Factory implementation for creating ExampleEntity instances
75
- * @param id The ID for the entity's AppObject
75
+ * @param appObject The AppObject for the entity
76
76
  * @returns A newly created ExampleEntity
77
77
  */
78
- entityFactory(id) {
79
- const ao = this.appObjects.getOrCreate(id);
80
- return makeExampleEntity(ao);
78
+ entityFactory(appObject) {
79
+ return makeExampleEntity(appObject);
81
80
  }
82
81
  /**
83
82
  * Deletes an ExampleEntity from the repository by its AppObject ID
@@ -1 +1 @@
1
- {"version":3,"file":"ExampleRepo.js","sourceRoot":"","sources":["../../../../src/ExampleFeature/Entities/ExampleRepo.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAa,mBAAmB,EAAiB,MAAM,iBAAiB,CAAC;AAChF,OAAO,EAAiB,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAKnE;;;GAGG;AACH,MAAM,OAAgB,WAAY,SAAQ,mBAAkC;IAO1E;;;;OAIG;IACH,MAAM,CAAC,GAAG,CAAC,MAAiB;QAC1B,OAAO,MAAM,CAAC,YAAY,CAAc,IAAI,CAAC,IAAI,CAAC,CAAC;IACrD,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,OAAO,CACZ,EAAU,EACV,UAAyB;QAEzB,OAAO,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,YAAY,CAAc,IAAI,CAAC,IAAI,CAAC,CAAC;IAClE,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,YAAY,CAAC,SAAoB;QACtC,MAAM,QAAQ,GAAG,SAAS,CAAC,YAAY,CAAc,WAAW,CAAC,IAAI,CAAC,CAAC;QACvE,IAAI,QAAQ,EAAE,CAAC;YACb,OAAO,QAAQ,CAAC;QAClB,CAAC;aAAM,CAAC;YACN,OAAO,eAAe,CAAC,SAAS,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;;AAxCD,gDAAgD;AAChC,gBAAI,GAAG,iBAAiB,CAAC;AA0C3C;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,SAAoB;IAClD,OAAO,IAAI,cAAc,CAAC,SAAS,CAAC,CAAC;AACvC,CAAC;AAED;;;GAGG;AACH,MAAM,cAAe,SAAQ,WAAW;IACtC;;;;OAIG;IACH,aAAa,CAAC,EAAU;QACtB,MAAM,EAAE,GAAG,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;QAC3C,OAAO,iBAAiB,CAAC,EAAE,CAAC,CAAC;IAC/B,CAAC;IAED;;;OAGG;IACH,mBAAmB,CAAC,EAAU;QAC5B,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAChC,IAAI,CAAC,MAAM;YAAE,OAAO;QAEpB,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC;QAC3B,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;IACtB,CAAC;IAED,YAAY,SAAoB;QAC9B,KAAK,CAAC,SAAS,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC;IACrC,CAAC;CACF","sourcesContent":["/**\r\n * ExampleRepo.ts\r\n *\r\n * This file demonstrates how to implement a repository to manage collections of entities.\r\n * Repositories are responsible for creating, retrieving, and deleting entities.\r\n *\r\n * Key concepts:\r\n * - Repositories extend AppObjectEntityRepo<T> where T is the entity type\r\n * - They provide methods to create and delete entities\r\n * - They manage collections of entities and provide access to them\r\n * - They can use custom entity factories to create specialized entities\r\n *\r\n * Usage pattern:\r\n * 1. Get or create a repository using getById, get, or addIfMissing\r\n * 2. Use the repository to create new entities\r\n * 3. Access entities through the repository's getters\r\n * 4. Delete entities through the repository when they're no longer needed\r\n */\r\n\r\nimport { AppObject, AppObjectEntityRepo, AppObjectRepo } from \"../../AppObject\";\r\nimport { ExampleEntity, makeExampleEntity } from \"./ExampleEntity\";\r\n\r\n/** Type definition for a factory function that creates ExampleEntity instances */\r\nexport type ExampleEntityFactory = (id: string) => ExampleEntity;\r\n\r\n/**\r\n * ExampleRepo manages a collection of ExampleEntity instances.\r\n * Abstract class provides the interface and static helper methods.\r\n */\r\nexport abstract class ExampleRepo extends AppObjectEntityRepo<ExampleEntity> {\r\n /** Unique type identifier for this component */\r\n static readonly type = \"ExampleRepoType\";\r\n\r\n /** Deletes an entity by its AppObject ID */\r\n abstract deleteExampleEntity(id: string): void;\r\n\r\n /**\r\n * Retrieves an ExampleRepo component from an AppObject\r\n * @param appObj The AppObject to get the component from\r\n * @returns The ExampleRepo component or undefined if not found\r\n */\r\n static get(appObj: AppObject): ExampleRepo | undefined {\r\n return appObj.getComponent<ExampleRepo>(this.type);\r\n }\r\n\r\n /**\r\n * Retrieves an ExampleRepo by its parent AppObject's ID\r\n * @param id The ID of the parent AppObject\r\n * @param appObjects The AppObjectRepo to search in\r\n * @returns The ExampleRepo component or undefined if not found\r\n */\r\n static getById(\r\n id: string,\r\n appObjects: AppObjectRepo\r\n ): ExampleRepo | undefined {\r\n return appObjects.get(id)?.getComponent<ExampleRepo>(this.type);\r\n }\r\n\r\n /**\r\n * Ensures an ExampleRepo exists on the AppObject, creating one if needed\r\n * @param appObject The AppObject to check/add the component to\r\n * @returns The existing or newly created ExampleRepo\r\n */\r\n static addIfMissing(appObject: AppObject): ExampleRepo {\r\n const existing = appObject.getComponent<ExampleRepo>(ExampleRepo.type);\r\n if (existing) {\r\n return existing;\r\n } else {\r\n return makeExampleRepo(appObject);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Factory function to create a new ExampleRepo\r\n * @param appObject The AppObject to attach the repo to\r\n * @returns A new ExampleRepo instance\r\n */\r\nexport function makeExampleRepo(appObject: AppObject): ExampleRepo {\r\n return new ExampleRepoImp(appObject);\r\n}\r\n\r\n/**\r\n * Concrete implementation of ExampleRepo\r\n * This private class handles the actual implementation details\r\n */\r\nclass ExampleRepoImp extends ExampleRepo {\r\n /**\r\n * Factory implementation for creating ExampleEntity instances\r\n * @param id The ID for the entity's AppObject\r\n * @returns A newly created ExampleEntity\r\n */\r\n entityFactory(id: string): ExampleEntity {\r\n const ao = this.appObjects.getOrCreate(id);\r\n return makeExampleEntity(ao);\r\n }\r\n\r\n /**\r\n * Deletes an ExampleEntity from the repository by its AppObject ID\r\n * @param id The ID of the entity's AppObject\r\n */\r\n deleteExampleEntity(id: string): void {\r\n const entity = this.getById(id);\r\n if (!entity) return;\r\n\r\n entity.appObject.dispose();\r\n this.removeById(id);\r\n }\r\n\r\n constructor(appObject: AppObject) {\r\n super(appObject, ExampleRepo.type);\r\n }\r\n}\r\n"]}
1
+ {"version":3,"file":"ExampleRepo.js","sourceRoot":"","sources":["../../../../src/ExampleFeature/Entities/ExampleRepo.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAa,mBAAmB,EAAiB,MAAM,iBAAiB,CAAC;AAChF,OAAO,EAAiB,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAKnE;;;GAGG;AACH,MAAM,OAAgB,WAAY,SAAQ,mBAAkC;IAO1E;;;;OAIG;IACH,MAAM,CAAC,GAAG,CAAC,MAAiB;QAC1B,OAAO,MAAM,CAAC,YAAY,CAAc,IAAI,CAAC,IAAI,CAAC,CAAC;IACrD,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,OAAO,CACZ,EAAU,EACV,UAAyB;QAEzB,OAAO,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,YAAY,CAAc,IAAI,CAAC,IAAI,CAAC,CAAC;IAClE,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,YAAY,CAAC,SAAoB;QACtC,MAAM,QAAQ,GAAG,SAAS,CAAC,YAAY,CAAc,WAAW,CAAC,IAAI,CAAC,CAAC;QACvE,IAAI,QAAQ,EAAE,CAAC;YACb,OAAO,QAAQ,CAAC;QAClB,CAAC;aAAM,CAAC;YACN,OAAO,eAAe,CAAC,SAAS,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;;AAxCD,gDAAgD;AAChC,gBAAI,GAAG,iBAAiB,CAAC;AA0C3C;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,SAAoB;IAClD,OAAO,IAAI,cAAc,CAAC,SAAS,CAAC,CAAC;AACvC,CAAC;AAED;;;GAGG;AACH,MAAM,cAAe,SAAQ,WAAW;IACtC;;;;OAIG;IACH,aAAa,CAAC,SAAoB;QAChC,OAAO,iBAAiB,CAAC,SAAS,CAAC,CAAC;IACtC,CAAC;IAED;;;OAGG;IACH,mBAAmB,CAAC,EAAU;QAC5B,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAChC,IAAI,CAAC,MAAM;YAAE,OAAO;QAEpB,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC;QAC3B,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;IACtB,CAAC;IAED,YAAY,SAAoB;QAC9B,KAAK,CAAC,SAAS,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC;IACrC,CAAC;CACF","sourcesContent":["/**\r\n * ExampleRepo.ts\r\n *\r\n * This file demonstrates how to implement a repository to manage collections of entities.\r\n * Repositories are responsible for creating, retrieving, and deleting entities.\r\n *\r\n * Key concepts:\r\n * - Repositories extend AppObjectEntityRepo<T> where T is the entity type\r\n * - They provide methods to create and delete entities\r\n * - They manage collections of entities and provide access to them\r\n * - They can use custom entity factories to create specialized entities\r\n *\r\n * Usage pattern:\r\n * 1. Get or create a repository using getById, get, or addIfMissing\r\n * 2. Use the repository to create new entities\r\n * 3. Access entities through the repository's getters\r\n * 4. Delete entities through the repository when they're no longer needed\r\n */\r\n\r\nimport { AppObject, AppObjectEntityRepo, AppObjectRepo } from \"../../AppObject\";\r\nimport { ExampleEntity, makeExampleEntity } from \"./ExampleEntity\";\r\n\r\n/** Type definition for a factory function that creates ExampleEntity instances */\r\nexport type ExampleEntityFactory = (appObject: AppObject) => ExampleEntity;\r\n\r\n/**\r\n * ExampleRepo manages a collection of ExampleEntity instances.\r\n * Abstract class provides the interface and static helper methods.\r\n */\r\nexport abstract class ExampleRepo extends AppObjectEntityRepo<ExampleEntity> {\r\n /** Unique type identifier for this component */\r\n static readonly type = \"ExampleRepoType\";\r\n\r\n /** Deletes an entity by its AppObject ID */\r\n abstract deleteExampleEntity(id: string): void;\r\n\r\n /**\r\n * Retrieves an ExampleRepo component from an AppObject\r\n * @param appObj The AppObject to get the component from\r\n * @returns The ExampleRepo component or undefined if not found\r\n */\r\n static get(appObj: AppObject): ExampleRepo | undefined {\r\n return appObj.getComponent<ExampleRepo>(this.type);\r\n }\r\n\r\n /**\r\n * Retrieves an ExampleRepo by its parent AppObject's ID\r\n * @param id The ID of the parent AppObject\r\n * @param appObjects The AppObjectRepo to search in\r\n * @returns The ExampleRepo component or undefined if not found\r\n */\r\n static getById(\r\n id: string,\r\n appObjects: AppObjectRepo\r\n ): ExampleRepo | undefined {\r\n return appObjects.get(id)?.getComponent<ExampleRepo>(this.type);\r\n }\r\n\r\n /**\r\n * Ensures an ExampleRepo exists on the AppObject, creating one if needed\r\n * @param appObject The AppObject to check/add the component to\r\n * @returns The existing or newly created ExampleRepo\r\n */\r\n static addIfMissing(appObject: AppObject): ExampleRepo {\r\n const existing = appObject.getComponent<ExampleRepo>(ExampleRepo.type);\r\n if (existing) {\r\n return existing;\r\n } else {\r\n return makeExampleRepo(appObject);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Factory function to create a new ExampleRepo\r\n * @param appObject The AppObject to attach the repo to\r\n * @returns A new ExampleRepo instance\r\n */\r\nexport function makeExampleRepo(appObject: AppObject): ExampleRepo {\r\n return new ExampleRepoImp(appObject);\r\n}\r\n\r\n/**\r\n * Concrete implementation of ExampleRepo\r\n * This private class handles the actual implementation details\r\n */\r\nclass ExampleRepoImp extends ExampleRepo {\r\n /**\r\n * Factory implementation for creating ExampleEntity instances\r\n * @param appObject The AppObject for the entity\r\n * @returns A newly created ExampleEntity\r\n */\r\n entityFactory(appObject: AppObject): ExampleEntity {\r\n return makeExampleEntity(appObject);\r\n }\r\n\r\n /**\r\n * Deletes an ExampleEntity from the repository by its AppObject ID\r\n * @param id The ID of the entity's AppObject\r\n */\r\n deleteExampleEntity(id: string): void {\r\n const entity = this.getById(id);\r\n if (!entity) return;\r\n\r\n entity.appObject.dispose();\r\n this.removeById(id);\r\n }\r\n\r\n constructor(appObject: AppObject) {\r\n super(appObject, ExampleRepo.type);\r\n }\r\n}\r\n"]}
@@ -0,0 +1,7 @@
1
+ // Controllers
2
+ export * from "./Controllers/setExampleText";
3
+ export * from "./Controllers/toggleExampleBoolean";
4
+ // Adapters
5
+ export * from "./Adapters/examplePmAdapter";
6
+ export * from "./Adapters/exampleSingletonPmAdapter";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/ExampleFeature/index.ts"],"names":[],"mappings":"AAAA,cAAc;AACd,cAAc,8BAA8B,CAAC;AAC7C,cAAc,oCAAoC,CAAC;AAEnD,WAAW;AACX,cAAc,6BAA6B,CAAC;AAC5C,cAAc,sCAAsC,CAAC","sourcesContent":["// Controllers\r\nexport * from \"./Controllers/setExampleText\";\r\nexport * from \"./Controllers/toggleExampleBoolean\";\r\n\r\n// Adapters\r\nexport * from \"./Adapters/examplePmAdapter\";\r\nexport * from \"./Adapters/exampleSingletonPmAdapter\";\r\n"]}
@@ -1,3 +1,4 @@
1
+ import { AppObject } from "./AppObject";
1
2
  import { AppObjectEntity } from "./AppObjectEntity";
2
3
  /**
3
4
  * A repository for managing collections of AppObjectEntity instances.
@@ -106,7 +107,7 @@ export declare class AppObjectEntityRepo<T extends AppObjectEntity> extends AppO
106
107
  * @returns {T} A new entity instance
107
108
  * @throws {Error} If not overridden in derived class
108
109
  */
109
- entityFactory(id: string): T;
110
+ entityFactory(appObject: AppObject): T;
110
111
  /**
111
112
  * Removes the entity associated with the specified ID.
112
113
  *
@@ -152,5 +153,12 @@ export declare class AppObjectEntityRepo<T extends AppObjectEntity> extends AppO
152
153
  * @returns {T[]} An array of all entities
153
154
  */
154
155
  getAll: () => T[];
156
+ /**
157
+ * Gets an entity by ID, or creates it if it doesn't exist.
158
+ *
159
+ * @param {string} id - The ID of the entity to get or create
160
+ * @returns {T} The existing or newly created entity
161
+ */
162
+ getOrCreate: (id: string) => T;
155
163
  }
156
164
  //# sourceMappingURL=AppObjectEntityRepo.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AppObjectEntityRepo.d.ts","sourceRoot":"","sources":["../../../src/AppObject/AppObjectEntityRepo.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,qBAAa,mBAAmB,CAC9B,CAAC,SAAS,eAAe,CACzB,SAAQ,eAAe;IACvB,OAAO,CAAC,YAAY,CAAwB;IAE5C,OAAO,CAAC,sBAAsB,CAAyB;IACvD;;;;OAIG;IACH,sBAAsB,GAAI,UAAU,CAAC,WAAW,EAAE,CAAC,KAAK,IAAI,UAE1D;IAEF;;;;OAIG;IACH,yBAAyB,GAAI,UAAU,CAAC,WAAW,EAAE,CAAC,KAAK,IAAI,KAAG,IAAI,CAEpE;IAEF,OAAO,CAAC,wBAAwB,CAAyB;IACzD;;;;OAIG;IACH,wBAAwB,GAAI,UAAU,CAAC,aAAa,EAAE,CAAC,KAAK,IAAI,UAE9D;IAEF;;;;OAIG;IACH,2BAA2B,GACzB,UAAU,CAAC,aAAa,EAAE,CAAC,KAAK,IAAI,KACnC,IAAI,CAEL;IAEF;;;;;OAKG;IACH,GAAG,GAAI,IAAI,MAAM,KAAG,OAAO,CAEzB;IAEF;;;;;;OAMG;IACH,eAAe,GAAI,aAAa,MAAM,KAAG,OAAO,CAE9C;IAEF;;;;;;;OAOG;IACH,GAAG,CAAC,MAAM,EAAE,CAAC;IAYb;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,CAAC;IAOtB;;;;;;;;;OASG;IACH,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,CAAC;IAK5B;;;;;;OAMG;IACH,UAAU,GAAI,IAAI,MAAM,UAQtB;IAEF;;;;;;;OAOG;IACH,kBAAkB,GAAI,IAAI,MAAM,UAE9B;IAEF;;;;;OAKG;IACH,SAAS,aAUP;IAEF;;;;;OAKG;IACH,OAAO,GAAI,IAAI,MAAM,KAAG,CAAC,GAAG,SAAS,CAEnC;IAEF;;;;;;OAMG;IACH,eAAe,GAAI,aAAa,MAAM,KAAG,CAAC,GAAG,SAAS,CAEpD;IAEF;;;;OAIG;IACH,MAAM,QAAO,CAAC,EAAE,CAEd;CACH"}
1
+ {"version":3,"file":"AppObjectEntityRepo.d.ts","sourceRoot":"","sources":["../../../src/AppObject/AppObjectEntityRepo.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,qBAAa,mBAAmB,CAC9B,CAAC,SAAS,eAAe,CACzB,SAAQ,eAAe;IACvB,OAAO,CAAC,YAAY,CAAwB;IAE5C,OAAO,CAAC,sBAAsB,CAAyB;IACvD;;;;OAIG;IACH,sBAAsB,GAAI,UAAU,CAAC,WAAW,EAAE,CAAC,KAAK,IAAI,UAE1D;IAEF;;;;OAIG;IACH,yBAAyB,GAAI,UAAU,CAAC,WAAW,EAAE,CAAC,KAAK,IAAI,KAAG,IAAI,CAEpE;IAEF,OAAO,CAAC,wBAAwB,CAAyB;IACzD;;;;OAIG;IACH,wBAAwB,GAAI,UAAU,CAAC,aAAa,EAAE,CAAC,KAAK,IAAI,UAE9D;IAEF;;;;OAIG;IACH,2BAA2B,GACzB,UAAU,CAAC,aAAa,EAAE,CAAC,KAAK,IAAI,KACnC,IAAI,CAEL;IAEF;;;;;OAKG;IACH,GAAG,GAAI,IAAI,MAAM,KAAG,OAAO,CAEzB;IAEF;;;;;;OAMG;IACH,eAAe,GAAI,aAAa,MAAM,KAAG,OAAO,CAE9C;IAEF;;;;;;;OAOG;IACH,GAAG,CAAC,MAAM,EAAE,CAAC;IAYb;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,CAAC;IAQtB;;;;;;;;;OASG;IACH,aAAa,CAAC,SAAS,EAAE,SAAS,GAAG,CAAC;IAKtC;;;;;;OAMG;IACH,UAAU,GAAI,IAAI,MAAM,UAQtB;IAEF;;;;;;;OAOG;IACH,kBAAkB,GAAI,IAAI,MAAM,UAE9B;IAEF;;;;;OAKG;IACH,SAAS,aAUP;IAEF;;;;;OAKG;IACH,OAAO,GAAI,IAAI,MAAM,KAAG,CAAC,GAAG,SAAS,CAEnC;IAEF;;;;;;OAMG;IACH,eAAe,GAAI,aAAa,MAAM,KAAG,CAAC,GAAG,SAAS,CAEpD;IAEF;;;;OAIG;IACH,MAAM,QAAO,CAAC,EAAE,CAEd;IAEF;;;;;OAKG;IACH,WAAW,GAAI,IAAI,MAAM,KAAG,CAAC,CAM3B;CACH"}
@@ -19,7 +19,7 @@
19
19
  import { AppObject, AppObjectEntityRepo, AppObjectRepo } from "../../AppObject";
20
20
  import { ExampleEntity } from "./ExampleEntity";
21
21
  /** Type definition for a factory function that creates ExampleEntity instances */
22
- export type ExampleEntityFactory = (id: string) => ExampleEntity;
22
+ export type ExampleEntityFactory = (appObject: AppObject) => ExampleEntity;
23
23
  /**
24
24
  * ExampleRepo manages a collection of ExampleEntity instances.
25
25
  * Abstract class provides the interface and static helper methods.
@@ -1 +1 @@
1
- {"version":3,"file":"ExampleRepo.d.ts","sourceRoot":"","sources":["../../../../src/ExampleFeature/Entities/ExampleRepo.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChF,OAAO,EAAE,aAAa,EAAqB,MAAM,iBAAiB,CAAC;AAEnE,kFAAkF;AAClF,MAAM,MAAM,oBAAoB,GAAG,CAAC,EAAE,EAAE,MAAM,KAAK,aAAa,CAAC;AAEjE;;;GAGG;AACH,8BAAsB,WAAY,SAAQ,mBAAmB,CAAC,aAAa,CAAC;IAC1E,gDAAgD;IAChD,MAAM,CAAC,QAAQ,CAAC,IAAI,qBAAqB;IAEzC,4CAA4C;IAC5C,QAAQ,CAAC,mBAAmB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAE9C;;;;OAIG;IACH,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,SAAS,GAAG,WAAW,GAAG,SAAS;IAItD;;;;;OAKG;IACH,MAAM,CAAC,OAAO,CACZ,EAAE,EAAE,MAAM,EACV,UAAU,EAAE,aAAa,GACxB,WAAW,GAAG,SAAS;IAI1B;;;;OAIG;IACH,MAAM,CAAC,YAAY,CAAC,SAAS,EAAE,SAAS,GAAG,WAAW;CAQvD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,SAAS,GAAG,WAAW,CAEjE"}
1
+ {"version":3,"file":"ExampleRepo.d.ts","sourceRoot":"","sources":["../../../../src/ExampleFeature/Entities/ExampleRepo.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChF,OAAO,EAAE,aAAa,EAAqB,MAAM,iBAAiB,CAAC;AAEnE,kFAAkF;AAClF,MAAM,MAAM,oBAAoB,GAAG,CAAC,SAAS,EAAE,SAAS,KAAK,aAAa,CAAC;AAE3E;;;GAGG;AACH,8BAAsB,WAAY,SAAQ,mBAAmB,CAAC,aAAa,CAAC;IAC1E,gDAAgD;IAChD,MAAM,CAAC,QAAQ,CAAC,IAAI,qBAAqB;IAEzC,4CAA4C;IAC5C,QAAQ,CAAC,mBAAmB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAE9C;;;;OAIG;IACH,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,SAAS,GAAG,WAAW,GAAG,SAAS;IAItD;;;;;OAKG;IACH,MAAM,CAAC,OAAO,CACZ,EAAE,EAAE,MAAM,EACV,UAAU,EAAE,aAAa,GACxB,WAAW,GAAG,SAAS;IAI1B;;;;OAIG;IACH,MAAM,CAAC,YAAY,CAAC,SAAS,EAAE,SAAS,GAAG,WAAW;CAQvD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,SAAS,GAAG,WAAW,CAEjE"}
@@ -0,0 +1,5 @@
1
+ export * from "./Controllers/setExampleText";
2
+ export * from "./Controllers/toggleExampleBoolean";
3
+ export * from "./Adapters/examplePmAdapter";
4
+ export * from "./Adapters/exampleSingletonPmAdapter";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ExampleFeature/index.ts"],"names":[],"mappings":"AACA,cAAc,8BAA8B,CAAC;AAC7C,cAAc,oCAAoC,CAAC;AAGnD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,sCAAsC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vived/core",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "description": "Core Components for VIVED Apps and Hosts",
5
5
  "main": "./dist/cjs/index.js",
6
6
  "module": "./dist/esm/index.js",
@@ -55,6 +55,7 @@
55
55
  "uuid": "^11.1.0"
56
56
  },
57
57
  "files": [
58
- "dist"
58
+ "dist",
59
+ "src/**/README.md"
59
60
  ]
60
61
  }
@@ -0,0 +1,476 @@
1
+ # App Object Architecture
2
+
3
+ > This document describes the `AppObject` architecture and its component ecosystem. The low-level `AppObjectController` base class file itself is not detailed here, but controller *behavior* and flow are now documented for completeness.
4
+
5
+ ## Overview
6
+ The architecture centers around the concept of an **App Object** – a composable, observable container of domain-specific components. Each `AppObject` aggregates behavior and state through **components** that follow clear separation-of-responsibility categories:
7
+
8
+ - Entity: stateful, observable model logic
9
+ - Presentation Manager (PM): derives and emits immutable view models
10
+ - Use Case (UC): encapsulates application operations / workflows
11
+ - View: renders or binds presentation output to a UI or rendering substrate
12
+ - (Other) Arbitrary/custom components categorized as `UNKNOWN`
13
+
14
+ The system encourages:
15
+ - Decoupling via **component lookup** and **repository-level singleton resolution**
16
+ - Reactive propagation via observer lists on entities, PMs, and AppObjects
17
+ - Composability: AppObjects can be extended at runtime by adding/replacing components
18
+ - Testability: Small, focused classes with explicit contracts
19
+
20
+ ## Core Building Blocks
21
+
22
+ ### AppObject (`AppObject.ts`)
23
+ An abstract observable node that:
24
+ - Owns a unique `id`
25
+ - Registers itself in an `AppObjectRepo`
26
+ - Manages a map of `type -> AppObjectComponent`
27
+ - Notifies observers when its component set changes (e.g., add/remove/replace)
28
+ - Handles lifecycle: `dispose()` removes itself from the repo and disposes all components
29
+
30
+ Creation is performed via `makeAppObject(id, repo)` which returns a concrete internal implementation.
31
+
32
+ ### AppObjectComponent (`AppObjectComponent.ts`)
33
+ The base class for all components. Responsibilities:
34
+ - Auto–attaches to its parent `AppObject` on construction
35
+ - Exposes `componentType` (categorical enum) and `type` (unique string identifier)
36
+ - Provides cached retrieval helpers:
37
+ - `getCachedLocalComponent(type)` – same AppObject
38
+ - `getCachedSingleton(type)` – repo-level singleton component
39
+ - `getSingleton(type, logLevel)` – non‑cached lookup with log control
40
+ - Supports disposal: removing itself if still attached
41
+ - Provides uniform logging APIs proxied to the repository
42
+
43
+ Design Notes:
44
+ - Caching avoids repeated map traversal and singleton scans
45
+ - Logging includes composite key `AppObjectID/ComponentTypeString`
46
+
47
+ ### AppObjectEntity (`AppObjectEntity.ts`)
48
+ Specialized component for **domain/application state**. Provides:
49
+ - Change observers via `addChangeObserver` / `notifyOnChange`
50
+ - Disposal observers via `addOnDisposeObserver`
51
+ - Automatic registration of the parent `AppObject`'s `notify` method as a change observer so AppObject-level observers react to entity changes
52
+
53
+ Patterns enabled:
54
+ - PM subscribes to one or more Entities to derive view state
55
+ - Repository-level aggregation via `AppObjectEntityRepo`
56
+
57
+ ### AppObjectSingletonEntity (`AppObjectSingletonEntity.ts`)
58
+ A specialized entity that automatically registers itself as a singleton. Extends `AppObjectEntity` with:
59
+ - Automatic singleton registration via `appObjects.registerSingleton(this)` in constructor
60
+ - Automatic singleton unregistration via `appObjects.unregisterSingleton(this.type)` on disposal
61
+ - Ensures only one instance of this entity type exists across the entire application
62
+
63
+ Use this when you need globally unique entities (e.g., application-wide configuration, user session state).
64
+
65
+ ### AppObjectEntityRepo (`AppObjectEntityRepo.ts`)
66
+ A stateful collection component (itself an `AppObjectEntity`) that manages many entity instances keyed by `AppObject.id`:
67
+ - Add/remove operations attach/detach change observers on each entity to bubble aggregate change notifications
68
+ - Emits addition/removal notifications through dedicated observer lists
69
+ - Factory pattern support:
70
+ - `create(id?: string)` – creates a new entity via `entityFactory` and adds it to the repository (auto-generates ID if not provided)
71
+ - `entityFactory(appObject: AppObject)` – abstract method that derived classes override to provide custom entity creation logic
72
+ - Query surface:
73
+ - `has(id)` – checks if an entity exists for the given ID
74
+ - `getById(id)` – retrieves an entity by its ID
75
+ - `getOrCreate(id)` – gets an existing entity by ID, or creates it if it doesn't exist
76
+ - `getAll()` – returns all entities in the repository
77
+ - Mutation operations:
78
+ - `add(entity)` – adds an entity to the repository
79
+ - `removeById(id)` – removes a single entity by its ID
80
+ - `deleteAll()` – removes all entities from the repository
81
+ - Legacy methods (deprecated):
82
+ - `hasForAppObject(id)` – use `has(id)` instead
83
+ - `getForAppObject(id)` – use `getById(id)` instead
84
+ - `removeForAppObject(id)` – use `removeById(id)` instead
85
+
86
+ **Factory Pattern Usage:**
87
+ Derived repositories should override `entityFactory` to provide entity creation logic:
88
+ ```ts
89
+ class PlayerRepo extends AppObjectEntityRepo<PlayerEntity> {
90
+ static type = "playerRepo";
91
+
92
+ constructor(appObject: AppObject) {
93
+ super(appObject, PlayerRepo.type);
94
+ }
95
+
96
+ entityFactory(appObject: AppObject): PlayerEntity {
97
+ return new PlayerEntity(appObject);
98
+ }
99
+ }
100
+
101
+ // Usage
102
+ const repo = new PlayerRepo(repoAppObject);
103
+ const player1 = repo.create("player-1"); // Create with specific ID
104
+ const player2 = repo.create(); // Create with auto-generated ID
105
+ const player3 = repo.getOrCreate("player-1"); // Get existing player1 or create if missing
106
+ ```
107
+
108
+ This enables higher-level coordination (e.g., multi-selection, batch processing, dashboards) with cohesive reactivity.
109
+
110
+ ### AppObjectSingletonEntityRepo (`AppObjectSingletonEntityRepo.ts`)
111
+ Generic singleton repository for managing entity collections. Extends `AppObjectEntityRepo<T>` with:
112
+ - Automatic singleton registration and unregistration (same pattern as `AppObjectSingletonEntity`)
113
+ - Type-safe entity collection management across the entire application
114
+ - Use when you need a single, centralized collection (e.g., all players, all items, all tasks)
115
+
116
+ Example:
117
+ ```ts
118
+ class PlayerRepo extends AppObjectSingletonEntityRepo<PlayerEntity> {
119
+ static type = "playerRepo";
120
+ constructor(appObject: AppObject) {
121
+ super(appObject, PlayerRepo.type);
122
+ }
123
+ }
124
+ ```
125
+
126
+ ### AppObjectPM (`AppObjectPM.ts`)
127
+ The Presentation Manager (Presentation Model / MVVM mediator). Responsibilities:
128
+ - Maintains last emitted view model (`lastVM`)
129
+ - Optionally provides a `defaultVM` for initial state before any view model is generated
130
+ - Compares new vs prior view model via abstract `vmsAreEqual(a,b)` to suppress redundant updates
131
+ - Manages a list of view observers (`addView` / `removeView`)
132
+ - Provides `doUpdateView(vm)` to push updates only when meaningfully changed
133
+ - Supports automatic entity observation via `observeEntity(entity)` for reactive view model updates
134
+ - Implements lazy evaluation: `formVM()` is only called when views are registered
135
+ - Provides `onViewAdded()` lifecycle hook that is called whenever a view is added
136
+ - Automatically cleans up entity observers on disposal
137
+
138
+ **New Reactive Pattern (Preferred):**
139
+ Derived PMs can now use automatic entity observation:
140
+ 1. Call `observeEntity(entity)` in the constructor to register entities
141
+ 2. Override `formVM()` to generate view models from entity state
142
+ 3. When observed entities change, `formVM()` is automatically called (only if views are registered)
143
+ 4. Optionally override `onViewAdded()` to react when views are added (e.g., start animations, log analytics)
144
+ 5. Entity observers are automatically cleaned up on disposal
145
+
146
+ **Backward Compatibility:**
147
+ All existing PM implementations continue to work without modification. The new features are optional enhancements that provide:
148
+ - Automatic view model regeneration on entity changes
149
+ - Default view model support for initial state
150
+ - Simplified entity observation with automatic cleanup
151
+ - Lifecycle hooks for reacting to view additions
152
+
153
+ This isolates transformation logic and prevents UI churn.
154
+
155
+ ### AppObjectSingletonPM (`AppObjectSingletonPM.ts`)
156
+ Abstract singleton presentation manager that extends `AppObjectPM<T>` with:
157
+ - Automatic singleton registration and unregistration
158
+ - Ensures only one PM instance of this type exists across the application
159
+ - Inherits all automatic entity observation and view model caching features
160
+ - Use for global UI state (e.g., application theme, notification center, global progress indicator)
161
+
162
+ Example:
163
+ ```ts
164
+ class GlobalThemePM extends AppObjectSingletonPM<ThemeVM> {
165
+ static type = "globalThemePM";
166
+
167
+ constructor(appObject: AppObject) {
168
+ super(appObject, GlobalThemePM.type);
169
+ const settings = this.getCachedSingleton<SettingsEntity>(SettingsEntity.type);
170
+ if (settings) this.observeEntity(settings);
171
+ }
172
+
173
+ vmsAreEqual(a: ThemeVM, b: ThemeVM): boolean {
174
+ return a.mode === b.mode && a.primaryColor === b.primaryColor;
175
+ }
176
+
177
+ formVM(): void {
178
+ // Transform settings into theme view model
179
+ }
180
+ }
181
+ ```
182
+
183
+ ### AppObjectUC (`AppObjectUC.ts`)
184
+ A semantic base for **application operations** (e.g., workflows, transactional steps). It adds categorical identity (`componentType = UC`) but intentionally stays minimal so concrete subclasses can:
185
+ - Orchestrate entities, PMs, and repositories
186
+ - Enforce validation and domain rules
187
+ - Trigger PM updates or entity mutations
188
+
189
+ ### AppObjectSingletonUC (`AppObjectSingletonUC.ts`)
190
+ Singleton use case component that extends `AppObjectUC` with:
191
+ - Automatic singleton registration and unregistration
192
+ - Ensures only one UC instance of this type exists across the application
193
+ - Use for application-wide orchestration logic (e.g., global purchase manager, authentication coordinator)
194
+
195
+ Example:
196
+ ```ts
197
+ class AuthenticationUC extends AppObjectSingletonUC {
198
+ static type = "authenticationUC";
199
+
200
+ constructor(appObject: AppObject) {
201
+ super(appObject, AuthenticationUC.type);
202
+ }
203
+
204
+ login(username: string, password: string): Promise<boolean> {
205
+ // Application-wide authentication logic
206
+ }
207
+
208
+ logout(): void {
209
+ // Clean up session, notify entities, etc.
210
+ }
211
+ }
212
+ ```
213
+
214
+ ### AppObjectView (`AppObjectView.ts`)
215
+ A rendering/binding endpoint:
216
+ - Categorized as `VIEW`
217
+ - Typically subscribes to one or more PMs (manually, via the PM's `addView` API)
218
+ - Focused purely on projecting view models to a target (DOM, WebGL scene graph, canvas, etc.)
219
+
220
+ ### getSingletonComponent (`getSingletonComponent.ts`)
221
+ A convenience helper:
222
+ ```ts
223
+ const camera = getSingletonComponent<CameraPM>(CameraPM.type, repo);
224
+ ```
225
+ - Wraps `repo.getSingleton(type)` to simplify imports and generics at call sites
226
+
227
+ ### printAppObjectDetails (`printAppObjectDetails.ts`)
228
+ Debugging utility that enumerates components attached to a given AppObject ID.
229
+
230
+ ## Repository Layer
231
+ `AppObjectRepo`:
232
+ - Tracks all AppObjects (`id -> AppObject`)
233
+ - Aggregates reactivity: registers itself as observer of each AppObject; its own observers can react to structural changes
234
+ - Maintains explicit singleton registry (Map of `type -> component`)
235
+ - `registerSingleton(component)` – explicitly register a component as singleton
236
+ - `unregisterSingleton(type)` – remove a singleton registration (called automatically by singleton components on disposal)
237
+ - `hasSingleton(type)` – check if a singleton exists (checks registry first, then falls back to scanning for single instance)
238
+ - `getSingleton(type)` – retrieve a singleton component
239
+ - Fallback heuristic: if not explicitly registered, scan all components of that type
240
+ - Warns on zero or multiple matches; caches first valid resolution
241
+ - Query Helpers:
242
+ - `getAllAppObjectsWithComponent(type)`
243
+ - `getAllComponents(type)`
244
+ - `getAppObjectComponent(appObjectID, type)`
245
+ - Logging hub consumed by components
246
+
247
+ ### Singleton Component Pattern
248
+ The framework provides four singleton base classes that automatically handle registration/unregistration:
249
+ - `AppObjectSingletonEntity` – singleton entities
250
+ - `AppObjectSingletonEntityRepo<T>` – singleton entity repositories
251
+ - `AppObjectSingletonPM<T>` – singleton presentation managers
252
+ - `AppObjectSingletonUC` – singleton use cases
253
+
254
+ All singleton components:
255
+ 1. Call `this.appObjects.registerSingleton(this)` in their constructor
256
+ 2. Call `this.appObjects.unregisterSingleton(this.type)` in their `dispose()` method
257
+ 3. Can be retrieved via `repo.getSingleton(type)` or `component.getCachedSingleton(type)`
258
+
259
+ This pattern ensures singleton lifecycle management is handled consistently across all component types.
260
+
261
+ ## Typical User → UI → Domain Flow
262
+
263
+ 1. **Controller Trigger**
264
+ A user action (click, input, gesture, hotkey, network event) calls a small *controller function*. Controllers are intentionally plain functions (see `ExampleFeature/Controllers/*.ts`). They:
265
+ - Accept raw UI parameters (strings, numbers, ids)
266
+ - Locate the appropriate Use Case (by `id` for per-object UCs or via static singleton accessors for global UCs)
267
+ - Guard against missing UCs and submit warnings through the repo
268
+ - Invoke a single semantic method on the UC
269
+
270
+ 2. **Use Case Mutation**
271
+ The Use Case applies business rules and mutates one or more **Entities** (and occasionally invokes other UCs). Entities are considered part of the inner domain and remain decoupled from presentation concerns.
272
+
273
+ 3. **Entity Notification**
274
+ Entities call `notifyOnChange()` after state mutation. Their change observers (PMs, the parent `AppObject`, repositories, etc.) are invoked.
275
+
276
+ 4. **Presentation Derivation**
277
+ PMs receiving the change recompute an immutable **View Model**. If `vmsAreEqual(last, next)` is false, `doUpdateView(nextVM)` broadcasts the new model.
278
+
279
+ 5. **View Update**
280
+ Views (or framework-side adapters) receive the new View Model and update rendering / UI bindings. React hooks, canvas redraws, WebGL scene updates, etc., occur here.
281
+
282
+ Controller Functions remain deliberately slim: *resolve UC → call UC → handle missing cases*. This keeps UI code declarative and reduces duplication of lookup logic.
283
+
284
+ ### Adapters (Domain Boundary Helpers)
285
+ Adapters (see `ExampleFeature/Adapters/*.ts`) standardize subscription mechanics between UI frameworks (React hooks, etc.) and PMs:
286
+ - Provide a `defaultVM` for initial render
287
+ - Encapsulate `subscribe` / `unsubscribe` logic
288
+ - Support both per-object (`PmAdapter`) and singleton (`SingletonPmAdapter`) patterns
289
+
290
+ ## Dependency Direction (Clean Architecture Constraints)
291
+
292
+ Strict layering reduces coupling and prevents presentation concerns from leaking inward:
293
+
294
+ | Layer | May Depend On | Must NOT Depend On | Notes |
295
+ |-------|----------------|--------------------|-------|
296
+ | Entities (incl. Repos & Value Objects) | Other Entities, Value Objects | UCs, PMs, Controllers, Adapters | Repositories are treated as Entities (state holders) |
297
+ | Use Cases (UCs) | Entities, other UCs | PMs | They orchestrate but never shape view models directly |
298
+ | PMs | Entities, UCs | Other PMs (allowed but avoided), Controllers | No current need for PM→PM dependency encountered |
299
+ | Controllers | UCs, (optionally) Repos for lookup | Entities, PM internals | Pure boundary functions; translate UI intent to UC invocation |
300
+ | Adapters | PMs (subscription), Repos (to resolve PM) | UCs, Entities (direct mutation) | Provide view-model streaming into UI layer |
301
+ | Views | PMs (via adapters or direct subscription) | UCs, Entities | Rendering only |
302
+
303
+ Additional Rules:
304
+ - Repos are considered data/state layer; treat them like Entities for dependency purposes.
305
+ - Singletons do not relax dependency direction—acquire them only where the layer already allows the dependency.
306
+ - Logging via repo methods is allowed anywhere because it does not introduce upward coupling.
307
+
308
+ Violation Signals:
309
+ - An Entity importing a UC or PM
310
+ - A UC importing a PM
311
+ - A PM invoking controller logic
312
+ - Adapters mutating Entity state directly
313
+
314
+ Refactor Strategy on Violation:
315
+ 1. Push mutation inward (Controller → UC → Entity)
316
+ 2. Introduce a new UC if orchestration spans multiple existing UCs
317
+ 3. Decompose a PM if it begins coordinating workflow rather than projecting state
318
+
319
+ ## Data & Reactive Flow
320
+ 1. Entity mutation occurs (e.g., property set in a subclass) → calls `notifyOnChange()`
321
+ 2. Entity notifies its observers (including its parent AppObject and any PMs)
322
+ 3. AppObject notifies its observers (repository + any external listeners)
323
+ 4. PM recalculates view model; if changed (`vmsAreEqual` is false), it calls `doUpdateView(vm)`
324
+ 5. Views previously registered with the PM receive the new view model and re-render
325
+
326
+ ```
327
+ Entity --(notifyOnChange)--> PM --(doUpdateView)--> View
328
+ | ^ |
329
+ +----> AppObject --(notify)----+ |
330
+ | |
331
+ v |
332
+ AppObjectRepo (optional higher-level observers)
333
+ ```
334
+
335
+ ## Lifecycle Summary
336
+ - Construct `AppObject` via `makeAppObject(id, repo)` → auto-added to repo
337
+ - Construct components with `(appObject, type)` → auto-attached
338
+ - Replace component: adding another of same `type` disposes previous instance
339
+ - Dispose entity / component: removes self from AppObject, clears observers
340
+ - Dispose AppObject: disposes all components, removes itself from repo
341
+
342
+ ## Extension Guidelines
343
+ When adding a new component type:
344
+ 1. Define a static string identifier (e.g., `export const MyFeatureEntityType = "MyFeatureEntity";`)
345
+ 2. Choose the appropriate base class:
346
+ - For **regular components**: `AppObjectEntity`, `AppObjectPM`, `AppObjectUC`, or `AppObjectView`
347
+ - For **singleton components**: `AppObjectSingletonEntity`, `AppObjectSingletonEntityRepo<T>`, `AppObjectSingletonPM<T>`, or `AppObjectSingletonUC`
348
+ 3. Invoke `super(appObject, MyFeatureEntityType);` in constructor
349
+ 4. For PMs: implement `vmsAreEqual` (and optionally `formVM()` with `observeEntity()` for automatic updates)
350
+ 5. For singleton components: No additional registration needed—handled automatically by the base class
351
+ 6. Use cached getters for performance when repeatedly accessing collaborating components
352
+
353
+ ### Choosing Between Regular and Singleton Components
354
+ Use **singleton components** when:
355
+ - Only one instance should exist across the entire application
356
+ - The component represents global application state or behavior
357
+ - Examples: user session, app configuration, global theme, authentication manager
358
+
359
+ Use **regular components** when:
360
+ - Multiple instances may exist (one per AppObject)
361
+ - The component represents per-object state or behavior
362
+ - Examples: player state, item properties, entity-specific UI
363
+
364
+ ### Choosing Component Categories
365
+ - Put durable, observable state in an Entity
366
+ - Put pure transformation / derivation logic in a PM
367
+ - Put orchestration / cross-entity logic in a UC
368
+ - Put rendering / binding logic in a View
369
+ - Avoid mixing responsibilities—compose instead
370
+
371
+ ## Example Composition (Pseudo-Code)
372
+ ```ts
373
+ // Create repo & object
374
+ const repo = makeAppObjectRepo();
375
+ const playerAO = makeAppObject("player-1", repo);
376
+
377
+ // Entity
378
+ class PlayerState extends AppObjectEntity {
379
+ static type = "PlayerState";
380
+ health = 100;
381
+ constructor(ao: AppObject) { super(ao, PlayerState.type); }
382
+ damage(amount: number) { this.health = Math.max(0, this.health - amount); this.notifyOnChange(); }
383
+ }
384
+
385
+ // PM using new automatic entity observation (v1.7+)
386
+ class PlayerHUDPM extends AppObjectPM<{ healthPercent: number }> {
387
+ static type = "PlayerHUDPM";
388
+ readonly defaultVM = { healthPercent: 1.0 }; // Initial full health
389
+
390
+ constructor(ao: AppObject) {
391
+ super(ao, PlayerHUDPM.type);
392
+ const state = ao.getComponent<PlayerState>(PlayerState.type);
393
+ if (state) {
394
+ this.observeEntity(state); // Automatic observation
395
+ }
396
+ }
397
+
398
+ vmsAreEqual(a, b) { return a.healthPercent === b.healthPercent; }
399
+
400
+ // Automatically called when observed entities change (if views are registered)
401
+ formVM(): void {
402
+ const state = this.getCachedLocalComponent<PlayerState>(PlayerState.type);
403
+ if (state) {
404
+ this.doUpdateView({ healthPercent: state.health / 100 });
405
+ }
406
+ }
407
+ }
408
+
409
+ // View (simplified)
410
+ class ConsoleHUDView extends AppObjectView {
411
+ static type = "ConsoleHUDView";
412
+ constructor(ao: AppObject, hudPM: PlayerHUDPM) {
413
+ super(ao, ConsoleHUDView.type);
414
+ hudPM.addView(vm => console.log("HP:", vm.healthPercent));
415
+ }
416
+ }
417
+
418
+ new PlayerState(playerAO);
419
+ const hudPM = new PlayerHUDPM(playerAO);
420
+ new ConsoleHUDView(playerAO, hudPM);
421
+
422
+ // Trigger - PM automatically updates views when entity changes
423
+ const ps = playerAO.getComponent<PlayerState>(PlayerState.type);
424
+ ps?.damage(10); // PM automatically calls formVM() and updates views
425
+ ```
426
+
427
+ **Legacy Pattern (Pre-v1.7, still supported):**
428
+ ```ts
429
+ class PlayerHUDPM extends AppObjectPM<{ healthPercent: number }> {
430
+ static type = "PlayerHUDPM";
431
+ constructor(ao: AppObject) { super(ao, PlayerHUDPM.type); }
432
+ vmsAreEqual(a, b) { return a.healthPercent === b.healthPercent; }
433
+
434
+ // Manual update method
435
+ update() {
436
+ const state = this.getCachedLocalComponent<PlayerState>(PlayerState.type);
437
+ if (!state) return;
438
+ this.doUpdateView({ healthPercent: state.health / 100 });
439
+ }
440
+ }
441
+
442
+ // Manual trigger required
443
+ ps?.damage(10);
444
+ hudPM.update(); // Must manually call update
445
+ ```
446
+
447
+ ## Logging & Diagnostics
448
+ - Components call `log|warn|error` → forwarded via repo (current implementation prints to console)
449
+ - `printAppObjectDetails(id, repo)` lists attached component types
450
+ - Replacing a component logs a warning
451
+
452
+ ## Performance Considerations
453
+ - Component caches avoid redundant map lookups and singleton scans
454
+ - PM equality check prevents spurious view updates
455
+ - Repository singleton cache resolves dynamic discovery only once per type
456
+
457
+ ## Error Handling & Warnings
458
+ - Missing singleton lookup emits a warning (not fatal)
459
+ - Multiple candidates for a supposed singleton: first is used + warning
460
+ - Replacing a component of same type logs a warning and disposes old instance
461
+
462
+ ## Glossary
463
+ - AppObject: A composable, observable unit of application composition
464
+ - Component: A behavior/state module attached to an AppObject
465
+ - Entity: Stateful, observable component storing domain data
466
+ - PM (Presentation Manager): Transforms Entities into view models for Views
467
+ - UC (Use Case): Encapsulates business workflow logic
468
+ - View: Renders or binds presentation logic to UI / output medium
469
+ - Singleton Component: A component intended to exist once across the repo, retrievable via `getSingleton`
470
+
471
+ ## Future Enhancements (Ideas)
472
+ - Stronger typing for component `type` identifiers (string literal unions)
473
+ - Async disposal hooks for resources (e.g., WebGL buffers)
474
+ - Built-in metrics / instrumentation hooks
475
+ - Dev tooling: Graph generation of AppObjects and dependencies
476
+