@@ -10,13 +10,8 @@ import {
1010 defineRouteContract ,
1111} from '@/lib/api/contracts/types'
1212
13- // .refine returns ZodEffects which has no .extend method, so the downstream
14- // MongoDB operation schemas have to extend the un-refined base. (Note: the
15- // resolved Zod is v3 despite package.json declaring v4 — same root issue as
16- // confluence and storage-transfer.) The five downstream extensions previously
17- // threw at module-init time in the TD bundle, so no consumer was actually
18- // running the username/password pairing constraint; not adding it back here
19- // preserves observed behavior.
13+ // Un-refined base so the downstream operation schemas can .extend it; each
14+ // reattaches mongoUsernamePasswordPaired after its own .extend.
2015const mongoConnectionBaseSchema = z . object ( {
2116 host : z . string ( ) . min ( 1 , 'Host is required' ) ,
2217 port : z . coerce . number ( ) . int ( ) . positive ( 'Port must be a positive integer' ) ,
@@ -27,6 +22,13 @@ const mongoConnectionBaseSchema = z.object({
2722 ssl : sslModeSchema ,
2823} )
2924
25+ const mongoUsernamePasswordPaired = ( data : { username ?: string ; password ?: string } ) =>
26+ Boolean ( data . username ) === Boolean ( data . password )
27+ const mongoUsernamePasswordPairedError = {
28+ message : 'Username and password must be provided together' ,
29+ path : [ 'password' as const ] ,
30+ }
31+
3032const mongoJsonStringOrObjectSchema = ( message : string ) =>
3133 z
3234 . union ( [ z . string ( ) , z . object ( { } ) . passthrough ( ) ] )
@@ -47,92 +49,102 @@ const booleanStringSchema = z
4749 return false
4850 } )
4951
50- export const mongodbQueryBodySchema = mongoConnectionBaseSchema . extend ( {
51- collection : z . string ( ) . min ( 1 , 'Collection name is required' ) ,
52- query : z
53- . union ( [ z . string ( ) , z . object ( { } ) . passthrough ( ) ] )
54- . optional ( )
55- . default ( '{}' )
56- . transform ( ( val ) => {
57- if ( typeof val === 'object' && val !== null ) {
58- return JSON . stringify ( val )
59- }
60- return val || '{}'
61- } ) ,
62- limit : z
63- . union ( [ z . coerce . number ( ) . int ( ) . positive ( ) , z . literal ( '' ) , z . undefined ( ) ] )
64- . optional ( )
65- . transform ( ( val ) => {
66- if ( val === '' || val === undefined || val === null ) {
67- return 100
68- }
69- return val
70- } ) ,
71- sort : z
72- . union ( [ z . string ( ) , z . object ( { } ) . passthrough ( ) , z . null ( ) ] )
73- . optional ( )
74- . transform ( ( val ) => {
75- if ( typeof val === 'object' && val !== null ) {
76- return JSON . stringify ( val )
77- }
78- return val
79- } ) ,
80- } )
81-
82- export const mongodbExecuteBodySchema = mongoConnectionBaseSchema . extend ( {
83- collection : z . string ( ) . min ( 1 , 'Collection name is required' ) ,
84- pipeline : z
85- . union ( [ z . string ( ) , z . array ( z . object ( { } ) . passthrough ( ) ) ] )
86- . transform ( ( val ) => {
87- if ( Array . isArray ( val ) ) {
88- return JSON . stringify ( val )
89- }
90- return val
91- } )
92- . refine ( ( val ) => val && val . trim ( ) !== '' , {
93- message : 'Pipeline is required' ,
94- } ) ,
95- } )
96-
97- export const mongodbInsertBodySchema = mongoConnectionBaseSchema . extend ( {
98- collection : z . string ( ) . min ( 1 , 'Collection name is required' ) ,
99- documents : z
100- . union ( [ z . array ( z . record ( z . string ( ) , z . unknown ( ) ) ) , z . string ( ) ] )
101- . transform ( ( val ) => {
102- if ( typeof val === 'string' ) {
103- try {
104- const parsed = JSON . parse ( val )
105- return Array . isArray ( parsed ) ? parsed : [ parsed ]
106- } catch {
107- throw new Error ( 'Invalid JSON in documents field' )
52+ export const mongodbQueryBodySchema = mongoConnectionBaseSchema
53+ . extend ( {
54+ collection : z . string ( ) . min ( 1 , 'Collection name is required' ) ,
55+ query : z
56+ . union ( [ z . string ( ) , z . object ( { } ) . passthrough ( ) ] )
57+ . optional ( )
58+ . default ( '{}' )
59+ . transform ( ( val ) => {
60+ if ( typeof val === 'object' && val !== null ) {
61+ return JSON . stringify ( val )
10862 }
109- }
110- return val
111- } )
112- . refine ( ( val ) => Array . isArray ( val ) && val . length > 0 , {
113- message : 'At least one document is required' ,
114- } ) ,
115- } )
116-
117- export const mongodbUpdateBodySchema = mongoConnectionBaseSchema . extend ( {
118- collection : z . string ( ) . min ( 1 , 'Collection name is required' ) ,
119- filter : mongoJsonStringOrObjectSchema ( 'Filter is required for MongoDB Update' ) . refine (
120- ( val ) => val !== '{}' ,
121- { message : 'Filter is required for MongoDB Update' }
122- ) ,
123- update : mongoJsonStringOrObjectSchema ( 'Update is required' ) ,
124- upsert : booleanStringSchema ,
125- multi : booleanStringSchema ,
126- } )
127-
128- export const mongodbDeleteBodySchema = mongoConnectionBaseSchema . extend ( {
129- collection : z . string ( ) . min ( 1 , 'Collection name is required' ) ,
130- filter : mongoJsonStringOrObjectSchema ( 'Filter is required for MongoDB Delete' ) . refine (
131- ( val ) => val !== '{}' ,
132- { message : 'Filter is required for MongoDB Delete' }
133- ) ,
134- multi : booleanStringSchema ,
135- } )
63+ return val || '{}'
64+ } ) ,
65+ limit : z
66+ . union ( [ z . coerce . number ( ) . int ( ) . positive ( ) , z . literal ( '' ) , z . undefined ( ) ] )
67+ . optional ( )
68+ . transform ( ( val ) => {
69+ if ( val === '' || val === undefined || val === null ) {
70+ return 100
71+ }
72+ return val
73+ } ) ,
74+ sort : z
75+ . union ( [ z . string ( ) , z . object ( { } ) . passthrough ( ) , z . null ( ) ] )
76+ . optional ( )
77+ . transform ( ( val ) => {
78+ if ( typeof val === 'object' && val !== null ) {
79+ return JSON . stringify ( val )
80+ }
81+ return val
82+ } ) ,
83+ } )
84+ . refine ( mongoUsernamePasswordPaired , mongoUsernamePasswordPairedError )
85+
86+ export const mongodbExecuteBodySchema = mongoConnectionBaseSchema
87+ . extend ( {
88+ collection : z . string ( ) . min ( 1 , 'Collection name is required' ) ,
89+ pipeline : z
90+ . union ( [ z . string ( ) , z . array ( z . object ( { } ) . passthrough ( ) ) ] )
91+ . transform ( ( val ) => {
92+ if ( Array . isArray ( val ) ) {
93+ return JSON . stringify ( val )
94+ }
95+ return val
96+ } )
97+ . refine ( ( val ) => val && val . trim ( ) !== '' , {
98+ message : 'Pipeline is required' ,
99+ } ) ,
100+ } )
101+ . refine ( mongoUsernamePasswordPaired , mongoUsernamePasswordPairedError )
102+
103+ export const mongodbInsertBodySchema = mongoConnectionBaseSchema
104+ . extend ( {
105+ collection : z . string ( ) . min ( 1 , 'Collection name is required' ) ,
106+ documents : z
107+ . union ( [ z . array ( z . record ( z . string ( ) , z . unknown ( ) ) ) , z . string ( ) ] )
108+ . transform ( ( val ) => {
109+ if ( typeof val === 'string' ) {
110+ try {
111+ const parsed = JSON . parse ( val )
112+ return Array . isArray ( parsed ) ? parsed : [ parsed ]
113+ } catch {
114+ throw new Error ( 'Invalid JSON in documents field' )
115+ }
116+ }
117+ return val
118+ } )
119+ . refine ( ( val ) => Array . isArray ( val ) && val . length > 0 , {
120+ message : 'At least one document is required' ,
121+ } ) ,
122+ } )
123+ . refine ( mongoUsernamePasswordPaired , mongoUsernamePasswordPairedError )
124+
125+ export const mongodbUpdateBodySchema = mongoConnectionBaseSchema
126+ . extend ( {
127+ collection : z . string ( ) . min ( 1 , 'Collection name is required' ) ,
128+ filter : mongoJsonStringOrObjectSchema ( 'Filter is required for MongoDB Update' ) . refine (
129+ ( val ) => val !== '{}' ,
130+ { message : 'Filter is required for MongoDB Update' }
131+ ) ,
132+ update : mongoJsonStringOrObjectSchema ( 'Update is required' ) ,
133+ upsert : booleanStringSchema ,
134+ multi : booleanStringSchema ,
135+ } )
136+ . refine ( mongoUsernamePasswordPaired , mongoUsernamePasswordPairedError )
137+
138+ export const mongodbDeleteBodySchema = mongoConnectionBaseSchema
139+ . extend ( {
140+ collection : z . string ( ) . min ( 1 , 'Collection name is required' ) ,
141+ filter : mongoJsonStringOrObjectSchema ( 'Filter is required for MongoDB Delete' ) . refine (
142+ ( val ) => val !== '{}' ,
143+ { message : 'Filter is required for MongoDB Delete' }
144+ ) ,
145+ multi : booleanStringSchema ,
146+ } )
147+ . refine ( mongoUsernamePasswordPaired , mongoUsernamePasswordPairedError )
136148
137149export const mongodbIntrospectBodySchema = z
138150 . object ( {
0 commit comments