@axi-engine/configs 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -1,13 +1,14 @@
1
- import { PathType } from "@axi-engine/utils";
1
+ import { PathType } from '@axi-engine/utils';
2
2
 
3
- //#region src/config-resolver.types.d.ts
4
3
  /**
5
4
  * Represents a specific configuration variant that can extend another variant.
6
5
  * @template T The base type of the configuration object.
7
6
  */
8
7
  type ConfigVariant<T extends object> = {
9
- /** Path to a base config (or multiple configs) that this variant extends. */extends?: PathType; /** The specific fields that override or extend the base config. */
10
- fields?: Partial<T>;
8
+ /** Path to a base config (or multiple configs) that this variant extends. */
9
+ extends?: PathType;
10
+ /** The specific fields that override or extend the base config. */
11
+ fields?: Partial<T>;
11
12
  };
12
13
  /**
13
14
  * Represents the hierarchical structure of the configuration.
@@ -15,42 +16,40 @@ type ConfigVariant<T extends object> = {
15
16
  * @template T The base type of the configuration object.
16
17
  */
17
18
  type ConfigTree<T extends object> = {
18
- [key: string]: ConfigVariant<T> | ConfigTree<T>;
19
+ [key: string]: ConfigVariant<T> | ConfigTree<T>;
19
20
  };
20
21
  /**
21
22
  * Type guard to check if an object is a ConfigVariant.
22
23
  */
23
24
  declare function isConfigVariant<T extends object>(obj: any): obj is ConfigVariant<T>;
24
- //#endregion
25
- //#region src/config-resolver.d.ts
25
+
26
26
  /**
27
27
  * Resolves hierarchical configurations with inheritance and caching.
28
28
  * @template TBase The base type for all configurations this resolver handles.
29
29
  */
30
30
  declare class ConfigResolver<TBase extends object> {
31
- readonly configs: ConfigTree<TBase>;
32
- private cache;
33
- constructor(configs: ConfigTree<TBase>);
34
- /**
35
- * Retrieves a fully resolved and merged configuration for a given path.
36
- * @template T A specific subtype of TBase that the caller expects.
37
- * @param path Path to the config, e.g., 'enemies.boss.goblin'.
38
- * @returns The resolved configuration object.
39
- */
40
- get<T extends TBase>(path: PathType): T;
41
- /**
42
- * Clears the cache of resolved configurations.
43
- */
44
- clearCache(): void;
45
- /**
46
- * Recursively calculates the configuration by merging a variant with its parents.
47
- */
48
- private calculateConfig;
49
- /**
50
- * Traverses the config tree to find the node at the specified path.
51
- */
52
- private getNode;
31
+ readonly configs: ConfigTree<TBase>;
32
+ private cache;
33
+ constructor(configs: ConfigTree<TBase>);
34
+ /**
35
+ * Retrieves a fully resolved and merged configuration for a given path.
36
+ * @template T A specific subtype of TBase that the caller expects.
37
+ * @param path Path to the config, e.g., 'enemies.boss.goblin'.
38
+ * @returns The resolved configuration object.
39
+ */
40
+ get<T extends TBase>(path: PathType): T;
41
+ /**
42
+ * Clears the cache of resolved configurations.
43
+ */
44
+ clearCache(): void;
45
+ /**
46
+ * Recursively calculates the configuration by merging a variant with its parents.
47
+ */
48
+ private calculateConfig;
49
+ /**
50
+ * Traverses the config tree to find the node at the specified path.
51
+ */
52
+ private getNode;
53
53
  }
