@cerios/cerios-builder
Advanced tools
+67
-0
@@ -33,2 +33,22 @@ /** | ||
| /** | ||
| * Recursively makes all properties readonly for deep immutability. | ||
| * Handles arrays, objects, and primitive types. | ||
| * | ||
| * @template T - The type to make deeply readonly | ||
| */ | ||
| type DeepReadonly<T> = T extends (infer R)[] ? DeepReadonlyArray<R> : T extends (...args: any[]) => any ? T : T extends object ? DeepReadonlyObject<T> : T; | ||
| /** | ||
| * Helper type for deep readonly arrays | ||
| * @internal | ||
| */ | ||
| interface DeepReadonlyArray<T> extends ReadonlyArray<DeepReadonly<T>> { | ||
| } | ||
| /** | ||
| * Helper type for deep readonly objects | ||
| * @internal | ||
| */ | ||
| type DeepReadonlyObject<T> = { | ||
| readonly [P in keyof T]: DeepReadonly<T[P]>; | ||
| }; | ||
| /** | ||
| * Cache the root key extraction to avoid repeated computation | ||
@@ -222,4 +242,51 @@ * Handles optional properties by ensuring the key is valid for T | ||
| buildSafe(): T; | ||
| /** | ||
| * Builds and freezes the final object with both compile-time and runtime validation. | ||
| * The returned object is shallowly frozen - top-level properties cannot be modified, | ||
| * but nested objects remain mutable. | ||
| * | ||
| * - Compile-time: TypeScript enforces all required properties are set | ||
| * - Runtime: Validates all fields in the requiredTemplate and applies Object.freeze() | ||
| * | ||
| * @returns The frozen object of type Readonly<T> | ||
| * @throws {Error} If any required field is missing at runtime | ||
| */ | ||
| buildFrozen(this: this & CeriosBrand<T>): Readonly<T>; | ||
| /** | ||
| * Builds and deeply freezes the final object with both compile-time and runtime validation. | ||
| * The returned object is recursively frozen - all nested objects and arrays are also frozen. | ||
| * | ||
| * - Compile-time: TypeScript enforces all required properties are set | ||
| * - Runtime: Validates all fields in the requiredTemplate and recursively applies Object.freeze() | ||
| * | ||
| * @returns The deeply frozen object of type DeepReadonly<T> | ||
| * @throws {Error} If any required field is missing at runtime | ||
| */ | ||
| buildDeepFrozen(this: this & CeriosBrand<T>): DeepReadonly<T>; | ||
| /** | ||
| * Builds and seals the final object with both compile-time and runtime validation. | ||
| * The returned object is shallowly sealed - properties cannot be added or removed, | ||
| * but existing properties can still be modified. Nested objects remain unsealed. | ||
| * | ||
| * - Compile-time: TypeScript enforces all required properties are set | ||
| * - Runtime: Validates all fields in the requiredTemplate and applies Object.seal() | ||
| * | ||
| * @returns The sealed object of type T | ||
| * @throws {Error} If any required field is missing at runtime | ||
| */ | ||
| buildSealed(this: this & CeriosBrand<T>): T; | ||
| /** | ||
| * Builds and deeply seals the final object with both compile-time and runtime validation. | ||
| * The returned object is recursively sealed - properties cannot be added or removed at any level, | ||
| * but existing properties can still be modified. | ||
| * | ||
| * - Compile-time: TypeScript enforces all required properties are set | ||
| * - Runtime: Validates all fields in the requiredTemplate and recursively applies Object.seal() | ||
| * | ||
| * @returns The deeply sealed object of type T | ||
| * @throws {Error} If any required field is missing at runtime | ||
| */ | ||
| buildDeepSealed(this: this & CeriosBrand<T>): T; | ||
| } | ||
| export { type CeriosBrand, CeriosBuilder, type RequiredFieldsTemplate }; |
+67
-0
@@ -33,2 +33,22 @@ /** | ||
| /** | ||
| * Recursively makes all properties readonly for deep immutability. | ||
| * Handles arrays, objects, and primitive types. | ||
| * | ||
| * @template T - The type to make deeply readonly | ||
| */ | ||
| type DeepReadonly<T> = T extends (infer R)[] ? DeepReadonlyArray<R> : T extends (...args: any[]) => any ? T : T extends object ? DeepReadonlyObject<T> : T; | ||
| /** | ||
| * Helper type for deep readonly arrays | ||
| * @internal | ||
| */ | ||
| interface DeepReadonlyArray<T> extends ReadonlyArray<DeepReadonly<T>> { | ||
| } | ||
| /** | ||
| * Helper type for deep readonly objects | ||
| * @internal | ||
| */ | ||
| type DeepReadonlyObject<T> = { | ||
| readonly [P in keyof T]: DeepReadonly<T[P]>; | ||
| }; | ||
| /** | ||
| * Cache the root key extraction to avoid repeated computation | ||
@@ -222,4 +242,51 @@ * Handles optional properties by ensuring the key is valid for T | ||
| buildSafe(): T; | ||
| /** | ||
| * Builds and freezes the final object with both compile-time and runtime validation. | ||
| * The returned object is shallowly frozen - top-level properties cannot be modified, | ||
| * but nested objects remain mutable. | ||
| * | ||
| * - Compile-time: TypeScript enforces all required properties are set | ||
| * - Runtime: Validates all fields in the requiredTemplate and applies Object.freeze() | ||
| * | ||
| * @returns The frozen object of type Readonly<T> | ||
| * @throws {Error} If any required field is missing at runtime | ||
| */ | ||
| buildFrozen(this: this & CeriosBrand<T>): Readonly<T>; | ||
| /** | ||
| * Builds and deeply freezes the final object with both compile-time and runtime validation. | ||
| * The returned object is recursively frozen - all nested objects and arrays are also frozen. | ||
| * | ||
| * - Compile-time: TypeScript enforces all required properties are set | ||
| * - Runtime: Validates all fields in the requiredTemplate and recursively applies Object.freeze() | ||
| * | ||
| * @returns The deeply frozen object of type DeepReadonly<T> | ||
| * @throws {Error} If any required field is missing at runtime | ||
| */ | ||
| buildDeepFrozen(this: this & CeriosBrand<T>): DeepReadonly<T>; | ||
| /** | ||
| * Builds and seals the final object with both compile-time and runtime validation. | ||
| * The returned object is shallowly sealed - properties cannot be added or removed, | ||
| * but existing properties can still be modified. Nested objects remain unsealed. | ||
| * | ||
| * - Compile-time: TypeScript enforces all required properties are set | ||
| * - Runtime: Validates all fields in the requiredTemplate and applies Object.seal() | ||
| * | ||
| * @returns The sealed object of type T | ||
| * @throws {Error} If any required field is missing at runtime | ||
| */ | ||
| buildSealed(this: this & CeriosBrand<T>): T; | ||
| /** | ||
| * Builds and deeply seals the final object with both compile-time and runtime validation. | ||
| * The returned object is recursively sealed - properties cannot be added or removed at any level, | ||
| * but existing properties can still be modified. | ||
| * | ||
| * - Compile-time: TypeScript enforces all required properties are set | ||
| * - Runtime: Validates all fields in the requiredTemplate and recursively applies Object.seal() | ||
| * | ||
| * @returns The deeply sealed object of type T | ||
| * @throws {Error} If any required field is missing at runtime | ||
| */ | ||
| buildDeepSealed(this: this & CeriosBrand<T>): T; | ||
| } | ||
| export { type CeriosBrand, CeriosBuilder, type RequiredFieldsTemplate }; |
+89
-0
@@ -28,2 +28,20 @@ "use strict"; | ||
| // src/cerios-builder.ts | ||
| function deepFreeze(obj) { | ||
| Object.getOwnPropertyNames(obj).forEach((prop) => { | ||
| const value = obj[prop]; | ||
| if (value !== null && (typeof value === "object" || typeof value === "function")) { | ||
| deepFreeze(value); | ||
| } | ||
| }); | ||
| return Object.freeze(obj); | ||
| } | ||
| function deepSeal(obj) { | ||
| Object.getOwnPropertyNames(obj).forEach((prop) => { | ||
| const value = obj[prop]; | ||
| if (value !== null && (typeof value === "object" || typeof value === "function")) { | ||
| deepSeal(value); | ||
| } | ||
| }); | ||
| return Object.seal(obj); | ||
| } | ||
| var CeriosBuilder = class { | ||
@@ -297,2 +315,73 @@ /** | ||
| } | ||
| /** | ||
| * Builds and freezes the final object with both compile-time and runtime validation. | ||
| * The returned object is shallowly frozen - top-level properties cannot be modified, | ||
| * but nested objects remain mutable. | ||
| * | ||
| * - Compile-time: TypeScript enforces all required properties are set | ||
| * - Runtime: Validates all fields in the requiredTemplate and applies Object.freeze() | ||
| * | ||
| * @returns The frozen object of type Readonly<T> | ||
| * @throws {Error} If any required field is missing at runtime | ||
| */ | ||
| buildFrozen() { | ||
| const missing = this.validateRequiredFields(); | ||
| if (missing.length > 0) { | ||
| throw new Error(`Missing required fields: ${missing.join(", ")}. Please set these fields before calling build.`); | ||
| } | ||
| return Object.freeze(this._actual); | ||
| } | ||
| /** | ||
| * Builds and deeply freezes the final object with both compile-time and runtime validation. | ||
| * The returned object is recursively frozen - all nested objects and arrays are also frozen. | ||
| * | ||
| * - Compile-time: TypeScript enforces all required properties are set | ||
| * - Runtime: Validates all fields in the requiredTemplate and recursively applies Object.freeze() | ||
| * | ||
| * @returns The deeply frozen object of type DeepReadonly<T> | ||
| * @throws {Error} If any required field is missing at runtime | ||
| */ | ||
| buildDeepFrozen() { | ||
| const missing = this.validateRequiredFields(); | ||
| if (missing.length > 0) { | ||
| throw new Error(`Missing required fields: ${missing.join(", ")}. Please set these fields before calling build.`); | ||
| } | ||
| return deepFreeze(this._actual); | ||
| } | ||
| /** | ||
| * Builds and seals the final object with both compile-time and runtime validation. | ||
| * The returned object is shallowly sealed - properties cannot be added or removed, | ||
| * but existing properties can still be modified. Nested objects remain unsealed. | ||
| * | ||
| * - Compile-time: TypeScript enforces all required properties are set | ||
| * - Runtime: Validates all fields in the requiredTemplate and applies Object.seal() | ||
| * | ||
| * @returns The sealed object of type T | ||
| * @throws {Error} If any required field is missing at runtime | ||
| */ | ||
| buildSealed() { | ||
| const missing = this.validateRequiredFields(); | ||
| if (missing.length > 0) { | ||
| throw new Error(`Missing required fields: ${missing.join(", ")}. Please set these fields before calling build.`); | ||
| } | ||
| return Object.seal(this._actual); | ||
| } | ||
| /** | ||
| * Builds and deeply seals the final object with both compile-time and runtime validation. | ||
| * The returned object is recursively sealed - properties cannot be added or removed at any level, | ||
| * but existing properties can still be modified. | ||
| * | ||
| * - Compile-time: TypeScript enforces all required properties are set | ||
| * - Runtime: Validates all fields in the requiredTemplate and recursively applies Object.seal() | ||
| * | ||
| * @returns The deeply sealed object of type T | ||
| * @throws {Error} If any required field is missing at runtime | ||
| */ | ||
| buildDeepSealed() { | ||
| const missing = this.validateRequiredFields(); | ||
| if (missing.length > 0) { | ||
| throw new Error(`Missing required fields: ${missing.join(", ")}. Please set these fields before calling build.`); | ||
| } | ||
| return deepSeal(this._actual); | ||
| } | ||
| }; | ||
@@ -299,0 +388,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 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":[]} | ||
| {"version":3,"sources":["../src/index.ts","../src/cerios-builder.ts"],"sourcesContent":["export { CeriosBrand, CeriosBuilder, RequiredFieldsTemplate } from \"./cerios-builder.js\";\n","/**\n * Unique symbol used internally to brand types and track which properties have been set in the builder's type.\n *\n * @internal\n */\ndeclare const __brand: unique symbol;\n\n/**\n * Type utility for branding builder types with information about which properties have been set.\n * This is used to enforce compile-time safety for required fields in the builder pattern.\n *\n * @template T - The type representing the set of properties that have been set\n * @internal\n */\nexport type CeriosBrand<T> = { [__brand]: T };\n\n/**\n * Helper type to represent a path through an object structure\n * Handles optional properties by unwrapping them with NonNullable\n */\ntype PathImpl<T, K extends keyof T = keyof T> = K extends string | number\n\t? NonNullable<T[K]> extends Record<string, any>\n\t\t? NonNullable<T[K]> extends Array<any>\n\t\t\t? K\n\t\t\t: K | `${K}.${PathImpl<NonNullable<T[K]>> & string}`\n\t\t: K\n\t: never;\n\nexport type Path<T> = PathImpl<T>;\n\n/**\n * Helper type to get the value at a specific path, handling optional properties\n */\ntype PathValue<T, P> = P extends keyof T\n\t? T[P]\n\t: P extends `${infer K}.${infer Rest}`\n\t\t? K extends keyof T\n\t\t\t? PathValue<NonNullable<T[K]>, Rest>\n\t\t\t: never\n\t\t: never;\n\n/**\n * Type-safe template for defining required fields using an array of paths.\n * Simply list the paths that are required.\n */\nexport type RequiredFieldsTemplate<T> = ReadonlyArray<Path<T>>;\n\n/**\n * Recursively makes all properties readonly for deep immutability.\n * Handles arrays, objects, and primitive types.\n *\n * @template T - The type to make deeply readonly\n */\nexport type DeepReadonly<T> = T extends (infer R)[]\n\t? DeepReadonlyArray<R>\n\t: T extends (...args: any[]) => any\n\t\t? T\n\t\t: T extends object\n\t\t\t? DeepReadonlyObject<T>\n\t\t\t: T;\n\n/**\n * Helper type for deep readonly arrays\n * @internal\n */\ninterface DeepReadonlyArray<T> extends ReadonlyArray<DeepReadonly<T>> {}\n\n/**\n * Helper type for deep readonly objects\n * @internal\n */\ntype DeepReadonlyObject<T> = {\n\treadonly [P in keyof T]: DeepReadonly<T[P]>;\n};\n\n/**\n * Cache the root key extraction to avoid repeated computation\n * Handles optional properties by ensuring the key is valid for T\n * @internal\n */\ntype RootKey<P extends string, T = any> = P extends `${infer K}.${string}`\n\t? K extends keyof T\n\t\t? K\n\t\t: never\n\t: P extends keyof T\n\t\t? P\n\t\t: never;\n\n/**\n * Recursively freezes an object and all its nested properties.\n * @param obj - The object to freeze\n * @returns The frozen object\n * @internal\n */\nfunction deepFreeze<T>(obj: T): T {\n\t// Retrieve the property names defined on obj\n\tObject.getOwnPropertyNames(obj).forEach(prop => {\n\t\tconst value = (obj as any)[prop];\n\n\t\t// Freeze properties before freezing self\n\t\tif (value !== null && (typeof value === \"object\" || typeof value === \"function\")) {\n\t\t\tdeepFreeze(value);\n\t\t}\n\t});\n\n\treturn Object.freeze(obj);\n}\n\n/**\n * Recursively seals an object and all its nested properties.\n * @param obj - The object to seal\n * @returns The sealed object\n * @internal\n */\nfunction deepSeal<T>(obj: T): T {\n\t// Retrieve the property names defined on obj\n\tObject.getOwnPropertyNames(obj).forEach(prop => {\n\t\tconst value = (obj as any)[prop];\n\n\t\t// Seal properties before sealing self\n\t\tif (value !== null && (typeof value === \"object\" || typeof value === \"function\")) {\n\t\t\tdeepSeal(value);\n\t\t}\n\t});\n\n\treturn Object.seal(obj);\n}\n\n/**\n * Abstract base class for creating type-safe builders with automatic property setters and compile-time validation of required fields.\n *\n * This class is intended to be extended by concrete builder implementations for your own types.\n * It provides utility methods for setting properties and building the final object, ensuring that all required fields are set at compile time.\n *\n * Example usage:\n * ```typescript\n * interface MyType { foo: string; bar: number[]; }\n * class MyTypeBuilder extends CeriosBuilder<MyType> {\n * static requiredTemplate: RequiredFieldsTemplate<MyType> = ['foo'];\n * setFoo(value: string) { return this.setProperty('foo', value); }\n * addBar(value: number) { return this.addToArrayProperty('bar', value); }\n * }\n * // Usage:\n * const obj = new MyTypeBuilder({})\n * .setFoo('hello')\n * .addBar(42)\n * .buildSafe(); // Validates that 'foo' is set\n * ```\n *\n * @template T - The complete type being built\n */\nexport abstract class CeriosBuilder<T extends object> {\n\t/**\n\t * Template defining which fields are required for this builder.\n\t * Subclasses should override this to specify their required fields as an array of paths.\n\t * The template is type-safe - only valid paths from type T can be used.\n\t */\n\tstatic requiredTemplate?: ReadonlyArray<string>;\n\n\t/**\n\t * Instance-level required fields that can be populated dynamically.\n\t * This allows adding required fields at runtime via the setRequiredFields method.\n\t * @private\n\t */\n\tprivate _requiredFields: Set<string> = new Set();\n\n\t/**\n\t * Sets the required fields for this builder instance.\n\t * This allows you to dynamically define which fields are required.\n\t *\n\t * @param fields - Array of dot-notation paths to required fields\n\t * @returns The builder instance for chaining\n\t *\n\t * @example\n\t * ```typescript\n\t * const builder = new MyBuilder({})\n\t * .setRequiredFields(['path.to.field1', 'path.to.field2'])\n\t * .setField1('value1')\n\t * .setField2('value2')\n\t * .buildSafe();\n\t * ```\n\t */\n\tsetRequiredFields(fields: ReadonlyArray<Path<T>>): this {\n\t\tthis._requiredFields = new Set([...fields] as string[]);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Gets the combined required fields from both the static template and instance-level fields.\n\t * @private\n\t */\n\tprivate getRequiredTemplate(): ReadonlyArray<string> {\n\t\tconst ctor = this.constructor as typeof CeriosBuilder;\n\t\tconst staticFields = ctor.requiredTemplate || [];\n\t\tconst instanceFields = Array.from(this._requiredFields);\n\n\t\t// Combine and deduplicate\n\t\treturn [...new Set([...staticFields, ...instanceFields])];\n\t}\n\n\t/**\n\t * Validates that all fields in the required template have been set.\n\t * @private\n\t */\n\tprivate validateRequiredFields(): string[] {\n\t\tconst requiredPaths = this.getRequiredTemplate();\n\t\tconst missing: string[] = [];\n\n\t\tfor (const path of requiredPaths) {\n\t\t\tconst keys = path.split(\".\");\n\t\t\tlet current: any = this._actual;\n\n\t\t\tfor (let i = 0; i < keys.length; i++) {\n\t\t\t\tconst key = keys[i];\n\t\t\t\tif (current === null || current === undefined || !(key in current)) {\n\t\t\t\t\tmissing.push(path);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcurrent = current[key];\n\t\t\t}\n\n\t\t\t// Check if the final value is null or undefined\n\t\t\tif (current === null || current === undefined) {\n\t\t\t\tif (!missing.includes(path)) {\n\t\t\t\t\tmissing.push(path);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn missing;\n\t}\n\n\t/**\n\t * Creates a new builder instance. Intended to be called by subclasses.\n\t *\n\t * @param _actual - The current partial state of the object being built\n\t * @param _requiredFields - Optional array of required field paths to preserve across instances\n\t * @protected\n\t */\n\tprotected constructor(\n\t\tprotected readonly _actual: Partial<T>,\n\t\t_requiredFields?: RequiredFieldsTemplate<T>\n\t) {\n\t\tif (_requiredFields) {\n\t\t\tthis._requiredFields = new Set([..._requiredFields] as string[]);\n\t\t}\n\t}\n\n\t/**\n\t * Sets a property value and returns a new builder instance with updated type state.\n\t * This method is intended to be wrapped by concrete builder methods in subclasses.\n\t *\n\t * @template K - The property key being set\n\t * @param key - The property key to set\n\t * @param value - The value to assign to the property\n\t * @returns A new builder instance with the property set and type state updated\n\t * @protected\n\t */\n\tprotected setProperty<K extends keyof T>(key: K, value: T[K]): this & CeriosBrand<Pick<T, K>> {\n\t\tconst BuilderClass = this.constructor as new (data: any, requiredFields?: RequiredFieldsTemplate<T>) => any;\n\t\treturn new BuilderClass(\n\t\t\t{\n\t\t\t\t...this._actual,\n\t\t\t\t[key]: value,\n\t\t\t},\n\t\t\tArray.from(this._requiredFields) as unknown as RequiredFieldsTemplate<T>\n\t\t) as this & CeriosBrand<Pick<T, K>>;\n\t}\n\n\t/**\n\t * Sets multiple property values at once and returns a new builder instance with updated type state.\n\t * @param props - An object with one or more properties to set.\n\t * @returns A new builder instance with the properties set and type state updated.\n\t * @protected\n\t */\n\tprotected setProperties<K extends keyof T>(props: Pick<T, K>): this & CeriosBrand<Pick<T, K>> {\n\t\tconst BuilderClass = this.constructor as new (data: any, requiredFields?: RequiredFieldsTemplate<T>) => any;\n\t\treturn new BuilderClass(\n\t\t\t{\n\t\t\t\t...this._actual,\n\t\t\t\t...props,\n\t\t\t},\n\t\t\tArray.from(this._requiredFields) as unknown as RequiredFieldsTemplate<T>\n\t\t) as this & CeriosBrand<Pick<T, K>>;\n\t}\n\n\t/**\n\t * Sets a deeply nested property value and returns a new builder instance with updated type state.\n\t * This method uses dot notation to set nested properties in a type-safe way.\n\t *\n\t * @template P - The property path (e.g., \"parent.child.grandchild\")\n\t * @param path - The dot-notation path to the property\n\t * @param value - The value to assign to the nested property\n\t * @returns A new builder instance with the nested property set\n\t * @protected\n\t *\n\t * @example\n\t * ```typescript\n\t * builder.setNestedProperty('Order.Details.CustomerId', 'value')\n\t * ```\n\t */\n\tprotected setNestedProperty<P extends Path<T>>(\n\t\tpath: P,\n\t\tvalue: PathValue<T, P>\n\t): this & CeriosBrand<Pick<T, RootKey<P & string, T> extends never ? keyof T : RootKey<P & string, T>>> {\n\t\tconst BuilderClass = this.constructor as new (data: any, requiredFields?: RequiredFieldsTemplate<T>) => any;\n\t\tconst keys = (path as string).split(\".\");\n\t\tconst newActual = this.deepClone(this._actual);\n\n\t\tlet current: any = newActual;\n\t\tfor (let i = 0; i < keys.length - 1; i++) {\n\t\t\tconst key = keys[i];\n\t\t\tif (!(key in current) || typeof current[key] !== \"object\" || current[key] === null) {\n\t\t\t\tcurrent[key] = {};\n\t\t\t} else {\n\t\t\t\tcurrent[key] = this.deepClone(current[key]);\n\t\t\t}\n\t\t\tcurrent = current[key];\n\t\t}\n\n\t\tcurrent[keys[keys.length - 1]] = value;\n\n\t\treturn new BuilderClass(\n\t\t\tnewActual,\n\t\t\tArray.from(this._requiredFields) as unknown as RequiredFieldsTemplate<T>\n\t\t) as this & CeriosBrand<Pick<T, RootKey<P & string, T> extends never ? keyof T : RootKey<P & string, T>>>;\n\t}\n\n\t/**\n\t * Deep clone helper for nested objects\n\t * @private\n\t */\n\tprivate deepClone<V>(obj: V): V {\n\t\tif (obj === null || typeof obj !== \"object\") {\n\t\t\treturn obj;\n\t\t}\n\t\tif (Array.isArray(obj)) {\n\t\t\treturn obj.map(item => this.deepClone(item)) as any;\n\t\t}\n\t\tconst cloned: any = {};\n\t\tconst hasOwn = Object.prototype.hasOwnProperty;\n\t\tfor (const key in obj) {\n\t\t\tif (hasOwn.call(obj, key)) {\n\t\t\t\tcloned[key] = this.deepClone(obj[key]);\n\t\t\t}\n\t\t}\n\t\treturn cloned;\n\t}\n\n\t/**\n\t * Adds a value to an array property and returns a new builder instance with updated type state.\n\t * This method is intended to be wrapped by concrete builder methods in subclasses for array properties.\n\t *\n\t * @template K - The property key (must be an array property)\n\t * @template V - The type of the array element\n\t * @param key - The array property key to add to\n\t * @param value - The value to add to the array\n\t * @returns A new builder instance with the array property updated and type state updated\n\t * @protected\n\t */\n\tprotected addToArrayProperty<\n\t\tK extends { [P in keyof T]: NonNullable<T[P]> extends Array<any> ? P : never }[keyof T],\n\t\tV extends T[K] extends Array<infer U> ? U : T[K] extends Array<infer U> | undefined ? U : never,\n\t>(key: K, value: V): this & CeriosBrand<Pick<T, K>> {\n\t\tconst BuilderClass = this.constructor as new (data: any, requiredFields?: RequiredFieldsTemplate<T>) => any;\n\t\tconst currentArray = (this._actual[key] as Array<V> | undefined) ?? [];\n\t\treturn new BuilderClass(\n\t\t\t{\n\t\t\t\t...this._actual,\n\t\t\t\t[key]: [...currentArray, value],\n\t\t\t},\n\t\t\tArray.from(this._requiredFields) as unknown as RequiredFieldsTemplate<T>\n\t\t) as this & CeriosBrand<Pick<T, K>>;\n\t}\n\n\t/**\n\t * Builds the final object with both compile-time and runtime validation.\n\t * This is the recommended and safest way to build objects.\n\t *\n\t * - Compile-time: TypeScript enforces all required properties are set\n\t * - Runtime: Validates all fields in the requiredTemplate\n\t *\n\t * @returns The fully built object of type T\n\t * @throws {Error} If any required field is missing at runtime\n\t */\n\tbuild(this: this & CeriosBrand<T>): T {\n\t\tconst missing = this.validateRequiredFields();\n\n\t\tif (missing.length > 0) {\n\t\t\tthrow new Error(`Missing required fields: ${missing.join(\", \")}. Please set these fields before calling build.`);\n\t\t}\n\n\t\treturn this._actual as T;\n\t}\n\n\t/**\n\t * Builds the final object with only compile-time validation, skipping runtime checks.\n\t * Use this when you want TypeScript safety but need to skip runtime validation for performance.\n\t *\n\t * - Compile-time: TypeScript enforces all required properties are set\n\t * - Runtime: No validation\n\t *\n\t * @returns The fully built object of type T\n\t */\n\tbuildWithoutRuntimeValidation(this: this & CeriosBrand<T>): T {\n\t\treturn this._actual as T;\n\t}\n\n\t/**\n\t * Builds the final object with only runtime validation, skipping compile-time checks.\n\t * Use this when building from external data where compile-time checks aren't possible.\n\t *\n\t * - Compile-time: No TypeScript enforcement\n\t * - Runtime: Validates all fields in the requiredTemplate\n\t *\n\t * @returns The fully built object of type T\n\t * @throws {Error} If any required field is missing at runtime\n\t */\n\tbuildWithoutCompileTimeValidation(): T {\n\t\tconst missing = this.validateRequiredFields();\n\n\t\tif (missing.length > 0) {\n\t\t\tthrow new Error(`Missing required fields: ${missing.join(\", \")}. Please set these fields before calling build.`);\n\t\t}\n\n\t\treturn this._actual as T;\n\t}\n\n\t/**\n\t * Builds the final object without any validation (neither compile-time nor runtime).\n\t * Use this only when you're certain the object is valid and need maximum performance.\n\t *\n\t * - Compile-time: No TypeScript enforcement\n\t * - Runtime: No validation\n\t *\n\t * @returns The object of type T (may be incomplete)\n\t */\n\tbuildUnsafe(): T {\n\t\treturn this._actual as T;\n\t}\n\n\t/**\n\t * Builds a partial object, which may not have all required fields set.\n\t * This is useful for scenarios where you want to inspect or validate the current state before finalizing.\n\t *\n\t * @returns The partially built object\n\t */\n\tbuildPartial(): Partial<T> {\n\t\treturn this._actual as Partial<T>;\n\t}\n\n\t/**\n\t * @deprecated Use build() instead. buildSafe() is now an alias for build().\n\t * Builds the final object with runtime validation using the requiredTemplate.\n\t * This method validates that all fields in the requiredTemplate array are present.\n\t *\n\t * @returns The fully built object of type T\n\t * @throws {Error} If any required field is missing at runtime\n\t */\n\tbuildSafe(): T {\n\t\treturn this.buildWithoutCompileTimeValidation();\n\t}\n\n\t/**\n\t * Builds and freezes the final object with both compile-time and runtime validation.\n\t * The returned object is shallowly frozen - top-level properties cannot be modified,\n\t * but nested objects remain mutable.\n\t *\n\t * - Compile-time: TypeScript enforces all required properties are set\n\t * - Runtime: Validates all fields in the requiredTemplate and applies Object.freeze()\n\t *\n\t * @returns The frozen object of type Readonly<T>\n\t * @throws {Error} If any required field is missing at runtime\n\t */\n\tbuildFrozen(this: this & CeriosBrand<T>): Readonly<T> {\n\t\tconst missing = this.validateRequiredFields();\n\n\t\tif (missing.length > 0) {\n\t\t\tthrow new Error(`Missing required fields: ${missing.join(\", \")}. Please set these fields before calling build.`);\n\t\t}\n\n\t\treturn Object.freeze(this._actual as T);\n\t}\n\n\t/**\n\t * Builds and deeply freezes the final object with both compile-time and runtime validation.\n\t * The returned object is recursively frozen - all nested objects and arrays are also frozen.\n\t *\n\t * - Compile-time: TypeScript enforces all required properties are set\n\t * - Runtime: Validates all fields in the requiredTemplate and recursively applies Object.freeze()\n\t *\n\t * @returns The deeply frozen object of type DeepReadonly<T>\n\t * @throws {Error} If any required field is missing at runtime\n\t */\n\tbuildDeepFrozen(this: this & CeriosBrand<T>): DeepReadonly<T> {\n\t\tconst missing = this.validateRequiredFields();\n\n\t\tif (missing.length > 0) {\n\t\t\tthrow new Error(`Missing required fields: ${missing.join(\", \")}. Please set these fields before calling build.`);\n\t\t}\n\n\t\treturn deepFreeze(this._actual as T) as DeepReadonly<T>;\n\t}\n\n\t/**\n\t * Builds and seals the final object with both compile-time and runtime validation.\n\t * The returned object is shallowly sealed - properties cannot be added or removed,\n\t * but existing properties can still be modified. Nested objects remain unsealed.\n\t *\n\t * - Compile-time: TypeScript enforces all required properties are set\n\t * - Runtime: Validates all fields in the requiredTemplate and applies Object.seal()\n\t *\n\t * @returns The sealed object of type T\n\t * @throws {Error} If any required field is missing at runtime\n\t */\n\tbuildSealed(this: this & CeriosBrand<T>): T {\n\t\tconst missing = this.validateRequiredFields();\n\n\t\tif (missing.length > 0) {\n\t\t\tthrow new Error(`Missing required fields: ${missing.join(\", \")}. Please set these fields before calling build.`);\n\t\t}\n\n\t\treturn Object.seal(this._actual as T);\n\t}\n\n\t/**\n\t * Builds and deeply seals the final object with both compile-time and runtime validation.\n\t * The returned object is recursively sealed - properties cannot be added or removed at any level,\n\t * but existing properties can still be modified.\n\t *\n\t * - Compile-time: TypeScript enforces all required properties are set\n\t * - Runtime: Validates all fields in the requiredTemplate and recursively applies Object.seal()\n\t *\n\t * @returns The deeply sealed object of type T\n\t * @throws {Error} If any required field is missing at runtime\n\t */\n\tbuildDeepSealed(this: this & CeriosBrand<T>): T {\n\t\tconst missing = this.validateRequiredFields();\n\n\t\tif (missing.length > 0) {\n\t\t\tthrow new Error(`Missing required fields: ${missing.join(\", \")}. Please set these fields before calling build.`);\n\t\t}\n\n\t\treturn deepSeal(this._actual as T);\n\t}\n}\n"],"mappings":"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","names":[]} |
+89
-0
| // src/cerios-builder.ts | ||
| function deepFreeze(obj) { | ||
| Object.getOwnPropertyNames(obj).forEach((prop) => { | ||
| const value = obj[prop]; | ||
| if (value !== null && (typeof value === "object" || typeof value === "function")) { | ||
| deepFreeze(value); | ||
| } | ||
| }); | ||
| return Object.freeze(obj); | ||
| } | ||
| function deepSeal(obj) { | ||
| Object.getOwnPropertyNames(obj).forEach((prop) => { | ||
| const value = obj[prop]; | ||
| if (value !== null && (typeof value === "object" || typeof value === "function")) { | ||
| deepSeal(value); | ||
| } | ||
| }); | ||
| return Object.seal(obj); | ||
| } | ||
| var CeriosBuilder = class { | ||
@@ -270,2 +288,73 @@ /** | ||
| } | ||
| /** | ||
| * Builds and freezes the final object with both compile-time and runtime validation. | ||
| * The returned object is shallowly frozen - top-level properties cannot be modified, | ||
| * but nested objects remain mutable. | ||
| * | ||
| * - Compile-time: TypeScript enforces all required properties are set | ||
| * - Runtime: Validates all fields in the requiredTemplate and applies Object.freeze() | ||
| * | ||
| * @returns The frozen object of type Readonly<T> | ||
| * @throws {Error} If any required field is missing at runtime | ||
| */ | ||
| buildFrozen() { | ||
| const missing = this.validateRequiredFields(); | ||
| if (missing.length > 0) { | ||
| throw new Error(`Missing required fields: ${missing.join(", ")}. Please set these fields before calling build.`); | ||
| } | ||
| return Object.freeze(this._actual); | ||
| } | ||
| /** | ||
| * Builds and deeply freezes the final object with both compile-time and runtime validation. | ||
| * The returned object is recursively frozen - all nested objects and arrays are also frozen. | ||
| * | ||
| * - Compile-time: TypeScript enforces all required properties are set | ||
| * - Runtime: Validates all fields in the requiredTemplate and recursively applies Object.freeze() | ||
| * | ||
| * @returns The deeply frozen object of type DeepReadonly<T> | ||
| * @throws {Error} If any required field is missing at runtime | ||
| */ | ||
| buildDeepFrozen() { | ||
| const missing = this.validateRequiredFields(); | ||
| if (missing.length > 0) { | ||
| throw new Error(`Missing required fields: ${missing.join(", ")}. Please set these fields before calling build.`); | ||
| } | ||
| return deepFreeze(this._actual); | ||
| } | ||
| /** | ||
| * Builds and seals the final object with both compile-time and runtime validation. | ||
| * The returned object is shallowly sealed - properties cannot be added or removed, | ||
| * but existing properties can still be modified. Nested objects remain unsealed. | ||
| * | ||
| * - Compile-time: TypeScript enforces all required properties are set | ||
| * - Runtime: Validates all fields in the requiredTemplate and applies Object.seal() | ||
| * | ||
| * @returns The sealed object of type T | ||
| * @throws {Error} If any required field is missing at runtime | ||
| */ | ||
| buildSealed() { | ||
| const missing = this.validateRequiredFields(); | ||
| if (missing.length > 0) { | ||
| throw new Error(`Missing required fields: ${missing.join(", ")}. Please set these fields before calling build.`); | ||
| } | ||
| return Object.seal(this._actual); | ||
| } | ||
| /** | ||
| * Builds and deeply seals the final object with both compile-time and runtime validation. | ||
| * The returned object is recursively sealed - properties cannot be added or removed at any level, | ||
| * but existing properties can still be modified. | ||
| * | ||
| * - Compile-time: TypeScript enforces all required properties are set | ||
| * - Runtime: Validates all fields in the requiredTemplate and recursively applies Object.seal() | ||
| * | ||
| * @returns The deeply sealed object of type T | ||
| * @throws {Error} If any required field is missing at runtime | ||
| */ | ||
| buildDeepSealed() { | ||
| const missing = this.validateRequiredFields(); | ||
| if (missing.length > 0) { | ||
| throw new Error(`Missing required fields: ${missing.join(", ")}. Please set these fields before calling build.`); | ||
| } | ||
| return deepSeal(this._actual); | ||
| } | ||
| }; | ||
@@ -272,0 +361,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 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":";AAmFO,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":[]} | ||
| {"version":3,"sources":["../src/cerios-builder.ts"],"sourcesContent":["/**\n * Unique symbol used internally to brand types and track which properties have been set in the builder's type.\n *\n * @internal\n */\ndeclare const __brand: unique symbol;\n\n/**\n * Type utility for branding builder types with information about which properties have been set.\n * This is used to enforce compile-time safety for required fields in the builder pattern.\n *\n * @template T - The type representing the set of properties that have been set\n * @internal\n */\nexport type CeriosBrand<T> = { [__brand]: T };\n\n/**\n * Helper type to represent a path through an object structure\n * Handles optional properties by unwrapping them with NonNullable\n */\ntype PathImpl<T, K extends keyof T = keyof T> = K extends string | number\n\t? NonNullable<T[K]> extends Record<string, any>\n\t\t? NonNullable<T[K]> extends Array<any>\n\t\t\t? K\n\t\t\t: K | `${K}.${PathImpl<NonNullable<T[K]>> & string}`\n\t\t: K\n\t: never;\n\nexport type Path<T> = PathImpl<T>;\n\n/**\n * Helper type to get the value at a specific path, handling optional properties\n */\ntype PathValue<T, P> = P extends keyof T\n\t? T[P]\n\t: P extends `${infer K}.${infer Rest}`\n\t\t? K extends keyof T\n\t\t\t? PathValue<NonNullable<T[K]>, Rest>\n\t\t\t: never\n\t\t: never;\n\n/**\n * Type-safe template for defining required fields using an array of paths.\n * Simply list the paths that are required.\n */\nexport type RequiredFieldsTemplate<T> = ReadonlyArray<Path<T>>;\n\n/**\n * Recursively makes all properties readonly for deep immutability.\n * Handles arrays, objects, and primitive types.\n *\n * @template T - The type to make deeply readonly\n */\nexport type DeepReadonly<T> = T extends (infer R)[]\n\t? DeepReadonlyArray<R>\n\t: T extends (...args: any[]) => any\n\t\t? T\n\t\t: T extends object\n\t\t\t? DeepReadonlyObject<T>\n\t\t\t: T;\n\n/**\n * Helper type for deep readonly arrays\n * @internal\n */\ninterface DeepReadonlyArray<T> extends ReadonlyArray<DeepReadonly<T>> {}\n\n/**\n * Helper type for deep readonly objects\n * @internal\n */\ntype DeepReadonlyObject<T> = {\n\treadonly [P in keyof T]: DeepReadonly<T[P]>;\n};\n\n/**\n * Cache the root key extraction to avoid repeated computation\n * Handles optional properties by ensuring the key is valid for T\n * @internal\n */\ntype RootKey<P extends string, T = any> = P extends `${infer K}.${string}`\n\t? K extends keyof T\n\t\t? K\n\t\t: never\n\t: P extends keyof T\n\t\t? P\n\t\t: never;\n\n/**\n * Recursively freezes an object and all its nested properties.\n * @param obj - The object to freeze\n * @returns The frozen object\n * @internal\n */\nfunction deepFreeze<T>(obj: T): T {\n\t// Retrieve the property names defined on obj\n\tObject.getOwnPropertyNames(obj).forEach(prop => {\n\t\tconst value = (obj as any)[prop];\n\n\t\t// Freeze properties before freezing self\n\t\tif (value !== null && (typeof value === \"object\" || typeof value === \"function\")) {\n\t\t\tdeepFreeze(value);\n\t\t}\n\t});\n\n\treturn Object.freeze(obj);\n}\n\n/**\n * Recursively seals an object and all its nested properties.\n * @param obj - The object to seal\n * @returns The sealed object\n * @internal\n */\nfunction deepSeal<T>(obj: T): T {\n\t// Retrieve the property names defined on obj\n\tObject.getOwnPropertyNames(obj).forEach(prop => {\n\t\tconst value = (obj as any)[prop];\n\n\t\t// Seal properties before sealing self\n\t\tif (value !== null && (typeof value === \"object\" || typeof value === \"function\")) {\n\t\t\tdeepSeal(value);\n\t\t}\n\t});\n\n\treturn Object.seal(obj);\n}\n\n/**\n * Abstract base class for creating type-safe builders with automatic property setters and compile-time validation of required fields.\n *\n * This class is intended to be extended by concrete builder implementations for your own types.\n * It provides utility methods for setting properties and building the final object, ensuring that all required fields are set at compile time.\n *\n * Example usage:\n * ```typescript\n * interface MyType { foo: string; bar: number[]; }\n * class MyTypeBuilder extends CeriosBuilder<MyType> {\n * static requiredTemplate: RequiredFieldsTemplate<MyType> = ['foo'];\n * setFoo(value: string) { return this.setProperty('foo', value); }\n * addBar(value: number) { return this.addToArrayProperty('bar', value); }\n * }\n * // Usage:\n * const obj = new MyTypeBuilder({})\n * .setFoo('hello')\n * .addBar(42)\n * .buildSafe(); // Validates that 'foo' is set\n * ```\n *\n * @template T - The complete type being built\n */\nexport abstract class CeriosBuilder<T extends object> {\n\t/**\n\t * Template defining which fields are required for this builder.\n\t * Subclasses should override this to specify their required fields as an array of paths.\n\t * The template is type-safe - only valid paths from type T can be used.\n\t */\n\tstatic requiredTemplate?: ReadonlyArray<string>;\n\n\t/**\n\t * Instance-level required fields that can be populated dynamically.\n\t * This allows adding required fields at runtime via the setRequiredFields method.\n\t * @private\n\t */\n\tprivate _requiredFields: Set<string> = new Set();\n\n\t/**\n\t * Sets the required fields for this builder instance.\n\t * This allows you to dynamically define which fields are required.\n\t *\n\t * @param fields - Array of dot-notation paths to required fields\n\t * @returns The builder instance for chaining\n\t *\n\t * @example\n\t * ```typescript\n\t * const builder = new MyBuilder({})\n\t * .setRequiredFields(['path.to.field1', 'path.to.field2'])\n\t * .setField1('value1')\n\t * .setField2('value2')\n\t * .buildSafe();\n\t * ```\n\t */\n\tsetRequiredFields(fields: ReadonlyArray<Path<T>>): this {\n\t\tthis._requiredFields = new Set([...fields] as string[]);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Gets the combined required fields from both the static template and instance-level fields.\n\t * @private\n\t */\n\tprivate getRequiredTemplate(): ReadonlyArray<string> {\n\t\tconst ctor = this.constructor as typeof CeriosBuilder;\n\t\tconst staticFields = ctor.requiredTemplate || [];\n\t\tconst instanceFields = Array.from(this._requiredFields);\n\n\t\t// Combine and deduplicate\n\t\treturn [...new Set([...staticFields, ...instanceFields])];\n\t}\n\n\t/**\n\t * Validates that all fields in the required template have been set.\n\t * @private\n\t */\n\tprivate validateRequiredFields(): string[] {\n\t\tconst requiredPaths = this.getRequiredTemplate();\n\t\tconst missing: string[] = [];\n\n\t\tfor (const path of requiredPaths) {\n\t\t\tconst keys = path.split(\".\");\n\t\t\tlet current: any = this._actual;\n\n\t\t\tfor (let i = 0; i < keys.length; i++) {\n\t\t\t\tconst key = keys[i];\n\t\t\t\tif (current === null || current === undefined || !(key in current)) {\n\t\t\t\t\tmissing.push(path);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcurrent = current[key];\n\t\t\t}\n\n\t\t\t// Check if the final value is null or undefined\n\t\t\tif (current === null || current === undefined) {\n\t\t\t\tif (!missing.includes(path)) {\n\t\t\t\t\tmissing.push(path);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn missing;\n\t}\n\n\t/**\n\t * Creates a new builder instance. Intended to be called by subclasses.\n\t *\n\t * @param _actual - The current partial state of the object being built\n\t * @param _requiredFields - Optional array of required field paths to preserve across instances\n\t * @protected\n\t */\n\tprotected constructor(\n\t\tprotected readonly _actual: Partial<T>,\n\t\t_requiredFields?: RequiredFieldsTemplate<T>\n\t) {\n\t\tif (_requiredFields) {\n\t\t\tthis._requiredFields = new Set([..._requiredFields] as string[]);\n\t\t}\n\t}\n\n\t/**\n\t * Sets a property value and returns a new builder instance with updated type state.\n\t * This method is intended to be wrapped by concrete builder methods in subclasses.\n\t *\n\t * @template K - The property key being set\n\t * @param key - The property key to set\n\t * @param value - The value to assign to the property\n\t * @returns A new builder instance with the property set and type state updated\n\t * @protected\n\t */\n\tprotected setProperty<K extends keyof T>(key: K, value: T[K]): this & CeriosBrand<Pick<T, K>> {\n\t\tconst BuilderClass = this.constructor as new (data: any, requiredFields?: RequiredFieldsTemplate<T>) => any;\n\t\treturn new BuilderClass(\n\t\t\t{\n\t\t\t\t...this._actual,\n\t\t\t\t[key]: value,\n\t\t\t},\n\t\t\tArray.from(this._requiredFields) as unknown as RequiredFieldsTemplate<T>\n\t\t) as this & CeriosBrand<Pick<T, K>>;\n\t}\n\n\t/**\n\t * Sets multiple property values at once and returns a new builder instance with updated type state.\n\t * @param props - An object with one or more properties to set.\n\t * @returns A new builder instance with the properties set and type state updated.\n\t * @protected\n\t */\n\tprotected setProperties<K extends keyof T>(props: Pick<T, K>): this & CeriosBrand<Pick<T, K>> {\n\t\tconst BuilderClass = this.constructor as new (data: any, requiredFields?: RequiredFieldsTemplate<T>) => any;\n\t\treturn new BuilderClass(\n\t\t\t{\n\t\t\t\t...this._actual,\n\t\t\t\t...props,\n\t\t\t},\n\t\t\tArray.from(this._requiredFields) as unknown as RequiredFieldsTemplate<T>\n\t\t) as this & CeriosBrand<Pick<T, K>>;\n\t}\n\n\t/**\n\t * Sets a deeply nested property value and returns a new builder instance with updated type state.\n\t * This method uses dot notation to set nested properties in a type-safe way.\n\t *\n\t * @template P - The property path (e.g., \"parent.child.grandchild\")\n\t * @param path - The dot-notation path to the property\n\t * @param value - The value to assign to the nested property\n\t * @returns A new builder instance with the nested property set\n\t * @protected\n\t *\n\t * @example\n\t * ```typescript\n\t * builder.setNestedProperty('Order.Details.CustomerId', 'value')\n\t * ```\n\t */\n\tprotected setNestedProperty<P extends Path<T>>(\n\t\tpath: P,\n\t\tvalue: PathValue<T, P>\n\t): this & CeriosBrand<Pick<T, RootKey<P & string, T> extends never ? keyof T : RootKey<P & string, T>>> {\n\t\tconst BuilderClass = this.constructor as new (data: any, requiredFields?: RequiredFieldsTemplate<T>) => any;\n\t\tconst keys = (path as string).split(\".\");\n\t\tconst newActual = this.deepClone(this._actual);\n\n\t\tlet current: any = newActual;\n\t\tfor (let i = 0; i < keys.length - 1; i++) {\n\t\t\tconst key = keys[i];\n\t\t\tif (!(key in current) || typeof current[key] !== \"object\" || current[key] === null) {\n\t\t\t\tcurrent[key] = {};\n\t\t\t} else {\n\t\t\t\tcurrent[key] = this.deepClone(current[key]);\n\t\t\t}\n\t\t\tcurrent = current[key];\n\t\t}\n\n\t\tcurrent[keys[keys.length - 1]] = value;\n\n\t\treturn new BuilderClass(\n\t\t\tnewActual,\n\t\t\tArray.from(this._requiredFields) as unknown as RequiredFieldsTemplate<T>\n\t\t) as this & CeriosBrand<Pick<T, RootKey<P & string, T> extends never ? keyof T : RootKey<P & string, T>>>;\n\t}\n\n\t/**\n\t * Deep clone helper for nested objects\n\t * @private\n\t */\n\tprivate deepClone<V>(obj: V): V {\n\t\tif (obj === null || typeof obj !== \"object\") {\n\t\t\treturn obj;\n\t\t}\n\t\tif (Array.isArray(obj)) {\n\t\t\treturn obj.map(item => this.deepClone(item)) as any;\n\t\t}\n\t\tconst cloned: any = {};\n\t\tconst hasOwn = Object.prototype.hasOwnProperty;\n\t\tfor (const key in obj) {\n\t\t\tif (hasOwn.call(obj, key)) {\n\t\t\t\tcloned[key] = this.deepClone(obj[key]);\n\t\t\t}\n\t\t}\n\t\treturn cloned;\n\t}\n\n\t/**\n\t * Adds a value to an array property and returns a new builder instance with updated type state.\n\t * This method is intended to be wrapped by concrete builder methods in subclasses for array properties.\n\t *\n\t * @template K - The property key (must be an array property)\n\t * @template V - The type of the array element\n\t * @param key - The array property key to add to\n\t * @param value - The value to add to the array\n\t * @returns A new builder instance with the array property updated and type state updated\n\t * @protected\n\t */\n\tprotected addToArrayProperty<\n\t\tK extends { [P in keyof T]: NonNullable<T[P]> extends Array<any> ? P : never }[keyof T],\n\t\tV extends T[K] extends Array<infer U> ? U : T[K] extends Array<infer U> | undefined ? U : never,\n\t>(key: K, value: V): this & CeriosBrand<Pick<T, K>> {\n\t\tconst BuilderClass = this.constructor as new (data: any, requiredFields?: RequiredFieldsTemplate<T>) => any;\n\t\tconst currentArray = (this._actual[key] as Array<V> | undefined) ?? [];\n\t\treturn new BuilderClass(\n\t\t\t{\n\t\t\t\t...this._actual,\n\t\t\t\t[key]: [...currentArray, value],\n\t\t\t},\n\t\t\tArray.from(this._requiredFields) as unknown as RequiredFieldsTemplate<T>\n\t\t) as this & CeriosBrand<Pick<T, K>>;\n\t}\n\n\t/**\n\t * Builds the final object with both compile-time and runtime validation.\n\t * This is the recommended and safest way to build objects.\n\t *\n\t * - Compile-time: TypeScript enforces all required properties are set\n\t * - Runtime: Validates all fields in the requiredTemplate\n\t *\n\t * @returns The fully built object of type T\n\t * @throws {Error} If any required field is missing at runtime\n\t */\n\tbuild(this: this & CeriosBrand<T>): T {\n\t\tconst missing = this.validateRequiredFields();\n\n\t\tif (missing.length > 0) {\n\t\t\tthrow new Error(`Missing required fields: ${missing.join(\", \")}. Please set these fields before calling build.`);\n\t\t}\n\n\t\treturn this._actual as T;\n\t}\n\n\t/**\n\t * Builds the final object with only compile-time validation, skipping runtime checks.\n\t * Use this when you want TypeScript safety but need to skip runtime validation for performance.\n\t *\n\t * - Compile-time: TypeScript enforces all required properties are set\n\t * - Runtime: No validation\n\t *\n\t * @returns The fully built object of type T\n\t */\n\tbuildWithoutRuntimeValidation(this: this & CeriosBrand<T>): T {\n\t\treturn this._actual as T;\n\t}\n\n\t/**\n\t * Builds the final object with only runtime validation, skipping compile-time checks.\n\t * Use this when building from external data where compile-time checks aren't possible.\n\t *\n\t * - Compile-time: No TypeScript enforcement\n\t * - Runtime: Validates all fields in the requiredTemplate\n\t *\n\t * @returns The fully built object of type T\n\t * @throws {Error} If any required field is missing at runtime\n\t */\n\tbuildWithoutCompileTimeValidation(): T {\n\t\tconst missing = this.validateRequiredFields();\n\n\t\tif (missing.length > 0) {\n\t\t\tthrow new Error(`Missing required fields: ${missing.join(\", \")}. Please set these fields before calling build.`);\n\t\t}\n\n\t\treturn this._actual as T;\n\t}\n\n\t/**\n\t * Builds the final object without any validation (neither compile-time nor runtime).\n\t * Use this only when you're certain the object is valid and need maximum performance.\n\t *\n\t * - Compile-time: No TypeScript enforcement\n\t * - Runtime: No validation\n\t *\n\t * @returns The object of type T (may be incomplete)\n\t */\n\tbuildUnsafe(): T {\n\t\treturn this._actual as T;\n\t}\n\n\t/**\n\t * Builds a partial object, which may not have all required fields set.\n\t * This is useful for scenarios where you want to inspect or validate the current state before finalizing.\n\t *\n\t * @returns The partially built object\n\t */\n\tbuildPartial(): Partial<T> {\n\t\treturn this._actual as Partial<T>;\n\t}\n\n\t/**\n\t * @deprecated Use build() instead. buildSafe() is now an alias for build().\n\t * Builds the final object with runtime validation using the requiredTemplate.\n\t * This method validates that all fields in the requiredTemplate array are present.\n\t *\n\t * @returns The fully built object of type T\n\t * @throws {Error} If any required field is missing at runtime\n\t */\n\tbuildSafe(): T {\n\t\treturn this.buildWithoutCompileTimeValidation();\n\t}\n\n\t/**\n\t * Builds and freezes the final object with both compile-time and runtime validation.\n\t * The returned object is shallowly frozen - top-level properties cannot be modified,\n\t * but nested objects remain mutable.\n\t *\n\t * - Compile-time: TypeScript enforces all required properties are set\n\t * - Runtime: Validates all fields in the requiredTemplate and applies Object.freeze()\n\t *\n\t * @returns The frozen object of type Readonly<T>\n\t * @throws {Error} If any required field is missing at runtime\n\t */\n\tbuildFrozen(this: this & CeriosBrand<T>): Readonly<T> {\n\t\tconst missing = this.validateRequiredFields();\n\n\t\tif (missing.length > 0) {\n\t\t\tthrow new Error(`Missing required fields: ${missing.join(\", \")}. Please set these fields before calling build.`);\n\t\t}\n\n\t\treturn Object.freeze(this._actual as T);\n\t}\n\n\t/**\n\t * Builds and deeply freezes the final object with both compile-time and runtime validation.\n\t * The returned object is recursively frozen - all nested objects and arrays are also frozen.\n\t *\n\t * - Compile-time: TypeScript enforces all required properties are set\n\t * - Runtime: Validates all fields in the requiredTemplate and recursively applies Object.freeze()\n\t *\n\t * @returns The deeply frozen object of type DeepReadonly<T>\n\t * @throws {Error} If any required field is missing at runtime\n\t */\n\tbuildDeepFrozen(this: this & CeriosBrand<T>): DeepReadonly<T> {\n\t\tconst missing = this.validateRequiredFields();\n\n\t\tif (missing.length > 0) {\n\t\t\tthrow new Error(`Missing required fields: ${missing.join(\", \")}. Please set these fields before calling build.`);\n\t\t}\n\n\t\treturn deepFreeze(this._actual as T) as DeepReadonly<T>;\n\t}\n\n\t/**\n\t * Builds and seals the final object with both compile-time and runtime validation.\n\t * The returned object is shallowly sealed - properties cannot be added or removed,\n\t * but existing properties can still be modified. Nested objects remain unsealed.\n\t *\n\t * - Compile-time: TypeScript enforces all required properties are set\n\t * - Runtime: Validates all fields in the requiredTemplate and applies Object.seal()\n\t *\n\t * @returns The sealed object of type T\n\t * @throws {Error} If any required field is missing at runtime\n\t */\n\tbuildSealed(this: this & CeriosBrand<T>): T {\n\t\tconst missing = this.validateRequiredFields();\n\n\t\tif (missing.length > 0) {\n\t\t\tthrow new Error(`Missing required fields: ${missing.join(\", \")}. Please set these fields before calling build.`);\n\t\t}\n\n\t\treturn Object.seal(this._actual as T);\n\t}\n\n\t/**\n\t * Builds and deeply seals the final object with both compile-time and runtime validation.\n\t * The returned object is recursively sealed - properties cannot be added or removed at any level,\n\t * but existing properties can still be modified.\n\t *\n\t * - Compile-time: TypeScript enforces all required properties are set\n\t * - Runtime: Validates all fields in the requiredTemplate and recursively applies Object.seal()\n\t *\n\t * @returns The deeply sealed object of type T\n\t * @throws {Error} If any required field is missing at runtime\n\t */\n\tbuildDeepSealed(this: this & CeriosBrand<T>): T {\n\t\tconst missing = this.validateRequiredFields();\n\n\t\tif (missing.length > 0) {\n\t\t\tthrow new Error(`Missing required fields: ${missing.join(\", \")}. Please set these fields before calling build.`);\n\t\t}\n\n\t\treturn deepSeal(this._actual as T);\n\t}\n}\n"],"mappings":"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","names":[]} |
+1
-1
| { | ||
| "name": "@cerios/cerios-builder", | ||
| "version": "1.3.0", | ||
| "version": "1.4.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", |
+340
-28
@@ -10,2 +10,3 @@ # @cerios/cerios-builder | ||
| - **Flexible Runtime Validation**: Choose between compile-time, runtime, or both validations | ||
| - **Immutability Options**: Create frozen or sealed objects with shallow or deep variants | ||
| - **Dynamic Required Fields**: Add required fields at runtime based on your business logic | ||
@@ -90,2 +91,6 @@ - **Method Chaining**: Fluent API for readable object construction | ||
| | **Partial Build** | `buildPartial()` | Build incomplete objects | | ||
| | **Shallow Freeze** | `buildFrozen()` | Create immutable object (top-level only) | | ||
| | **Deep Freeze** | `buildDeepFrozen()` | Create fully immutable object tree | | ||
| | **Shallow Seal** | `buildSealed()` | Lock structure, allow modifications | | ||
| | **Deep Seal** | `buildDeepSealed()` | Lock all structures, allow modifications | | ||
@@ -688,2 +693,10 @@ ## ๐ง Basic Usage | ||
| // NEW: Create with ALL required fields set | ||
| static createComplete() { | ||
| return this.create() | ||
| .street("123 Default Street") | ||
| .city("Othertown") | ||
| .country("United States"); | ||
| } | ||
| street(value: string) { | ||
@@ -735,2 +748,3 @@ return this.setProperty('street', value); | ||
| // Pattern 1: No defaults - must set all required fields | ||
| withAddress(builderFn: (builder: AddressBuilder) => AddressBuilder & CeriosBrand<Address>) { | ||
@@ -741,2 +755,3 @@ const address = builderFn(AddressBuilder.create()).build(); | ||
| // Pattern 2: Partial defaults - must set remaining required fields (street) | ||
| withAddressDefaults( | ||
@@ -750,6 +765,17 @@ builderFn: ( | ||
| } | ||
| // Pattern 3: Complete defaults - callback is OPTIONAL | ||
| withCompleteAddress( | ||
| builderFn?: ( | ||
| builder: AddressBuilder & CeriosBrand<Address> | ||
| ) => AddressBuilder & CeriosBrand<Address> | ||
| ) { | ||
| const builder = AddressBuilder.createComplete(); | ||
| const address = builderFn ? builderFn(builder).build() : builder.build(); | ||
| return this.setProperty('address', address); | ||
| } | ||
| } | ||
| // Usage with full address | ||
| const customer = CustomerBuilder.create() | ||
| // Pattern 1: No defaults - must set all required fields | ||
| const customer1 = CustomerBuilder.create() | ||
| .id('CUST-001') | ||
@@ -764,8 +790,7 @@ .name('Alice Johnson') | ||
| .addNote("VIP customer") | ||
| .addNote("Prefers email contact") | ||
| .phoneNumber('+1-555-0123') | ||
| .build(); | ||
| // Usage with defaults | ||
| const customerWithDefaults = CustomerBuilder.create() | ||
| // Pattern 2: Partial defaults - city and country already set, must provide street | ||
| const customer2 = CustomerBuilder.create() | ||
| .id('CUST-002') | ||
@@ -777,21 +802,72 @@ .name('Bob Smith') | ||
| console.log(customerWithDefaults); | ||
| console.log(customer2); | ||
| // Output: { id: 'CUST-002', name: 'Bob Smith', address: { street: '456 Elm St', city: 'Othertown', country: 'United States' }, notes: ['New customer'] } | ||
| // Pattern 3a: Complete defaults - no callback needed, use all defaults | ||
| const customer3 = CustomerBuilder.create() | ||
| .id('CUST-003') | ||
| .name('Charlie Davis') | ||
| .withCompleteAddress() // โ No callback needed - all required fields already set! | ||
| .build(); | ||
| console.log(customer3); | ||
| // Output: { id: 'CUST-003', name: 'Charlie Davis', address: { street: '123 Default Street', city: 'Othertown', country: 'United States' } } | ||
| // Pattern 3b: Complete defaults - optional callback to modify/add optional fields | ||
| const customer4 = CustomerBuilder.create() | ||
| .id('CUST-004') | ||
| .name('Diana Evans') | ||
| .withCompleteAddress(addr => addr | ||
| .zipCode('90210') // Only modify/add optional fields if needed | ||
| ) | ||
| .build(); | ||
| console.log(customer4); | ||
| // Output: { id: 'CUST-004', name: 'Diana Evans', address: { street: '123 Default Street', city: 'Othertown', country: 'United States', zipCode: '90210' } } | ||
| // Pattern 3c: Complete defaults - optional callback to override any field | ||
| const customer5 = CustomerBuilder.create() | ||
| .id('CUST-005') | ||
| .name('Eve Foster') | ||
| .withCompleteAddress(addr => addr | ||
| .street('789 Custom Ave') // Override the default street | ||
| .zipCode('10001') | ||
| ) | ||
| .addNote("Premium customer") | ||
| .build(); | ||
| console.log(customer5); | ||
| // Output: { id: 'CUST-005', name: 'Eve Foster', address: { street: '789 Custom Ave', city: 'Othertown', country: 'United States', zipCode: '10001' }, notes: ['Premium customer'] } | ||
| ``` | ||
| ### Partial Building for Flexibility | ||
| #### Using `createWithDefaults()` with Nested Builders | ||
| Sometimes you need to build incomplete objects: | ||
| The key pattern is: | ||
| 1. **`createComplete()`**: Returns a builder with **all required fields already set** | ||
| 2. **Optional callback parameter**: The callback parameter is typed as `optional` (`?`), so you don't have to provide it | ||
| 3. **Already branded**: Since all required fields are set, the builder is already branded with `CeriosBrand<Address>`, so you can call `build()` immediately | ||
| This pattern is perfect when: | ||
| - You have sensible defaults for all required fields | ||
| - Most users will use the defaults | ||
| - Some users may want to customize optional fields or override defaults | ||
| - You want a clean API without forcing users to write empty callbacks | ||
| **Comparison of the three patterns:** | ||
| ```typescript | ||
| // Build partial objects when not all data is available | ||
| const partialUser = UserBuilder.create() | ||
| .name("Unknown User") | ||
| .addRole("guest") | ||
| .buildPartial(); // Returns Partial<User> | ||
| // Pattern 1: Must set ALL required fields | ||
| .withAddress(addr => addr.street('...').city('...').country('...')) | ||
| // This is useful for: | ||
| // - Progressive form filling | ||
| // - API responses with optional fields | ||
| // - Template objects | ||
| // Pattern 2: Must set REMAINING required fields (street) | ||
| .withAddressDefaults(addr => addr.street('...')) | ||
| // Pattern 3a: NO callback needed - all defaults | ||
| .withCompleteAddress() | ||
| // Pattern 3b: OPTIONAL callback - only if you want to customize | ||
| .withCompleteAddress(addr => addr.zipCode('...')) | ||
| // Pattern 3c: OPTIONAL callback - can override any field | ||
| .withCompleteAddress(addr => addr.street('...').zipCode('...')) | ||
| ``` | ||
@@ -1116,4 +1192,248 @@ | ||
| ## ๐ก Best Practices | ||
| ## ๏ฟฝ Immutable Build Methods | ||
| In addition to the standard build methods, `@cerios/cerios-builder` provides **frozen** and **sealed** variants that create immutable objects. These methods help prevent accidental mutations and enforce data integrity. | ||
| ### Understanding Freeze vs Seal | ||
| Before diving into the methods, it's important to understand the difference between **frozen** and **sealed** objects: | ||
| | Feature | `Object.freeze()` | `Object.seal()` | | ||
| |---------|------------------|-----------------| | ||
| | **Add properties** | โ Prevented | โ Prevented | | ||
| | **Delete properties** | โ Prevented | โ Prevented | | ||
| | **Modify properties** | โ Prevented | โ Allowed | | ||
| | **Use case** | Complete immutability | Prevent structural changes | | ||
| **Shallow vs Deep:** | ||
| - **Shallow**: Only applies to the top-level object | ||
| - **Deep**: Recursively applies to all nested objects and arrays | ||
| ### `buildFrozen()` - Shallow Freeze | ||
| Creates a **shallowly frozen** object where top-level properties cannot be modified, but nested objects remain mutable. | ||
| ```typescript | ||
| const user = UserBuilder.create() | ||
| .id('123') | ||
| .name('John') | ||
| .email('john@example.com') | ||
| .buildFrozen(); // Returns Readonly<User> | ||
| // โ Error: Cannot modify top-level properties | ||
| user.name = 'Jane'; // TypeError in strict mode | ||
| // โ ๏ธ Nested objects can still be modified (shallow freeze) | ||
| user.address.city = 'New York'; // Works if address is an object | ||
| ``` | ||
| **Use when:** | ||
| - You want to prevent accidental top-level modifications | ||
| - Nested objects are intentionally mutable | ||
| - Performance is a concern (shallow operations are faster) | ||
| ### `buildDeepFrozen()` - Deep Freeze | ||
| Creates a **deeply frozen** object where all properties at all levels are immutable. | ||
| ```typescript | ||
| const user = UserBuilder.create() | ||
| .id('123') | ||
| .name('John') | ||
| .email('john@example.com') | ||
| .setNestedProperty('address.city', 'Boston') | ||
| .buildDeepFrozen(); // Returns DeepReadonly<User> | ||
| // โ Error: Cannot modify top-level properties | ||
| user.name = 'Jane'; // TypeError | ||
| // โ Error: Cannot modify nested properties | ||
| user.address.city = 'New York'; // TypeError | ||
| // โ Error: Cannot modify arrays | ||
| user.tags.push('new tag'); // TypeError | ||
| ``` | ||
| **Use when:** | ||
| - You need complete immutability | ||
| - Building configuration objects | ||
| - Creating frozen state snapshots | ||
| - Working with Redux/immutable state patterns | ||
| - Passing objects to untrusted code | ||
| ### `buildSealed()` - Shallow Seal | ||
| Creates a **shallowly sealed** object where properties cannot be added or removed, but existing properties can be modified. | ||
| ```typescript | ||
| const user = UserBuilder.create() | ||
| .id('123') | ||
| .name('John') | ||
| .email('john@example.com') | ||
| .buildSealed(); // Returns T | ||
| // โ Can modify existing properties | ||
| user.name = 'Jane'; // Works! | ||
| // โ Cannot add new properties | ||
| user.newProp = 'value'; // TypeError in strict mode | ||
| // โ Cannot delete properties | ||
| delete user.email; // TypeError in strict mode | ||
| // โ ๏ธ Can modify nested objects (shallow seal) | ||
| user.address.city = 'New York'; // Works if address is an object | ||
| ``` | ||
| **Use when:** | ||
| - You want to lock the object structure | ||
| - Properties should be modifiable | ||
| - Preventing accidental property additions | ||
| - Building objects with fixed schemas | ||
| ### `buildDeepSealed()` - Deep Seal | ||
| Creates a **deeply sealed** object where no properties can be added or removed at any level, but all properties can still be modified. | ||
| ```typescript | ||
| const user = UserBuilder.create() | ||
| .id('123') | ||
| .name('John') | ||
| .email('john@example.com') | ||
| .setNestedProperty('address.city', 'Boston') | ||
| .buildDeepSealed(); // Returns T | ||
| // โ Can modify properties at all levels | ||
| user.name = 'Jane'; // Works | ||
| user.address.city = 'New York'; // Works | ||
| // โ Cannot add properties at any level | ||
| user.newProp = 'value'; // TypeError | ||
| user.address.newProp = 'value'; // TypeError | ||
| // โ Cannot add array elements | ||
| user.tags.push('new tag'); // TypeError | ||
| // โ Can modify array elements | ||
| user.tags[0] = 'updated tag'; // Works | ||
| ``` | ||
| **Use when:** | ||
| - You want to lock all object structures | ||
| - Values should be modifiable | ||
| - Preventing structural changes throughout the tree | ||
| - Building objects with deeply fixed schemas | ||
| ### Immutability Comparison | ||
| | Method | Modify Top-Level | Modify Nested | Add Properties | Delete Properties | Performance | | ||
| |--------|-----------------|---------------|----------------|-------------------|-------------| | ||
| | `buildFrozen()` | โ | โ | โ | โ | Fast | | ||
| | `buildDeepFrozen()` | โ | โ | โ | โ | Slower | | ||
| | `buildSealed()` | โ | โ | โ | โ | Fast | | ||
| | `buildDeepSealed()` | โ | โ | โ | โ | Slower | | ||
| ### Immutability Examples | ||
| #### Example 1: Configuration Object | ||
| ```typescript | ||
| class ConfigBuilder extends CeriosBuilder<AppConfig> { | ||
| static requiredTemplate: RequiredFieldsTemplate<AppConfig> = [ | ||
| 'apiUrl', | ||
| 'timeout', | ||
| 'features.authentication' | ||
| ]; | ||
| static create() { | ||
| return new ConfigBuilder({}); | ||
| } | ||
| // ... builder methods | ||
| } | ||
| const config = ConfigBuilder.create() | ||
| .setApiUrl('https://api.example.com') | ||
| .setTimeout(5000) | ||
| .setNestedProperty('features.authentication', true) | ||
| .buildDeepFrozen(); // Configuration should never change | ||
| // โ Configuration is completely frozen | ||
| config.timeout = 10000; // TypeError | ||
| config.features.authentication = false; // TypeError | ||
| ``` | ||
| #### Example 2: Mutable Data with Fixed Structure | ||
| ```typescript | ||
| class UserStateBuilder extends CeriosBuilder<UserState> { | ||
| static create() { | ||
| return new UserStateBuilder({}); | ||
| } | ||
| // ... builder methods | ||
| } | ||
| const userState = UserStateBuilder.create() | ||
| .setUserId('123') | ||
| .setStatus('active') | ||
| .setLastSeen(new Date()) | ||
| .buildDeepSealed(); // Structure is fixed, but values can change | ||
| // โ Can update state values | ||
| userState.status = 'inactive'; | ||
| userState.lastSeen = new Date(); | ||
| // โ Cannot add new properties | ||
| userState.newField = 'value'; // TypeError | ||
| ``` | ||
| #### Example 3: Snapshot for Time Travel Debugging | ||
| ```typescript | ||
| class AppStateBuilder extends CeriosBuilder<AppState> { | ||
| // ... methods | ||
| } | ||
| const snapshots: DeepReadonly<AppState>[] = []; | ||
| // Capture immutable snapshots | ||
| snapshots.push( | ||
| AppStateBuilder.create() | ||
| .setCurrentUser(user) | ||
| .setOpenDocuments(documents) | ||
| .buildDeepFrozen() | ||
| ); | ||
| // Snapshots cannot be modified | ||
| snapshots[0].currentUser.name = 'Changed'; // TypeError | ||
| ``` | ||
| ### When to Use Immutability | ||
| **Use `buildDeepFrozen()`:** | ||
| - โ Configuration objects | ||
| - โ State snapshots | ||
| - โ Event data | ||
| - โ Cached results | ||
| - โ Objects passed to untrusted code | ||
| **Use `buildDeepSealed()`:** | ||
| - โ State objects with fixed schemas | ||
| - โ Form data models | ||
| - โ Database records with fixed columns | ||
| - โ API response models | ||
| **Use shallow variants (`buildFrozen()`, `buildSealed()`):** | ||
| - โ When nested objects need flexibility | ||
| - โ Performance-critical scenarios | ||
| - โ Intentional selective mutability | ||
| **Avoid immutable builds when:** | ||
| - โ Building frequently updated objects | ||
| - โ Working with large data structures (performance cost) | ||
| - โ Objects that need frequent transformations | ||
| ## ๏ฟฝ๐ก Best Practices | ||
| 1. **Use `setNestedProperty()` for deep structures**: Instead of building nested objects separately, use dot notation for better type safety and cleaner code. | ||
@@ -1128,2 +1448,4 @@ | ||
| - Use `buildUnsafe()` only when absolutely necessary | ||
| - Use `buildDeepFrozen()` for configuration and immutable data | ||
| - Use `buildDeepSealed()` when you need fixed schemas with mutable values | ||
@@ -1156,11 +1478,1 @@ 4. **Use `setRequiredFields()` for conditional requirements**: When requirements change based on context (e.g., new vs existing records), use dynamic required fields. | ||
| ``` | ||
| ## ๐ License | ||
| MIT ยฉ [Cerios](LICENSE) | ||
| ## ๐ Links | ||
| - [GitHub Repository](https://github.com/CeriosTesting/cerios-builder) | ||
| - [Issues](https://github.com/CeriosTesting/cerios-builder/issues) | ||
| - [NPM Package](https://www.npmjs.com/package/@cerios/cerios-builder) |
147202
33.99%1038
30.9%1468
27.1%