@@ -16,7 +16,7 @@ import { once } from 'events';
1616import tar from 'tar' ;
1717import zlib from 'zlib' ;
1818import bson from 'bson' ;
19- import joi from 'joi ' ;
19+ import { z } from 'zod ' ;
2020import type {
2121 AgentWithInitialize ,
2222 DevtoolsProxyOptions ,
@@ -34,63 +34,46 @@ export interface SnippetOptions {
3434 proxyOptions ?: DevtoolsProxyOptions | AgentWithInitialize ;
3535}
3636
37- export interface ErrorMatcher {
38- matches : RegExp [ ] ;
39- message : string ;
40- }
41-
42- export interface SnippetDescription {
43- name : string ;
44- snippetName : string ;
45- installSpec ?: string ;
46- version : string ;
47- description : string ;
48- license : string ;
49- readme : string ;
50- errorMatchers ?: ErrorMatcher [ ] ;
51- }
52-
53- export interface SnippetIndexFile {
54- indexFileVersion : 1 ;
55- index : SnippetDescription [ ] ;
56- metadata : { homepage : string } ;
57- sourceURL : string ;
58- }
59-
6037interface NpmMetaDataResponse {
6138 dist ?: {
6239 tarball ?: string ;
6340 } ;
6441}
6542
66- const indexFileSchema = joi . object ( {
67- indexFileVersion : joi . number ( ) . integer ( ) . max ( 1 ) . required ( ) ,
68-
69- metadata : joi . object ( {
70- homepage : joi . string ( ) ,
71- } ) ,
72-
73- index : joi
74- . array ( )
75- . required ( )
76- . items (
77- joi . object ( {
78- name : joi . string ( ) . required ( ) ,
79- snippetName : joi . string ( ) . required ( ) ,
80- installSpec : joi . string ( ) ,
81- version : joi . string ( ) . required ( ) ,
82- description : joi . string ( ) . required ( ) . allow ( '' ) ,
83- license : joi . string ( ) . required ( ) ,
84- readme : joi . string ( ) . required ( ) . allow ( '' ) ,
85- errorMatchers : joi . array ( ) . items (
86- joi . object ( {
87- message : joi . string ( ) . required ( ) ,
88- matches : joi . array ( ) . required ( ) . items ( joi . object ( ) . regex ( ) ) ,
89- } )
90- ) ,
91- } )
92- ) ,
43+ const regExpTag = Object . prototype . toString . call ( / f o o / ) ;
44+ const errorMatcherSchema = z . object ( {
45+ message : z . string ( ) ,
46+ matches : z . array (
47+ z . custom < RegExp > ( ( val ) => Object . prototype . toString . call ( val ) === regExpTag )
48+ ) ,
49+ } ) ;
50+ const indexDescriptionSchema = z . object ( {
51+ name : z . string ( ) ,
52+ snippetName : z . string ( ) ,
53+ installSpec : z . string ( ) . optional ( ) ,
54+ version : z . string ( ) ,
55+ description : z . string ( ) ,
56+ license : z . string ( ) ,
57+ readme : z . string ( ) ,
58+ errorMatchers : z . array ( errorMatcherSchema ) . optional ( ) ,
9359} ) ;
60+ const indexFileSchema = z . object ( {
61+ indexFileVersion : z . number ( ) . int ( ) . max ( 1 ) ,
62+
63+ metadata : z
64+ . object ( {
65+ homepage : z . string ( ) ,
66+ } )
67+ . passthrough ( ) ,
68+
69+ index : z . array ( indexDescriptionSchema ) ,
70+ } ) ;
71+
72+ export type ErrorMatcher = z . infer < typeof errorMatcherSchema > ;
73+ export type SnippetIndexFile = z . infer < typeof indexFileSchema > & {
74+ sourceURL : string ;
75+ } ;
76+ export type SnippetDescription = z . infer < typeof indexDescriptionSchema > ;
9477
9578async function unpackBSON < T = any > ( data : Buffer ) : Promise < T > {
9679 return bson . deserialize ( await brotliDecompress ( data ) ) as T ;
@@ -361,9 +344,8 @@ export class SnippetManager implements ShellPlugin {
361344 `The specified index file ${ url } could not be parsed: ${ err . message } `
362345 ) ;
363346 }
364- const { error } = indexFileSchema . validate ( data , {
365- allowUnknown : true ,
366- } ) ;
347+ const { error, data : parsedData } =
348+ indexFileSchema . safeParse ( data ) ;
367349 if ( error ) {
368350 this . messageBus . emit ( 'mongosh-snippets:fetch-index-error' , {
369351 action : 'validate-fetched' ,
@@ -374,7 +356,7 @@ export class SnippetManager implements ShellPlugin {
374356 `The specified index file ${ url } is not a valid index file: ${ error . message } `
375357 ) ;
376358 }
377- return { ...data , sourceURL : url } ;
359+ return { ...parsedData , sourceURL : url } ;
378360 } )
379361 ) ;
380362 // If possible, write the result to disk for caching.
0 commit comments