54
- //#endregion
55
- export { ConfigResolver, ConfigTree, ConfigVariant, isConfigVariant };
56
- //# sourceMappingURL=index.d.mts.map
54
+
55
+ export { ConfigResolver, type ConfigTree, type ConfigVariant, isConfigVariant };
@@ -0,0 +1,55 @@
1
+ import { PathType } from '@axi-engine/utils';
2
+
3
+ /**
4
+ * Represents a specific configuration variant that can extend another variant.
5
+ * @template T The base type of the configuration object.
6
+ */
7
+ type ConfigVariant<T extends object> = {
8
+ /** Path to a base config (or multiple configs) that this variant extends. */
9
+ extends?: PathType;
10
+ /** The specific fields that override or extend the base config. */
11
+ fields?: Partial<T>;
12
+ };
13
+ /**
14
+ * Represents the hierarchical structure of the configuration.
15
+ * It's a recursive type that can contain other trees or final variants.
16
+ * @template T The base type of the configuration object.
17
+ */
18
+ type ConfigTree<T extends object> = {
19
+ [key: string]: ConfigVariant<T> | ConfigTree<T>;
20
+ };
21
+ /**
22
+ * Type guard to check if an object is a ConfigVariant.
23
+ */
24
+ declare function isConfigVariant<T extends object>(obj: any): obj is ConfigVariant<T>;
25
+
26
+ /**
27
+ * Resolves hierarchical configurations with inheritance and caching.
28
+ * @template TBase The base type for all configurations this resolver handles.
29
+ */
30
+ declare class ConfigResolver<TBase extends object> {
31
+ readonly configs: ConfigTree<TBase>;
32
+ private cache;
33
+ constructor(configs: ConfigTree<TBase>);
34
+ /**
35
+ * Retrieves a fully resolved and merged configuration for a given path.
36
+ * @template T A specific subtype of TBase that the caller expects.
37
+ * @param path Path to the config, e.g., 'enemies.boss.goblin'.
38
+ * @returns The resolved configuration object.
39
+ */
40
+ get<T extends TBase>(path: PathType): T;
41
+ /**
42
+ * Clears the cache of resolved configurations.
43
+ */
44
+ clearCache(): void;
45
+ /**
46
+ * Recursively calculates the configuration by merging a variant with its parents.
47
+ */
48
+ private calculateConfig;
49
+ /**
50
+ * Traverses the config tree to find the node at the specified path.
51
+ */
52
+ private getNode;
53
+ }
54
+
55
+ export { ConfigResolver, type ConfigTree, type ConfigVariant, isConfigVariant };
package/dist/index.js ADDED
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ ConfigResolver: () => ConfigResolver,
24
+ isConfigVariant: () => isConfigVariant
25
+ });
26
+ module.exports = __toCommonJS(index_exports);
27
+
28
+ // src/config-resolver.types.ts
29
+ function isConfigVariant(obj) {
30
+ return obj && (obj.fields !== void 0 || obj.extends !== void 0);
31
+ }
32
+
33
+ // src/config-resolver.ts
34
+ var import_deepmerge_ts = require("deepmerge-ts");
35
+ var import_utils = require("@axi-engine/utils");
36
+ var ConfigResolver = class {
37
+ constructor(configs) {
38
+ this.configs = configs;
39
+ }
40
+ cache = /* @__PURE__ */ new Map();
41
+ /**
42
+ * Retrieves a fully resolved and merged configuration for a given path.
43
+ * @template T A specific subtype of TBase that the caller expects.
44
+ * @param path Path to the config, e.g., 'enemies.boss.goblin'.
45
+ * @returns The resolved configuration object.
46
+ */
47
+ get(path) {
48
+ const pathArr = (0, import_utils.ensurePathArray)(path);
49
+ const fullPathKey = pathArr.join(import_utils.axiSettings.pathSeparator);
50
+ if (this.cache.has(fullPathKey)) {
51
+ return this.cache.get(fullPathKey);
52
+ }
53
+ const config = this.calculateConfig(pathArr);
54
+ this.cache.set(fullPathKey, config);
55
+ return config;
56
+ }
57
+ /**
58
+ * Clears the cache of resolved configurations.
59
+ */
60
+ clearCache() {
61
+ this.cache.clear();
62
+ }
63
+ /**
64
+ * Recursively calculates the configuration by merging a variant with its parents.
65
+ */
66
+ calculateConfig(path, visited = []) {
67
+ const node = this.getNode(path, this.configs);
68
+ const pathStr = path.join(import_utils.axiSettings.pathSeparator);
69
+ (0, import_utils.throwIf)(visited.includes(pathStr), `Cyclic dependency detected in ${pathStr}`);
70
+ visited.push(pathStr);
71
+ const parentNode = node.extends ? this.calculateConfig((0, import_utils.ensurePathArray)(node.extends), [...visited]) : {};
72
+ return (0, import_deepmerge_ts.deepmerge)(parentNode, node);
73
+ }
74
+ /**
75
+ * Traverses the config tree to find the node at the specified path.
76
+ */
77
+ getNode(path, tree) {
78
+ const pathStr = path.join(import_utils.axiSettings.pathSeparator);
79
+ const [current, ...rest] = path;
80
+ const node = tree[current];
81
+ (0, import_utils.throwIfEmpty)(node, `Can't find node with path: ${pathStr}`);
82
+ if (rest.length > 0) {
83
+ (0, import_utils.throwIf)(isConfigVariant(node), `Path leads through a variant, not a tree branch: ${pathStr}`);
84
+ return this.getNode(rest, node);
85
+ }
86
+ (0, import_utils.throwIf)(!isConfigVariant(node), `Path does not lead to a variant: ${pathStr}`);
87
+ return node;
88
+ }
89
+ };
90
+ // Annotate the CommonJS export names for ESM import in node:
91
+ 0 && (module.exports = {
92
+ ConfigResolver,
93
+ isConfigVariant
94
+ });
package/dist/index.mjs CHANGED
@@ -1,72 +1,66 @@
1
- import { deepmerge } from "deepmerge-ts";
2
- import { axiSettings, ensurePathArray, throwIf, throwIfEmpty } from "@axi-engine/utils";
3
-
4
- //#region src/config-resolver.types.ts
5
- /**
6
- * Type guard to check if an object is a ConfigVariant.
7
- */
1
+ // src/config-resolver.types.ts
8
2
  function isConfigVariant(obj) {
9
- return obj && (obj.fields !== void 0 || obj.extends !== void 0);
3
+ return obj && (obj.fields !== void 0 || obj.extends !== void 0);
10
4
  }
