@daiso-tech/core
Version:
The library offers flexible, framework-agnostic solutions for modern web applications, built on adaptable components that integrate seamlessly with popular frameworks like Next Js.
122 lines • 3.93 kB
JavaScript
/**
* @module ConfigAccessor
*/
import {} from "@standard-schema/spec";
import {} from "../../config-accessor/contracts/_module.js";
import { validateSync, UnexpectedError } from "../../utilities/_module.js";
/**
* `ConfigAccessor` provides type-safe access to configuration objects, with optional schema validation.
*
* **Note:** Only exactly 1 level of nesting is supported for config fields (e.g., "app.foo").
* Paths with more than 1 level of nesting are not supported.
*
* @template TOutputConfig The configuration object type.
* @group Implementations
*/
export class ConfigAccessor {
configObject;
/**
* @example
* ```ts
* import { ConfigAccessor } from "@daiso-tech/core/config-accessor";
* import { z } from "zod";
*
* const config = {}
*
* const schema = z.object({
* // Supports primitive string, number, boolean values
* a: z.string(),
*
* // Supports nested object with fields of string, number, boolean values
* b: z.object({
* a: z.string(),
* }),
*
* // Supports array with item of string, number, boolean values
* c: z.string().array(),
*
* // Supports array of object with fields of string, number, boolean values
* d: z.object({
* a: z.string(),
* })
* .array(),
* })
*
* const accessor = new ConfigAccessor({
* config,
* // Schema is optional, you can pass in a type
* schema,
* })
*
* // Return the value of field a
* accessor.get("a")
*
* // Return the value of field b which is an object
* accessor.get("b")
*
* // Return the value of field b.a which is an primitive
* accessor.get("b.a")
*
* // Return the first item of field c which an primitive
* accessor.get("c.1")
*
* // Return the first item of field d which an object
* accessor.get("d.2")
* ```
*/
constructor(settings) {
const { config: configObject, schema } = settings;
if (schema !== undefined) {
this.configObject = validateSync(schema, configObject);
}
else {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
this.configObject = configObject;
}
}
extractPathSegments(path) {
const path_ = path;
const [root, nested, ...rest] = path_.split(".");
if (rest.length > 0) {
throw new UnexpectedError("Config path supports at most one nested path segment.");
}
if (root === undefined) {
throw new UnexpectedError("BUG: Config path must specify a field name (received undefined). This is an internal error and should be reported.");
}
return {
root,
nested,
};
}
get(path) {
const { root: field, nested: nestedField } = this.extractPathSegments(path);
const configObject_ = this.configObject;
if (
// Is array
Array.isArray(configObject_[field]) &&
// Nested field exists
nestedField !== undefined) {
const nestedValue = configObject_[field][Number(nestedField)] ?? null;
return nestedValue;
}
if (
// Is object
typeof configObject_[field] === "object" &&
!Array.isArray(configObject_[field]) &&
// Nested field exists
nestedField !== undefined) {
const nestedValue = configObject_[field][nestedField] ?? null;
return nestedValue;
}
const rootValue = configObject_[field] ?? null;
return rootValue;
}
getOr(path, defaultValue) {
const value = this.get(path);
if (value === null) {
return defaultValue;
}
return value;
}
}
//# sourceMappingURL=config-accessor.js.map