@stacksjs/validation 0.70.45 → 0.70.54

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/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ # MIT License
2
+
3
+ Copyright (c) 2023 Open Web Foundation
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,59 @@
1
+ import type { Validator } from '@stacksjs/ts-validation';
2
+ /**
3
+ * Mixin: add `.when()` / `.sometimes()` to a validator instance.
4
+ *
5
+ * Mutates the validator in place (assigning the new methods) so the
6
+ * existing chainable surface (`.required()`, `.minLength()`, etc.)
7
+ * keeps returning `this`. Returns the same instance with the augmented
8
+ * type so callers see the new methods on the fluent chain.
9
+ */
10
+ export declare function withConditionals<V extends Validator<any>>(validator: V): V & ConditionalAPI<V>;
11
+ /**
12
+ * Evaluate a single `when` clause against a parent object.
13
+ *
14
+ * Returns `true` when the conditional should fire (and its refinement
15
+ * should be applied). Pure function — no side effects, safe to call
16
+ * during introspection too.
17
+ */
18
+ export declare function shouldApplyConditional(record: ConditionalRecord<Validator<any>>, parent: Record<string, unknown> | null | undefined): boolean;
19
+ /**
20
+ * Apply every matching conditional on `base` against `parent`, in
21
+ * registration order. Returns the (possibly refined) validator that
22
+ * should be used to validate the field.
23
+ *
24
+ * The base validator is NEVER mutated — ts-validation methods like
25
+ * `.required()` and `.min(3)` MUTATE `this` and return it, so calling
26
+ * refine functions directly on the base would leak across `validate()`
27
+ * invocations (each call would compound the previous refinements,
28
+ * making "VAT required for EU customers" sticky across requests).
29
+ * {@link cloneValidator} produces an isolated instance the refine
30
+ * pipeline can safely mutate without polluting the schema definition.
31
+ */
32
+ export declare function applyConditionals<V extends Validator<any>>(base: V, parent: Record<string, unknown> | null | undefined): V;
33
+ /**
34
+ * A single `.when()` clause attached to a field validator. Multiple
35
+ * `.when()` calls stack — they're evaluated in registration order and
36
+ * each matching one further refines the base validator.
37
+ */
38
+ export declare interface ConditionalRecord<V extends Validator<any>> {
39
+ field: string
40
+ match: unknown | ((value: unknown) => boolean)
41
+ refine: (validator: V) => V
42
+ }
43
+ /** Anything that has a mutable conditionals array — duck-typed so this
44
+ * works across all the ts-validation validator subclasses. */
45
+ export declare interface ValidatorWithConditionals<V extends Validator<any>> {
46
+ __conditionals?: ConditionalRecord<V>[]
47
+ }
48
+ /**
49
+ * Conditional-rules surface — apps reach these methods through
50
+ * `.when()` / `.sometimes()` on any validator the schema proxy returns.
51
+ */
52
+ export declare interface ConditionalAPI<V extends Validator<any>> {
53
+ when: (
54
+ field: string,
55
+ match: unknown | ((value: unknown) => boolean),
56
+ refine: (validator: V) => V,
57
+ ) => V & ConditionalAPI<V>
58
+ sometimes: () => V & ConditionalAPI<V>
59
+ }
@@ -0,0 +1,65 @@
1
+ /** Factory matching ts-validation's `v.string()` / `v.number()` style. */
2
+ export declare function file(): FileValidator;
3
+ /**
4
+ * `schema.file()` — chainable validator for uploaded files
5
+ * (stacksjs/stacks#1856).
6
+ *
7
+ * Mirrors the ergonomics of `schema.string()` / `schema.number()` /
8
+ * `schema.enum()` from ts-validation but targets the `UploadedFile`
9
+ * shape that `req.file('avatar')` / `req.files` return. Conforms to the
10
+ * `{ rule: { validate(value): { valid, errors? } } }` contract that the
11
+ * Action layer ({@link `@stacksjs/router`} → `validateActionInput`)
12
+ * iterates, so file validations slot into the same `validations:` block
13
+ * as the rest of the field rules:
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * new Action({
18
+ * method: 'POST',
19
+ * validations: {
20
+ * avatar: { rule: schema.file().image().maxBytes(2 * 1024 * 1024) },
21
+ * },
22
+ * async handle(req) {
23
+ * const file = req.file('avatar')!
24
+ * const { url } = await Storage.put(file, { disk: 'public', dir: 'avatars' })
25
+ * // …
26
+ * },
27
+ * })
28
+ * ```
29
+ *
30
+ * The validator is intentionally narrow on the input it accepts: a
31
+ * structural shape with `size: number` and either `mimetype` or
32
+ * `mimeType`, optionally with `originalName` / `name`. Both the
33
+ * router's wrapping `UploadedFile` class and the raw multipart-parse
34
+ * shape satisfy it.
35
+ */
36
+ /** Shape this validator runs against. */
37
+ export declare interface FileLike {
38
+ size: number
39
+ mimetype?: string
40
+ mimeType?: string
41
+ originalName?: string
42
+ name?: string
43
+ }
44
+ declare interface ValidationError { message: string }
45
+ declare interface ValidationResult { valid: boolean, errors?: ValidationError[] }
46
+ /**
47
+ * Chainable file validator. Each method returns `this` so callers can
48
+ * stack constraints in any order; rules accumulate into an internal
49
+ * list and run sequentially on `.validate()`.
50
+ *
51
+ * Designed to be cheap to construct — most validators in an Action's
52
+ * `validations:` block are built once at module load. No I/O happens
53
+ * here; image dimension / content-sniffing rules belong in a separate
54
+ * `@stacksjs/storage/image` opt-in (deliverable 5 of #1856).
55
+ */
56
+ export declare class FileValidator {
57
+ required(): this;
58
+ image(): this;
59
+ mimeTypes(allowed: string[]): this;
60
+ maxBytes(max: number): this;
61
+ minBytes(min: number): this;
62
+ extensions(allowed: string[]): this;
63
+ custom(rule: (file: FileLike) => string | null): this;
64
+ validate(value: unknown): ValidationResult;
65
+ }
package/dist/index.d.ts CHANGED
@@ -1,3 +1,9 @@
1
+ export type {
2
+ ConditionalAPI,
3
+ ConditionalRecord,
4
+ ObjectWithContextValidator,
5
+ ValidatorWithConditionals,
6
+ } from './schema';
1
7
  // Type guard utilities
