@warlock.js/seal 5.27.0 → 5.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,18 @@ All notable changes to `@warlock.js/seal` are documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). `@warlock.js/*` packages are released in lockstep — every package shares the same version number, so a version below may list only the changes that affected this package.
6
6
 
7
+ ## 5.29.0 - 2026-10-01
8
+
9
+ ### Changed
10
+
11
+ - Lockstep release; package APIs are unchanged. Skills refer to other topics by name instead of file paths, so cross-references keep working after `agent-kit` copies them into an app.
12
+
13
+ ## 5.28.0 - 2026-10-01
14
+
15
+ ### Fixed
16
+
17
+ - `v.object()` now emits its validated output (and reports child errors) in declared schema order instead of the order async field validators happened to finish, so output key order is deterministic; unknown keys kept by `allowUnknown()` follow the declared keys in input order.
18
+
7
19
  ## 5.27.0 - 2026-10-01
8
20
 
9
21
  ### Changed
package/cjs/index.cjs CHANGED
@@ -27,7 +27,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
27
27
 
28
28
  //#endregion
29
29
  const require_required_methods = require('./required-methods-BOVNRtci.cjs');
30
- const require_object = require('./object-BuKApaax.cjs');
30
+ const require_object = require('./object-DubGC-xu.cjs');
31
31
  const require_string = require('./string-DpDpgefH.cjs');
32
32
  let _mongez_reinforcements = require("@mongez/reinforcements");
33
33
  let _mongez_supportive_is = require("@mongez/supportive-is");
@@ -436,7 +436,8 @@ var ObjectValidator = class ObjectValidator extends require_required_methods.Bas
436
436
  if (mutatedData === void 0 || mutatedData === null) return result;
437
437
  const errors = [];
438
438
  const validatedData = {};