11
5
 
12
- //#endregion
13
- //#region src/config-resolver.ts
14
- /**
15
- * Resolves hierarchical configurations with inheritance and caching.
16
- * @template TBase The base type for all configurations this resolver handles.
17
- */
6
+ // src/config-resolver.ts
7
+ import { deepmerge } from "deepmerge-ts";
8
+ import { axiSettings, ensurePathArray, throwIf, throwIfEmpty } from "@axi-engine/utils";
18
9
  var ConfigResolver = class {
19
- cache = /* @__PURE__ */ new Map();
20
- constructor(configs) {
21
- this.configs = configs;
22
- }
23
- /**
24
- * Retrieves a fully resolved and merged configuration for a given path.
25
- * @template T A specific subtype of TBase that the caller expects.
26
- * @param path Path to the config, e.g., 'enemies.boss.goblin'.
27
- * @returns The resolved configuration object.
28
- */
29
- get(path) {
30
- const pathArr = ensurePathArray(path);
31
- const fullPathKey = pathArr.join(axiSettings.pathSeparator);
32
- if (this.cache.has(fullPathKey)) return this.cache.get(fullPathKey);
33
- const config = this.calculateConfig(pathArr);
34
- this.cache.set(fullPathKey, config);
35
- return config;
36
- }
37
- /**
38
- * Clears the cache of resolved configurations.
39
- */
40
- clearCache() {
41
- this.cache.clear();
42
- }
43
- /**
44
- * Recursively calculates the configuration by merging a variant with its parents.
45
- */
46
- calculateConfig(path, visited = []) {
47
- const node = this.getNode(path, this.configs);
48
- const pathStr = path.join(axiSettings.pathSeparator);
49
- throwIf(visited.includes(pathStr), `Cyclic dependency detected in ${pathStr}`);
50
- visited.push(pathStr);
51
- return deepmerge(node.extends ? this.calculateConfig(ensurePathArray(node.extends), [...visited]) : {}, node);
52
- }
53
- /**
54
- * Traverses the config tree to find the node at the specified path.
55
- */
56
- getNode(path, tree) {
57
- const pathStr = path.join(axiSettings.pathSeparator);
58
- const [current, ...rest] = path;
59
- const node = tree[current];
60
- throwIfEmpty(node, `Can't find node with path: ${pathStr}`);
61
- if (rest.length > 0) {
62
- throwIf(isConfigVariant(node), `Path leads through a variant, not a tree branch: ${pathStr}`);
63
- return this.getNode(rest, node);
64
- }
65
- throwIf(!isConfigVariant(node), `Path does not lead to a variant: ${pathStr}`);
66
- return node;
67
- }
10
+ constructor(configs) {
11
+ this.configs = configs;
12
+ }
13
+ cache = /* @__PURE__ */ new Map();
14
+ /**
15
+ * Retrieves a fully resolved and merged configuration for a given path.
16
+ * @template T A specific subtype of TBase that the caller expects.
17
+ * @param path Path to the config, e.g., 'enemies.boss.goblin'.
18
+ * @returns The resolved configuration object.
19
+ */
20
+ get(path) {
21
+ const pathArr = ensurePathArray(path);
22
+ const fullPathKey = pathArr.join(axiSettings.pathSeparator);
23
+ if (this.cache.has(fullPathKey)) {
24
+ return this.cache.get(fullPathKey);
25
+ }
26
+ const config = this.calculateConfig(pathArr);
27
+ this.cache.set(fullPathKey, config);
28
+ return config;
29
+ }
30
+ /**
31
+ * Clears the cache of resolved configurations.
32
+ */
33
+ clearCache() {
34
+ this.cache.clear();
35
+ }
36
+ /**
37
+ * Recursively calculates the configuration by merging a variant with its parents.
38
+ */
39
+ calculateConfig(path, visited = []) {
40
+ const node = this.getNode(path, this.configs);
41
+ const pathStr = path.join(axiSettings.pathSeparator);
42
+ throwIf(visited.includes(pathStr), `Cyclic dependency detected in ${pathStr}`);
43
+ visited.push(pathStr);
44
+ const parentNode = node.extends ? this.calculateConfig(ensurePathArray(node.extends), [...visited]) : {};
45
+ return deepmerge(parentNode, node);
46
+ }
47
+ /**
48
+ * Traverses the config tree to find the node at the specified path.
49
+ */
50
+ getNode(path, tree) {
51
+ const pathStr = path.join(axiSettings.pathSeparator);
52
+ const [current, ...rest] = path;
53
+ const node = tree[current];
54
+ throwIfEmpty(node, `Can't find node with path: ${pathStr}`);
55
+ if (rest.length > 0) {
56
+ throwIf(isConfigVariant(node), `Path leads through a variant, not a tree branch: ${pathStr}`);
57
+ return this.getNode(rest, node);
58
+ }
59
+ throwIf(!isConfigVariant(node), `Path does not lead to a variant: ${pathStr}`);
60
+ return node;
61
+ }
62
+ };
63
+ export {
64
+ ConfigResolver,
65
+ isConfigVariant
68
66
  };