2
8
  export declare function isString(value: unknown): value is string;
3
9
  export declare function isNumber(value: unknown): value is number;
@@ -16,6 +22,14 @@ export declare function isNullOrUndefined(value: unknown): value is null | undef
16
22
  // `export *` re-exports from `@stacksjs/ts-validation` (which also exports
17
23
  // a `schema` symbol) and see whichever binding was registered first.
18
24
  export { schema } from './schema';
25
+ // Conditional validation surface — `.when()` / `.sometimes()` mixins +
26
+ // context-aware object validator (stacksjs/stacks#1890).
27
+ export {
28
+ applyConditionals,
29
+ objectWithContext,
30
+ shouldApplyConditional,
31
+ withConditionals,
32
+ } from './schema';
19
33
  // Re-export everything from @stacksjs/ts-validation. The explicit `schema`
20
34
  // export above shadows ts-validation's own `schema` for `import { schema }
21
35
  // from '@stacksjs/validation'` consumers — which is what we want, since the
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // @bun
2
- var $=import.meta.require;import{v as A}from"@stacksjs/ts-validation";var Z=A;export*from"@stacksjs/ts-validation";var T=[];function x(V){T=V}function E(){return T}import{HttpError as W}from"@stacksjs/error-handling";import{path as C}from"@stacksjs/path";import{snakeCase as q}from"@stacksjs/strings";import{MessageProvider as I,setCustomMessages as S}from"@stacksjs/ts-validation";function F(V,z={}){let J=[];for(let D of V){let B=D.startsWith("/")?D.lastIndexOf("/"):-1,N=D.indexOf("*"),Q=N===-1?B:D.lastIndexOf("/",N),L=Q>0?D.slice(0,Q):".",U=Q>0?D.slice(Q+1):D,X=new Bun.Glob(U);for(let Y of X.scanSync({cwd:L,onlyFiles:!0}))J.push(z.absolute?`${L}/${Y}`:Y)}return J}function b(V){if(V===void 0)return!1;return Object.keys(V).length>0}async function K(V,z){let D=F([C.userModelsPath("*.ts"),C.storagePath("framework/defaults/app/Models/**/*.ts")],{absolute:!0}).find((L)=>L.endsWith(`${V}.ts`));if(!D)throw new W(404,`Model ${V} not found`);let B=(await import(D)).default.attributes,N={},Q={};for(let L in B)if(Object.prototype.hasOwnProperty.call(B,L)){let U=B[L];if(!U)continue;let X="isRequired"in(U.validation?.rule??{})?(U.validation?.rule).isRequired:!1;if(U.default!==void 0&&X===!1)continue;N[q(L)]=U.validation?.rule;let Y=U.validation?.message;if(Y)for(let _ in Y){let w=`${L}.${_}`;Q[w]=Y[_]||""}}try{S(new I(Q));let U=await Z.object(N).validate(z);if(!U.valid)throw x(U.errors),new W(422,"Validation failed",{errors:U.errors});return U}catch(L){if(L instanceof W)throw L;throw new W(500,L?.message||"An unexpected validation error occurred")}}var P=new Map;function m(V,z){P.set(V,z)}function p(V){return P.get(V)}function v(V,z,J){return async(D)=>{try{let{db:G}=await import("@stacksjs/database"),B=G.selectFrom(V).where(z,"=",D);if(J)B=B.where("id","!=",J);return await B.selectAll().executeTakeFirst()?`The ${z} has already been taken`:!0}catch{return!0}}}function d(V,z){return async(J)=>{try{let{db:D}=await import("@stacksjs/database");return await D.selectFrom(V).where(z,"=",J).selectAll().executeTakeFirst()?!0:`The selected ${z} does not exist`}catch{return!0}}}async function c(V,z,J){let D=await K(V,z);if(J&&Object.keys(J).length>0){let G={};if(await Promise.all(Object.entries(J).map(async([B,N])=>{let Q=await N(z[B],z);if(Q!==!0)G[B]=typeof Q==="string"?Q:`${B} validation failed`})),Object.keys(G).length>0)throw new W(422,"Validation failed",{errors:G})}return D}async function M(V){if(!V||typeof V!=="object")return{};if(typeof V.all==="function"){let D=V.all();return D instanceof Promise?await D:D}let z=V,J={};if(z.url)try{new URL(z.url).searchParams.forEach((G,B)=>{J[B]=G})}catch{}if(z.query&&typeof z.query==="object")Object.assign(J,z.query);if(z.jsonBody&&typeof z.jsonBody==="object")Object.assign(J,z.jsonBody);if(z.formBody&&typeof z.formBody==="object")Object.assign(J,z.formBody);if(z.params&&typeof z.params==="object")Object.assign(J,z.params);if(Object.keys(J).length===0&&!("all"in V)&&!("jsonBody"in V))return V;return J}async function i(V,z){let J=await M(V),D={},G={};for(let[B,N]of Object.entries(z)){if(!N)continue;if(typeof N.rule==="object"||typeof N.rule==="function"){D[B]=N.rule;let Q=N.message;if(typeof Q==="string")G[`${B}.default`]=Q;else if(Q&&typeof Q==="object")for(let[L,U]of Object.entries(Q))G[`${B}.${L}`]=U}else D[B]=N}try{if(Object.keys(G).length>0)S(new I(G));let N=await Z.object(D).validate(J);if(!N.valid){let L={};for(let U of N.errors||[]){let X=U.field??"_";if(!L[X])L[X]=[];L[X].push(U.message)}throw new W(422,"Validation failed",{errors:L})}return N.value??J}catch(B){if(B instanceof W)throw B;throw new W(500,B?.message||"An unexpected validation error occurred")}}async function o(V,z){let J={},D={};for(let G in V)if(Object.prototype.hasOwnProperty.call(V,G)){let B=V[G]?.rule;if(B)J[G]=B;let N=V[G]?.message;for(let Q in N){let L=`${G}.${Q}`;D[L]=V[G]?.message[Q]||""}}try{let B=await Z.object().shape(J).validate(z);if(!B.valid)throw new W(422,"Validation failed",{errors:B.errors});return B}catch(G){if(G instanceof W)throw G;throw new W(500,G?.message||"An unexpected validation error occurred")}}function l(V){return typeof V==="string"}function s(V){return typeof V==="number"&&!Number.isNaN(V)}function a(V){return typeof V==="boolean"}function t(V){return typeof V==="object"&&V!==null&&!Array.isArray(V)}function r(V){return Array.isArray(V)}function e(V){return typeof V==="function"}function VV(V){return V===void 0}function zV(V){return V===null}function BV(V){return V===null||V===void 0}export{c as validateFieldAsync,K as validateField,i as validate,v as unique,Z as schema,x as reportError,m as registerRule,VV as isUndefined,l as isString,b as isObjectNotEmpty,t as isObject,s as isNumber,BV as isNullOrUndefined,zV as isNull,e as isFunction,a as isBoolean,r as isArray,E as getErrors,p as getCustomRule,d as exists,o as customValidate};
2
+ var q=import.meta.require;import{v as b}from"@stacksjs/ts-validation";var h=new Set(["image/jpeg","image/png","image/webp","image/gif","image/avif"]);function F(B){return B.mimetype??B.mimeType}function f(B){return B.originalName??B.name}function g(B){let D=f(B);if(!D)return null;let J=D.lastIndexOf(".");if(J<=0||J===D.length-1)return null;let L=D.slice(J+1).toLowerCase();return/^[a-z0-9]+$/.test(L)?L:null}class A{_required=!1;_rules=[];required(){return this._required=!0,this}image(){return this._rules.push((B)=>{let D=F(B);if(!D||!h.has(D.toLowerCase()))return`must be an image (got ${D||"no mimetype"})`;return null}),this}mimeTypes(B){let D=new Set(B.map((J)=>J.toLowerCase()));return this._rules.push((J)=>{let L=F(J);if(!L||!D.has(L.toLowerCase()))return`must be one of: ${B.join(", ")} (got ${L||"no mimetype"})`;return null}),this}maxBytes(B){return this._rules.push((D)=>{if(typeof D.size!=="number")return"has no size \u2014 cannot enforce maxBytes";if(D.size>B)return`is too large: ${D.size} bytes exceeds the ${B}-byte cap`;return null}),this}minBytes(B){return this._rules.push((D)=>{if(typeof D.size!=="number")return"has no size \u2014 cannot enforce minBytes";if(D.size<B)return`is too small: ${D.size} bytes is below the ${B}-byte minimum`;return null}),this}extensions(B){let D=new Set(B.map((J)=>J.toLowerCase().replace(/^\./,"")));return this._rules.push((J)=>{let L=g(J);if(!L||!D.has(L))return`must have one of these extensions: ${B.join(", ")} (got ${L?`.${L}`:"no extension"})`;return null}),this}custom(B){return this._rules.push(B),this}validate(B){if(B===null||B===void 0){if(this._required)return{valid:!1,errors:[{message:"is required"}]};return{valid:!0}}if(typeof B!=="object"||typeof B.size!=="number")return{valid:!1,errors:[{message:"must be an uploaded file"}]};let D=B,J=[];for(let L of this._rules){let U=L(D);if(U)J.push({message:U})}return J.length===0?{valid:!0}:{valid:!1,errors:J}}}function C(){return new A}function T(B){let D=B;return D.when=function(L,U,Q){if(!this.__conditionals)this.__conditionals=[];return this.__conditionals.push({field:L,match:U,refine:Q}),this},D.sometimes=function(){return this.optional(),this},D}function H(B,D){if(D===null||D===void 0)return!1;let J=D[B.field];if(typeof B.match==="function")return B.match(J);return J===B.match}function V(B,D){let J=B.__conditionals;if(!J||J.length===0)return B;let L=null;for(let U of J){if(!H(U,D))continue;if(L===null)L=y(B);L=U.refine(L)}return L??B}function y(B){let D=Object.create(Object.getPrototypeOf(B));Object.assign(D,B);let J=B.rules;if(Array.isArray(J))D.rules=J.slice();return D}function I(B){let D=B??{},J=!0,L={name:"object",isPartOfShape:!1,shape(Q){return D=Q,L},getShape(){return D},required(){return J=!0,U},optional(){return J=!1,U},validate(Q){let Z=Q===void 0||Q===null;if(!J&&Z)return{valid:!0,errors:this.isPartOfShape?{}:[]};if(J&&Z)return this.isPartOfShape?{valid:!1,errors:{object:[{message:"value is required"}]}}:{valid:!1,errors:[{message:"value is required"}]};if(typeof Q!=="object"||Array.isArray(Q))return this.isPartOfShape?{valid:!1,errors:{object:[{message:"Must be an object"}]}}:{valid:!1,errors:[{message:"Must be an object"}]};let $=Q,X={},G=!1;for(let N of Object.keys(D)){let _=D[N];if(!_)continue;let Y=V(_,$),S=$[N],P=Y.validate(S);if(P.valid)continue;if(G=!0,!Array.isArray(P.errors))for(let[w,k]of Object.entries(P.errors))X[`${N}.${w}`]=k;else X[N]=P.errors}if(G)return{valid:!1,errors:X};return{valid:!0,errors:{}}}},U=T(L);return U}var v=new Set(["array","bigint","binary","blob","boolean","custom","date","datetime","decimal","double","enum","float","integer","json","number","password","smallint","string","text","time","timestamp","timestampTz","unix"]);function m(B){return(...D)=>{let J=B(...D);return T(J)}}var W=new Proxy(b,{get(B,D,J){if(D==="file")return C;if(D==="object")return I;if(typeof D==="string"&&v.has(D)){let L=Reflect.get(B,D,J);if(typeof L==="function")return m(L)}return Reflect.get(B,D,J)}});export*from"@stacksjs/ts-validation";var M=[];function O(B){M=B}function XB(){return M}import{HttpError as z}from"@stacksjs/error-handling";import{path as R}from"@stacksjs/path";import{snakeCase as p}from"@stacksjs/strings";import{getErrorMessage as K}from"@stacksjs/utils";import{MessageProvider as E,setCustomMessages as x}from"@stacksjs/ts-validation";function c(B,D={}){let J=[];for(let L of B){let Q=L.startsWith("/")?L.lastIndexOf("/"):-1,Z=L.indexOf("*"),$=Z===-1?Q:L.lastIndexOf("/",Z),X=$>0?L.slice(0,$):".",G=$>0?L.slice($+1):L,N=new Bun.Glob(G);for(let _ of N.scanSync({cwd:X,onlyFiles:!0}))J.push(D.absolute?`${X}/${_}`:_)}return J}function YB(B){if(B===void 0)return!1;return Object.keys(B).length>0}async function d(B,D){let L=c([R.userModelsPath("*.ts"),R.storagePath("framework/defaults/app/Models/**/*.ts")],{absolute:!0}).find((X)=>X.endsWith(`${B}.ts`));if(!L)throw new z(404,`Model ${B} not found`);let Q=(await import(L)).default.attributes,Z={},$={};for(let X in Q)if(Object.prototype.hasOwnProperty.call(Q,X)){let G=Q[X];if(!G)continue;let N="isRequired"in(G.validation?.rule??{})?(G.validation?.rule).isRequired:!1;if(G.default!==void 0&&N===!1)continue;Z[p(X)]=G.validation?.rule;let _=G.validation?.message;if(_)for(let Y in _){let S=`${X}.${Y}`;$[S]=_[Y]||""}}try{x(new E($));let G=await W.object(Z).validate(D);if(!G.valid)throw O(G.errors),new z(422,"Validation failed",{errors:G.errors});return G}catch(X){if(X instanceof z)throw X;throw new z(500,K(X)||"An unexpected validation error occurred")}}var j=new Map;function PB(B,D){j.set(B,D)}function VB(B){return j.get(B)}function IB(B,D,J){return async(L)=>{try{let{db:U}=await import("@stacksjs/database"),Q=U.selectFrom(B).where(D,"=",L);if(J)Q=Q.where("id","!=",J);return await Q.selectAll().executeTakeFirst()?`The ${D} has already been taken`:!0}catch{return!0}}}function SB(B,D){return async(J)=>{try{let{db:L}=await import("@stacksjs/database");return await L.selectFrom(B).where(D,"=",J).selectAll().executeTakeFirst()?!0:`The selected ${D} does not exist`}catch{return!0}}}async function AB(B,D,J){let L=await d(B,D);if(J&&Object.keys(J).length>0){let U={};if(await Promise.all(Object.entries(J).map(async([Q,Z])=>{let $=await Z(D[Q],D);if($!==!0)U[Q]=typeof $==="string"?$:`${Q} validation failed`})),Object.keys(U).length>0)throw new z(422,"Validation failed",{errors:U})}return L}async function n(B){if(!B||typeof B!=="object")return{};if(typeof B.all==="function"){let L=B.all();return L instanceof Promise?await L:L}let D=B,J={};if(D.url)try{new URL(D.url).searchParams.forEach((U,Q)=>{J[Q]=U})}catch{}if(D.query&&typeof D.query==="object")Object.assign(J,D.query);if(D.jsonBody&&typeof D.jsonBody==="object")Object.assign(J,D.jsonBody);if(D.formBody&&typeof D.formBody==="object")Object.assign(J,D.formBody);if(D.params&&typeof D.params==="object")Object.assign(J,D.params);if(Object.keys(J).length===0&&!("all"in B)&&!("jsonBody"in B))return B;return J}async function CB(B,D){let J=await n(B),L={},U={};for(let[Q,Z]of Object.entries(D)){if(!Z)continue;if(typeof Z.rule==="object"||typeof Z.rule==="function"){L[Q]=Z.rule;let $=Z.message;if(typeof $==="string")U[`${Q}.default`]=$;else if($&&typeof $==="object")for(let[X,G]of Object.entries($))U[`${Q}.${X}`]=G}else L[Q]=Z}try{if(Object.keys(U).length>0)x(new E(U));let Z=await W.object(L).validate(J);if(!Z.valid){let X={};for(let G of Z.errors||[]){let N=G.field??"_";if(!X[N])X[N]=[];X[N].push(G.message)}throw new z(422,"Validation failed",{errors:X})}return Z.value??J}catch(Q){if(Q instanceof z)throw Q;throw new z(500,K(Q)||"An unexpected validation error occurred")}}async function HB(B,D){let J={},L={};for(let U in B)if(Object.prototype.hasOwnProperty.call(B,U)){let Q=B[U]?.rule;if(Q)J[U]=Q;let Z=B[U]?.message;for(let $ in Z){let X=`${U}.${$}`;L[X]=B[U]?.message[$]||""}}try{let Q=await W.object().shape(J).validate(D);if(!Q.valid)throw new z(422,"Validation failed",{errors:Q.errors});return Q}catch(U){if(U instanceof z)throw U;throw new z(500,K(U)||"An unexpected validation error occurred")}}function qB(B){return typeof B==="string"}function FB(B){return typeof B==="number"&&!Number.isNaN(B)}function MB(B){return typeof B==="boolean"}function OB(B){return typeof B==="object"&&B!==null&&!Array.isArray(B)}function RB(B){return Array.isArray(B)}function EB(B){return typeof B==="function"}function xB(B){return B===void 0}function jB(B){return B===null}function wB(B){return B===null||B===void 0}export{T as withConditionals,AB as validateFieldAsync,d as validateField,CB as validate,IB as unique,H as shouldApplyConditional,W as schema,O as reportError,PB as registerRule,I as objectWithContext,xB as isUndefined,qB as isString,YB as isObjectNotEmpty,OB as isObject,FB as isNumber,wB as isNullOrUndefined,jB as isNull,EB as isFunction,MB as isBoolean,RB as isArray,XB as getErrors,VB as getCustomRule,SB as exists,HB as customValidate,V as applyConditionals};
@@ -0,0 +1,37 @@
1
+ import type { ConditionalAPI } from './conditional';
2
+ import type { ValidationResult, Validator } from '@stacksjs/ts-validation';
3
+ /**
4
+ * Build a Stacks-flavored object validator that respects conditional
5
+ * rules on its child validators. Drop-in for `v.object(...)` in
6
+ * `@stacksjs/ts-validation` — same call shape, broader behavior.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * objectWithContext({
11
+ * payment_method: schema.enum(['card', 'bank']),
12
+ * card_token: schema.string().when('payment_method', 'card', s => s.required()),
13
+ * bank_account: schema.string().when('payment_method', 'bank', s => s.required()),
14
+ * username: schema.string().sometimes().minLength(3),
15
+ * }).validate(input)
16
+ * ```
17
+ */
18
+ export declare function objectWithContext<T extends Record<string, Validator<any>>>(initialShape?: T): ObjectWithContextValidator<T> & ConditionalAPI<ObjectWithContextValidator<T>>;
19
+ /**
20
+ * Validator-shaped object returned by `schema.object()`. Mirrors the
21
+ * subset of ts-validation's `ObjectValidatorType` that Stacks code
22
+ * actually uses today (`validate`, `shape`, `optional`, `required`).
23
+ *
24
+ * Extra method:
25
+ * - `getShape()` — exposes the underlying shape map so OpenAPI export
26
+ * and other introspection passes can walk the per-field validators
27
+ * (and their `__conditionals` arrays).
28
+ */
29
+ export declare interface ObjectWithContextValidator<T extends Record<string, Validator<any>>> {
30
+ readonly name: 'object'
31
+ validate: (value: unknown) => ValidationResult
32
+ getShape: () => T
33
+ required: () => ObjectWithContextValidator<T> & ConditionalAPI<ObjectWithContextValidator<T>>
34
+ optional: () => ObjectWithContextValidator<T> & ConditionalAPI<ObjectWithContextValidator<T>>
35
+ isPartOfShape: boolean
36
+ shape: (shape: T) => ObjectWithContextValidator<T>
37
+ }
package/dist/schema.d.ts CHANGED
@@ -1,2 +1,35 @@
1
+ import { file, FileValidator } from './file-validator';
2
+ import { objectWithContext } from './object-with-context';
3
+ import { withConditionals } from './conditional';
4
+ import type { ConditionalAPI } from './conditional';
5
+ import type { ObjectWithContextValidator } from './object-with-context';
1
6
  import type { ValidationInstance } from '@stacksjs/ts-validation';
