@cerios/cerios-builder
Advanced tools
+218
-21
| /** | ||
| * Helper type to extract the builder type from a builder instance. | ||
| * This is useful when you want to accept a builder with some fields already set | ||
| * without manually specifying which fields are set. | ||
| * | ||
| * @template B - A builder instance type | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * // Instead of: | ||
| * function withAddress( | ||
| * builder: AddressBuilder & CeriosBrand<Pick<Address, "city" | "country">> | ||
| * ) { ... } | ||
| * | ||
| * // You can write: | ||
| * function withAddress(builder: BuilderType<ReturnType<typeof AddressBuilder.createWithDefaults>>) { ... } | ||
| * ``` | ||
| */ | ||
| type BuilderType<B> = B; | ||
| /** | ||
| * Recursively makes all properties readonly for deep immutability. | ||
| * Handles arrays, objects, and primitive types. | ||
| * | ||
| * @template T - The type to make deeply readonly | ||
| */ | ||
| type DeepReadonly<T> = T extends (infer R)[] ? DeepReadonlyArray<R> : T extends (...args: any[]) => any ? T : T extends object ? DeepReadonlyObject<T> : T; | ||
| /** | ||
| * Helper type for deep readonly arrays | ||
| * @internal | ||
| */ | ||
| interface DeepReadonlyArray<T> extends ReadonlyArray<DeepReadonly<T>> { | ||
| } | ||
| /** | ||
| * Helper type for deep readonly objects | ||
| * @internal | ||
| */ | ||
| type DeepReadonlyObject<T> = { | ||
| readonly [P in keyof T]: DeepReadonly<T[P]>; | ||
| }; | ||
| /** | ||
| * Unique symbol used internally to brand types and track which properties have been set in the builder's type. | ||
@@ -33,22 +73,2 @@ * | ||
| /** | ||
| * Recursively makes all properties readonly for deep immutability. | ||
| * Handles arrays, objects, and primitive types. | ||
| * | ||
| * @template T - The type to make deeply readonly | ||
| */ | ||
| type DeepReadonly<T> = T extends (infer R)[] ? DeepReadonlyArray<R> : T extends (...args: any[]) => any ? T : T extends object ? DeepReadonlyObject<T> : T; | ||
| /** | ||
| * Helper type for deep readonly arrays | ||
| * @internal | ||
| */ | ||
| interface DeepReadonlyArray<T> extends ReadonlyArray<DeepReadonly<T>> { | ||
| } | ||
| /** | ||
| * Helper type for deep readonly objects | ||
| * @internal | ||
| */ | ||
| type DeepReadonlyObject<T> = { | ||
| readonly [P in keyof T]: DeepReadonly<T[P]>; | ||
| }; | ||
| /** | ||
| * Cache the root key extraction to avoid repeated computation | ||
@@ -291,2 +311,179 @@ * Handles optional properties by ensuring the key is valid for T | ||
| export { type CeriosBrand, CeriosBuilder, type RequiredFieldsTemplate }; | ||
| /** | ||
| * Type representing a class constructor that can be instantiated with optional partial data. | ||
| * @template T - The type that the constructor creates | ||
| */ | ||
| type ClassConstructor<T> = new (data?: Partial<T>) => T; | ||
| /** | ||
| * Helper to extract only data properties (exclude methods) from a class type. | ||
| * This allows compile-time validation to work properly with classes. | ||
| */ | ||
| type DataPropertiesOnly<T> = { | ||
| [K in keyof T as T[K] extends (...args: any[]) => any ? never : K]: T[K]; | ||
| }; | ||
| /** | ||
| * Brand type specifically for class builders that only tracks data properties. | ||
| */ | ||
| type CeriosClassBrand<T> = { | ||
| readonly __classBuilderBrand: T; | ||
| }; | ||
| /** | ||
| * Type-safe builder specifically designed for classes. | ||
| * This builder automatically instantiates the target class and provides: | ||
| * - Compile-time validation that works with class methods | ||
| * - Automatic runtime validation of nested class instances | ||
| * - Preservation of decorators and class methods | ||
| * | ||
| * Example usage: | ||
| * ```typescript | ||
| * class Person { | ||
| * name!: string; | ||
| * age!: number; | ||
| * email?: string; | ||
| * | ||
| * constructor(data?: Partial<Person>) { | ||
| * if (data) Object.assign(this, data); | ||
| * } | ||
| * | ||
| * greet() { return `Hello, I'm ${this.name}`; } | ||
| * } | ||
| * | ||
| * const builder = new CeriosClassBuilder(Person); | ||
| * const person = builder | ||
| * .setProperty('name', 'John') | ||
| * .setProperty('age', 30) | ||
| * .build(); | ||
| * ``` | ||
| * | ||
| * @template T - The class type being built | ||
| */ | ||
| declare class CeriosClassBuilder<T extends object> { | ||
| /** | ||
| * The class constructor to instantiate when building. | ||
| * @private | ||
| */ | ||
| private readonly _classConstructor; | ||
| /** | ||
| * The current partial state of the object being built. | ||
| * @private | ||
| */ | ||
| protected readonly _actual: Partial<T>; | ||
| /** | ||
| * Creates a new class builder instance. | ||
| * @param classConstructor - The class constructor to use for building | ||
| * @param data - Optional initial data | ||
| */ | ||
| protected constructor(classConstructor: ClassConstructor<T>, data?: Partial<T>); | ||
| /** | ||
| * Gets the class constructor for this builder. | ||
| * @private | ||
| */ | ||
| private getClassConstructor; | ||
| /** | ||
| * Creates a new instance of the builder with updated data. | ||
| * Subclasses can override this to return the correct subclass type. | ||
| * @private | ||
| */ | ||
| private createBuilder; | ||
| /** | ||
| * Sets a property value and returns a new builder instance with updated type state. | ||
| * @template K - The property key being set | ||
| * @param key - The property key to set | ||
| * @param value - The value to assign to the property | ||
| * @returns A new builder instance with the property set | ||
| */ | ||
| setProperty<K extends keyof DataPropertiesOnly<T>>(key: K, value: DataPropertiesOnly<T>[K]): this & CeriosClassBrand<Pick<DataPropertiesOnly<T>, K>>; | ||
| /** | ||
| * Sets multiple properties at once. | ||
| * @template K - The property keys being set | ||
| * @param props - Object with properties to set | ||
| * @returns A new builder instance with the properties set | ||
| */ | ||
| setProperties<K extends keyof DataPropertiesOnly<T>>(props: Pick<DataPropertiesOnly<T>, K>): this & CeriosClassBrand<Pick<DataPropertiesOnly<T>, K>>; | ||
| /** | ||
| * Adds a value to an array property. | ||
| * @template K - The array property key | ||
| * @template V - The array element type | ||
| * @param key - The array property key | ||
| * @param value - The value to add to the array | ||
| * @returns A new builder instance with the value added | ||
| */ | ||
| addToArrayProperty<K extends { | ||
| [P in keyof DataPropertiesOnly<T>]: NonNullable<DataPropertiesOnly<T>[P]> extends Array<any> ? P : never; | ||
| }[keyof DataPropertiesOnly<T>], V extends DataPropertiesOnly<T>[K] extends Array<infer U> ? U : DataPropertiesOnly<T>[K] extends Array<infer U> | undefined ? U : never>(key: K, value: V): this & CeriosClassBrand<Pick<DataPropertiesOnly<T>, K>>; | ||
| /** | ||
| * Builds the final class instance with compile-time and runtime validation. | ||
| * - Compile-time: TypeScript enforces all data properties are set | ||
| * - Runtime: Validates nested class instances are properly instantiated | ||
| * | ||
| * @returns The fully built and validated class instance | ||
| * @throws {Error} If nested validation fails | ||
| */ | ||
| build(this: this & CeriosClassBrand<DataPropertiesOnly<T>>): T; | ||
| /** | ||
| * Builds the class instance with runtime validation only (no compile-time). | ||
| * Use this when building from external/dynamic data. | ||
| * | ||
| * @returns The built class instance | ||
| * @throws {Error} If validation fails | ||
| */ | ||
| buildWithoutCompileTimeValidation(): T; | ||
| /** | ||
| * Builds the class instance without any validation. | ||
| * Use only when you're certain the object is valid. | ||
| * | ||
| * @returns The built class instance (may be incomplete) | ||
| */ | ||
| buildUnsafe(): T; | ||
| /** | ||
| * Builds a partial object (may not have all required fields). | ||
| * | ||
| * @returns The partially built object | ||
| */ | ||
| buildPartial(): Partial<T>; | ||
| /** | ||
| * Builds and freezes the class instance (shallow freeze). | ||
| * | ||
| * @returns The frozen class instance | ||
| * @throws {Error} If validation fails | ||
| */ | ||
| buildFrozen(this: this & CeriosClassBrand<DataPropertiesOnly<T>>): Readonly<T>; | ||
| /** | ||
| * Builds and deeply freezes the class instance. | ||
| * | ||
| * @returns The deeply frozen class instance | ||
| * @throws {Error} If validation fails | ||
| */ | ||
| buildDeepFrozen(this: this & CeriosClassBrand<DataPropertiesOnly<T>>): DeepReadonly<T>; | ||
| /** | ||
| * Builds and seals the class instance (shallow freeze). | ||
| * | ||
| * @returns The sealed class instance | ||
| * @throws {Error} If validation fails | ||
| */ | ||
| buildSealed(this: this & CeriosClassBrand<DataPropertiesOnly<T>>): T; | ||
| /** | ||
| * Builds and deeply seals the class instance. | ||
| * | ||
| * @returns The deeply sealed class instance | ||
| * @throws {Error} If validation fails | ||
| */ | ||
| buildDeepSealed(this: this & CeriosClassBrand<DataPropertiesOnly<T>>): T; | ||
| /** | ||
| * Deep freeze helper. | ||
| * @private | ||
| */ | ||
| private deepFreeze; | ||
| /** | ||
| * Deep seal helper. | ||
| * @private | ||
| */ | ||
| private deepSeal; | ||
| /** | ||
| * Deep clone helper for nested objects. | ||
| * @private | ||
| */ | ||
| private deepClone; | ||
| } | ||
| export { type BuilderType, type CeriosBrand, CeriosBuilder, CeriosClassBuilder, type ClassConstructor, type DeepReadonly, type RequiredFieldsTemplate }; |
+218
-21
| /** | ||
| * Helper type to extract the builder type from a builder instance. | ||
| * This is useful when you want to accept a builder with some fields already set | ||
| * without manually specifying which fields are set. | ||
| * | ||
| * @template B - A builder instance type | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * // Instead of: | ||
| * function withAddress( | ||
| * builder: AddressBuilder & CeriosBrand<Pick<Address, "city" | "country">> | ||
| * ) { ... } | ||
| * | ||
| * // You can write: | ||
| * function withAddress(builder: BuilderType<ReturnType<typeof AddressBuilder.createWithDefaults>>) { ... } | ||
| * ``` | ||
| */ | ||
| type BuilderType<B> = B; | ||
| /** | ||
| * Recursively makes all properties readonly for deep immutability. | ||
| * Handles arrays, objects, and primitive types. | ||
| * | ||
| * @template T - The type to make deeply readonly | ||
| */ | ||
| type DeepReadonly<T> = T extends (infer R)[] ? DeepReadonlyArray<R> : T extends (...args: any[]) => any ? T : T extends object ? DeepReadonlyObject<T> : T; | ||
| /** | ||
| * Helper type for deep readonly arrays | ||
| * @internal | ||
| */ | ||
| interface DeepReadonlyArray<T> extends ReadonlyArray<DeepReadonly<T>> { | ||
| } | ||
| /** | ||
| * Helper type for deep readonly objects | ||
| * @internal | ||
| */ | ||
| type DeepReadonlyObject<T> = { | ||
| readonly [P in keyof T]: DeepReadonly<T[P]>; | ||
| }; | ||
| /** | ||
| * Unique symbol used internally to brand types and track which properties have been set in the builder's type. | ||
@@ -33,22 +73,2 @@ * | ||
| /** | ||
| * Recursively makes all properties readonly for deep immutability. | ||
| * Handles arrays, objects, and primitive types. | ||
| * | ||
| * @template T - The type to make deeply readonly | ||
| */ | ||
| type DeepReadonly<T> = T extends (infer R)[] ? DeepReadonlyArray<R> : T extends (...args: any[]) => any ? T : T extends object ? DeepReadonlyObject<T> : T; | ||
| /** | ||
| * Helper type for deep readonly arrays | ||
| * @internal | ||
| */ | ||
| interface DeepReadonlyArray<T> extends ReadonlyArray<DeepReadonly<T>> { | ||
| } | ||
| /** | ||
| * Helper type for deep readonly objects | ||
| * @internal | ||
| */ | ||
| type DeepReadonlyObject<T> = { | ||
| readonly [P in keyof T]: DeepReadonly<T[P]>; | ||
| }; | ||
| /** | ||
| * Cache the root key extraction to avoid repeated computation | ||
@@ -291,2 +311,179 @@ * Handles optional properties by ensuring the key is valid for T | ||
| export { type CeriosBrand, CeriosBuilder, type RequiredFieldsTemplate }; | ||
| /** | ||
| * Type representing a class constructor that can be instantiated with optional partial data. | ||
| * @template T - The type that the constructor creates | ||
| */ | ||
| type ClassConstructor<T> = new (data?: Partial<T>) => T; | ||
| /** | ||
| * Helper to extract only data properties (exclude methods) from a class type. | ||
| * This allows compile-time validation to work properly with classes. | ||
| */ | ||
| type DataPropertiesOnly<T> = { | ||
| [K in keyof T as T[K] extends (...args: any[]) => any ? never : K]: T[K]; | ||
| }; | ||
| /** | ||
| * Brand type specifically for class builders that only tracks data properties. | ||
| */ | ||
| type CeriosClassBrand<T> = { | ||
| readonly __classBuilderBrand: T; | ||
| }; | ||
| /** | ||
| * Type-safe builder specifically designed for classes. | ||
| * This builder automatically instantiates the target class and provides: | ||
| * - Compile-time validation that works with class methods | ||
| * - Automatic runtime validation of nested class instances | ||
| * - Preservation of decorators and class methods | ||
| * | ||
| * Example usage: | ||
| * ```typescript | ||
| * class Person { | ||
| * name!: string; | ||
| * age!: number; | ||
| * email?: string; | ||
| * | ||
| * constructor(data?: Partial<Person>) { | ||
| * if (data) Object.assign(this, data); | ||
| * } | ||
| * | ||
| * greet() { return `Hello, I'm ${this.name}`; } | ||
| * } | ||
| * | ||
| * const builder = new CeriosClassBuilder(Person); | ||
| * const person = builder | ||
| * .setProperty('name', 'John') | ||
| * .setProperty('age', 30) | ||
| * .build(); | ||
| * ``` | ||
| * | ||
| * @template T - The class type being built | ||
| */ | ||
| declare class CeriosClassBuilder<T extends object> { | ||
| /** | ||
| * The class constructor to instantiate when building. | ||
| * @private | ||
| */ | ||
| private readonly _classConstructor; | ||
| /** | ||
| * The current partial state of the object being built. | ||
| * @private | ||
| */ | ||
| protected readonly _actual: Partial<T>; | ||
| /** | ||
| * Creates a new class builder instance. | ||
| * @param classConstructor - The class constructor to use for building | ||
| * @param data - Optional initial data | ||
| */ | ||
| protected constructor(classConstructor: ClassConstructor<T>, data?: Partial<T>); | ||
| /** | ||
| * Gets the class constructor for this builder. | ||
| * @private | ||
| */ | ||
| private getClassConstructor; | ||
| /** | ||
| * Creates a new instance of the builder with updated data. | ||
| * Subclasses can override this to return the correct subclass type. | ||
| * @private | ||
| */ | ||
| private createBuilder; | ||
| /** | ||
| * Sets a property value and returns a new builder instance with updated type state. | ||
| * @template K - The property key being set | ||
| * @param key - The property key to set | ||
| * @param value - The value to assign to the property | ||
| * @returns A new builder instance with the property set | ||
| */ | ||
| setProperty<K extends keyof DataPropertiesOnly<T>>(key: K, value: DataPropertiesOnly<T>[K]): this & CeriosClassBrand<Pick<DataPropertiesOnly<T>, K>>; | ||
| /** | ||
| * Sets multiple properties at once. | ||
| * @template K - The property keys being set | ||
| * @param props - Object with properties to set | ||
| * @returns A new builder instance with the properties set | ||
| */ | ||
| setProperties<K extends keyof DataPropertiesOnly<T>>(props: Pick<DataPropertiesOnly<T>, K>): this & CeriosClassBrand<Pick<DataPropertiesOnly<T>, K>>; | ||
| /** | ||
| * Adds a value to an array property. | ||
| * @template K - The array property key | ||
| * @template V - The array element type | ||
| * @param key - The array property key | ||
| * @param value - The value to add to the array | ||
| * @returns A new builder instance with the value added | ||
| */ | ||
| addToArrayProperty<K extends { | ||
| [P in keyof DataPropertiesOnly<T>]: NonNullable<DataPropertiesOnly<T>[P]> extends Array<any> ? P : never; | ||
| }[keyof DataPropertiesOnly<T>], V extends DataPropertiesOnly<T>[K] extends Array<infer U> ? U : DataPropertiesOnly<T>[K] extends Array<infer U> | undefined ? U : never>(key: K, value: V): this & CeriosClassBrand<Pick<DataPropertiesOnly<T>, K>>; | ||
| /** | ||
| * Builds the final class instance with compile-time and runtime validation. | ||
| * - Compile-time: TypeScript enforces all data properties are set | ||
| * - Runtime: Validates nested class instances are properly instantiated | ||
| * | ||
| * @returns The fully built and validated class instance | ||
| * @throws {Error} If nested validation fails | ||
| */ | ||
| build(this: this & CeriosClassBrand<DataPropertiesOnly<T>>): T; | ||
| /** | ||
| * Builds the class instance with runtime validation only (no compile-time). | ||
| * Use this when building from external/dynamic data. | ||
| * | ||
| * @returns The built class instance | ||
| * @throws {Error} If validation fails | ||
| */ | ||
| buildWithoutCompileTimeValidation(): T; | ||
| /** | ||
| * Builds the class instance without any validation. | ||
| * Use only when you're certain the object is valid. | ||
| * | ||
| * @returns The built class instance (may be incomplete) | ||
| */ | ||
| buildUnsafe(): T; | ||
| /** | ||
| * Builds a partial object (may not have all required fields). | ||
| * | ||
| * @returns The partially built object | ||
| */ | ||
| buildPartial(): Partial<T>; | ||
| /** | ||
| * Builds and freezes the class instance (shallow freeze). | ||
| * | ||
| * @returns The frozen class instance | ||
| * @throws {Error} If validation fails | ||
| */ | ||
| buildFrozen(this: this & CeriosClassBrand<DataPropertiesOnly<T>>): Readonly<T>; | ||
| /** | ||
| * Builds and deeply freezes the class instance. | ||
| * | ||
| * @returns The deeply frozen class instance | ||
| * @throws {Error} If validation fails | ||
| */ | ||
| buildDeepFrozen(this: this & CeriosClassBrand<DataPropertiesOnly<T>>): DeepReadonly<T>; | ||
| /** | ||
| * Builds and seals the class instance (shallow freeze). | ||
| * | ||
| * @returns The sealed class instance | ||
| * @throws {Error} If validation fails | ||
| */ | ||
| buildSealed(this: this & CeriosClassBrand<DataPropertiesOnly<T>>): T; | ||
| /** | ||
| * Builds and deeply seals the class instance. | ||
| * | ||
| * @returns The deeply sealed class instance | ||
| * @throws {Error} If validation fails | ||
| */ | ||
| buildDeepSealed(this: this & CeriosClassBrand<DataPropertiesOnly<T>>): T; | ||
| /** | ||
| * Deep freeze helper. | ||
| * @private | ||
| */ | ||
| private deepFreeze; | ||
| /** | ||
| * Deep seal helper. | ||
| * @private | ||
| */ | ||
| private deepSeal; | ||
| /** | ||
| * Deep clone helper for nested objects. | ||
| * @private | ||
| */ | ||
| private deepClone; | ||
| } | ||
| export { type BuilderType, type CeriosBrand, CeriosBuilder, CeriosClassBuilder, type ClassConstructor, type DeepReadonly, type RequiredFieldsTemplate }; |
+249
-2
@@ -23,3 +23,4 @@ "use strict"; | ||
| __export(index_exports, { | ||
| CeriosBuilder: () => CeriosBuilder | ||
| CeriosBuilder: () => CeriosBuilder, | ||
| CeriosClassBuilder: () => CeriosClassBuilder | ||
| }); | ||
@@ -387,6 +388,252 @@ module.exports = __toCommonJS(index_exports); | ||
| }; | ||
| // src/cerios-class-builder.ts | ||
| var CeriosClassBuilder = class { | ||
| /** | ||
| * Creates a new class builder instance. | ||
| * @param classConstructor - The class constructor to use for building | ||
| * @param data - Optional initial data | ||
| */ | ||
| constructor(classConstructor, data = {}) { | ||
| this._classConstructor = classConstructor; | ||
| this._actual = data; | ||
| } | ||
| /** | ||
| * Gets the class constructor for this builder. | ||
| * @private | ||
| */ | ||
| getClassConstructor() { | ||
| return this._classConstructor; | ||
| } | ||
| /** | ||
| * Creates a new instance of the builder with updated data. | ||
| * Subclasses can override this to return the correct subclass type. | ||
| * @private | ||
| */ | ||
| createBuilder(data) { | ||
| const BuilderClass = this.constructor; | ||
| return new BuilderClass(this._classConstructor, data); | ||
| } | ||
| /** | ||
| * Sets a property value and returns a new builder instance with updated type state. | ||
| * @template K - The property key being set | ||
| * @param key - The property key to set | ||
| * @param value - The value to assign to the property | ||
| * @returns A new builder instance with the property set | ||
| */ | ||
| setProperty(key, value) { | ||
| const newBuilder = this.createBuilder({ | ||
| ...this._actual, | ||
| [key]: value | ||
| }); | ||
| return newBuilder; | ||
| } | ||
| /** | ||
| * Sets multiple properties at once. | ||
| * @template K - The property keys being set | ||
| * @param props - Object with properties to set | ||
| * @returns A new builder instance with the properties set | ||
| */ | ||
| setProperties(props) { | ||
| const newBuilder = this.createBuilder({ | ||
| ...this._actual, | ||
| ...props | ||
| }); | ||
| return newBuilder; | ||
| } | ||
| /** | ||
| * Adds a value to an array property. | ||
| * @template K - The array property key | ||
| * @template V - The array element type | ||
| * @param key - The array property key | ||
| * @param value - The value to add to the array | ||
| * @returns A new builder instance with the value added | ||
| */ | ||
| addToArrayProperty(key, value) { | ||
| var _a; | ||
| const currentArray = (_a = this._actual[key]) != null ? _a : []; | ||
| const newBuilder = this.createBuilder({ | ||
| ...this._actual, | ||
| [key]: [...currentArray, value] | ||
| }); | ||
| return newBuilder; | ||
| } | ||
| /** | ||
| * Builds the final class instance with compile-time and runtime validation. | ||
| * - Compile-time: TypeScript enforces all data properties are set | ||
| * - Runtime: Validates nested class instances are properly instantiated | ||
| * | ||
| * @returns The fully built and validated class instance | ||
| * @throws {Error} If nested validation fails | ||
| */ | ||
| build() { | ||
| const ctor = this.getClassConstructor(); | ||
| const instance = new ctor(this._actual); | ||
| const dataKeys = Object.keys(this._actual); | ||
| const needsAssign = dataKeys.some((key) => instance[key] === void 0 && this._actual[key] !== void 0); | ||
| if (needsAssign) { | ||
| Object.assign(instance, this._actual); | ||
| } | ||
| return instance; | ||
| } | ||
| /** | ||
| * Builds the class instance with runtime validation only (no compile-time). | ||
| * Use this when building from external/dynamic data. | ||
| * | ||
| * @returns The built class instance | ||
| * @throws {Error} If validation fails | ||
| */ | ||
| buildWithoutCompileTimeValidation() { | ||
| const ctor = this.getClassConstructor(); | ||
| const instance = new ctor(this._actual); | ||
| const dataKeys = Object.keys(this._actual); | ||
| const needsAssign = dataKeys.some((key) => instance[key] === void 0 && this._actual[key] !== void 0); | ||
| if (needsAssign) { | ||
| Object.assign(instance, this._actual); | ||
| } | ||
| return instance; | ||
| } | ||
| /** | ||
| * Builds the class instance without any validation. | ||
| * Use only when you're certain the object is valid. | ||
| * | ||
| * @returns The built class instance (may be incomplete) | ||
| */ | ||
| buildUnsafe() { | ||
| const ctor = this.getClassConstructor(); | ||
| const instance = new ctor(this._actual); | ||
| const dataKeys = Object.keys(this._actual); | ||
| const needsAssign = dataKeys.some((key) => instance[key] === void 0 && this._actual[key] !== void 0); | ||
| if (needsAssign) { | ||
| Object.assign(instance, this._actual); | ||
| } | ||
| return instance; | ||
| } | ||
| /** | ||
| * Builds a partial object (may not have all required fields). | ||
| * | ||
| * @returns The partially built object | ||
| */ | ||
| buildPartial() { | ||
| return { ...this._actual }; | ||
| } | ||
| /** | ||
| * Builds and freezes the class instance (shallow freeze). | ||
| * | ||
| * @returns The frozen class instance | ||
| * @throws {Error} If validation fails | ||
| */ | ||
| buildFrozen() { | ||
| const ctor = this.getClassConstructor(); | ||
| const instance = new ctor(this._actual); | ||
| const dataKeys = Object.keys(this._actual); | ||
| const needsAssign = dataKeys.some((key) => instance[key] === void 0 && this._actual[key] !== void 0); | ||
| if (needsAssign) { | ||
| Object.assign(instance, this._actual); | ||
| } | ||
| return Object.freeze(instance); | ||
| } | ||
| /** | ||
| * Builds and deeply freezes the class instance. | ||
| * | ||
| * @returns The deeply frozen class instance | ||
| * @throws {Error} If validation fails | ||
| */ | ||
| buildDeepFrozen() { | ||
| const ctor = this.getClassConstructor(); | ||
| const instance = new ctor(this._actual); | ||
| const dataKeys = Object.keys(this._actual); | ||
| const needsAssign = dataKeys.some((key) => instance[key] === void 0 && this._actual[key] !== void 0); | ||
| if (needsAssign) { | ||
| Object.assign(instance, this._actual); | ||
| } | ||
| return this.deepFreeze(instance); | ||
| } | ||
| /** | ||
| * Builds and seals the class instance (shallow freeze). | ||
| * | ||
| * @returns The sealed class instance | ||
| * @throws {Error} If validation fails | ||
| */ | ||
| buildSealed() { | ||
| const ctor = this.getClassConstructor(); | ||
| const instance = new ctor(this._actual); | ||
| const dataKeys = Object.keys(this._actual); | ||
| const needsAssign = dataKeys.some((key) => instance[key] === void 0 && this._actual[key] !== void 0); | ||
| if (needsAssign) { | ||
| Object.assign(instance, this._actual); | ||
| } | ||
| return Object.seal(instance); | ||
| } | ||
| /** | ||
| * Builds and deeply seals the class instance. | ||
| * | ||
| * @returns The deeply sealed class instance | ||
| * @throws {Error} If validation fails | ||
| */ | ||
| buildDeepSealed() { | ||
| const ctor = this.getClassConstructor(); | ||
| const instance = new ctor(this._actual); | ||
| const dataKeys = Object.keys(this._actual); | ||
| const needsAssign = dataKeys.some((key) => instance[key] === void 0 && this._actual[key] !== void 0); | ||
| if (needsAssign) { | ||
| Object.assign(instance, this._actual); | ||
| } | ||
| return this.deepSeal(instance); | ||
| } | ||
| /** | ||
| * Deep freeze helper. | ||
| * @private | ||
| */ | ||
| deepFreeze(obj) { | ||
| if (obj === null || typeof obj !== "object") { | ||
| return obj; | ||
| } | ||
| Object.getOwnPropertyNames(obj).forEach((prop) => { | ||
| const value = obj[prop]; | ||
| if (value !== null && (typeof value === "object" || typeof value === "function")) { | ||
| this.deepFreeze(value); | ||
| } | ||
| }); | ||
| return Object.freeze(obj); | ||
| } | ||
| /** | ||
| * Deep seal helper. | ||
| * @private | ||
| */ | ||
| deepSeal(obj) { | ||
| if (obj === null || typeof obj !== "object") { | ||
| return obj; | ||
| } | ||
| Object.getOwnPropertyNames(obj).forEach((prop) => { | ||
| const value = obj[prop]; | ||
| if (value !== null && (typeof value === "object" || typeof value === "function")) { | ||
| this.deepSeal(value); | ||
| } | ||
| }); | ||
| return Object.seal(obj); | ||
| } | ||
| /** | ||
| * Deep clone helper for nested objects. | ||
| * @private | ||
| */ | ||
| deepClone(obj) { | ||
| if (obj === null || typeof obj !== "object") { | ||
| return obj; | ||
| } | ||
| if (Array.isArray(obj)) { | ||
| return obj.map((item) => this.deepClone(item)); | ||
| } | ||
| const cloned = {}; | ||
| for (const key of Object.keys(obj)) { | ||
| cloned[key] = this.deepClone(obj[key]); | ||
| } | ||
| return cloned; | ||
| } | ||
| }; | ||
| // Annotate the CommonJS export names for ESM import in node: | ||
| 0 && (module.exports = { | ||
| CeriosBuilder | ||
| CeriosBuilder, | ||
| CeriosClassBuilder | ||
| }); | ||
| //# sourceMappingURL=index.js.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/index.ts","../src/cerios-builder.ts"],"sourcesContent":["export { CeriosBrand, CeriosBuilder, RequiredFieldsTemplate } from \"./cerios-builder.js\";\n","/**\n * Unique symbol used internally to brand types and track which properties have been set in the builder's type.\n *\n * @internal\n */\ndeclare const __brand: unique symbol;\n\n/**\n * Type utility for branding builder types with information about which properties have been set.\n * This is used to enforce compile-time safety for required fields in the builder pattern.\n *\n * @template T - The type representing the set of properties that have been set\n * @internal\n */\nexport type CeriosBrand<T> = { [__brand]: T };\n\n/**\n * Helper type to represent a path through an object structure\n * Handles optional properties by unwrapping them with NonNullable\n */\ntype PathImpl<T, K extends keyof T = keyof T> = K extends string | number\n\t? NonNullable<T[K]> extends Record<string, any>\n\t\t? NonNullable<T[K]> extends Array<any>\n\t\t\t? K\n\t\t\t: K | `${K}.${PathImpl<NonNullable<T[K]>> & string}`\n\t\t: K\n\t: never;\n\nexport type Path<T> = PathImpl<T>;\n\n/**\n * Helper type to get the value at a specific path, handling optional properties\n */\ntype PathValue<T, P> = P extends keyof T\n\t? T[P]\n\t: P extends `${infer K}.${infer Rest}`\n\t\t? K extends keyof T\n\t\t\t? PathValue<NonNullable<T[K]>, Rest>\n\t\t\t: never\n\t\t: never;\n\n/**\n * Type-safe template for defining required fields using an array of paths.\n * Simply list the paths that are required.\n */\nexport type RequiredFieldsTemplate<T> = ReadonlyArray<Path<T>>;\n\n/**\n * Recursively makes all properties readonly for deep immutability.\n * Handles arrays, objects, and primitive types.\n *\n * @template T - The type to make deeply readonly\n */\nexport type DeepReadonly<T> = T extends (infer R)[]\n\t? DeepReadonlyArray<R>\n\t: T extends (...args: any[]) => any\n\t\t? T\n\t\t: T extends object\n\t\t\t? DeepReadonlyObject<T>\n\t\t\t: T;\n\n/**\n * Helper type for deep readonly arrays\n * @internal\n */\ninterface DeepReadonlyArray<T> extends ReadonlyArray<DeepReadonly<T>> {}\n\n/**\n * Helper type for deep readonly objects\n * @internal\n */\ntype DeepReadonlyObject<T> = {\n\treadonly [P in keyof T]: DeepReadonly<T[P]>;\n};\n\n/**\n * Cache the root key extraction to avoid repeated computation\n * Handles optional properties by ensuring the key is valid for T\n * @internal\n */\ntype RootKey<P extends string, T = any> = P extends `${infer K}.${string}`\n\t? K extends keyof T\n\t\t? K\n\t\t: never\n\t: P extends keyof T\n\t\t? P\n\t\t: never;\n\n/**\n * Recursively freezes an object and all its nested properties.\n * @param obj - The object to freeze\n * @returns The frozen object\n * @internal\n */\nfunction deepFreeze<T>(obj: T): T {\n\t// Retrieve the property names defined on obj\n\tObject.getOwnPropertyNames(obj).forEach(prop => {\n\t\tconst value = (obj as any)[prop];\n\n\t\t// Freeze properties before freezing self\n\t\tif (value !== null && (typeof value === \"object\" || typeof value === \"function\")) {\n\t\t\tdeepFreeze(value);\n\t\t}\n\t});\n\n\treturn Object.freeze(obj);\n}\n\n/**\n * Recursively seals an object and all its nested properties.\n * @param obj - The object to seal\n * @returns The sealed object\n * @internal\n */\nfunction deepSeal<T>(obj: T): T {\n\t// Retrieve the property names defined on obj\n\tObject.getOwnPropertyNames(obj).forEach(prop => {\n\t\tconst value = (obj as any)[prop];\n\n\t\t// Seal properties before sealing self\n\t\tif (value !== null && (typeof value === \"object\" || typeof value === \"function\")) {\n\t\t\tdeepSeal(value);\n\t\t}\n\t});\n\n\treturn Object.seal(obj);\n}\n\n/**\n * Abstract base class for creating type-safe builders with automatic property setters and compile-time validation of required fields.\n *\n * This class is intended to be extended by concrete builder implementations for your own types.\n * It provides utility methods for setting properties and building the final object, ensuring that all required fields are set at compile time.\n *\n * Example usage:\n * ```typescript\n * interface MyType { foo: string; bar: number[]; }\n * class MyTypeBuilder extends CeriosBuilder<MyType> {\n * static requiredTemplate: RequiredFieldsTemplate<MyType> = ['foo'];\n * setFoo(value: string) { return this.setProperty('foo', value); }\n * addBar(value: number) { return this.addToArrayProperty('bar', value); }\n * }\n * // Usage:\n * const obj = new MyTypeBuilder({})\n * .setFoo('hello')\n * .addBar(42)\n * .buildSafe(); // Validates that 'foo' is set\n * ```\n *\n * @template T - The complete type being built\n */\nexport abstract class CeriosBuilder<T extends object> {\n\t/**\n\t * Template defining which fields are required for this builder.\n\t * Subclasses should override this to specify their required fields as an array of paths.\n\t * The template is type-safe - only valid paths from type T can be used.\n\t */\n\tstatic requiredTemplate?: ReadonlyArray<string>;\n\n\t/**\n\t * Instance-level required fields that can be populated dynamically.\n\t * This allows adding required fields at runtime via the setRequiredFields method.\n\t * @private\n\t */\n\tprivate _requiredFields: Set<string> = new Set();\n\n\t/**\n\t * Sets the required fields for this builder instance.\n\t * This allows you to dynamically define which fields are required.\n\t *\n\t * @param fields - Array of dot-notation paths to required fields\n\t * @returns The builder instance for chaining\n\t *\n\t * @example\n\t * ```typescript\n\t * const builder = new MyBuilder({})\n\t * .setRequiredFields(['path.to.field1', 'path.to.field2'])\n\t * .setField1('value1')\n\t * .setField2('value2')\n\t * .buildSafe();\n\t * ```\n\t */\n\tsetRequiredFields(fields: ReadonlyArray<Path<T>>): this {\n\t\tthis._requiredFields = new Set([...fields] as string[]);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Gets the combined required fields from both the static template and instance-level fields.\n\t * @private\n\t */\n\tprivate getRequiredTemplate(): ReadonlyArray<string> {\n\t\tconst ctor = this.constructor as typeof CeriosBuilder;\n\t\tconst staticFields = ctor.requiredTemplate || [];\n\t\tconst instanceFields = Array.from(this._requiredFields);\n\n\t\t// Combine and deduplicate\n\t\treturn [...new Set([...staticFields, ...instanceFields])];\n\t}\n\n\t/**\n\t * Validates that all fields in the required template have been set.\n\t * @private\n\t */\n\tprivate validateRequiredFields(): string[] {\n\t\tconst requiredPaths = this.getRequiredTemplate();\n\t\tconst missing: string[] = [];\n\n\t\tfor (const path of requiredPaths) {\n\t\t\tconst keys = path.split(\".\");\n\t\t\tlet current: any = this._actual;\n\n\t\t\tfor (let i = 0; i < keys.length; i++) {\n\t\t\t\tconst key = keys[i];\n\t\t\t\tif (current === null || current === undefined || !(key in current)) {\n\t\t\t\t\tmissing.push(path);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcurrent = current[key];\n\t\t\t}\n\n\t\t\t// Check if the final value is null or undefined\n\t\t\tif (current === null || current === undefined) {\n\t\t\t\tif (!missing.includes(path)) {\n\t\t\t\t\tmissing.push(path);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn missing;\n\t}\n\n\t/**\n\t * Creates a new builder instance. Intended to be called by subclasses.\n\t *\n\t * @param _actual - The current partial state of the object being built\n\t * @param _requiredFields - Optional array of required field paths to preserve across instances\n\t * @protected\n\t */\n\tprotected constructor(\n\t\tprotected readonly _actual: Partial<T>,\n\t\t_requiredFields?: RequiredFieldsTemplate<T>\n\t) {\n\t\tif (_requiredFields) {\n\t\t\tthis._requiredFields = new Set([..._requiredFields] as string[]);\n\t\t}\n\t}\n\n\t/**\n\t * Sets a property value and returns a new builder instance with updated type state.\n\t * This method is intended to be wrapped by concrete builder methods in subclasses.\n\t *\n\t * @template K - The property key being set\n\t * @param key - The property key to set\n\t * @param value - The value to assign to the property\n\t * @returns A new builder instance with the property set and type state updated\n\t * @protected\n\t */\n\tprotected setProperty<K extends keyof T>(key: K, value: T[K]): this & CeriosBrand<Pick<T, K>> {\n\t\tconst BuilderClass = this.constructor as new (data: any, requiredFields?: RequiredFieldsTemplate<T>) => any;\n\t\treturn new BuilderClass(\n\t\t\t{\n\t\t\t\t...this._actual,\n\t\t\t\t[key]: value,\n\t\t\t},\n\t\t\tArray.from(this._requiredFields) as unknown as RequiredFieldsTemplate<T>\n\t\t) as this & CeriosBrand<Pick<T, K>>;\n\t}\n\n\t/**\n\t * Sets multiple property values at once and returns a new builder instance with updated type state.\n\t * @param props - An object with one or more properties to set.\n\t * @returns A new builder instance with the properties set and type state updated.\n\t * @protected\n\t */\n\tprotected setProperties<K extends keyof T>(props: Pick<T, K>): this & CeriosBrand<Pick<T, K>> {\n\t\tconst BuilderClass = this.constructor as new (data: any, requiredFields?: RequiredFieldsTemplate<T>) => any;\n\t\treturn new BuilderClass(\n\t\t\t{\n\t\t\t\t...this._actual,\n\t\t\t\t...props,\n\t\t\t},\n\t\t\tArray.from(this._requiredFields) as unknown as RequiredFieldsTemplate<T>\n\t\t) as this & CeriosBrand<Pick<T, K>>;\n\t}\n\n\t/**\n\t * Sets a deeply nested property value and returns a new builder instance with updated type state.\n\t * This method uses dot notation to set nested properties in a type-safe way.\n\t *\n\t * @template P - The property path (e.g., \"parent.child.grandchild\")\n\t * @param path - The dot-notation path to the property\n\t * @param value - The value to assign to the nested property\n\t * @returns A new builder instance with the nested property set\n\t * @protected\n\t *\n\t * @example\n\t * ```typescript\n\t * builder.setNestedProperty('Order.Details.CustomerId', 'value')\n\t * ```\n\t */\n\tprotected setNestedProperty<P extends Path<T>>(\n\t\tpath: P,\n\t\tvalue: PathValue<T, P>\n\t): this & CeriosBrand<Pick<T, RootKey<P & string, T> extends never ? keyof T : RootKey<P & string, T>>> {\n\t\tconst BuilderClass = this.constructor as new (data: any, requiredFields?: RequiredFieldsTemplate<T>) => any;\n\t\tconst keys = (path as string).split(\".\");\n\t\tconst newActual = this.deepClone(this._actual);\n\n\t\tlet current: any = newActual;\n\t\tfor (let i = 0; i < keys.length - 1; i++) {\n\t\t\tconst key = keys[i];\n\t\t\tif (!(key in current) || typeof current[key] !== \"object\" || current[key] === null) {\n\t\t\t\tcurrent[key] = {};\n\t\t\t} else {\n\t\t\t\tcurrent[key] = this.deepClone(current[key]);\n\t\t\t}\n\t\t\tcurrent = current[key];\n\t\t}\n\n\t\tcurrent[keys[keys.length - 1]] = value;\n\n\t\treturn new BuilderClass(\n\t\t\tnewActual,\n\t\t\tArray.from(this._requiredFields) as unknown as RequiredFieldsTemplate<T>\n\t\t) as this & CeriosBrand<Pick<T, RootKey<P & string, T> extends never ? keyof T : RootKey<P & string, T>>>;\n\t}\n\n\t/**\n\t * Deep clone helper for nested objects\n\t * @private\n\t */\n\tprivate deepClone<V>(obj: V): V {\n\t\tif (obj === null || typeof obj !== \"object\") {\n\t\t\treturn obj;\n\t\t}\n\t\tif (Array.isArray(obj)) {\n\t\t\treturn obj.map(item => this.deepClone(item)) as any;\n\t\t}\n\t\tconst cloned: any = {};\n\t\tconst hasOwn = Object.prototype.hasOwnProperty;\n\t\tfor (const key in obj) {\n\t\t\tif (hasOwn.call(obj, key)) {\n\t\t\t\tcloned[key] = this.deepClone(obj[key]);\n\t\t\t}\n\t\t}\n\t\treturn cloned;\n\t}\n\n\t/**\n\t * Adds a value to an array property and returns a new builder instance with updated type state.\n\t * This method is intended to be wrapped by concrete builder methods in subclasses for array properties.\n\t *\n\t * @template K - The property key (must be an array property)\n\t * @template V - The type of the array element\n\t * @param key - The array property key to add to\n\t * @param value - The value to add to the array\n\t * @returns A new builder instance with the array property updated and type state updated\n\t * @protected\n\t */\n\tprotected addToArrayProperty<\n\t\tK extends { [P in keyof T]: NonNullable<T[P]> extends Array<any> ? P : never }[keyof T],\n\t\tV extends T[K] extends Array<infer U> ? U : T[K] extends Array<infer U> | undefined ? U : never,\n\t>(key: K, value: V): this & CeriosBrand<Pick<T, K>> {\n\t\tconst BuilderClass = this.constructor as new (data: any, requiredFields?: RequiredFieldsTemplate<T>) => any;\n\t\tconst currentArray = (this._actual[key] as Array<V> | undefined) ?? [];\n\t\treturn new BuilderClass(\n\t\t\t{\n\t\t\t\t...this._actual,\n\t\t\t\t[key]: [...currentArray, value],\n\t\t\t},\n\t\t\tArray.from(this._requiredFields) as unknown as RequiredFieldsTemplate<T>\n\t\t) as this & CeriosBrand<Pick<T, K>>;\n\t}\n\n\t/**\n\t * Builds the final object with both compile-time and runtime validation.\n\t * This is the recommended and safest way to build objects.\n\t *\n\t * - Compile-time: TypeScript enforces all required properties are set\n\t * - Runtime: Validates all fields in the requiredTemplate\n\t *\n\t * @returns The fully built object of type T\n\t * @throws {Error} If any required field is missing at runtime\n\t */\n\tbuild(this: this & CeriosBrand<T>): T {\n\t\tconst missing = this.validateRequiredFields();\n\n\t\tif (missing.length > 0) {\n\t\t\tthrow new Error(`Missing required fields: ${missing.join(\", \")}. Please set these fields before calling build.`);\n\t\t}\n\n\t\treturn this._actual as T;\n\t}\n\n\t/**\n\t * Builds the final object with only compile-time validation, skipping runtime checks.\n\t * Use this when you want TypeScript safety but need to skip runtime validation for performance.\n\t *\n\t * - Compile-time: TypeScript enforces all required properties are set\n\t * - Runtime: No validation\n\t *\n\t * @returns The fully built object of type T\n\t */\n\tbuildWithoutRuntimeValidation(this: this & CeriosBrand<T>): T {\n\t\treturn this._actual as T;\n\t}\n\n\t/**\n\t * Builds the final object with only runtime validation, skipping compile-time checks.\n\t * Use this when building from external data where compile-time checks aren't possible.\n\t *\n\t * - Compile-time: No TypeScript enforcement\n\t * - Runtime: Validates all fields in the requiredTemplate\n\t *\n\t * @returns The fully built object of type T\n\t * @throws {Error} If any required field is missing at runtime\n\t */\n\tbuildWithoutCompileTimeValidation(): T {\n\t\tconst missing = this.validateRequiredFields();\n\n\t\tif (missing.length > 0) {\n\t\t\tthrow new Error(`Missing required fields: ${missing.join(\", \")}. Please set these fields before calling build.`);\n\t\t}\n\n\t\treturn this._actual as T;\n\t}\n\n\t/**\n\t * Builds the final object without any validation (neither compile-time nor runtime).\n\t * Use this only when you're certain the object is valid and need maximum performance.\n\t *\n\t * - Compile-time: No TypeScript enforcement\n\t * - Runtime: No validation\n\t *\n\t * @returns The object of type T (may be incomplete)\n\t */\n\tbuildUnsafe(): T {\n\t\treturn this._actual as T;\n\t}\n\n\t/**\n\t * Builds a partial object, which may not have all required fields set.\n\t * This is useful for scenarios where you want to inspect or validate the current state before finalizing.\n\t *\n\t * @returns The partially built object\n\t */\n\tbuildPartial(): Partial<T> {\n\t\treturn this._actual as Partial<T>;\n\t}\n\n\t/**\n\t * @deprecated Use build() instead. buildSafe() is now an alias for build().\n\t * Builds the final object with runtime validation using the requiredTemplate.\n\t * This method validates that all fields in the requiredTemplate array are present.\n\t *\n\t * @returns The fully built object of type T\n\t * @throws {Error} If any required field is missing at runtime\n\t */\n\tbuildSafe(): T {\n\t\treturn this.buildWithoutCompileTimeValidation();\n\t}\n\n\t/**\n\t * Builds and freezes the final object with both compile-time and runtime validation.\n\t * The returned object is shallowly frozen - top-level properties cannot be modified,\n\t * but nested objects remain mutable.\n\t *\n\t * - Compile-time: TypeScript enforces all required properties are set\n\t * - Runtime: Validates all fields in the requiredTemplate and applies Object.freeze()\n\t *\n\t * @returns The frozen object of type Readonly<T>\n\t * @throws {Error} If any required field is missing at runtime\n\t */\n\tbuildFrozen(this: this & CeriosBrand<T>): Readonly<T> {\n\t\tconst missing = this.validateRequiredFields();\n\n\t\tif (missing.length > 0) {\n\t\t\tthrow new Error(`Missing required fields: ${missing.join(\", \")}. Please set these fields before calling build.`);\n\t\t}\n\n\t\treturn Object.freeze(this._actual as T);\n\t}\n\n\t/**\n\t * Builds and deeply freezes the final object with both compile-time and runtime validation.\n\t * The returned object is recursively frozen - all nested objects and arrays are also frozen.\n\t *\n\t * - Compile-time: TypeScript enforces all required properties are set\n\t * - Runtime: Validates all fields in the requiredTemplate and recursively applies Object.freeze()\n\t *\n\t * @returns The deeply frozen object of type DeepReadonly<T>\n\t * @throws {Error} If any required field is missing at runtime\n\t */\n\tbuildDeepFrozen(this: this & CeriosBrand<T>): DeepReadonly<T> {\n\t\tconst missing = this.validateRequiredFields();\n\n\t\tif (missing.length > 0) {\n\t\t\tthrow new Error(`Missing required fields: ${missing.join(\", \")}. Please set these fields before calling build.`);\n\t\t}\n\n\t\treturn deepFreeze(this._actual as T) as DeepReadonly<T>;\n\t}\n\n\t/**\n\t * Builds and seals the final object with both compile-time and runtime validation.\n\t * The returned object is shallowly sealed - properties cannot be added or removed,\n\t * but existing properties can still be modified. Nested objects remain unsealed.\n\t *\n\t * - Compile-time: TypeScript enforces all required properties are set\n\t * - Runtime: Validates all fields in the requiredTemplate and applies Object.seal()\n\t *\n\t * @returns The sealed object of type T\n\t * @throws {Error} If any required field is missing at runtime\n\t */\n\tbuildSealed(this: this & CeriosBrand<T>): T {\n\t\tconst missing = this.validateRequiredFields();\n\n\t\tif (missing.length > 0) {\n\t\t\tthrow new Error(`Missing required fields: ${missing.join(\", \")}. Please set these fields before calling build.`);\n\t\t}\n\n\t\treturn Object.seal(this._actual as T);\n\t}\n\n\t/**\n\t * Builds and deeply seals the final object with both compile-time and runtime validation.\n\t * The returned object is recursively sealed - properties cannot be added or removed at any level,\n\t * but existing properties can still be modified.\n\t *\n\t * - Compile-time: TypeScript enforces all required properties are set\n\t * - Runtime: Validates all fields in the requiredTemplate and recursively applies Object.seal()\n\t *\n\t * @returns The deeply sealed object of type T\n\t * @throws {Error} If any required field is missing at runtime\n\t */\n\tbuildDeepSealed(this: this & CeriosBrand<T>): T {\n\t\tconst missing = this.validateRequiredFields();\n\n\t\tif (missing.length > 0) {\n\t\t\tthrow new Error(`Missing required fields: ${missing.join(\", \")}. Please set these fields before calling build.`);\n\t\t}\n\n\t\treturn deepSeal(this._actual as T);\n\t}\n}\n"],"mappings":"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","names":[]} | ||
| {"version":3,"sources":["../src/index.ts","../src/cerios-builder.ts","../src/cerios-class-builder.ts"],"sourcesContent":["export { CeriosBrand, CeriosBuilder, RequiredFieldsTemplate } from \"./cerios-builder.js\";\nexport { CeriosClassBuilder, ClassConstructor } from \"./cerios-class-builder.js\";\nexport { BuilderType, DeepReadonly } from \"./types.js\";\n","import { DeepReadonly } from \"./types\";\n\n/**\n * Unique symbol used internally to brand types and track which properties have been set in the builder's type.\n *\n * @internal\n */\ndeclare const __brand: unique symbol;\n\n/**\n * Type utility for branding builder types with information about which properties have been set.\n * This is used to enforce compile-time safety for required fields in the builder pattern.\n *\n * @template T - The type representing the set of properties that have been set\n * @internal\n */\nexport type CeriosBrand<T> = { [__brand]: T };\n\n/**\n * Helper type to represent a path through an object structure\n * Handles optional properties by unwrapping them with NonNullable\n */\ntype PathImpl<T, K extends keyof T = keyof T> = K extends string | number\n\t? NonNullable<T[K]> extends Record<string, any>\n\t\t? NonNullable<T[K]> extends Array<any>\n\t\t\t? K\n\t\t\t: K | `${K}.${PathImpl<NonNullable<T[K]>> & string}`\n\t\t: K\n\t: never;\n\nexport type Path<T> = PathImpl<T>;\n\n/**\n * Helper type to get the value at a specific path, handling optional properties\n */\ntype PathValue<T, P> = P extends keyof T\n\t? T[P]\n\t: P extends `${infer K}.${infer Rest}`\n\t\t? K extends keyof T\n\t\t\t? PathValue<NonNullable<T[K]>, Rest>\n\t\t\t: never\n\t\t: never;\n\n/**\n * Type-safe template for defining required fields using an array of paths.\n * Simply list the paths that are required.\n */\nexport type RequiredFieldsTemplate<T> = ReadonlyArray<Path<T>>;\n\n/**\n * Cache the root key extraction to avoid repeated computation\n * Handles optional properties by ensuring the key is valid for T\n * @internal\n */\ntype RootKey<P extends string, T = any> = P extends `${infer K}.${string}`\n\t? K extends keyof T\n\t\t? K\n\t\t: never\n\t: P extends keyof T\n\t\t? P\n\t\t: never;\n\n/**\n * Recursively freezes an object and all its nested properties.\n * @param obj - The object to freeze\n * @returns The frozen object\n * @internal\n */\nfunction deepFreeze<T>(obj: T): T {\n\t// Retrieve the property names defined on obj\n\tObject.getOwnPropertyNames(obj).forEach(prop => {\n\t\tconst value = (obj as any)[prop];\n\n\t\t// Freeze properties before freezing self\n\t\tif (value !== null && (typeof value === \"object\" || typeof value === \"function\")) {\n\t\t\tdeepFreeze(value);\n\t\t}\n\t});\n\n\treturn Object.freeze(obj);\n}\n\n/**\n * Recursively seals an object and all its nested properties.\n * @param obj - The object to seal\n * @returns The sealed object\n * @internal\n */\nfunction deepSeal<T>(obj: T): T {\n\t// Retrieve the property names defined on obj\n\tObject.getOwnPropertyNames(obj).forEach(prop => {\n\t\tconst value = (obj as any)[prop];\n\n\t\t// Seal properties before sealing self\n\t\tif (value !== null && (typeof value === \"object\" || typeof value === \"function\")) {\n\t\t\tdeepSeal(value);\n\t\t}\n\t});\n\n\treturn Object.seal(obj);\n}\n\n/**\n * Abstract base class for creating type-safe builders with automatic property setters and compile-time validation of required fields.\n *\n * This class is intended to be extended by concrete builder implementations for your own types.\n * It provides utility methods for setting properties and building the final object, ensuring that all required fields are set at compile time.\n *\n * Example usage:\n * ```typescript\n * interface MyType { foo: string; bar: number[]; }\n * class MyTypeBuilder extends CeriosBuilder<MyType> {\n * static requiredTemplate: RequiredFieldsTemplate<MyType> = ['foo'];\n * setFoo(value: string) { return this.setProperty('foo', value); }\n * addBar(value: number) { return this.addToArrayProperty('bar', value); }\n * }\n * // Usage:\n * const obj = new MyTypeBuilder({})\n * .setFoo('hello')\n * .addBar(42)\n * .buildSafe(); // Validates that 'foo' is set\n * ```\n *\n * @template T - The complete type being built\n */\nexport abstract class CeriosBuilder<T extends object> {\n\t/**\n\t * Template defining which fields are required for this builder.\n\t * Subclasses should override this to specify their required fields as an array of paths.\n\t * The template is type-safe - only valid paths from type T can be used.\n\t */\n\tstatic requiredTemplate?: ReadonlyArray<string>;\n\n\t/**\n\t * Instance-level required fields that can be populated dynamically.\n\t * This allows adding required fields at runtime via the setRequiredFields method.\n\t * @private\n\t */\n\tprivate _requiredFields: Set<string> = new Set();\n\n\t/**\n\t * Sets the required fields for this builder instance.\n\t * This allows you to dynamically define which fields are required.\n\t *\n\t * @param fields - Array of dot-notation paths to required fields\n\t * @returns The builder instance for chaining\n\t *\n\t * @example\n\t * ```typescript\n\t * const builder = new MyBuilder({})\n\t * .setRequiredFields(['path.to.field1', 'path.to.field2'])\n\t * .setField1('value1')\n\t * .setField2('value2')\n\t * .buildSafe();\n\t * ```\n\t */\n\tsetRequiredFields(fields: ReadonlyArray<Path<T>>): this {\n\t\tthis._requiredFields = new Set([...fields] as string[]);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Gets the combined required fields from both the static template and instance-level fields.\n\t * @private\n\t */\n\tprivate getRequiredTemplate(): ReadonlyArray<string> {\n\t\tconst ctor = this.constructor as typeof CeriosBuilder;\n\t\tconst staticFields = ctor.requiredTemplate || [];\n\t\tconst instanceFields = Array.from(this._requiredFields);\n\n\t\t// Combine and deduplicate\n\t\treturn [...new Set([...staticFields, ...instanceFields])];\n\t}\n\n\t/**\n\t * Validates that all fields in the required template have been set.\n\t * @private\n\t */\n\tprivate validateRequiredFields(): string[] {\n\t\tconst requiredPaths = this.getRequiredTemplate();\n\t\tconst missing: string[] = [];\n\n\t\tfor (const path of requiredPaths) {\n\t\t\tconst keys = path.split(\".\");\n\t\t\tlet current: any = this._actual;\n\n\t\t\tfor (let i = 0; i < keys.length; i++) {\n\t\t\t\tconst key = keys[i];\n\t\t\t\tif (current === null || current === undefined || !(key in current)) {\n\t\t\t\t\tmissing.push(path);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcurrent = current[key];\n\t\t\t}\n\n\t\t\t// Check if the final value is null or undefined\n\t\t\tif (current === null || current === undefined) {\n\t\t\t\tif (!missing.includes(path)) {\n\t\t\t\t\tmissing.push(path);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn missing;\n\t}\n\n\t/**\n\t * Creates a new builder instance. Intended to be called by subclasses.\n\t *\n\t * @param _actual - The current partial state of the object being built\n\t * @param _requiredFields - Optional array of required field paths to preserve across instances\n\t * @protected\n\t */\n\tprotected constructor(\n\t\tprotected readonly _actual: Partial<T>,\n\t\t_requiredFields?: RequiredFieldsTemplate<T>\n\t) {\n\t\tif (_requiredFields) {\n\t\t\tthis._requiredFields = new Set([..._requiredFields] as string[]);\n\t\t}\n\t}\n\n\t/**\n\t * Sets a property value and returns a new builder instance with updated type state.\n\t * This method is intended to be wrapped by concrete builder methods in subclasses.\n\t *\n\t * @template K - The property key being set\n\t * @param key - The property key to set\n\t * @param value - The value to assign to the property\n\t * @returns A new builder instance with the property set and type state updated\n\t * @protected\n\t */\n\tprotected setProperty<K extends keyof T>(key: K, value: T[K]): this & CeriosBrand<Pick<T, K>> {\n\t\tconst BuilderClass = this.constructor as new (data: any, requiredFields?: RequiredFieldsTemplate<T>) => any;\n\t\treturn new BuilderClass(\n\t\t\t{\n\t\t\t\t...this._actual,\n\t\t\t\t[key]: value,\n\t\t\t},\n\t\t\tArray.from(this._requiredFields) as unknown as RequiredFieldsTemplate<T>\n\t\t) as this & CeriosBrand<Pick<T, K>>;\n\t}\n\n\t/**\n\t * Sets multiple property values at once and returns a new builder instance with updated type state.\n\t * @param props - An object with one or more properties to set.\n\t * @returns A new builder instance with the properties set and type state updated.\n\t * @protected\n\t */\n\tprotected setProperties<K extends keyof T>(props: Pick<T, K>): this & CeriosBrand<Pick<T, K>> {\n\t\tconst BuilderClass = this.constructor as new (data: any, requiredFields?: RequiredFieldsTemplate<T>) => any;\n\t\treturn new BuilderClass(\n\t\t\t{\n\t\t\t\t...this._actual,\n\t\t\t\t...props,\n\t\t\t},\n\t\t\tArray.from(this._requiredFields) as unknown as RequiredFieldsTemplate<T>\n\t\t) as this & CeriosBrand<Pick<T, K>>;\n\t}\n\n\t/**\n\t * Sets a deeply nested property value and returns a new builder instance with updated type state.\n\t * This method uses dot notation to set nested properties in a type-safe way.\n\t *\n\t * @template P - The property path (e.g., \"parent.child.grandchild\")\n\t * @param path - The dot-notation path to the property\n\t * @param value - The value to assign to the nested property\n\t * @returns A new builder instance with the nested property set\n\t * @protected\n\t *\n\t * @example\n\t * ```typescript\n\t * builder.setNestedProperty('Order.Details.CustomerId', 'value')\n\t * ```\n\t */\n\tprotected setNestedProperty<P extends Path<T>>(\n\t\tpath: P,\n\t\tvalue: PathValue<T, P>\n\t): this & CeriosBrand<Pick<T, RootKey<P & string, T> extends never ? keyof T : RootKey<P & string, T>>> {\n\t\tconst BuilderClass = this.constructor as new (data: any, requiredFields?: RequiredFieldsTemplate<T>) => any;\n\t\tconst keys = (path as string).split(\".\");\n\t\tconst newActual = this.deepClone(this._actual);\n\n\t\tlet current: any = newActual;\n\t\tfor (let i = 0; i < keys.length - 1; i++) {\n\t\t\tconst key = keys[i];\n\t\t\tif (!(key in current) || typeof current[key] !== \"object\" || current[key] === null) {\n\t\t\t\tcurrent[key] = {};\n\t\t\t} else {\n\t\t\t\tcurrent[key] = this.deepClone(current[key]);\n\t\t\t}\n\t\t\tcurrent = current[key];\n\t\t}\n\n\t\tcurrent[keys[keys.length - 1]] = value;\n\n\t\treturn new BuilderClass(\n\t\t\tnewActual,\n\t\t\tArray.from(this._requiredFields) as unknown as RequiredFieldsTemplate<T>\n\t\t) as this & CeriosBrand<Pick<T, RootKey<P & string, T> extends never ? keyof T : RootKey<P & string, T>>>;\n\t}\n\n\t/**\n\t * Deep clone helper for nested objects\n\t * @private\n\t */\n\tprivate deepClone<V>(obj: V): V {\n\t\tif (obj === null || typeof obj !== \"object\") {\n\t\t\treturn obj;\n\t\t}\n\t\tif (Array.isArray(obj)) {\n\t\t\treturn obj.map(item => this.deepClone(item)) as any;\n\t\t}\n\t\tconst cloned: any = {};\n\t\tconst hasOwn = Object.prototype.hasOwnProperty;\n\t\tfor (const key in obj) {\n\t\t\tif (hasOwn.call(obj, key)) {\n\t\t\t\tcloned[key] = this.deepClone(obj[key]);\n\t\t\t}\n\t\t}\n\t\treturn cloned;\n\t}\n\n\t/**\n\t * Adds a value to an array property and returns a new builder instance with updated type state.\n\t * This method is intended to be wrapped by concrete builder methods in subclasses for array properties.\n\t *\n\t * @template K - The property key (must be an array property)\n\t * @template V - The type of the array element\n\t * @param key - The array property key to add to\n\t * @param value - The value to add to the array\n\t * @returns A new builder instance with the array property updated and type state updated\n\t * @protected\n\t */\n\tprotected addToArrayProperty<\n\t\tK extends { [P in keyof T]: NonNullable<T[P]> extends Array<any> ? P : never }[keyof T],\n\t\tV extends T[K] extends Array<infer U> ? U : T[K] extends Array<infer U> | undefined ? U : never,\n\t>(key: K, value: V): this & CeriosBrand<Pick<T, K>> {\n\t\tconst BuilderClass = this.constructor as new (data: any, requiredFields?: RequiredFieldsTemplate<T>) => any;\n\t\tconst currentArray = (this._actual[key] as Array<V> | undefined) ?? [];\n\t\treturn new BuilderClass(\n\t\t\t{\n\t\t\t\t...this._actual,\n\t\t\t\t[key]: [...currentArray, value],\n\t\t\t},\n\t\t\tArray.from(this._requiredFields) as unknown as RequiredFieldsTemplate<T>\n\t\t) as this & CeriosBrand<Pick<T, K>>;\n\t}\n\n\t/**\n\t * Builds the final object with both compile-time and runtime validation.\n\t * This is the recommended and safest way to build objects.\n\t *\n\t * - Compile-time: TypeScript enforces all required properties are set\n\t * - Runtime: Validates all fields in the requiredTemplate\n\t *\n\t * @returns The fully built object of type T\n\t * @throws {Error} If any required field is missing at runtime\n\t */\n\tbuild(this: this & CeriosBrand<T>): T {\n\t\tconst missing = this.validateRequiredFields();\n\n\t\tif (missing.length > 0) {\n\t\t\tthrow new Error(`Missing required fields: ${missing.join(\", \")}. Please set these fields before calling build.`);\n\t\t}\n\n\t\treturn this._actual as T;\n\t}\n\n\t/**\n\t * Builds the final object with only compile-time validation, skipping runtime checks.\n\t * Use this when you want TypeScript safety but need to skip runtime validation for performance.\n\t *\n\t * - Compile-time: TypeScript enforces all required properties are set\n\t * - Runtime: No validation\n\t *\n\t * @returns The fully built object of type T\n\t */\n\tbuildWithoutRuntimeValidation(this: this & CeriosBrand<T>): T {\n\t\treturn this._actual as T;\n\t}\n\n\t/**\n\t * Builds the final object with only runtime validation, skipping compile-time checks.\n\t * Use this when building from external data where compile-time checks aren't possible.\n\t *\n\t * - Compile-time: No TypeScript enforcement\n\t * - Runtime: Validates all fields in the requiredTemplate\n\t *\n\t * @returns The fully built object of type T\n\t * @throws {Error} If any required field is missing at runtime\n\t */\n\tbuildWithoutCompileTimeValidation(): T {\n\t\tconst missing = this.validateRequiredFields();\n\n\t\tif (missing.length > 0) {\n\t\t\tthrow new Error(`Missing required fields: ${missing.join(\", \")}. Please set these fields before calling build.`);\n\t\t}\n\n\t\treturn this._actual as T;\n\t}\n\n\t/**\n\t * Builds the final object without any validation (neither compile-time nor runtime).\n\t * Use this only when you're certain the object is valid and need maximum performance.\n\t *\n\t * - Compile-time: No TypeScript enforcement\n\t * - Runtime: No validation\n\t *\n\t * @returns The object of type T (may be incomplete)\n\t */\n\tbuildUnsafe(): T {\n\t\treturn this._actual as T;\n\t}\n\n\t/**\n\t * Builds a partial object, which may not have all required fields set.\n\t * This is useful for scenarios where you want to inspect or validate the current state before finalizing.\n\t *\n\t * @returns The partially built object\n\t */\n\tbuildPartial(): Partial<T> {\n\t\treturn this._actual as Partial<T>;\n\t}\n\n\t/**\n\t * @deprecated Use build() instead. buildSafe() is now an alias for build().\n\t * Builds the final object with runtime validation using the requiredTemplate.\n\t * This method validates that all fields in the requiredTemplate array are present.\n\t *\n\t * @returns The fully built object of type T\n\t * @throws {Error} If any required field is missing at runtime\n\t */\n\tbuildSafe(): T {\n\t\treturn this.buildWithoutCompileTimeValidation();\n\t}\n\n\t/**\n\t * Builds and freezes the final object with both compile-time and runtime validation.\n\t * The returned object is shallowly frozen - top-level properties cannot be modified,\n\t * but nested objects remain mutable.\n\t *\n\t * - Compile-time: TypeScript enforces all required properties are set\n\t * - Runtime: Validates all fields in the requiredTemplate and applies Object.freeze()\n\t *\n\t * @returns The frozen object of type Readonly<T>\n\t * @throws {Error} If any required field is missing at runtime\n\t */\n\tbuildFrozen(this: this & CeriosBrand<T>): Readonly<T> {\n\t\tconst missing = this.validateRequiredFields();\n\n\t\tif (missing.length > 0) {\n\t\t\tthrow new Error(`Missing required fields: ${missing.join(\", \")}. Please set these fields before calling build.`);\n\t\t}\n\n\t\treturn Object.freeze(this._actual as T);\n\t}\n\n\t/**\n\t * Builds and deeply freezes the final object with both compile-time and runtime validation.\n\t * The returned object is recursively frozen - all nested objects and arrays are also frozen.\n\t *\n\t * - Compile-time: TypeScript enforces all required properties are set\n\t * - Runtime: Validates all fields in the requiredTemplate and recursively applies Object.freeze()\n\t *\n\t * @returns The deeply frozen object of type DeepReadonly<T>\n\t * @throws {Error} If any required field is missing at runtime\n\t */\n\tbuildDeepFrozen(this: this & CeriosBrand<T>): DeepReadonly<T> {\n\t\tconst missing = this.validateRequiredFields();\n\n\t\tif (missing.length > 0) {\n\t\t\tthrow new Error(`Missing required fields: ${missing.join(\", \")}. Please set these fields before calling build.`);\n\t\t}\n\n\t\treturn deepFreeze(this._actual as T) as DeepReadonly<T>;\n\t}\n\n\t/**\n\t * Builds and seals the final object with both compile-time and runtime validation.\n\t * The returned object is shallowly sealed - properties cannot be added or removed,\n\t * but existing properties can still be modified. Nested objects remain unsealed.\n\t *\n\t * - Compile-time: TypeScript enforces all required properties are set\n\t * - Runtime: Validates all fields in the requiredTemplate and applies Object.seal()\n\t *\n\t * @returns The sealed object of type T\n\t * @throws {Error} If any required field is missing at runtime\n\t */\n\tbuildSealed(this: this & CeriosBrand<T>): T {\n\t\tconst missing = this.validateRequiredFields();\n\n\t\tif (missing.length > 0) {\n\t\t\tthrow new Error(`Missing required fields: ${missing.join(\", \")}. Please set these fields before calling build.`);\n\t\t}\n\n\t\treturn Object.seal(this._actual as T);\n\t}\n\n\t/**\n\t * Builds and deeply seals the final object with both compile-time and runtime validation.\n\t * The returned object is recursively sealed - properties cannot be added or removed at any level,\n\t * but existing properties can still be modified.\n\t *\n\t * - Compile-time: TypeScript enforces all required properties are set\n\t * - Runtime: Validates all fields in the requiredTemplate and recursively applies Object.seal()\n\t *\n\t * @returns The deeply sealed object of type T\n\t * @throws {Error} If any required field is missing at runtime\n\t */\n\tbuildDeepSealed(this: this & CeriosBrand<T>): T {\n\t\tconst missing = this.validateRequiredFields();\n\n\t\tif (missing.length > 0) {\n\t\t\tthrow new Error(`Missing required fields: ${missing.join(\", \")}. Please set these fields before calling build.`);\n\t\t}\n\n\t\treturn deepSeal(this._actual as T);\n\t}\n}\n","import type { DeepReadonly } from \"./types\";\n\n/**\n * Type representing a class constructor that can be instantiated with optional partial data.\n * @template T - The type that the constructor creates\n */\nexport type ClassConstructor<T> = new (data?: Partial<T>) => T;\n\n/**\n * Helper to extract only data properties (exclude methods) from a class type.\n * This allows compile-time validation to work properly with classes.\n */\ntype DataPropertiesOnly<T> = {\n\t[K in keyof T as T[K] extends (...args: any[]) => any ? never : K]: T[K];\n};\n\n/**\n * Brand type specifically for class builders that only tracks data properties.\n */\nexport type CeriosClassBrand<T> = {\n\treadonly __classBuilderBrand: T;\n};\n\n/**\n * Type-safe builder specifically designed for classes.\n * This builder automatically instantiates the target class and provides:\n * - Compile-time validation that works with class methods\n * - Automatic runtime validation of nested class instances\n * - Preservation of decorators and class methods\n *\n * Example usage:\n * ```typescript\n * class Person {\n * name!: string;\n * age!: number;\n * email?: string;\n *\n * constructor(data?: Partial<Person>) {\n * if (data) Object.assign(this, data);\n * }\n *\n * greet() { return `Hello, I'm ${this.name}`; }\n * }\n *\n * const builder = new CeriosClassBuilder(Person);\n * const person = builder\n * .setProperty('name', 'John')\n * .setProperty('age', 30)\n * .build();\n * ```\n *\n * @template T - The class type being built\n */\nexport class CeriosClassBuilder<T extends object> {\n\t/**\n\t * The class constructor to instantiate when building.\n\t * @private\n\t */\n\tprivate readonly _classConstructor: ClassConstructor<T>;\n\n\t/**\n\t * The current partial state of the object being built.\n\t * @private\n\t */\n\tprotected readonly _actual: Partial<T>;\n\n\t/**\n\t * Creates a new class builder instance.\n\t * @param classConstructor - The class constructor to use for building\n\t * @param data - Optional initial data\n\t */\n\tprotected constructor(classConstructor: ClassConstructor<T>, data: Partial<T> = {}) {\n\t\tthis._classConstructor = classConstructor;\n\t\tthis._actual = data;\n\t}\n\n\t/**\n\t * Gets the class constructor for this builder.\n\t * @private\n\t */\n\tprivate getClassConstructor(): ClassConstructor<T> {\n\t\treturn this._classConstructor;\n\t}\n\n\t/**\n\t * Creates a new instance of the builder with updated data.\n\t * Subclasses can override this to return the correct subclass type.\n\t * @private\n\t */\n\tprivate createBuilder(data: Partial<T>): this {\n\t\tconst BuilderClass = this.constructor as new (classConstructor: ClassConstructor<T>, data: Partial<T>) => this;\n\t\treturn new BuilderClass(this._classConstructor, data);\n\t}\n\n\t/**\n\t * Sets a property value and returns a new builder instance with updated type state.\n\t * @template K - The property key being set\n\t * @param key - The property key to set\n\t * @param value - The value to assign to the property\n\t * @returns A new builder instance with the property set\n\t */\n\tsetProperty<K extends keyof DataPropertiesOnly<T>>(\n\t\tkey: K,\n\t\tvalue: DataPropertiesOnly<T>[K]\n\t): this & CeriosClassBrand<Pick<DataPropertiesOnly<T>, K>> {\n\t\tconst newBuilder = this.createBuilder({\n\t\t\t...this._actual,\n\t\t\t[key]: value,\n\t\t} as Partial<T>);\n\t\treturn newBuilder as this & CeriosClassBrand<Pick<DataPropertiesOnly<T>, K>>;\n\t}\n\n\t/**\n\t * Sets multiple properties at once.\n\t * @template K - The property keys being set\n\t * @param props - Object with properties to set\n\t * @returns A new builder instance with the properties set\n\t */\n\tsetProperties<K extends keyof DataPropertiesOnly<T>>(\n\t\tprops: Pick<DataPropertiesOnly<T>, K>\n\t): this & CeriosClassBrand<Pick<DataPropertiesOnly<T>, K>> {\n\t\tconst newBuilder = this.createBuilder({\n\t\t\t...this._actual,\n\t\t\t...props,\n\t\t} as Partial<T>);\n\t\treturn newBuilder as this & CeriosClassBrand<Pick<DataPropertiesOnly<T>, K>>;\n\t}\n\n\t/**\n\t * Adds a value to an array property.\n\t * @template K - The array property key\n\t * @template V - The array element type\n\t * @param key - The array property key\n\t * @param value - The value to add to the array\n\t * @returns A new builder instance with the value added\n\t */\n\taddToArrayProperty<\n\t\tK extends {\n\t\t\t[P in keyof DataPropertiesOnly<T>]: NonNullable<DataPropertiesOnly<T>[P]> extends Array<any> ? P : never;\n\t\t}[keyof DataPropertiesOnly<T>],\n\t\tV extends DataPropertiesOnly<T>[K] extends Array<infer U>\n\t\t\t? U\n\t\t\t: DataPropertiesOnly<T>[K] extends Array<infer U> | undefined\n\t\t\t\t? U\n\t\t\t\t: never,\n\t>(key: K, value: V): this & CeriosClassBrand<Pick<DataPropertiesOnly<T>, K>> {\n\t\tconst currentArray = (this._actual[key as keyof T] as Array<V> | undefined) ?? [];\n\t\tconst newBuilder = this.createBuilder({\n\t\t\t...this._actual,\n\t\t\t[key]: [...currentArray, value],\n\t\t} as Partial<T>);\n\t\treturn newBuilder as this & CeriosClassBrand<Pick<DataPropertiesOnly<T>, K>>;\n\t}\n\n\t/**\n\t * Builds the final class instance with compile-time and runtime validation.\n\t * - Compile-time: TypeScript enforces all data properties are set\n\t * - Runtime: Validates nested class instances are properly instantiated\n\t *\n\t * @returns The fully built and validated class instance\n\t * @throws {Error} If nested validation fails\n\t */\n\tbuild(this: this & CeriosClassBrand<DataPropertiesOnly<T>>): T {\n\t\tconst ctor = this.getClassConstructor();\n\t\tconst instance: T = new ctor(this._actual as T);\n\t\t// If the class does not assign properties in the constructor, assign them manually\n\t\tconst dataKeys = Object.keys(this._actual) as (keyof T)[];\n\t\tconst needsAssign = dataKeys.some(key => instance[key] === undefined && this._actual[key] !== undefined);\n\t\tif (needsAssign) {\n\t\t\tObject.assign(instance, this._actual);\n\t\t}\n\t\treturn instance;\n\t}\n\n\t/**\n\t * Builds the class instance with runtime validation only (no compile-time).\n\t * Use this when building from external/dynamic data.\n\t *\n\t * @returns The built class instance\n\t * @throws {Error} If validation fails\n\t */\n\tbuildWithoutCompileTimeValidation(): T {\n\t\tconst ctor = this.getClassConstructor();\n\t\tconst instance: T = new ctor(this._actual as T);\n\t\tconst dataKeys = Object.keys(this._actual) as (keyof T)[];\n\t\tconst needsAssign = dataKeys.some(key => instance[key] === undefined && this._actual[key] !== undefined);\n\t\tif (needsAssign) {\n\t\t\tObject.assign(instance, this._actual);\n\t\t}\n\t\treturn instance;\n\t}\n\n\t/**\n\t * Builds the class instance without any validation.\n\t * Use only when you're certain the object is valid.\n\t *\n\t * @returns The built class instance (may be incomplete)\n\t */\n\tbuildUnsafe(): T {\n\t\tconst ctor = this.getClassConstructor();\n\t\tconst instance: T = new ctor(this._actual as T);\n\t\tconst dataKeys = Object.keys(this._actual) as (keyof T)[];\n\t\tconst needsAssign = dataKeys.some(key => instance[key] === undefined && this._actual[key] !== undefined);\n\t\tif (needsAssign) {\n\t\t\tObject.assign(instance, this._actual);\n\t\t}\n\t\treturn instance;\n\t}\n\n\t/**\n\t * Builds a partial object (may not have all required fields).\n\t *\n\t * @returns The partially built object\n\t */\n\tbuildPartial(): Partial<T> {\n\t\treturn { ...this._actual };\n\t}\n\n\t/**\n\t * Builds and freezes the class instance (shallow freeze).\n\t *\n\t * @returns The frozen class instance\n\t * @throws {Error} If validation fails\n\t */\n\tbuildFrozen(this: this & CeriosClassBrand<DataPropertiesOnly<T>>): Readonly<T> {\n\t\tconst ctor = this.getClassConstructor();\n\t\tconst instance: T = new ctor(this._actual as T);\n\t\t// If the class does not assign properties in the constructor, assign them manually\n\t\tconst dataKeys = Object.keys(this._actual) as (keyof T)[];\n\t\tconst needsAssign = dataKeys.some(key => instance[key] === undefined && this._actual[key] !== undefined);\n\t\tif (needsAssign) {\n\t\t\tObject.assign(instance, this._actual);\n\t\t}\n\t\treturn Object.freeze(instance);\n\t}\n\n\t/**\n\t * Builds and deeply freezes the class instance.\n\t *\n\t * @returns The deeply frozen class instance\n\t * @throws {Error} If validation fails\n\t */\n\tbuildDeepFrozen(this: this & CeriosClassBrand<DataPropertiesOnly<T>>): DeepReadonly<T> {\n\t\tconst ctor = this.getClassConstructor();\n\t\tconst instance: T = new ctor(this._actual as T);\n\t\t// If the class does not assign properties in the constructor, assign them manually\n\t\tconst dataKeys = Object.keys(this._actual) as (keyof T)[];\n\t\tconst needsAssign = dataKeys.some(key => instance[key] === undefined && this._actual[key] !== undefined);\n\t\tif (needsAssign) {\n\t\t\tObject.assign(instance, this._actual);\n\t\t}\n\t\treturn this.deepFreeze(instance) as DeepReadonly<T>;\n\t}\n\n\t/**\n\t * Builds and seals the class instance (shallow freeze).\n\t *\n\t * @returns The sealed class instance\n\t * @throws {Error} If validation fails\n\t */\n\tbuildSealed(this: this & CeriosClassBrand<DataPropertiesOnly<T>>): T {\n\t\tconst ctor = this.getClassConstructor();\n\t\tconst instance: T = new ctor(this._actual as T);\n\t\t// If the class does not assign properties in the constructor, assign them manually\n\t\tconst dataKeys = Object.keys(this._actual) as (keyof T)[];\n\t\tconst needsAssign = dataKeys.some(key => instance[key] === undefined && this._actual[key] !== undefined);\n\t\tif (needsAssign) {\n\t\t\tObject.assign(instance, this._actual);\n\t\t}\n\t\treturn Object.seal(instance);\n\t}\n\n\t/**\n\t * Builds and deeply seals the class instance.\n\t *\n\t * @returns The deeply sealed class instance\n\t * @throws {Error} If validation fails\n\t */\n\tbuildDeepSealed(this: this & CeriosClassBrand<DataPropertiesOnly<T>>): T {\n\t\tconst ctor = this.getClassConstructor();\n\t\tconst instance: T = new ctor(this._actual as T);\n\t\t// If the class does not assign properties in the constructor, assign them manually\n\t\tconst dataKeys = Object.keys(this._actual) as (keyof T)[];\n\t\tconst needsAssign = dataKeys.some(key => instance[key] === undefined && this._actual[key] !== undefined);\n\t\tif (needsAssign) {\n\t\t\tObject.assign(instance, this._actual);\n\t\t}\n\t\treturn this.deepSeal(instance);\n\t}\n\n\t/**\n\t * Deep freeze helper.\n\t * @private\n\t */\n\tprivate deepFreeze<V>(obj: V): V {\n\t\tif (obj === null || typeof obj !== \"object\") {\n\t\t\treturn obj;\n\t\t}\n\t\tObject.getOwnPropertyNames(obj).forEach(prop => {\n\t\t\tconst value = (obj as any)[prop];\n\t\t\tif (value !== null && (typeof value === \"object\" || typeof value === \"function\")) {\n\t\t\t\tthis.deepFreeze(value);\n\t\t\t}\n\t\t});\n\t\treturn Object.freeze(obj);\n\t}\n\n\t/**\n\t * Deep seal helper.\n\t * @private\n\t */\n\tprivate deepSeal<V>(obj: V): V {\n\t\tif (obj === null || typeof obj !== \"object\") {\n\t\t\treturn obj;\n\t\t}\n\t\tObject.getOwnPropertyNames(obj).forEach(prop => {\n\t\t\tconst value = (obj as any)[prop];\n\t\t\tif (value !== null && (typeof value === \"object\" || typeof value === \"function\")) {\n\t\t\t\tthis.deepSeal(value);\n\t\t\t}\n\t\t});\n\t\treturn Object.seal(obj);\n\t}\n\n\t/**\n\t * Deep clone helper for nested objects.\n\t * @private\n\t */\n\tprivate deepClone<V>(obj: V): V {\n\t\tif (obj === null || typeof obj !== \"object\") {\n\t\t\treturn obj;\n\t\t}\n\t\tif (Array.isArray(obj)) {\n\t\t\treturn obj.map(item => this.deepClone(item)) as any;\n\t\t}\n\t\tconst cloned: any = {};\n\t\tfor (const key of Object.keys(obj)) {\n\t\t\tcloned[key] = this.deepClone((obj as any)[key]);\n\t\t}\n\t\treturn cloned;\n\t}\n}\n"],"mappings":"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","names":[]} |
+247
-1
@@ -360,5 +360,251 @@ // src/cerios-builder.ts | ||
| }; | ||
| // src/cerios-class-builder.ts | ||
| var CeriosClassBuilder = class { | ||
| /** | ||
| * Creates a new class builder instance. | ||
| * @param classConstructor - The class constructor to use for building | ||
| * @param data - Optional initial data | ||
| */ | ||
| constructor(classConstructor, data = {}) { | ||
| this._classConstructor = classConstructor; | ||
| this._actual = data; | ||
| } | ||
| /** | ||
| * Gets the class constructor for this builder. | ||
| * @private | ||
| */ | ||
| getClassConstructor() { | ||
| return this._classConstructor; | ||
| } | ||
| /** | ||
| * Creates a new instance of the builder with updated data. | ||
| * Subclasses can override this to return the correct subclass type. | ||
| * @private | ||
| */ | ||
| createBuilder(data) { | ||
| const BuilderClass = this.constructor; | ||
| return new BuilderClass(this._classConstructor, data); | ||
| } | ||
| /** | ||
| * Sets a property value and returns a new builder instance with updated type state. | ||
| * @template K - The property key being set | ||
| * @param key - The property key to set | ||
| * @param value - The value to assign to the property | ||
| * @returns A new builder instance with the property set | ||
| */ | ||
| setProperty(key, value) { | ||
| const newBuilder = this.createBuilder({ | ||
| ...this._actual, | ||
| [key]: value | ||
| }); | ||
| return newBuilder; | ||
| } | ||
| /** | ||
| * Sets multiple properties at once. | ||
| * @template K - The property keys being set | ||
| * @param props - Object with properties to set | ||
| * @returns A new builder instance with the properties set | ||
| */ | ||
| setProperties(props) { | ||
| const newBuilder = this.createBuilder({ | ||
| ...this._actual, | ||
| ...props | ||
| }); | ||
| return newBuilder; | ||
| } | ||
| /** | ||
| * Adds a value to an array property. | ||
| * @template K - The array property key | ||
| * @template V - The array element type | ||
| * @param key - The array property key | ||
| * @param value - The value to add to the array | ||
| * @returns A new builder instance with the value added | ||
| */ | ||
| addToArrayProperty(key, value) { | ||
| var _a; | ||
| const currentArray = (_a = this._actual[key]) != null ? _a : []; | ||
| const newBuilder = this.createBuilder({ | ||
| ...this._actual, | ||
| [key]: [...currentArray, value] | ||
| }); | ||
| return newBuilder; | ||
| } | ||
| /** | ||
| * Builds the final class instance with compile-time and runtime validation. | ||
| * - Compile-time: TypeScript enforces all data properties are set | ||
| * - Runtime: Validates nested class instances are properly instantiated | ||
| * | ||
| * @returns The fully built and validated class instance | ||
| * @throws {Error} If nested validation fails | ||
| */ | ||
| build() { | ||
| const ctor = this.getClassConstructor(); | ||
| const instance = new ctor(this._actual); | ||
| const dataKeys = Object.keys(this._actual); | ||
| const needsAssign = dataKeys.some((key) => instance[key] === void 0 && this._actual[key] !== void 0); | ||
| if (needsAssign) { | ||
| Object.assign(instance, this._actual); | ||
| } | ||
| return instance; | ||
| } | ||
| /** | ||
| * Builds the class instance with runtime validation only (no compile-time). | ||
| * Use this when building from external/dynamic data. | ||
| * | ||
| * @returns The built class instance | ||
| * @throws {Error} If validation fails | ||
| */ | ||
| buildWithoutCompileTimeValidation() { | ||
| const ctor = this.getClassConstructor(); | ||
| const instance = new ctor(this._actual); | ||
| const dataKeys = Object.keys(this._actual); | ||
| const needsAssign = dataKeys.some((key) => instance[key] === void 0 && this._actual[key] !== void 0); | ||
| if (needsAssign) { | ||
| Object.assign(instance, this._actual); | ||
| } | ||
| return instance; | ||
| } | ||
| /** | ||
| * Builds the class instance without any validation. | ||
| * Use only when you're certain the object is valid. | ||
| * | ||
| * @returns The built class instance (may be incomplete) | ||
| */ | ||
| buildUnsafe() { | ||
| const ctor = this.getClassConstructor(); | ||
| const instance = new ctor(this._actual); | ||
| const dataKeys = Object.keys(this._actual); | ||
| const needsAssign = dataKeys.some((key) => instance[key] === void 0 && this._actual[key] !== void 0); | ||
| if (needsAssign) { | ||
| Object.assign(instance, this._actual); | ||
| } | ||
| return instance; | ||
| } | ||
| /** | ||
| * Builds a partial object (may not have all required fields). | ||
| * | ||
| * @returns The partially built object | ||
| */ | ||
| buildPartial() { | ||
| return { ...this._actual }; | ||
| } | ||
| /** | ||
| * Builds and freezes the class instance (shallow freeze). | ||
| * | ||
| * @returns The frozen class instance | ||
| * @throws {Error} If validation fails | ||
| */ | ||
| buildFrozen() { | ||
| const ctor = this.getClassConstructor(); | ||
| const instance = new ctor(this._actual); | ||
| const dataKeys = Object.keys(this._actual); | ||
| const needsAssign = dataKeys.some((key) => instance[key] === void 0 && this._actual[key] !== void 0); | ||
| if (needsAssign) { | ||
| Object.assign(instance, this._actual); | ||
| } | ||
| return Object.freeze(instance); | ||
| } | ||
| /** | ||
| * Builds and deeply freezes the class instance. | ||
| * | ||
| * @returns The deeply frozen class instance | ||
| * @throws {Error} If validation fails | ||
| */ | ||
| buildDeepFrozen() { | ||
| const ctor = this.getClassConstructor(); | ||
| const instance = new ctor(this._actual); | ||
| const dataKeys = Object.keys(this._actual); | ||
| const needsAssign = dataKeys.some((key) => instance[key] === void 0 && this._actual[key] !== void 0); | ||
| if (needsAssign) { | ||
| Object.assign(instance, this._actual); | ||
| } | ||
| return this.deepFreeze(instance); | ||
| } | ||
| /** | ||
| * Builds and seals the class instance (shallow freeze). | ||
| * | ||
| * @returns The sealed class instance | ||
| * @throws {Error} If validation fails | ||
| */ | ||
| buildSealed() { | ||
| const ctor = this.getClassConstructor(); | ||
| const instance = new ctor(this._actual); | ||
| const dataKeys = Object.keys(this._actual); | ||
| const needsAssign = dataKeys.some((key) => instance[key] === void 0 && this._actual[key] !== void 0); | ||
| if (needsAssign) { | ||
| Object.assign(instance, this._actual); | ||
| } | ||
| return Object.seal(instance); | ||
| } | ||
| /** | ||
| * Builds and deeply seals the class instance. | ||
| * | ||
| * @returns The deeply sealed class instance | ||
| * @throws {Error} If validation fails | ||
| */ | ||
| buildDeepSealed() { | ||
| const ctor = this.getClassConstructor(); | ||
| const instance = new ctor(this._actual); | ||
| const dataKeys = Object.keys(this._actual); | ||
| const needsAssign = dataKeys.some((key) => instance[key] === void 0 && this._actual[key] !== void 0); | ||
| if (needsAssign) { | ||
| Object.assign(instance, this._actual); | ||
| } | ||
| return this.deepSeal(instance); | ||
| } | ||
| /** | ||
| * Deep freeze helper. | ||
| * @private | ||
| */ | ||
| deepFreeze(obj) { | ||
| if (obj === null || typeof obj !== "object") { | ||
| return obj; | ||
| } | ||
| Object.getOwnPropertyNames(obj).forEach((prop) => { | ||
| const value = obj[prop]; | ||
| if (value !== null && (typeof value === "object" || typeof value === "function")) { | ||
| this.deepFreeze(value); | ||
| } | ||
| }); | ||
| return Object.freeze(obj); | ||
| } | ||
| /** | ||
| * Deep seal helper. | ||
| * @private | ||
| */ | ||
| deepSeal(obj) { | ||
| if (obj === null || typeof obj !== "object") { | ||
| return obj; | ||
| } | ||
| Object.getOwnPropertyNames(obj).forEach((prop) => { | ||
| const value = obj[prop]; | ||
| if (value !== null && (typeof value === "object" || typeof value === "function")) { | ||
| this.deepSeal(value); | ||
| } | ||
| }); | ||
| return Object.seal(obj); | ||
| } | ||
| /** | ||
| * Deep clone helper for nested objects. | ||
| * @private | ||
| */ | ||
| deepClone(obj) { | ||
| if (obj === null || typeof obj !== "object") { | ||
| return obj; | ||
| } | ||
| if (Array.isArray(obj)) { | ||
| return obj.map((item) => this.deepClone(item)); | ||
| } | ||
| const cloned = {}; | ||
| for (const key of Object.keys(obj)) { | ||
| cloned[key] = this.deepClone(obj[key]); | ||
| } | ||
| return cloned; | ||
| } | ||
| }; | ||
| export { | ||
| CeriosBuilder | ||
| CeriosBuilder, | ||
| CeriosClassBuilder | ||
| }; | ||
| //# sourceMappingURL=index.mjs.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/cerios-builder.ts"],"sourcesContent":["/**\n * Unique symbol used internally to brand types and track which properties have been set in the builder's type.\n *\n * @internal\n */\ndeclare const __brand: unique symbol;\n\n/**\n * Type utility for branding builder types with information about which properties have been set.\n * This is used to enforce compile-time safety for required fields in the builder pattern.\n *\n * @template T - The type representing the set of properties that have been set\n * @internal\n */\nexport type CeriosBrand<T> = { [__brand]: T };\n\n/**\n * Helper type to represent a path through an object structure\n * Handles optional properties by unwrapping them with NonNullable\n */\ntype PathImpl<T, K extends keyof T = keyof T> = K extends string | number\n\t? NonNullable<T[K]> extends Record<string, any>\n\t\t? NonNullable<T[K]> extends Array<any>\n\t\t\t? K\n\t\t\t: K | `${K}.${PathImpl<NonNullable<T[K]>> & string}`\n\t\t: K\n\t: never;\n\nexport type Path<T> = PathImpl<T>;\n\n/**\n * Helper type to get the value at a specific path, handling optional properties\n */\ntype PathValue<T, P> = P extends keyof T\n\t? T[P]\n\t: P extends `${infer K}.${infer Rest}`\n\t\t? K extends keyof T\n\t\t\t? PathValue<NonNullable<T[K]>, Rest>\n\t\t\t: never\n\t\t: never;\n\n/**\n * Type-safe template for defining required fields using an array of paths.\n * Simply list the paths that are required.\n */\nexport type RequiredFieldsTemplate<T> = ReadonlyArray<Path<T>>;\n\n/**\n * Recursively makes all properties readonly for deep immutability.\n * Handles arrays, objects, and primitive types.\n *\n * @template T - The type to make deeply readonly\n */\nexport type DeepReadonly<T> = T extends (infer R)[]\n\t? DeepReadonlyArray<R>\n\t: T extends (...args: any[]) => any\n\t\t? T\n\t\t: T extends object\n\t\t\t? DeepReadonlyObject<T>\n\t\t\t: T;\n\n/**\n * Helper type for deep readonly arrays\n * @internal\n */\ninterface DeepReadonlyArray<T> extends ReadonlyArray<DeepReadonly<T>> {}\n\n/**\n * Helper type for deep readonly objects\n * @internal\n */\ntype DeepReadonlyObject<T> = {\n\treadonly [P in keyof T]: DeepReadonly<T[P]>;\n};\n\n/**\n * Cache the root key extraction to avoid repeated computation\n * Handles optional properties by ensuring the key is valid for T\n * @internal\n */\ntype RootKey<P extends string, T = any> = P extends `${infer K}.${string}`\n\t? K extends keyof T\n\t\t? K\n\t\t: never\n\t: P extends keyof T\n\t\t? P\n\t\t: never;\n\n/**\n * Recursively freezes an object and all its nested properties.\n * @param obj - The object to freeze\n * @returns The frozen object\n * @internal\n */\nfunction deepFreeze<T>(obj: T): T {\n\t// Retrieve the property names defined on obj\n\tObject.getOwnPropertyNames(obj).forEach(prop => {\n\t\tconst value = (obj as any)[prop];\n\n\t\t// Freeze properties before freezing self\n\t\tif (value !== null && (typeof value === \"object\" || typeof value === \"function\")) {\n\t\t\tdeepFreeze(value);\n\t\t}\n\t});\n\n\treturn Object.freeze(obj);\n}\n\n/**\n * Recursively seals an object and all its nested properties.\n * @param obj - The object to seal\n * @returns The sealed object\n * @internal\n */\nfunction deepSeal<T>(obj: T): T {\n\t// Retrieve the property names defined on obj\n\tObject.getOwnPropertyNames(obj).forEach(prop => {\n\t\tconst value = (obj as any)[prop];\n\n\t\t// Seal properties before sealing self\n\t\tif (value !== null && (typeof value === \"object\" || typeof value === \"function\")) {\n\t\t\tdeepSeal(value);\n\t\t}\n\t});\n\n\treturn Object.seal(obj);\n}\n\n/**\n * Abstract base class for creating type-safe builders with automatic property setters and compile-time validation of required fields.\n *\n * This class is intended to be extended by concrete builder implementations for your own types.\n * It provides utility methods for setting properties and building the final object, ensuring that all required fields are set at compile time.\n *\n * Example usage:\n * ```typescript\n * interface MyType { foo: string; bar: number[]; }\n * class MyTypeBuilder extends CeriosBuilder<MyType> {\n * static requiredTemplate: RequiredFieldsTemplate<MyType> = ['foo'];\n * setFoo(value: string) { return this.setProperty('foo', value); }\n * addBar(value: number) { return this.addToArrayProperty('bar', value); }\n * }\n * // Usage:\n * const obj = new MyTypeBuilder({})\n * .setFoo('hello')\n * .addBar(42)\n * .buildSafe(); // Validates that 'foo' is set\n * ```\n *\n * @template T - The complete type being built\n */\nexport abstract class CeriosBuilder<T extends object> {\n\t/**\n\t * Template defining which fields are required for this builder.\n\t * Subclasses should override this to specify their required fields as an array of paths.\n\t * The template is type-safe - only valid paths from type T can be used.\n\t */\n\tstatic requiredTemplate?: ReadonlyArray<string>;\n\n\t/**\n\t * Instance-level required fields that can be populated dynamically.\n\t * This allows adding required fields at runtime via the setRequiredFields method.\n\t * @private\n\t */\n\tprivate _requiredFields: Set<string> = new Set();\n\n\t/**\n\t * Sets the required fields for this builder instance.\n\t * This allows you to dynamically define which fields are required.\n\t *\n\t * @param fields - Array of dot-notation paths to required fields\n\t * @returns The builder instance for chaining\n\t *\n\t * @example\n\t * ```typescript\n\t * const builder = new MyBuilder({})\n\t * .setRequiredFields(['path.to.field1', 'path.to.field2'])\n\t * .setField1('value1')\n\t * .setField2('value2')\n\t * .buildSafe();\n\t * ```\n\t */\n\tsetRequiredFields(fields: ReadonlyArray<Path<T>>): this {\n\t\tthis._requiredFields = new Set([...fields] as string[]);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Gets the combined required fields from both the static template and instance-level fields.\n\t * @private\n\t */\n\tprivate getRequiredTemplate(): ReadonlyArray<string> {\n\t\tconst ctor = this.constructor as typeof CeriosBuilder;\n\t\tconst staticFields = ctor.requiredTemplate || [];\n\t\tconst instanceFields = Array.from(this._requiredFields);\n\n\t\t// Combine and deduplicate\n\t\treturn [...new Set([...staticFields, ...instanceFields])];\n\t}\n\n\t/**\n\t * Validates that all fields in the required template have been set.\n\t * @private\n\t */\n\tprivate validateRequiredFields(): string[] {\n\t\tconst requiredPaths = this.getRequiredTemplate();\n\t\tconst missing: string[] = [];\n\n\t\tfor (const path of requiredPaths) {\n\t\t\tconst keys = path.split(\".\");\n\t\t\tlet current: any = this._actual;\n\n\t\t\tfor (let i = 0; i < keys.length; i++) {\n\t\t\t\tconst key = keys[i];\n\t\t\t\tif (current === null || current === undefined || !(key in current)) {\n\t\t\t\t\tmissing.push(path);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcurrent = current[key];\n\t\t\t}\n\n\t\t\t// Check if the final value is null or undefined\n\t\t\tif (current === null || current === undefined) {\n\t\t\t\tif (!missing.includes(path)) {\n\t\t\t\t\tmissing.push(path);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn missing;\n\t}\n\n\t/**\n\t * Creates a new builder instance. Intended to be called by subclasses.\n\t *\n\t * @param _actual - The current partial state of the object being built\n\t * @param _requiredFields - Optional array of required field paths to preserve across instances\n\t * @protected\n\t */\n\tprotected constructor(\n\t\tprotected readonly _actual: Partial<T>,\n\t\t_requiredFields?: RequiredFieldsTemplate<T>\n\t) {\n\t\tif (_requiredFields) {\n\t\t\tthis._requiredFields = new Set([..._requiredFields] as string[]);\n\t\t}\n\t}\n\n\t/**\n\t * Sets a property value and returns a new builder instance with updated type state.\n\t * This method is intended to be wrapped by concrete builder methods in subclasses.\n\t *\n\t * @template K - The property key being set\n\t * @param key - The property key to set\n\t * @param value - The value to assign to the property\n\t * @returns A new builder instance with the property set and type state updated\n\t * @protected\n\t */\n\tprotected setProperty<K extends keyof T>(key: K, value: T[K]): this & CeriosBrand<Pick<T, K>> {\n\t\tconst BuilderClass = this.constructor as new (data: any, requiredFields?: RequiredFieldsTemplate<T>) => any;\n\t\treturn new BuilderClass(\n\t\t\t{\n\t\t\t\t...this._actual,\n\t\t\t\t[key]: value,\n\t\t\t},\n\t\t\tArray.from(this._requiredFields) as unknown as RequiredFieldsTemplate<T>\n\t\t) as this & CeriosBrand<Pick<T, K>>;\n\t}\n\n\t/**\n\t * Sets multiple property values at once and returns a new builder instance with updated type state.\n\t * @param props - An object with one or more properties to set.\n\t * @returns A new builder instance with the properties set and type state updated.\n\t * @protected\n\t */\n\tprotected setProperties<K extends keyof T>(props: Pick<T, K>): this & CeriosBrand<Pick<T, K>> {\n\t\tconst BuilderClass = this.constructor as new (data: any, requiredFields?: RequiredFieldsTemplate<T>) => any;\n\t\treturn new BuilderClass(\n\t\t\t{\n\t\t\t\t...this._actual,\n\t\t\t\t...props,\n\t\t\t},\n\t\t\tArray.from(this._requiredFields) as unknown as RequiredFieldsTemplate<T>\n\t\t) as this & CeriosBrand<Pick<T, K>>;\n\t}\n\n\t/**\n\t * Sets a deeply nested property value and returns a new builder instance with updated type state.\n\t * This method uses dot notation to set nested properties in a type-safe way.\n\t *\n\t * @template P - The property path (e.g., \"parent.child.grandchild\")\n\t * @param path - The dot-notation path to the property\n\t * @param value - The value to assign to the nested property\n\t * @returns A new builder instance with the nested property set\n\t * @protected\n\t *\n\t * @example\n\t * ```typescript\n\t * builder.setNestedProperty('Order.Details.CustomerId', 'value')\n\t * ```\n\t */\n\tprotected setNestedProperty<P extends Path<T>>(\n\t\tpath: P,\n\t\tvalue: PathValue<T, P>\n\t): this & CeriosBrand<Pick<T, RootKey<P & string, T> extends never ? keyof T : RootKey<P & string, T>>> {\n\t\tconst BuilderClass = this.constructor as new (data: any, requiredFields?: RequiredFieldsTemplate<T>) => any;\n\t\tconst keys = (path as string).split(\".\");\n\t\tconst newActual = this.deepClone(this._actual);\n\n\t\tlet current: any = newActual;\n\t\tfor (let i = 0; i < keys.length - 1; i++) {\n\t\t\tconst key = keys[i];\n\t\t\tif (!(key in current) || typeof current[key] !== \"object\" || current[key] === null) {\n\t\t\t\tcurrent[key] = {};\n\t\t\t} else {\n\t\t\t\tcurrent[key] = this.deepClone(current[key]);\n\t\t\t}\n\t\t\tcurrent = current[key];\n\t\t}\n\n\t\tcurrent[keys[keys.length - 1]] = value;\n\n\t\treturn new BuilderClass(\n\t\t\tnewActual,\n\t\t\tArray.from(this._requiredFields) as unknown as RequiredFieldsTemplate<T>\n\t\t) as this & CeriosBrand<Pick<T, RootKey<P & string, T> extends never ? keyof T : RootKey<P & string, T>>>;\n\t}\n\n\t/**\n\t * Deep clone helper for nested objects\n\t * @private\n\t */\n\tprivate deepClone<V>(obj: V): V {\n\t\tif (obj === null || typeof obj !== \"object\") {\n\t\t\treturn obj;\n\t\t}\n\t\tif (Array.isArray(obj)) {\n\t\t\treturn obj.map(item => this.deepClone(item)) as any;\n\t\t}\n\t\tconst cloned: any = {};\n\t\tconst hasOwn = Object.prototype.hasOwnProperty;\n\t\tfor (const key in obj) {\n\t\t\tif (hasOwn.call(obj, key)) {\n\t\t\t\tcloned[key] = this.deepClone(obj[key]);\n\t\t\t}\n\t\t}\n\t\treturn cloned;\n\t}\n\n\t/**\n\t * Adds a value to an array property and returns a new builder instance with updated type state.\n\t * This method is intended to be wrapped by concrete builder methods in subclasses for array properties.\n\t *\n\t * @template K - The property key (must be an array property)\n\t * @template V - The type of the array element\n\t * @param key - The array property key to add to\n\t * @param value - The value to add to the array\n\t * @returns A new builder instance with the array property updated and type state updated\n\t * @protected\n\t */\n\tprotected addToArrayProperty<\n\t\tK extends { [P in keyof T]: NonNullable<T[P]> extends Array<any> ? P : never }[keyof T],\n\t\tV extends T[K] extends Array<infer U> ? U : T[K] extends Array<infer U> | undefined ? U : never,\n\t>(key: K, value: V): this & CeriosBrand<Pick<T, K>> {\n\t\tconst BuilderClass = this.constructor as new (data: any, requiredFields?: RequiredFieldsTemplate<T>) => any;\n\t\tconst currentArray = (this._actual[key] as Array<V> | undefined) ?? [];\n\t\treturn new BuilderClass(\n\t\t\t{\n\t\t\t\t...this._actual,\n\t\t\t\t[key]: [...currentArray, value],\n\t\t\t},\n\t\t\tArray.from(this._requiredFields) as unknown as RequiredFieldsTemplate<T>\n\t\t) as this & CeriosBrand<Pick<T, K>>;\n\t}\n\n\t/**\n\t * Builds the final object with both compile-time and runtime validation.\n\t * This is the recommended and safest way to build objects.\n\t *\n\t * - Compile-time: TypeScript enforces all required properties are set\n\t * - Runtime: Validates all fields in the requiredTemplate\n\t *\n\t * @returns The fully built object of type T\n\t * @throws {Error} If any required field is missing at runtime\n\t */\n\tbuild(this: this & CeriosBrand<T>): T {\n\t\tconst missing = this.validateRequiredFields();\n\n\t\tif (missing.length > 0) {\n\t\t\tthrow new Error(`Missing required fields: ${missing.join(\", \")}. Please set these fields before calling build.`);\n\t\t}\n\n\t\treturn this._actual as T;\n\t}\n\n\t/**\n\t * Builds the final object with only compile-time validation, skipping runtime checks.\n\t * Use this when you want TypeScript safety but need to skip runtime validation for performance.\n\t *\n\t * - Compile-time: TypeScript enforces all required properties are set\n\t * - Runtime: No validation\n\t *\n\t * @returns The fully built object of type T\n\t */\n\tbuildWithoutRuntimeValidation(this: this & CeriosBrand<T>): T {\n\t\treturn this._actual as T;\n\t}\n\n\t/**\n\t * Builds the final object with only runtime validation, skipping compile-time checks.\n\t * Use this when building from external data where compile-time checks aren't possible.\n\t *\n\t * - Compile-time: No TypeScript enforcement\n\t * - Runtime: Validates all fields in the requiredTemplate\n\t *\n\t * @returns The fully built object of type T\n\t * @throws {Error} If any required field is missing at runtime\n\t */\n\tbuildWithoutCompileTimeValidation(): T {\n\t\tconst missing = this.validateRequiredFields();\n\n\t\tif (missing.length > 0) {\n\t\t\tthrow new Error(`Missing required fields: ${missing.join(\", \")}. Please set these fields before calling build.`);\n\t\t}\n\n\t\treturn this._actual as T;\n\t}\n\n\t/**\n\t * Builds the final object without any validation (neither compile-time nor runtime).\n\t * Use this only when you're certain the object is valid and need maximum performance.\n\t *\n\t * - Compile-time: No TypeScript enforcement\n\t * - Runtime: No validation\n\t *\n\t * @returns The object of type T (may be incomplete)\n\t */\n\tbuildUnsafe(): T {\n\t\treturn this._actual as T;\n\t}\n\n\t/**\n\t * Builds a partial object, which may not have all required fields set.\n\t * This is useful for scenarios where you want to inspect or validate the current state before finalizing.\n\t *\n\t * @returns The partially built object\n\t */\n\tbuildPartial(): Partial<T> {\n\t\treturn this._actual as Partial<T>;\n\t}\n\n\t/**\n\t * @deprecated Use build() instead. buildSafe() is now an alias for build().\n\t * Builds the final object with runtime validation using the requiredTemplate.\n\t * This method validates that all fields in the requiredTemplate array are present.\n\t *\n\t * @returns The fully built object of type T\n\t * @throws {Error} If any required field is missing at runtime\n\t */\n\tbuildSafe(): T {\n\t\treturn this.buildWithoutCompileTimeValidation();\n\t}\n\n\t/**\n\t * Builds and freezes the final object with both compile-time and runtime validation.\n\t * The returned object is shallowly frozen - top-level properties cannot be modified,\n\t * but nested objects remain mutable.\n\t *\n\t * - Compile-time: TypeScript enforces all required properties are set\n\t * - Runtime: Validates all fields in the requiredTemplate and applies Object.freeze()\n\t *\n\t * @returns The frozen object of type Readonly<T>\n\t * @throws {Error} If any required field is missing at runtime\n\t */\n\tbuildFrozen(this: this & CeriosBrand<T>): Readonly<T> {\n\t\tconst missing = this.validateRequiredFields();\n\n\t\tif (missing.length > 0) {\n\t\t\tthrow new Error(`Missing required fields: ${missing.join(\", \")}. Please set these fields before calling build.`);\n\t\t}\n\n\t\treturn Object.freeze(this._actual as T);\n\t}\n\n\t/**\n\t * Builds and deeply freezes the final object with both compile-time and runtime validation.\n\t * The returned object is recursively frozen - all nested objects and arrays are also frozen.\n\t *\n\t * - Compile-time: TypeScript enforces all required properties are set\n\t * - Runtime: Validates all fields in the requiredTemplate and recursively applies Object.freeze()\n\t *\n\t * @returns The deeply frozen object of type DeepReadonly<T>\n\t * @throws {Error} If any required field is missing at runtime\n\t */\n\tbuildDeepFrozen(this: this & CeriosBrand<T>): DeepReadonly<T> {\n\t\tconst missing = this.validateRequiredFields();\n\n\t\tif (missing.length > 0) {\n\t\t\tthrow new Error(`Missing required fields: ${missing.join(\", \")}. Please set these fields before calling build.`);\n\t\t}\n\n\t\treturn deepFreeze(this._actual as T) as DeepReadonly<T>;\n\t}\n\n\t/**\n\t * Builds and seals the final object with both compile-time and runtime validation.\n\t * The returned object is shallowly sealed - properties cannot be added or removed,\n\t * but existing properties can still be modified. Nested objects remain unsealed.\n\t *\n\t * - Compile-time: TypeScript enforces all required properties are set\n\t * - Runtime: Validates all fields in the requiredTemplate and applies Object.seal()\n\t *\n\t * @returns The sealed object of type T\n\t * @throws {Error} If any required field is missing at runtime\n\t */\n\tbuildSealed(this: this & CeriosBrand<T>): T {\n\t\tconst missing = this.validateRequiredFields();\n\n\t\tif (missing.length > 0) {\n\t\t\tthrow new Error(`Missing required fields: ${missing.join(\", \")}. Please set these fields before calling build.`);\n\t\t}\n\n\t\treturn Object.seal(this._actual as T);\n\t}\n\n\t/**\n\t * Builds and deeply seals the final object with both compile-time and runtime validation.\n\t * The returned object is recursively sealed - properties cannot be added or removed at any level,\n\t * but existing properties can still be modified.\n\t *\n\t * - Compile-time: TypeScript enforces all required properties are set\n\t * - Runtime: Validates all fields in the requiredTemplate and recursively applies Object.seal()\n\t *\n\t * @returns The deeply sealed object of type T\n\t * @throws {Error} If any required field is missing at runtime\n\t */\n\tbuildDeepSealed(this: this & CeriosBrand<T>): T {\n\t\tconst missing = this.validateRequiredFields();\n\n\t\tif (missing.length > 0) {\n\t\t\tthrow new Error(`Missing required fields: ${missing.join(\", \")}. Please set these fields before calling build.`);\n\t\t}\n\n\t\treturn deepSeal(this._actual as T);\n\t}\n}\n"],"mappings":"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","names":[]} | ||
| {"version":3,"sources":["../src/cerios-builder.ts","../src/cerios-class-builder.ts"],"sourcesContent":["import { DeepReadonly } from \"./types\";\n\n/**\n * Unique symbol used internally to brand types and track which properties have been set in the builder's type.\n *\n * @internal\n */\ndeclare const __brand: unique symbol;\n\n/**\n * Type utility for branding builder types with information about which properties have been set.\n * This is used to enforce compile-time safety for required fields in the builder pattern.\n *\n * @template T - The type representing the set of properties that have been set\n * @internal\n */\nexport type CeriosBrand<T> = { [__brand]: T };\n\n/**\n * Helper type to represent a path through an object structure\n * Handles optional properties by unwrapping them with NonNullable\n */\ntype PathImpl<T, K extends keyof T = keyof T> = K extends string | number\n\t? NonNullable<T[K]> extends Record<string, any>\n\t\t? NonNullable<T[K]> extends Array<any>\n\t\t\t? K\n\t\t\t: K | `${K}.${PathImpl<NonNullable<T[K]>> & string}`\n\t\t: K\n\t: never;\n\nexport type Path<T> = PathImpl<T>;\n\n/**\n * Helper type to get the value at a specific path, handling optional properties\n */\ntype PathValue<T, P> = P extends keyof T\n\t? T[P]\n\t: P extends `${infer K}.${infer Rest}`\n\t\t? K extends keyof T\n\t\t\t? PathValue<NonNullable<T[K]>, Rest>\n\t\t\t: never\n\t\t: never;\n\n/**\n * Type-safe template for defining required fields using an array of paths.\n * Simply list the paths that are required.\n */\nexport type RequiredFieldsTemplate<T> = ReadonlyArray<Path<T>>;\n\n/**\n * Cache the root key extraction to avoid repeated computation\n * Handles optional properties by ensuring the key is valid for T\n * @internal\n */\ntype RootKey<P extends string, T = any> = P extends `${infer K}.${string}`\n\t? K extends keyof T\n\t\t? K\n\t\t: never\n\t: P extends keyof T\n\t\t? P\n\t\t: never;\n\n/**\n * Recursively freezes an object and all its nested properties.\n * @param obj - The object to freeze\n * @returns The frozen object\n * @internal\n */\nfunction deepFreeze<T>(obj: T): T {\n\t// Retrieve the property names defined on obj\n\tObject.getOwnPropertyNames(obj).forEach(prop => {\n\t\tconst value = (obj as any)[prop];\n\n\t\t// Freeze properties before freezing self\n\t\tif (value !== null && (typeof value === \"object\" || typeof value === \"function\")) {\n\t\t\tdeepFreeze(value);\n\t\t}\n\t});\n\n\treturn Object.freeze(obj);\n}\n\n/**\n * Recursively seals an object and all its nested properties.\n * @param obj - The object to seal\n * @returns The sealed object\n * @internal\n */\nfunction deepSeal<T>(obj: T): T {\n\t// Retrieve the property names defined on obj\n\tObject.getOwnPropertyNames(obj).forEach(prop => {\n\t\tconst value = (obj as any)[prop];\n\n\t\t// Seal properties before sealing self\n\t\tif (value !== null && (typeof value === \"object\" || typeof value === \"function\")) {\n\t\t\tdeepSeal(value);\n\t\t}\n\t});\n\n\treturn Object.seal(obj);\n}\n\n/**\n * Abstract base class for creating type-safe builders with automatic property setters and compile-time validation of required fields.\n *\n * This class is intended to be extended by concrete builder implementations for your own types.\n * It provides utility methods for setting properties and building the final object, ensuring that all required fields are set at compile time.\n *\n * Example usage:\n * ```typescript\n * interface MyType { foo: string; bar: number[]; }\n * class MyTypeBuilder extends CeriosBuilder<MyType> {\n * static requiredTemplate: RequiredFieldsTemplate<MyType> = ['foo'];\n * setFoo(value: string) { return this.setProperty('foo', value); }\n * addBar(value: number) { return this.addToArrayProperty('bar', value); }\n * }\n * // Usage:\n * const obj = new MyTypeBuilder({})\n * .setFoo('hello')\n * .addBar(42)\n * .buildSafe(); // Validates that 'foo' is set\n * ```\n *\n * @template T - The complete type being built\n */\nexport abstract class CeriosBuilder<T extends object> {\n\t/**\n\t * Template defining which fields are required for this builder.\n\t * Subclasses should override this to specify their required fields as an array of paths.\n\t * The template is type-safe - only valid paths from type T can be used.\n\t */\n\tstatic requiredTemplate?: ReadonlyArray<string>;\n\n\t/**\n\t * Instance-level required fields that can be populated dynamically.\n\t * This allows adding required fields at runtime via the setRequiredFields method.\n\t * @private\n\t */\n\tprivate _requiredFields: Set<string> = new Set();\n\n\t/**\n\t * Sets the required fields for this builder instance.\n\t * This allows you to dynamically define which fields are required.\n\t *\n\t * @param fields - Array of dot-notation paths to required fields\n\t * @returns The builder instance for chaining\n\t *\n\t * @example\n\t * ```typescript\n\t * const builder = new MyBuilder({})\n\t * .setRequiredFields(['path.to.field1', 'path.to.field2'])\n\t * .setField1('value1')\n\t * .setField2('value2')\n\t * .buildSafe();\n\t * ```\n\t */\n\tsetRequiredFields(fields: ReadonlyArray<Path<T>>): this {\n\t\tthis._requiredFields = new Set([...fields] as string[]);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Gets the combined required fields from both the static template and instance-level fields.\n\t * @private\n\t */\n\tprivate getRequiredTemplate(): ReadonlyArray<string> {\n\t\tconst ctor = this.constructor as typeof CeriosBuilder;\n\t\tconst staticFields = ctor.requiredTemplate || [];\n\t\tconst instanceFields = Array.from(this._requiredFields);\n\n\t\t// Combine and deduplicate\n\t\treturn [...new Set([...staticFields, ...instanceFields])];\n\t}\n\n\t/**\n\t * Validates that all fields in the required template have been set.\n\t * @private\n\t */\n\tprivate validateRequiredFields(): string[] {\n\t\tconst requiredPaths = this.getRequiredTemplate();\n\t\tconst missing: string[] = [];\n\n\t\tfor (const path of requiredPaths) {\n\t\t\tconst keys = path.split(\".\");\n\t\t\tlet current: any = this._actual;\n\n\t\t\tfor (let i = 0; i < keys.length; i++) {\n\t\t\t\tconst key = keys[i];\n\t\t\t\tif (current === null || current === undefined || !(key in current)) {\n\t\t\t\t\tmissing.push(path);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcurrent = current[key];\n\t\t\t}\n\n\t\t\t// Check if the final value is null or undefined\n\t\t\tif (current === null || current === undefined) {\n\t\t\t\tif (!missing.includes(path)) {\n\t\t\t\t\tmissing.push(path);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn missing;\n\t}\n\n\t/**\n\t * Creates a new builder instance. Intended to be called by subclasses.\n\t *\n\t * @param _actual - The current partial state of the object being built\n\t * @param _requiredFields - Optional array of required field paths to preserve across instances\n\t * @protected\n\t */\n\tprotected constructor(\n\t\tprotected readonly _actual: Partial<T>,\n\t\t_requiredFields?: RequiredFieldsTemplate<T>\n\t) {\n\t\tif (_requiredFields) {\n\t\t\tthis._requiredFields = new Set([..._requiredFields] as string[]);\n\t\t}\n\t}\n\n\t/**\n\t * Sets a property value and returns a new builder instance with updated type state.\n\t * This method is intended to be wrapped by concrete builder methods in subclasses.\n\t *\n\t * @template K - The property key being set\n\t * @param key - The property key to set\n\t * @param value - The value to assign to the property\n\t * @returns A new builder instance with the property set and type state updated\n\t * @protected\n\t */\n\tprotected setProperty<K extends keyof T>(key: K, value: T[K]): this & CeriosBrand<Pick<T, K>> {\n\t\tconst BuilderClass = this.constructor as new (data: any, requiredFields?: RequiredFieldsTemplate<T>) => any;\n\t\treturn new BuilderClass(\n\t\t\t{\n\t\t\t\t...this._actual,\n\t\t\t\t[key]: value,\n\t\t\t},\n\t\t\tArray.from(this._requiredFields) as unknown as RequiredFieldsTemplate<T>\n\t\t) as this & CeriosBrand<Pick<T, K>>;\n\t}\n\n\t/**\n\t * Sets multiple property values at once and returns a new builder instance with updated type state.\n\t * @param props - An object with one or more properties to set.\n\t * @returns A new builder instance with the properties set and type state updated.\n\t * @protected\n\t */\n\tprotected setProperties<K extends keyof T>(props: Pick<T, K>): this & CeriosBrand<Pick<T, K>> {\n\t\tconst BuilderClass = this.constructor as new (data: any, requiredFields?: RequiredFieldsTemplate<T>) => any;\n\t\treturn new BuilderClass(\n\t\t\t{\n\t\t\t\t...this._actual,\n\t\t\t\t...props,\n\t\t\t},\n\t\t\tArray.from(this._requiredFields) as unknown as RequiredFieldsTemplate<T>\n\t\t) as this & CeriosBrand<Pick<T, K>>;\n\t}\n\n\t/**\n\t * Sets a deeply nested property value and returns a new builder instance with updated type state.\n\t * This method uses dot notation to set nested properties in a type-safe way.\n\t *\n\t * @template P - The property path (e.g., \"parent.child.grandchild\")\n\t * @param path - The dot-notation path to the property\n\t * @param value - The value to assign to the nested property\n\t * @returns A new builder instance with the nested property set\n\t * @protected\n\t *\n\t * @example\n\t * ```typescript\n\t * builder.setNestedProperty('Order.Details.CustomerId', 'value')\n\t * ```\n\t */\n\tprotected setNestedProperty<P extends Path<T>>(\n\t\tpath: P,\n\t\tvalue: PathValue<T, P>\n\t): this & CeriosBrand<Pick<T, RootKey<P & string, T> extends never ? keyof T : RootKey<P & string, T>>> {\n\t\tconst BuilderClass = this.constructor as new (data: any, requiredFields?: RequiredFieldsTemplate<T>) => any;\n\t\tconst keys = (path as string).split(\".\");\n\t\tconst newActual = this.deepClone(this._actual);\n\n\t\tlet current: any = newActual;\n\t\tfor (let i = 0; i < keys.length - 1; i++) {\n\t\t\tconst key = keys[i];\n\t\t\tif (!(key in current) || typeof current[key] !== \"object\" || current[key] === null) {\n\t\t\t\tcurrent[key] = {};\n\t\t\t} else {\n\t\t\t\tcurrent[key] = this.deepClone(current[key]);\n\t\t\t}\n\t\t\tcurrent = current[key];\n\t\t}\n\n\t\tcurrent[keys[keys.length - 1]] = value;\n\n\t\treturn new BuilderClass(\n\t\t\tnewActual,\n\t\t\tArray.from(this._requiredFields) as unknown as RequiredFieldsTemplate<T>\n\t\t) as this & CeriosBrand<Pick<T, RootKey<P & string, T> extends never ? keyof T : RootKey<P & string, T>>>;\n\t}\n\n\t/**\n\t * Deep clone helper for nested objects\n\t * @private\n\t */\n\tprivate deepClone<V>(obj: V): V {\n\t\tif (obj === null || typeof obj !== \"object\") {\n\t\t\treturn obj;\n\t\t}\n\t\tif (Array.isArray(obj)) {\n\t\t\treturn obj.map(item => this.deepClone(item)) as any;\n\t\t}\n\t\tconst cloned: any = {};\n\t\tconst hasOwn = Object.prototype.hasOwnProperty;\n\t\tfor (const key in obj) {\n\t\t\tif (hasOwn.call(obj, key)) {\n\t\t\t\tcloned[key] = this.deepClone(obj[key]);\n\t\t\t}\n\t\t}\n\t\treturn cloned;\n\t}\n\n\t/**\n\t * Adds a value to an array property and returns a new builder instance with updated type state.\n\t * This method is intended to be wrapped by concrete builder methods in subclasses for array properties.\n\t *\n\t * @template K - The property key (must be an array property)\n\t * @template V - The type of the array element\n\t * @param key - The array property key to add to\n\t * @param value - The value to add to the array\n\t * @returns A new builder instance with the array property updated and type state updated\n\t * @protected\n\t */\n\tprotected addToArrayProperty<\n\t\tK extends { [P in keyof T]: NonNullable<T[P]> extends Array<any> ? P : never }[keyof T],\n\t\tV extends T[K] extends Array<infer U> ? U : T[K] extends Array<infer U> | undefined ? U : never,\n\t>(key: K, value: V): this & CeriosBrand<Pick<T, K>> {\n\t\tconst BuilderClass = this.constructor as new (data: any, requiredFields?: RequiredFieldsTemplate<T>) => any;\n\t\tconst currentArray = (this._actual[key] as Array<V> | undefined) ?? [];\n\t\treturn new BuilderClass(\n\t\t\t{\n\t\t\t\t...this._actual,\n\t\t\t\t[key]: [...currentArray, value],\n\t\t\t},\n\t\t\tArray.from(this._requiredFields) as unknown as RequiredFieldsTemplate<T>\n\t\t) as this & CeriosBrand<Pick<T, K>>;\n\t}\n\n\t/**\n\t * Builds the final object with both compile-time and runtime validation.\n\t * This is the recommended and safest way to build objects.\n\t *\n\t * - Compile-time: TypeScript enforces all required properties are set\n\t * - Runtime: Validates all fields in the requiredTemplate\n\t *\n\t * @returns The fully built object of type T\n\t * @throws {Error} If any required field is missing at runtime\n\t */\n\tbuild(this: this & CeriosBrand<T>): T {\n\t\tconst missing = this.validateRequiredFields();\n\n\t\tif (missing.length > 0) {\n\t\t\tthrow new Error(`Missing required fields: ${missing.join(\", \")}. Please set these fields before calling build.`);\n\t\t}\n\n\t\treturn this._actual as T;\n\t}\n\n\t/**\n\t * Builds the final object with only compile-time validation, skipping runtime checks.\n\t * Use this when you want TypeScript safety but need to skip runtime validation for performance.\n\t *\n\t * - Compile-time: TypeScript enforces all required properties are set\n\t * - Runtime: No validation\n\t *\n\t * @returns The fully built object of type T\n\t */\n\tbuildWithoutRuntimeValidation(this: this & CeriosBrand<T>): T {\n\t\treturn this._actual as T;\n\t}\n\n\t/**\n\t * Builds the final object with only runtime validation, skipping compile-time checks.\n\t * Use this when building from external data where compile-time checks aren't possible.\n\t *\n\t * - Compile-time: No TypeScript enforcement\n\t * - Runtime: Validates all fields in the requiredTemplate\n\t *\n\t * @returns The fully built object of type T\n\t * @throws {Error} If any required field is missing at runtime\n\t */\n\tbuildWithoutCompileTimeValidation(): T {\n\t\tconst missing = this.validateRequiredFields();\n\n\t\tif (missing.length > 0) {\n\t\t\tthrow new Error(`Missing required fields: ${missing.join(\", \")}. Please set these fields before calling build.`);\n\t\t}\n\n\t\treturn this._actual as T;\n\t}\n\n\t/**\n\t * Builds the final object without any validation (neither compile-time nor runtime).\n\t * Use this only when you're certain the object is valid and need maximum performance.\n\t *\n\t * - Compile-time: No TypeScript enforcement\n\t * - Runtime: No validation\n\t *\n\t * @returns The object of type T (may be incomplete)\n\t */\n\tbuildUnsafe(): T {\n\t\treturn this._actual as T;\n\t}\n\n\t/**\n\t * Builds a partial object, which may not have all required fields set.\n\t * This is useful for scenarios where you want to inspect or validate the current state before finalizing.\n\t *\n\t * @returns The partially built object\n\t */\n\tbuildPartial(): Partial<T> {\n\t\treturn this._actual as Partial<T>;\n\t}\n\n\t/**\n\t * @deprecated Use build() instead. buildSafe() is now an alias for build().\n\t * Builds the final object with runtime validation using the requiredTemplate.\n\t * This method validates that all fields in the requiredTemplate array are present.\n\t *\n\t * @returns The fully built object of type T\n\t * @throws {Error} If any required field is missing at runtime\n\t */\n\tbuildSafe(): T {\n\t\treturn this.buildWithoutCompileTimeValidation();\n\t}\n\n\t/**\n\t * Builds and freezes the final object with both compile-time and runtime validation.\n\t * The returned object is shallowly frozen - top-level properties cannot be modified,\n\t * but nested objects remain mutable.\n\t *\n\t * - Compile-time: TypeScript enforces all required properties are set\n\t * - Runtime: Validates all fields in the requiredTemplate and applies Object.freeze()\n\t *\n\t * @returns The frozen object of type Readonly<T>\n\t * @throws {Error} If any required field is missing at runtime\n\t */\n\tbuildFrozen(this: this & CeriosBrand<T>): Readonly<T> {\n\t\tconst missing = this.validateRequiredFields();\n\n\t\tif (missing.length > 0) {\n\t\t\tthrow new Error(`Missing required fields: ${missing.join(\", \")}. Please set these fields before calling build.`);\n\t\t}\n\n\t\treturn Object.freeze(this._actual as T);\n\t}\n\n\t/**\n\t * Builds and deeply freezes the final object with both compile-time and runtime validation.\n\t * The returned object is recursively frozen - all nested objects and arrays are also frozen.\n\t *\n\t * - Compile-time: TypeScript enforces all required properties are set\n\t * - Runtime: Validates all fields in the requiredTemplate and recursively applies Object.freeze()\n\t *\n\t * @returns The deeply frozen object of type DeepReadonly<T>\n\t * @throws {Error} If any required field is missing at runtime\n\t */\n\tbuildDeepFrozen(this: this & CeriosBrand<T>): DeepReadonly<T> {\n\t\tconst missing = this.validateRequiredFields();\n\n\t\tif (missing.length > 0) {\n\t\t\tthrow new Error(`Missing required fields: ${missing.join(\", \")}. Please set these fields before calling build.`);\n\t\t}\n\n\t\treturn deepFreeze(this._actual as T) as DeepReadonly<T>;\n\t}\n\n\t/**\n\t * Builds and seals the final object with both compile-time and runtime validation.\n\t * The returned object is shallowly sealed - properties cannot be added or removed,\n\t * but existing properties can still be modified. Nested objects remain unsealed.\n\t *\n\t * - Compile-time: TypeScript enforces all required properties are set\n\t * - Runtime: Validates all fields in the requiredTemplate and applies Object.seal()\n\t *\n\t * @returns The sealed object of type T\n\t * @throws {Error} If any required field is missing at runtime\n\t */\n\tbuildSealed(this: this & CeriosBrand<T>): T {\n\t\tconst missing = this.validateRequiredFields();\n\n\t\tif (missing.length > 0) {\n\t\t\tthrow new Error(`Missing required fields: ${missing.join(\", \")}. Please set these fields before calling build.`);\n\t\t}\n\n\t\treturn Object.seal(this._actual as T);\n\t}\n\n\t/**\n\t * Builds and deeply seals the final object with both compile-time and runtime validation.\n\t * The returned object is recursively sealed - properties cannot be added or removed at any level,\n\t * but existing properties can still be modified.\n\t *\n\t * - Compile-time: TypeScript enforces all required properties are set\n\t * - Runtime: Validates all fields in the requiredTemplate and recursively applies Object.seal()\n\t *\n\t * @returns The deeply sealed object of type T\n\t * @throws {Error} If any required field is missing at runtime\n\t */\n\tbuildDeepSealed(this: this & CeriosBrand<T>): T {\n\t\tconst missing = this.validateRequiredFields();\n\n\t\tif (missing.length > 0) {\n\t\t\tthrow new Error(`Missing required fields: ${missing.join(\", \")}. Please set these fields before calling build.`);\n\t\t}\n\n\t\treturn deepSeal(this._actual as T);\n\t}\n}\n","import type { DeepReadonly } from \"./types\";\n\n/**\n * Type representing a class constructor that can be instantiated with optional partial data.\n * @template T - The type that the constructor creates\n */\nexport type ClassConstructor<T> = new (data?: Partial<T>) => T;\n\n/**\n * Helper to extract only data properties (exclude methods) from a class type.\n * This allows compile-time validation to work properly with classes.\n */\ntype DataPropertiesOnly<T> = {\n\t[K in keyof T as T[K] extends (...args: any[]) => any ? never : K]: T[K];\n};\n\n/**\n * Brand type specifically for class builders that only tracks data properties.\n */\nexport type CeriosClassBrand<T> = {\n\treadonly __classBuilderBrand: T;\n};\n\n/**\n * Type-safe builder specifically designed for classes.\n * This builder automatically instantiates the target class and provides:\n * - Compile-time validation that works with class methods\n * - Automatic runtime validation of nested class instances\n * - Preservation of decorators and class methods\n *\n * Example usage:\n * ```typescript\n * class Person {\n * name!: string;\n * age!: number;\n * email?: string;\n *\n * constructor(data?: Partial<Person>) {\n * if (data) Object.assign(this, data);\n * }\n *\n * greet() { return `Hello, I'm ${this.name}`; }\n * }\n *\n * const builder = new CeriosClassBuilder(Person);\n * const person = builder\n * .setProperty('name', 'John')\n * .setProperty('age', 30)\n * .build();\n * ```\n *\n * @template T - The class type being built\n */\nexport class CeriosClassBuilder<T extends object> {\n\t/**\n\t * The class constructor to instantiate when building.\n\t * @private\n\t */\n\tprivate readonly _classConstructor: ClassConstructor<T>;\n\n\t/**\n\t * The current partial state of the object being built.\n\t * @private\n\t */\n\tprotected readonly _actual: Partial<T>;\n\n\t/**\n\t * Creates a new class builder instance.\n\t * @param classConstructor - The class constructor to use for building\n\t * @param data - Optional initial data\n\t */\n\tprotected constructor(classConstructor: ClassConstructor<T>, data: Partial<T> = {}) {\n\t\tthis._classConstructor = classConstructor;\n\t\tthis._actual = data;\n\t}\n\n\t/**\n\t * Gets the class constructor for this builder.\n\t * @private\n\t */\n\tprivate getClassConstructor(): ClassConstructor<T> {\n\t\treturn this._classConstructor;\n\t}\n\n\t/**\n\t * Creates a new instance of the builder with updated data.\n\t * Subclasses can override this to return the correct subclass type.\n\t * @private\n\t */\n\tprivate createBuilder(data: Partial<T>): this {\n\t\tconst BuilderClass = this.constructor as new (classConstructor: ClassConstructor<T>, data: Partial<T>) => this;\n\t\treturn new BuilderClass(this._classConstructor, data);\n\t}\n\n\t/**\n\t * Sets a property value and returns a new builder instance with updated type state.\n\t * @template K - The property key being set\n\t * @param key - The property key to set\n\t * @param value - The value to assign to the property\n\t * @returns A new builder instance with the property set\n\t */\n\tsetProperty<K extends keyof DataPropertiesOnly<T>>(\n\t\tkey: K,\n\t\tvalue: DataPropertiesOnly<T>[K]\n\t): this & CeriosClassBrand<Pick<DataPropertiesOnly<T>, K>> {\n\t\tconst newBuilder = this.createBuilder({\n\t\t\t...this._actual,\n\t\t\t[key]: value,\n\t\t} as Partial<T>);\n\t\treturn newBuilder as this & CeriosClassBrand<Pick<DataPropertiesOnly<T>, K>>;\n\t}\n\n\t/**\n\t * Sets multiple properties at once.\n\t * @template K - The property keys being set\n\t * @param props - Object with properties to set\n\t * @returns A new builder instance with the properties set\n\t */\n\tsetProperties<K extends keyof DataPropertiesOnly<T>>(\n\t\tprops: Pick<DataPropertiesOnly<T>, K>\n\t): this & CeriosClassBrand<Pick<DataPropertiesOnly<T>, K>> {\n\t\tconst newBuilder = this.createBuilder({\n\t\t\t...this._actual,\n\t\t\t...props,\n\t\t} as Partial<T>);\n\t\treturn newBuilder as this & CeriosClassBrand<Pick<DataPropertiesOnly<T>, K>>;\n\t}\n\n\t/**\n\t * Adds a value to an array property.\n\t * @template K - The array property key\n\t * @template V - The array element type\n\t * @param key - The array property key\n\t * @param value - The value to add to the array\n\t * @returns A new builder instance with the value added\n\t */\n\taddToArrayProperty<\n\t\tK extends {\n\t\t\t[P in keyof DataPropertiesOnly<T>]: NonNullable<DataPropertiesOnly<T>[P]> extends Array<any> ? P : never;\n\t\t}[keyof DataPropertiesOnly<T>],\n\t\tV extends DataPropertiesOnly<T>[K] extends Array<infer U>\n\t\t\t? U\n\t\t\t: DataPropertiesOnly<T>[K] extends Array<infer U> | undefined\n\t\t\t\t? U\n\t\t\t\t: never,\n\t>(key: K, value: V): this & CeriosClassBrand<Pick<DataPropertiesOnly<T>, K>> {\n\t\tconst currentArray = (this._actual[key as keyof T] as Array<V> | undefined) ?? [];\n\t\tconst newBuilder = this.createBuilder({\n\t\t\t...this._actual,\n\t\t\t[key]: [...currentArray, value],\n\t\t} as Partial<T>);\n\t\treturn newBuilder as this & CeriosClassBrand<Pick<DataPropertiesOnly<T>, K>>;\n\t}\n\n\t/**\n\t * Builds the final class instance with compile-time and runtime validation.\n\t * - Compile-time: TypeScript enforces all data properties are set\n\t * - Runtime: Validates nested class instances are properly instantiated\n\t *\n\t * @returns The fully built and validated class instance\n\t * @throws {Error} If nested validation fails\n\t */\n\tbuild(this: this & CeriosClassBrand<DataPropertiesOnly<T>>): T {\n\t\tconst ctor = this.getClassConstructor();\n\t\tconst instance: T = new ctor(this._actual as T);\n\t\t// If the class does not assign properties in the constructor, assign them manually\n\t\tconst dataKeys = Object.keys(this._actual) as (keyof T)[];\n\t\tconst needsAssign = dataKeys.some(key => instance[key] === undefined && this._actual[key] !== undefined);\n\t\tif (needsAssign) {\n\t\t\tObject.assign(instance, this._actual);\n\t\t}\n\t\treturn instance;\n\t}\n\n\t/**\n\t * Builds the class instance with runtime validation only (no compile-time).\n\t * Use this when building from external/dynamic data.\n\t *\n\t * @returns The built class instance\n\t * @throws {Error} If validation fails\n\t */\n\tbuildWithoutCompileTimeValidation(): T {\n\t\tconst ctor = this.getClassConstructor();\n\t\tconst instance: T = new ctor(this._actual as T);\n\t\tconst dataKeys = Object.keys(this._actual) as (keyof T)[];\n\t\tconst needsAssign = dataKeys.some(key => instance[key] === undefined && this._actual[key] !== undefined);\n\t\tif (needsAssign) {\n\t\t\tObject.assign(instance, this._actual);\n\t\t}\n\t\treturn instance;\n\t}\n\n\t/**\n\t * Builds the class instance without any validation.\n\t * Use only when you're certain the object is valid.\n\t *\n\t * @returns The built class instance (may be incomplete)\n\t */\n\tbuildUnsafe(): T {\n\t\tconst ctor = this.getClassConstructor();\n\t\tconst instance: T = new ctor(this._actual as T);\n\t\tconst dataKeys = Object.keys(this._actual) as (keyof T)[];\n\t\tconst needsAssign = dataKeys.some(key => instance[key] === undefined && this._actual[key] !== undefined);\n\t\tif (needsAssign) {\n\t\t\tObject.assign(instance, this._actual);\n\t\t}\n\t\treturn instance;\n\t}\n\n\t/**\n\t * Builds a partial object (may not have all required fields).\n\t *\n\t * @returns The partially built object\n\t */\n\tbuildPartial(): Partial<T> {\n\t\treturn { ...this._actual };\n\t}\n\n\t/**\n\t * Builds and freezes the class instance (shallow freeze).\n\t *\n\t * @returns The frozen class instance\n\t * @throws {Error} If validation fails\n\t */\n\tbuildFrozen(this: this & CeriosClassBrand<DataPropertiesOnly<T>>): Readonly<T> {\n\t\tconst ctor = this.getClassConstructor();\n\t\tconst instance: T = new ctor(this._actual as T);\n\t\t// If the class does not assign properties in the constructor, assign them manually\n\t\tconst dataKeys = Object.keys(this._actual) as (keyof T)[];\n\t\tconst needsAssign = dataKeys.some(key => instance[key] === undefined && this._actual[key] !== undefined);\n\t\tif (needsAssign) {\n\t\t\tObject.assign(instance, this._actual);\n\t\t}\n\t\treturn Object.freeze(instance);\n\t}\n\n\t/**\n\t * Builds and deeply freezes the class instance.\n\t *\n\t * @returns The deeply frozen class instance\n\t * @throws {Error} If validation fails\n\t */\n\tbuildDeepFrozen(this: this & CeriosClassBrand<DataPropertiesOnly<T>>): DeepReadonly<T> {\n\t\tconst ctor = this.getClassConstructor();\n\t\tconst instance: T = new ctor(this._actual as T);\n\t\t// If the class does not assign properties in the constructor, assign them manually\n\t\tconst dataKeys = Object.keys(this._actual) as (keyof T)[];\n\t\tconst needsAssign = dataKeys.some(key => instance[key] === undefined && this._actual[key] !== undefined);\n\t\tif (needsAssign) {\n\t\t\tObject.assign(instance, this._actual);\n\t\t}\n\t\treturn this.deepFreeze(instance) as DeepReadonly<T>;\n\t}\n\n\t/**\n\t * Builds and seals the class instance (shallow freeze).\n\t *\n\t * @returns The sealed class instance\n\t * @throws {Error} If validation fails\n\t */\n\tbuildSealed(this: this & CeriosClassBrand<DataPropertiesOnly<T>>): T {\n\t\tconst ctor = this.getClassConstructor();\n\t\tconst instance: T = new ctor(this._actual as T);\n\t\t// If the class does not assign properties in the constructor, assign them manually\n\t\tconst dataKeys = Object.keys(this._actual) as (keyof T)[];\n\t\tconst needsAssign = dataKeys.some(key => instance[key] === undefined && this._actual[key] !== undefined);\n\t\tif (needsAssign) {\n\t\t\tObject.assign(instance, this._actual);\n\t\t}\n\t\treturn Object.seal(instance);\n\t}\n\n\t/**\n\t * Builds and deeply seals the class instance.\n\t *\n\t * @returns The deeply sealed class instance\n\t * @throws {Error} If validation fails\n\t */\n\tbuildDeepSealed(this: this & CeriosClassBrand<DataPropertiesOnly<T>>): T {\n\t\tconst ctor = this.getClassConstructor();\n\t\tconst instance: T = new ctor(this._actual as T);\n\t\t// If the class does not assign properties in the constructor, assign them manually\n\t\tconst dataKeys = Object.keys(this._actual) as (keyof T)[];\n\t\tconst needsAssign = dataKeys.some(key => instance[key] === undefined && this._actual[key] !== undefined);\n\t\tif (needsAssign) {\n\t\t\tObject.assign(instance, this._actual);\n\t\t}\n\t\treturn this.deepSeal(instance);\n\t}\n\n\t/**\n\t * Deep freeze helper.\n\t * @private\n\t */\n\tprivate deepFreeze<V>(obj: V): V {\n\t\tif (obj === null || typeof obj !== \"object\") {\n\t\t\treturn obj;\n\t\t}\n\t\tObject.getOwnPropertyNames(obj).forEach(prop => {\n\t\t\tconst value = (obj as any)[prop];\n\t\t\tif (value !== null && (typeof value === \"object\" || typeof value === \"function\")) {\n\t\t\t\tthis.deepFreeze(value);\n\t\t\t}\n\t\t});\n\t\treturn Object.freeze(obj);\n\t}\n\n\t/**\n\t * Deep seal helper.\n\t * @private\n\t */\n\tprivate deepSeal<V>(obj: V): V {\n\t\tif (obj === null || typeof obj !== \"object\") {\n\t\t\treturn obj;\n\t\t}\n\t\tObject.getOwnPropertyNames(obj).forEach(prop => {\n\t\t\tconst value = (obj as any)[prop];\n\t\t\tif (value !== null && (typeof value === \"object\" || typeof value === \"function\")) {\n\t\t\t\tthis.deepSeal(value);\n\t\t\t}\n\t\t});\n\t\treturn Object.seal(obj);\n\t}\n\n\t/**\n\t * Deep clone helper for nested objects.\n\t * @private\n\t */\n\tprivate deepClone<V>(obj: V): V {\n\t\tif (obj === null || typeof obj !== \"object\") {\n\t\t\treturn obj;\n\t\t}\n\t\tif (Array.isArray(obj)) {\n\t\t\treturn obj.map(item => this.deepClone(item)) as any;\n\t\t}\n\t\tconst cloned: any = {};\n\t\tfor (const key of Object.keys(obj)) {\n\t\t\tcloned[key] = this.deepClone((obj as any)[key]);\n\t\t}\n\t\treturn cloned;\n\t}\n}\n"],"mappings":"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","names":[]} |
+1
-1
| { | ||
| "name": "@cerios/cerios-builder", | ||
| "version": "1.4.0", | ||
| "version": "1.5.0", | ||
| "author": "Ronald Veth - Cerios", | ||
@@ -5,0 +5,0 @@ "description": "A TypeScript builder pattern library providing compile-time type safety for object construction with method chaining and required field validation", |
+293
-2
@@ -604,2 +604,202 @@ # @cerios/cerios-builder | ||
| #### 3. Constructor Parameter (For Instance-Specific Requirements) | ||
| ```typescript | ||
| class OrderBuilder extends CeriosBuilder<Order> { | ||
| // Base template with common required fields | ||
| static requiredTemplate: RequiredFieldsTemplate<Order> = [ | ||
| 'Details.CustomerId', | ||
| 'Details.TotalAmount', | ||
| ]; | ||
| // Constructor accepts additional required fields | ||
| constructor( | ||
| initial: Partial<Order>, | ||
| additionalRequiredFields?: RequiredFieldsTemplate<Order> | ||
| ) { | ||
| super(initial, additionalRequiredFields); | ||
| } | ||
| static create() { | ||
| return new OrderBuilder({}); | ||
| } | ||
| // Factory method for orders with shipping requirements | ||
| static createWithShipping() { | ||
| return new OrderBuilder({}, [ | ||
| 'Details.ShippingAddress.Street', | ||
| 'Details.ShippingAddress.City', | ||
| 'Details.ShippingAddress.Country', | ||
| ]); | ||
| } | ||
| customerId(value: string) { | ||
| return this.setNestedProperty('Details.CustomerId', value); | ||
| } | ||
| totalAmount(value: number) { | ||
| return this.setNestedProperty('Details.TotalAmount', value); | ||
| } | ||
| shippingStreet(value: string) { | ||
| return this.setNestedProperty('Details.ShippingAddress.Street', value); | ||
| } | ||
| shippingCity(value: string) { | ||
| return this.setNestedProperty('Details.ShippingAddress.City', value); | ||
| } | ||
| shippingCountry(value: string) { | ||
| return this.setNestedProperty('Details.ShippingAddress.Country', value); | ||
| } | ||
| } | ||
| // Order without shipping (only validates base required fields) | ||
| const digitalOrder = OrderBuilder.create() | ||
| .customerId('CUST-001') | ||
| .totalAmount(49.99) | ||
| .buildWithoutCompileTimeValidation(); // β Valid - shipping not required | ||
| // Order with shipping (validates base + shipping fields) | ||
| const physicalOrder = OrderBuilder.createWithShipping() | ||
| .customerId('CUST-002') | ||
| .totalAmount(99.99) | ||
| .shippingStreet('123 Main St') | ||
| .shippingCity('New York') | ||
| .shippingCountry('USA') | ||
| .buildWithoutCompileTimeValidation(); // β All required fields validated | ||
| // β This will fail - missing shipping address | ||
| try { | ||
| const invalidOrder = OrderBuilder.createWithShipping() | ||
| .customerId('CUST-003') | ||
| .totalAmount(75.00) | ||
| .buildWithoutCompileTimeValidation(); // Missing shipping fields | ||
| } catch (error) { | ||
| console.error(error.message); | ||
| // "Missing required fields: Details.ShippingAddress.Street, ..." | ||
| } | ||
| ``` | ||
| #### 4. Hybrid Approach (Static + Dynamic Combined) | ||
| ```typescript | ||
| class OrderBuilder extends CeriosBuilder<Order> { | ||
| // Static template for always-required fields | ||
| static requiredTemplate: RequiredFieldsTemplate<Order> = [ | ||
| 'Details.CustomerId', | ||
| 'Details.TotalAmount', | ||
| ]; | ||
| static create() { | ||
| return new OrderBuilder({}); | ||
| } | ||
| // Factory with static template + additional dynamic fields | ||
| static createPriorityOrder() { | ||
| return this.create() | ||
| .setRequiredFields([ | ||
| 'Details.ShippingAddress.Street', | ||
| 'Details.ShippingAddress.City', | ||
| 'Details.ShippingAddress.Country', | ||
| 'Details.ShippingAddress.PostalCode', | ||
| 'Details.Priority', // Extra field for priority orders | ||
| ]); | ||
| } | ||
| // Another factory combining different requirements | ||
| static createGiftOrder() { | ||
| return this.create() | ||
| .setRequiredFields([ | ||
| 'Details.ShippingAddress.Street', | ||
| 'Details.ShippingAddress.City', | ||
| 'Details.ShippingAddress.Country', | ||
| 'Details.GiftMessage', // Required for gift orders | ||
| 'Details.RecipientName', // Required for gift orders | ||
| ]); | ||
| } | ||
| customerId(value: string) { | ||
| return this.setNestedProperty('Details.CustomerId', value); | ||
| } | ||
| totalAmount(value: number) { | ||
| return this.setNestedProperty('Details.TotalAmount', value); | ||
| } | ||
| priority(value: string) { | ||
| return this.setNestedProperty('Details.Priority', value); | ||
| } | ||
| giftMessage(value: string) { | ||
| return this.setNestedProperty('Details.GiftMessage', value); | ||
| } | ||
| recipientName(value: string) { | ||
| return this.setNestedProperty('Details.RecipientName', value); | ||
| } | ||
| shippingStreet(value: string) { | ||
| return this.setNestedProperty('Details.ShippingAddress.Street', value); | ||
| } | ||
| shippingCity(value: string) { | ||
| return this.setNestedProperty('Details.ShippingAddress.City', value); | ||
| } | ||
| shippingCountry(value: string) { | ||
| return this.setNestedProperty('Details.ShippingAddress.Country', value); | ||
| } | ||
| shippingPostalCode(value: string) { | ||
| return this.setNestedProperty('Details.ShippingAddress.PostalCode', value); | ||
| } | ||
| } | ||
| // Priority order: requires shipping + priority + base fields | ||
| const priorityOrder = OrderBuilder.createPriorityOrder() | ||
| .customerId('CUST-001') | ||
| .totalAmount(299.99) | ||
| .priority('express') | ||
| .shippingStreet('123 Main St') | ||
| .shippingCity('New York') | ||
| .shippingCountry('USA') | ||
| .shippingPostalCode('10001') | ||
| .buildWithoutCompileTimeValidation(); // β All priority order fields validated | ||
| // Gift order: requires shipping + gift fields + base fields | ||
| const giftOrder = OrderBuilder.createGiftOrder() | ||
| .customerId('CUST-002') | ||
| .totalAmount(149.99) | ||
| .giftMessage('Happy Birthday!') | ||
| .recipientName('John Doe') | ||
| .shippingStreet('456 Oak Ave') | ||
| .shippingCity('Boston') | ||
| .shippingCountry('USA') | ||
| .buildWithoutCompileTimeValidation(); // β All gift order fields validated | ||
| // β This will fail - missing gift-specific fields | ||
| try { | ||
| const invalidGiftOrder = OrderBuilder.createGiftOrder() | ||
| .customerId('CUST-003') | ||
| .totalAmount(99.99) | ||
| .shippingStreet('789 Elm St') | ||
| .shippingCity('Chicago') | ||
| .shippingCountry('USA') | ||
| .buildWithoutCompileTimeValidation(); // Missing GiftMessage and RecipientName | ||
| } catch (error) { | ||
| console.error(error.message); | ||
| // "Missing required fields: Details.GiftMessage, Details.RecipientName" | ||
| } | ||
| ``` | ||
| **Summary of the Four Approaches:** | ||
| | Approach | When to Use | Flexibility | Complexity | | ||
| |----------|-------------|-------------|------------| | ||
| | **1. Static Template** | Fixed requirements that never change | Low | Low | | ||
| | **2. Dynamic via `setRequiredFields()`** | Requirements change based on runtime conditions | High | Medium | | ||
| | **3. Constructor Parameter** | Requirements known at builder creation time | Medium | Medium | | ||
| | **4. Hybrid (Static + Dynamic)** | Common base requirements + context-specific additions | High | Higher | | ||
| ### Building Test Data | ||
@@ -773,3 +973,36 @@ | ||
| } | ||
| ``` | ||
| #### Using `BuilderType<>` for Simpler Type Signatures | ||
| When accepting builders with defaults already set, you can use the `BuilderType<>` helper to avoid manually specifying which fields are set: | ||
| ```typescript | ||
| import { BuilderType, CeriosBuilder, CeriosBrand } from '@cerios/cerios-builder'; | ||
| class CustomerBuilder extends CeriosBuilder<Customer> { | ||
| // Instead of manually picking fields: | ||
| // withAddressDefaults( | ||
| // builderFn: (builder: AddressBuilder & CeriosBrand<Pick<Address, "city" | "country">>) => ... | ||
| // ) { ... } | ||
| // Use BuilderType with ReturnType: | ||
| withAddressDefaults( | ||
| builderFn: (builder: BuilderType<ReturnType<typeof AddressBuilder.createWithDefaults>>) => AddressBuilder & CeriosBrand<Address> | ||
| ) { | ||
| const address = builderFn(AddressBuilder.createWithDefaults()).build(); | ||
| return this.setProperty('address', address); | ||
| } | ||
| } | ||
| ``` | ||
| **Benefits:** | ||
| - β No need to manually track which fields are set with `Pick<>` | ||
| - β Automatically infers the correct type from your factory method | ||
| - β Changes to `createWithDefaults()` automatically update the type | ||
| - β Much cleaner and more maintainable | ||
| **Usage examples:** | ||
| ```typescript | ||
| // Pattern 1: No defaults - must set all required fields | ||
@@ -1186,3 +1419,3 @@ const customer1 = CustomerBuilder.create() | ||
| ## οΏ½ Immutable Build Methods | ||
| ## π§ Immutable Build Methods | ||
@@ -1431,3 +1664,3 @@ In addition to the standard build methods, `@cerios/cerios-builder` provides **frozen** and **sealed** variants that create immutable objects. These methods help prevent accidental mutations and enforce data integrity. | ||
| ## οΏ½π‘ Best Practices | ||
| ## π‘ Best Practices | ||
@@ -1472,1 +1705,59 @@ 1. **Use `setNestedProperty()` for deep structures**: Instead of building nested objects separately, use dot notation for better type safety and cleaner code. | ||
| ``` | ||
| ## π§© CeriosClassBuilder (Work in Progress) | ||
| `CeriosClassBuilder` is an experimental builder base class for **TypeScript classes** (not just types/interfaces). It provides the same fluent, type-safe builder API as `CeriosBuilder`, but returns actual class instancesβpreserving methods, decorators, and prototype chains. | ||
| ### Key Features | ||
| - **Class Instance Output**: Returns real class instances, not plain objects. | ||
| - **Decorator Support**: Works with classes using decorators (e.g., for serialization). | ||
| - **Type-Safe Chaining**: Same compile-time safety and method chaining as `CeriosBuilder`. | ||
| - **Nested Property Support**: Supports `setNestedProperty` for deep class graphs. | ||
| ### Example | ||
| ```typescript | ||
| import { CeriosClassBuilder } from '@cerios/cerios-builder'; | ||
| class Person { | ||
| name!: string; | ||
| age!: number; | ||
| greet() { return `Hello, I'm ${this.name}`; } | ||
| } | ||
| class PersonBuilder extends CeriosClassBuilder<Person> { | ||
| constructor(classConstructor: ClassConstructor<Person> = Person, data: Partial<Person> = {}) { | ||
| super(classConstructor, data); | ||
| } | ||
| static create() { | ||
| return new PersonBuilder(Person); | ||
| } | ||
| name(value: string) { return this.setProperty('name', value); } | ||
| age(value: number) { return this.setProperty('age', value); } | ||
| } | ||
| const person = PersonBuilder.create().name('Alice').age(30).build(); | ||
| console.log(person.greet()); // "Hello, I'm Alice" | ||
| console.log(person instanceof Person); // true | ||
| ``` | ||
| ### Status | ||
| - β οΈ **Work in progress:** API and features may change. | ||
| - Feature parity with `CeriosBuilder` is the goal. | ||
| - Please report issues or suggestions! | ||
| ## π€ Contributing | ||
| Contributions are welcome! Please feel free to submit a Pull Request. | ||
| ## π License | ||
| MIT Β© Ronald Veth - Cerios | ||
| ## π Links | ||
| - [GitHub Repository](https://github.com/CeriosTesting/cerios-builder) | ||
| - [Issue Tracker](https://github.com/CeriosTesting/cerios-builder/issues) | ||
| - [NPM Package](https://www.npmjs.com/package/@cerios/cerios-builder) |
218265
48.28%1724
66.09%1758
19.75%