69
-
70
- //#endregion
71
- export { ConfigResolver, isConfigVariant };
72
- //# sourceMappingURL=index.mjs.map
package/package.json CHANGED
@@ -1,44 +1,44 @@
1
- {
2
- "name": "@axi-engine/configs",
3
- "version": "0.1.2",
4
- "description": "A lightweight, type-safe, and hierarchical configuration resolver",
5
- "license": "MIT",
6
- "repository": {
7
- "type": "git",
8
- "url": "https://github.com/axijs/engine.git",
9
- "directory": "packages/configs"
10
- },
11
- "keywords": [
12
- "axi-engine",
13
- "typescript",
14
- "gamedev",
15
- "configs"
16
- ],
17
- "types": "./dist/index.d.ts",
18
- "main": "./dist/index.js",
19
- "module": "./dist/index.mjs",
20
- "exports": {
21
- ".": {
22
- "types": "./dist/index.d.ts",
23
- "import": "./dist/index.mjs",
24
- "require": "./dist/index.js"
25
- }
26
- },
27
- "scripts": {
28
- "build": "tsdown",
29
- "docs": "typedoc src/index.ts --out docs/api --options ../../typedoc.json",
30
- "test": "echo 'No tests yet for @axi-engine/configs'"
31
- },
32
- "files": [
33
- "dist"
34
- ],
35
- "dependencies": {
36
- "deepmerge-ts": "^7.1.5"
37
- },
38
- "devDependencies": {
39
- "@axi-engine/utils": "^0.1.6"
40
- },
41
- "peerDependencies": {
42
- "@axi-engine/utils": "^0.1.6"
43
- }
44
- }
1
+ {
2
+ "name": "@axi-engine/configs",
3
+ "version": "0.1.3",
4
+ "description": "A lightweight, type-safe, and hierarchical configuration resolver",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/axijs/engine.git",
9
+ "directory": "packages/configs"
10
+ },
11
+ "keywords": [
12
+ "axi-engine",
13
+ "typescript",
14
+ "gamedev",
15
+ "configs"
16
+ ],
17
+ "types": "./dist/index.d.ts",
18
+ "main": "./dist/index.js",
19
+ "module": "./dist/index.mjs",
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist/index.d.ts",
23
+ "import": "./dist/index.mjs",
24
+ "require": "./dist/index.js"
25
+ }
26
+ },
27
+ "scripts": {
28
+ "build": "tsup",
29
+ "docs": "typedoc src/index.ts --out docs/api --options ../../typedoc.json",
30
+ "test": "echo 'No tests yet for @axi-engine/configs'"
31
+ },
32
+ "files": [
33
+ "dist"
34
+ ],
35
+ "dependencies": {
36
+ "deepmerge-ts": "^7.1.5"
37
+ },
38
+ "devDependencies": {
39
+ "@axi-engine/utils": "^0.1.6"
40
+ },
41
+ "peerDependencies": {
42
+ "@axi-engine/utils": "^0.1.6"
43
+ }
44
+ }
package/dist/index.cjs DELETED
@@ -1,72 +0,0 @@
1
- let deepmerge_ts = require("deepmerge-ts");
2
- let _axi_engine_utils = require("@axi-engine/utils");
3
-
4
- //#region src/config-resolver.types.ts
5
- /**
6
- * Type guard to check if an object is a ConfigVariant.
7
- */
8
- function isConfigVariant(obj) {
9
- return obj && (obj.fields !== void 0 || obj.extends !== void 0);
10
- }
11
-
12
- //#endregion
13
- //#region src/config-resolver.ts
14
- /**
15
- * Resolves hierarchical configurations with inheritance and caching.
16
- * @template TBase The base type for all configurations this resolver handles.
17
- */
18
- var ConfigResolver = class {
19
- cache = /* @__PURE__ */ new Map();
20
- constructor(configs) {
21
- this.configs = configs;
22
- }
23
- /**
24
- * Retrieves a fully resolved and merged configuration for a given path.
25
- * @template T A specific subtype of TBase that the caller expects.
26
- * @param path Path to the config, e.g., 'enemies.boss.goblin'.
27
- * @returns The resolved configuration object.
28
- */
29
- get(path) {
30
- const pathArr = (0, _axi_engine_utils.ensurePathArray)(path);
31
- const fullPathKey = pathArr.join(_axi_engine_utils.axiSettings.pathSeparator);
32
- if (this.cache.has(fullPathKey)) return this.cache.get(fullPathKey);
33
- const config = this.calculateConfig(pathArr);
34
- this.cache.set(fullPathKey, config);
35
- return config;
36
- }
37
- /**
38
- * Clears the cache of resolved configurations.
39
- */
40
- clearCache() {
41
- this.cache.clear();
42
- }
43
- /**
44
- * Recursively calculates the configuration by merging a variant with its parents.
45
- */
46
- calculateConfig(path, visited = []) {
47
- const node = this.getNode(path, this.configs);
48
- const pathStr = path.join(_axi_engine_utils.axiSettings.pathSeparator);
49
- (0, _axi_engine_utils.throwIf)(visited.includes(pathStr), `Cyclic dependency detected in ${pathStr}`);
50
- visited.push(pathStr);
51
- return (0, deepmerge_ts.deepmerge)(node.extends ? this.calculateConfig((0, _axi_engine_utils.ensurePathArray)(node.extends), [...visited]) : {}, node);
52
- }
53
- /**
54
- * Traverses the config tree to find the node at the specified path.
55
- */
56
- getNode(path, tree) {
57
- const pathStr = path.join(_axi_engine_utils.axiSettings.pathSeparator);
58
- const [current, ...rest] = path;
59
- const node = tree[current];
60
- (0, _axi_engine_utils.throwIfEmpty)(node, `Can't find node with path: ${pathStr}`);
61
- if (rest.length > 0) {
62
- (0, _axi_engine_utils.throwIf)(isConfigVariant(node), `Path leads through a variant, not a tree branch: ${pathStr}`);
63
- return this.getNode(rest, node);
64
- }
65
- (0, _axi_engine_utils.throwIf)(!isConfigVariant(node), `Path does not lead to a variant: ${pathStr}`);
66
- return node;
67
- }
68
- };
69
-
70
- //#endregion
71
- exports.ConfigResolver = ConfigResolver;
72
- exports.isConfigVariant = isConfigVariant;
package/dist/index.d.cts DELETED
@@ -1,56 +0,0 @@
1
- import { PathType } from "@axi-engine/utils";
2
-
3
- //#region src/config-resolver.types.d.ts
4
- /**
5
- * Represents a specific configuration variant that can extend another variant.
6
- * @template T The base type of the configuration object.
7
- */
8
- type ConfigVariant<T extends object> = {
9
- /** Path to a base config (or multiple configs) that this variant extends. */extends?: PathType; /** The specific fields that override or extend the base config. */
10
- fields?: Partial<T>;
11
- };
12
- /**
13
- * Represents the hierarchical structure of the configuration.
14
- * It's a recursive type that can contain other trees or final variants.
15
- * @template T The base type of the configuration object.
16
- */
17
- type ConfigTree<T extends object> = {
18
- [key: string]: ConfigVariant<T> | ConfigTree<T>;
19
- };
20
- /**
21
- * Type guard to check if an object is a ConfigVariant.
22
- */
23
- declare function isConfigVariant<T extends object>(obj: any): obj is ConfigVariant<T>;
24
- //#endregion
25
- //#region src/config-resolver.d.ts
26
- /**
27
- * Resolves hierarchical configurations with inheritance and caching.
28
- * @template TBase The base type for all configurations this resolver handles.
29
- */
30
- declare class ConfigResolver<TBase extends object> {
31
- readonly configs: ConfigTree<TBase>;
32
- private cache;
33
- constructor(configs: ConfigTree<TBase>);
34
- /**
35
- * Retrieves a fully resolved and merged configuration for a given path.
36
- * @template T A specific subtype of TBase that the caller expects.
37
- * @param path Path to the config, e.g., 'enemies.boss.goblin'.
38
- * @returns The resolved configuration object.
39
- */
40
- get<T extends TBase>(path: PathType): T;
41
- /**
42
- * Clears the cache of resolved configurations.
43
- */
44
- clearCache(): void;
45
- /**
46
- * Recursively calculates the configuration by merging a variant with its parents.
47
- */
48
- private calculateConfig;
49
- /**
50
- * Traverses the config tree to find the node at the specified path.
51
- */
52
- private getNode;
53
- }
54
- //#endregion
55
- export { ConfigResolver, ConfigTree, ConfigVariant, isConfigVariant };
56
- //# sourceMappingURL=index.d.cts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../src/config-resolver.types.ts","../src/config-resolver.ts"],"mappings":";;;;AAMA;;;KAAY,aAAA;EAAA,6EAAA,OAAA,GAEA,QAAA;EAAA,MAAA,GAED,OAAA,CAAQ,CAAA;AAAA;AAAA;;;AAQnB;;AARmB,KAQP,UAAA;EAAA,CAAA,GAAA,WACK,aAAA,CAAc,CAAA,IAAK,UAAA,CAAW,CAAA;AAAA;AAAA;;;AAAA,iBAM/B,eAAA,kBAAA,CAAA,GAAA,QAAA,GAAA,IAAoD,aAAA,CAAc,CAAA;;;;ACjBlF;;;cAAa,cAAA;EAAA,SAAA,OAAA,EAG0B,UAAA,CAAW,KAAA;EAAA,QAAA,KAAA;EAAA,YAAA,OAAA,EAAX,UAAA,CAAW,KAAA;EAAA;;;;;;EAAA,GAAA,WAQlC,KAAA,CAAA,CAAA,IAAA,EAAa,QAAA,GAAW,CAAA;EAAA;;;EAAA,WAAA;EAAA;;;EAAA,QAAA,eAAA;EAAA;;;EAAA,QAAA,OAAA;AAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/config-resolver.types.ts","../src/config-resolver.ts"],"mappings":";;;;AAMA;;;KAAY,aAAA;EAAA,6EAAA,OAAA,GAEA,QAAA;EAAA,MAAA,GAED,OAAA,CAAQ,CAAA;AAAA;AAAA;;;AAQnB;;AARmB,KAQP,UAAA;EAAA,CAAA,GAAA,WACK,aAAA,CAAc,CAAA,IAAK,UAAA,CAAW,CAAA;AAAA;AAAA;;;AAAA,iBAM/B,eAAA,kBAAA,CAAA,GAAA,QAAA,GAAA,IAAoD,aAAA,CAAc,CAAA;;;;ACjBlF;;;cAAa,cAAA;EAAA,SAAA,OAAA,EAG0B,UAAA,CAAW,KAAA;EAAA,QAAA,KAAA;EAAA,YAAA,OAAA,EAAX,UAAA,CAAW,KAAA;EAAA;;;;;;EAAA,GAAA,WAQlC,KAAA,CAAA,CAAA,IAAA,EAAa,QAAA,GAAW,CAAA;EAAA;;;EAAA,WAAA;EAAA;;;EAAA,QAAA,eAAA;EAAA;;;EAAA,QAAA,OAAA;AAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/config-resolver.types.ts","../src/config-resolver.ts"],"sourcesContent":["import {PathType} from '@axi-engine/utils';\r\n\r\n/**\r\n * Represents a specific configuration variant that can extend another variant.\r\n * @template T The base type of the configuration object.\r\n */\r\nexport type ConfigVariant<T extends object> = {\r\n /** Path to a base config (or multiple configs) that this variant extends. */\r\n extends?: PathType;\r\n /** The specific fields that override or extend the base config. */\r\n fields?: Partial<T>;\r\n};\r\n\r\n/**\r\n * Represents the hierarchical structure of the configuration.\r\n * It's a recursive type that can contain other trees or final variants.\r\n * @template T The base type of the configuration object.\r\n */\r\nexport type ConfigTree<T extends object> = {\r\n [key: string]: ConfigVariant<T> | ConfigTree<T>;\r\n};\r\n\r\n/**\r\n * Type guard to check if an object is a ConfigVariant.\r\n */\r\nexport function isConfigVariant<T extends object>(obj: any): obj is ConfigVariant<T> {\r\n // The logic remains the same, but the signature is now generic.\r\n return obj && (obj.fields !== undefined || obj.extends !== undefined);\r\n}\r\n","import {deepmerge} from \"deepmerge-ts\";\r\nimport {ConfigTree, ConfigVariant, isConfigVariant} from './config-resolver.types';\r\nimport {axiSettings, ensurePathArray, PathType, throwIf, throwIfEmpty} from '@axi-engine/utils';\r\n\r\n/**\r\n * Resolves hierarchical configurations with inheritance and caching.\r\n * @template TBase The base type for all configurations this resolver handles.\r\n */\r\nexport class ConfigResolver<TBase extends object> {\r\n private cache = new Map<string, TBase>();\r\n\r\n constructor(public readonly configs: ConfigTree<TBase>) {}\r\n\r\n /**\r\n * Retrieves a fully resolved and merged configuration for a given path.\r\n * @template T A specific subtype of TBase that the caller expects.\r\n * @param path Path to the config, e.g., 'enemies.boss.goblin'.\r\n * @returns The resolved configuration object.\r\n */\r\n get<T extends TBase>(path: PathType): T {\r\n const pathArr = ensurePathArray(path);\r\n const fullPathKey = pathArr.join(axiSettings.pathSeparator);\r\n\r\n if (this.cache.has(fullPathKey)) {\r\n return this.cache.get(fullPathKey) as T;\r\n }\r\n\r\n const config = this.calculateConfig<T>(pathArr);\r\n this.cache.set(fullPathKey, config);\r\n return config;\r\n }\r\n\r\n /**\r\n * Clears the cache of resolved configurations.\r\n */\r\n clearCache(): void {\r\n this.cache.clear();\r\n }\r\n\r\n /**\r\n * Recursively calculates the configuration by merging a variant with its parents.\r\n */\r\n private calculateConfig<T extends TBase>(path: string[], visited: string[] = []): T {\r\n const node = this.getNode(path, this.configs) as ConfigVariant<T>;\r\n const pathStr = path.join(axiSettings.pathSeparator);\r\n\r\n throwIf(visited.includes(pathStr), `Cyclic dependency detected in ${pathStr}`);\r\n visited.push(pathStr);\r\n\r\n const parentNode = node.extends ? this.calculateConfig<T>(ensurePathArray(node.extends), [...visited]) : {};\r\n return deepmerge(parentNode, node) as T;\r\n }\r\n\r\n /**\r\n * Traverses the config tree to find the node at the specified path.\r\n */\r\n private getNode(path: string[], tree: ConfigTree<TBase>): ConfigVariant<TBase> {\r\n const pathStr = path.join(axiSettings.pathSeparator);\r\n const [current, ...rest] = path;\r\n const node = tree[current];\r\n throwIfEmpty(node, `Can't find node with path: ${pathStr}`);\r\n\r\n if (rest.length > 0) {\r\n throwIf(isConfigVariant(node), `Path leads through a variant, not a tree branch: ${pathStr}`);\r\n return this.getNode(rest, node as ConfigTree<TBase>);\r\n }\r\n\r\n throwIf(!isConfigVariant(node), `Path does not lead to a variant: ${pathStr}`);\r\n return node as ConfigVariant<TBase>;\r\n }\r\n}\r\n"],"mappings":";;;;;;;AAyBA,SAAgB,gBAAkC,KAAmC;AAEnF,QAAO,QAAQ,IAAI,WAAW,UAAa,IAAI,YAAY;;;;;;;;;ACnB7D,IAAa,iBAAb,MAAkD;CAChD,AAAQ,wBAAQ,IAAI,KAAoB;CAExC,YAAY,AAAgB,SAA4B;EAA5B;;;;;;;;CAQ5B,IAAqB,MAAmB;EACtC,MAAM,UAAU,gBAAgB,KAAK;EACrC,MAAM,cAAc,QAAQ,KAAK,YAAY,cAAc;AAE3D,MAAI,KAAK,MAAM,IAAI,YAAY,CAC7B,QAAO,KAAK,MAAM,IAAI,YAAY;EAGpC,MAAM,SAAS,KAAK,gBAAmB,QAAQ;AAC/C,OAAK,MAAM,IAAI,aAAa,OAAO;AACnC,SAAO;;;;;CAMT,aAAmB;AACjB,OAAK,MAAM,OAAO;;;;;CAMpB,AAAQ,gBAAiC,MAAgB,UAAoB,EAAE,EAAK;EAClF,MAAM,OAAO,KAAK,QAAQ,MAAM,KAAK,QAAQ;EAC7C,MAAM,UAAU,KAAK,KAAK,YAAY,cAAc;AAEpD,UAAQ,QAAQ,SAAS,QAAQ,EAAE,iCAAiC,UAAU;AAC9E,UAAQ,KAAK,QAAQ;AAGrB,SAAO,UADY,KAAK,UAAU,KAAK,gBAAmB,gBAAgB,KAAK,QAAQ,EAAE,CAAC,GAAG,QAAQ,CAAC,GAAG,EAAE,EAC9E,KAAK;;;;;CAMpC,AAAQ,QAAQ,MAAgB,MAA+C;EAC7E,MAAM,UAAU,KAAK,KAAK,YAAY,cAAc;EACpD,MAAM,CAAC,SAAS,GAAG,QAAQ;EAC3B,MAAM,OAAO,KAAK;AAClB,eAAa,MAAM,8BAA8B,UAAU;AAE3D,MAAI,KAAK,SAAS,GAAG;AACnB,WAAQ,gBAAgB,KAAK,EAAE,oDAAoD,UAAU;AAC7F,UAAO,KAAK,QAAQ,MAAM,KAA0B;;AAGtD,UAAQ,CAAC,gBAAgB,KAAK,EAAE,oCAAoC,UAAU;AAC9E,SAAO"}