439
- const validationPromises = Object.entries(this.schema).filter(([, validator]) => !this.isComputedValidator(validator)).map(async ([key, validator]) => {
439
+ const fieldErrors = [];
440
+ const validationPromises = Object.entries(this.schema).filter(([, validator]) => !this.isComputedValidator(validator)).map(async ([key, validator], index) => {
440
441
  const value = mutatedData?.[key] !== void 0 ? mutatedData[key] : validator.getDefaultValue();
441
442
  const childContext = {
442
443
  ...context,
@@ -447,16 +448,18 @@ var ObjectValidator = class ObjectValidator extends require_required_methods.Bas
447
448
  };
448
449
  const childResult = await validator.validate(value, childContext);
449
450
  if (childResult.data !== void 0 && !validator.isOmitted()) validatedData[key] = childResult.data;
450
- if (childResult.isValid === false) errors.push(...childResult.errors);
451
+ if (childResult.isValid === false) fieldErrors[index] = childResult.errors;
451
452
  });
452
453
  await Promise.all(validationPromises);
454
+ for (const childErrors of fieldErrors) if (childErrors) errors.push(...childErrors);
453
455
  if (errors.length > 0) return {
454
456
  isValid: false,
455
457
  errors,
456
458
  data: void 0
457
459
  };
458
460
  const computedFields = this.getComputedFields();
459
- const computedPromises = Object.entries(computedFields).map(async ([key, validator]) => {
461
+ const computedErrors = [];
462
+ const computedPromises = Object.entries(computedFields).map(async ([key, validator], index) => {
460
463
  const childContext = {
461
464
  ...context,
462
465
  parent: validatedData,
@@ -464,15 +467,18 @@ var ObjectValidator = class ObjectValidator extends require_required_methods.Bas
464
467
  };
465
468
  const childResult = await validator.validate(validatedData, childContext);
466
469
  if (childResult.data !== void 0 && !validator.isOmitted()) validatedData[key] = childResult.data;
467
- if (childResult.isValid === false) errors.push(...childResult.errors);
470
+ if (childResult.isValid === false) computedErrors[index] = childResult.errors;
468
471
  });
469
472
  await Promise.all(computedPromises);
473
+ for (const childErrors of computedErrors) if (childErrors) errors.push(...childErrors);
470
474
  if (errors.length > 0) return {
471
475
  isValid: false,
472
476
  errors,
473
477
  data: void 0
474
478
  };
475
- const cleanedData = removeUndefinedValues(validatedData);
479
+ const orderedData = {};
480
+ for (const key of Object.keys(this.schema)) if (key in validatedData) orderedData[key] = validatedData[key];
481
+ const cleanedData = removeUndefinedValues(orderedData);
476
482
  const transformedData = await this.startTransformationPipeline(cleanedData, context);
477
483
  return {
478
484
  isValid: true,
@@ -610,4 +616,4 @@ Object.defineProperty(exports, 'stripUnknownMutator', {
610
616
  return stripUnknownMutator;
611
617
  }
612
618
  });
613
- //# sourceMappingURL=object-BuKApaax.cjs.map
619
+ //# sourceMappingURL=object-DubGC-xu.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"object-BuKApaax.cjs","names":["BaseValidator","BaseValidator","objectRule","unknownKeyRule","setKeyPath","wrapNullableStrict"],"sources":["../../../../../../seal/src/mutators/object-mutators.ts","../../../../../../seal/src/validators/computed-validator.ts","../../../../../../seal/src/validators/object-validator.ts","../../../../../../seal/src/object.ts"],"sourcesContent":["import { trim } from \"@mongez/reinforcements\";\nimport { isPlainObject } from \"@mongez/supportive-is\";\nimport type { Mutator } from \"../types\";\n\n/** Strip unknown keys from object */\nexport const stripUnknownMutator: Mutator = async (value, context) => {\n const allowedKeys = [\n ...(context?.ctx.schema ? Object.keys(context.ctx.schema) : []),\n ...(context?.options.allowedKeys ?? []),\n ];\n\n const result: Record<string, any> = {};\n\n for (const key in value) {\n if (allowedKeys.includes(key)) {\n result[key] = value[key];\n }\n }\n\n return result;\n};\n\n/** Trim all string values in object */\nexport const objectTrimMutator: Mutator = async (value, context) => {\n if (!isPlainObject(value)) return value;\n\n const result: Record<string, any> = {};\n const recursive = context?.options.recursive ?? false;\n\n for (const key in value) {\n const item = value[key];\n\n if (recursive) {\n if (Array.isArray(item)) {\n result[key] = await Promise.all(\n item.map(async (i: any) =>\n typeof i === \"string\" ? trim(i) : await objectTrimMutator(i, context),\n ),\n );\n } else if (isPlainObject(item)) {\n result[key] = await objectTrimMutator(item, context);\n } else {\n result[key] = typeof item === \"string\" ? trim(item) : item;\n }\n } else {\n result[key] = typeof item === \"string\" ? trim(item) : item;\n }\n }\n\n return result;\n};\n\n/** Parse JSON string */\nexport const jsonMutator: Mutator = async (value) => {\n try {\n return JSON.parse(value);\n } catch {\n return value;\n }\n};\n","import type { SchemaContext, ValidationResult } from \"../types\";\nimport { BaseValidator } from \"./base-validator\";\nimport type { JsonSchemaResult, JsonSchemaTarget } from \"../standard-schema/json-schema\";\n\n/**\n * Callback function for computed fields\n * Receives validated & mutated data and full schema context\n */\nexport type ComputedCallback<TResult = any> = (\n data: any,\n context: SchemaContext,\n) => TResult | Promise<TResult>;\n\n/**\n * Computed field validator\n *\n * Computes a value based on other validated fields in the schema.\n * The computed value is persisted and can optionally be validated.\n *\n * @example\n * ```ts\n * // Basic computed field\n * const schema = v.object({\n * title: v.string().required(),\n * slug: v.computed(data => slugify(data.title)),\n * });\n *\n * // With result validation\n * const schema = v.object({\n * title: v.string().required(),\n * slug: v.computed(\n * data => slugify(data.title),\n * v.string().minLength(3)\n * ),\n * });\n *\n * // Async computation\n * const schema = v.object({\n * image: v.string().url(),\n * thumbnail: v.computed(async data => {\n * return await generateThumbnail(data.image);\n * }),\n * });\n * ```\n */\nexport class ComputedValidator<TResult = any> extends BaseValidator {\n /**\n * Create a new computed field validator\n *\n * @param callback - Function to compute the value from validated data\n * @param resultValidator - Optional validator to validate the computed result\n */\n public constructor(\n protected callback: ComputedCallback<TResult>,\n protected resultValidator?: BaseValidator,\n ) {\n super();\n }\n\n /**\n * Execute the callback and optionally validate the result\n */\n public async validate(data: any, context: SchemaContext): Promise<ValidationResult> {\n try {\n // Execute the callback with validated data\n const result = await this.callback(data, context);\n\n // Optionally validate the computed result\n if (this.resultValidator) {\n const validation = await this.resultValidator.validate(result, context);\n\n if (!validation.isValid) {\n return {\n isValid: false,\n errors: validation.errors,\n data: undefined,\n };\n }\n\n return {\n isValid: true,\n errors: [],\n data: validation.data,\n };\n }\n\n // No validation - return computed result directly\n return {\n isValid: true,\n errors: [],\n data: result,\n };\n } catch (error) {\n // Handle callback execution errors\n return {\n isValid: false,\n errors: [\n {\n type: \"computed\",\n error: error instanceof Error ? error.message : \"Computed field callback failed\",\n input: context.path,\n },\n ],\n data: undefined,\n };\n }\n }\n\n /**\n * Clone this validator with all its configuration\n * Critical for ObjectValidator.clone(), extend(), merge(), etc.\n */\n public override clone(): this {\n const cloned = super.clone();\n\n // Copy ComputedValidator-specific properties\n cloned.callback = this.callback; // Functions are safe to copy by reference\n cloned.resultValidator = this.resultValidator?.clone(); // Deep clone validator\n\n return cloned;\n }\n\n /**\n * Computed fields don't have a specific type to match\n */\n public matchesType(value: any): boolean {\n return true; // Computed fields accept any input (they generate their own value)\n }\n\n /**\n * @inheritdoc\n *\n * Computed fields are server-side runtime values — they have no input schema\n * and cannot be represented in JSON Schema. Calling this method is always a\n * programming error.\n *\n * @throws Error Always throws — computed/managed fields have no JSON Schema representation.\n */\n public override toJsonSchema(_target: JsonSchemaTarget = \"draft-2020-12\"): JsonSchemaResult {\n throw new Error(\n `[Seal] toJsonSchema() is not supported on ComputedValidator / ManagedValidator. ` +\n `Computed fields are runtime-only and have no input JSON Schema representation. ` +\n `ObjectValidator.toJsonSchema() automatically skips computed fields — ` +\n `do not call toJsonSchema() on a computed validator directly.`,\n );\n }\n}\n","import { except } from \"@mongez/reinforcements\";\nimport { isPlainObject } from \"@mongez/supportive-is\";\nimport { setKeyPath } from \"../helpers/path-helpers\";\nimport { objectTrimMutator, stripUnknownMutator } from \"../mutators/object-mutators\";\nimport { objectRule } from \"../rules/common/type-rules\";\nimport { unknownKeyRule } from \"../rules/common/unknown-key\";\nimport type { JsonSchemaResult, JsonSchemaTarget } from \"../standard-schema/json-schema\";\nimport { applyNullable, wrapNullableStrict } from \"../standard-schema/json-schema\";\nimport type { Schema, SchemaContext, ValidationResult } from \"../types\";\nimport type { InferInputObjectShape, InferOutputObjectShape } from \"../types/inference-types\";\nimport { BaseValidator } from \"./base-validator\";\nimport { ComputedValidator } from \"./computed-validator\";\n\n/**\n * Object validator class with generic schema type for proper type inference.\n *\n * Threads two distinct inferred shapes into `BaseValidator`'s `TInput`/`TOutput`:\n *\n * - `TInput` = `InferInputObjectShape<TSchema>` — what `~standard.validate()`\n * accepts (caller's pre-validation payload; defaults/catches make\n * keys optional)\n * - `TOutput` = `InferOutputObjectShape<TSchema>` — what `~standard.validate()`\n * returns on success (post-default, post-catch shape; guaranteed\n * keys are required)\n *\n * Both helpers are used directly (not the full `Infer.*` walker) to avoid a\n * recursive class-base evaluation that would otherwise occur when the\n * brand-aware widening tries to inspect the class type.\n */\nexport class ObjectValidator<TSchema extends Schema = Schema> extends BaseValidator<\n InferInputObjectShape<TSchema>,\n InferOutputObjectShape<TSchema>\n> {\n protected shouldAllowUnknown = false;\n protected allowedKeys: string[] = [];\n protected hasUnknownKeyRule = false;\n\n public constructor(\n public schema: TSchema,\n errorMessage?: string,\n ) {\n super();\n this.addMutableRule(objectRule, errorMessage);\n }\n\n /**\n * Check if value is an object type (plain object, not array or date)\n */\n public matchesType(value: any): boolean {\n return isPlainObject(value);\n }\n\n /** Strip unknown keys from the data */\n public stripUnknown() {\n const validator = this.instance;\n return validator.addMutator(stripUnknownMutator, {\n get allowedKeys() {\n return validator.allowedKeys;\n },\n });\n }\n\n /** Add list of allowed keys that could be in the data but not necessarily validated */\n public allow(...keys: string[]) {\n const validator = this.instance;\n validator.allowedKeys.push(...keys);\n return validator;\n }\n\n /** Trim values of the object properties */\n public trim(recursive = true) {\n const validator = this.instance;\n return validator.addMutator(objectTrimMutator, { recursive });\n }\n\n /** Whether to allow unknown properties\n * Please note it will allow only unknown direct children keys, not nested children keys\n */\n public allowUnknown(allow = true) {\n const validator = this.instance;\n validator.shouldAllowUnknown = allow;\n return validator;\n }\n\n /**\n * Create a copy of this object validator with the same configuration\n * Copies schema, rules, mutators, transformers, and object-specific settings\n *\n * @returns A new ObjectValidator instance with copied configuration\n *\n * @example\n * ```ts\n * const baseUser = v.object({ name: v.string() }).allowUnknown();\n * const userCopy = baseUser.clone();\n * // userCopy has the same schema and allowUnknown setting\n * ```\n */\n public override clone(keys?: string[]): this {\n // Get cloned instance with all BaseValidator properties\n const cloned = super.clone();\n\n // Clone schema with deep copy of validators\n const newSchema = {} as TSchema;\n for (const [key, validator] of Object.entries(this.schema)) {\n if (keys && !keys.includes(key)) continue;\n (newSchema as Schema)[key] = validator.clone();\n }\n\n cloned.schema = newSchema;\n\n // Add ObjectValidator-specific properties\n cloned.shouldAllowUnknown = this.shouldAllowUnknown;\n cloned.allowedKeys = [...this.allowedKeys];\n // NOTE: hasUnknownKeyRule is intentionally NOT copied.\n // Each clone must add its own unknownKeyRule on first validate()\n // so it holds references to its own schema/allowedKeys, not the original's.\n\n return cloned;\n }\n\n /**\n * Extend this schema with additional fields\n * Clones the current validator and adds new fields to the schema\n * **Keeps original configuration** (allowUnknown, stripUnknown, etc.)\n *\n * If an ObjectValidator is provided, only its schema is used - its configuration is ignored.\n * This is useful for creating reusable field collections that can be added to different schemas.\n *\n * @param schemaOrValidator - Plain schema object or ObjectValidator to extend with\n * @returns A new ObjectValidator with merged schema and original configuration\n *\n * @example\n * ```ts\n * // Extend with plain schema\n * const baseUser = v.object({\n * name: v.string().required(),\n * email: v.string().email().required()\n * }).allowUnknown();\n *\n * const adminUser = baseUser.extend({\n * role: v.string().in(['admin', 'superadmin']).required()\n * });\n * // adminUser has: name, email, role\n * // adminUser keeps: allowUnknown() from base ✅\n *\n * // Extend with ObjectValidator (only schema is used)\n * const auditFields = v.object({\n * createdAt: v.date().required(),\n * updatedAt: v.date().required()\n * }).stripUnknown(); // This config is ignored!\n *\n * const fullUser = baseUser.extend(auditFields);\n * // fullUser has: name, email, createdAt, updatedAt\n * // fullUser keeps: allowUnknown() from base (NOT stripUnknown from auditFields) ✅\n *\n * // Chain multiple extends\n * const complexSchema = baseUser\n * .extend(auditFields)\n * .extend({ metadata: v.object({}) });\n * ```\n */\n public extend<TExtension extends Schema>(\n schemaOrValidator: TExtension | ObjectValidator<TExtension>,\n ): ObjectValidator<TSchema & TExtension> {\n // Clone current validator to preserve original\n const extended = this.clone() as any;\n\n // Extract schema from parameter\n const schemaToAdd =\n schemaOrValidator instanceof ObjectValidator ? schemaOrValidator.schema : schemaOrValidator;\n\n // Merge schemas with cloned validators (later fields override earlier ones)\n for (const [key, validator] of Object.entries(schemaToAdd)) {\n (extended.schema as Schema)[key] = validator.clone();\n }\n\n return extended as ObjectValidator<TSchema & TExtension>;\n }\n\n /**\n * Merge with another ObjectValidator\n * Clones current validator, merges schemas, and **overrides configuration** with other validator's config\n *\n * Unlike extend(), merge() combines both schemas AND configurations.\n * The other validator's configuration (allowUnknown, stripUnknown, etc.) takes precedence.\n *\n * @param validator - Another ObjectValidator to merge with\n * @returns A new ObjectValidator with merged schema and configuration\n *\n * @example\n * ```ts\n * const baseUser = v.object({\n * name: v.string().required()\n * }).allowUnknown();\n *\n * const timestamps = v.object({\n * createdAt: v.date().required(),\n * updatedAt: v.date().required()\n * }).stripUnknown();\n *\n * const merged = baseUser.merge(timestamps);\n * // merged has: name, createdAt, updatedAt\n * // merged config: stripUnknown() from timestamps (overrides allowUnknown) ✅\n *\n * // Chain multiple merges\n * const full = baseUser.merge(timestamps).merge(softDeleteSchema);\n * ```\n */\n public merge<TMerge extends Schema>(\n validator: ObjectValidator<TMerge>,\n ): ObjectValidator<TSchema & TMerge> {\n // Clone current validator\n const merged = this.clone() as any;\n\n // Merge schemas with cloned validators (later fields override earlier ones)\n for (const [key, schemaValidator] of Object.entries(validator.schema)) {\n (merged.schema as Schema)[key] = schemaValidator.clone();\n }\n\n // Override configuration with other validator's config\n merged.shouldAllowUnknown = validator.shouldAllowUnknown;\n merged.allowedKeys = [...merged.allowedKeys, ...validator.allowedKeys];\n\n // Append rules, mutators, transformers from other validator\n merged.rules.push(...validator.rules);\n merged.mutators.push(...validator.mutators);\n merged.dataTransformers.push(...validator.dataTransformers);\n\n // Merge attributes text (later wins)\n merged.attributesText = {\n ...merged.attributesText,\n ...validator.attributesText,\n };\n\n merged.translatedAttributes = {\n ...merged.translatedAttributes,\n ...validator.translatedAttributes,\n };\n\n return merged as ObjectValidator<TSchema & TMerge>;\n }\n\n /**\n * Create a new schema with only the specified fields\n * Clones the current validator and keeps only the selected fields\n * **Preserves all configuration** (allowUnknown, stripUnknown, etc.)\n *\n * @param keys - Field names to keep in the schema\n * @returns A new ObjectValidator with only the picked fields\n *\n * @example\n * ```ts\n * const fullUser = v.object({\n * id: v.int().required(),\n * name: v.string().required(),\n * email: v.string().email().required(),\n * password: v.string().required(),\n * role: v.string()\n * }).allowUnknown();\n *\n * // For login - only need email and password\n * const loginSchema = fullUser.pick('email', 'password');\n * // loginSchema has: { email, password }\n * // loginSchema keeps: allowUnknown() ✅\n *\n * // For public profile\n * const publicSchema = fullUser.pick('id', 'name', 'role');\n * // publicSchema has: { id, name, role }\n * ```\n */\n public pick<K extends keyof TSchema>(...keys: K[]): ObjectValidator<Pick<TSchema, K>> {\n // Clone current validator\n const picked = this.clone() as any;\n\n // Create new schema with only picked keys\n const newSchema = {} as Pick<TSchema, K>;\n for (const key of keys) {\n if (key in picked.schema) {\n (newSchema as any)[key] = picked.schema[key];\n }\n }\n\n picked.schema = newSchema;\n\n return picked as ObjectValidator<Pick<TSchema, K>>;\n }\n\n /**\n * Mark all or the given schema fields as optional\n */\n public partial<K extends keyof TSchema>(...keys: K[]) {\n const validationSchema = this.clone();\n\n if (keys.length === 0) {\n keys = Object.keys(validationSchema.schema) as K[];\n }\n\n for (const [key, validator] of Object.entries(validationSchema.schema)) {\n if (keys.length === 0 || keys.includes(key as K)) {\n (validationSchema.schema as Schema)[key] = validator.optional();\n }\n }\n\n return validationSchema;\n }\n\n /**\n * Make the all or the given fields as required\n */\n public requiredFields<K extends keyof TSchema>(...keys: K[]) {\n const validationSchema = this.clone();\n\n if (keys.length === 0) {\n keys = Object.keys(validationSchema.schema) as K[];\n }\n\n for (const [key, validator] of Object.entries(validationSchema.schema)) {\n if (keys.length === 0 || keys.includes(key as K)) {\n (validationSchema.schema as Schema)[key] = validator.required();\n }\n }\n\n return validationSchema;\n }\n\n /**\n * Create a new schema excluding the specified fields\n * Clones the current validator and removes the specified fields\n * **Preserves all configuration** (allowUnknown, stripUnknown, etc.)\n *\n * @param keys - Field names to exclude from the schema\n * @returns A new ObjectValidator without the excluded fields\n *\n * @example\n * ```ts\n * const fullUser = v.object({\n * id: v.int().required(),\n * name: v.string().required(),\n * email: v.string().email().required(),\n * password: v.string().required(),\n * role: v.string()\n * }).allowUnknown();\n *\n * // For updates - exclude id\n * const updateSchema = fullUser.without('id');\n * // updateSchema has: { name, email, password, role }\n * // updateSchema keeps: allowUnknown() ✅\n *\n * // For public API - exclude sensitive fields\n * const publicSchema = fullUser.without('password', 'role');\n * // publicSchema has: { id, name, email }\n *\n * // Combine with other methods\n * const patchSchema = fullUser.without('id', 'password');\n * // patchSchema has: { name, email, role }\n * ```\n */\n public without<K extends keyof TSchema>(...keys: K[]): ObjectValidator<Omit<TSchema, K>> {\n // Clone current validator\n const filtered = this.clone() as any;\n\n // Create new schema excluding specified keys\n const newSchema = {} as Omit<TSchema, K>;\n for (const key in filtered.schema) {\n if (!keys.includes(key as any)) {\n (newSchema as any)[key] = filtered.schema[key];\n }\n }\n\n filtered.schema = newSchema;\n\n return filtered as ObjectValidator<Omit<TSchema, K>>;\n }\n\n /** Mutate the data */\n public mutate(data: any, context: SchemaContext) {\n if (!isPlainObject(data)) return data;\n return super.mutate({ ...data }, context);\n }\n\n /** Validate the data */\n public async validate(\n data: any,\n context: SchemaContext = { path: \"\" } as SchemaContext,\n ): Promise<ValidationResult> {\n context.schema = this.schema;\n const mutatedData = await this.mutate(data, context);\n\n // Check for unknown properties\n if (this.shouldAllowUnknown === false && !this.hasUnknownKeyRule) {\n this.hasUnknownKeyRule = true;\n const rule = this.addMutableRule(unknownKeyRule, undefined, {\n allowedKeys: this.allowedKeys,\n schema: this.schema,\n });\n\n this.setRuleAttributesList(rule);\n }\n\n const result = await super.validate(mutatedData, context);\n\n if (result.isValid === false) return result;\n\n // Absent (no default) or null (nullable) inputs short-circuit here —\n // BaseValidator.validate already resolved the outcome, so recursing into\n // the shape below would wrongly re-run required checks against `undefined`.\n if (mutatedData === undefined || mutatedData === null) return result;\n\n // ═══════════════════════════════════════════════════════════\n // PHASE 1: Validate user input fields (skip computed/managed)\n // ═══════════════════════════════════════════════════════════\n const errors: ValidationResult[\"errors\"] = [];\n const validatedData: any = {};\n\n const userInputEntries = Object.entries(this.schema).filter(\n ([, validator]) => !this.isComputedValidator(validator),\n );\n\n const validationPromises = userInputEntries.map(async ([key, validator]) => {\n const value =\n mutatedData?.[key] !== undefined ? mutatedData[key] : validator.getDefaultValue();\n\n const childContext: SchemaContext = {\n ...context,\n parent: mutatedData,\n value,\n key,\n path: setKeyPath(context.path, key),\n };\n\n const childResult = await validator.validate(value, childContext);\n\n // Only include in validated data if not omitted\n if (childResult.data !== undefined && !validator.isOmitted()) {\n validatedData[key] = childResult.data;\n }\n\n if (childResult.isValid === false) {\n errors.push(...childResult.errors);\n }\n });\n\n await Promise.all(validationPromises);\n\n // If Phase 1 failed, return early\n if (errors.length > 0) {\n return {\n isValid: false,\n errors,\n data: undefined,\n };\n }\n\n // ═══════════════════════════════════════════════════════════\n // PHASE 2: Execute computed/managed fields with validated data\n // ═══════════════════════════════════════════════════════════\n const computedFields = this.getComputedFields();\n\n const computedPromises = Object.entries(computedFields).map(async ([key, validator]) => {\n const childContext: SchemaContext = {\n ...context,\n parent: validatedData,\n value: undefined, // Computed fields don't have input value\n };\n\n // Execute computed callback with validated data\n const childResult = await validator.validate(validatedData, childContext);\n\n // Only include in final data if not omitted\n if (childResult.data !== undefined && !validator.isOmitted()) {\n validatedData[key] = childResult.data;\n }\n\n if (childResult.isValid === false) {\n errors.push(...childResult.errors);\n }\n });\n\n await Promise.all(computedPromises);\n\n // If Phase 2 failed, return early\n if (errors.length > 0) {\n return {\n isValid: false,\n errors,\n data: undefined,\n };\n }\n\n // Remove undefined values\n const cleanedData = removeUndefinedValues(validatedData);\n\n const transformedData = await this.startTransformationPipeline(cleanedData, context);\n\n const output =\n this.shouldAllowUnknown === false\n ? transformedData\n : {\n ...transformedData,\n ...except(mutatedData, Object.keys(this.schema)),\n };\n\n return {\n isValid: true,\n errors: [],\n data: output,\n };\n }\n\n /**\n * Check if a validator is a computed or managed field\n * ManagedValidator extends ComputedValidator, so instanceof catches both\n */\n private isComputedValidator(validator: BaseValidator): boolean {\n return validator instanceof ComputedValidator;\n }\n\n /**\n * Get all computed/managed fields from the schema\n */\n private getComputedFields(): Record<string, ComputedValidator> {\n const computed: Record<string, ComputedValidator> = {};\n\n for (const [key, validator] of Object.entries(this.schema)) {\n if (validator instanceof ComputedValidator) {\n computed[key] = validator;\n }\n }\n\n return computed;\n }\n\n /**\n * @inheritdoc\n *\n * Recursively generates JSON Schema for all input fields in the schema.\n * Computed/managed fields are skipped — they have no input representation.\n *\n * **Standard targets** (`draft-2020-12`, `draft-07`, `openapi-3.0`):\n * - Fields marked `.optional()` are excluded from `required`.\n *\n * **`openai-strict` target** (OpenAI Structured Outputs):\n * - ALL fields appear in `required` — OpenAI rejects schemas with optional fields.\n * - Optional fields are instead expressed as nullable types:\n * `{ type: [\"string\", \"null\"] }` so the model can output `null` for them.\n * - Recursively applies `openai-strict` to all nested objects.\n *\n * @example\n * ```ts\n * v.object({\n * name: v.string().required(),\n * age: v.int().optional(),\n * }).toJsonSchema(\"draft-2020-12\")\n * // → { type: \"object\",\n * // properties: { name: { type: \"string\" }, age: { type: \"integer\" } },\n * // required: [\"name\"], additionalProperties: false }\n *\n * v.object({\n * name: v.string().required(),\n * age: v.int().optional(),\n * }).toJsonSchema(\"openai-strict\")\n * // → { type: \"object\",\n * // properties: { name: { type: \"string\" }, age: { type: [\"integer\", \"null\"] } },\n * // required: [\"name\", \"age\"], ← all fields\n * // additionalProperties: false }\n * ```\n */\n public override toJsonSchema(target: JsonSchemaTarget = \"draft-2020-12\"): JsonSchemaResult {\n const properties: Record<string, JsonSchemaResult> = {};\n const required: string[] = [];\n const isOpenAIStrict = target === \"openai-strict\";\n\n for (const [key, validator] of Object.entries(this.schema)) {\n // Skip computed/managed — runtime-only, no input schema\n if (validator instanceof ComputedValidator) continue;\n\n let fieldSchema = validator.toJsonSchema(target);\n\n if (isOpenAIStrict) {\n // OpenAI strict: every field must be in required.\n // Optional fields are expressed as nullable rather than absent.\n if (validator.isOptional) {\n fieldSchema = wrapNullableStrict(fieldSchema);\n }\n required.push(key);\n } else {\n // Standard JSON Schema: only non-optional fields go in required\n if (!validator.isOptional) {\n required.push(key);\n }\n }\n\n properties[key] = fieldSchema;\n }\n\n const schema: JsonSchemaResult = { type: \"object\", properties };\n\n if (required.length > 0) schema.required = required;\n if (!this.shouldAllowUnknown) schema.additionalProperties = false;\n if (this.isNullable) applyNullable(schema, target);\n\n return schema;\n }\n}\n\n/** Recursively remove undefined values from an object */\nfunction removeUndefinedValues(obj: any, visited = new WeakMap<object, any>()): any {\n // Handle primitives and null\n if (obj === null) {\n return obj;\n }\n\n // Handle arrays\n if (Array.isArray(obj)) {\n return obj.map((item) => removeUndefinedValues(item, visited));\n }\n\n // Skip non-plain objects (class instances, Dates, Buffers, etc.)\n if (!isPlainObject(obj)) {\n return obj;\n }\n\n // Handle circular references - return already processed result\n if (visited.has(obj)) {\n return visited.get(obj);\n }\n\n // Process plain objects\n const result: any = {};\n visited.set(obj, result); // Mark as processing BEFORE recursion\n\n for (const [key, value] of Object.entries(obj)) {\n if (value !== undefined) {\n result[key] = removeUndefinedValues(value, visited);\n }\n }\n\n return result;\n}\n","import type { StandardSchemaV1 } from \"./standard-schema/types\";\nimport type { Schema } from \"./types\";\nimport type { Infer } from \"./types/inference-types\";\nimport { ObjectValidator } from \"./validators/object-validator\";\n\n// Keep both runtime chain methods and their declaration augmentations in\n// this standalone entry, without loading the complete validator factory.\nexport { equalityConditionalMethodsApplied } from \"./validators/methods/equality-conditional-methods\";\nexport { forbiddenMethodsApplied } from \"./validators/methods/forbidden-methods\";\nexport { presentMethodsApplied } from \"./validators/methods/present-methods\";\nexport { requiredMethodsApplied } from \"./validators/methods/required-methods\";\nexport { ObjectValidator };\n\n/** Create an object schema without importing the complete `v` factory. */\nexport const object = <T extends Schema>(schema: T, errorMessage?: string) =>\n new ObjectValidator<T>(schema, errorMessage) as ObjectValidator<T> &\n StandardSchemaV1<Infer<ObjectValidator<T>>>;\n"],"mappings":";;;;;;AAKA,MAAa,sBAA+B,OAAO,OAAO,YAAY;CACpE,MAAM,cAAc,CAClB,GAAI,SAAS,IAAI,SAAS,OAAO,KAAK,QAAQ,IAAI,MAAM,IAAI,CAAC,GAC7D,GAAI,SAAS,QAAQ,eAAe,CAAC,CACvC;CAEA,MAAM,SAA8B,CAAC;CAErC,KAAK,MAAM,OAAO,OAChB,IAAI,YAAY,SAAS,GAAG,GAC1B,OAAO,OAAO,MAAM;CAIxB,OAAO;AACT;;AAGA,MAAa,oBAA6B,OAAO,OAAO,YAAY;CAClE,IAAI,0CAAe,KAAK,GAAG,OAAO;CAElC,MAAM,SAA8B,CAAC;CACrC,MAAM,YAAY,SAAS,QAAQ,aAAa;CAEhD,KAAK,MAAM,OAAO,OAAO;EACvB,MAAM,OAAO,MAAM;EAEnB,IAAI,WACF,IAAI,MAAM,QAAQ,IAAI,GACpB,OAAO,OAAO,MAAM,QAAQ,IAC1B,KAAK,IAAI,OAAO,MACd,OAAO,MAAM,4CAAgB,CAAC,IAAI,MAAM,kBAAkB,GAAG,OAAO,CACtE,CACF;OACK,6CAAkB,IAAI,GAC3B,OAAO,OAAO,MAAM,kBAAkB,MAAM,OAAO;OAEnD,OAAO,OAAO,OAAO,SAAS,4CAAgB,IAAI,IAAI;OAGxD,OAAO,OAAO,OAAO,SAAS,4CAAgB,IAAI,IAAI;CAE1D;CAEA,OAAO;AACT;;AAGA,MAAa,cAAuB,OAAO,UAAU;CACnD,IAAI;EACF,OAAO,KAAK,MAAM,KAAK;CACzB,QAAQ;EACN,OAAO;CACT;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACdA,IAAa,oBAAb,cAAsDA,uCAAc;;;;;;;CAOlE,AAAO,YACL,AAAU,UACV,AAAU,iBACV;EACA,MAAM;EAHI;EACA;CAGZ;;;;CAKA,MAAa,SAAS,MAAW,SAAmD;EAClF,IAAI;GAEF,MAAM,SAAS,MAAM,KAAK,SAAS,MAAM,OAAO;GAGhD,IAAI,KAAK,iBAAiB;IACxB,MAAM,aAAa,MAAM,KAAK,gBAAgB,SAAS,QAAQ,OAAO;IAEtE,IAAI,CAAC,WAAW,SACd,OAAO;KACL,SAAS;KACT,QAAQ,WAAW;KACnB,MAAM;IACR;IAGF,OAAO;KACL,SAAS;KACT,QAAQ,CAAC;KACT,MAAM,WAAW;IACnB;GACF;GAGA,OAAO;IACL,SAAS;IACT,QAAQ,CAAC;IACT,MAAM;GACR;EACF,SAAS,OAAO;GAEd,OAAO;IACL,SAAS;IACT,QAAQ,CACN;KACE,MAAM;KACN,OAAO,iBAAiB,QAAQ,MAAM,UAAU;KAChD,OAAO,QAAQ;IACjB,CACF;IACA,MAAM;GACR;EACF;CACF;;;;;CAMA,AAAgB,QAAc;EAC5B,MAAM,SAAS,MAAM,MAAM;EAG3B,OAAO,WAAW,KAAK;EACvB,OAAO,kBAAkB,KAAK,iBAAiB,MAAM;EAErD,OAAO;CACT;;;;CAKA,AAAO,YAAY,OAAqB;EACtC,OAAO;CACT;;;;;;;;;;CAWA,AAAgB,aAAa,UAA4B,iBAAmC;EAC1F,MAAM,IAAI,MACR,kSAIF;CACF;AACF;;;;;;;;;;;;;;;;;;;;ACrHA,IAAa,kBAAb,MAAa,wBAAyDC,uCAGpE;CAKA,AAAO,YACL,AAAO,QACP,cACA;EACA,MAAM;EAHC;4BALsB;qBACG,CAAC;2BACL;EAO5B,KAAK,eAAeC,qCAAY,YAAY;CAC9C;;;;CAKA,AAAO,YAAY,OAAqB;EACtC,gDAAqB,KAAK;CAC5B;;CAGA,AAAO,eAAe;EACpB,MAAM,YAAY,KAAK;EACvB,OAAO,UAAU,WAAW,qBAAqB,EAC/C,IAAI,cAAc;GAChB,OAAO,UAAU;EACnB,EACF,CAAC;CACH;;CAGA,AAAO,MAAM,GAAG,MAAgB;EAC9B,MAAM,YAAY,KAAK;EACvB,UAAU,YAAY,KAAK,GAAG,IAAI;EAClC,OAAO;CACT;;CAGA,AAAO,KAAK,YAAY,MAAM;EAE5B,OADkB,KAAK,SACN,WAAW,mBAAmB,EAAE,UAAU,CAAC;CAC9D;;;;CAKA,AAAO,aAAa,QAAQ,MAAM;EAChC,MAAM,YAAY,KAAK;EACvB,UAAU,qBAAqB;EAC/B,OAAO;CACT;;;;;;;;;;;;;;CAeA,AAAgB,MAAM,MAAuB;EAE3C,MAAM,SAAS,MAAM,MAAM;EAG3B,MAAM,YAAY,CAAC;EACnB,KAAK,MAAM,CAAC,KAAK,cAAc,OAAO,QAAQ,KAAK,MAAM,GAAG;GAC1D,IAAI,QAAQ,CAAC,KAAK,SAAS,GAAG,GAAG;GACjC,AAAC,UAAqB,OAAO,UAAU,MAAM;EAC/C;EAEA,OAAO,SAAS;EAGhB,OAAO,qBAAqB,KAAK;EACjC,OAAO,cAAc,CAAC,GAAG,KAAK,WAAW;EAKzC,OAAO;CACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2CA,AAAO,OACL,mBACuC;EAEvC,MAAM,WAAW,KAAK,MAAM;EAG5B,MAAM,cACJ,6BAA6B,kBAAkB,kBAAkB,SAAS;EAG5E,KAAK,MAAM,CAAC,KAAK,cAAc,OAAO,QAAQ,WAAW,GACvD,AAAC,SAAS,OAAkB,OAAO,UAAU,MAAM;EAGrD,OAAO;CACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+BA,AAAO,MACL,WACmC;EAEnC,MAAM,SAAS,KAAK,MAAM;EAG1B,KAAK,MAAM,CAAC,KAAK,oBAAoB,OAAO,QAAQ,UAAU,MAAM,GAClE,AAAC,OAAO,OAAkB,OAAO,gBAAgB,MAAM;EAIzD,OAAO,qBAAqB,UAAU;EACtC,OAAO,cAAc,CAAC,GAAG,OAAO,aAAa,GAAG,UAAU,WAAW;EAGrE,OAAO,MAAM,KAAK,GAAG,UAAU,KAAK;EACpC,OAAO,SAAS,KAAK,GAAG,UAAU,QAAQ;EAC1C,OAAO,iBAAiB,KAAK,GAAG,UAAU,gBAAgB;EAG1D,OAAO,iBAAiB;GACtB,GAAG,OAAO;GACV,GAAG,UAAU;EACf;EAEA,OAAO,uBAAuB;GAC5B,GAAG,OAAO;GACV,GAAG,UAAU;EACf;EAEA,OAAO;CACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8BA,AAAO,KAA8B,GAAG,MAA8C;EAEpF,MAAM,SAAS,KAAK,MAAM;EAG1B,MAAM,YAAY,CAAC;EACnB,KAAK,MAAM,OAAO,MAChB,IAAI,OAAO,OAAO,QAChB,AAAC,UAAkB,OAAO,OAAO,OAAO;EAI5C,OAAO,SAAS;EAEhB,OAAO;CACT;;;;CAKA,AAAO,QAAiC,GAAG,MAAW;EACpD,MAAM,mBAAmB,KAAK,MAAM;EAEpC,IAAI,KAAK,WAAW,GAClB,OAAO,OAAO,KAAK,iBAAiB,MAAM;EAG5C,KAAK,MAAM,CAAC,KAAK,cAAc,OAAO,QAAQ,iBAAiB,MAAM,GACnE,IAAI,KAAK,WAAW,KAAK,KAAK,SAAS,GAAQ,GAC7C,AAAC,iBAAiB,OAAkB,OAAO,UAAU,SAAS;EAIlE,OAAO;CACT;;;;CAKA,AAAO,eAAwC,GAAG,MAAW;EAC3D,MAAM,mBAAmB,KAAK,MAAM;EAEpC,IAAI,KAAK,WAAW,GAClB,OAAO,OAAO,KAAK,iBAAiB,MAAM;EAG5C,KAAK,MAAM,CAAC,KAAK,cAAc,OAAO,QAAQ,iBAAiB,MAAM,GACnE,IAAI,KAAK,WAAW,KAAK,KAAK,SAAS,GAAQ,GAC7C,AAAC,iBAAiB,OAAkB,OAAO,UAAU,SAAS;EAIlE,OAAO;CACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkCA,AAAO,QAAiC,GAAG,MAA8C;EAEvF,MAAM,WAAW,KAAK,MAAM;EAG5B,MAAM,YAAY,CAAC;EACnB,KAAK,MAAM,OAAO,SAAS,QACzB,IAAI,CAAC,KAAK,SAAS,GAAU,GAC3B,AAAC,UAAkB,OAAO,SAAS,OAAO;EAI9C,SAAS,SAAS;EAElB,OAAO;CACT;;CAGA,AAAO,OAAO,MAAW,SAAwB;EAC/C,IAAI,0CAAe,IAAI,GAAG,OAAO;EACjC,OAAO,MAAM,OAAO,EAAE,GAAG,KAAK,GAAG,OAAO;CAC1C;;CAGA,MAAa,SACX,MACA,UAAyB,EAAE,MAAM,GAAG,GACT;EAC3B,QAAQ,SAAS,KAAK;EACtB,MAAM,cAAc,MAAM,KAAK,OAAO,MAAM,OAAO;EAGnD,IAAI,KAAK,uBAAuB,SAAS,CAAC,KAAK,mBAAmB;GAChE,KAAK,oBAAoB;GACzB,MAAM,OAAO,KAAK,eAAeC,yCAAgB,QAAW;IAC1D,aAAa,KAAK;IAClB,QAAQ,KAAK;GACf,CAAC;GAED,KAAK,sBAAsB,IAAI;EACjC;EAEA,MAAM,SAAS,MAAM,MAAM,SAAS,aAAa,OAAO;EAExD,IAAI,OAAO,YAAY,OAAO,OAAO;EAKrC,IAAI,gBAAgB,UAAa,gBAAgB,MAAM,OAAO;EAK9D,MAAM,SAAqC,CAAC;EAC5C,MAAM,gBAAqB,CAAC;EAM5B,MAAM,qBAJmB,OAAO,QAAQ,KAAK,MAAM,CAAC,CAAC,QAClD,GAAG,eAAe,CAAC,KAAK,oBAAoB,SAAS,CAGd,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,eAAe;GAC1E,MAAM,QACJ,cAAc,SAAS,SAAY,YAAY,OAAO,UAAU,gBAAgB;GAElF,MAAM,eAA8B;IAClC,GAAG;IACH,QAAQ;IACR;IACA;IACA,MAAMC,oCAAW,QAAQ,MAAM,GAAG;GACpC;GAEA,MAAM,cAAc,MAAM,UAAU,SAAS,OAAO,YAAY;GAGhE,IAAI,YAAY,SAAS,UAAa,CAAC,UAAU,UAAU,GACzD,cAAc,OAAO,YAAY;GAGnC,IAAI,YAAY,YAAY,OAC1B,OAAO,KAAK,GAAG,YAAY,MAAM;EAErC,CAAC;EAED,MAAM,QAAQ,IAAI,kBAAkB;EAGpC,IAAI,OAAO,SAAS,GAClB,OAAO;GACL,SAAS;GACT;GACA,MAAM;EACR;EAMF,MAAM,iBAAiB,KAAK,kBAAkB;EAE9C,MAAM,mBAAmB,OAAO,QAAQ,cAAc,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,eAAe;GACtF,MAAM,eAA8B;IAClC,GAAG;IACH,QAAQ;IACR,OAAO;GACT;GAGA,MAAM,cAAc,MAAM,UAAU,SAAS,eAAe,YAAY;GAGxE,IAAI,YAAY,SAAS,UAAa,CAAC,UAAU,UAAU,GACzD,cAAc,OAAO,YAAY;GAGnC,IAAI,YAAY,YAAY,OAC1B,OAAO,KAAK,GAAG,YAAY,MAAM;EAErC,CAAC;EAED,MAAM,QAAQ,IAAI,gBAAgB;EAGlC,IAAI,OAAO,SAAS,GAClB,OAAO;GACL,SAAS;GACT;GACA,MAAM;EACR;EAIF,MAAM,cAAc,sBAAsB,aAAa;EAEvD,MAAM,kBAAkB,MAAM,KAAK,4BAA4B,aAAa,OAAO;EAUnF,OAAO;GACL,SAAS;GACT,QAAQ,CAAC;GACT,MAVA,KAAK,uBAAuB,QACxB,kBACA;IACE,GAAG;IACH,sCAAU,aAAa,OAAO,KAAK,KAAK,MAAM,CAAC;GACjD;EAMN;CACF;;;;;CAMA,AAAQ,oBAAoB,WAAmC;EAC7D,OAAO,qBAAqB;CAC9B;;;;CAKA,AAAQ,oBAAuD;EAC7D,MAAM,WAA8C,CAAC;EAErD,KAAK,MAAM,CAAC,KAAK,cAAc,OAAO,QAAQ,KAAK,MAAM,GACvD,IAAI,qBAAqB,mBACvB,SAAS,OAAO;EAIpB,OAAO;CACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqCA,AAAgB,aAAa,SAA2B,iBAAmC;EACzF,MAAM,aAA+C,CAAC;EACtD,MAAM,WAAqB,CAAC;EAC5B,MAAM,iBAAiB,WAAW;EAElC,KAAK,MAAM,CAAC,KAAK,cAAc,OAAO,QAAQ,KAAK,MAAM,GAAG;GAE1D,IAAI,qBAAqB,mBAAmB;GAE5C,IAAI,cAAc,UAAU,aAAa,MAAM;GAE/C,IAAI,gBAAgB;IAGlB,IAAI,UAAU,YACZ,cAAcC,4CAAmB,WAAW;IAE9C,SAAS,KAAK,GAAG;GACnB,OAEE,IAAI,CAAC,UAAU,YACb,SAAS,KAAK,GAAG;GAIrB,WAAW,OAAO;EACpB;EAEA,MAAM,SAA2B;GAAE,MAAM;GAAU;EAAW;EAE9D,IAAI,SAAS,SAAS,GAAG,OAAO,WAAW;EAC3C,IAAI,CAAC,KAAK,oBAAoB,OAAO,uBAAuB;EAC5D,IAAI,KAAK,YAAY,uCAAc,QAAQ,MAAM;EAEjD,OAAO;CACT;AACF;;AAGA,SAAS,sBAAsB,KAAU,0BAAU,IAAI,QAAqB,GAAQ;CAElF,IAAI,QAAQ,MACV,OAAO;CAIT,IAAI,MAAM,QAAQ,GAAG,GACnB,OAAO,IAAI,KAAK,SAAS,sBAAsB,MAAM,OAAO,CAAC;CAI/D,IAAI,0CAAe,GAAG,GACpB,OAAO;CAIT,IAAI,QAAQ,IAAI,GAAG,GACjB,OAAO,QAAQ,IAAI,GAAG;CAIxB,MAAM,SAAc,CAAC;CACrB,QAAQ,IAAI,KAAK,MAAM;CAEvB,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,GAAG,GAC3C,IAAI,UAAU,QACZ,OAAO,OAAO,sBAAsB,OAAO,OAAO;CAItD,OAAO;AACT;;;;;AChnBA,MAAa,UAA4B,QAAW,iBAClD,IAAI,gBAAmB,QAAQ,YAAY"}
1
+ {"version":3,"file":"object-DubGC-xu.cjs","names":["BaseValidator","BaseValidator","objectRule","unknownKeyRule","setKeyPath","wrapNullableStrict"],"sources":["../../../../../../seal/src/mutators/object-mutators.ts","../../../../../../seal/src/validators/computed-validator.ts","../../../../../../seal/src/validators/object-validator.ts","../../../../../../seal/src/object.ts"],"sourcesContent":["import { trim } from \"@mongez/reinforcements\";\nimport { isPlainObject } from \"@mongez/supportive-is\";\nimport type { Mutator } from \"../types\";\n\n/** Strip unknown keys from object */\nexport const stripUnknownMutator: Mutator = async (value, context) => {\n const allowedKeys = [\n ...(context?.ctx.schema ? Object.keys(context.ctx.schema) : []),\n ...(context?.options.allowedKeys ?? []),\n ];\n\n const result: Record<string, any> = {};\n\n for (const key in value) {\n if (allowedKeys.includes(key)) {\n result[key] = value[key];\n }\n }\n\n return result;\n};\n\n/** Trim all string values in object */\nexport const objectTrimMutator: Mutator = async (value, context) => {\n if (!isPlainObject(value)) return value;\n\n const result: Record<string, any> = {};\n const recursive = context?.options.recursive ?? false;\n\n for (const key in value) {\n const item = value[key];\n\n if (recursive) {\n if (Array.isArray(item)) {\n result[key] = await Promise.all(\n item.map(async (i: any) =>\n typeof i === \"string\" ? trim(i) : await objectTrimMutator(i, context),\n ),\n );\n } else if (isPlainObject(item)) {\n result[key] = await objectTrimMutator(item, context);\n } else {\n result[key] = typeof item === \"string\" ? trim(item) : item;\n }\n } else {\n result[key] = typeof item === \"string\" ? trim(item) : item;\n }\n }\n\n return result;\n};\n\n/** Parse JSON string */\nexport const jsonMutator: Mutator = async (value) => {\n try {\n return JSON.parse(value);\n } catch {\n return value;\n }\n};\n","import type { SchemaContext, ValidationResult } from \"../types\";\nimport { BaseValidator } from \"./base-validator\";\nimport type { JsonSchemaResult, JsonSchemaTarget } from \"../standard-schema/json-schema\";\n\n/**\n * Callback function for computed fields\n * Receives validated & mutated data and full schema context\n */\nexport type ComputedCallback<TResult = any> = (\n data: any,\n context: SchemaContext,\n) => TResult | Promise<TResult>;\n\n/**\n * Computed field validator\n *\n * Computes a value based on other validated fields in the schema.\n * The computed value is persisted and can optionally be validated.\n *\n * @example\n * ```ts\n * // Basic computed field\n * const schema = v.object({\n * title: v.string().required(),\n * slug: v.computed(data => slugify(data.title)),\n * });\n *\n * // With result validation\n * const schema = v.object({\n * title: v.string().required(),\n * slug: v.computed(\n * data => slugify(data.title),\n * v.string().minLength(3)\n * ),\n * });\n *\n * // Async computation\n * const schema = v.object({\n * image: v.string().url(),\n * thumbnail: v.computed(async data => {\n * return await generateThumbnail(data.image);\n * }),\n * });\n * ```\n */\nexport class ComputedValidator<TResult = any> extends BaseValidator {\n /**\n * Create a new computed field validator\n *\n * @param callback - Function to compute the value from validated data\n * @param resultValidator - Optional validator to validate the computed result\n */\n public constructor(\n protected callback: ComputedCallback<TResult>,\n protected resultValidator?: BaseValidator,\n ) {\n super();\n }\n\n /**\n * Execute the callback and optionally validate the result\n */\n public async validate(data: any, context: SchemaContext): Promise<ValidationResult> {\n try {\n // Execute the callback with validated data\n const result = await this.callback(data, context);\n\n // Optionally validate the computed result\n if (this.resultValidator) {\n const validation = await this.resultValidator.validate(result, context);\n\n if (!validation.isValid) {\n return {\n isValid: false,\n errors: validation.errors,\n data: undefined,\n };\n }\n\n return {\n isValid: true,\n errors: [],\n data: validation.data,\n };\n }\n\n // No validation - return computed result directly\n return {\n isValid: true,\n errors: [],\n data: result,\n };\n } catch (error) {\n // Handle callback execution errors\n return {\n isValid: false,\n errors: [\n {\n type: \"computed\",\n error: error instanceof Error ? error.message : \"Computed field callback failed\",\n input: context.path,\n },\n ],\n data: undefined,\n };\n }\n }\n\n /**\n * Clone this validator with all its configuration\n * Critical for ObjectValidator.clone(), extend(), merge(), etc.\n */\n public override clone(): this {\n const cloned = super.clone();\n\n // Copy ComputedValidator-specific properties\n cloned.callback = this.callback; // Functions are safe to copy by reference\n cloned.resultValidator = this.resultValidator?.clone(); // Deep clone validator\n\n return cloned;\n }\n\n /**\n * Computed fields don't have a specific type to match\n */\n public matchesType(value: any): boolean {\n return true; // Computed fields accept any input (they generate their own value)\n }\n\n /**\n * @inheritdoc\n *\n * Computed fields are server-side runtime values — they have no input schema\n * and cannot be represented in JSON Schema. Calling this method is always a\n * programming error.\n *\n * @throws Error Always throws — computed/managed fields have no JSON Schema representation.\n */\n public override toJsonSchema(_target: JsonSchemaTarget = \"draft-2020-12\"): JsonSchemaResult {\n throw new Error(\n `[Seal] toJsonSchema() is not supported on ComputedValidator / ManagedValidator. ` +\n `Computed fields are runtime-only and have no input JSON Schema representation. ` +\n `ObjectValidator.toJsonSchema() automatically skips computed fields — ` +\n `do not call toJsonSchema() on a computed validator directly.`,\n );\n }\n}\n","import { except } from \"@mongez/reinforcements\";\nimport { isPlainObject } from \"@mongez/supportive-is\";\nimport { setKeyPath } from \"../helpers/path-helpers\";\nimport { objectTrimMutator, stripUnknownMutator } from \"../mutators/object-mutators\";\nimport { objectRule } from \"../rules/common/type-rules\";\nimport { unknownKeyRule } from \"../rules/common/unknown-key\";\nimport type { JsonSchemaResult, JsonSchemaTarget } from \"../standard-schema/json-schema\";\nimport { applyNullable, wrapNullableStrict } from \"../standard-schema/json-schema\";\nimport type { Schema, SchemaContext, ValidationResult } from \"../types\";\nimport type { InferInputObjectShape, InferOutputObjectShape } from \"../types/inference-types\";\nimport { BaseValidator } from \"./base-validator\";\nimport { ComputedValidator } from \"./computed-validator\";\n\n/**\n * Object validator class with generic schema type for proper type inference.\n *\n * Threads two distinct inferred shapes into `BaseValidator`'s `TInput`/`TOutput`:\n *\n * - `TInput` = `InferInputObjectShape<TSchema>` — what `~standard.validate()`\n * accepts (caller's pre-validation payload; defaults/catches make\n * keys optional)\n * - `TOutput` = `InferOutputObjectShape<TSchema>` — what `~standard.validate()`\n * returns on success (post-default, post-catch shape; guaranteed\n * keys are required)\n *\n * Both helpers are used directly (not the full `Infer.*` walker) to avoid a\n * recursive class-base evaluation that would otherwise occur when the\n * brand-aware widening tries to inspect the class type.\n */\nexport class ObjectValidator<TSchema extends Schema = Schema> extends BaseValidator<\n InferInputObjectShape<TSchema>,\n InferOutputObjectShape<TSchema>\n> {\n protected shouldAllowUnknown = false;\n protected allowedKeys: string[] = [];\n protected hasUnknownKeyRule = false;\n\n public constructor(\n public schema: TSchema,\n errorMessage?: string,\n ) {\n super();\n this.addMutableRule(objectRule, errorMessage);\n }\n\n /**\n * Check if value is an object type (plain object, not array or date)\n */\n public matchesType(value: any): boolean {\n return isPlainObject(value);\n }\n\n /** Strip unknown keys from the data */\n public stripUnknown() {\n const validator = this.instance;\n return validator.addMutator(stripUnknownMutator, {\n get allowedKeys() {\n return validator.allowedKeys;\n },\n });\n }\n\n /** Add list of allowed keys that could be in the data but not necessarily validated */\n public allow(...keys: string[]) {\n const validator = this.instance;\n validator.allowedKeys.push(...keys);\n return validator;\n }\n\n /** Trim values of the object properties */\n public trim(recursive = true) {\n const validator = this.instance;\n return validator.addMutator(objectTrimMutator, { recursive });\n }\n\n /** Whether to allow unknown properties\n * Please note it will allow only unknown direct children keys, not nested children keys\n */\n public allowUnknown(allow = true) {\n const validator = this.instance;\n validator.shouldAllowUnknown = allow;\n return validator;\n }\n\n /**\n * Create a copy of this object validator with the same configuration\n * Copies schema, rules, mutators, transformers, and object-specific settings\n *\n * @returns A new ObjectValidator instance with copied configuration\n *\n * @example\n * ```ts\n * const baseUser = v.object({ name: v.string() }).allowUnknown();\n * const userCopy = baseUser.clone();\n * // userCopy has the same schema and allowUnknown setting\n * ```\n */\n public override clone(keys?: string[]): this {\n // Get cloned instance with all BaseValidator properties\n const cloned = super.clone();\n\n // Clone schema with deep copy of validators\n const newSchema = {} as TSchema;\n for (const [key, validator] of Object.entries(this.schema)) {\n if (keys && !keys.includes(key)) continue;\n (newSchema as Schema)[key] = validator.clone();\n }\n\n cloned.schema = newSchema;\n\n // Add ObjectValidator-specific properties\n cloned.shouldAllowUnknown = this.shouldAllowUnknown;\n cloned.allowedKeys = [...this.allowedKeys];\n // NOTE: hasUnknownKeyRule is intentionally NOT copied.\n // Each clone must add its own unknownKeyRule on first validate()\n // so it holds references to its own schema/allowedKeys, not the original's.\n\n return cloned;\n }\n\n /**\n * Extend this schema with additional fields\n * Clones the current validator and adds new fields to the schema\n * **Keeps original configuration** (allowUnknown, stripUnknown, etc.)\n *\n * If an ObjectValidator is provided, only its schema is used - its configuration is ignored.\n * This is useful for creating reusable field collections that can be added to different schemas.\n *\n * @param schemaOrValidator - Plain schema object or ObjectValidator to extend with\n * @returns A new ObjectValidator with merged schema and original configuration\n *\n * @example\n * ```ts\n * // Extend with plain schema\n * const baseUser = v.object({\n * name: v.string().required(),\n * email: v.string().email().required()\n * }).allowUnknown();\n *\n * const adminUser = baseUser.extend({\n * role: v.string().in(['admin', 'superadmin']).required()\n * });\n * // adminUser has: name, email, role\n * // adminUser keeps: allowUnknown() from base ✅\n *\n * // Extend with ObjectValidator (only schema is used)\n * const auditFields = v.object({\n * createdAt: v.date().required(),\n * updatedAt: v.date().required()\n * }).stripUnknown(); // This config is ignored!\n *\n * const fullUser = baseUser.extend(auditFields);\n * // fullUser has: name, email, createdAt, updatedAt\n * // fullUser keeps: allowUnknown() from base (NOT stripUnknown from auditFields) ✅\n *\n * // Chain multiple extends\n * const complexSchema = baseUser\n * .extend(auditFields)\n * .extend({ metadata: v.object({}) });\n * ```\n */\n public extend<TExtension extends Schema>(\n schemaOrValidator: TExtension | ObjectValidator<TExtension>,\n ): ObjectValidator<TSchema & TExtension> {\n // Clone current validator to preserve original\n const extended = this.clone() as any;\n\n // Extract schema from parameter\n const schemaToAdd =\n schemaOrValidator instanceof ObjectValidator ? schemaOrValidator.schema : schemaOrValidator;\n\n // Merge schemas with cloned validators (later fields override earlier ones)\n for (const [key, validator] of Object.entries(schemaToAdd)) {\n (extended.schema as Schema)[key] = validator.clone();\n }\n\n return extended as ObjectValidator<TSchema & TExtension>;\n }\n\n /**\n * Merge with another ObjectValidator\n * Clones current validator, merges schemas, and **overrides configuration** with other validator's config\n *\n * Unlike extend(), merge() combines both schemas AND configurations.\n * The other validator's configuration (allowUnknown, stripUnknown, etc.) takes precedence.\n *\n * @param validator - Another ObjectValidator to merge with\n * @returns A new ObjectValidator with merged schema and configuration\n *\n * @example\n * ```ts\n * const baseUser = v.object({\n * name: v.string().required()\n * }).allowUnknown();\n *\n * const timestamps = v.object({\n * createdAt: v.date().required(),\n * updatedAt: v.date().required()\n * }).stripUnknown();\n *\n * const merged = baseUser.merge(timestamps);\n * // merged has: name, createdAt, updatedAt\n * // merged config: stripUnknown() from timestamps (overrides allowUnknown) ✅\n *\n * // Chain multiple merges\n * const full = baseUser.merge(timestamps).merge(softDeleteSchema);\n * ```\n */\n public merge<TMerge extends Schema>(\n validator: ObjectValidator<TMerge>,\n ): ObjectValidator<TSchema & TMerge> {\n // Clone current validator\n const merged = this.clone() as any;\n\n // Merge schemas with cloned validators (later fields override earlier ones)\n for (const [key, schemaValidator] of Object.entries(validator.schema)) {\n (merged.schema as Schema)[key] = schemaValidator.clone();\n }\n\n // Override configuration with other validator's config\n merged.shouldAllowUnknown = validator.shouldAllowUnknown;\n merged.allowedKeys = [...merged.allowedKeys, ...validator.allowedKeys];\n\n // Append rules, mutators, transformers from other validator\n merged.rules.push(...validator.rules);\n merged.mutators.push(...validator.mutators);\n merged.dataTransformers.push(...validator.dataTransformers);\n\n // Merge attributes text (later wins)\n merged.attributesText = {\n ...merged.attributesText,\n ...validator.attributesText,\n };\n\n merged.translatedAttributes = {\n ...merged.translatedAttributes,\n ...validator.translatedAttributes,\n };\n\n return merged as ObjectValidator<TSchema & TMerge>;\n }\n\n /**\n * Create a new schema with only the specified fields\n * Clones the current validator and keeps only the selected fields\n * **Preserves all configuration** (allowUnknown, stripUnknown, etc.)\n *\n * @param keys - Field names to keep in the schema\n * @returns A new ObjectValidator with only the picked fields\n *\n * @example\n * ```ts\n * const fullUser = v.object({\n * id: v.int().required(),\n * name: v.string().required(),\n * email: v.string().email().required(),\n * password: v.string().required(),\n * role: v.string()\n * }).allowUnknown();\n *\n * // For login - only need email and password\n * const loginSchema = fullUser.pick('email', 'password');\n * // loginSchema has: { email, password }\n * // loginSchema keeps: allowUnknown() ✅\n *\n * // For public profile\n * const publicSchema = fullUser.pick('id', 'name', 'role');\n * // publicSchema has: { id, name, role }\n * ```\n */\n public pick<K extends keyof TSchema>(...keys: K[]): ObjectValidator<Pick<TSchema, K>> {\n // Clone current validator\n const picked = this.clone() as any;\n\n // Create new schema with only picked keys\n const newSchema = {} as Pick<TSchema, K>;\n for (const key of keys) {\n if (key in picked.schema) {\n (newSchema as any)[key] = picked.schema[key];\n }\n }\n\n picked.schema = newSchema;\n\n return picked as ObjectValidator<Pick<TSchema, K>>;\n }\n\n /**\n * Mark all or the given schema fields as optional\n */\n public partial<K extends keyof TSchema>(...keys: K[]) {\n const validationSchema = this.clone();\n\n if (keys.length === 0) {\n keys = Object.keys(validationSchema.schema) as K[];\n }\n\n for (const [key, validator] of Object.entries(validationSchema.schema)) {\n if (keys.length === 0 || keys.includes(key as K)) {\n (validationSchema.schema as Schema)[key] = validator.optional();\n }\n }\n\n return validationSchema;\n }\n\n /**\n * Make the all or the given fields as required\n */\n public requiredFields<K extends keyof TSchema>(...keys: K[]) {\n const validationSchema = this.clone();\n\n if (keys.length === 0) {\n keys = Object.keys(validationSchema.schema) as K[];\n }\n\n for (const [key, validator] of Object.entries(validationSchema.schema)) {\n if (keys.length === 0 || keys.includes(key as K)) {\n (validationSchema.schema as Schema)[key] = validator.required();\n }\n }\n\n return validationSchema;\n }\n\n /**\n * Create a new schema excluding the specified fields\n * Clones the current validator and removes the specified fields\n * **Preserves all configuration** (allowUnknown, stripUnknown, etc.)\n *\n * @param keys - Field names to exclude from the schema\n * @returns A new ObjectValidator without the excluded fields\n *\n * @example\n * ```ts\n * const fullUser = v.object({\n * id: v.int().required(),\n * name: v.string().required(),\n * email: v.string().email().required(),\n * password: v.string().required(),\n * role: v.string()\n * }).allowUnknown();\n *\n * // For updates - exclude id\n * const updateSchema = fullUser.without('id');\n * // updateSchema has: { name, email, password, role }\n * // updateSchema keeps: allowUnknown() ✅\n *\n * // For public API - exclude sensitive fields\n * const publicSchema = fullUser.without('password', 'role');\n * // publicSchema has: { id, name, email }\n *\n * // Combine with other methods\n * const patchSchema = fullUser.without('id', 'password');\n * // patchSchema has: { name, email, role }\n * ```\n */\n public without<K extends keyof TSchema>(...keys: K[]): ObjectValidator<Omit<TSchema, K>> {\n // Clone current validator\n const filtered = this.clone() as any;\n\n // Create new schema excluding specified keys\n const newSchema = {} as Omit<TSchema, K>;\n for (const key in filtered.schema) {\n if (!keys.includes(key as any)) {\n (newSchema as any)[key] = filtered.schema[key];\n }\n }\n\n filtered.schema = newSchema;\n\n return filtered as ObjectValidator<Omit<TSchema, K>>;\n }\n\n /** Mutate the data */\n public mutate(data: any, context: SchemaContext) {\n if (!isPlainObject(data)) return data;\n return super.mutate({ ...data }, context);\n }\n\n /** Validate the data */\n public async validate(\n data: any,\n context: SchemaContext = { path: \"\" } as SchemaContext,\n ): Promise<ValidationResult> {\n context.schema = this.schema;\n const mutatedData = await this.mutate(data, context);\n\n // Check for unknown properties\n if (this.shouldAllowUnknown === false && !this.hasUnknownKeyRule) {\n this.hasUnknownKeyRule = true;\n const rule = this.addMutableRule(unknownKeyRule, undefined, {\n allowedKeys: this.allowedKeys,\n schema: this.schema,\n });\n\n this.setRuleAttributesList(rule);\n }\n\n const result = await super.validate(mutatedData, context);\n\n if (result.isValid === false) return result;\n\n // Absent (no default) or null (nullable) inputs short-circuit here —\n // BaseValidator.validate already resolved the outcome, so recursing into\n // the shape below would wrongly re-run required checks against `undefined`.\n if (mutatedData === undefined || mutatedData === null) return result;\n\n // ═══════════════════════════════════════════════════════════\n // PHASE 1: Validate user input fields (skip computed/managed)\n // ═══════════════════════════════════════════════════════════\n const errors: ValidationResult[\"errors\"] = [];\n const validatedData: any = {};\n\n // Child validators settle in completion order, so errors are collected per\n // field and flattened in declared schema order for deterministic output.\n const fieldErrors: Array<ValidationResult[\"errors\"]> = [];\n\n const userInputEntries = Object.entries(this.schema).filter(\n ([, validator]) => !this.isComputedValidator(validator),\n );\n\n const validationPromises = userInputEntries.map(async ([key, validator], index) => {\n const value =\n mutatedData?.[key] !== undefined ? mutatedData[key] : validator.getDefaultValue();\n\n const childContext: SchemaContext = {\n ...context,\n parent: mutatedData,\n value,\n key,\n path: setKeyPath(context.path, key),\n };\n\n const childResult = await validator.validate(value, childContext);\n\n // Only include in validated data if not omitted\n if (childResult.data !== undefined && !validator.isOmitted()) {\n validatedData[key] = childResult.data;\n }\n\n if (childResult.isValid === false) {\n fieldErrors[index] = childResult.errors;\n }\n });\n\n await Promise.all(validationPromises);\n\n for (const childErrors of fieldErrors) {\n if (childErrors) {\n errors.push(...childErrors);\n }\n }\n\n // If Phase 1 failed, return early\n if (errors.length > 0) {\n return {\n isValid: false,\n errors,\n data: undefined,\n };\n }\n\n // ═══════════════════════════════════════════════════════════\n // PHASE 2: Execute computed/managed fields with validated data\n // ═══════════════════════════════════════════════════════════\n const computedFields = this.getComputedFields();\n\n const computedErrors: Array<ValidationResult[\"errors\"]> = [];\n\n const computedPromises = Object.entries(computedFields).map(async ([key, validator], index) => {\n const childContext: SchemaContext = {\n ...context,\n parent: validatedData,\n value: undefined, // Computed fields don't have input value\n };\n\n // Execute computed callback with validated data\n const childResult = await validator.validate(validatedData, childContext);\n\n // Only include in final data if not omitted\n if (childResult.data !== undefined && !validator.isOmitted()) {\n validatedData[key] = childResult.data;\n }\n\n if (childResult.isValid === false) {\n computedErrors[index] = childResult.errors;\n }\n });\n\n await Promise.all(computedPromises);\n\n for (const childErrors of computedErrors) {\n if (childErrors) {\n errors.push(...childErrors);\n }\n }\n\n // If Phase 2 failed, return early\n if (errors.length > 0) {\n return {\n isValid: false,\n errors,\n data: undefined,\n };\n }\n\n // Fields were assigned in the order their validators finished; re-emit\n // them in declared schema order so the output shape is deterministic.\n const orderedData: any = {};\n\n for (const key of Object.keys(this.schema)) {\n if (key in validatedData) {\n orderedData[key] = validatedData[key];\n }\n }\n\n // Remove undefined values\n const cleanedData = removeUndefinedValues(orderedData);\n\n const transformedData = await this.startTransformationPipeline(cleanedData, context);\n\n const output =\n this.shouldAllowUnknown === false\n ? transformedData\n : {\n ...transformedData,\n ...except(mutatedData, Object.keys(this.schema)),\n };\n\n return {\n isValid: true,\n errors: [],\n data: output,\n };\n }\n\n /**\n * Check if a validator is a computed or managed field\n * ManagedValidator extends ComputedValidator, so instanceof catches both\n */\n private isComputedValidator(validator: BaseValidator): boolean {\n return validator instanceof ComputedValidator;\n }\n\n /**\n * Get all computed/managed fields from the schema\n */\n private getComputedFields(): Record<string, ComputedValidator> {\n const computed: Record<string, ComputedValidator> = {};\n\n for (const [key, validator] of Object.entries(this.schema)) {\n if (validator instanceof ComputedValidator) {\n computed[key] = validator;\n }\n }\n\n return computed;\n }\n\n /**\n * @inheritdoc\n *\n * Recursively generates JSON Schema for all input fields in the schema.\n * Computed/managed fields are skipped — they have no input representation.\n *\n * **Standard targets** (`draft-2020-12`, `draft-07`, `openapi-3.0`):\n * - Fields marked `.optional()` are excluded from `required`.\n *\n * **`openai-strict` target** (OpenAI Structured Outputs):\n * - ALL fields appear in `required` — OpenAI rejects schemas with optional fields.\n * - Optional fields are instead expressed as nullable types:\n * `{ type: [\"string\", \"null\"] }` so the model can output `null` for them.\n * - Recursively applies `openai-strict` to all nested objects.\n *\n * @example\n * ```ts\n * v.object({\n * name: v.string().required(),\n * age: v.int().optional(),\n * }).toJsonSchema(\"draft-2020-12\")\n * // → { type: \"object\",\n * // properties: { name: { type: \"string\" }, age: { type: \"integer\" } },\n * // required: [\"name\"], additionalProperties: false }\n *\n * v.object({\n * name: v.string().required(),\n * age: v.int().optional(),\n * }).toJsonSchema(\"openai-strict\")\n * // → { type: \"object\",\n * // properties: { name: { type: \"string\" }, age: { type: [\"integer\", \"null\"] } },\n * // required: [\"name\", \"age\"], ← all fields\n * // additionalProperties: false }\n * ```\n */\n public override toJsonSchema(target: JsonSchemaTarget = \"draft-2020-12\"): JsonSchemaResult {\n const properties: Record<string, JsonSchemaResult> = {};\n const required: string[] = [];\n const isOpenAIStrict = target === \"openai-strict\";\n\n for (const [key, validator] of Object.entries(this.schema)) {\n // Skip computed/managed — runtime-only, no input schema\n if (validator instanceof ComputedValidator) continue;\n\n let fieldSchema = validator.toJsonSchema(target);\n\n if (isOpenAIStrict) {\n // OpenAI strict: every field must be in required.\n // Optional fields are expressed as nullable rather than absent.\n if (validator.isOptional) {\n fieldSchema = wrapNullableStrict(fieldSchema);\n }\n required.push(key);\n } else {\n // Standard JSON Schema: only non-optional fields go in required\n if (!validator.isOptional) {\n required.push(key);\n }\n }\n\n properties[key] = fieldSchema;\n }\n\n const schema: JsonSchemaResult = { type: \"object\", properties };\n\n if (required.length > 0) schema.required = required;\n if (!this.shouldAllowUnknown) schema.additionalProperties = false;\n if (this.isNullable) applyNullable(schema, target);\n\n return schema;\n }\n}\n\n/** Recursively remove undefined values from an object */\nfunction removeUndefinedValues(obj: any, visited = new WeakMap<object, any>()): any {\n // Handle primitives and null\n if (obj === null) {\n return obj;\n }\n\n // Handle arrays\n if (Array.isArray(obj)) {\n return obj.map((item) => removeUndefinedValues(item, visited));\n }\n\n // Skip non-plain objects (class instances, Dates, Buffers, etc.)\n if (!isPlainObject(obj)) {\n return obj;\n }\n\n // Handle circular references - return already processed result\n if (visited.has(obj)) {\n return visited.get(obj);\n }\n\n // Process plain objects\n const result: any = {};\n visited.set(obj, result); // Mark as processing BEFORE recursion\n\n for (const [key, value] of Object.entries(obj)) {\n if (value !== undefined) {\n result[key] = removeUndefinedValues(value, visited);\n }\n }\n\n return result;\n}\n","import type { StandardSchemaV1 } from \"./standard-schema/types\";\nimport type { Schema } from \"./types\";\nimport type { Infer } from \"./types/inference-types\";\nimport { ObjectValidator } from \"./validators/object-validator\";\n\n// Keep both runtime chain methods and their declaration augmentations in\n// this standalone entry, without loading the complete validator factory.\nexport { equalityConditionalMethodsApplied } from \"./validators/methods/equality-conditional-methods\";\nexport { forbiddenMethodsApplied } from \"./validators/methods/forbidden-methods\";\nexport { presentMethodsApplied } from \"./validators/methods/present-methods\";\nexport { requiredMethodsApplied } from \"./validators/methods/required-methods\";\nexport { ObjectValidator };\n\n/** Create an object schema without importing the complete `v` factory. */\nexport const object = <T extends Schema>(schema: T, errorMessage?: string) =>\n new ObjectValidator<T>(schema, errorMessage) as ObjectValidator<T> &\n StandardSchemaV1<Infer<ObjectValidator<T>>>;\n"],"mappings":"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"}
package/cjs/object.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require_required_methods = require('./required-methods-BOVNRtci.cjs');
3
- const require_object = require('./object-BuKApaax.cjs');
3
+ const require_object = require('./object-DubGC-xu.cjs');
4
4
 
5
5
  exports.ObjectValidator = require_object.ObjectValidator;
6
6
  exports.equalityConditionalMethodsApplied = require_required_methods.equalityConditionalMethodsApplied;
@@ -1 +1 @@
1
- {"version":3,"file":"object-validator.d.mts","names":[],"sources":["../../../../../../../seal/src/validators/object-validator.ts"],"mappings":";;;;;;;;;;;;;AA6BA;;;;;;;;;;;cAAa,eAAA,iBAAgC,MAAA,GAAS,MAAA,UAAgB,aAAA,CACpE,qBAAA,CAAsB,OAAA,GACtB,sBAAA,CAAuB,OAAA;EAOd,MAAA,EAAQ,OAAA;EAAA,UALP,kBAAA;EAAA,UACA,WAAA;EAAA,UACA,iBAAA;cAGD,MAAA,EAAQ,OAAA,EACf,YAAA;EA4HC;;;EAnHI,WAAA,CAAY,KAAA;EAkKA;EA7JZ,YAAA;EA6JJ;EAnJI,KAAA,IAAS,IAAA;EA+M8B;EAxMvC,IAAA,CAAK,SAAA;EAwMsE;;;EAhM3E,YAAA,CAAa,KAAA;EAoN6B;;;;;;;;;;;;;EAjMjC,KAAA,CAAM,IAAA;EAsdoD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAtZnE,MAAA,oBAA0B,MAAA,EAC/B,iBAAA,EAAmB,UAAA,GAAa,eAAA,CAAgB,UAAA,IAC/C,eAAA,CAAgB,OAAA,GAAU,UAAA;EA6CtB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAAA,KAAA,gBAAqB,MAAA,EAC1B,SAAA,EAAW,eAAA,CAAgB,MAAA,IAC1B,eAAA,CAAgB,OAAA,GAAU,MAAA;EAmJE;;;;;;;;;;;;;;;;;;;;;;;;AAkN2D;;;;EAzSnF,IAAA,iBAAqB,OAAA,KAAY,IAAA,EAAM,CAAA,KAAM,eAAA,CAAgB,IAAA,CAAK,OAAA,EAAS,CAAA;;;;EAoB3E,OAAA,iBAAwB,OAAA,KAAY,IAAA,EAAM,CAAA;;;;EAmB1C,cAAA,iBAA+B,OAAA,KAAY,IAAA,EAAM,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgDjD,OAAA,iBAAwB,OAAA,KAAY,IAAA,EAAM,CAAA,KAAM,eAAA,CAAgB,IAAA,CAAK,OAAA,EAAS,CAAA;;EAkB9E,MAAA,CAAO,IAAA,OAAW,OAAA,EAAS,aAAA;;EAMrB,QAAA,CACX,IAAA,OACA,OAAA,GAAS,aAAA,GACR,OAAA,CAAQ,gBAAA;;;;;UAiIH,mBAAA;;;;UAOA,iBAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+CQ,YAAA,CAAa,MAAA,GAAQ,gBAAA,GAAqC,gBAAA;AAAA"}
1
+ {"version":3,"file":"object-validator.d.mts","names":[],"sources":["../../../../../../../seal/src/validators/object-validator.ts"],"mappings":";;;;;;;;;;;;;AA6BA;;;;;;;;;;;cAAa,eAAA,iBAAgC,MAAA,GAAS,MAAA,UAAgB,aAAA,CACpE,qBAAA,CAAsB,OAAA,GACtB,sBAAA,CAAuB,OAAA;EAOd,MAAA,EAAQ,OAAA;EAAA,UALP,kBAAA;EAAA,UACA,WAAA;EAAA,UACA,iBAAA;cAGD,MAAA,EAAQ,OAAA,EACf,YAAA;EA4HC;;;EAnHI,WAAA,CAAY,KAAA;EAkKA;EA7JZ,YAAA;EA6JJ;EAnJI,KAAA,IAAS,IAAA;EA+M8B;EAxMvC,IAAA,CAAK,SAAA;EAwMsE;;;EAhM3E,YAAA,CAAa,KAAA;EAoN6B;;;;;;;;;;;;;EAjMjC,KAAA,CAAM,IAAA;EAkfoD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAlbnE,MAAA,oBAA0B,MAAA,EAC/B,iBAAA,EAAmB,UAAA,GAAa,eAAA,CAAgB,UAAA,IAC/C,eAAA,CAAgB,OAAA,GAAU,UAAA;EA6CtB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAAA,KAAA,gBAAqB,MAAA,EAC1B,SAAA,EAAW,eAAA,CAAgB,MAAA,IAC1B,eAAA,CAAgB,OAAA,GAAU,MAAA;EAmJE;;;;;;;;;;;;;;;;;;;;;;;;AA8O2D;;;;EArUnF,IAAA,iBAAqB,OAAA,KAAY,IAAA,EAAM,CAAA,KAAM,eAAA,CAAgB,IAAA,CAAK,OAAA,EAAS,CAAA;;;;EAoB3E,OAAA,iBAAwB,OAAA,KAAY,IAAA,EAAM,CAAA;;;;EAmB1C,cAAA,iBAA+B,OAAA,KAAY,IAAA,EAAM,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgDjD,OAAA,iBAAwB,OAAA,KAAY,IAAA,EAAM,CAAA,KAAM,eAAA,CAAgB,IAAA,CAAK,OAAA,EAAS,CAAA;;EAkB9E,MAAA,CAAO,IAAA,OAAW,OAAA,EAAS,aAAA;;EAMrB,QAAA,CACX,IAAA,OACA,OAAA,GAAS,aAAA,GACR,OAAA,CAAQ,gBAAA;;;;;UA6JH,mBAAA;;;;UAOA,iBAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+CQ,YAAA,CAAa,MAAA,GAAQ,gBAAA,GAAqC,gBAAA;AAAA"}
@@ -298,7 +298,8 @@ var ObjectValidator = class ObjectValidator extends BaseValidator {
298
298
  if (mutatedData === void 0 || mutatedData === null) return result;
299
299
  const errors = [];
300
300
  const validatedData = {};
301
- const validationPromises = Object.entries(this.schema).filter(([, validator]) => !this.isComputedValidator(validator)).map(async ([key, validator]) => {
301
+ const fieldErrors = [];
302
+ const validationPromises = Object.entries(this.schema).filter(([, validator]) => !this.isComputedValidator(validator)).map(async ([key, validator], index) => {
302
303
  const value = mutatedData?.[key] !== void 0 ? mutatedData[key] : validator.getDefaultValue();
303
304
  const childContext = {
304
305
  ...context,
@@ -309,16 +310,18 @@ var ObjectValidator = class ObjectValidator extends BaseValidator {
309
310
  };
310
311
  const childResult = await validator.validate(value, childContext);
311
312
  if (childResult.data !== void 0 && !validator.isOmitted()) validatedData[key] = childResult.data;
312
- if (childResult.isValid === false) errors.push(...childResult.errors);
313
+ if (childResult.isValid === false) fieldErrors[index] = childResult.errors;
313
314
  });
314
315
  await Promise.all(validationPromises);
316
+ for (const childErrors of fieldErrors) if (childErrors) errors.push(...childErrors);
315
317
  if (errors.length > 0) return {
316
318
  isValid: false,
317
319
  errors,
318
320
  data: void 0
319
321
  };
320
322
  const computedFields = this.getComputedFields();
321
- const computedPromises = Object.entries(computedFields).map(async ([key, validator]) => {
323
+ const computedErrors = [];
324
+ const computedPromises = Object.entries(computedFields).map(async ([key, validator], index) => {
322
325
  const childContext = {
323
326
  ...context,
324
327
  parent: validatedData,
@@ -326,15 +329,18 @@ var ObjectValidator = class ObjectValidator extends BaseValidator {
326
329
  };
327
330
  const childResult = await validator.validate(validatedData, childContext);
328
331
  if (childResult.data !== void 0 && !validator.isOmitted()) validatedData[key] = childResult.data;
329
- if (childResult.isValid === false) errors.push(...childResult.errors);
332
+ if (childResult.isValid === false) computedErrors[index] = childResult.errors;
330
333
  });
331
334
  await Promise.all(computedPromises);
335
+ for (const childErrors of computedErrors) if (childErrors) errors.push(...childErrors);
332
336
  if (errors.length > 0) return {
333
337
  isValid: false,
334
338
  errors,
335
339
  data: void 0
336
340
  };
337
- const cleanedData = removeUndefinedValues(validatedData);
341
+ const orderedData = {};
342
+ for (const key of Object.keys(this.schema)) if (key in validatedData) orderedData[key] = validatedData[key];
343
+ const cleanedData = removeUndefinedValues(orderedData);
338
344
  const transformedData = await this.startTransformationPipeline(cleanedData, context);
339
345
  return {
340
346
  isValid: true,
@@ -1 +1 @@
1
- {"version":3,"file":"object-validator.mjs","names":[],"sources":["../../../../../../../seal/src/validators/object-validator.ts"],"sourcesContent":["import { except } from \"@mongez/reinforcements\";\nimport { isPlainObject } from \"@mongez/supportive-is\";\nimport { setKeyPath } from \"../helpers/path-helpers\";\nimport { objectTrimMutator, stripUnknownMutator } from \"../mutators/object-mutators\";\nimport { objectRule } from \"../rules/common/type-rules\";\nimport { unknownKeyRule } from \"../rules/common/unknown-key\";\nimport type { JsonSchemaResult, JsonSchemaTarget } from \"../standard-schema/json-schema\";\nimport { applyNullable, wrapNullableStrict } from \"../standard-schema/json-schema\";\nimport type { Schema, SchemaContext, ValidationResult } from \"../types\";\nimport type { InferInputObjectShape, InferOutputObjectShape } from \"../types/inference-types\";\nimport { BaseValidator } from \"./base-validator\";\nimport { ComputedValidator } from \"./computed-validator\";\n\n/**\n * Object validator class with generic schema type for proper type inference.\n *\n * Threads two distinct inferred shapes into `BaseValidator`'s `TInput`/`TOutput`:\n *\n * - `TInput` = `InferInputObjectShape<TSchema>` — what `~standard.validate()`\n * accepts (caller's pre-validation payload; defaults/catches make\n * keys optional)\n * - `TOutput` = `InferOutputObjectShape<TSchema>` — what `~standard.validate()`\n * returns on success (post-default, post-catch shape; guaranteed\n * keys are required)\n *\n * Both helpers are used directly (not the full `Infer.*` walker) to avoid a\n * recursive class-base evaluation that would otherwise occur when the\n * brand-aware widening tries to inspect the class type.\n */\nexport class ObjectValidator<TSchema extends Schema = Schema> extends BaseValidator<\n InferInputObjectShape<TSchema>,\n InferOutputObjectShape<TSchema>\n> {\n protected shouldAllowUnknown = false;\n protected allowedKeys: string[] = [];\n protected hasUnknownKeyRule = false;\n\n public constructor(\n public schema: TSchema,\n errorMessage?: string,\n ) {\n super();\n this.addMutableRule(objectRule, errorMessage);\n }\n\n /**\n * Check if value is an object type (plain object, not array or date)\n */\n public matchesType(value: any): boolean {\n return isPlainObject(value);\n }\n\n /** Strip unknown keys from the data */\n public stripUnknown() {\n const validator = this.instance;\n return validator.addMutator(stripUnknownMutator, {\n get allowedKeys() {\n return validator.allowedKeys;\n },\n });\n }\n\n /** Add list of allowed keys that could be in the data but not necessarily validated */\n public allow(...keys: string[]) {\n const validator = this.instance;\n validator.allowedKeys.push(...keys);\n return validator;\n }\n\n /** Trim values of the object properties */\n public trim(recursive = true) {\n const validator = this.instance;\n return validator.addMutator(objectTrimMutator, { recursive });\n }\n\n /** Whether to allow unknown properties\n * Please note it will allow only unknown direct children keys, not nested children keys\n */\n public allowUnknown(allow = true) {\n const validator = this.instance;\n validator.shouldAllowUnknown = allow;\n return validator;\n }\n\n /**\n * Create a copy of this object validator with the same configuration\n * Copies schema, rules, mutators, transformers, and object-specific settings\n *\n * @returns A new ObjectValidator instance with copied configuration\n *\n * @example\n * ```ts\n * const baseUser = v.object({ name: v.string() }).allowUnknown();\n * const userCopy = baseUser.clone();\n * // userCopy has the same schema and allowUnknown setting\n * ```\n */\n public override clone(keys?: string[]): this {\n // Get cloned instance with all BaseValidator properties\n const cloned = super.clone();\n\n // Clone schema with deep copy of validators\n const newSchema = {} as TSchema;\n for (const [key, validator] of Object.entries(this.schema)) {\n if (keys && !keys.includes(key)) continue;\n (newSchema as Schema)[key] = validator.clone();\n }\n\n cloned.schema = newSchema;\n\n // Add ObjectValidator-specific properties\n cloned.shouldAllowUnknown = this.shouldAllowUnknown;\n cloned.allowedKeys = [...this.allowedKeys];\n // NOTE: hasUnknownKeyRule is intentionally NOT copied.\n // Each clone must add its own unknownKeyRule on first validate()\n // so it holds references to its own schema/allowedKeys, not the original's.\n\n return cloned;\n }\n\n /**\n * Extend this schema with additional fields\n * Clones the current validator and adds new fields to the schema\n * **Keeps original configuration** (allowUnknown, stripUnknown, etc.)\n *\n * If an ObjectValidator is provided, only its schema is used - its configuration is ignored.\n * This is useful for creating reusable field collections that can be added to different schemas.\n *\n * @param schemaOrValidator - Plain schema object or ObjectValidator to extend with\n * @returns A new ObjectValidator with merged schema and original configuration\n *\n * @example\n * ```ts\n * // Extend with plain schema\n * const baseUser = v.object({\n * name: v.string().required(),\n * email: v.string().email().required()\n * }).allowUnknown();\n *\n * const adminUser = baseUser.extend({\n * role: v.string().in(['admin', 'superadmin']).required()\n * });\n * // adminUser has: name, email, role\n * // adminUser keeps: allowUnknown() from base ✅\n *\n * // Extend with ObjectValidator (only schema is used)\n * const auditFields = v.object({\n * createdAt: v.date().required(),\n * updatedAt: v.date().required()\n * }).stripUnknown(); // This config is ignored!\n *\n * const fullUser = baseUser.extend(auditFields);\n * // fullUser has: name, email, createdAt, updatedAt\n * // fullUser keeps: allowUnknown() from base (NOT stripUnknown from auditFields) ✅\n *\n * // Chain multiple extends\n * const complexSchema = baseUser\n * .extend(auditFields)\n * .extend({ metadata: v.object({}) });\n * ```\n */\n public extend<TExtension extends Schema>(\n schemaOrValidator: TExtension | ObjectValidator<TExtension>,\n ): ObjectValidator<TSchema & TExtension> {\n // Clone current validator to preserve original\n const extended = this.clone() as any;\n\n // Extract schema from parameter\n const schemaToAdd =\n schemaOrValidator instanceof ObjectValidator ? schemaOrValidator.schema : schemaOrValidator;\n\n // Merge schemas with cloned validators (later fields override earlier ones)\n for (const [key, validator] of Object.entries(schemaToAdd)) {\n (extended.schema as Schema)[key] = validator.clone();\n }\n\n return extended as ObjectValidator<TSchema & TExtension>;\n }\n\n /**\n * Merge with another ObjectValidator\n * Clones current validator, merges schemas, and **overrides configuration** with other validator's config\n *\n * Unlike extend(), merge() combines both schemas AND configurations.\n * The other validator's configuration (allowUnknown, stripUnknown, etc.) takes precedence.\n *\n * @param validator - Another ObjectValidator to merge with\n * @returns A new ObjectValidator with merged schema and configuration\n *\n * @example\n * ```ts\n * const baseUser = v.object({\n * name: v.string().required()\n * }).allowUnknown();\n *\n * const timestamps = v.object({\n * createdAt: v.date().required(),\n * updatedAt: v.date().required()\n * }).stripUnknown();\n *\n * const merged = baseUser.merge(timestamps);\n * // merged has: name, createdAt, updatedAt\n * // merged config: stripUnknown() from timestamps (overrides allowUnknown) ✅\n *\n * // Chain multiple merges\n * const full = baseUser.merge(timestamps).merge(softDeleteSchema);\n * ```\n */\n public merge<TMerge extends Schema>(\n validator: ObjectValidator<TMerge>,\n ): ObjectValidator<TSchema & TMerge> {\n // Clone current validator\n const merged = this.clone() as any;\n\n // Merge schemas with cloned validators (later fields override earlier ones)\n for (const [key, schemaValidator] of Object.entries(validator.schema)) {\n (merged.schema as Schema)[key] = schemaValidator.clone();\n }\n\n // Override configuration with other validator's config\n merged.shouldAllowUnknown = validator.shouldAllowUnknown;\n merged.allowedKeys = [...merged.allowedKeys, ...validator.allowedKeys];\n\n // Append rules, mutators, transformers from other validator\n merged.rules.push(...validator.rules);\n merged.mutators.push(...validator.mutators);\n merged.dataTransformers.push(...validator.dataTransformers);\n\n // Merge attributes text (later wins)\n merged.attributesText = {\n ...merged.attributesText,\n ...validator.attributesText,\n };\n\n merged.translatedAttributes = {\n ...merged.translatedAttributes,\n ...validator.translatedAttributes,\n };\n\n return merged as ObjectValidator<TSchema & TMerge>;\n }\n\n /**\n * Create a new schema with only the specified fields\n * Clones the current validator and keeps only the selected fields\n * **Preserves all configuration** (allowUnknown, stripUnknown, etc.)\n *\n * @param keys - Field names to keep in the schema\n * @returns A new ObjectValidator with only the picked fields\n *\n * @example\n * ```ts\n * const fullUser = v.object({\n * id: v.int().required(),\n * name: v.string().required(),\n * email: v.string().email().required(),\n * password: v.string().required(),\n * role: v.string()\n * }).allowUnknown();\n *\n * // For login - only need email and password\n * const loginSchema = fullUser.pick('email', 'password');\n * // loginSchema has: { email, password }\n * // loginSchema keeps: allowUnknown() ✅\n *\n * // For public profile\n * const publicSchema = fullUser.pick('id', 'name', 'role');\n * // publicSchema has: { id, name, role }\n * ```\n */\n public pick<K extends keyof TSchema>(...keys: K[]): ObjectValidator<Pick<TSchema, K>> {\n // Clone current validator\n const picked = this.clone() as any;\n\n // Create new schema with only picked keys\n const newSchema = {} as Pick<TSchema, K>;\n for (const key of keys) {\n if (key in picked.schema) {\n (newSchema as any)[key] = picked.schema[key];\n }\n }\n\n picked.schema = newSchema;\n\n return picked as ObjectValidator<Pick<TSchema, K>>;\n }\n\n /**\n * Mark all or the given schema fields as optional\n */\n public partial<K extends keyof TSchema>(...keys: K[]) {\n const validationSchema = this.clone();\n\n if (keys.length === 0) {\n keys = Object.keys(validationSchema.schema) as K[];\n }\n\n for (const [key, validator] of Object.entries(validationSchema.schema)) {\n if (keys.length === 0 || keys.includes(key as K)) {\n (validationSchema.schema as Schema)[key] = validator.optional();\n }\n }\n\n return validationSchema;\n }\n\n /**\n * Make the all or the given fields as required\n */\n public requiredFields<K extends keyof TSchema>(...keys: K[]) {\n const validationSchema = this.clone();\n\n if (keys.length === 0) {\n keys = Object.keys(validationSchema.schema) as K[];\n }\n\n for (const [key, validator] of Object.entries(validationSchema.schema)) {\n if (keys.length === 0 || keys.includes(key as K)) {\n (validationSchema.schema as Schema)[key] = validator.required();\n }\n }\n\n return validationSchema;\n }\n\n /**\n * Create a new schema excluding the specified fields\n * Clones the current validator and removes the specified fields\n * **Preserves all configuration** (allowUnknown, stripUnknown, etc.)\n *\n * @param keys - Field names to exclude from the schema\n * @returns A new ObjectValidator without the excluded fields\n *\n * @example\n * ```ts\n * const fullUser = v.object({\n * id: v.int().required(),\n * name: v.string().required(),\n * email: v.string().email().required(),\n * password: v.string().required(),\n * role: v.string()\n * }).allowUnknown();\n *\n * // For updates - exclude id\n * const updateSchema = fullUser.without('id');\n * // updateSchema has: { name, email, password, role }\n * // updateSchema keeps: allowUnknown() ✅\n *\n * // For public API - exclude sensitive fields\n * const publicSchema = fullUser.without('password', 'role');\n * // publicSchema has: { id, name, email }\n *\n * // Combine with other methods\n * const patchSchema = fullUser.without('id', 'password');\n * // patchSchema has: { name, email, role }\n * ```\n */\n public without<K extends keyof TSchema>(...keys: K[]): ObjectValidator<Omit<TSchema, K>> {\n // Clone current validator\n const filtered = this.clone() as any;\n\n // Create new schema excluding specified keys\n const newSchema = {} as Omit<TSchema, K>;\n for (const key in filtered.schema) {\n if (!keys.includes(key as any)) {\n (newSchema as any)[key] = filtered.schema[key];\n }\n }\n\n filtered.schema = newSchema;\n\n return filtered as ObjectValidator<Omit<TSchema, K>>;\n }\n\n /** Mutate the data */\n public mutate(data: any, context: SchemaContext) {\n if (!isPlainObject(data)) return data;\n return super.mutate({ ...data }, context);\n }\n\n /** Validate the data */\n public async validate(\n data: any,\n context: SchemaContext = { path: \"\" } as SchemaContext,\n ): Promise<ValidationResult> {\n context.schema = this.schema;\n const mutatedData = await this.mutate(data, context);\n\n // Check for unknown properties\n if (this.shouldAllowUnknown === false && !this.hasUnknownKeyRule) {\n this.hasUnknownKeyRule = true;\n const rule = this.addMutableRule(unknownKeyRule, undefined, {\n allowedKeys: this.allowedKeys,\n schema: this.schema,\n });\n\n this.setRuleAttributesList(rule);\n }\n\n const result = await super.validate(mutatedData, context);\n\n if (result.isValid === false) return result;\n\n // Absent (no default) or null (nullable) inputs short-circuit here —\n // BaseValidator.validate already resolved the outcome, so recursing into\n // the shape below would wrongly re-run required checks against `undefined`.\n if (mutatedData === undefined || mutatedData === null) return result;\n\n // ═══════════════════════════════════════════════════════════\n // PHASE 1: Validate user input fields (skip computed/managed)\n // ═══════════════════════════════════════════════════════════\n const errors: ValidationResult[\"errors\"] = [];\n const validatedData: any = {};\n\n const userInputEntries = Object.entries(this.schema).filter(\n ([, validator]) => !this.isComputedValidator(validator),\n );\n\n const validationPromises = userInputEntries.map(async ([key, validator]) => {\n const value =\n mutatedData?.[key] !== undefined ? mutatedData[key] : validator.getDefaultValue();\n\n const childContext: SchemaContext = {\n ...context,\n parent: mutatedData,\n value,\n key,\n path: setKeyPath(context.path, key),\n };\n\n const childResult = await validator.validate(value, childContext);\n\n // Only include in validated data if not omitted\n if (childResult.data !== undefined && !validator.isOmitted()) {\n validatedData[key] = childResult.data;\n }\n\n if (childResult.isValid === false) {\n errors.push(...childResult.errors);\n }\n });\n\n await Promise.all(validationPromises);\n\n // If Phase 1 failed, return early\n if (errors.length > 0) {\n return {\n isValid: false,\n errors,\n data: undefined,\n };\n }\n\n // ═══════════════════════════════════════════════════════════\n // PHASE 2: Execute computed/managed fields with validated data\n // ═══════════════════════════════════════════════════════════\n const computedFields = this.getComputedFields();\n\n const computedPromises = Object.entries(computedFields).map(async ([key, validator]) => {\n const childContext: SchemaContext = {\n ...context,\n parent: validatedData,\n value: undefined, // Computed fields don't have input value\n };\n\n // Execute computed callback with validated data\n const childResult = await validator.validate(validatedData, childContext);\n\n // Only include in final data if not omitted\n if (childResult.data !== undefined && !validator.isOmitted()) {\n validatedData[key] = childResult.data;\n }\n\n if (childResult.isValid === false) {\n errors.push(...childResult.errors);\n }\n });\n\n await Promise.all(computedPromises);\n\n // If Phase 2 failed, return early\n if (errors.length > 0) {\n return {\n isValid: false,\n errors,\n data: undefined,\n };\n }\n\n // Remove undefined values\n const cleanedData = removeUndefinedValues(validatedData);\n\n const transformedData = await this.startTransformationPipeline(cleanedData, context);\n\n const output =\n this.shouldAllowUnknown === false\n ? transformedData\n : {\n ...transformedData,\n ...except(mutatedData, Object.keys(this.schema)),\n };\n\n return {\n isValid: true,\n errors: [],\n data: output,\n };\n }\n\n /**\n * Check if a validator is a computed or managed field\n * ManagedValidator extends ComputedValidator, so instanceof catches both\n */\n private isComputedValidator(validator: BaseValidator): boolean {\n return validator instanceof ComputedValidator;\n }\n\n /**\n * Get all computed/managed fields from the schema\n */\n private getComputedFields(): Record<string, ComputedValidator> {\n const computed: Record<string, ComputedValidator> = {};\n\n for (const [key, validator] of Object.entries(this.schema)) {\n if (validator instanceof ComputedValidator) {\n computed[key] = validator;\n }\n }\n\n return computed;\n }\n\n /**\n * @inheritdoc\n *\n * Recursively generates JSON Schema for all input fields in the schema.\n * Computed/managed fields are skipped — they have no input representation.\n *\n * **Standard targets** (`draft-2020-12`, `draft-07`, `openapi-3.0`):\n * - Fields marked `.optional()` are excluded from `required`.\n *\n * **`openai-strict` target** (OpenAI Structured Outputs):\n * - ALL fields appear in `required` — OpenAI rejects schemas with optional fields.\n * - Optional fields are instead expressed as nullable types:\n * `{ type: [\"string\", \"null\"] }` so the model can output `null` for them.\n * - Recursively applies `openai-strict` to all nested objects.\n *\n * @example\n * ```ts\n * v.object({\n * name: v.string().required(),\n * age: v.int().optional(),\n * }).toJsonSchema(\"draft-2020-12\")\n * // → { type: \"object\",\n * // properties: { name: { type: \"string\" }, age: { type: \"integer\" } },\n * // required: [\"name\"], additionalProperties: false }\n *\n * v.object({\n * name: v.string().required(),\n * age: v.int().optional(),\n * }).toJsonSchema(\"openai-strict\")\n * // → { type: \"object\",\n * // properties: { name: { type: \"string\" }, age: { type: [\"integer\", \"null\"] } },\n * // required: [\"name\", \"age\"], ← all fields\n * // additionalProperties: false }\n * ```\n */\n public override toJsonSchema(target: JsonSchemaTarget = \"draft-2020-12\"): JsonSchemaResult {\n const properties: Record<string, JsonSchemaResult> = {};\n const required: string[] = [];\n const isOpenAIStrict = target === \"openai-strict\";\n\n for (const [key, validator] of Object.entries(this.schema)) {\n // Skip computed/managed — runtime-only, no input schema\n if (validator instanceof ComputedValidator) continue;\n\n let fieldSchema = validator.toJsonSchema(target);\n\n if (isOpenAIStrict) {\n // OpenAI strict: every field must be in required.\n // Optional fields are expressed as nullable rather than absent.\n if (validator.isOptional) {\n fieldSchema = wrapNullableStrict(fieldSchema);\n }\n required.push(key);\n } else {\n // Standard JSON Schema: only non-optional fields go in required\n if (!validator.isOptional) {\n required.push(key);\n }\n }\n\n properties[key] = fieldSchema;\n }\n\n const schema: JsonSchemaResult = { type: \"object\", properties };\n\n if (required.length > 0) schema.required = required;\n if (!this.shouldAllowUnknown) schema.additionalProperties = false;\n if (this.isNullable) applyNullable(schema, target);\n\n return schema;\n }\n}\n\n/** Recursively remove undefined values from an object */\nfunction removeUndefinedValues(obj: any, visited = new WeakMap<object, any>()): any {\n // Handle primitives and null\n if (obj === null) {\n return obj;\n }\n\n // Handle arrays\n if (Array.isArray(obj)) {\n return obj.map((item) => removeUndefinedValues(item, visited));\n }\n\n // Skip non-plain objects (class instances, Dates, Buffers, etc.)\n if (!isPlainObject(obj)) {\n return obj;\n }\n\n // Handle circular references - return already processed result\n if (visited.has(obj)) {\n return visited.get(obj);\n }\n\n // Process plain objects\n const result: any = {};\n visited.set(obj, result); // Mark as processing BEFORE recursion\n\n for (const [key, value] of Object.entries(obj)) {\n if (value !== undefined) {\n result[key] = removeUndefinedValues(value, visited);\n }\n }\n\n return result;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"object-validator.mjs","names":[],"sources":["../../../../../../../seal/src/validators/object-validator.ts"],"sourcesContent":["import { except } from \"@mongez/reinforcements\";\nimport { isPlainObject } from \"@mongez/supportive-is\";\nimport { setKeyPath } from \"../helpers/path-helpers\";\nimport { objectTrimMutator, stripUnknownMutator } from \"../mutators/object-mutators\";\nimport { objectRule } from \"../rules/common/type-rules\";\nimport { unknownKeyRule } from \"../rules/common/unknown-key\";\nimport type { JsonSchemaResult, JsonSchemaTarget } from \"../standard-schema/json-schema\";\nimport { applyNullable, wrapNullableStrict } from \"../standard-schema/json-schema\";\nimport type { Schema, SchemaContext, ValidationResult } from \"../types\";\nimport type { InferInputObjectShape, InferOutputObjectShape } from \"../types/inference-types\";\nimport { BaseValidator } from \"./base-validator\";\nimport { ComputedValidator } from \"./computed-validator\";\n\n/**\n * Object validator class with generic schema type for proper type inference.\n *\n * Threads two distinct inferred shapes into `BaseValidator`'s `TInput`/`TOutput`:\n *\n * - `TInput` = `InferInputObjectShape<TSchema>` — what `~standard.validate()`\n * accepts (caller's pre-validation payload; defaults/catches make\n * keys optional)\n * - `TOutput` = `InferOutputObjectShape<TSchema>` — what `~standard.validate()`\n * returns on success (post-default, post-catch shape; guaranteed\n * keys are required)\n *\n * Both helpers are used directly (not the full `Infer.*` walker) to avoid a\n * recursive class-base evaluation that would otherwise occur when the\n * brand-aware widening tries to inspect the class type.\n */\nexport class ObjectValidator<TSchema extends Schema = Schema> extends BaseValidator<\n InferInputObjectShape<TSchema>,\n InferOutputObjectShape<TSchema>\n> {\n protected shouldAllowUnknown = false;\n protected allowedKeys: string[] = [];\n protected hasUnknownKeyRule = false;\n\n public constructor(\n public schema: TSchema,\n errorMessage?: string,\n ) {\n super();\n this.addMutableRule(objectRule, errorMessage);\n }\n\n /**\n * Check if value is an object type (plain object, not array or date)\n */\n public matchesType(value: any): boolean {\n return isPlainObject(value);\n }\n\n /** Strip unknown keys from the data */\n public stripUnknown() {\n const validator = this.instance;\n return validator.addMutator(stripUnknownMutator, {\n get allowedKeys() {\n return validator.allowedKeys;\n },\n });\n }\n\n /** Add list of allowed keys that could be in the data but not necessarily validated */\n public allow(...keys: string[]) {\n const validator = this.instance;\n validator.allowedKeys.push(...keys);\n return validator;\n }\n\n /** Trim values of the object properties */\n public trim(recursive = true) {\n const validator = this.instance;\n return validator.addMutator(objectTrimMutator, { recursive });\n }\n\n /** Whether to allow unknown properties\n * Please note it will allow only unknown direct children keys, not nested children keys\n */\n public allowUnknown(allow = true) {\n const validator = this.instance;\n validator.shouldAllowUnknown = allow;\n return validator;\n }\n\n /**\n * Create a copy of this object validator with the same configuration\n * Copies schema, rules, mutators, transformers, and object-specific settings\n *\n * @returns A new ObjectValidator instance with copied configuration\n *\n * @example\n * ```ts\n * const baseUser = v.object({ name: v.string() }).allowUnknown();\n * const userCopy = baseUser.clone();\n * // userCopy has the same schema and allowUnknown setting\n * ```\n */\n public override clone(keys?: string[]): this {\n // Get cloned instance with all BaseValidator properties\n const cloned = super.clone();\n\n // Clone schema with deep copy of validators\n const newSchema = {} as TSchema;\n for (const [key, validator] of Object.entries(this.schema)) {\n if (keys && !keys.includes(key)) continue;\n (newSchema as Schema)[key] = validator.clone();\n }\n\n cloned.schema = newSchema;\n\n // Add ObjectValidator-specific properties\n cloned.shouldAllowUnknown = this.shouldAllowUnknown;\n cloned.allowedKeys = [...this.allowedKeys];\n // NOTE: hasUnknownKeyRule is intentionally NOT copied.\n // Each clone must add its own unknownKeyRule on first validate()\n // so it holds references to its own schema/allowedKeys, not the original's.\n\n return cloned;\n }\n\n /**\n * Extend this schema with additional fields\n * Clones the current validator and adds new fields to the schema\n * **Keeps original configuration** (allowUnknown, stripUnknown, etc.)\n *\n * If an ObjectValidator is provided, only its schema is used - its configuration is ignored.\n * This is useful for creating reusable field collections that can be added to different schemas.\n *\n * @param schemaOrValidator - Plain schema object or ObjectValidator to extend with\n * @returns A new ObjectValidator with merged schema and original configuration\n *\n * @example\n * ```ts\n * // Extend with plain schema\n * const baseUser = v.object({\n * name: v.string().required(),\n * email: v.string().email().required()\n * }).allowUnknown();\n *\n * const adminUser = baseUser.extend({\n * role: v.string().in(['admin', 'superadmin']).required()\n * });\n * // adminUser has: name, email, role\n * // adminUser keeps: allowUnknown() from base ✅\n *\n * // Extend with ObjectValidator (only schema is used)\n * const auditFields = v.object({\n * createdAt: v.date().required(),\n * updatedAt: v.date().required()\n * }).stripUnknown(); // This config is ignored!\n *\n * const fullUser = baseUser.extend(auditFields);\n * // fullUser has: name, email, createdAt, updatedAt\n * // fullUser keeps: allowUnknown() from base (NOT stripUnknown from auditFields) ✅\n *\n * // Chain multiple extends\n * const complexSchema = baseUser\n * .extend(auditFields)\n * .extend({ metadata: v.object({}) });\n * ```\n */\n public extend<TExtension extends Schema>(\n schemaOrValidator: TExtension | ObjectValidator<TExtension>,\n ): ObjectValidator<TSchema & TExtension> {\n // Clone current validator to preserve original\n const extended = this.clone() as any;\n\n // Extract schema from parameter\n const schemaToAdd =\n schemaOrValidator instanceof ObjectValidator ? schemaOrValidator.schema : schemaOrValidator;\n\n // Merge schemas with cloned validators (later fields override earlier ones)\n for (const [key, validator] of Object.entries(schemaToAdd)) {\n (extended.schema as Schema)[key] = validator.clone();\n }\n\n return extended as ObjectValidator<TSchema & TExtension>;\n }\n\n /**\n * Merge with another ObjectValidator\n * Clones current validator, merges schemas, and **overrides configuration** with other validator's config\n *\n * Unlike extend(), merge() combines both schemas AND configurations.\n * The other validator's configuration (allowUnknown, stripUnknown, etc.) takes precedence.\n *\n * @param validator - Another ObjectValidator to merge with\n * @returns A new ObjectValidator with merged schema and configuration\n *\n * @example\n * ```ts\n * const baseUser = v.object({\n * name: v.string().required()\n * }).allowUnknown();\n *\n * const timestamps = v.object({\n * createdAt: v.date().required(),\n * updatedAt: v.date().required()\n * }).stripUnknown();\n *\n * const merged = baseUser.merge(timestamps);\n * // merged has: name, createdAt, updatedAt\n * // merged config: stripUnknown() from timestamps (overrides allowUnknown) ✅\n *\n * // Chain multiple merges\n * const full = baseUser.merge(timestamps).merge(softDeleteSchema);\n * ```\n */\n public merge<TMerge extends Schema>(\n validator: ObjectValidator<TMerge>,\n ): ObjectValidator<TSchema & TMerge> {\n // Clone current validator\n const merged = this.clone() as any;\n\n // Merge schemas with cloned validators (later fields override earlier ones)\n for (const [key, schemaValidator] of Object.entries(validator.schema)) {\n (merged.schema as Schema)[key] = schemaValidator.clone();\n }\n\n // Override configuration with other validator's config\n merged.shouldAllowUnknown = validator.shouldAllowUnknown;\n merged.allowedKeys = [...merged.allowedKeys, ...validator.allowedKeys];\n\n // Append rules, mutators, transformers from other validator\n merged.rules.push(...validator.rules);\n merged.mutators.push(...validator.mutators);\n merged.dataTransformers.push(...validator.dataTransformers);\n\n // Merge attributes text (later wins)\n merged.attributesText = {\n ...merged.attributesText,\n ...validator.attributesText,\n };\n\n merged.translatedAttributes = {\n ...merged.translatedAttributes,\n ...validator.translatedAttributes,\n };\n\n return merged as ObjectValidator<TSchema & TMerge>;\n }\n\n /**\n * Create a new schema with only the specified fields\n * Clones the current validator and keeps only the selected fields\n * **Preserves all configuration** (allowUnknown, stripUnknown, etc.)\n *\n * @param keys - Field names to keep in the schema\n * @returns A new ObjectValidator with only the picked fields\n *\n * @example\n * ```ts\n * const fullUser = v.object({\n * id: v.int().required(),\n * name: v.string().required(),\n * email: v.string().email().required(),\n * password: v.string().required(),\n * role: v.string()\n * }).allowUnknown();\n *\n * // For login - only need email and password\n * const loginSchema = fullUser.pick('email', 'password');\n * // loginSchema has: { email, password }\n * // loginSchema keeps: allowUnknown() ✅\n *\n * // For public profile\n * const publicSchema = fullUser.pick('id', 'name', 'role');\n * // publicSchema has: { id, name, role }\n * ```\n */\n public pick<K extends keyof TSchema>(...keys: K[]): ObjectValidator<Pick<TSchema, K>> {\n // Clone current validator\n const picked = this.clone() as any;\n\n // Create new schema with only picked keys\n const newSchema = {} as Pick<TSchema, K>;\n for (const key of keys) {\n if (key in picked.schema) {\n (newSchema as any)[key] = picked.schema[key];\n }\n }\n\n picked.schema = newSchema;\n\n return picked as ObjectValidator<Pick<TSchema, K>>;\n }\n\n /**\n * Mark all or the given schema fields as optional\n */\n public partial<K extends keyof TSchema>(...keys: K[]) {\n const validationSchema = this.clone();\n\n if (keys.length === 0) {\n keys = Object.keys(validationSchema.schema) as K[];\n }\n\n for (const [key, validator] of Object.entries(validationSchema.schema)) {\n if (keys.length === 0 || keys.includes(key as K)) {\n (validationSchema.schema as Schema)[key] = validator.optional();\n }\n }\n\n return validationSchema;\n }\n\n /**\n * Make the all or the given fields as required\n */\n public requiredFields<K extends keyof TSchema>(...keys: K[]) {\n const validationSchema = this.clone();\n\n if (keys.length === 0) {\n keys = Object.keys(validationSchema.schema) as K[];\n }\n\n for (const [key, validator] of Object.entries(validationSchema.schema)) {\n if (keys.length === 0 || keys.includes(key as K)) {\n (validationSchema.schema as Schema)[key] = validator.required();\n }\n }\n\n return validationSchema;\n }\n\n /**\n * Create a new schema excluding the specified fields\n * Clones the current validator and removes the specified fields\n * **Preserves all configuration** (allowUnknown, stripUnknown, etc.)\n *\n * @param keys - Field names to exclude from the schema\n * @returns A new ObjectValidator without the excluded fields\n *\n * @example\n * ```ts\n * const fullUser = v.object({\n * id: v.int().required(),\n * name: v.string().required(),\n * email: v.string().email().required(),\n * password: v.string().required(),\n * role: v.string()\n * }).allowUnknown();\n *\n * // For updates - exclude id\n * const updateSchema = fullUser.without('id');\n * // updateSchema has: { name, email, password, role }\n * // updateSchema keeps: allowUnknown() ✅\n *\n * // For public API - exclude sensitive fields\n * const publicSchema = fullUser.without('password', 'role');\n * // publicSchema has: { id, name, email }\n *\n * // Combine with other methods\n * const patchSchema = fullUser.without('id', 'password');\n * // patchSchema has: { name, email, role }\n * ```\n */\n public without<K extends keyof TSchema>(...keys: K[]): ObjectValidator<Omit<TSchema, K>> {\n // Clone current validator\n const filtered = this.clone() as any;\n\n // Create new schema excluding specified keys\n const newSchema = {} as Omit<TSchema, K>;\n for (const key in filtered.schema) {\n if (!keys.includes(key as any)) {\n (newSchema as any)[key] = filtered.schema[key];\n }\n }\n\n filtered.schema = newSchema;\n\n return filtered as ObjectValidator<Omit<TSchema, K>>;\n }\n\n /** Mutate the data */\n public mutate(data: any, context: SchemaContext) {\n if (!isPlainObject(data)) return data;\n return super.mutate({ ...data }, context);\n }\n\n /** Validate the data */\n public async validate(\n data: any,\n context: SchemaContext = { path: \"\" } as SchemaContext,\n ): Promise<ValidationResult> {\n context.schema = this.schema;\n const mutatedData = await this.mutate(data, context);\n\n // Check for unknown properties\n if (this.shouldAllowUnknown === false && !this.hasUnknownKeyRule) {\n this.hasUnknownKeyRule = true;\n const rule = this.addMutableRule(unknownKeyRule, undefined, {\n allowedKeys: this.allowedKeys,\n schema: this.schema,\n });\n\n this.setRuleAttributesList(rule);\n }\n\n const result = await super.validate(mutatedData, context);\n\n if (result.isValid === false) return result;\n\n // Absent (no default) or null (nullable) inputs short-circuit here —\n // BaseValidator.validate already resolved the outcome, so recursing into\n // the shape below would wrongly re-run required checks against `undefined`.\n if (mutatedData === undefined || mutatedData === null) return result;\n\n // ═══════════════════════════════════════════════════════════\n // PHASE 1: Validate user input fields (skip computed/managed)\n // ═══════════════════════════════════════════════════════════\n const errors: ValidationResult[\"errors\"] = [];\n const validatedData: any = {};\n\n // Child validators settle in completion order, so errors are collected per\n // field and flattened in declared schema order for deterministic output.\n const fieldErrors: Array<ValidationResult[\"errors\"]> = [];\n\n const userInputEntries = Object.entries(this.schema).filter(\n ([, validator]) => !this.isComputedValidator(validator),\n );\n\n const validationPromises = userInputEntries.map(async ([key, validator], index) => {\n const value =\n mutatedData?.[key] !== undefined ? mutatedData[key] : validator.getDefaultValue();\n\n const childContext: SchemaContext = {\n ...context,\n parent: mutatedData,\n value,\n key,\n path: setKeyPath(context.path, key),\n };\n\n const childResult = await validator.validate(value, childContext);\n\n // Only include in validated data if not omitted\n if (childResult.data !== undefined && !validator.isOmitted()) {\n validatedData[key] = childResult.data;\n }\n\n if (childResult.isValid === false) {\n fieldErrors[index] = childResult.errors;\n }\n });\n\n await Promise.all(validationPromises);\n\n for (const childErrors of fieldErrors) {\n if (childErrors) {\n errors.push(...childErrors);\n }\n }\n\n // If Phase 1 failed, return early\n if (errors.length > 0) {\n return {\n isValid: false,\n errors,\n data: undefined,\n };\n }\n\n // ═══════════════════════════════════════════════════════════\n // PHASE 2: Execute computed/managed fields with validated data\n // ═══════════════════════════════════════════════════════════\n const computedFields = this.getComputedFields();\n\n const computedErrors: Array<ValidationResult[\"errors\"]> = [];\n\n const computedPromises = Object.entries(computedFields).map(async ([key, validator], index) => {\n const childContext: SchemaContext = {\n ...context,\n parent: validatedData,\n value: undefined, // Computed fields don't have input value\n };\n\n // Execute computed callback with validated data\n const childResult = await validator.validate(validatedData, childContext);\n\n // Only include in final data if not omitted\n if (childResult.data !== undefined && !validator.isOmitted()) {\n validatedData[key] = childResult.data;\n }\n\n if (childResult.isValid === false) {\n computedErrors[index] = childResult.errors;\n }\n });\n\n await Promise.all(computedPromises);\n\n for (const childErrors of computedErrors) {\n if (childErrors) {\n errors.push(...childErrors);\n }\n }\n\n // If Phase 2 failed, return early\n if (errors.length > 0) {\n return {\n isValid: false,\n errors,\n data: undefined,\n };\n }\n\n // Fields were assigned in the order their validators finished; re-emit\n // them in declared schema order so the output shape is deterministic.\n const orderedData: any = {};\n\n for (const key of Object.keys(this.schema)) {\n if (key in validatedData) {\n orderedData[key] = validatedData[key];\n }\n }\n\n // Remove undefined values\n const cleanedData = removeUndefinedValues(orderedData);\n\n const transformedData = await this.startTransformationPipeline(cleanedData, context);\n\n const output =\n this.shouldAllowUnknown === false\n ? transformedData\n : {\n ...transformedData,\n ...except(mutatedData, Object.keys(this.schema)),\n };\n\n return {\n isValid: true,\n errors: [],\n data: output,\n };\n }\n\n /**\n * Check if a validator is a computed or managed field\n * ManagedValidator extends ComputedValidator, so instanceof catches both\n */\n private isComputedValidator(validator: BaseValidator): boolean {\n return validator instanceof ComputedValidator;\n }\n\n /**\n * Get all computed/managed fields from the schema\n */\n private getComputedFields(): Record<string, ComputedValidator> {\n const computed: Record<string, ComputedValidator> = {};\n\n for (const [key, validator] of Object.entries(this.schema)) {\n if (validator instanceof ComputedValidator) {\n computed[key] = validator;\n }\n }\n\n return computed;\n }\n\n /**\n * @inheritdoc\n *\n * Recursively generates JSON Schema for all input fields in the schema.\n * Computed/managed fields are skipped — they have no input representation.\n *\n * **Standard targets** (`draft-2020-12`, `draft-07`, `openapi-3.0`):\n * - Fields marked `.optional()` are excluded from `required`.\n *\n * **`openai-strict` target** (OpenAI Structured Outputs):\n * - ALL fields appear in `required` — OpenAI rejects schemas with optional fields.\n * - Optional fields are instead expressed as nullable types:\n * `{ type: [\"string\", \"null\"] }` so the model can output `null` for them.\n * - Recursively applies `openai-strict` to all nested objects.\n *\n * @example\n * ```ts\n * v.object({\n * name: v.string().required(),\n * age: v.int().optional(),\n * }).toJsonSchema(\"draft-2020-12\")\n * // → { type: \"object\",\n * // properties: { name: { type: \"string\" }, age: { type: \"integer\" } },\n * // required: [\"name\"], additionalProperties: false }\n *\n * v.object({\n * name: v.string().required(),\n * age: v.int().optional(),\n * }).toJsonSchema(\"openai-strict\")\n * // → { type: \"object\",\n * // properties: { name: { type: \"string\" }, age: { type: [\"integer\", \"null\"] } },\n * // required: [\"name\", \"age\"], ← all fields\n * // additionalProperties: false }\n * ```\n */\n public override toJsonSchema(target: JsonSchemaTarget = \"draft-2020-12\"): JsonSchemaResult {\n const properties: Record<string, JsonSchemaResult> = {};\n const required: string[] = [];\n const isOpenAIStrict = target === \"openai-strict\";\n\n for (const [key, validator] of Object.entries(this.schema)) {\n // Skip computed/managed — runtime-only, no input schema\n if (validator instanceof ComputedValidator) continue;\n\n let fieldSchema = validator.toJsonSchema(target);\n\n if (isOpenAIStrict) {\n // OpenAI strict: every field must be in required.\n // Optional fields are expressed as nullable rather than absent.\n if (validator.isOptional) {\n fieldSchema = wrapNullableStrict(fieldSchema);\n }\n required.push(key);\n } else {\n // Standard JSON Schema: only non-optional fields go in required\n if (!validator.isOptional) {\n required.push(key);\n }\n }\n\n properties[key] = fieldSchema;\n }\n\n const schema: JsonSchemaResult = { type: \"object\", properties };\n\n if (required.length > 0) schema.required = required;\n if (!this.shouldAllowUnknown) schema.additionalProperties = false;\n if (this.isNullable) applyNullable(schema, target);\n\n return schema;\n }\n}\n\n/** Recursively remove undefined values from an object */\nfunction removeUndefinedValues(obj: any, visited = new WeakMap<object, any>()): any {\n // Handle primitives and null\n if (obj === null) {\n return obj;\n }\n\n // Handle arrays\n if (Array.isArray(obj)) {\n return obj.map((item) => removeUndefinedValues(item, visited));\n }\n\n // Skip non-plain objects (class instances, Dates, Buffers, etc.)\n if (!isPlainObject(obj)) {\n return obj;\n }\n\n // Handle circular references - return already processed result\n if (visited.has(obj)) {\n return visited.get(obj);\n }\n\n // Process plain objects\n const result: any = {};\n visited.set(obj, result); // Mark as processing BEFORE recursion\n\n for (const [key, value] of Object.entries(obj)) {\n if (value !== undefined) {\n result[key] = removeUndefinedValues(value, visited);\n }\n }\n\n return result;\n}\n"],"mappings":"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"}
package/llms-full.txt CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  ---
10
10
  name: bridge-standard-schema
11
- description: 'Diagnose `StandardSchemaV1` interop — phantom-intersection at the `v` factory return, why typed slots reject a schema, cascade `Model<TSchema>` variance. Triggers: `StandardSchemaV1`, `~standard`, `Infer`, `ObjectValidator`, `StringValidator`, `LiteralValidator`, `BaseValidator`, `Model<TSchema>`, `StandardJSONSchemaV1`, `Result<unknown>`; "StandardSchemaV1 slot rejecting my schema", "Result<unknown> error", "drop as unknown as cast", "cascade Model schema variance"; typical import `import { v, type Infer } from "@warlock.js/seal"`. Skip: foundations — `@warlock.js/seal/seal-basics/SKILL.md`; JSON Schema gen — `@warlock.js/seal/generate-json-schema/SKILL.md`; competing spec `@standard-schema/spec`.'
11
+ description: 'Diagnose `StandardSchemaV1` interop — phantom-intersection at the `v` factory return, why typed slots reject a schema, cascade `Model<TSchema>` variance. Triggers: `StandardSchemaV1`, `~standard`, `Infer`, `ObjectValidator`, `StringValidator`, `LiteralValidator`, `BaseValidator`, `Model<TSchema>`, `StandardJSONSchemaV1`, `Result<unknown>`; "StandardSchemaV1 slot rejecting my schema", "Result<unknown> error", "drop as unknown as cast", "cascade Model schema variance"; typical import `import { v, type Infer } from "@warlock.js/seal"`. Skip: foundations — the `seal-basics` topic; JSON Schema gen — the `generate-json-schema` topic; competing spec `@standard-schema/spec`.'
12
12
  ---
13
13
 
14
14
  # The Standard Schema bridge
@@ -150,12 +150,12 @@ If V2 lands, re-fork. Until then the local copy is the right call.
150
150
 
151
151
  ---
152
152
  name: compose-seal-modifiers
153
- description: 'Apply cross-cutting modifiers — `.optional` / `.nullable` / `.default` / `.catch` / `.omit` / membership rules — plus the mutator-vs-transformer pipeline and `Infer.Input` vs `Infer.Output`. Triggers: `.optional`, `.required`, `.nullable`, `.default`, `.catch`, `.present`, `.requiredIf`, `.requiredWith`, `.in`, `.oneOf`, `.notIn`, `.omit`, `.attribute`, `.mutable`, `.addMutator`, `Infer.Input`, `Infer.Output`; "mark field optional in seal", "default value in schema", "mutator vs transformer", "when does .catch fire"; typical import `import { v, type Infer } from "@warlock.js/seal"`. Skip: primitives — `@warlock.js/seal/pick-seal-primitive/SKILL.md`; containers — `@warlock.js/seal/define-structural-shape/SKILL.md`; errors — `@warlock.js/seal/handle-seal-errors/SKILL.md`; competing `zod` `.optional`/`.default`.'
153
+ description: 'Apply cross-cutting modifiers — `.optional` / `.nullable` / `.default` / `.catch` / `.omit` / membership rules — plus the mutator-vs-transformer pipeline and `Infer.Input` vs `Infer.Output`. Triggers: `.optional`, `.required`, `.nullable`, `.default`, `.catch`, `.present`, `.requiredIf`, `.requiredWith`, `.in`, `.oneOf`, `.notIn`, `.omit`, `.attribute`, `.mutable`, `.addMutator`, `Infer.Input`, `Infer.Output`; "mark field optional in seal", "default value in schema", "mutator vs transformer", "when does .catch fire"; typical import `import { v, type Infer } from "@warlock.js/seal"`. Skip: primitives — the `pick-seal-primitive` topic; containers — the `define-structural-shape` topic; errors — the `handle-seal-errors` topic; competing `zod` `.optional`/`.default`.'
154
154
  ---
155
155
 
156
156
  # Cross-cutting modifiers + the pipeline
157
157
 
158
- This skill covers methods that work on **every** validator. Type-specific methods (`.email()`, `.min()` on strings, `.between()` on numbers, `.weekDay()` on dates, etc.) live in the per-type method references — see [`@warlock.js/seal/pick-seal-primitive/SKILL.md`](@warlock.js/seal/pick-seal-primitive/SKILL.md) and [`@warlock.js/seal/define-structural-shape/SKILL.md`](@warlock.js/seal/define-structural-shape/SKILL.md).
158
+ This skill covers methods that work on **every** validator. Type-specific methods (`.email()`, `.min()` on strings, `.between()` on numbers, `.weekDay()` on dates, etc.) live in the per-type method references — see the `pick-seal-primitive` topic and the `define-structural-shape` topic.
159
159
 
160
160
  ## The pipeline
161
161
 
@@ -423,7 +423,7 @@ Switch back with `.immutable`. Default is fine 99% of the time. Reach for `.muta
423
423
 
424
424
  ---
425
425
  name: define-structural-shape
426
- description: 'Compose `v.object` / `v.array` / `v.record` / `v.tuple` / `v.union` / `v.discriminatedUnion` / `v.lazy`. Triggers: `v.object`, `v.array`, `v.record`, `v.tuple`, `v.union`, `v.discriminatedUnion`, `v.lazy`, `ObjectValidator`; "how do I build an object schema", "dynamic-keyed record", "tagged union with discriminator", "recursive schema", "self-referencing schema"; typical import `import { v, type Infer } from "@warlock.js/seal"`. Skip: leaf primitives — `@warlock.js/seal/pick-seal-primitive/SKILL.md`; modifiers — `@warlock.js/seal/compose-seal-modifiers/SKILL.md`; standard-schema bridge — `@warlock.js/seal/bridge-standard-schema/SKILL.md`; competing libs `zod`, `valibot`.'
426
+ description: 'Compose `v.object` / `v.array` / `v.record` / `v.tuple` / `v.union` / `v.discriminatedUnion` / `v.lazy`. Triggers: `v.object`, `v.array`, `v.record`, `v.tuple`, `v.union`, `v.discriminatedUnion`, `v.lazy`, `ObjectValidator`; "how do I build an object schema", "dynamic-keyed record", "tagged union with discriminator", "recursive schema", "self-referencing schema"; typical import `import { v, type Infer } from "@warlock.js/seal"`. Skip: leaf primitives — the `pick-seal-primitive` topic; modifiers — the `compose-seal-modifiers` topic; standard-schema bridge — the `bridge-standard-schema` topic; competing libs `zod`, `valibot`.'
427
427
  ---
428
428
 
429
429
  # Structural validators — picking guide
@@ -547,11 +547,11 @@ Use for: trees (categories, file systems), threaded data (comment chains), mutua
547
547
  | One of N scalar types | `v.union([...])` |
548
548
  | One of N object shapes with a tag field | `v.discriminatedUnion(key, [...])` |
549
549
  | Self-referencing or forward reference | `v.lazy(() => schema)` |
550
- | One of N constants | `v.literal(...values)` (not structural — see [`@warlock.js/seal/pick-seal-primitive/SKILL.md`](@warlock.js/seal/pick-seal-primitive/SKILL.md)) |
550
+ | One of N constants | `v.literal(...values)` (not structural — see the `pick-seal-primitive` topic) |
551
551
 
552
552
  ## A note on cascade Models
553
553
 
554
- `@warlock.js/cascade` Models declare `static schema: ObjectValidator<TSchema>`. Passing a `v.object({...})` works directly — the factory return widens to fit the Model's invariant generic without breaking type checking. If TS complains *"`ObjectValidator<{specific}>` is not assignable to `ObjectValidator<TSchema>`"*, that's the variance trap and the answer isn't to widen the schema — it's almost always that `Model<TSchema>` was parameterized with a hand-rolled type that drifted from the schema's inferred shape. Fix the type, not the schema. See [`@warlock.js/seal/bridge-standard-schema/SKILL.md`](@warlock.js/seal/bridge-standard-schema/SKILL.md).
554
+ `@warlock.js/cascade` Models declare `static schema: ObjectValidator<TSchema>`. Passing a `v.object({...})` works directly — the factory return widens to fit the Model's invariant generic without breaking type checking. If TS complains *"`ObjectValidator<{specific}>` is not assignable to `ObjectValidator<TSchema>`"*, that's the variance trap and the answer isn't to widen the schema — it's almost always that `Model<TSchema>` was parameterized with a hand-rolled type that drifted from the schema's inferred shape. Fix the type, not the schema. See the `bridge-standard-schema` topic.
555
555
 
556
556
  ## Method-surface reference
557
557
 
@@ -565,7 +565,7 @@ Use for: trees (categories, file systems), threaded data (comment chains), mutua
565
565
 
566
566
  # `v.array(itemValidator)` — method reference
567
567
 
568
- The inner validator runs against each element. Failure on any element fails the array. Picking guide (array vs tuple vs record) is in [`@warlock.js/seal/define-structural-shape/SKILL.md`](@warlock.js/seal/define-structural-shape/SKILL.md).
568
+ The inner validator runs against each element. Failure on any element fails the array. Picking guide (array vs tuple vs record) is in the `define-structural-shape` topic.
569
569
 
570
570
  ## Length
571
571
 
@@ -647,7 +647,7 @@ v.array(v.string()).default([]).optional()
647
647
 
648
648
  # `v.object({...})` — method reference
649
649
 
650
- Picking guide for structural validators is in [`@warlock.js/seal/define-structural-shape/SKILL.md`](@warlock.js/seal/define-structural-shape/SKILL.md). For field-level chaining (`.required` / `.optional` / `.attribute`), see [`@warlock.js/seal/compose-seal-modifiers/SKILL.md`](@warlock.js/seal/compose-seal-modifiers/SKILL.md).
650
+ Picking guide for structural validators is in the `define-structural-shape` topic. For field-level chaining (`.required` / `.optional` / `.attribute`), see the `compose-seal-modifiers` topic.
651
651
 
652
652
  ## Schema composition
653
653
 
@@ -837,7 +837,7 @@ const signupSchema = v.object({
837
837
 
838
838
  ---
839
839
  name: extend-seal-with-plugins
840
- description: 'Author a custom seal plugin to add validator methods (`.slug`, `.postalCode`, etc) — `SealPlugin` shape, `registerPlugin` lifecycle, TS prototype augmentation. Triggers: `SealPlugin`, `registerPlugin`, `unregisterPlugin`, `hasPlugin`, `getInstalledPlugins`, `StringValidator`, `NumberValidator`, `DateValidator`, `BaseValidator`, `install`, `uninstall`; "how do I add a custom validator method", "write a seal plugin", "extend v.string with .slug", "module augmentation for seal"; typical import `import { StringValidator, registerPlugin, type SealPlugin } from "@warlock.js/seal"`. Skip: built-in primitives — `@warlock.js/seal/pick-seal-primitive/SKILL.md`; bridge typing — `@warlock.js/seal/bridge-standard-schema/SKILL.md`; competing `zod` `.refine`/`.transform`.'
840
+ description: 'Author a custom seal plugin to add validator methods (`.slug`, `.postalCode`, etc) — `SealPlugin` shape, `registerPlugin` lifecycle, TS prototype augmentation. Triggers: `SealPlugin`, `registerPlugin`, `unregisterPlugin`, `hasPlugin`, `getInstalledPlugins`, `StringValidator`, `NumberValidator`, `DateValidator`, `BaseValidator`, `install`, `uninstall`; "how do I add a custom validator method", "write a seal plugin", "extend v.string with .slug", "module augmentation for seal"; typical import `import { StringValidator, registerPlugin, type SealPlugin } from "@warlock.js/seal"`. Skip: built-in primitives — the `pick-seal-primitive` topic; bridge typing — the `bridge-standard-schema` topic; competing `zod` `.refine`/`.transform`.'
841
841
  ---
842
842
 
843
843
  # Extend seal with plugins
@@ -846,7 +846,7 @@ Seal exposes a plugin system so you can add validator methods without forking th
846
846
 
847
847
  ## When to reach for a plugin
848
848
 
849
- - The validator method you want **does not exist** in built-in seal — check [`@warlock.js/seal/pick-seal-primitive/SKILL.md`](@warlock.js/seal/pick-seal-primitive/SKILL.md) and its method references first.
849
+ - The validator method you want **does not exist** in built-in seal — check the `pick-seal-primitive` topic and its method references first.
850
850
  - The validation is **stable and reusable** across modules — domain-specific formats (IBAN, postal codes, tax IDs, license plates, internal ID schemes).
851
851
  - You want the **chainable syntax** — `v.string().slug()` reads better than `v.string().pattern(/.../).addMutator(s => slugify(s))` at every call site.
852
852
 
@@ -937,7 +937,7 @@ declare module "@warlock.js/seal" {
937
937
 
938
938
  After this file is included in the project's `tsconfig.json` (via `include` or a side-effect import), `v.string().slug()` autocompletes and type-checks everywhere.
939
939
 
940
- **Important.** The augmentation has to declare on the **class** (`StringValidator`), not the factory return type. The factory return widens with `& StandardSchemaV1<...>` (see [`@warlock.js/seal/bridge-standard-schema/SKILL.md`](@warlock.js/seal/bridge-standard-schema/SKILL.md)) — augmentations on the intersection don't propagate. Patch the class, augment the class; the factory return picks up the new methods through structural inference.
940
+ **Important.** The augmentation has to declare on the **class** (`StringValidator`), not the factory return type. The factory return widens with `& StandardSchemaV1<...>` (see the `bridge-standard-schema` topic) — augmentations on the intersection don't propagate. Patch the class, augment the class; the factory return picks up the new methods through structural inference.
941
941
 
942
942
  ## Larger example — postal codes per country
943
943
 
@@ -1019,7 +1019,7 @@ Use these in startup diagnostics or in tests that need to assert a plugin is reg
1019
1019
 
1020
1020
  ---
1021
1021
  name: generate-json-schema
1022
- description: 'Generate JSON Schema via `schema.toJsonSchema(target)` — `draft-2020-12` / `draft-07` / `openapi-3.0` / `openai-strict`. Triggers: `toJsonSchema`, `JsonSchemaTarget`, `draft-2020-12`, `draft-07`, `openapi-3.0`, `openai-strict`, `response_format`, `additionalProperties`, `nullable`; "how do I generate JSON Schema from seal", "OpenAI structured outputs from schema", "OpenAPI 3.0 nullable", "json_schema strict mode"; typical import `import { v } from "@warlock.js/seal"`. Skip: foundations — `@warlock.js/seal/seal-basics/SKILL.md`; bridge typing — `@warlock.js/seal/bridge-standard-schema/SKILL.md`; competing libs `zod-to-json-schema`, `ajv`, `@anatine/zod-openapi`.'
1022
+ description: 'Generate JSON Schema via `schema.toJsonSchema(target)` — `draft-2020-12` / `draft-07` / `openapi-3.0` / `openai-strict`. Triggers: `toJsonSchema`, `JsonSchemaTarget`, `draft-2020-12`, `draft-07`, `openapi-3.0`, `openai-strict`, `response_format`, `additionalProperties`, `nullable`; "how do I generate JSON Schema from seal", "OpenAI structured outputs from schema", "OpenAPI 3.0 nullable", "json_schema strict mode"; typical import `import { v } from "@warlock.js/seal"`. Skip: foundations — the `seal-basics` topic; bridge typing — the `bridge-standard-schema` topic; competing libs `zod-to-json-schema`, `ajv`, `@anatine/zod-openapi`.'
1023
1023
  ---
1024
1024
 
1025
1025
  # JSON Schema generation
@@ -1166,7 +1166,7 @@ Generating JSON Schema is cheap (pure-function tree walk), so don't worry about
1166
1166
 
1167
1167
  ---
1168
1168
  name: handle-seal-errors
1169
- description: 'Read `ValidationResult` — `isValid`, `errors[]`, `data`. Branch on `error.type`, customize messages, hook translation. Triggers: `ValidationResult`, `ValidationError`, `validate`, `isValid`, `errors`, `data`, `error.type`, `error.input`, `error.error`, `translationParams`, `.attribute`, `SealConfig`; "how to read seal errors", "branch on a specific rule failure", "customize validation error message", "translate seal error", "surface validation errors as 422"; typical import `import { validate, v } from "@warlock.js/seal"`. Skip: modifiers — `@warlock.js/seal/compose-seal-modifiers/SKILL.md`; structural shapes — `@warlock.js/seal/define-structural-shape/SKILL.md`; competing libs `zod` `.safeParse`, `yup` `ValidationError`, `joi`.'
1169
+ description: 'Read `ValidationResult` — `isValid`, `errors[]`, `data`. Branch on `error.type`, customize messages, hook translation. Triggers: `ValidationResult`, `ValidationError`, `validate`, `isValid`, `errors`, `data`, `error.type`, `error.input`, `error.error`, `translationParams`, `.attribute`, `SealConfig`; "how to read seal errors", "branch on a specific rule failure", "customize validation error message", "translate seal error", "surface validation errors as 422"; typical import `import { validate, v } from "@warlock.js/seal"`. Skip: modifiers — the `compose-seal-modifiers` topic; structural shapes — the `define-structural-shape` topic; competing libs `zod` `.safeParse`, `yup` `ValidationError`, `joi`.'
1170
1170
  ---
1171
1171
 
1172
1172
  # `ValidationResult` — reading errors
@@ -1345,32 +1345,32 @@ Eight task skills. Most schemas only need `seal-basics` + `pick-seal-primitive`
1345
1345
 
1346
1346
  ### Foundations
1347
1347
 
1348
- #### [`seal-basics/`](../seal-basics/SKILL.md)
1348
+ #### `seal-basics`
1349
1349
  Start here. The `v` factory, `validate(schema, data)`, and `Infer<typeof schema>`.
1350
1350
 
1351
1351
  ### Building schemas
1352
1352
 
1353
- #### [`pick-seal-primitive/`](../pick-seal-primitive/SKILL.md)
1353
+ #### `pick-seal-primitive`
1354
1354
  Choose the right primitive — `string` / `int` / `literal` / `date` / `enum` / `computed` / `managed` / `instanceof` / `any`. Covers the close calls (`string` vs `scalar`, `int` vs `number`, `literal` vs `enum`).
1355
1355
 
1356
- #### [`define-structural-shape/`](../define-structural-shape/SKILL.md)
1356
+ #### `define-structural-shape`
1357
1357
  Compose `v.object` / `v.array` / `v.record` / `v.tuple` / `v.union` / `v.discriminatedUnion` / `v.lazy` — object schemas, dynamic-keyed records, tagged unions, recursive shapes.
1358
1358
 
1359
- #### [`compose-seal-modifiers/`](../compose-seal-modifiers/SKILL.md)
1359
+ #### `compose-seal-modifiers`
1360
1360
  Cross-cutting modifiers — `.optional` / `.nullable` / `.default` / `.catch` / `.omit` / membership rules — plus the mutator-vs-transformer pipeline and `Infer.Input` vs `Infer.Output`.
1361
1361
 
1362
1362
  ### Output + interop
1363
1363
 
1364
- #### [`handle-seal-errors/`](../handle-seal-errors/SKILL.md)
1364
+ #### `handle-seal-errors`
1365
1365
  Read a `ValidationResult` — `isValid`, `errors[]`, `data`. Branch on `error.type`, customize messages, hook translation.
1366
1366
 
1367
- #### [`generate-json-schema/`](../generate-json-schema/SKILL.md)
1367
+ #### `generate-json-schema`
1368
1368
  `schema.toJsonSchema(target)` — draft-2020-12 / draft-07 / openapi-3.0 / openai-strict. For OpenAI structured outputs, OpenAPI specs, any JSON-Schema consumer.
1369
1369
 
1370
- #### [`bridge-standard-schema/`](../bridge-standard-schema/SKILL.md)
1370
+ #### `bridge-standard-schema`
1371
1371
  Standard Schema interop — why a `StandardSchemaV1<T>` slot might reject a schema, the phantom-intersection at the `v` factory return, Cascade `Model<TSchema>` variance. For migrating off `as unknown as` casts.
1372
1372
 
1373
- #### [`extend-seal-with-plugins/`](../extend-seal-with-plugins/SKILL.md)
1373
+ #### `extend-seal-with-plugins`
1374
1374
  Author a plugin to add validator methods (`.slug`, `.postalCode`, …) — the `SealPlugin` shape, `registerPlugin` lifecycle, TS prototype augmentation.
1375
1375
 
1376
1376
  ## Configuration
@@ -1385,8 +1385,8 @@ Author a plugin to add validator methods (`.slug`, `.postalCode`, …) — the `
1385
1385
 
1386
1386
  ## See also
1387
1387
 
1388
- - [`@warlock.js/cascade`](../../../cascade/skills/cascade-basics/SKILL.md) — uses seal schemas as `Model.schema`.
1389
- - [`@warlock.js/core`](../../../core/skills/validate-input/SKILL.md) — re-exports `v` (aliased) and adds framework validators in `core/v`.
1388
+ - `@warlock.js/cascade` (the `cascade-basics` topic of the `warlock-js-cascade` skill) — uses seal schemas as `Model.schema`.
1389
+ - `@warlock.js/core` (the `validate-input` topic of the `warlock-js-core` skill) — re-exports `v` (aliased) and adds framework validators in `core/v`.
1390
1390
  - [`mongez-agent-kit-authoring-skills`](../../../../domains/shared/skills/) (load via agent-kit sync) — how this becomes `.claude/skills/warlock-js-seal-overview/`.
1391
1391
 
1392
1392
 
@@ -1394,7 +1394,7 @@ Author a plugin to add validator methods (`.slug`, `.postalCode`, …) — the `
1394
1394
 
1395
1395
  ---
1396
1396
  name: pick-seal-primitive
1397
- description: 'Pick the right `v` factory primitive — string / int / literal / date / enum / computed / managed / instanceof / any. Triggers: `v.string`, `v.email`, `v.number`, `v.int`, `v.float`, `v.numeric`, `v.boolean`, `v.scalar`, `v.date`, `v.literal`, `v.enum`, `v.instanceof`, `v.computed`, `v.managed`, `v.any`; "v.string vs v.scalar", "v.literal vs v.enum", "v.date vs v.instanceof(Date)", "what is v.computed"; typical import `import { v } from "@warlock.js/seal"`. Skip: structural shapes — `@warlock.js/seal/define-structural-shape/SKILL.md`; modifiers — `@warlock.js/seal/compose-seal-modifiers/SKILL.md`; competing libs `zod`, `valibot`, `yup`.'
1397
+ description: 'Pick the right `v` factory primitive — string / int / literal / date / enum / computed / managed / instanceof / any. Triggers: `v.string`, `v.email`, `v.number`, `v.int`, `v.float`, `v.numeric`, `v.boolean`, `v.scalar`, `v.date`, `v.literal`, `v.enum`, `v.instanceof`, `v.computed`, `v.managed`, `v.any`; "v.string vs v.scalar", "v.literal vs v.enum", "v.date vs v.instanceof(Date)", "what is v.computed"; typical import `import { v } from "@warlock.js/seal"`. Skip: structural shapes — the `define-structural-shape` topic; modifiers — the `compose-seal-modifiers` topic; competing libs `zod`, `valibot`, `yup`.'
1398
1398
  ---
1399
1399
 
1400
1400
  # Picking the right primitive
@@ -1518,7 +1518,7 @@ Both are **skipped** when their parent `v.object` generates JSON Schema — they
1518
1518
  | Date in the past | `v.date().past()` |
1519
1519
  | File upload | `v.instanceof(File)` |
1520
1520
  | Class instance (not Date) | `v.instanceof(Ctor)` |
1521
- | Discriminated union | see [`@warlock.js/seal/define-structural-shape/SKILL.md`](@warlock.js/seal/define-structural-shape/SKILL.md) |
1521
+ | Discriminated union | see the `define-structural-shape` topic |
1522
1522
  | Derived value (computed from siblings) | `v.computed<T>(callback)` |
1523
1523
  | Framework-injected value | `v.managed<T>(callback)` |
1524
1524
  | Free-form / pass-through | `v.any()` (only when you've thought about it) |
@@ -1532,7 +1532,7 @@ Each primitive's full method list lives in a sibling file:
1532
1532
  - [`date-methods.md`](./date-methods.md) — `.min` / `.before` / `.after` / `.today` / `.past` / `.future` / `.weekDay` / `.minAge` / `.year` / `.quarter` / `.toISOString` / …
1533
1533
  - [`boolean-methods.md`](./boolean-methods.md) — `.accepted` / `.declined` / `.mustBeTrue` / `.mustBeFalse` / `.acceptedIf` / `.declinedWithout` / …
1534
1534
 
1535
- For cross-cutting modifiers (`.optional`/`.nullable`/`.default`/`.catch`/`.in`/`.oneOf`), see [`@warlock.js/seal/compose-seal-modifiers/SKILL.md`](@warlock.js/seal/compose-seal-modifiers/SKILL.md). For containers (object/array/record/tuple/union), see [`@warlock.js/seal/define-structural-shape/SKILL.md`](@warlock.js/seal/define-structural-shape/SKILL.md).
1535
+ For cross-cutting modifiers (`.optional`/`.nullable`/`.default`/`.catch`/`.in`/`.oneOf`), see the `compose-seal-modifiers` topic. For containers (object/array/record/tuple/union), see the `define-structural-shape` topic.
1536
1536
 
1537
1537
  ## Things NOT to do
1538
1538
 
@@ -1547,7 +1547,7 @@ For cross-cutting modifiers (`.optional`/`.nullable`/`.default`/`.catch`/`.in`/`
1547
1547
 
1548
1548
  # `v.boolean()` — method reference
1549
1549
 
1550
- Picking guide is in [`@warlock.js/seal/pick-seal-primitive/SKILL.md`](@warlock.js/seal/pick-seal-primitive/SKILL.md). Membership rules (`.in`/`.oneOf`) inherited from PrimitiveValidator — see [`@warlock.js/seal/compose-seal-modifiers/SKILL.md`](@warlock.js/seal/compose-seal-modifiers/SKILL.md).
1550
+ Picking guide is in the `pick-seal-primitive` topic. Membership rules (`.in`/`.oneOf`) inherited from PrimitiveValidator — see the `compose-seal-modifiers` topic.
1551
1551
 
1552
1552
  ## Strict equality
1553
1553
 
@@ -1655,7 +1655,7 @@ v.object({
1655
1655
 
1656
1656
  # `v.date()` — method reference
1657
1657
 
1658
- `v.date()` ships a built-in mutator that normalizes strings, timestamps, and `Date` objects to a `Date` before rules run. Picking guide (`v.date()` vs `v.instanceof(Date)`) is in [`@warlock.js/seal/pick-seal-primitive/SKILL.md`](@warlock.js/seal/pick-seal-primitive/SKILL.md).
1658
+ `v.date()` ships a built-in mutator that normalizes strings, timestamps, and `Date` objects to a `Date` before rules run. Picking guide (`v.date()` vs `v.instanceof(Date)`) is in the `pick-seal-primitive` topic.
1659
1659
 
1660
1660
  ## Range — global value comparison
1661
1661
 
@@ -1834,7 +1834,7 @@ v.date().toEndOfDay().toDateOnly()
1834
1834
 
1835
1835
  # `v.number()` / `v.int()` / `v.float()` / `v.numeric()` — method reference
1836
1836
 
1837
- All four share this surface. The picking guide (which factory to call) is in [`@warlock.js/seal/pick-seal-primitive/SKILL.md`](@warlock.js/seal/pick-seal-primitive/SKILL.md). For `.optional()` / `.in()` / `.oneOf()`, see [`@warlock.js/seal/compose-seal-modifiers/SKILL.md`](@warlock.js/seal/compose-seal-modifiers/SKILL.md).
1837
+ All four share this surface. The picking guide (which factory to call) is in the `pick-seal-primitive` topic. For `.optional()` / `.in()` / `.oneOf()`, see the `compose-seal-modifiers` topic.
1838
1838
 
1839
1839
  ## Range — global value comparison
1840
1840
 
@@ -1978,7 +1978,7 @@ v.int().min(0).default(0).optional()
1978
1978
 
1979
1979
  Reference for `v.string()` / `v.email()`. Covers length, format, content, color, mutators (case, trim, mask, slug, base64, …) and what each maps to in JSON Schema.
1980
1980
 
1981
- For the picking guide (`v.string` vs `v.scalar` vs `v.literal`), see [`@warlock.js/seal/pick-seal-primitive/SKILL.md`](@warlock.js/seal/pick-seal-primitive/SKILL.md). For `.optional()` / `.nullable()` / `.default()` / `.in()` / `.oneOf()`, see [`@warlock.js/seal/compose-seal-modifiers/SKILL.md`](@warlock.js/seal/compose-seal-modifiers/SKILL.md).
1981
+ For the picking guide (`v.string` vs `v.scalar` vs `v.literal`), see the `pick-seal-primitive` topic. For `.optional()` / `.nullable()` / `.default()` / `.in()` / `.oneOf()`, see the `compose-seal-modifiers` topic.
1982
1982
 
1983
1983
  ## Length
1984
1984
 
@@ -2112,7 +2112,7 @@ JSON Schema: `format: "color"` for `.hexColor()`; the others map to `format: "co
2112
2112
 
2113
2113
  ## Mutator vs transformer
2114
2114
 
2115
- The methods above are **all mutators** — they reshape the value *before* validation rules run. If you want post-validation reshaping, attach via `.addTransformer(fn)` (see [`@warlock.js/seal/compose-seal-modifiers/SKILL.md`](@warlock.js/seal/compose-seal-modifiers/SKILL.md)).
2115
+ The methods above are **all mutators** — they reshape the value *before* validation rules run. If you want post-validation reshaping, attach via `.addTransformer(fn)` (see the `compose-seal-modifiers` topic).
2116
2116
 
2117
2117
  Practical implication: `v.string().min(3).trim()` runs `min(3)` against the *un-trimmed* input. To check trimmed length, mutate first: `v.string().trim().min(3)`.
2118
2118
 
@@ -2214,15 +2214,15 @@ root `v` factory when a schema needs other factories.
2214
2214
 
2215
2215
  The 12 things that are true in every seal use:
2216
2216
 
2217
- 1. **Public API is the `v` factory.** Never `new ObjectValidator(...)` from app code — bare classes lose the StandardSchema bridge typing. See [`@warlock.js/seal/bridge-standard-schema/SKILL.md`](@warlock.js/seal/bridge-standard-schema/SKILL.md).
2217
+ 1. **Public API is the `v` factory.** Never `new ObjectValidator(...)` from app code — bare classes lose the StandardSchema bridge typing. See the `bridge-standard-schema` topic.
2218
2218
  2. **Every factory return is a `StandardSchemaV1<Infer.Output<T>>`.** Pass seal schemas straight into `StandardSchemaV1<T>`-typed slots — no casts.
2219
2219
  3. **Two inference helpers — `Infer.Input<T>` and `Infer.Output<T>`.** Bare `Infer<T>` is an alias for `Infer.Input<T>` (the dominant usage: HTTP bodies, DTOs, form payloads). Use `Infer.Output<T>` for validated state (Cascade `Model<>` params, post-`validate()` data). See [the rules below](#inferinput-vs-inferoutput).
2220
2220
  4. **Fields are required by default.** Mark optional explicitly: `.optional()`. Skip `.required()` — canonical seal style omits the redundant call; inferred types already show what's required.
2221
- 5. **`validate(schema, data)` never throws.** Returns `Promise<ValidationResult>` with `{ isValid, data, errors }` — the validated value is `result.data`. See [`@warlock.js/seal/handle-seal-errors/SKILL.md`](@warlock.js/seal/handle-seal-errors/SKILL.md).
2221
+ 5. **`validate(schema, data)` never throws.** Returns `Promise<ValidationResult>` with `{ isValid, data, errors }` — the validated value is `result.data`. See the `handle-seal-errors` topic.
2222
2222
  6. **Validators are immutable by default.** Every chain method (`.min(3)`, `.email()`, `.optional()`, …) returns a clone. Toggle with the `.mutable` getter.
2223
2223
  7. **Two pipelines: mutators (pre-validation), transformers (post-validation).** Order: `default → mutators → required check → required-condition rule → other rules → transformers → data`. `.catch(fallback)` rescues any failure on leaf validators.
2224
2224
  8. **Cross-field rules need a `v.object` parent.** Standalone scalar validators have no siblings to resolve against.
2225
- 9. **JSON Schema generation is built-in.** `schema.toJsonSchema(target)` for `"draft-2020-12"` (default), `"draft-07"`, `"openapi-3.0"`, `"openai-strict"`. See [`@warlock.js/seal/generate-json-schema/SKILL.md`](@warlock.js/seal/generate-json-schema/SKILL.md).
2225
+ 9. **JSON Schema generation is built-in.** `schema.toJsonSchema(target)` for `"draft-2020-12"` (default), `"draft-07"`, `"openapi-3.0"`, `"openai-strict"`. See the `generate-json-schema` topic.
2226
2226
  10. **`v.computed` / `v.managed` derive — they don't validate inputs.** They produce values from siblings or context, and are **skipped** when the parent `v.object` emits JSON Schema (runtime-only constructs — calling `.toJsonSchema()` on one directly throws).
2227
2227
  11. **`v.lazy(() => schema)` for recursive shapes.** Defers resolution until validate-time so self-referencing types work.
2228
2228
  12. **`v.discriminatedUnion(field, branches)` for tagged unions.** Routes by a literal discriminator field instead of `matchesType()` trial — precise errors, exact inference.
@@ -2260,13 +2260,13 @@ Both flavours widen with `| null` when `.nullable()` is set.
2260
2260
 
2261
2261
  | If the task is about… | Load |
2262
2262
  | --- | --- |
2263
- | Picking the right primitive (`v.string` vs `v.scalar`, `v.literal` vs `v.enum`, `v.date` vs `v.instanceof(Date)`) | [`@warlock.js/seal/pick-seal-primitive/SKILL.md`](@warlock.js/seal/pick-seal-primitive/SKILL.md) |
2264
- | Building object / array / record / tuple / union schemas, discriminated unions, recursive schemas | [`@warlock.js/seal/define-structural-shape/SKILL.md`](@warlock.js/seal/define-structural-shape/SKILL.md) |
2265
- | Modifiers — `.required` / `.optional` / `.nullable` / `.default` / `.catch` / `.omit`, transformer vs mutator pipelines, membership rules | [`@warlock.js/seal/compose-seal-modifiers/SKILL.md`](@warlock.js/seal/compose-seal-modifiers/SKILL.md) |
2266
- | Reading `ValidationResult`, branching on `error.type`, error message customization, translation | [`@warlock.js/seal/handle-seal-errors/SKILL.md`](@warlock.js/seal/handle-seal-errors/SKILL.md) |
2267
- | Generating JSON Schema for OpenAI strict / OpenAPI / draft-07 | [`@warlock.js/seal/generate-json-schema/SKILL.md`](@warlock.js/seal/generate-json-schema/SKILL.md) |
2268
- | Why a `StandardSchemaV1<T>` slot accepts/rejects a schema, the phantom-intersection design, `Result<unknown>` errors | [`@warlock.js/seal/bridge-standard-schema/SKILL.md`](@warlock.js/seal/bridge-standard-schema/SKILL.md) |
2269
- | Authoring custom seal plugins to add validator methods | [`@warlock.js/seal/extend-seal-with-plugins/SKILL.md`](@warlock.js/seal/extend-seal-with-plugins/SKILL.md) |
2263
+ | Picking the right primitive (`v.string` vs `v.scalar`, `v.literal` vs `v.enum`, `v.date` vs `v.instanceof(Date)`) | The `pick-seal-primitive` topic |
2264
+ | Building object / array / record / tuple / union schemas, discriminated unions, recursive schemas | The `define-structural-shape` topic |
2265
+ | Modifiers — `.required` / `.optional` / `.nullable` / `.default` / `.catch` / `.omit`, transformer vs mutator pipelines, membership rules | The `compose-seal-modifiers` topic |
2266
+ | Reading `ValidationResult`, branching on `error.type`, error message customization, translation | The `handle-seal-errors` topic |
2267
+ | Generating JSON Schema for OpenAI strict / OpenAPI / draft-07 | The `generate-json-schema` topic |
2268
+ | Why a `StandardSchemaV1<T>` slot accepts/rejects a schema, the phantom-intersection design, `Result<unknown>` errors | The `bridge-standard-schema` topic |
2269
+ | Authoring custom seal plugins to add validator methods | The `extend-seal-with-plugins` topic |
2270
2270
 
2271
2271
  ## Things NOT to do
2272
2272
 
package/llms.txt CHANGED
@@ -6,12 +6,12 @@
6
6
 
7
7
  ## Skills
8
8
 
9
- - [bridge-standard-schema](@warlock.js/seal/bridge-standard-schema/SKILL.md): (no description)
10
- - [compose-seal-modifiers](@warlock.js/seal/compose-seal-modifiers/SKILL.md): Apply cross-cutting modifiers — `.optional` / `.nullable` / `.default` / `.catch` / `.omit` / membership rules — plus the mutator-vs-transformer pipeline and `Infer.Input` vs `Infer.Output`. Triggers: `.optional`, `.required`, `.nullable`, `.default`, `.catch`, `.present`, `.requiredIf`, `.requiredWith`, `.in`, `.oneOf`, `.notIn`, `.omit`, `.attribute`, `.mutable`, `.addMutator`, `Infer.Input`, `Infer.Output`; "mark field optional in seal", "default value in schema", "mutator vs transformer", "when does .catch fire"; typical import `import { v, type Infer } from "@warlock.js/seal"`. Skip: primitives — `@warlock.js/seal/pick-seal-primitive/SKILL.md`; containers — `@warlock.js/seal/define-structural-shape/SKILL.md`; errors — `@warlock.js/seal/handle-seal-errors/SKILL.md`; competing `zod` `.optional`/`.default`.
11
- - [define-structural-shape](@warlock.js/seal/define-structural-shape/SKILL.md): (no description)
12
- - [extend-seal-with-plugins](@warlock.js/seal/extend-seal-with-plugins/SKILL.md): (no description)
13
- - [generate-json-schema](@warlock.js/seal/generate-json-schema/SKILL.md): (no description)
14
- - [handle-seal-errors](@warlock.js/seal/handle-seal-errors/SKILL.md): (no description)
9
+ - [bridge-standard-schema](@warlock.js/seal/bridge-standard-schema/SKILL.md): Diagnose `StandardSchemaV1` interop — phantom-intersection at the `v` factory return, why typed slots reject a schema, cascade `Model<TSchema>` variance. Triggers: `StandardSchemaV1`, `~standard`, `Infer`, `ObjectValidator`, `StringValidator`, `LiteralValidator`, `BaseValidator`, `Model<TSchema>`, `StandardJSONSchemaV1`, `Result<unknown>`; "StandardSchemaV1 slot rejecting my schema", "Result<unknown> error", "drop as unknown as cast", "cascade Model schema variance"; typical import `import { v, type Infer } from "@warlock.js/seal"`. Skip: foundations — the `seal-basics` topic; JSON Schema gen — the `generate-json-schema` topic; competing spec `@standard-schema/spec`.
10
+ - [compose-seal-modifiers](@warlock.js/seal/compose-seal-modifiers/SKILL.md): Apply cross-cutting modifiers — `.optional` / `.nullable` / `.default` / `.catch` / `.omit` / membership rules — plus the mutator-vs-transformer pipeline and `Infer.Input` vs `Infer.Output`. Triggers: `.optional`, `.required`, `.nullable`, `.default`, `.catch`, `.present`, `.requiredIf`, `.requiredWith`, `.in`, `.oneOf`, `.notIn`, `.omit`, `.attribute`, `.mutable`, `.addMutator`, `Infer.Input`, `Infer.Output`; "mark field optional in seal", "default value in schema", "mutator vs transformer", "when does .catch fire"; typical import `import { v, type Infer } from "@warlock.js/seal"`. Skip: primitives — the `pick-seal-primitive` topic; containers — the `define-structural-shape` topic; errors — the `handle-seal-errors` topic; competing `zod` `.optional`/`.default`.
11
+ - [define-structural-shape](@warlock.js/seal/define-structural-shape/SKILL.md): Compose `v.object` / `v.array` / `v.record` / `v.tuple` / `v.union` / `v.discriminatedUnion` / `v.lazy`. Triggers: `v.object`, `v.array`, `v.record`, `v.tuple`, `v.union`, `v.discriminatedUnion`, `v.lazy`, `ObjectValidator`; "how do I build an object schema", "dynamic-keyed record", "tagged union with discriminator", "recursive schema", "self-referencing schema"; typical import `import { v, type Infer } from "@warlock.js/seal"`. Skip: leaf primitives — the `pick-seal-primitive` topic; modifiers — the `compose-seal-modifiers` topic; standard-schema bridge — the `bridge-standard-schema` topic; competing libs `zod`, `valibot`.
12
+ - [extend-seal-with-plugins](@warlock.js/seal/extend-seal-with-plugins/SKILL.md): Author a custom seal plugin to add validator methods (`.slug`, `.postalCode`, etc) — `SealPlugin` shape, `registerPlugin` lifecycle, TS prototype augmentation. Triggers: `SealPlugin`, `registerPlugin`, `unregisterPlugin`, `hasPlugin`, `getInstalledPlugins`, `StringValidator`, `NumberValidator`, `DateValidator`, `BaseValidator`, `install`, `uninstall`; "how do I add a custom validator method", "write a seal plugin", "extend v.string with .slug", "module augmentation for seal"; typical import `import { StringValidator, registerPlugin, type SealPlugin } from "@warlock.js/seal"`. Skip: built-in primitives — the `pick-seal-primitive` topic; bridge typing — the `bridge-standard-schema` topic; competing `zod` `.refine`/`.transform`.
13
+ - [generate-json-schema](@warlock.js/seal/generate-json-schema/SKILL.md): Generate JSON Schema via `schema.toJsonSchema(target)` — `draft-2020-12` / `draft-07` / `openapi-3.0` / `openai-strict`. Triggers: `toJsonSchema`, `JsonSchemaTarget`, `draft-2020-12`, `draft-07`, `openapi-3.0`, `openai-strict`, `response_format`, `additionalProperties`, `nullable`; "how do I generate JSON Schema from seal", "OpenAI structured outputs from schema", "OpenAPI 3.0 nullable", "json_schema strict mode"; typical import `import { v } from "@warlock.js/seal"`. Skip: foundations — the `seal-basics` topic; bridge typing — the `bridge-standard-schema` topic; competing libs `zod-to-json-schema`, `ajv`, `@anatine/zod-openapi`.
14
+ - [handle-seal-errors](@warlock.js/seal/handle-seal-errors/SKILL.md): Read `ValidationResult` — `isValid`, `errors[]`, `data`. Branch on `error.type`, customize messages, hook translation. Triggers: `ValidationResult`, `ValidationError`, `validate`, `isValid`, `errors`, `data`, `error.type`, `error.input`, `error.error`, `translationParams`, `.attribute`, `SealConfig`; "how to read seal errors", "branch on a specific rule failure", "customize validation error message", "translate seal error", "surface validation errors as 422"; typical import `import { validate, v } from "@warlock.js/seal"`. Skip: modifiers — the `compose-seal-modifiers` topic; structural shapes — the `define-structural-shape` topic; competing libs `zod` `.safeParse`, `yup` `ValidationError`, `joi`.
15
15
  - [overview](@warlock.js/seal/overview/SKILL.md): @warlock.js/seal overview for orientation and choosing the focused skill for the task.
16
- - [pick-seal-primitive](@warlock.js/seal/pick-seal-primitive/SKILL.md): (no description)
16
+ - [pick-seal-primitive](@warlock.js/seal/pick-seal-primitive/SKILL.md): Pick the right `v` factory primitive — string / int / literal / date / enum / computed / managed / instanceof / any. Triggers: `v.string`, `v.email`, `v.number`, `v.int`, `v.float`, `v.numeric`, `v.boolean`, `v.scalar`, `v.date`, `v.literal`, `v.enum`, `v.instanceof`, `v.computed`, `v.managed`, `v.any`; "v.string vs v.scalar", "v.literal vs v.enum", "v.date vs v.instanceof(Date)", "what is v.computed"; typical import `import { v } from "@warlock.js/seal"`. Skip: structural shapes — the `define-structural-shape` topic; modifiers — the `compose-seal-modifiers` topic; competing libs `zod`, `valibot`, `yup`.
17
17
  - [seal-basics](@warlock.js/seal/seal-basics/SKILL.md): Start here for @warlock.js/seal schemas, validation, parsing, and typed structural contracts.
package/package.json CHANGED
@@ -22,7 +22,7 @@
22
22
  ],
23
23
  "author": "hassanzohdy",
24
24
  "license": "MIT",
25
- "version": "5.27.0",
25
+ "version": "5.29.0",
26
26
  "main": "./cjs/index.cjs",
27
27
  "module": "./esm/index.mjs",
28
28
  "types": "./esm/index.d.mts",
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: bridge-standard-schema
3
- description: 'Diagnose `StandardSchemaV1` interop — phantom-intersection at the `v` factory return, why typed slots reject a schema, cascade `Model<TSchema>` variance. Triggers: `StandardSchemaV1`, `~standard`, `Infer`, `ObjectValidator`, `StringValidator`, `LiteralValidator`, `BaseValidator`, `Model<TSchema>`, `StandardJSONSchemaV1`, `Result<unknown>`; "StandardSchemaV1 slot rejecting my schema", "Result<unknown> error", "drop as unknown as cast", "cascade Model schema variance"; typical import `import { v, type Infer } from "@warlock.js/seal"`. Skip: foundations — `@warlock.js/seal/seal-basics/SKILL.md`; JSON Schema gen — `@warlock.js/seal/generate-json-schema/SKILL.md`; competing spec `@standard-schema/spec`.'
3
+ description: 'Diagnose `StandardSchemaV1` interop — phantom-intersection at the `v` factory return, why typed slots reject a schema, cascade `Model<TSchema>` variance. Triggers: `StandardSchemaV1`, `~standard`, `Infer`, `ObjectValidator`, `StringValidator`, `LiteralValidator`, `BaseValidator`, `Model<TSchema>`, `StandardJSONSchemaV1`, `Result<unknown>`; "StandardSchemaV1 slot rejecting my schema", "Result<unknown> error", "drop as unknown as cast", "cascade Model schema variance"; typical import `import { v, type Infer } from "@warlock.js/seal"`. Skip: foundations — the `seal-basics` topic; JSON Schema gen — the `generate-json-schema` topic; competing spec `@standard-schema/spec`.'
4
4
  ---
5
5
 
6
6
  # The Standard Schema bridge
@@ -1,11 +1,11 @@
1
1
  ---
2
2
  name: compose-seal-modifiers
3
- description: 'Apply cross-cutting modifiers — `.optional` / `.nullable` / `.default` / `.catch` / `.omit` / membership rules — plus the mutator-vs-transformer pipeline and `Infer.Input` vs `Infer.Output`. Triggers: `.optional`, `.required`, `.nullable`, `.default`, `.catch`, `.present`, `.requiredIf`, `.requiredWith`, `.in`, `.oneOf`, `.notIn`, `.omit`, `.attribute`, `.mutable`, `.addMutator`, `Infer.Input`, `Infer.Output`; "mark field optional in seal", "default value in schema", "mutator vs transformer", "when does .catch fire"; typical import `import { v, type Infer } from "@warlock.js/seal"`. Skip: primitives — `@warlock.js/seal/pick-seal-primitive/SKILL.md`; containers — `@warlock.js/seal/define-structural-shape/SKILL.md`; errors — `@warlock.js/seal/handle-seal-errors/SKILL.md`; competing `zod` `.optional`/`.default`.'
3
+ description: 'Apply cross-cutting modifiers — `.optional` / `.nullable` / `.default` / `.catch` / `.omit` / membership rules — plus the mutator-vs-transformer pipeline and `Infer.Input` vs `Infer.Output`. Triggers: `.optional`, `.required`, `.nullable`, `.default`, `.catch`, `.present`, `.requiredIf`, `.requiredWith`, `.in`, `.oneOf`, `.notIn`, `.omit`, `.attribute`, `.mutable`, `.addMutator`, `Infer.Input`, `Infer.Output`; "mark field optional in seal", "default value in schema", "mutator vs transformer", "when does .catch fire"; typical import `import { v, type Infer } from "@warlock.js/seal"`. Skip: primitives — the `pick-seal-primitive` topic; containers — the `define-structural-shape` topic; errors — the `handle-seal-errors` topic; competing `zod` `.optional`/`.default`.'
4
4
  ---
5
5
 
6
6
  # Cross-cutting modifiers + the pipeline
7
7
 
8
- This skill covers methods that work on **every** validator. Type-specific methods (`.email()`, `.min()` on strings, `.between()` on numbers, `.weekDay()` on dates, etc.) live in the per-type method references — see [`@warlock.js/seal/pick-seal-primitive/SKILL.md`](@warlock.js/seal/pick-seal-primitive/SKILL.md) and [`@warlock.js/seal/define-structural-shape/SKILL.md`](@warlock.js/seal/define-structural-shape/SKILL.md).
8
+ This skill covers methods that work on **every** validator. Type-specific methods (`.email()`, `.min()` on strings, `.between()` on numbers, `.weekDay()` on dates, etc.) live in the per-type method references — see the `pick-seal-primitive` topic and the `define-structural-shape` topic.
9
9
 
10
10
  ## The pipeline
11
11
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: define-structural-shape
3
- description: 'Compose `v.object` / `v.array` / `v.record` / `v.tuple` / `v.union` / `v.discriminatedUnion` / `v.lazy`. Triggers: `v.object`, `v.array`, `v.record`, `v.tuple`, `v.union`, `v.discriminatedUnion`, `v.lazy`, `ObjectValidator`; "how do I build an object schema", "dynamic-keyed record", "tagged union with discriminator", "recursive schema", "self-referencing schema"; typical import `import { v, type Infer } from "@warlock.js/seal"`. Skip: leaf primitives — `@warlock.js/seal/pick-seal-primitive/SKILL.md`; modifiers — `@warlock.js/seal/compose-seal-modifiers/SKILL.md`; standard-schema bridge — `@warlock.js/seal/bridge-standard-schema/SKILL.md`; competing libs `zod`, `valibot`.'
3
+ description: 'Compose `v.object` / `v.array` / `v.record` / `v.tuple` / `v.union` / `v.discriminatedUnion` / `v.lazy`. Triggers: `v.object`, `v.array`, `v.record`, `v.tuple`, `v.union`, `v.discriminatedUnion`, `v.lazy`, `ObjectValidator`; "how do I build an object schema", "dynamic-keyed record", "tagged union with discriminator", "recursive schema", "self-referencing schema"; typical import `import { v, type Infer } from "@warlock.js/seal"`. Skip: leaf primitives — the `pick-seal-primitive` topic; modifiers — the `compose-seal-modifiers` topic; standard-schema bridge — the `bridge-standard-schema` topic; competing libs `zod`, `valibot`.'
4
4
  ---
5
5
 
6
6
  # Structural validators — picking guide
@@ -124,11 +124,11 @@ Use for: trees (categories, file systems), threaded data (comment chains), mutua
124
124
  | One of N scalar types | `v.union([...])` |
125
125
  | One of N object shapes with a tag field | `v.discriminatedUnion(key, [...])` |
126
126
  | Self-referencing or forward reference | `v.lazy(() => schema)` |
127
- | One of N constants | `v.literal(...values)` (not structural — see [`@warlock.js/seal/pick-seal-primitive/SKILL.md`](@warlock.js/seal/pick-seal-primitive/SKILL.md)) |
127
+ | One of N constants | `v.literal(...values)` (not structural — see the `pick-seal-primitive` topic) |
128
128
 
129
129
  ## A note on cascade Models
130
130
 
131
- `@warlock.js/cascade` Models declare `static schema: ObjectValidator<TSchema>`. Passing a `v.object({...})` works directly — the factory return widens to fit the Model's invariant generic without breaking type checking. If TS complains *"`ObjectValidator<{specific}>` is not assignable to `ObjectValidator<TSchema>`"*, that's the variance trap and the answer isn't to widen the schema — it's almost always that `Model<TSchema>` was parameterized with a hand-rolled type that drifted from the schema's inferred shape. Fix the type, not the schema. See [`@warlock.js/seal/bridge-standard-schema/SKILL.md`](@warlock.js/seal/bridge-standard-schema/SKILL.md).
131
+ `@warlock.js/cascade` Models declare `static schema: ObjectValidator<TSchema>`. Passing a `v.object({...})` works directly — the factory return widens to fit the Model's invariant generic without breaking type checking. If TS complains *"`ObjectValidator<{specific}>` is not assignable to `ObjectValidator<TSchema>`"*, that's the variance trap and the answer isn't to widen the schema — it's almost always that `Model<TSchema>` was parameterized with a hand-rolled type that drifted from the schema's inferred shape. Fix the type, not the schema. See the `bridge-standard-schema` topic.
132
132
 
133
133
  ## Method-surface reference
134
134
 
@@ -1,6 +1,6 @@
1
1
  # `v.array(itemValidator)` — method reference
2
2
 
3
- The inner validator runs against each element. Failure on any element fails the array. Picking guide (array vs tuple vs record) is in [`@warlock.js/seal/define-structural-shape/SKILL.md`](@warlock.js/seal/define-structural-shape/SKILL.md).
3
+ The inner validator runs against each element. Failure on any element fails the array. Picking guide (array vs tuple vs record) is in the `define-structural-shape` topic.
4
4
 
5
5
  ## Length
6
6
 
@@ -1,6 +1,6 @@
1
1
  # `v.object({...})` — method reference
2
2
 
3
- Picking guide for structural validators is in [`@warlock.js/seal/define-structural-shape/SKILL.md`](@warlock.js/seal/define-structural-shape/SKILL.md). For field-level chaining (`.required` / `.optional` / `.attribute`), see [`@warlock.js/seal/compose-seal-modifiers/SKILL.md`](@warlock.js/seal/compose-seal-modifiers/SKILL.md).
3
+ Picking guide for structural validators is in the `define-structural-shape` topic. For field-level chaining (`.required` / `.optional` / `.attribute`), see the `compose-seal-modifiers` topic.
4
4
 
5
5
  ## Schema composition
6
6
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: extend-seal-with-plugins
3
- description: 'Author a custom seal plugin to add validator methods (`.slug`, `.postalCode`, etc) — `SealPlugin` shape, `registerPlugin` lifecycle, TS prototype augmentation. Triggers: `SealPlugin`, `registerPlugin`, `unregisterPlugin`, `hasPlugin`, `getInstalledPlugins`, `StringValidator`, `NumberValidator`, `DateValidator`, `BaseValidator`, `install`, `uninstall`; "how do I add a custom validator method", "write a seal plugin", "extend v.string with .slug", "module augmentation for seal"; typical import `import { StringValidator, registerPlugin, type SealPlugin } from "@warlock.js/seal"`. Skip: built-in primitives — `@warlock.js/seal/pick-seal-primitive/SKILL.md`; bridge typing — `@warlock.js/seal/bridge-standard-schema/SKILL.md`; competing `zod` `.refine`/`.transform`.'
3
+ description: 'Author a custom seal plugin to add validator methods (`.slug`, `.postalCode`, etc) — `SealPlugin` shape, `registerPlugin` lifecycle, TS prototype augmentation. Triggers: `SealPlugin`, `registerPlugin`, `unregisterPlugin`, `hasPlugin`, `getInstalledPlugins`, `StringValidator`, `NumberValidator`, `DateValidator`, `BaseValidator`, `install`, `uninstall`; "how do I add a custom validator method", "write a seal plugin", "extend v.string with .slug", "module augmentation for seal"; typical import `import { StringValidator, registerPlugin, type SealPlugin } from "@warlock.js/seal"`. Skip: built-in primitives — the `pick-seal-primitive` topic; bridge typing — the `bridge-standard-schema` topic; competing `zod` `.refine`/`.transform`.'
4
4
  ---
5
5
 
6
6
  # Extend seal with plugins
@@ -9,7 +9,7 @@ Seal exposes a plugin system so you can add validator methods without forking th
9
9
 
10
10
  ## When to reach for a plugin
11
11
 
12
- - The validator method you want **does not exist** in built-in seal — check [`@warlock.js/seal/pick-seal-primitive/SKILL.md`](@warlock.js/seal/pick-seal-primitive/SKILL.md) and its method references first.
12
+ - The validator method you want **does not exist** in built-in seal — check the `pick-seal-primitive` topic and its method references first.
13
13
  - The validation is **stable and reusable** across modules — domain-specific formats (IBAN, postal codes, tax IDs, license plates, internal ID schemes).
14
14
  - You want the **chainable syntax** — `v.string().slug()` reads better than `v.string().pattern(/.../).addMutator(s => slugify(s))` at every call site.
15
15
 
@@ -100,7 +100,7 @@ declare module "@warlock.js/seal" {
100
100
 
101
101
  After this file is included in the project's `tsconfig.json` (via `include` or a side-effect import), `v.string().slug()` autocompletes and type-checks everywhere.
102
102
 
103
- **Important.** The augmentation has to declare on the **class** (`StringValidator`), not the factory return type. The factory return widens with `& StandardSchemaV1<...>` (see [`@warlock.js/seal/bridge-standard-schema/SKILL.md`](@warlock.js/seal/bridge-standard-schema/SKILL.md)) — augmentations on the intersection don't propagate. Patch the class, augment the class; the factory return picks up the new methods through structural inference.
103
+ **Important.** The augmentation has to declare on the **class** (`StringValidator`), not the factory return type. The factory return widens with `& StandardSchemaV1<...>` (see the `bridge-standard-schema` topic) — augmentations on the intersection don't propagate. Patch the class, augment the class; the factory return picks up the new methods through structural inference.
104
104
 
105
105
  ## Larger example — postal codes per country
106
106
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: generate-json-schema
3
- description: 'Generate JSON Schema via `schema.toJsonSchema(target)` — `draft-2020-12` / `draft-07` / `openapi-3.0` / `openai-strict`. Triggers: `toJsonSchema`, `JsonSchemaTarget`, `draft-2020-12`, `draft-07`, `openapi-3.0`, `openai-strict`, `response_format`, `additionalProperties`, `nullable`; "how do I generate JSON Schema from seal", "OpenAI structured outputs from schema", "OpenAPI 3.0 nullable", "json_schema strict mode"; typical import `import { v } from "@warlock.js/seal"`. Skip: foundations — `@warlock.js/seal/seal-basics/SKILL.md`; bridge typing — `@warlock.js/seal/bridge-standard-schema/SKILL.md`; competing libs `zod-to-json-schema`, `ajv`, `@anatine/zod-openapi`.'
3
+ description: 'Generate JSON Schema via `schema.toJsonSchema(target)` — `draft-2020-12` / `draft-07` / `openapi-3.0` / `openai-strict`. Triggers: `toJsonSchema`, `JsonSchemaTarget`, `draft-2020-12`, `draft-07`, `openapi-3.0`, `openai-strict`, `response_format`, `additionalProperties`, `nullable`; "how do I generate JSON Schema from seal", "OpenAI structured outputs from schema", "OpenAPI 3.0 nullable", "json_schema strict mode"; typical import `import { v } from "@warlock.js/seal"`. Skip: foundations — the `seal-basics` topic; bridge typing — the `bridge-standard-schema` topic; competing libs `zod-to-json-schema`, `ajv`, `@anatine/zod-openapi`.'
4
4
  ---
5
5
 
6
6
  # JSON Schema generation
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: handle-seal-errors
3
- description: 'Read `ValidationResult` — `isValid`, `errors[]`, `data`. Branch on `error.type`, customize messages, hook translation. Triggers: `ValidationResult`, `ValidationError`, `validate`, `isValid`, `errors`, `data`, `error.type`, `error.input`, `error.error`, `translationParams`, `.attribute`, `SealConfig`; "how to read seal errors", "branch on a specific rule failure", "customize validation error message", "translate seal error", "surface validation errors as 422"; typical import `import { validate, v } from "@warlock.js/seal"`. Skip: modifiers — `@warlock.js/seal/compose-seal-modifiers/SKILL.md`; structural shapes — `@warlock.js/seal/define-structural-shape/SKILL.md`; competing libs `zod` `.safeParse`, `yup` `ValidationError`, `joi`.'
3
+ description: 'Read `ValidationResult` — `isValid`, `errors[]`, `data`. Branch on `error.type`, customize messages, hook translation. Triggers: `ValidationResult`, `ValidationError`, `validate`, `isValid`, `errors`, `data`, `error.type`, `error.input`, `error.error`, `translationParams`, `.attribute`, `SealConfig`; "how to read seal errors", "branch on a specific rule failure", "customize validation error message", "translate seal error", "surface validation errors as 422"; typical import `import { validate, v } from "@warlock.js/seal"`. Skip: modifiers — the `compose-seal-modifiers` topic; structural shapes — the `define-structural-shape` topic; competing libs `zod` `.safeParse`, `yup` `ValidationError`, `joi`.'
4
4
  ---
5
5
 
6
6
  # `ValidationResult` — reading errors
@@ -27,32 +27,32 @@ Eight task skills. Most schemas only need `seal-basics` + `pick-seal-primitive`
27
27
 
28
28
  ### Foundations
29
29
 
30
- #### [`seal-basics/`](../seal-basics/SKILL.md)
30
+ #### `seal-basics`
31
31
  Start here. The `v` factory, `validate(schema, data)`, and `Infer<typeof schema>`.
32
32
 
33
33
  ### Building schemas
34
34
 
35
- #### [`pick-seal-primitive/`](../pick-seal-primitive/SKILL.md)
35
+ #### `pick-seal-primitive`
36
36
  Choose the right primitive — `string` / `int` / `literal` / `date` / `enum` / `computed` / `managed` / `instanceof` / `any`. Covers the close calls (`string` vs `scalar`, `int` vs `number`, `literal` vs `enum`).
37
37
 
38
- #### [`define-structural-shape/`](../define-structural-shape/SKILL.md)
38
+ #### `define-structural-shape`
39
39
  Compose `v.object` / `v.array` / `v.record` / `v.tuple` / `v.union` / `v.discriminatedUnion` / `v.lazy` — object schemas, dynamic-keyed records, tagged unions, recursive shapes.
40
40
 
41
- #### [`compose-seal-modifiers/`](../compose-seal-modifiers/SKILL.md)
41
+ #### `compose-seal-modifiers`
42
42
  Cross-cutting modifiers — `.optional` / `.nullable` / `.default` / `.catch` / `.omit` / membership rules — plus the mutator-vs-transformer pipeline and `Infer.Input` vs `Infer.Output`.
43
43
 
44
44
  ### Output + interop
45
45
 
46
- #### [`handle-seal-errors/`](../handle-seal-errors/SKILL.md)
46
+ #### `handle-seal-errors`
47
47
  Read a `ValidationResult` — `isValid`, `errors[]`, `data`. Branch on `error.type`, customize messages, hook translation.
48
48
 
49
- #### [`generate-json-schema/`](../generate-json-schema/SKILL.md)
49
+ #### `generate-json-schema`
50
50
  `schema.toJsonSchema(target)` — draft-2020-12 / draft-07 / openapi-3.0 / openai-strict. For OpenAI structured outputs, OpenAPI specs, any JSON-Schema consumer.
51
51
 
52
- #### [`bridge-standard-schema/`](../bridge-standard-schema/SKILL.md)
52
+ #### `bridge-standard-schema`
53
53
  Standard Schema interop — why a `StandardSchemaV1<T>` slot might reject a schema, the phantom-intersection at the `v` factory return, Cascade `Model<TSchema>` variance. For migrating off `as unknown as` casts.
54
54
 
55
- #### [`extend-seal-with-plugins/`](../extend-seal-with-plugins/SKILL.md)
55
+ #### `extend-seal-with-plugins`
56
56
  Author a plugin to add validator methods (`.slug`, `.postalCode`, …) — the `SealPlugin` shape, `registerPlugin` lifecycle, TS prototype augmentation.
57
57
 
58
58
  ## Configuration
@@ -67,6 +67,6 @@ Author a plugin to add validator methods (`.slug`, `.postalCode`, …) — the `
67
67
 
68
68
  ## See also
69
69
 
70
- - [`@warlock.js/cascade`](../../../cascade/skills/cascade-basics/SKILL.md) — uses seal schemas as `Model.schema`.
71
- - [`@warlock.js/core`](../../../core/skills/validate-input/SKILL.md) — re-exports `v` (aliased) and adds framework validators in `core/v`.
70
+ - `@warlock.js/cascade` (the `cascade-basics` topic of the `warlock-js-cascade` skill) — uses seal schemas as `Model.schema`.
71
+ - `@warlock.js/core` (the `validate-input` topic of the `warlock-js-core` skill) — re-exports `v` (aliased) and adds framework validators in `core/v`.
72
72
  - [`mongez-agent-kit-authoring-skills`](../../../../domains/shared/skills/) (load via agent-kit sync) — how this becomes `.claude/skills/warlock-js-seal-overview/`.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: pick-seal-primitive
3
- description: 'Pick the right `v` factory primitive — string / int / literal / date / enum / computed / managed / instanceof / any. Triggers: `v.string`, `v.email`, `v.number`, `v.int`, `v.float`, `v.numeric`, `v.boolean`, `v.scalar`, `v.date`, `v.literal`, `v.enum`, `v.instanceof`, `v.computed`, `v.managed`, `v.any`; "v.string vs v.scalar", "v.literal vs v.enum", "v.date vs v.instanceof(Date)", "what is v.computed"; typical import `import { v } from "@warlock.js/seal"`. Skip: structural shapes — `@warlock.js/seal/define-structural-shape/SKILL.md`; modifiers — `@warlock.js/seal/compose-seal-modifiers/SKILL.md`; competing libs `zod`, `valibot`, `yup`.'
3
+ description: 'Pick the right `v` factory primitive — string / int / literal / date / enum / computed / managed / instanceof / any. Triggers: `v.string`, `v.email`, `v.number`, `v.int`, `v.float`, `v.numeric`, `v.boolean`, `v.scalar`, `v.date`, `v.literal`, `v.enum`, `v.instanceof`, `v.computed`, `v.managed`, `v.any`; "v.string vs v.scalar", "v.literal vs v.enum", "v.date vs v.instanceof(Date)", "what is v.computed"; typical import `import { v } from "@warlock.js/seal"`. Skip: structural shapes — the `define-structural-shape` topic; modifiers — the `compose-seal-modifiers` topic; competing libs `zod`, `valibot`, `yup`.'
4
4
  ---
5
5
 
6
6
  # Picking the right primitive
@@ -124,7 +124,7 @@ Both are **skipped** when their parent `v.object` generates JSON Schema — they
124
124
  | Date in the past | `v.date().past()` |
125
125
  | File upload | `v.instanceof(File)` |
126
126
  | Class instance (not Date) | `v.instanceof(Ctor)` |
127
- | Discriminated union | see [`@warlock.js/seal/define-structural-shape/SKILL.md`](@warlock.js/seal/define-structural-shape/SKILL.md) |
127
+ | Discriminated union | see the `define-structural-shape` topic |
128
128
  | Derived value (computed from siblings) | `v.computed<T>(callback)` |
129
129
  | Framework-injected value | `v.managed<T>(callback)` |
130
130
  | Free-form / pass-through | `v.any()` (only when you've thought about it) |
@@ -138,7 +138,7 @@ Each primitive's full method list lives in a sibling file:
138
138
  - [`date-methods.md`](./date-methods.md) — `.min` / `.before` / `.after` / `.today` / `.past` / `.future` / `.weekDay` / `.minAge` / `.year` / `.quarter` / `.toISOString` / …
139
139
  - [`boolean-methods.md`](./boolean-methods.md) — `.accepted` / `.declined` / `.mustBeTrue` / `.mustBeFalse` / `.acceptedIf` / `.declinedWithout` / …
140
140
 
141
- For cross-cutting modifiers (`.optional`/`.nullable`/`.default`/`.catch`/`.in`/`.oneOf`), see [`@warlock.js/seal/compose-seal-modifiers/SKILL.md`](@warlock.js/seal/compose-seal-modifiers/SKILL.md). For containers (object/array/record/tuple/union), see [`@warlock.js/seal/define-structural-shape/SKILL.md`](@warlock.js/seal/define-structural-shape/SKILL.md).
141
+ For cross-cutting modifiers (`.optional`/`.nullable`/`.default`/`.catch`/`.in`/`.oneOf`), see the `compose-seal-modifiers` topic. For containers (object/array/record/tuple/union), see the `define-structural-shape` topic.
142
142
 
143
143
  ## Things NOT to do
144
144
 
@@ -1,6 +1,6 @@
1
1
  # `v.boolean()` — method reference
2
2
 
3
- Picking guide is in [`@warlock.js/seal/pick-seal-primitive/SKILL.md`](@warlock.js/seal/pick-seal-primitive/SKILL.md). Membership rules (`.in`/`.oneOf`) inherited from PrimitiveValidator — see [`@warlock.js/seal/compose-seal-modifiers/SKILL.md`](@warlock.js/seal/compose-seal-modifiers/SKILL.md).
3
+ Picking guide is in the `pick-seal-primitive` topic. Membership rules (`.in`/`.oneOf`) inherited from PrimitiveValidator — see the `compose-seal-modifiers` topic.
4
4
 
5
5
  ## Strict equality
6
6
 
@@ -1,6 +1,6 @@
1
1
  # `v.date()` — method reference
2
2
 
3
- `v.date()` ships a built-in mutator that normalizes strings, timestamps, and `Date` objects to a `Date` before rules run. Picking guide (`v.date()` vs `v.instanceof(Date)`) is in [`@warlock.js/seal/pick-seal-primitive/SKILL.md`](@warlock.js/seal/pick-seal-primitive/SKILL.md).
3
+ `v.date()` ships a built-in mutator that normalizes strings, timestamps, and `Date` objects to a `Date` before rules run. Picking guide (`v.date()` vs `v.instanceof(Date)`) is in the `pick-seal-primitive` topic.
4
4
 
5
5
  ## Range — global value comparison
6
6
 
@@ -1,6 +1,6 @@
1
1
  # `v.number()` / `v.int()` / `v.float()` / `v.numeric()` — method reference
2
2
 
3
- All four share this surface. The picking guide (which factory to call) is in [`@warlock.js/seal/pick-seal-primitive/SKILL.md`](@warlock.js/seal/pick-seal-primitive/SKILL.md). For `.optional()` / `.in()` / `.oneOf()`, see [`@warlock.js/seal/compose-seal-modifiers/SKILL.md`](@warlock.js/seal/compose-seal-modifiers/SKILL.md).
3
+ All four share this surface. The picking guide (which factory to call) is in the `pick-seal-primitive` topic. For `.optional()` / `.in()` / `.oneOf()`, see the `compose-seal-modifiers` topic.
4
4
 
5
5
  ## Range — global value comparison
6
6
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  Reference for `v.string()` / `v.email()`. Covers length, format, content, color, mutators (case, trim, mask, slug, base64, …) and what each maps to in JSON Schema.
4
4
 
5
- For the picking guide (`v.string` vs `v.scalar` vs `v.literal`), see [`@warlock.js/seal/pick-seal-primitive/SKILL.md`](@warlock.js/seal/pick-seal-primitive/SKILL.md). For `.optional()` / `.nullable()` / `.default()` / `.in()` / `.oneOf()`, see [`@warlock.js/seal/compose-seal-modifiers/SKILL.md`](@warlock.js/seal/compose-seal-modifiers/SKILL.md).
5
+ For the picking guide (`v.string` vs `v.scalar` vs `v.literal`), see the `pick-seal-primitive` topic. For `.optional()` / `.nullable()` / `.default()` / `.in()` / `.oneOf()`, see the `compose-seal-modifiers` topic.
6
6
 
7
7
  ## Length
8
8
 
@@ -136,7 +136,7 @@ JSON Schema: `format: "color"` for `.hexColor()`; the others map to `format: "co
136
136
 
137
137
  ## Mutator vs transformer
138
138
 
139
- The methods above are **all mutators** — they reshape the value *before* validation rules run. If you want post-validation reshaping, attach via `.addTransformer(fn)` (see [`@warlock.js/seal/compose-seal-modifiers/SKILL.md`](@warlock.js/seal/compose-seal-modifiers/SKILL.md)).
139
+ The methods above are **all mutators** — they reshape the value *before* validation rules run. If you want post-validation reshaping, attach via `.addTransformer(fn)` (see the `compose-seal-modifiers` topic).
140
140
 
141
141
  Practical implication: `v.string().min(3).trim()` runs `min(3)` against the *un-trimmed* input. To check trimmed length, mutate first: `v.string().trim().min(3)`.
142
142
 
@@ -65,15 +65,15 @@ root `v` factory when a schema needs other factories.
65
65
 
66
66
  The 12 things that are true in every seal use:
67
67
 
68
- 1. **Public API is the `v` factory.** Never `new ObjectValidator(...)` from app code — bare classes lose the StandardSchema bridge typing. See [`@warlock.js/seal/bridge-standard-schema/SKILL.md`](@warlock.js/seal/bridge-standard-schema/SKILL.md).
68
+ 1. **Public API is the `v` factory.** Never `new ObjectValidator(...)` from app code — bare classes lose the StandardSchema bridge typing. See the `bridge-standard-schema` topic.
69
69
  2. **Every factory return is a `StandardSchemaV1<Infer.Output<T>>`.** Pass seal schemas straight into `StandardSchemaV1<T>`-typed slots — no casts.
70
70
  3. **Two inference helpers — `Infer.Input<T>` and `Infer.Output<T>`.** Bare `Infer<T>` is an alias for `Infer.Input<T>` (the dominant usage: HTTP bodies, DTOs, form payloads). Use `Infer.Output<T>` for validated state (Cascade `Model<>` params, post-`validate()` data). See [the rules below](#inferinput-vs-inferoutput).
71
71
  4. **Fields are required by default.** Mark optional explicitly: `.optional()`. Skip `.required()` — canonical seal style omits the redundant call; inferred types already show what's required.
72
- 5. **`validate(schema, data)` never throws.** Returns `Promise<ValidationResult>` with `{ isValid, data, errors }` — the validated value is `result.data`. See [`@warlock.js/seal/handle-seal-errors/SKILL.md`](@warlock.js/seal/handle-seal-errors/SKILL.md).
72
+ 5. **`validate(schema, data)` never throws.** Returns `Promise<ValidationResult>` with `{ isValid, data, errors }` — the validated value is `result.data`. See the `handle-seal-errors` topic.
73
73
  6. **Validators are immutable by default.** Every chain method (`.min(3)`, `.email()`, `.optional()`, …) returns a clone. Toggle with the `.mutable` getter.
74
74
  7. **Two pipelines: mutators (pre-validation), transformers (post-validation).** Order: `default → mutators → required check → required-condition rule → other rules → transformers → data`. `.catch(fallback)` rescues any failure on leaf validators.
75
75
  8. **Cross-field rules need a `v.object` parent.** Standalone scalar validators have no siblings to resolve against.
76
- 9. **JSON Schema generation is built-in.** `schema.toJsonSchema(target)` for `"draft-2020-12"` (default), `"draft-07"`, `"openapi-3.0"`, `"openai-strict"`. See [`@warlock.js/seal/generate-json-schema/SKILL.md`](@warlock.js/seal/generate-json-schema/SKILL.md).
76
+ 9. **JSON Schema generation is built-in.** `schema.toJsonSchema(target)` for `"draft-2020-12"` (default), `"draft-07"`, `"openapi-3.0"`, `"openai-strict"`. See the `generate-json-schema` topic.
77
77
  10. **`v.computed` / `v.managed` derive — they don't validate inputs.** They produce values from siblings or context, and are **skipped** when the parent `v.object` emits JSON Schema (runtime-only constructs — calling `.toJsonSchema()` on one directly throws).
78
78
  11. **`v.lazy(() => schema)` for recursive shapes.** Defers resolution until validate-time so self-referencing types work.
79
79
  12. **`v.discriminatedUnion(field, branches)` for tagged unions.** Routes by a literal discriminator field instead of `matchesType()` trial — precise errors, exact inference.
@@ -111,13 +111,13 @@ Both flavours widen with `| null` when `.nullable()` is set.
111
111
 
112
112
  | If the task is about… | Load |
113
113
  | --- | --- |
114
- | Picking the right primitive (`v.string` vs `v.scalar`, `v.literal` vs `v.enum`, `v.date` vs `v.instanceof(Date)`) | [`@warlock.js/seal/pick-seal-primitive/SKILL.md`](@warlock.js/seal/pick-seal-primitive/SKILL.md) |
115
- | Building object / array / record / tuple / union schemas, discriminated unions, recursive schemas | [`@warlock.js/seal/define-structural-shape/SKILL.md`](@warlock.js/seal/define-structural-shape/SKILL.md) |
116
- | Modifiers — `.required` / `.optional` / `.nullable` / `.default` / `.catch` / `.omit`, transformer vs mutator pipelines, membership rules | [`@warlock.js/seal/compose-seal-modifiers/SKILL.md`](@warlock.js/seal/compose-seal-modifiers/SKILL.md) |
117
- | Reading `ValidationResult`, branching on `error.type`, error message customization, translation | [`@warlock.js/seal/handle-seal-errors/SKILL.md`](@warlock.js/seal/handle-seal-errors/SKILL.md) |
118
- | Generating JSON Schema for OpenAI strict / OpenAPI / draft-07 | [`@warlock.js/seal/generate-json-schema/SKILL.md`](@warlock.js/seal/generate-json-schema/SKILL.md) |
119
- | Why a `StandardSchemaV1<T>` slot accepts/rejects a schema, the phantom-intersection design, `Result<unknown>` errors | [`@warlock.js/seal/bridge-standard-schema/SKILL.md`](@warlock.js/seal/bridge-standard-schema/SKILL.md) |
120
- | Authoring custom seal plugins to add validator methods | [`@warlock.js/seal/extend-seal-with-plugins/SKILL.md`](@warlock.js/seal/extend-seal-with-plugins/SKILL.md) |
114
+ | Picking the right primitive (`v.string` vs `v.scalar`, `v.literal` vs `v.enum`, `v.date` vs `v.instanceof(Date)`) | The `pick-seal-primitive` topic |
115
+ | Building object / array / record / tuple / union schemas, discriminated unions, recursive schemas | The `define-structural-shape` topic |
116
+ | Modifiers — `.required` / `.optional` / `.nullable` / `.default` / `.catch` / `.omit`, transformer vs mutator pipelines, membership rules | The `compose-seal-modifiers` topic |
117
+ | Reading `ValidationResult`, branching on `error.type`, error message customization, translation | The `handle-seal-errors` topic |
118
+ | Generating JSON Schema for OpenAI strict / OpenAPI / draft-07 | The `generate-json-schema` topic |
119
+ | Why a `StandardSchemaV1<T>` slot accepts/rejects a schema, the phantom-intersection design, `Result<unknown>` errors | The `bridge-standard-schema` topic |
120
+ | Authoring custom seal plugins to add validator methods | The `extend-seal-with-plugins` topic |
121
121
 
122
122
  ## Things NOT to do
123
123