@cerios/cerios-builder
Advanced tools
+38
-20
@@ -164,34 +164,43 @@ /** | ||
| /** | ||
| * Builds the final object. This method uses TypeScript's contextual typing to ensure | ||
| * all required fields are set before allowing build() to be called. | ||
| * Builds the final object with both compile-time and runtime validation. | ||
| * This is the recommended and safest way to build objects. | ||
| * | ||
| * The type constraint checks that all required properties are present. | ||
| * - Compile-time: TypeScript enforces all required properties are set | ||
| * - Runtime: Validates all fields in the requiredTemplate | ||
| * | ||
| * @returns The fully built object of type T | ||
| * @throws {TypeError} If called without all required fields set (compile-time error) | ||
| * @throws {Error} If any required field is missing at runtime | ||
| */ | ||
| build(this: this & CeriosBrand<T>): T; | ||
| /** | ||
| * Builds the final object with runtime validation using the requiredTemplate. | ||
| * This method validates that all fields in the requiredTemplate array are present. | ||
| * Builds the final object with only compile-time validation, skipping runtime checks. | ||
| * Use this when you want TypeScript safety but need to skip runtime validation for performance. | ||
| * | ||
| * - Compile-time: TypeScript enforces all required properties are set | ||
| * - Runtime: No validation | ||
| * | ||
| * @returns The fully built object of type T | ||
| */ | ||
| buildWithoutRuntimeValidation(this: this & CeriosBrand<T>): T; | ||
| /** | ||
| * Builds the final object with only runtime validation, skipping compile-time checks. | ||
| * Use this when building from external data where compile-time checks aren't possible. | ||
| * | ||
| * - Compile-time: No TypeScript enforcement | ||
| * - Runtime: Validates all fields in the requiredTemplate | ||
| * | ||
| * @returns The fully built object of type T | ||
| * @throws {Error} If any required field is missing at runtime | ||
| */ | ||
| buildWithoutCompileTimeValidation(): T; | ||
| /** | ||
| * Builds the final object without any validation (neither compile-time nor runtime). | ||
| * Use this only when you're certain the object is valid and need maximum performance. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * class MyBuilder extends CeriosBuilder<MyType> { | ||
| * static requiredTemplate: RequiredFieldsTemplate<MyType> = [ | ||
| * 'path.to.field1', | ||
| * 'path.to.field2' | ||
| * ]; | ||
| * } | ||
| * - Compile-time: No TypeScript enforcement | ||
| * - Runtime: No validation | ||
| * | ||
| * const obj = new MyBuilder({}) | ||
| * .setRequiredField1("value1") | ||
| * .setRequiredField2("value2") | ||
| * .buildSafe(); // Validates that both required fields are present | ||
| * ``` | ||
| * @returns The object of type T (may be incomplete) | ||
| */ | ||
| buildSafe(): T; | ||
| buildUnsafe(): T; | ||
| /** | ||
@@ -204,4 +213,13 @@ * Builds a partial object, which may not have all required fields set. | ||
| buildPartial(): Partial<T>; | ||
| /** | ||
| * @deprecated Use build() instead. buildSafe() is now an alias for build(). | ||
| * Builds the final object with runtime validation using the requiredTemplate. | ||
| * This method validates that all fields in the requiredTemplate array are present. | ||
| * | ||
| * @returns The fully built object of type T | ||
| * @throws {Error} If any required field is missing at runtime | ||
| */ | ||
| buildSafe(): T; | ||
| } | ||
| export { type CeriosBrand, CeriosBuilder, type RequiredFieldsTemplate }; |
+38
-20
@@ -164,34 +164,43 @@ /** | ||
| /** | ||
| * Builds the final object. This method uses TypeScript's contextual typing to ensure | ||
| * all required fields are set before allowing build() to be called. | ||
| * Builds the final object with both compile-time and runtime validation. | ||
| * This is the recommended and safest way to build objects. | ||
| * | ||
| * The type constraint checks that all required properties are present. | ||
| * - Compile-time: TypeScript enforces all required properties are set | ||
| * - Runtime: Validates all fields in the requiredTemplate | ||
| * | ||
| * @returns The fully built object of type T | ||
| * @throws {TypeError} If called without all required fields set (compile-time error) | ||
| * @throws {Error} If any required field is missing at runtime | ||
| */ | ||
| build(this: this & CeriosBrand<T>): T; | ||
| /** | ||
| * Builds the final object with runtime validation using the requiredTemplate. | ||
| * This method validates that all fields in the requiredTemplate array are present. | ||
| * Builds the final object with only compile-time validation, skipping runtime checks. | ||
| * Use this when you want TypeScript safety but need to skip runtime validation for performance. | ||
| * | ||
| * - Compile-time: TypeScript enforces all required properties are set | ||
| * - Runtime: No validation | ||
| * | ||
| * @returns The fully built object of type T | ||
| */ | ||
| buildWithoutRuntimeValidation(this: this & CeriosBrand<T>): T; | ||
| /** | ||
| * Builds the final object with only runtime validation, skipping compile-time checks. | ||
| * Use this when building from external data where compile-time checks aren't possible. | ||
| * | ||
| * - Compile-time: No TypeScript enforcement | ||
| * - Runtime: Validates all fields in the requiredTemplate | ||
| * | ||
| * @returns The fully built object of type T | ||
| * @throws {Error} If any required field is missing at runtime | ||
| */ | ||
| buildWithoutCompileTimeValidation(): T; | ||
| /** | ||
| * Builds the final object without any validation (neither compile-time nor runtime). | ||
| * Use this only when you're certain the object is valid and need maximum performance. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * class MyBuilder extends CeriosBuilder<MyType> { | ||
| * static requiredTemplate: RequiredFieldsTemplate<MyType> = [ | ||
| * 'path.to.field1', | ||
| * 'path.to.field2' | ||
| * ]; | ||
| * } | ||
| * - Compile-time: No TypeScript enforcement | ||
| * - Runtime: No validation | ||
| * | ||
| * const obj = new MyBuilder({}) | ||
| * .setRequiredField1("value1") | ||
| * .setRequiredField2("value2") | ||
| * .buildSafe(); // Validates that both required fields are present | ||
| * ``` | ||
| * @returns The object of type T (may be incomplete) | ||
| */ | ||
| buildSafe(): T; | ||
| buildUnsafe(): T; | ||
| /** | ||
@@ -204,4 +213,13 @@ * Builds a partial object, which may not have all required fields set. | ||
| buildPartial(): Partial<T>; | ||
| /** | ||
| * @deprecated Use build() instead. buildSafe() is now an alias for build(). | ||
| * Builds the final object with runtime validation using the requiredTemplate. | ||
| * This method validates that all fields in the requiredTemplate array are present. | ||
| * | ||
| * @returns The fully built object of type T | ||
| * @throws {Error} If any required field is missing at runtime | ||
| */ | ||
| buildSafe(): T; | ||
| } | ||
| export { type CeriosBrand, CeriosBuilder, type RequiredFieldsTemplate }; |
+49
-21
@@ -219,36 +219,41 @@ "use strict"; | ||
| /** | ||
| * Builds the final object. This method uses TypeScript's contextual typing to ensure | ||
| * all required fields are set before allowing build() to be called. | ||
| * Builds the final object with both compile-time and runtime validation. | ||
| * This is the recommended and safest way to build objects. | ||
| * | ||
| * The type constraint checks that all required properties are present. | ||
| * - Compile-time: TypeScript enforces all required properties are set | ||
| * - Runtime: Validates all fields in the requiredTemplate | ||
| * | ||
| * @returns The fully built object of type T | ||
| * @throws {TypeError} If called without all required fields set (compile-time error) | ||
| * @throws {Error} If any required field is missing at runtime | ||
| */ | ||
| build() { | ||
| const missing = this.validateRequiredFields(); | ||
| if (missing.length > 0) { | ||
| throw new Error(`Missing required fields: ${missing.join(", ")}. Please set these fields before calling build.`); | ||
| } | ||
| return this._actual; | ||
| } | ||
| /** | ||
| * Builds the final object with runtime validation using the requiredTemplate. | ||
| * This method validates that all fields in the requiredTemplate array are present. | ||
| * Builds the final object with only compile-time validation, skipping runtime checks. | ||
| * Use this when you want TypeScript safety but need to skip runtime validation for performance. | ||
| * | ||
| * - Compile-time: TypeScript enforces all required properties are set | ||
| * - Runtime: No validation | ||
| * | ||
| * @returns The fully built object of type T | ||
| * @throws {Error} If any required field is missing at runtime | ||
| */ | ||
| buildWithoutRuntimeValidation() { | ||
| return this._actual; | ||
| } | ||
| /** | ||
| * Builds the final object with only runtime validation, skipping compile-time checks. | ||
| * Use this when building from external data where compile-time checks aren't possible. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * class MyBuilder extends CeriosBuilder<MyType> { | ||
| * static requiredTemplate: RequiredFieldsTemplate<MyType> = [ | ||
| * 'path.to.field1', | ||
| * 'path.to.field2' | ||
| * ]; | ||
| * } | ||
| * - Compile-time: No TypeScript enforcement | ||
| * - Runtime: Validates all fields in the requiredTemplate | ||
| * | ||
| * const obj = new MyBuilder({}) | ||
| * .setRequiredField1("value1") | ||
| * .setRequiredField2("value2") | ||
| * .buildSafe(); // Validates that both required fields are present | ||
| * ``` | ||
| * @returns The fully built object of type T | ||
| * @throws {Error} If any required field is missing at runtime | ||
| */ | ||
| buildSafe() { | ||
| buildWithoutCompileTimeValidation() { | ||
| const missing = this.validateRequiredFields(); | ||
@@ -261,2 +266,14 @@ if (missing.length > 0) { | ||
| /** | ||
| * Builds the final object without any validation (neither compile-time nor runtime). | ||
| * Use this only when you're certain the object is valid and need maximum performance. | ||
| * | ||
| * - Compile-time: No TypeScript enforcement | ||
| * - Runtime: No validation | ||
| * | ||
| * @returns The object of type T (may be incomplete) | ||
| */ | ||
| buildUnsafe() { | ||
| return this._actual; | ||
| } | ||
| /** | ||
| * Builds a partial object, which may not have all required fields set. | ||
@@ -270,2 +287,13 @@ * This is useful for scenarios where you want to inspect or validate the current state before finalizing. | ||
| } | ||
| /** | ||
| * @deprecated Use build() instead. buildSafe() is now an alias for build(). | ||
| * Builds the final object with runtime validation using the requiredTemplate. | ||
| * This method validates that all fields in the requiredTemplate array are present. | ||
| * | ||
| * @returns The fully built object of type T | ||
| * @throws {Error} If any required field is missing at runtime | ||
| */ | ||
| buildSafe() { | ||
| return this.buildWithoutCompileTimeValidation(); | ||
| } | ||
| }; | ||
@@ -272,0 +300,0 @@ // Annotate the CommonJS export names for ESM import in node: |
@@ -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 * 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 * 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. This method uses TypeScript's contextual typing to ensure\n\t * all required fields are set before allowing build() to be called.\n\t *\n\t * The type constraint checks that all required properties are present.\n\t *\n\t * @returns The fully built object of type T\n\t * @throws {TypeError} If called without all required fields set (compile-time error)\n\t */\n\tbuild(this: this & CeriosBrand<T>): T {\n\t\treturn this._actual as T;\n\t}\n\n\t/**\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\t * @example\n\t * ```typescript\n\t * class MyBuilder extends CeriosBuilder<MyType> {\n\t * static requiredTemplate: RequiredFieldsTemplate<MyType> = [\n\t * 'path.to.field1',\n\t * 'path.to.field2'\n\t * ];\n\t * }\n\t *\n\t * const obj = new MyBuilder({})\n\t * .setRequiredField1(\"value1\")\n\t * .setRequiredField2(\"value2\")\n\t * .buildSafe(); // Validates that both required fields are present\n\t * ```\n\t */\n\tbuildSafe(): 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 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"],"mappings":"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","names":[]} | ||
| {"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 * 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 * 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"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACmFO,IAAe,gBAAf,MAA+C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAwF3C,YACU,SACnB,iBACC;AAFkB;AA5EpB;AAAA;AAAA;AAAA;AAAA;AAAA,SAAQ,kBAA+B,oBAAI,IAAI;AA+E9C,QAAI,iBAAiB;AACpB,WAAK,kBAAkB,oBAAI,IAAI,CAAC,GAAG,eAAe,CAAa;AAAA,IAChE;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAhEA,kBAAkB,QAAsC;AACvD,SAAK,kBAAkB,oBAAI,IAAI,CAAC,GAAG,MAAM,CAAa;AACtD,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,sBAA6C;AACpD,UAAM,OAAO,KAAK;AAClB,UAAM,eAAe,KAAK,oBAAoB,CAAC;AAC/C,UAAM,iBAAiB,MAAM,KAAK,KAAK,eAAe;AAGtD,WAAO,CAAC,GAAG,oBAAI,IAAI,CAAC,GAAG,cAAc,GAAG,cAAc,CAAC,CAAC;AAAA,EACzD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,yBAAmC;AAC1C,UAAM,gBAAgB,KAAK,oBAAoB;AAC/C,UAAM,UAAoB,CAAC;AAE3B,eAAW,QAAQ,eAAe;AACjC,YAAM,OAAO,KAAK,MAAM,GAAG;AAC3B,UAAI,UAAe,KAAK;AAExB,eAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACrC,cAAM,MAAM,KAAK,CAAC;AAClB,YAAI,YAAY,QAAQ,YAAY,UAAa,EAAE,OAAO,UAAU;AACnE,kBAAQ,KAAK,IAAI;AACjB;AAAA,QACD;AACA,kBAAU,QAAQ,GAAG;AAAA,MACtB;AAGA,UAAI,YAAY,QAAQ,YAAY,QAAW;AAC9C,YAAI,CAAC,QAAQ,SAAS,IAAI,GAAG;AAC5B,kBAAQ,KAAK,IAAI;AAAA,QAClB;AAAA,MACD;AAAA,IACD;AAEA,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA4BU,YAA+B,KAAQ,OAA6C;AAC7F,UAAM,eAAe,KAAK;AAC1B,WAAO,IAAI;AAAA,MACV;AAAA,QACC,GAAG,KAAK;AAAA,QACR,CAAC,GAAG,GAAG;AAAA,MACR;AAAA,MACA,MAAM,KAAK,KAAK,eAAe;AAAA,IAChC;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQU,cAAiC,OAAmD;AAC7F,UAAM,eAAe,KAAK;AAC1B,WAAO,IAAI;AAAA,MACV;AAAA,QACC,GAAG,KAAK;AAAA,QACR,GAAG;AAAA,MACJ;AAAA,MACA,MAAM,KAAK,KAAK,eAAe;AAAA,IAChC;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBU,kBACT,MACA,OACuG;AACvG,UAAM,eAAe,KAAK;AAC1B,UAAM,OAAQ,KAAgB,MAAM,GAAG;AACvC,UAAM,YAAY,KAAK,UAAU,KAAK,OAAO;AAE7C,QAAI,UAAe;AACnB,aAAS,IAAI,GAAG,IAAI,KAAK,SAAS,GAAG,KAAK;AACzC,YAAM,MAAM,KAAK,CAAC;AAClB,UAAI,EAAE,OAAO,YAAY,OAAO,QAAQ,GAAG,MAAM,YAAY,QAAQ,GAAG,MAAM,MAAM;AACnF,gBAAQ,GAAG,IAAI,CAAC;AAAA,MACjB,OAAO;AACN,gBAAQ,GAAG,IAAI,KAAK,UAAU,QAAQ,GAAG,CAAC;AAAA,MAC3C;AACA,gBAAU,QAAQ,GAAG;AAAA,IACtB;AAEA,YAAQ,KAAK,KAAK,SAAS,CAAC,CAAC,IAAI;AAEjC,WAAO,IAAI;AAAA,MACV;AAAA,MACA,MAAM,KAAK,KAAK,eAAe;AAAA,IAChC;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,UAAa,KAAW;AAC/B,QAAI,QAAQ,QAAQ,OAAO,QAAQ,UAAU;AAC5C,aAAO;AAAA,IACR;AACA,QAAI,MAAM,QAAQ,GAAG,GAAG;AACvB,aAAO,IAAI,IAAI,UAAQ,KAAK,UAAU,IAAI,CAAC;AAAA,IAC5C;AACA,UAAM,SAAc,CAAC;AACrB,UAAM,SAAS,OAAO,UAAU;AAChC,eAAW,OAAO,KAAK;AACtB,UAAI,OAAO,KAAK,KAAK,GAAG,GAAG;AAC1B,eAAO,GAAG,IAAI,KAAK,UAAU,IAAI,GAAG,CAAC;AAAA,MACtC;AAAA,IACD;AACA,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaU,mBAGR,KAAQ,OAA0C;AAvSrD;AAwSE,UAAM,eAAe,KAAK;AAC1B,UAAM,gBAAgB,UAAK,QAAQ,GAAG,MAAhB,YAA8C,CAAC;AACrE,WAAO,IAAI;AAAA,MACV;AAAA,QACC,GAAG,KAAK;AAAA,QACR,CAAC,GAAG,GAAG,CAAC,GAAG,cAAc,KAAK;AAAA,MAC/B;AAAA,MACA,MAAM,KAAK,KAAK,eAAe;AAAA,IAChC;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,QAAsC;AACrC,UAAM,UAAU,KAAK,uBAAuB;AAE5C,QAAI,QAAQ,SAAS,GAAG;AACvB,YAAM,IAAI,MAAM,4BAA4B,QAAQ,KAAK,IAAI,CAAC,iDAAiD;AAAA,IAChH;AAEA,WAAO,KAAK;AAAA,EACb;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,gCAA8D;AAC7D,WAAO,KAAK;AAAA,EACb;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,oCAAuC;AACtC,UAAM,UAAU,KAAK,uBAAuB;AAE5C,QAAI,QAAQ,SAAS,GAAG;AACvB,YAAM,IAAI,MAAM,4BAA4B,QAAQ,KAAK,IAAI,CAAC,iDAAiD;AAAA,IAChH;AAEA,WAAO,KAAK;AAAA,EACb;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,cAAiB;AAChB,WAAO,KAAK;AAAA,EACb;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,eAA2B;AAC1B,WAAO,KAAK;AAAA,EACb;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,YAAe;AACd,WAAO,KAAK,kCAAkC;AAAA,EAC/C;AACD;","names":[]} |
+49
-21
@@ -193,36 +193,41 @@ // src/cerios-builder.ts | ||
| /** | ||
| * Builds the final object. This method uses TypeScript's contextual typing to ensure | ||
| * all required fields are set before allowing build() to be called. | ||
| * Builds the final object with both compile-time and runtime validation. | ||
| * This is the recommended and safest way to build objects. | ||
| * | ||
| * The type constraint checks that all required properties are present. | ||
| * - Compile-time: TypeScript enforces all required properties are set | ||
| * - Runtime: Validates all fields in the requiredTemplate | ||
| * | ||
| * @returns The fully built object of type T | ||
| * @throws {TypeError} If called without all required fields set (compile-time error) | ||
| * @throws {Error} If any required field is missing at runtime | ||
| */ | ||
| build() { | ||
| const missing = this.validateRequiredFields(); | ||
| if (missing.length > 0) { | ||
| throw new Error(`Missing required fields: ${missing.join(", ")}. Please set these fields before calling build.`); | ||
| } | ||
| return this._actual; | ||
| } | ||
| /** | ||
| * Builds the final object with runtime validation using the requiredTemplate. | ||
| * This method validates that all fields in the requiredTemplate array are present. | ||
| * Builds the final object with only compile-time validation, skipping runtime checks. | ||
| * Use this when you want TypeScript safety but need to skip runtime validation for performance. | ||
| * | ||
| * - Compile-time: TypeScript enforces all required properties are set | ||
| * - Runtime: No validation | ||
| * | ||
| * @returns The fully built object of type T | ||
| * @throws {Error} If any required field is missing at runtime | ||
| */ | ||
| buildWithoutRuntimeValidation() { | ||
| return this._actual; | ||
| } | ||
| /** | ||
| * Builds the final object with only runtime validation, skipping compile-time checks. | ||
| * Use this when building from external data where compile-time checks aren't possible. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * class MyBuilder extends CeriosBuilder<MyType> { | ||
| * static requiredTemplate: RequiredFieldsTemplate<MyType> = [ | ||
| * 'path.to.field1', | ||
| * 'path.to.field2' | ||
| * ]; | ||
| * } | ||
| * - Compile-time: No TypeScript enforcement | ||
| * - Runtime: Validates all fields in the requiredTemplate | ||
| * | ||
| * const obj = new MyBuilder({}) | ||
| * .setRequiredField1("value1") | ||
| * .setRequiredField2("value2") | ||
| * .buildSafe(); // Validates that both required fields are present | ||
| * ``` | ||
| * @returns The fully built object of type T | ||
| * @throws {Error} If any required field is missing at runtime | ||
| */ | ||
| buildSafe() { | ||
| buildWithoutCompileTimeValidation() { | ||
| const missing = this.validateRequiredFields(); | ||
@@ -235,2 +240,14 @@ if (missing.length > 0) { | ||
| /** | ||
| * Builds the final object without any validation (neither compile-time nor runtime). | ||
| * Use this only when you're certain the object is valid and need maximum performance. | ||
| * | ||
| * - Compile-time: No TypeScript enforcement | ||
| * - Runtime: No validation | ||
| * | ||
| * @returns The object of type T (may be incomplete) | ||
| */ | ||
| buildUnsafe() { | ||
| return this._actual; | ||
| } | ||
| /** | ||
| * Builds a partial object, which may not have all required fields set. | ||
@@ -244,2 +261,13 @@ * This is useful for scenarios where you want to inspect or validate the current state before finalizing. | ||
| } | ||
| /** | ||
| * @deprecated Use build() instead. buildSafe() is now an alias for build(). | ||
| * Builds the final object with runtime validation using the requiredTemplate. | ||
| * This method validates that all fields in the requiredTemplate array are present. | ||
| * | ||
| * @returns The fully built object of type T | ||
| * @throws {Error} If any required field is missing at runtime | ||
| */ | ||
| buildSafe() { | ||
| return this.buildWithoutCompileTimeValidation(); | ||
| } | ||
| }; | ||
@@ -246,0 +274,0 @@ export { |
@@ -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 * 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 * 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. This method uses TypeScript's contextual typing to ensure\n\t * all required fields are set before allowing build() to be called.\n\t *\n\t * The type constraint checks that all required properties are present.\n\t *\n\t * @returns The fully built object of type T\n\t * @throws {TypeError} If called without all required fields set (compile-time error)\n\t */\n\tbuild(this: this & CeriosBrand<T>): T {\n\t\treturn this._actual as T;\n\t}\n\n\t/**\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\t * @example\n\t * ```typescript\n\t * class MyBuilder extends CeriosBuilder<MyType> {\n\t * static requiredTemplate: RequiredFieldsTemplate<MyType> = [\n\t * 'path.to.field1',\n\t * 'path.to.field2'\n\t * ];\n\t * }\n\t *\n\t * const obj = new MyBuilder({})\n\t * .setRequiredField1(\"value1\")\n\t * .setRequiredField2(\"value2\")\n\t * .buildSafe(); // Validates that both required fields are present\n\t * ```\n\t */\n\tbuildSafe(): 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 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"],"mappings":"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","names":[]} | ||
| {"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 * 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 * 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"],"mappings":"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","names":[]} |
+1
-1
| { | ||
| "name": "@cerios/cerios-builder", | ||
| "version": "1.2.1", | ||
| "version": "1.3.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", |
+257
-310
@@ -9,7 +9,7 @@ # @cerios/cerios-builder | ||
| - **Nested Properties Support**: Build deeply nested objects with dot-notation paths | ||
| - **Runtime Validation**: Optional runtime validation with `buildSafe()` and required field templates | ||
| - **Flexible Runtime Validation**: Choose between compile-time, runtime, or both validations | ||
| - **Dynamic Required Fields**: Add required fields at runtime based on your business logic | ||
| - **Method Chaining**: Fluent API for readable object construction | ||
| - **Partial Building**: Build incomplete objects when needed | ||
| - **Zero Runtime Overhead**: All type checking happens at compile time (unless using `buildSafe()`) | ||
| - **Zero Runtime Overhead**: All type checking happens at compile time (unless using runtime validation) | ||
| - **Extensible**: Easy to create custom builder methods | ||
@@ -85,4 +85,6 @@ - **TypeScript First**: Built with TypeScript, for TypeScript | ||
| | **Dynamic Requirements** | `setRequiredFields()` | Add required fields at runtime | | ||
| | **Compile-Time Build** | `build()` | Build with TypeScript type checking | | ||
| | **Runtime Validation** | `buildSafe()` | Build with runtime validation | | ||
| | **Full Validation** | `build()` | Build with both compile-time and runtime validation | | ||
| | **Compile-Time Only** | `buildWithoutRuntimeValidation()` | Build with TypeScript checking only | | ||
| | **Runtime Only** | `buildWithoutCompileTimeValidation()` | Build with runtime validation only | | ||
| | **No Validation** | `buildUnsafe()` | Build without any validation | | ||
| | **Partial Build** | `buildPartial()` | Build incomplete objects | | ||
@@ -128,3 +130,2 @@ | ||
| // New: Add to array property | ||
| addRole(role: string) { | ||
@@ -154,3 +155,3 @@ return this.addToArrayProperty('roles', role); | ||
| .age(30) | ||
| .addRole("admin") // Add to array property | ||
| .addRole("admin") | ||
| .addRole("editor") | ||
@@ -213,3 +214,3 @@ .build(); | ||
| type Order = { | ||
| Details: OrderDetails; | ||
| Details?: OrderDetails; | ||
| }; | ||
@@ -222,11 +223,13 @@ | ||
| class OrderRequestBuilder extends CeriosBuilder<OrderRequest> { | ||
| static requiredTemplate: RequiredFieldsTemplate<OrderRequest> = [ | ||
| 'Order.Details.CustomerId', | ||
| 'Order.Details.TotalAmount', | ||
| 'Order.Details.Status', | ||
| 'Order.Details.ShippingAddress.Street', | ||
| 'Order.Details.ShippingAddress.City', | ||
| 'Order.Details.ShippingAddress.Country', | ||
| ]; | ||
| static create() { | ||
| return new OrderRequestBuilder({}, [ | ||
| 'Order.Details.CustomerId', | ||
| 'Order.Details.TotalAmount', | ||
| 'Order.Details.Status', | ||
| 'Order.Details.ShippingAddress.Street', | ||
| 'Order.Details.ShippingAddress.City', | ||
| 'Order.Details.ShippingAddress.Country', | ||
| ]); | ||
| return new OrderRequestBuilder({}); | ||
| } | ||
@@ -271,3 +274,3 @@ | ||
| // Usage with runtime validation | ||
| // Usage with full validation (both compile-time and runtime) | ||
| const order = OrderRequestBuilder.createWithDefaults() | ||
@@ -280,3 +283,3 @@ .customerId('CUST-001') | ||
| .shippingCountry('USA') | ||
| .buildSafe(); // โ Validates all required fields are set | ||
| .build(); // โ TypeScript + runtime validation | ||
@@ -287,3 +290,3 @@ // โ This will throw an error at runtime | ||
| .customerId('CUST-002') | ||
| .buildSafe(); // Missing TotalAmount and shipping address fields | ||
| .buildWithoutCompileTimeValidation(); // Missing TotalAmount and shipping address | ||
| } catch (error) { | ||
@@ -300,9 +303,18 @@ console.error(error.message); | ||
| ```typescript | ||
| type Product = { | ||
| id: string; | ||
| name: string; | ||
| price: number; | ||
| description?: string; | ||
| }; | ||
| class ProductBuilder extends CeriosBuilder<Product> { | ||
| static requiredTemplate: RequiredFieldsTemplate<Product> = [ | ||
| 'id', | ||
| 'name', | ||
| 'price', | ||
| ]; | ||
| static create() { | ||
| return new ProductBuilder({}, [ | ||
| 'id', | ||
| 'name', | ||
| 'price', | ||
| ]); | ||
| return new ProductBuilder({}); | ||
| } | ||
@@ -327,3 +339,3 @@ | ||
| // Use buildSafe() for runtime validation | ||
| // Full validation (compile-time + runtime) | ||
| const product = ProductBuilder.create() | ||
@@ -333,9 +345,21 @@ .id('PROD-001') | ||
| .price(999.99) | ||
| .buildSafe(); // โ All required fields are set | ||
| .build(); // โ All required fields are set | ||
| // This will throw an error | ||
| const invalidProduct = ProductBuilder.create() | ||
| // Runtime-only validation (useful for dynamic data) | ||
| const dynamicProduct = ProductBuilder.create() | ||
| .id('PROD-002') | ||
| .name('Mouse') | ||
| .buildSafe(); // โ Error: Missing required fields: price | ||
| .price(29.99) | ||
| .buildWithoutCompileTimeValidation(); // โ Runtime check passes | ||
| // โ This will throw an error at runtime | ||
| try { | ||
| const invalidProduct = ProductBuilder.create() | ||
| .id('PROD-003') | ||
| .name('Keyboard') | ||
| .buildWithoutCompileTimeValidation(); // Missing price | ||
| } catch (error) { | ||
| console.error(error.message); | ||
| // "Missing required fields: price" | ||
| } | ||
| ``` | ||
@@ -348,9 +372,19 @@ | ||
| ```typescript | ||
| type Employee = { | ||
| firstName: string; | ||
| lastName: string; | ||
| email: string; | ||
| employeeId?: string; | ||
| phone?: string; | ||
| }; | ||
| class EmployeeBuilder extends CeriosBuilder<Employee> { | ||
| static requiredTemplate: RequiredFieldsTemplate<Employee> = [ | ||
| 'firstName', | ||
| 'lastName', | ||
| 'email', | ||
| ]; | ||
| static create() { | ||
| return new EmployeeBuilder({}, [ | ||
| 'firstName', | ||
| 'lastName', | ||
| 'email', | ||
| ]); | ||
| return new EmployeeBuilder({}); | ||
| } | ||
@@ -379,3 +413,3 @@ | ||
| // Scenario 1: New employee (no ID required) | ||
| // Scenario 1: New employee (only basic fields required) | ||
| const newEmployee = EmployeeBuilder.create() | ||
@@ -385,3 +419,3 @@ .firstName('John') | ||
| .email('john.doe@company.com') | ||
| .buildSafe(); // โ Only validates firstName, lastName, email | ||
| .build(); // โ Only validates firstName, lastName, email | ||
@@ -394,4 +428,4 @@ // Scenario 2: Existing employee (ID required) | ||
| .email('jane.smith@company.com') | ||
| .employeeId('EMP-12345') // Must provide employeeId | ||
| .buildSafe(); // โ Validates firstName, lastName, email, AND employeeId | ||
| .employeeId('EMP-12345') | ||
| .buildWithoutCompileTimeValidation(); // โ Runtime validates all fields including employeeId | ||
@@ -406,3 +440,16 @@ // Scenario 3: Employee with contact requirement | ||
| .employeeId('EMP-67890') | ||
| .buildSafe(); // โ Validates all fields including phone and employeeId | ||
| .buildWithoutCompileTimeValidation(); // โ Validates all including phone and employeeId | ||
| // โ This will fail - missing dynamically required field | ||
| try { | ||
| const invalidEmployee = EmployeeBuilder.create() | ||
| .setRequiredFields(['employeeId']) | ||
| .firstName('Alice') | ||
| .lastName('Brown') | ||
| .email('alice.brown@company.com') | ||
| .buildWithoutCompileTimeValidation(); // Missing employeeId | ||
| } catch (error) { | ||
| console.error(error.message); | ||
| // "Missing required fields: employeeId" | ||
| } | ||
| ``` | ||
@@ -412,8 +459,6 @@ | ||
| When working with deeply nested structures, you have **four approaches** to configure required field validation: | ||
| When working with deeply nested structures, you have **multiple approaches** to configure required field validation: | ||
| #### 1. Static Template in Constructor (Recommended for Fixed Requirements) | ||
| #### 1. Static Template at Class Level (Recommended for Fixed Requirements) | ||
| Define required fields once at the class level and pass them through the constructor. Best for when required fields never change. | ||
| ```typescript | ||
@@ -447,3 +492,2 @@ type Address = { | ||
| static create() { | ||
| // Pass template through constructor | ||
| return new OrderBuilder({}); | ||
@@ -473,3 +517,3 @@ } | ||
| // Usage: All required fields validated by template | ||
| // Usage: Runtime validation checks all required fields | ||
| const order = OrderBuilder.create() | ||
@@ -481,90 +525,9 @@ .customerId('CUST-001') | ||
| .shippingCountry('USA') | ||
| .buildSafe(); // โ Validates all 5 required nested fields | ||
| .buildWithoutCompileTimeValidation(); // โ Validates all 5 required nested fields | ||
| ``` | ||
| #### 2. Inline Constructor Template (Simple and Direct) | ||
| #### 2. Dynamic Required Fields via `setRequiredFields()` (Best for Conditional Logic) | ||
| Pass the required template directly in the constructor without defining a static property. Best for simple cases or when you want to keep everything in one place. | ||
| ```typescript | ||
| type Address = { | ||
| Street: string; | ||
| City: string; | ||
| Country: string; | ||
| PostalCode?: string; | ||
| }; | ||
| type OrderDetails = { | ||
| CustomerId: string; | ||
| TotalAmount: number; | ||
| ShippingAddress: Address; | ||
| }; | ||
| type Order = { | ||
| Details: OrderDetails; | ||
| }; | ||
| class OrderBuilder extends CeriosBuilder<Order> { | ||
| // Method 2: Pass template directly in constructor (no static property needed) | ||
| static create() { | ||
| return new OrderBuilder({}, [ | ||
| 'Details.CustomerId', | ||
| 'Details.TotalAmount', | ||
| '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); | ||
| } | ||
| shippingPostalCode(value: string) { | ||
| return this.setNestedProperty('Details.ShippingAddress.PostalCode', value); | ||
| } | ||
| } | ||
| // Usage: Same validation as Method 1, but simpler setup | ||
| const order = OrderBuilder.create() | ||
| .customerId('CUST-001') | ||
| .totalAmount(299.99) | ||
| .shippingStreet('123 Main St') | ||
| .shippingCity('New York') | ||
| .shippingCountry('USA') | ||
| .buildSafe(); // โ Validates all required nested fields | ||
| // โ Still validates properly | ||
| try { | ||
| const invalidOrder = OrderBuilder.create() | ||
| .customerId('CUST-002') | ||
| .buildSafe(); | ||
| } catch (error) { | ||
| console.error(error.message); | ||
| // "Missing required fields: Details.TotalAmount, Details.ShippingAddress.Street, ..." | ||
| } | ||
| ``` | ||
| #### 3. Dynamic Required Fields via `setRequiredFields()` (Best for Conditional Logic) | ||
| Add required fields at runtime based on business logic. Combines static template with dynamic additions. | ||
| ```typescript | ||
| class OrderBuilder extends CeriosBuilder<Order> { | ||
| static requiredTemplate: RequiredFieldsTemplate<Order> = [ | ||
@@ -579,3 +542,2 @@ 'Details.CustomerId', | ||
| // Method 3: Add required fields dynamically for specific scenarios | ||
| static createInternational() { | ||
@@ -632,3 +594,3 @@ return this.create() | ||
| .shippingCountry('USA') | ||
| .buildSafe(); // โ Valid without postal code | ||
| .buildWithoutCompileTimeValidation(); // โ Valid without postal code | ||
@@ -642,4 +604,4 @@ // International order: postal code is required | ||
| .shippingCountry('UK') | ||
| .shippingPostalCode('SW1A 2AA') // Must provide postal code | ||
| .buildSafe(); // โ All fields including postal code validated | ||
| .shippingPostalCode('SW1A 2AA') | ||
| .buildWithoutCompileTimeValidation(); // โ All fields including postal code validated | ||
@@ -654,3 +616,3 @@ // โ This will fail - missing postal code for international | ||
| .shippingCountry('France') | ||
| .buildSafe(); // Missing PostalCode | ||
| .buildWithoutCompileTimeValidation(); // Missing PostalCode | ||
| } catch (error) { | ||
@@ -662,120 +624,2 @@ console.error(error.message); | ||
| #### 4. Inline Dynamic Fields (Best for One-Off Requirements) | ||
| Add required fields inline during the building process. Useful for special cases. | ||
| ```typescript | ||
| class OrderBuilder extends CeriosBuilder<Order> { | ||
| static create() { | ||
| return new OrderBuilder({}, ['Details.CustomerId']); | ||
| } | ||
| 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); | ||
| } | ||
| } | ||
| // Method 4: Inline requirement based on runtime data | ||
| function createOrder(isPaidOrder: boolean, requiresShipping: boolean) { | ||
| let builder = OrderBuilder.create() | ||
| .customerId('CUST-001'); | ||
| // Add requirements inline based on conditions | ||
| if (isPaidOrder) { | ||
| builder = builder.setRequiredFields(['Details.TotalAmount']); | ||
| } | ||
| if (requiresShipping) { | ||
| builder = builder.setRequiredFields([ | ||
| 'Details.ShippingAddress.Street', | ||
| 'Details.ShippingAddress.City', | ||
| 'Details.ShippingAddress.Country', | ||
| ]); | ||
| } | ||
| return builder; | ||
| } | ||
| // Free order without shipping (digital product) | ||
| const digitalOrder = createOrder(false, false) | ||
| .buildSafe(); // โ Only CustomerId required | ||
| // Paid order without shipping (in-store pickup) | ||
| const pickupOrder = createOrder(true, false) | ||
| .totalAmount(49.99) | ||
| .buildSafe(); // โ CustomerId + TotalAmount required | ||
| // Paid order with shipping | ||
| const shippedOrder = createOrder(true, true) | ||
| .totalAmount(99.99) | ||
| .shippingStreet('456 Oak Ave') | ||
| .shippingCity('Boston') | ||
| .shippingCountry('USA') | ||
| .buildSafe(); // โ All fields validated | ||
| ``` | ||
| #### Choosing the Right Approach | ||
| | Approach | When to Use | Pros | Cons | | ||
| |----------|-------------|------|------| | ||
| | **Static Template** | Requirements never change, need reusability | Simple, clear, type-safe, reusable | Inflexible, extra line of code | | ||
| | **Inline Constructor** | Simple cases, one factory method | Minimal code, clear, type-safe | Template not reusable elsewhere | | ||
| | **Dynamic via Factory** | Multiple predefined scenarios | Reusable, organized, type-safe | More boilerplate | | ||
| | **Inline Dynamic** | Runtime-dependent logic | Maximum flexibility | Less discoverable | | ||
| **Best Practice**: Combine approaches! Use a static template for core required fields, then add dynamic requirements for conditional scenarios: | ||
| ```typescript | ||
| class OrderBuilder extends CeriosBuilder<Order> { | ||
| // Core fields always required | ||
| static requiredTemplate: RequiredFieldsTemplate<Order> = [ | ||
| 'Details.CustomerId', | ||
| ]; | ||
| static create() { | ||
| return new OrderBuilder({}); | ||
| } | ||
| // Scenario-specific factory methods | ||
| static createPaid() { | ||
| return this.create().setRequiredFields(['Details.TotalAmount']); | ||
| } | ||
| static createShippable() { | ||
| return this.createPaid().setRequiredFields([ | ||
| 'Details.ShippingAddress.Street', | ||
| 'Details.ShippingAddress.City', | ||
| 'Details.ShippingAddress.Country', | ||
| ]); | ||
| } | ||
| // ... builder methods | ||
| } | ||
| // Usage is clear and type-safe | ||
| const order = OrderBuilder.createShippable() | ||
| .customerId('CUST-001') | ||
| .totalAmount(199.99) | ||
| .shippingStreet('789 Maple Dr') | ||
| .shippingCity('Seattle') | ||
| .shippingCountry('USA') | ||
| .buildSafe(); // โ All required fields validated | ||
| ``` | ||
| ### Building Test Data | ||
@@ -786,2 +630,9 @@ | ||
| ```typescript | ||
| type Product = { | ||
| name: string; | ||
| price: number; | ||
| category: string; | ||
| tags?: string[]; | ||
| }; | ||
| class ProductBuilder extends CeriosBuilder<Product> { | ||
@@ -804,3 +655,2 @@ static create() { | ||
| // Add to array property | ||
| addTag(tag: string) { | ||
@@ -880,3 +730,2 @@ return this.addToArrayProperty('tags', tag); | ||
| // Preset addresses | ||
| asUSAddress() { | ||
@@ -908,3 +757,2 @@ return this.country('United States'); | ||
| // Add to array property | ||
| addNote(note: string) { | ||
@@ -914,3 +762,2 @@ return this.addToArrayProperty('notes', note); | ||
| // Build address inline | ||
| withAddress(builderFn: (builder: AddressBuilder) => AddressBuilder & CeriosBrand<Address>) { | ||
@@ -921,3 +768,2 @@ const address = builderFn(AddressBuilder.create()).build(); | ||
| // Build address with defaults (pre-sets city and country) | ||
| withAddressDefaults( | ||
@@ -948,7 +794,7 @@ builderFn: ( | ||
| // Usage with defaults (only set street, city/country are pre-filled) | ||
| // Usage with defaults | ||
| const customerWithDefaults = CustomerBuilder.create() | ||
| .id('CUST-002') | ||
| .name('Bob Smith') | ||
| .withAddressDefaults(addr => addr.street('456 Elm St')) // city and country are already set to defaults | ||
| .withAddressDefaults(addr => addr.street('456 Elm St')) | ||
| .addNote("New customer") | ||
@@ -1049,9 +895,9 @@ .build(); | ||
| .shippingCountry('USA') | ||
| .buildSafe(); // โ Runtime validation ensures all required fields are set | ||
| .build(); // โ Both compile-time and runtime validation | ||
| // โ Clear error messages | ||
| // โ Clear error messages at runtime | ||
| try { | ||
| const invalid = OrderRequestBuilder.createWithDefaults() | ||
| .customerId('CUST-002') | ||
| .buildSafe(); | ||
| .buildWithoutCompileTimeValidation(); | ||
| } catch (error) { | ||
@@ -1067,4 +913,4 @@ console.error(error.message); | ||
| - **Type-Safe Paths**: Dot notation with full IntelliSense support | ||
| - **Runtime Validation**: Optional runtime checks with clear error messages | ||
| - **Flexible Requirements**: Static templates + dynamic required fields | ||
| - **Flexible Validation**: Choose compile-time, runtime, both, or neither | ||
| - **Dynamic Requirements**: Static templates + dynamic required fields | ||
| - **Deep Nesting**: Handle complex nested structures easily | ||
@@ -1090,6 +936,31 @@ - **Developer Experience**: Better autocomplete and error messages | ||
| - `setRequiredFields(fields: ReadonlyArray<Path<T>>)` - Sets required fields dynamically for this instance | ||
| - `build()` - Builds the final object (only available when all required properties are set via type system) | ||
| - `buildSafe()` - Builds the final object with runtime validation of required fields from template | ||
| - `buildPartial()` - Builds a partial object with currently set properties | ||
| #### Build Methods | ||
| - **`build()`** - Builds with **both compile-time and runtime validation** (recommended) | ||
| - Compile-time: TypeScript enforces all required properties are set | ||
| - Runtime: Validates all fields in the `requiredTemplate` | ||
| - Throws error if any required field is missing | ||
| - **`buildWithoutRuntimeValidation()`** - Builds with **compile-time validation only** | ||
| - Compile-time: TypeScript enforces all required properties are set | ||
| - Runtime: No validation (better performance) | ||
| - Use when you trust the type system and need speed | ||
| - **`buildWithoutCompileTimeValidation()`** - Builds with **runtime validation only** | ||
| - Compile-time: No TypeScript enforcement | ||
| - Runtime: Validates all fields in the `requiredTemplate` | ||
| - Use when building from external data where compile-time checks aren't possible | ||
| - Throws error if any required field is missing | ||
| - **`buildUnsafe()`** - Builds **without any validation** | ||
| - Compile-time: No TypeScript enforcement | ||
| - Runtime: No validation | ||
| - Use only when you're certain the object is valid and need maximum performance | ||
| - Returns potentially incomplete object | ||
| - **`buildPartial()`** - Builds a **partial object** | ||
| - Returns `Partial<T>` with currently set properties | ||
| - Useful for progressive form filling or template objects | ||
| ### RequiredFieldsTemplate<T> | ||
@@ -1133,14 +1004,13 @@ | ||
| ## ๏ฟฝ Build Methods Explained | ||
| ## ๐ ๏ธ Build Methods Explained | ||
| ### `build()` - Compile-Time Safety | ||
| ### `build()` - Full Validation (Recommended) | ||
| Use `build()` when you want **compile-time type safety**. TypeScript will enforce that all required properties are set before allowing the build. | ||
| Use `build()` when you want **both compile-time and runtime validation**. This is the safest option. | ||
| ```typescript | ||
| type User = { | ||
| id: string; | ||
| name: string; | ||
| email: string; | ||
| }; | ||
| class UserBuilder extends CeriosBuilder<User> { | ||
| static requiredTemplate: RequiredFieldsTemplate<User> = ['id', 'name', 'email']; | ||
| // ... methods | ||
| } | ||
@@ -1151,45 +1021,103 @@ const user = UserBuilder.create() | ||
| .email('john@example.com') | ||
| .build(); // โ Compiles - all required fields set | ||
| .build(); // โ TypeScript + runtime validation | ||
| // โ TypeScript error - missing email | ||
| const invalid1 = UserBuilder.create() | ||
| .id('123') | ||
| .name('John') | ||
| .build(); | ||
| // โ Runtime error - missing email | ||
| try { | ||
| const invalid2 = UserBuilder.create() | ||
| .id('123') | ||
| .name('John') | ||
| .build(); | ||
| } catch (error) { | ||
| console.error(error.message); // "Missing required fields: email" | ||
| } | ||
| ``` | ||
| **Use when:** | ||
| - You want maximum safety | ||
| - Building critical business objects | ||
| - You have a required template defined | ||
| ### `buildWithoutRuntimeValidation()` - Compile-Time Only | ||
| Use when you want **TypeScript safety** but need to skip runtime validation for performance. | ||
| ```typescript | ||
| const user = UserBuilder.create() | ||
| .id('123') | ||
| .name('John') | ||
| .email('john@example.com') | ||
| .buildWithoutRuntimeValidation(); // โ Fast build, no runtime check | ||
| // โ Still won't compile - TypeScript enforces types | ||
| const invalid = UserBuilder.create() | ||
| .id('123') | ||
| .name('John') | ||
| .build(); // โ TypeScript error - missing email | ||
| .buildWithoutRuntimeValidation(); | ||
| ``` | ||
| ### `buildSafe()` - Runtime Validation | ||
| **Use when:** | ||
| - Performance is critical | ||
| - You trust the type system | ||
| - Building many objects in loops | ||
| Use `buildSafe()` when you want **runtime validation** using the `requiredTemplate`. This is useful when: | ||
| - Building from user input or external data | ||
| - You want to validate deeply nested required fields | ||
| - You need dynamic required fields based on business logic | ||
| ### `buildWithoutCompileTimeValidation()` - Runtime Only | ||
| Use when building from **external data** where compile-time checks aren't possible. | ||
| ```typescript | ||
| class OrderBuilder extends CeriosBuilder<Order> { | ||
| static requiredTemplate: RequiredFieldsTemplate<Order> = [ | ||
| 'Details.CustomerId', | ||
| 'Details.TotalAmount', | ||
| ]; | ||
| function buildFromAPI(data: any) { | ||
| const builder = UserBuilder.create(); | ||
| static create() { | ||
| return new OrderBuilder({}); | ||
| } | ||
| // ... methods | ||
| if (data.id) builder = builder.id(data.id); | ||
| if (data.name) builder = builder.name(data.name); | ||
| if (data.email) builder = builder.email(data.email); | ||
| return builder.buildWithoutCompileTimeValidation(); // Runtime validation | ||
| } | ||
| const order = OrderBuilder.create() | ||
| .customerId('CUST-001') | ||
| .totalAmount(100) | ||
| .buildSafe(); // โ Runtime validation passes | ||
| // โ Valid data passes | ||
| const user = buildFromAPI({ id: '123', name: 'John', email: 'john@example.com' }); | ||
| const invalid = OrderBuilder.create() | ||
| .customerId('CUST-002') | ||
| .buildSafe(); // โ Throws: "Missing required fields: Details.TotalAmount" | ||
| // โ Invalid data throws | ||
| try { | ||
| const invalid = buildFromAPI({ id: '123', name: 'John' }); // Missing email | ||
| } catch (error) { | ||
| console.error(error.message); // "Missing required fields: email" | ||
| } | ||
| ``` | ||
| ### `buildPartial()` - No Validation | ||
| **Use when:** | ||
| - Building from API responses | ||
| - Processing user input | ||
| - Working with dynamic data | ||
| - Need runtime validation without TypeScript constraints | ||
| Use `buildPartial()` when you need to build an incomplete object: | ||
| ### `buildUnsafe()` - No Validation | ||
| Use **only when you're certain** the object is valid and need maximum performance. | ||
| ```typescript | ||
| const user = UserBuilder.create() | ||
| .id('123') | ||
| .buildUnsafe(); // โ ๏ธ No checks at all | ||
| console.log(user); // { id: '123' } - potentially incomplete! | ||
| ``` | ||
| **Use when:** | ||
| - You're absolutely certain the data is valid | ||
| - Maximum performance is required | ||
| - Building throwaway test objects | ||
| ### `buildPartial()` - Partial Objects | ||
| Use when you need **incomplete objects**. | ||
| ```typescript | ||
| const partial = UserBuilder.create() | ||
@@ -1199,8 +1127,21 @@ .name('Incomplete User') | ||
| // Useful for: | ||
| // - Progressive form filling | ||
| // - Template objects | ||
| // - Partial updates | ||
| console.log(partial); // { name: 'Incomplete User' } | ||
| ``` | ||
| **Use when:** | ||
| - Progressive form filling | ||
| - Template objects | ||
| - Partial updates | ||
| - Inspecting builder state | ||
| ### Comparison Table | ||
| | Method | Compile-Time Check | Runtime Check | Performance | Safety | Use Case | | ||
| |--------|-------------------|---------------|-------------|--------|----------| | ||
| | `build()` | โ | โ | Medium | Highest | Production code | | ||
| | `buildWithoutRuntimeValidation()` | โ | โ | Fast | High | Performance-critical | | ||
| | `buildWithoutCompileTimeValidation()` | โ | โ | Medium | Medium | External data | | ||
| | `buildUnsafe()` | โ | โ | Fastest | Lowest | Trusted scenarios only | | ||
| | `buildPartial()` | โ | โ | Fast | N/A | Incomplete objects | | ||
| ## ๐ก Best Practices | ||
@@ -1210,7 +1151,13 @@ | ||
| 2. **Define `requiredTemplate` for runtime validation**: When working with external data or complex validation rules, define a required template and use `buildSafe()`. | ||
| 2. **Define `requiredTemplate` for runtime validation**: When working with external data or complex validation rules, define a required template. | ||
| 3. **Use `setRequiredFields()` for conditional requirements**: When requirements change based on context (e.g., new vs existing records), use dynamic required fields. | ||
| 3. **Choose the right build method**: | ||
| - Default to `build()` for safety | ||
| - Use `buildWithoutRuntimeValidation()` for performance | ||
| - Use `buildWithoutCompileTimeValidation()` for external data | ||
| - Use `buildUnsafe()` only when absolutely necessary | ||
| 4. **Create factory methods**: Use static `create()` and `createWithDefaults()` methods for better ergonomics: | ||
| 4. **Use `setRequiredFields()` for conditional requirements**: When requirements change based on context (e.g., new vs existing records), use dynamic required fields. | ||
| 5. **Create factory methods**: Use static `create()` and `createWithDefaults()` methods for better ergonomics: | ||
| ```typescript | ||
@@ -1224,11 +1171,11 @@ static createWithDefaults() { | ||
| 5. **Combine with validation libraries**: Use `buildSafe()` with libraries like Zod for comprehensive validation: | ||
| 6. **Combine with validation libraries**: Use runtime validation with libraries like Zod for comprehensive validation: | ||
| ```typescript | ||
| const data = builder.buildSafe(); | ||
| const data = builder.buildWithoutCompileTimeValidation(); | ||
| const validated = orderSchema.parse(data); // Zod validation | ||
| ``` | ||
| 6. **Keep builders focused**: One builder per entity type - don't try to handle multiple unrelated types in one builder. | ||
| 7. **Keep builders focused**: One builder per entity type - don't try to handle multiple unrelated types in one builder. | ||
| 7. **Use type-safe paths**: The `RequiredFieldsTemplate` type ensures you can only specify valid paths: | ||
| 8. **Use type-safe paths**: The `RequiredFieldsTemplate` type ensures you can only specify valid paths: | ||
| ```typescript | ||
@@ -1241,3 +1188,3 @@ static requiredTemplate: RequiredFieldsTemplate<Order> = [ | ||
| ## ๏ฟฝ๐ License | ||
| ## ๐ License | ||
@@ -1244,0 +1191,0 @@ MIT ยฉ [Cerios](LICENSE) |
109861
5.66%793
10.29%1155
-4.39%