2
- export declare const schema: ValidationInstance;
7
+ export type { FileLike } from './file-validator';
8
+ export type { ConditionalAPI, ConditionalRecord, ValidatorWithConditionals } from './conditional';
9
+ export type { ObjectWithContextValidator } from './object-with-context';
10
+ /**
11
+ * `Object.assign({}, v, { file })` would defeat the ts-validation
12
+ * proxy's late-binding. Wrapping with `new Proxy` keeps every existing
13
+ * `schema.<method>()` call going through the upstream proxy while only
14
+ * intercepting the slots we own (`file`, `object`, and the conditional
15
+ * mixin on primitive factories).
16
+ */
17
+ export declare const schema: SchemaWithFile;
18
+ /**
19
+ * Extended `ValidationInstance` that adds `schema.file()` on top of the
20
+ * ts-validation surface (stacksjs/stacks#1856). Upstream ts-validation
21
+ * has `string()`, `number()`, `enum()`, etc. but no `file()` validator;
22
+ * we layer one here without forking ts-validation so the rest of the
23
+ * surface keeps working unchanged.
24
+ *
25
+ * The runtime value is the ts-validation proxy with `file` patched in,
26
+ * `object` swapped for the context-aware variant (stacksjs/stacks#1890),
27
+ * and every primitive factory wrapped to attach `.when()` / `.sometimes()`
28
+ * to the returned validator.
29
+ */
30
+ export declare interface SchemaWithFile extends ValidationInstance {
31
+ file: () => FileValidator
32
+ }
33
+ export { file, FileValidator } from './file-validator';
34
+ export { applyConditionals, shouldApplyConditional, withConditionals } from './conditional';
35
+ export { objectWithContext } from './object-with-context';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stacksjs/validation",
3
3
  "type": "module",
4
- "version": "0.70.45",
4
+ "version": "0.70.54",
5
5
  "description": "The Stacks Validation ways.",
6
6
  "author": "Chris Breuer",
7
7
  "contributors": [
@@ -48,11 +48,11 @@
48
48
  "prepublishOnly": "bun run build"
49
49
  },
50
50
  "dependencies": {
51
- "@stacksjs/ts-validation": "^0.4.9"
51
+ "@stacksjs/ts-validation": "^0.5.0"
52
52
  },
53
53
  "devDependencies": {
54
54
  "better-dx": "^0.2.12",
55
- "@stacksjs/strings": "^0.70.45",
56
- "@stacksjs/types": "^0.70.45"
55
+ "@stacksjs/strings": "0.70.54",
56
+ "@stacksjs/types": "0.70.54"
57
57
  }
58
58
  }