@axi-engine/configs 0.1.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/README.md ADDED
@@ -0,0 +1,8 @@
1
+ # @axi-engine/configs
2
+
3
+ [![NPM version](https://img.shields.io/npm/v/@axi-engine/utils.svg)](https://www.npmjs.com/package/@axi-engine/configs)
4
+
5
+
6
+ ## Description
7
+
8
+ Description
@@ -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 };
@@ -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 ADDED
@@ -0,0 +1,66 @@
1
+ // src/config-resolver.types.ts
2
+ function isConfigVariant(obj) {
3
+ return obj && (obj.fields !== void 0 || obj.extends !== void 0);
4
+ }
5
+
6
+ // src/config-resolver.ts
7
+ import { deepmerge } from "deepmerge-ts";
8
+ import { axiSettings, ensurePathArray, throwIf, throwIfEmpty } from "@axi-engine/utils";
9
+ var ConfigResolver = class {
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
66
+ };
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@axi-engine/configs",
3
+ "version": "0.1.0",
4
+ "description": "",
5
+ "license": "MIT",
6
+ "keywords": [
7
+ "axi-engine",
8
+ "typescript",
9
+ "gamedev",
10
+ "configs"
11
+ ],
12
+ "types": "./dist/index.d.ts",
13
+ "main": "./dist/index.js",
14
+ "module": "./dist/index.mjs",
15
+ "exports": {
16
+ ".": {
17
+ "types": "./dist/index.d.ts",
18
+ "import": "./dist/index.mjs",
19
+ "require": "./dist/index.js"
20
+ }
21
+ },
22
+ "scripts": {
23
+ "build": "tsup",
24
+ "docs": "typedoc src/index.ts --out docs/api --options ../../typedoc.json",
25
+ "test": "echo 'No tests yet for @axi-engine/configs'"
26
+ },
27
+ "files": [
28
+ "dist"
29
+ ],
30
+ "dependencies": {
31
+ "@axi-engine/utils": "^0.1.6",
32
+ "deepmerge-ts": "^7.1.5"
33
+ }
34
+ }