From 9e4cf7fe98ce273884ffc01e23edaab9b98bc844 Mon Sep 17 00:00:00 2001 From: Mbaye THIAM Date: Fri, 12 Apr 2024 16:07:29 +0200 Subject: [PATCH 1/8] =?UTF-8?q?=F0=9F=90=9B=20fix=20generated=20sources=20?= =?UTF-8?q?names?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/graphql-mesh/.meshrc.ts | 8 +++-- .../graphql-mesh/scripts/download-sources.ts | 24 +++++++++++--- .../graphql-mesh/utils/ConfigFromSwaggers.ts | 32 +++++++++++-------- packages/graphql-mesh/utils/config/index.ts | 15 +++++++-- 4 files changed, 54 insertions(+), 25 deletions(-) diff --git a/packages/graphql-mesh/.meshrc.ts b/packages/graphql-mesh/.meshrc.ts index 21ada87..76aa748 100644 --- a/packages/graphql-mesh/.meshrc.ts +++ b/packages/graphql-mesh/.meshrc.ts @@ -17,13 +17,15 @@ const config = { } }, ...(defaultConfig.transforms || []) - ], + ].filter(Boolean), sources: [...sources], - additionalTypeDefs: [defaultConfig.additionalTypeDefs || '', ...additionalTypeDefs], + additionalTypeDefs: [defaultConfig.additionalTypeDefs || '', ...additionalTypeDefs].filter( + Boolean + ), additionalResolvers: [ ...(defaultConfig.additionalResolvers || []), './utils/additionalResolvers.ts' - ] + ].filter(Boolean) } export default config diff --git a/packages/graphql-mesh/scripts/download-sources.ts b/packages/graphql-mesh/scripts/download-sources.ts index 31bad97..89a975b 100644 --- a/packages/graphql-mesh/scripts/download-sources.ts +++ b/packages/graphql-mesh/scripts/download-sources.ts @@ -10,20 +10,34 @@ let config = getConfig() const sources = config?.sources?.filter((source) => source?.handler?.openapi) || [] const swaggers = sources.map((source) => source?.handler?.openapi?.source) || [] +/** + * Get the name of generated from the source object + * @param {Record} source + * @returns {string | undefined} + */ +const getFileName = (url: string): string | undefined => { + return sources.find((source) => source?.handler?.openapi?.source === url)?.name +} + /** * Download the swagger from the given URL and save it to the sources folder * @param {string} url */ -const downSwaggerFromUrl = async (url: string): Promise => { +const downSwaggerFromUrl = async (url: string | undefined, index: string): Promise => { + if (!url) return Promise.resolve() try { const content: Record = await readFileOrUrl(url, { allowUnknownExtensions: true, cwd: '.', fetch: fetch, - importFn: null, + importFn: (mod) => import(mod), logger: logger }) - const fileName = url.split('/').pop() + let fileName = getFileName(url) || `${index}-${url.split('/').pop()}` + if (!fileName.endsWith('.json')) { + fileName += '.json' + } + if (fileName) { const filePath = `./sources/${fileName}` writeFileSync(filePath, JSON.stringify(content, null, 2), 'utf8') @@ -37,7 +51,7 @@ const downSwaggerFromUrl = async (url: string): Promise => { * Download all the swaggers from the given URLs * @param {string[]} swaggers */ -const downloadSwaggers = (swaggers: string[]) => { +const downloadSwaggers = (swaggers: (string | undefined)[]) => { logger.info(`Downloading ${swaggers.length} swaggers sources...`) // Create the sources folder if it doesn't exist @@ -46,7 +60,7 @@ const downloadSwaggers = (swaggers: string[]) => { } if (swaggers.length) { - swaggers.forEach(downSwaggerFromUrl) + swaggers.forEach((file, index) => downSwaggerFromUrl(file, index.toString())) } } diff --git a/packages/graphql-mesh/utils/ConfigFromSwaggers.ts b/packages/graphql-mesh/utils/ConfigFromSwaggers.ts index 2082f37..73f7f22 100644 --- a/packages/graphql-mesh/utils/ConfigFromSwaggers.ts +++ b/packages/graphql-mesh/utils/ConfigFromSwaggers.ts @@ -1,7 +1,7 @@ import { globSync } from 'glob' import { readFileSync } from 'node:fs' import { Catalog, Spec, SwaggerName, ConfigExtension } from '../types' -import { getConfig, getSourceOpenapiEnpoint } from './config' +import { getConfig, getSourceName, getSourceOpenapiEnpoint } from './config' import { getAvailableTypes } from './swaggers' import { mergeObjects } from './helpers' import { generateTypeDefsAndResolversFromSwagger } from './swaggers' @@ -28,7 +28,7 @@ export default class ConfigFromSwaggers { content?.['application/json']?.schema['$ref'] ?? content?.['*/*']?.schema['$ref'] const schema = ref?.replace('#/components/schemas/', '') if (schema) { - acc[path] = [query?.operationId, schema, this.swaggers[i]] + acc[path] = [query?.operationId || '', schema, this.swaggers[i]] } }) return acc @@ -37,18 +37,20 @@ export default class ConfigFromSwaggers { getInterfacesWithChildren() { this.specs.forEach((s) => { - const { schemas } = s.components - const entries = Object.entries(schemas).filter(([_, value]) => + const { schemas } = s.components || {} + const entries = Object.entries(schemas || {}).filter(([_, value]) => Object.keys(value).includes('discriminator') ) for (const [schemaKey, schemaValue] of entries) { - const mapping = schemaValue['discriminator']['mapping'] ?? {} - const mappingTypes = [] - mappingTypes.push( - ...Object.keys(mapping) - .filter((k) => k !== schemaKey) - .map((k) => mapping[k].replace('#/components/schemas/', '')) - ) + const mapping: { [key: string]: string } = schemaValue['discriminator']['mapping'] ?? {} + const mappingTypes: string[] = [] + if (Object.keys(mapping).length > 0) { + mappingTypes.push( + ...Object.keys(mapping) + .filter((k) => k !== schemaKey) + .map((k) => mapping[k].replace('#/components/schemas/', '')) + ) + } if (this.interfacesWithChildren[schemaKey] === undefined) { this.interfacesWithChildren[schemaKey] = mappingTypes } else { @@ -84,14 +86,16 @@ export default class ConfigFromSwaggers { getOpenApiSources() { return ( this.swaggers.map((source) => ({ - name: source, + name: getSourceName(source, this.config), handler: { openapi: { source, endpoint: getSourceOpenapiEnpoint(source, this.config) || '{env.ENDPOINT}', ignoreErrorResponses: true, operationHeaders: { - Authorization: `{context.headers["authorization"]}` + Authorization: `{context.headers["authorization"]}`, + ...(this.config.sources.find((item) => source.includes(item.name))?.handler?.openapi + ?.operationHeaders || {}) } } } @@ -126,7 +130,7 @@ export default class ConfigFromSwaggers { return { defaultConfig: this.config, - additionalTypeDefs: [typeDefs, directiveTypeDefs], + additionalTypeDefs: [typeDefs, directiveTypeDefs].filter(Boolean), additionalResolvers: resolvers, sources: [...this.getOpenApiSources(), ...this.getOtherSources()] } diff --git a/packages/graphql-mesh/utils/config/index.ts b/packages/graphql-mesh/utils/config/index.ts index 4859499..edd32d7 100644 --- a/packages/graphql-mesh/utils/config/index.ts +++ b/packages/graphql-mesh/utils/config/index.ts @@ -43,8 +43,17 @@ export const getSourceOpenapiEnpoint = ( source: string, config: YamlConfig.Config ): string | undefined => { - const data = config.sources?.find((item) => - item?.handler?.openapi?.source?.includes(source.split('/').pop()) - ) + const data = config.sources?.find((item) => source.includes(item.name)) return data?.handler.openapi?.endpoint } + +/** Get source name from config + * @param source {string} - source name + * @param config {YamlConfig.Config} - config object + * @returns {string} - source name + * + */ +export const getSourceName = (source: string, config: YamlConfig.Config): string => { + const data = config.sources?.find((item) => source.includes(item.name)) + return data?.name || source +} From 93a28b55435c395f81677fa2353b2288563086d6 Mon Sep 17 00:00:00 2001 From: Mbaye THIAM Date: Fri, 12 Apr 2024 17:40:27 +0200 Subject: [PATCH 2/8] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Get=20directive=20type?= =?UTF-8?q?=20definition=20from=20lib=20for=20more=20isolation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/directive-headers/src/index.ts | 12 ++++++++ packages/directive-no-auth/src/index.ts | 7 +++++ packages/directive-spl/src/index.ts | 7 +++++ packages/directive-spl/vite.config.ts | 14 +--------- packages/graphql-mesh/.meshrc.ts | 13 +++++++-- .../local-pkg/directive-headers-1.0.0.tgz | Bin 2127 -> 2302 bytes .../local-pkg/directive-no-auth-1.0.0.tgz | Bin 2071 -> 2206 bytes .../local-pkg/directive-spl-1.0.0.tgz | Bin 52076 -> 52269 bytes .../inject-additional-transforms-1.0.0.tgz | Bin 2387 -> 2377 bytes .../graphql-mesh/utils/ConfigFromSwaggers.ts | 7 ++--- .../utils/directive-typedefs/index.ts | 26 ------------------ packages/graphql-mesh/utils/swaggers/index.ts | 8 ++++++ 12 files changed, 48 insertions(+), 46 deletions(-) delete mode 100644 packages/graphql-mesh/utils/directive-typedefs/index.ts diff --git a/packages/directive-headers/src/index.ts b/packages/directive-headers/src/index.ts index cab6ce1..42d1433 100644 --- a/packages/directive-headers/src/index.ts +++ b/packages/directive-headers/src/index.ts @@ -56,3 +56,15 @@ export default class HeadersDirectiveTransform implements MeshTransform { }) } } + +export const headersDirectiveTypeDef: string = /* GraphQL */ ` + input Header { + key: String + value: String + } + + """ + This directive is used to add headers to the request. + """ + directive @headers(input: [Header]) on FIELD +` diff --git a/packages/directive-no-auth/src/index.ts b/packages/directive-no-auth/src/index.ts index 51d4808..c6b84ca 100644 --- a/packages/directive-no-auth/src/index.ts +++ b/packages/directive-no-auth/src/index.ts @@ -50,3 +50,10 @@ export default class NoAuthDirectiveTransform implements MeshTransform { }) } } + +export const noAuthDirectiveTypeDef: string = /* GraphQL */ ` + """ + This directive is used to disable the authorization header for the request + """ + directive @noAuth on FIELD +` diff --git a/packages/directive-spl/src/index.ts b/packages/directive-spl/src/index.ts index 400455d..4d26017 100644 --- a/packages/directive-spl/src/index.ts +++ b/packages/directive-spl/src/index.ts @@ -52,3 +52,10 @@ export default class SplDirectiveTransform implements MeshTransform { }) } } + +export const splDirectiveTypeDef: string = /* GraphQL */ ` + """ + This is a very small, lightweight, straightforward and non-evaluated expression language to sort, filter and paginate arrays of maps. + """ + directive @SPL(query: String) on FIELD +` diff --git a/packages/directive-spl/vite.config.ts b/packages/directive-spl/vite.config.ts index 2abcb3e..0324084 100644 --- a/packages/directive-spl/vite.config.ts +++ b/packages/directive-spl/vite.config.ts @@ -1,16 +1,4 @@ /// // Configure Vitest (https://vitest.dev/config/) -import { resolve } from 'path' import { defineConfig } from 'vite' -import dts from 'vite-plugin-dts' -// https://vitejs.dev/guide/build.html#library-mode -export default defineConfig({ - build: { - lib: { - entry: resolve(__dirname, 'src/index.ts'), - name: 'my-lib', - fileName: 'my-lib' - } - }, - plugins: [dts()] -}) +export default defineConfig({}) diff --git a/packages/graphql-mesh/.meshrc.ts b/packages/graphql-mesh/.meshrc.ts index 76aa748..2784d42 100644 --- a/packages/graphql-mesh/.meshrc.ts +++ b/packages/graphql-mesh/.meshrc.ts @@ -1,5 +1,8 @@ import { YamlConfig } from '@graphql-mesh/types' import ConfigFromSwaggers from './utils/ConfigFromSwaggers' +import { splDirectiveTypeDef } from 'directive-spl' +import { headersDirectiveTypeDef } from 'directive-headers' +import { noAuthDirectiveTypeDef } from 'directive-no-auth' const configFromSwaggers = new ConfigFromSwaggers() const { defaultConfig, additionalTypeDefs, sources } = @@ -19,9 +22,13 @@ const config = { ...(defaultConfig.transforms || []) ].filter(Boolean), sources: [...sources], - additionalTypeDefs: [defaultConfig.additionalTypeDefs || '', ...additionalTypeDefs].filter( - Boolean - ), + additionalTypeDefs: [ + splDirectiveTypeDef, + headersDirectiveTypeDef, + noAuthDirectiveTypeDef, + additionalTypeDefs, + defaultConfig.additionalTypeDefs || '' + ].filter(Boolean), additionalResolvers: [ ...(defaultConfig.additionalResolvers || []), './utils/additionalResolvers.ts' diff --git a/packages/graphql-mesh/local-pkg/directive-headers-1.0.0.tgz b/packages/graphql-mesh/local-pkg/directive-headers-1.0.0.tgz index 4618084b0e104180c50d4f64a85ed2ad2a455aa5..e0ed99a0ed1ba45fe63a11e1cd5915abc7e4bf8f 100644 GIT binary patch literal 2302 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 Yzh3`!t6Sab_TAcl00@=$-vB}Y0F;w}^8f$< literal 2127 zcmV-V2(b4biwFP!00002|Ls~^bK5o+_OpKl%AKjD3`J2_H{xhh5Rx4=c5ZH($$GpV znt~)cBtZj|9dAbeeFqm_L{qlYb=7Q3JV+#P?l=ed2r_cq&(0h-&o7pqKW(~~tm%bQ z{KdFrJ5SY2)9iP0?a? zU%|EGFYy4xbcw;LZtc1xWHFdiCt6(j=PfV<6kjbpiffwc)KaQt?D=e+=4VCJhzapo zQ&0#7-(d{A#Lg5`Hu5NTWA7UON}Z6+2o1p3T>te~L&r^X7Fh{AWQz>JcE+n|q<-zsHRD*O-DK$gvt!c7q>= zAYA%Bcu_h!$YzDluCw&x15TC*wKgcHnNo+{hAyau_)DyVb4Hfb#X2~5=$z@CIu-+7 zI3pX}$)tV-(_|JISb?G7GKAf)$rQ6w^US)3eNo5+FwDs_W?ne0)zS`OOnPU8PJj)O zw_rFNf;}Z`uU@Xun}J$UU44UZdHNvP&W+>6fY)G6 zMWaMYiPEw)VF+qT6(?&9{H9`7HYICBEj5X=g!l%$r6lkeuGJKY4k+du%mWFWE!SEGwuaJ9%+$-c>A@>ToZ$j?h2*#tk=fA4UxMU>U0{%12cDIQC zI=u@2ZKr+J;@b#o1E9Gi2uOIzv{m&~^!I!Id-9@xbp5yb`*)uITD@Mks{gIj|1JO; zht_-vNWpeP0X2;yHSHQx=8=$7v<%ZQWpA=>1nPYd?+YAHkd?emR0NVZBgr2)WO7+4 z#ngxujXIvv5(ugpT82h7`Had%7|l&;1fhEKv_`pRbNW2F5^86jFHAd?N)^YmXU%7v z@=VY$mwHj&G(j|QFNIGrb3vmKQNrN}h?y()^@e-NMCB>d6c1*>Jv^8NcX>EH4M2m= zfV2+$_#+6T0MKO!QmJ=4-Ag7q$rJ;lseEWg6h{ul4YfEl^6GdW*S01n5zGg<&5rxI zF~uzQLMQe}2n?f{91H}QEt)=Y9lsHMo@;50r}%n=BOFd~=z2I8$Dc)FG`Wdf@4wro z(P|m3=ZQfv^Sui>V)Tu6+ReFyKVqzBw2gMAEy9>?P`75N>$nTt5a!MZb>_l^XNiu| z7nh|3LRgyN8^-VQCvQ(h->w9Df5`b?KKq~S5cSb2(N7Y)`sxbwk8lhN7g%t#`;GHkM%~;MHIf4b1BGRzEC}#0 z$Do9uSh1Zz$WUfP+Ig}@4v}yLK+5T^`brq|YKJ#*G2+|B*%KZWuClbw@;$S4ox}1FM2oT2w;?)6V})LprNu9DLgZu#>x843 z-)o^1NsfBJ^NFC(A|=*RBj#mTB2lDr8}2MKQ{GQy}HRxwLbR zj`{2!WP)UKS=(X-p;%@})LX7M-a4LCI2{qzi)Rz=DvDMtT>) zs}ZWsA^zm|zuUrpold6&|Me>T_ZPx{0{vy{9+4Pl456o1vSdq!3LIv%*+zPjq8P!# zYzUd1GWGJiuV5EHPY)?G^iT>WKYT}6l2ll-!jcu1tgz(Y4@*|?@qau20>3#)<_c_uHv z^&5Z-LVWCm);-_(4c7gs7w)YmJ3s%+KL7pM^zRrO!hf7Pm1wW|GL?Y}s}r@#O} F001zQ6uJNa diff --git a/packages/graphql-mesh/local-pkg/directive-no-auth-1.0.0.tgz b/packages/graphql-mesh/local-pkg/directive-no-auth-1.0.0.tgz index 7667a245939a4a01a5cce39d88f317b6148ba765..23c6179e65e7c02d5eb4431ae7fc6cc9814ff55d 100644 GIT binary patch literal 2206 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 B3bp_M diff --git a/packages/graphql-mesh/local-pkg/directive-spl-1.0.0.tgz b/packages/graphql-mesh/local-pkg/directive-spl-1.0.0.tgz index 4c91b36e94dee0ea298b0f758ab66abbf461615a..eb6d3a502649f7b133fe32f529df046ae6d98610 100644 GIT binary patch literal 52269 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 literal 52076 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ΠUnQ`{6>zI-V-F^uU&HbBiG1Fvlf#( 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 diff --git a/packages/graphql-mesh/local-pkg/inject-additional-transforms-1.0.0.tgz b/packages/graphql-mesh/local-pkg/inject-additional-transforms-1.0.0.tgz index 0c456c0fe501815367dcb22f95efcbf20cfb0289..93b007f4f93377ff8ef425a82dd7ec712eb84653 100644 GIT binary patch literal 2377 zcmV-P3AXkhiwFP!00002|Ls~^bK5o+_OpHkmb=qPE=5t2gnM1?m4U>dKh|K7ocBq&j?)419uAs!_1;NaYV1AYWg;(Bl01?pT}rhYK%cq`KJ zqZxW{tjNKAHPbYYkB&eU-89Xk{&5c+^asagw|jhS9)Sb1+k1HQ5FD5XduS=4EVux?omFOCQWlm!0eMHh?!MpvniQB&h2E$2w7ACNM~;{|q;_AywirqZQPj41vYy9t{>oFc2-M+@O2@cPY0Zzgk;Nw-QtpunxNas(j8 z?zQ0){@)Z9w(_B@ok+V&#Gxb_m10j$N(NT(81ooUTyNQI=6r&$85qaq?QRp^f>@WPt4OpTjezXu| z9gy^r@JmN#X+99m1dr+(o05Or%2CptF@O=M)?fIt-rV$>rk*_v3^z%FYcZlorPfFI zq;D>p>e#(NS1BSiS0wLsFhc=axNP2>IHKr1RdlH2*7jz;<(Cv%<=6-7yR%<}r9`2I z-QKrXOlGxHAEs@$PwR$+A6b%v(Jjv=tI_HF6OcH?9y${X=GBX*qYTm-p_?4kO=(FP z|05Fa9shTa`rG(_(5>mjqC;grCzx%Mi z_5R--40?6`?e*8vmsE1h3@Ul#>7X{07)UenO8_+SBEv)&on2};qkNAqah7b( zVcVLko%;bd?X^%UM;sn@4jJbg&ONI|nPAq*Y<9i3?HMA}k2tUdhS4b<60z5)pq;#3XnBlh=-n74D4LP%W zKaJyn$dA{R49drCwO@7%n&SSg#t3B+!HYO_O`ekt0nGm_{=0YVe*6C4@Atdg_^*3h z@BjP3fBBZ)*ZHod4Ygu>)Wns9BZ;;S}m$J8*hrTM#+}TfZJd_|~x?oCJ<#S=K3>p0cj*^_LL7)XyP2*BuBQ zeG1{ZHHFTSUrssEdd?W1LHJDkWQ?Mh{XjN^+Jbw^h@9~WgirJdgp*v%T#>+g`PEy8 zStmqtcK%cJXBw81OoKBPf-);0bLLX?LO2=gXAoZ5x@kjb+SU|4dFTiyx#Tkl&%_lM zGJ!B=*8Q-W=s&ZrQwUEL(a+)Jsg59g#X1&t?h)%b4b)I@aeQ>5gX~ib}J0;CG?3zO7hb-wPW0nO-cUYFB-0 z*;ed6ht4}YBlG!XBD1CP96AFhH>=2Ys2|%rwI|T&+S!l^&oOS?gheA)=B(`P`Er(a z?%el^a|@d%rURWBljU=dR>Jg{-z1KH#kLggUe{5q`gd+ff2Sc=x#jo+tSUn8LHIQv z-mxoevbjz6u1I+hdhBxckv2Kd`ObncnvC^h2Zj@;KE(L=@BjP4e@Fe^Hva1kYW(*Z z;Xe-kHU8UQ{pTq=R(}3eRsm&& zobs@_DBipQXNWBGl9C-2pZttm9W>r9GqXRH<0MLmB3ztIwv~MAM%TPk{#1+-zt$a!w}zWb@*`bvqwCg|%-rHf v)?L5%-1+_g|God$@Bj7wU+@3*{$KC^f9d{Tx4PBs%Wb~{Np+&y07d`+W8|sZ literal 2387 zcmV-Z39R-XiwFP!00002|Lq%VbK5r3pYG|hwkeNYECO>=+W+ygs%!vnM5KR7V=!H(G?woqicTkTSk;Fct3A&6PEO4oX{avkAuTIf-dX(QD=phlQ!jIg5IbY&O z3jx*vNzV!II?{9ViD)KpRMps${Ov}J67LKF9D;iOg)ZyOK-f0*>~t{PBnhs>jKY;V zU!jw}xozrg_ZD5Ih|rvoyw|}D1!&>2b#vl~qR&*}p@KWr0AJBd60KA0i}~GJFTz}+ zV8d?jS}R7gUa2qRwq2)nOXee)lY_$>&MT|c>-`;&IK>`15gX?H+n0w~j%$Q&vh$Ez zQqKPo3GY4s?;q?}^Z(&~lmFjGlR4uxsEz+Yb-t1MnLo8FpQ%W(bkPuJUsuubEm(x z|Mws7RlfiAhr>bR|M%kmEdaF04S5Lg`2%COc=+iS2fU^;%ufgtKaLo3-!KhRK(i1M z%1?s~7P>x1tNE}j5d?C9qP}}7=#pBDnL#Cvd>N>XWXhu%c^3dpJhw0sMsFJ%jwm1H zOPD2@^DJ%6+0Oldi}pbvBH;~l5DBYu7(Na`7teu^4t)0=h>{TCGy*x*D$rXI4vc3A7(J;&FTp5r zG3v@V(N)Lp9OCt8v6m*k|g_hfRhCYo^f}$CU zJRd1`e9kvUkBP|m{%2qs{l3wEk_jX}H}etWz!>CUHjC@S$A-qh7!=$Rgzy%2B|-B| zxtr0=$7vh~M1H)kMNmF&tK)K5z!ddw6h<(U3A?aE-{d~o5_$OV&HwH^Rlhm@@9mp| zYW~+BHu3*{^1nQ!@98|MX(O!&kD7RpdmG188)^AH(MBIM!{`}Ck6=&k7)rE{IzLO; z@JC$>#p55*Mw(?=5Kf`~y#uHFwgsUxwDqeogcptl;UsV@%d(E)^q39(pudCgoqh`8 zsqR4N=u-$!ttoVtygTJU>kUJE1>q}k$q>aTyFfOD+5&sZfE@8h2!GTk5KeM3bA8>w5=&TdF%)!Ipq@w zPehLsnLrpb>0YiT`p<0W7{X(P^cy&NsUryAvw?-3Ys5ylacu}=Tg+0&j(fIZCeDiE zWG3MAsfrUTlnV6=lX1Z_lc<-vfdVGU7WU1UKY3AQzQK}T_$J9$#yW-06P12JxPWmT zira!9r^S2u&d4vs7NVU(r$13P;KF7*kl&Y-aIDyd&QF35UtiAlJzJ}dUAnOloWjN) zX0$$!1+f#k58f!gQS@^3Cz5cnqqtPR+R*v8LVl}2bq3@KolTi=hvULcm^E@{&dRQ? zm$S5U<$h3&iKXXC$whD5YOOC%ls>0+R zgx~Y&J-bFGo6BTxvy=y+$GWqxl*xh43k$+vGS&|qcrkGv`rD2hzW?7>{x_`F|LzS3 z&G-NRr~Hp6fAWlTUX}x2$%%^_YKuchLb7~nXe;Lkcl6?DlsFG4om9_herb}+Cb?{q z%ZHa-=27yml2_&u|20y|l6~$noBYNT$gS`H*cLMRTODS%n=YJv$9-AmhIfG;%rpjB$djo(dL@glyV(y5b12( zTem9d8r5upO6k(5Zr9aTGmXGo source.includes(item.name))?.handler?.openapi + ...(this.config.sources?.find((item) => source.includes(item.name))?.handler?.openapi ?.operationHeaders || {}) } } @@ -122,7 +121,7 @@ export default class ConfigFromSwaggers { getMeshConfigFromSwaggers(): { defaultConfig: any - additionalTypeDefs: string[] + additionalTypeDefs: string additionalResolvers: any sources: any[] } { @@ -130,7 +129,7 @@ export default class ConfigFromSwaggers { return { defaultConfig: this.config, - additionalTypeDefs: [typeDefs, directiveTypeDefs].filter(Boolean), + additionalTypeDefs: typeDefs, additionalResolvers: resolvers, sources: [...this.getOpenApiSources(), ...this.getOtherSources()] } diff --git a/packages/graphql-mesh/utils/directive-typedefs/index.ts b/packages/graphql-mesh/utils/directive-typedefs/index.ts deleted file mode 100644 index 6c92b9a..0000000 --- a/packages/graphql-mesh/utils/directive-typedefs/index.ts +++ /dev/null @@ -1,26 +0,0 @@ -export const directiveTypeDefs = /* GraphQL */ ` - """ - This is a very small, lightweight, straightforward and non-evaluated expression language to sort, filter and paginate arrays of maps. - """ - directive @SPL(query: String) on FIELD - - """ - This directive is used to disable authentication for a specific operation. - """ - directive @noAuth on FIELD - - type LinkItem { - rel: String - href: String - } - - input Header { - key: String - value: String - } - - """ - This directive is used to add headers to the request. - """ - directive @headers(input: [Header]) on FIELD -` diff --git a/packages/graphql-mesh/utils/swaggers/index.ts b/packages/graphql-mesh/utils/swaggers/index.ts index 5bc1a04..c88c721 100644 --- a/packages/graphql-mesh/utils/swaggers/index.ts +++ b/packages/graphql-mesh/utils/swaggers/index.ts @@ -30,7 +30,15 @@ export const generateTypeDefsAndResolversFromSwagger = ( } } + const linkItemTypeDef = /* GraphQL */ ` + type LinkItem { + rel: String + href: String + } + ` + let typeDefs = '' + typeDefs += linkItemTypeDef const resolvers: Resolvers = {} From a03a267f3aef586282a1eea1d00d52a06dda1611 Mon Sep 17 00:00:00 2001 From: Mbaye THIAM Date: Mon, 15 Apr 2024 15:02:10 +0200 Subject: [PATCH 3/8] =?UTF-8?q?=F0=9F=8F=97=EF=B8=8F=20Add=20api=20for=20i?= =?UTF-8?q?ntegration=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/integration-tests.yml | 43 + .github/workflows/unit-tests.yml | 2 - packages/directive-spl/package.json | 1 - packages/graphql-mesh/Dockerfile | 1 + .../local-pkg/directive-spl-1.0.0.tgz | Bin 52269 -> 52258 bytes .../graphql-mesh/utils/ConfigFromSwaggers.ts | 3 +- packages/graphql-mesh/utils/swaggers/index.ts | 7 +- test/api/.gitignore | 2 + test/api/Dockerfile | 21 + test/api/api-docs/products.yml | 82 + test/api/api-docs/suppliers.yml | 60 + test/api/models/products.ts | 39 + test/api/models/suppliers.ts | 18 + test/api/package-lock.json | 1102 ++++++++++++ test/api/package.json | 28 + test/api/server.ts | 43 + test/api/tsconfig.json | 109 ++ test/integration/config.yaml | 27 + test/integration/docker-compose.yaml | 25 + test/integration/plugins/index.ts | 45 + test/integration/tests/.gitignore | 1 + test/integration/tests/get-products.test.ts | 73 + test/integration/tests/package-lock.json | 1515 +++++++++++++++++ test/integration/tests/package.json | 16 + test/integration/tests/vite.config.ts | 4 + test/integration/transforms/index.ts | 51 + 26 files changed, 3312 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/integration-tests.yml create mode 100644 test/api/.gitignore create mode 100644 test/api/Dockerfile create mode 100644 test/api/api-docs/products.yml create mode 100644 test/api/api-docs/suppliers.yml create mode 100644 test/api/models/products.ts create mode 100644 test/api/models/suppliers.ts create mode 100644 test/api/package-lock.json create mode 100644 test/api/package.json create mode 100644 test/api/server.ts create mode 100644 test/api/tsconfig.json create mode 100644 test/integration/config.yaml create mode 100644 test/integration/docker-compose.yaml create mode 100644 test/integration/plugins/index.ts create mode 100644 test/integration/tests/.gitignore create mode 100644 test/integration/tests/get-products.test.ts create mode 100644 test/integration/tests/package-lock.json create mode 100644 test/integration/tests/package.json create mode 100644 test/integration/tests/vite.config.ts create mode 100644 test/integration/transforms/index.ts diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml new file mode 100644 index 0000000..c354fd7 --- /dev/null +++ b/.github/workflows/integration-tests.yml @@ -0,0 +1,43 @@ +name: Run Integration Tests + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + integration-tests: + runs-on: ubuntu-latest + services: + registry: + image: registry:2 + ports: + - 5000:5000 + steps: + - name: Check out the repo + uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + id: buildx + uses: docker/setup-buildx-action@v3 + with: + driver-opts: network=host + + - name: Copy patches for Docker Buildx + run: cp -r patches/* packages/graphql-mesh/patches + + - name: Build and push on local registry + id: docker_build + uses: docker/build-push-action@v5 + with: + context: ./packages/graphql-mesh + push: true + tags: localhost:5000/test/graphql-mesh:latest + platforms: linux/amd64 + + - name: Setup services for testing purpose + run: export IMAGE_TAG=localhost:5000/test/graphql-mesh:latest && cd ./test/integration && docker compose up -d diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 2dea47d..49ac0f0 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -15,8 +15,6 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v3 - with: - node-version: ${{ matrix.node-version }} - name: Set up Node.js uses: actions/setup-node@v3 diff --git a/packages/directive-spl/package.json b/packages/directive-spl/package.json index 523197a..b874f8e 100644 --- a/packages/directive-spl/package.json +++ b/packages/directive-spl/package.json @@ -19,7 +19,6 @@ "build:cjs": "tsc --project tsconfig-cjs.json", "build:esm": "tsc --project tsconfig-esm.json", "build": "rm -rf _build && npm run build:esm && npm run build:cjs && node ./scripts/prepare-package-json", - "dev": "vite", "pack": "npm run build && npm pack --pack-destination ../graphql-mesh/local-pkg", "test": "vitest" }, diff --git a/packages/graphql-mesh/Dockerfile b/packages/graphql-mesh/Dockerfile index 276d6f2..e2a7129 100644 --- a/packages/graphql-mesh/Dockerfile +++ b/packages/graphql-mesh/Dockerfile @@ -31,5 +31,6 @@ VOLUME /app/sources VOLUME /app/config.yaml VOLUME /app/transforms VOLUME /app/plugins +VOLUME /app/resolvers CMD [ "npm", "run", "serve" ] diff --git a/packages/graphql-mesh/local-pkg/directive-spl-1.0.0.tgz b/packages/graphql-mesh/local-pkg/directive-spl-1.0.0.tgz index eb6d3a502649f7b133fe32f529df046ae6d98610..506efb665dbc5f8c47e7685b707f8723fc54486d 100644 GIT binary patch delta 24850 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

6P3CW zCK`q7l*`{_jek9C$eyP6(w?qrH^|CVt2;9my75Cl@*ymQh%37&ypn3gr6o@~XE#Zd z;NiNNSIz4s2gZeXs+S9}oBSrv9psE4T&#_BOj4h+0S1yA_B?Ek#x!N0K9$;W_!5hf z@)Y!HvY&c(-K$T{b$7tvAq}qXh24nLnUO~&pB~0|!;TxZxAX=oev*cu*TYV6m!LPz zJT^&>VGCp-Rp$tV;VgmqJhMx##Tto>sl|}BlEFv$RBx;bwL7CR5S{%ChW92LwT#P8 zc7~kS6ll#Vc4tE6;5k}ME&?G{;XO#)eloeIF-eN|V`ec3Oe9y;G2`IqJN|==MZYld zL{q5DmO>gVI>|}5khMRUF&8P5@(^b4b+Cf0n~aaU@cs`RxF-E_SR1O}xck3x1Kzd7 zl(A=-<-gARd2gP~?ezv3C9sU%Gi*Md$mL{@A$Op*5&zEoQ*e6ry!ci*B=P*4s1q zUQl-Io<-GXxCm$}7wD+M{x-#p9`2FY{>YDY%m0-;n4UJcdxV_$f3kw9heMV@mwPQN z!4)=n!Wik(`eufns`00ZbeA@a%&%O>_+-g(0P>|t$eH=9`>OkytI5#{%!|`j7F}#j zc}{a^#Aw9CzNqo%p4rX$)nr$kN~QwfqkuW8pJP^NTEwbAE1gZjeQJ{F@f=)lv^cNU7x}d@0n|MgcX}a+ zTyfinpC|+gjv#&QdnLY~V`>h-`t7v@CEbd4{A6ecsTx)}ODx&(2{x*5#6Z+W=rgi@ zzQE;uLvur1po*I^5+qr;L5JyuZVm`Dp!)ee1ch^~H-STYFC{w4cm86EeSK{nBH?|< zC5M*Q{bK|rR`Xz%0~d-n64-}-qjpc(MLV41psYROE;~})kgcmo+|i5>XejWji?C%>tSitO)(hOXtuFfTLzKWYlI81IKS?iMUJ)J#->xJ2J%AvFB!TKWi#lUOqy#h;n6(~0SwIUNMl z6SO)4GV#J!iEYSPLakQlTU4$kgU#}hhgfujHDV2}l`JTI`qecBTXk0L${GV9A#$d?{V`2Z^7P{&=UG25hLXn2w1m$M<%(5 zDg)oqBWQG1wX{jhvBzqlMCM&pepgu|HXMcw9Lyp)tMCKk4L>GGb5}Qlx!@0eAU9q7*|`x)T_`=*D0PG5Z)Y`w*^L4%F>{+Zc-thvY0y z{qaGrV5(@L>gtAKw5@;z+Cz#{Jg&Ow#W`Do-5wikzR(Qa= z?vSu-9X@4hVg|kh#rk-Pl*&I0LKJWQTobD;!tf;P*yiRAC3{zP+#UsSt*yC_qhQBx ztfdJ2%gB08*Zf3hK}zH{N<^FMD{_8?ok;v*t*aQG>;A7|--uA-RcXyjK3c5A&Y;D) zYt>_w!FbtNa^wD_Gw-G-T8+xi0041rNIAvk#w8s1p6e@dtZCk>L$l0H0t=1r)`Fdh z6p%$TH>BqD4&R{~vy+V^Fn+B)Sl!otWk(4%#m<;|QNz|{yu1sF4i8&nZHJe$wH?op{L3exdymAV z@azkjp_@Sz=>FAxsG>a(k{Yiz?G8JIRk3-R#ItwFvK*pj9Z%x$$!(km3t zImdz)_cjR(Agwy&sZFV!*cf1I@INgq0!*fhfjRpN`hQ`8K_4zC7A_)rDMEI)WA;`0 z+?7Z=xDCpIz@I2VFtL3vw0{YM$UUl&pI4|o`Ytg4E1a-tf?&Hmm_=zVp)O3uC$K9W z%v41E4}2glr1i<>eG$C#%5H~0{B0RAuJ(oCwaS0(CMC+6doYvpjMbi0jSV1D0PFWs(;oSM|oUVxKdwmXkCjM zsdVONTB!2o&lMh}GgEDetmzTCg^%hKMibCyIkH(bXVqA2y2&=Wt1iwav zBB80U;<3q5(&dM+a>D2i9&dH?E3>vMW!leGa(nJ8R_dyJWC?!NkS|%4w{g+#y&qmi zYM8 zZ@*HVjWZ3b5Vi6_|cPnM=q(fpg)(<*|)NHeww4H`L$4 z9&4Pza)B2z18;J10vm3FVa}y_Pm13(kw+!#F-k zeqp|&-ySJM59pYdfDqgnmp^ECe9q*qE4?Y-_s+~Xu2{+Wdlno#)fOs`!GGU4;_ZP9 zLBlQl2c3>P%ED~Zo!k&fQW77FC~rqQBe0*kCE(Kvu~~PfTL44wv`oMzpaa(%GaSws zj}PG+Po~D$Yj}_yk5s!zT)HaJ>tarTLY`X`WM}H;!;WJ`E=*YGWCCK!}*_?Pk}l-U3cygW!o}@V>dVE`?S9M zuo4Ut8SY{`Qx5>TKSfiA=F+m&IyqDmg8zdcBJaHQ{(~SC{~?G|m45_+$9r;V{(lh& zViQuN{}2e-#*>>c{ROL8&Nh9l{~{2A{~-`a1+s)-kacrU-SXR!!ElNoV0b!@{O@+T65r(Yu|QO>(HPBEm$>G zH0gF2*r)iHFI8kqHa)04mQcQL%8N2Rm+{`~&yFW0fCb}wz;+w2e~N?%9PGHp2#-NO zd+NIK1@8W#2s1a#hxgb*mDakhc)wgbJFu;(YeT}jBgTUZgbMfSb44f1Lp3l80VH!( zcipxMb*9xYNQ%XX^!GeiL)8A* z;Fx!Q(wO|lv};<4llk^PDmm|zy%h`5+w<;~^~CBQ;??RBRMS}<`3)8q-R-OV&-${Hzobkl^?eA4l2McL@h;|+a=aXyZ6Bt+ z22mz`N^l#T8oK7%=F}H)zjsTrc~wV9XfI(vRYY+{qH0hz+I8fEwJq~9uc&bDtD84s zERsZg2NDDBHov9)zCxwm$xGp_Wn#|1QSR@bz0!N)L{u~>)--6*(ynU2n49=Q>P-F> zwI9M@x37Bk+61l18i$TiN!vi?z-k+RiwYF8!c;ea^&5Mcn#(mpt#-&(nNMQL@A`T* zU&qoW!Y02?+2H<1hq~z>e{(IPUOiX0`}v5&HHe(%B>7z<9W1cVs(aPVjFr@2qeUkK z419#`KHGEK5ua~LcWgd8BrQFmw7Y%9+-zo&Z&2~qh3=)EnA9YZTjdJh$e)fm(XK%f zg)IYZ*RLDb={+*@j<+Sz|2*UuTzrMD{Nd0CIJd|On{I=WEw=$b_0oFY!v-SBDT`9`w58<_Fo*<?+x-SnJ96)-duAIAPdjU0&cDxD#S*v# z1iab$D;DKkz7H`GU_oZKkTf7S2m#SC}#v5l?zN~#21a}VB|((lsb70(KWeiTS^j)Zl!ZEX2k3fhaNr*h{$ zv1oARuey^;c}HMDR7zj@-VvtdZk5uc(`QY@0~^ zNCL;hI=HvL!`gIjG6P?Z1-m$1_|-cv4hD=s`BA|A_n_YA(=Vbkgx=Y3Vh~FzEGe~r zl}QTx%`-w5FGbcbgUk?6pBM{rgx~gX zQ{t%}A3fW?P2ooj_*^DC0xBPQR8>g!U2wgUaa*YCqfWb7F^BBMJ({;9}G z=85L9dvjo89EX!Sz3um`7arlD^kj7@VL6Ny>U(M;el<5o7oG5Na4kLUCf+?YpZl+3 z#qJ-s$Qs^~qf~kNPg4%%cke8CSP&02b<$>x**!PTwIw&>on@BE)|?fzX_~C3QyL_} zqNFyV!gpgp2?LSfV zGcCU$y${#VRAq~AE9vF02lw9DOt`r0pF80cD`eag@HwtZpQmdZrd`5BZ}_=OmwLYV zi>^#4)4fVUHg3GO!cc0%2l@^Fr`ty9wd}* zWr|M~UQqfrnjP*j(nBfdlDeV%C|mYoTi}4!{2yDG-kPB9S^<(R7Fu{{AKyMvlJ8)c z!C%qZ9dji~u$vgq%>?IjmdiQNqw+VLzS7F)+8kc&e^MenFwaX2;2xDl zz9Rbobp2eg4;m-}cP2pjJjodt+(hus{0mHawh`wMyl-M|`oAPr9vi_86=a8I92EqY z!0fYfJPi5^P>QZrL#kZlwwv%`UR5)X_r)M{==G@grt2H#`aM@=`1#4os5C+zO@jEr z;g_Z>$b3r=x3$-fIF4GpC5=KE6;kx`2~)O4Ap((6s9<5-`nu`Id+eBX?o9=i^FxVa zo7Oq8JJIa!Q8E;`xPi|;V3K3*mP`8`ig^sQYvS2_*f}f4$CUpvt922Hzz0~I9W`R6 zQ17|Luo4!Vab*1Sfm6XM69a#?JA8uiyTe9i0C5XjJwZf(i-^J09)9@s!T17HUPkbU zpCQ1A4V3nrUYX5IL?0fs8e2=x{p7~Qp8!)dYzh3enQF&JX(RJk*N}%A5C&B-aS!Lp ziH4dnyV_TlcgJ3r_I_g^)}&|Ri(ZJl4UJ!B=cR!Vl^A4X)@~KlB$X&)Rh{0?1IDcXRBHf3G@TBkGiWBB>WNKBQ{``3hjykDkUB@4%A3yrY$KjlK8~I+WkH zmGEm84F;k9;MG2OhDK1G!2b$fu+M?L4%-K%SGarIA)tMG2$*fjTmwCKdmyft5A-Q{ zf?(HzYtJD*LfcIKZDK-`t>d$XT&PWx8NmJOsq`~(0()#T1k?J3mpFXwS-sAxJ=`yj zNCufpARRB+ooylY?rBVLc6z5i;g_M)gsx<2udkG|KAL;viCXs8U;N79TyumT92F`_HS zp|E)}&9SIW*;Vq<`3yGQBKcPWSwb1mwhvHni$O`Txe0kbQw&(w`4eC;Q599R*C-Wu z=f%0c6o2xH>>9|_2i$nl4fqID00DHP7ajpuIoNf&cS;b|F~b6v1ojP(DFSxM38GBS zpviaBCNSm)6bj*PVrGYib$dt&d$I-5k!c4eZn<7qia)1kKl2|w=!~j`FEW#^&wsyG z6dSs{*S<*+EWV98dw$JO^V-lQD;4BuRy&8Qw6Zc@GG1A$JXn$&%s?!t)#5`=CN>LO zNp{1|v2dFyw-I0!I5;2cZx1%NQK~QJFT}g$M2Nh2ZZT8u z(I1(v|6BaaH&TCS{|w=s41PZb1;LCI&)*d@qDtL1`@7jC_?cfnAa}_ZFay=x*LY`#I7yVah;*Cqr)34_?B=iWYHn*-$?fCUXg2#0VBpulb2Y&hFp24XD01^UylhNl3iV(g{~D@^Iz z;OOaT7rh;CtXnEk6o$ziPx+5#hMybS{$iKGAPvlmXl`RK1Fry!uTa363IsQo1j2=;2cv<>w^Wb~JK zGu=Qn8JNAl@UO3#`2d$?#7aoYTQBZ%ycxm$&0Xc%@r{qL`FC;Re2l_vB<2-7@%Wav zLXY>}mWEXr*D#v@eryz?afcZ+8(=8_yd7!>8NYy6ApC4T2PTpZs_WUmoI!3toC8Q^ zNBzYw<+*XMr`stY@g9*=vq(3}QunX)1ANr*LuL85^0XAZIb9Uu+0NrSmy~$gVSF^p z1yEdEOne0MAIcY0Ro!uKqW^rgrp@{Az`#B8#nac;rlu=_PF~UGqRpah=kZTdinELT z#{6(E>yE^SGV&YlCOI<<)SYXJ`$hY%q`x}?h^Kh}|v?pg4b#>$+n z(Vr*bnNkezU{U>>a5SU_Lh+q@ZQ6MU8)rgJv*rUjV0;cS$9^#&z+-?XTz11O^p; z4nxDxki!XF_$*X=%3PH(G0cjdqnoRwgp3V?swz8zu_ptUK8$M}C}9gpdWl_7Pr2d| zN2|2ZZ~Hs-`>-^F z5cer0i$Q((>$wEq{f##=ZwdmzwZ{nUa*L@IKy*r&SY3kx@)wj8v5pEK{T6GU1O*Hq zU^m8S5|QoDN!7$oMFJG2mi#jZJ9u&)^y9$L^@26Jz{l~0VCOBF6bN>P{6v*2$0L?D zLUXRV1XE$Z$fcM6HMlY}Saib2pt63fy7)_Nz4clB4~YgjJw)a|-II=+Q>FvcKZS4) zpK(~u7Tf4^+PRb-x@;!Y9^KiT8q2KprrFh=Xyvqr8ciE$cFn%m*haxPPp-d|e?GHF zaZ-4+nd~LGJsXwu3t4S~*jWsmtId#O`C~WT3%>{tUR~eL&J;bA+O9a7XZXU_aD|iW zRn3y2H&Po0olOTJqLi=roHd;XyP|OYXKX%mirD8gI9g>_7}w6SBeDtsT8;jp3v4?- zRqcc|&j7yFHAn@TS=(@BB(?f-Cm8uM+k@VkNi#@!ni-v% z;KATGHDz5hn`CWveAZ=^TKPC(&v9Wb26ot{4o2ahZnjOS4(BHYN3Y!b;pngxzS-61 zjH6eY0wByj4KfEn3>B+_-PaN0!Lwm=byM^3oXfo@GKq#ns(td0yTlFd zMufZ0v!Ig3N<87)R;h}nk4QXt$*yUt+&R#AfXlzX2yt#hf5&~7$6(7y15!w%7GVA> zi&OY+8gLW<3#b9Wv?q;1cY7$Rgls$8PlWXl&r=IZ$#q|bR!`k*m^L~$AHw%^rN}z1 z<2EJ-l)W~{7pH1{Q+!2DJbqN$VFA`?Q63Y!TKEfe~xp0eT6F^Js!{FyJ;U<82JoiZKvV8>o;jrp)W%6MTVWNTUR3S5ikSkv)#k) z0qEist_Ip#&wkCjfhj)Rtb+?4MZJT)4OB&VX?u7Z=vjZ1@8r^aXT#p*wejk5dh6H~ zdgD%BN@eo=Ux(OFW1FO0zfpZ?Fpp&`ohXu)k5XzW#{YbPN_gemch46|vb<6BG?hXp zFBACsZJd`^wys-LE?VaxeTG_h&VvJ5tq zIY{zkImh2|!cfM|-K%UVN(R%Z6LZ3nX<_oh2_ZP*x>JPE(i$JmBu zouE%5SvtX<<}Y@BsV3czitv#$QMs{|ejiHloE}}$Z{3ZOu9N^`Wz-W-&Oat1CtuwA zIb@f#jwbPITyMkA@sAi?9*>aA+pJGJ5-QtKe11S%{?|rK>co2KKv0`Lx=O|3YPDiM za*f$?lF*i)%LEVA(`pK&oEbfSW_BG*vdS8V$}(_{{Lt@DdiS=kxTc5edwDih+ z(Bi?>y=*NO?5($Hrj2LgexPQ$YXkr>WP0?Gcc8I{SxS11|8JEIp4f zLR1Z$BA(@HbN+@zrT|5u!E@QT#5cp68Pn@R&)^+qT75I$=$;G;@+YWmf6v#RR!GuD zTEdfA(l8~PN_uu5$uE#Mk`*h;vO#Q`WP;2y_A1G84!2*jqRgK@V0nKy(pK@$k%3iT z!z>BUmM|rF)})YYolY$ol&>;=zY#ZHd8fd0pR!Aq~m}?`A+&z+3hZ zL41k|`8Uv^KZNQ};UBNo*PP`iJZve(fJUWCxe-ub0qVAnDDV~8!&d}K3C`zGC)i_$ht*e&QxOij2;OE*_mL5gZ)0|6&t%f)C zgsm%e#NEX;vr`U3ppo&B)h)l4%PEp}>(O;f9pSyHA5u(zu!^suW60zL1o?&s+=)&2 zW8#*JUTSc~omL&1!X9+j7r1R$fS+{SQyG zD6Ov++oyJPBEq&HDpX2n@b)5>!O`*dph3}ghmtHe@^N^)4 z$T%4!bl5X$OQialkAx~d)Qx;#bz!OFx&~W<_;goV(2@Y}FM)|BecN6{w*q=;>+z3+ z3tg87b(0)4tiK?o!Oh4psSdWq(yZCy@lof6K+P0qsqo@Y+Z)vL(Frt_R;A385!WD- z8!Hjpz&{}gJ zW80jVR(^bsQ-4dccdGV2D=N=D3a_#OyZR4)WIu0m3hMj*bl>>cp-SDb8}}{uEQd9? zzp{8~_3}2=HSY_rqJK!yD43oW6bU>Vi-G&9PLY*zzm4>|E}Bx;DT&s3x3b=+L{&zE z-0c@Ww)K^5anW%kvsAGu6SEH%GSD>*w`{VMHdLRks9?Ir!bY^CGAmTVHGmu$yFGR! zE{%HM`~$inG+88M|6@n+AyvI+S%CI<(8K!)Fa6mQXp>`UvmY?6IE2c6vL+er)t4!m zo}Uh;ee)*JQ?V@B)wd;T*EY~|(@kbdXObmuhplE<@z|mn_UTvwd5htI{*ioMghaN? zf^nCov!gwu8hs_l5p|L+KB;fy*31HTyYA zj~dJh+N~H)NA0UF;FeTACVlWF(tavhL`J7Oti-mxEd07vZ;7tyoewV;V&)f*_{Mhd zG@DWj>v7D`&e3w%z_Z_o3ITliuMmuQGzj~@7SR8;T8H>wJB17XNAu}N|EVHrbc_Zh zbktptI+nF&{}LeIv#CM+d-}Kda%S_C9F)*q82A9pw|b9YTN94Vzk!%MULXB?j2W`L zr&%XJaWHx^)J2k@@%mC1Gby7d+VP!E3`tNbODyli2W--irr1Bz3zk_1qK$XIycv{W zP^NXg@Oot?MlU|Rx$)a~L6;KLVhM}yLO7_aqcE;xy3cP74I@OQsXITiJ27V)uM@eH^lwQosO8TYNP|ohbsLe^Ba`c zucTAautQ(soi3wlD)5et#qs?2r*h&c$O3I{6iKazS9m)=-^dYW_})3QUQlcP8&-zh z9~dsS(aRS*XJTxJy@d!d*wtwF(kuIJJNqTl!>(#HtKo1@zM&ID%DT#T`h(9mB zrLFq+3dP9euvkBk(oPdVcRHjy-|NQq{dAQD_u%)|;F0S4AG9<9F&lY?^J+~E>z`iz zBk@FitF%IMp9JC&mMkUJxss^GsUbzIOqtV{L(qWn4`eyK$%xEoP0wR^1IxVR^CC1_ zqnp(&!s*ib0T=H*FZEqD^B?v&6{J#W{q5&}-amjP3{8qlj2`%tX7V_wi+A{)uBcu< z4o-}!ZDNgapxskoIbg(%y1jE9h0zsHJznZ=^*Jp!S|R~))C=4;v1sT0=cb3 z;^xC~9C_h1b`kvCJrw-0v5Fn^r9MGgBv_78|+#Sl4)G@Q?N4 zJ+-(N^Cn#k;~TB}aH?wZhGMpDM0p)yr9_eTwVyj}u%8Kf42aK%A1y=Xc#0Bi*{#v# zE2V`-B3ExtsH8K1sb}>o7=&Xv^&VWm0TdNG8gnEphG<_BJgR+m@)H)xWVQCXNw0*N zY@T%JHQKV`A}40OcI0T&w&KSj?HOy<(?9Vk+{|!ymTW8C`cFEnRxZF)ajz|>6)wGpu(*$fTjmCkqM?tN z-LcQXwZtJythdQ%RSnXKkaS775~}<7>**ECCI;jQmU)ho%@xhu|LE-GaNjySuw<7!raz1PSg0cY*{b*uvf2-JP{gzVCl_or_bu zYG3S&>FSz`nX2iYnfH0-*Hf4QTb%)0muC;0MuW>19K_!yJ+2^!8uqZK8yrY{rwGC5 zLl)h+u?WO^l^yP<2q|E@NJGc0`EiFkM#mL2%o?}Q*zWZX%E|B)ljHkS zclN%MHVwYxxbVaGn8nUYByqiKLck;ECRVgfC$INC^mOKQ>km#Rfr<_HC)4zAS>_Cc z+VAzg+!Uhm6K^v%VO=6EBo?>*jtYRL$?a^;^KZX~)!?Hc(QTI}Uh^YA*0o5}{?jjk zUk+6J!0ZZS&29Q^h<~p4AA3(70S|qO@Px%(SN!W=1)@m1KWVpRJmPJ^)w>Dr+doCm z?!R1AfyF~=ziq;aK6c-q*i^UZuSO%pPF{MuPSKH6WL1x!IJ&ZT-SMPYqR;`MVEel_ zo5050x7Z=tRml~DEbj%G2g;Xb8O&JlOMp;o+Y!2Ho;1c?ciedVlipo4#BkNgNoScU z{);I4pQ1Wj;acD0ZdyTK5#&E84bi4M*C}1wcAqn}T>8Lfg_mORifW5fH6p56MVqa+ z(=6Gt)b_h6bh4JHgd06qC%u3jdn=)CA> zyLLD(POj9Obi}KWm8G?+D~O^?8w@$F3J;hO`{6^vo38(^0G1UUF|bN3C@7e=Z?F#w zx*2s|ZCH6wS(MQIb!M2>?P*m|Z!FSO)qg~0rT=TKxr!BRwVnG_Ie35!eH6dN+IZ=y zJVfMvqG7{6mxxxi4Pl=x?H*3lQ$CME_>tL>?ZnffK+^?rv0?3q={e#HOEKD&4{`snhJ>BjD~jf#tK==12^L%ipyu$U>h^z0y{HbEL(X(m!vc%E{Xa^E#Na z0Wgwm@buGD4aT)sdoX-UWxn;{HK1#lx`?+%b1D;ZuXg2uY4ag^hj1PPjim*UL-GUDx7VCr#IbsWe+E)<3nE&0BeVncD@X;A`viieE zRe9f-Bz5vu%Y{)jI4c^5O92nv+ZDf)qqshvMXNCn7F)C_=Z4tI7#6!5?+xqUM}-0pDL zLoiYo19P--SKn@UmLcj75iW;3Ea~>ffn5F-Vu4v149aVSW>;f1o8wTA%`3o03Nhry zg%p5S6G9+Zu8%hfGG63PNG=u1qfSC@3WVcm`1bKa@ z8mmZUD!ww_b9}{Md6-ST?=SYa*{yT)d@&+9}$*^T;0Os_aRlAc+6@&DQ zy)Dz#S>@K2qRQyE3IEeI3-1o$ua<<^Z-$pnad|~y_o2EjQbB$+6hC!mo^tf)y$D8D z+v-^Ecrux7P~wYJW0_~RKO+y=1qjIiTcdA?&qR++!z>mpjO`0fWpCpx z6{qy=Hmd-hDkO^~6)gEabf^rWS^nq()e4<^idMH!sK^7koG~)dbJskh9IgN$8rnAy zF9Gg4NNqe=QC4JDnmjhukg~@BDTqv{;;|Q6T4D#ut@8duq$}*V4nrYzf!+)Tb^OwA z>?T+NqhG8F!m(!&VMzo=l*3iLwCKYs-})I6rp8F6m)|z~uDjRme?6HZfG-jHfhejE z$aOe1l#q9_!<*v$Po?B=`u9M0R>gZQ{C(a66l9(%4}~EyLb(Vz|IxG=Y|H($8S*9? z0ttasCPH>mBRv(D^5P+J0oA1kkZNdf?MH}y%b1hXkRR#&B+dm(jv%E4O{YER(VA3)-eBVtWl{U$xK zQY2ECD4gEBl7&+KlO;Tfbn=HN1Tz6?r;2(J&s^N(OBhcta&(LHZ7PzeXtKZoj;K4! z6Gi#k5pdFre6_0+h-f*gaHd*)ohL~qBRIRNOmWi@J;5vaNjE57q<(WJ_zq2m$GutZ zq4160UuMYxhx9(r^G@G*U233>27x$CQs=V%5WmW1U)?y*-I7BimpbV@ zrU>nt%uGwC|I87rf@x`EG0Rebw{h#yVQ}uvac)B!AmQ?Nv$*YvEYZnsj_5?qL3rCyNAL^RBU(j(w$&Yep@9>G~pZ>l3;XpCmSyex0X9QDfW4k)L48 z(RHKpb}AA-9#aSmQO75@WZMB|WkKaP86ID&GfmFnlJTC|>0D0_PTiI*op3NA8)0Wc zdMr3LJyv8u5s-O3JKIp;!4$9z3Xu}!Xu&RPFs@c5)K)PO7%+V8qriFpkoQx&tA*t> zb7`0KjN~n%%J)&~iv@JnZ<~iaf6lLj4-f;{5y*xUPXV}pb_?RC&L&FccaE`NWnF;G z2>3Ersy_8Fm96=(oc8`TC0)O1R&;u4&C=|0|98kH0CoK)$5Ef^#`-L6lP^u1X5B&M zd9c3fLByQnk&068DN(q}iRKu(oTt^94j%!`U+g=G)=Och{ecr*or%$(H))#k19#UGXx}b^;eoFyDSnL)Z)yQB^vxvj z)*Pp#TRhJdY(mAoi#Qjnsy}{oIM4aQ|05P`;T0?Li$MG@pDb?C>9FhIU5u&T90NTO z$}_;M4q0^J)p{q}1A@|CM5EBD^+t!Sf2{IuA2A9EpVN%e%-|B+ch}eG3f=lzaye^$ zX2nBOpLtHAtNK#fi(MF=EPX{atFI3nqA+jQCq>kxjg;inUp+_13HgkpI?mJXSg$n? zvh92;r`!#m`FJh-^k|86MSEvp2CmU*8N8FIh23B1aa2OrgEr9}E| zK2q}$uNhu4uTHyaLpH>rZN4u%N3rAa&k(D1KHWc4$zzI+CeV*>{YyZ`qKhxVOYZ>P zCLSh4yOJ9R;`;Vs3X(bv90S9)9xr%nliEF89l2a-p4T;vP!75{> z1lXtK&!Nc8JkQ;Wf{GzTuRt^W9R#5W^1KUd!|TlwLq&qQ_8^6iphk zStI$LjynSc>~T$h;goR+CY!}mItt zHT_GuA$Zw_$nOV+d9xXxYVZ7qo`DK}85orJj!9YLbEh_}hg0m#v?tpp%6@aI-EuWm1|+jbO5B!g%<%(_jq-7)Uz6u(sftrnI%a4$qYtwPa zR831SRaF?%s&MT1LeIWM@#$*uUafwx7WISlc^dV#6O zU{hMKF83pVawaMRczd z*!B8pQ|%toOtb{a;|Ak5LRb=D-sq!y%Wp)xy4ytVL;Lq4vb`uI!Ka^pJS7Amya75< zkQ4q%4jdywVctA3Op%%GT~u_feKxBo35k{#snyNqHQ0EI51K+@v)J3Vtp4~tQV)O` z*4M#q^bvlVVjT`kLei6sT!@}kwPqZRoK8JtmnfEI&o;}w`(N=b|JK2}t%ZL`Y%UM_ z!#BA}lME-%P)?kcn1cp7rE~rWqM{TYgtzWSg0EgR{_5G;oa2G5&)YT^t+@};!+5Xd z<-Q~PSIvMcgDBUa++cgq5(8kB=zzA2C%_=SRq;Yw?V9=4OEiJ5atTb_)PG@*P6-Cm&P}%WYeyu76Ea< z>-t;O1@HS zPJ0lC>ZnsvU-9oYT5Bd9I^2yPlbUo({p#7bOZ$0oR76ir?gy#$Mjmp0l;@uu+9t#u z%tlPYaM5sK+43w>XnGZmRYx)HGdMDB3^LPvsfT&bsYI)-?$14R0Mm5PQUsB@Nhhy+ z&7{`A&TLx4drGSq!@2KXKDks+@U#V)Xlo69$o9K&8ljagYI9o)*HCdnJ8U{i+dK_h zcO+IR+0FwM=%uveA@hwvk`DHCi7K{07LQ19#IZB={B9w$BP!L_*bl%?TQ~Mh z+Lo99rng5#nhrJxh|}Q6Yr;rUe7p%Y{;(IR(zu~voFKSPa848zvGKvseZ zP?TMLR!N4?Wyp5{$Y9h&R`XSdqk+F7*}&zVe{#1UV!IUgdT!k{Btz3}wC? zx9lr!SFbIgZy#uVv-8qT$-x0Y^dAWYz81$tfjhS_YwKPLrb*sI?v)`)Y zj<{tH6(STzMD6(PnaoBSy*u2m|(AX30oQd$5fS6|`nFyd*5+K(#Tn!FM$ zsa+0P{l!ZTw)K7i&i_5{VC1-hCzR?_^w>9!4G<_+mY0|Lo7w}_ZBn)2>^{+j2J^t# z8J#2PbaRyjZR>S%%dv83zinDv<;}gMU3bractdzww2JQZgU2z8Tf&`vhfwq!;)(=a z!vmW(-cWM?-khBUD-dGuVMv|ATlz=900GKAL3QjTw4RzMrcK>5nA(x@QXN)D6=_a}|BG&hMR&3f=f z;7#SL9g3x5bKPeL;O0ScVBUV8Y+gP(s6$<6peVdXrjoEWXDkl45y6S@vbnO(Vtgx# zRv5GK&+hLs9YnSo8`2WKz64$;4?zYW0x7S19(#g_5wn- z3r=7IPUzu3LAgQoK))q^f--t>q=qyq<&u2~l6>N-gVMhz<9^Pgfb23}0zP|O`yp`= zBRQK%YstnSqDp-;%NrR7vBen&KrBX^e3wYgbmmjj_qF@_%8ISWL-*pOFUcCi77%%A z4;oV6&2kew-10~K>Jr@?8lLIFP9z4B6%UeUhj@XT<>x&71W4xFB*`zxi{)1Lk4<#? zo@{zC6Pr;lh^h=U>#pycIayrIkjYk9k=f#DSkacbgF<3FWIRB#9-_MnIRApw3B2)U z2S8gS!ppQ(!>AK%sVuHk%1vA9QU7G6k0`^X)2r_3c;$pnCd7gRZhs9@C&T16rREqP zXfOs-cP(+t)>t$6H!6ZHD)Kms;Lv^`8U{%_Ff=i3#3^RoP8C|pt|Sa}{D1_-D&AmN zLo0swsk$8~OaA@MWu$PY134Cgw;To3Q>WnUF~Adb-{Sx#5(ooy*jxc?d*|G90?>(y$9aKK|?=6Ne@x3seK_(pwQTcVn2 z)?P5sUoz_pFE{h8M29E3uInqPIsuea{fLh*!882jO3;@Ck?0Y#jfgA>2RjSnb<{WB>ZNVF#Sz+i#cs2;77yJEP3wph+Eauj>9p{q?W?cQZ8Ei2x7J;~)! zCKf0tqlwUvUICt1}6BC3%OS6MH^N9nxW?N31u?U~K z7I#suGu?zKTE1l*-J%{_luzv;)OVoo(jNHKSEO$$VT~v*7#KAsnOJL7ufro%l}97# zG=3|+@ke;eOUJLnK1sjjHW^QHB7%XtvGJS=bknx_je(mWvTk$kJ^9AM_+sQ+XN3vo z+L?RedfD&&Qs?zA5_=#LBqR;*rbP@U|EVLa4C+yF0qpI5Bsdp(Z=2fY2eFjK>FD=Z>VrZ=X5D&ed*PRum?Qblh%5Nqv611+7Yq5&I zuFVGn)jO_Y%p?t$IuLA&xZOrQuVoiyoHEwlr_quC>W+ z#rftc#%B%C`(L4+phNwIV(FHX-sUQVoBe6r$-@rEe}+F@>?B;e5a}7~Aei5i*WB)Q zWo-Ss_F)kJEmH;^_oQVw;OMD{xx~@^&0oWl_cklj)bJ_!HppWl1>0z>Ozp!!w3A$t zB{NLtK^$Adx9rADb^cGClo zL%Isi!q`B|6fE@mv6lS|l%(qmwxo>=?~;4`)`{T;RxEYm(^P%HHjO>Y>q{NW=~tHR zTwcz4sb!15pDW@;#<68}3H1G#KC@`>&FT6@Exo#@ zeufUz!;U9nA}lEhlB^=&GriLog=hKRjsASLP+X)6*C2cgn3SvECB*Ap1gYvsRJguG z?(MYbC$w@?{|HvaBcvm=Rh7%PWY${6ofo$7VQZM zv4EMiSWECYfY#ySXN(;idQ*V=sgKjZx?`%q8ejT>E)9^ZSd87nu=~=13}=~+GWaks zTBzZJ#%;6)6hcq6N0;T&>SV?J9ipMZgD(Cd=-+taK}Y*~$$1Hp(e{LdzCR*#LPqvo zo6b33_4)|ZRvFz8dPtyrq1MFRFSgb{wT1d}xg&rq5gT@a*OV!;i9MJ!Oo(Z1ke6eK{*F%2_l|e*BLwLv zF&Xftisk}jQNTGH`@26K7z&0xP>Aolr8(%Nj zH!Uy|v0^l*L>_?wqWjpAB7bwnj!(a%OmR~#3%-&L)`>KA(| z)LrZI*FZLJ9xZ9mOruuI_h#4wp~Faoj_pBywp1^UznMmtYQuGWj$dJ5hwN05hjmMvu=nM>r=qKioRP7Sn10RjdGL<$3QUG0kZO*dbx}C zJl>6)MF@pwIWisRIBa@preD0E#kV~{Lmd`shREkx&L1V(HdZgtrDxc;#WMFg{&BF+ z)Y2xA`mJ)T>xRLc3?r4rlu2UBW$3e7KSIZn8+i3;F2B;!nm^vhU0@@zLNVrKTc^-svPc_78<@ zaf3}?3qPhfiH{$sJV_z(M7BIk^IQTffk?(piVgGJpU(^G5%H7X1yHJgBJ-kE3o_WT zWUx!r!`QVdHk2v*%$~(8-;3f+LU=QYz+@)?*zXM(d(%vY#)6WAB8Lw70QLWMdOl-` ziD)X6zC%Lsm28xtQU)Q52IDq{%@E#-L`Y!C8{l!)s0TSBp9QR;e3>+6E~!cQR8E0< zM}6gVYMrRU?=pL!B^KTkyNAHzpT(;i8G)sOKFdOZ6r6k50RADi=-@!&p38Mn&&r5s zgyx=$Id4{CM@dR%hc%|W=90vBBdThKW>;~|(TU9ZxukT?jYey?|flVw_YX!|2OmJuaAdEX@F z$4b0xcFSv}L!^oxa>hmwEBzL=VnqnCx$W{X;AJbiD-lgG=;{`d`Tp7BTIBbw6N zE6#$N1(|!EXPaGyi{gM{n|PSLyiT%1)z2sPK2P$lF@U0@m%Bsd;g_=rrB3E2NwPlQUm#0ZMWt~FU^^h|VoQrgpm}|%&jLkV>q$;Gw7VdIQ zMNw>ger)l1Eb+uh?};*!du=L?pAoLsQddrUHwmvvDRwx(L^rGtd`Id+D;{&Q;7~He zn}IWNG9&gw&eWw<5TUEXRes6QeNqAGefB|N#{-zxDd=hiDy^uz6sVksghIdIX_HHJ zT1`5N)4@EPSNuiR(GLQjFF%L%H(sdUhP zEdVNMsMe`L2S{!03z||CtG@yh?N`ypP?8zAU#UHyEmh!DYMud3R^HRUnKLo!ewfSQ zvhm~OI{ChSCXfAZrPk?*;q|r&b7w}o3NR4^(S-gkl=jSK`+lwlCwL~*eU#6?esd_u z8_<|=R@b%RZbHX=a~k|LLHy@uy%bs6&<7wo1O5`}P9}-A7=ilJ4xeQUyr{fC-Ss*F zRLy4_GfZoNK7TTrV?9EKPRkil>7R%=7~o+S$FOKF6{*7Yr;$uc_7^URVTFdu6G4|^ z7G4y$L`1%J)PZ(lS>*88c%&p@YxVr%oRSxr{gyZu&70MBr^#%Fbf$VF56@f*4J2SB z5f(~Cm;4vnOc}9q!q1`-^MfV0XSa>)$_@F5-45iDXzKSv%w}13`0K2kVz%q(sHor4 zcax?#;qI-pFNgeg&eVtBkLS&Z4pgp$lPTYnhY2&s=4VLK!2woZa|t@xOfoI06^Hx4 z(s%T%v%<+;-DCmaBSRFjVz=alG+{sprqj36UbZz`@J>q_F^AG~)n z*3e%rP;v6UvtE`aIE8DP@fvti;t5ja#7HqOh~AQdXOrs{@Cax3@^%2J=>gAW zect2}TFc6iYMXO%L7y06#fK?@_ju5Y67si)T^H!7gHXwZep2L~cdf62KI=kEHoJ%)kb%o6b z`9n_c-CN9EF$GuGL%=#NQtoADElBl5LM4z*>i9kvOis`$5x8<_>ugH_S9Fw*2O|Ywb@&V|H4DAc zK^b2^qYyj&_)6ngH^4$n_*lA}W8czT22K6kyDy}9wC`Fd@n|PaYV1jo-^dqV2WZ$5 zg4I}!HDMg%Qyf_TR^xOCAi0z*NyfbRZX65E5u94ysrFB<z#?E<94K``sGsW1`3 z3O>^MaE`goK~exB+|vJ0NBC>7|NAMz(iv-d8B`B^;KAL)7&i=&GehSA5~vY2zf&Zi zq)d)TE|?zuPVf?svpL1FzzwM68L5r;Ua1MVM*?c=mQUpS@Farq1Ta;s!$ zHUuP7el2at?eBRGjltV?=JYji0yXKW)N8y6d=?xcnk66Ge_=c!i77?VmNxpuXi-ps z(%zHlPY_{qLDiZP%j)X?syIlZyJaQAl{Z$DJqANwf)y*Bk@zIo{@}Z(eXGwyb0^)E(!kN{a-tL4(6m|=QIzZNGObR zmr!hONjQ?d(nG0nxfV^x?N7Xmj+Gf|msag>EXQM{`OFIuu2wNhk$H@voDnxUU|K$Y zRxR!3Rk67S=DVj}PRI}=at`2wleQ0xt8&~?@ojjolE%j*{E^_M4GOBnMC0`n8Q?&b znX$f(w+4-S74xt030BtkoOmq5p3{D1FWf0)9__Yy?3YgYk*AWFNEswFMMHvq=AQMc zVe~t7h|*fodNoxdql`aH;l7m~7AhFd;@E&tqDG1U{GCF-G~3qlUuU|%76QkjRJn#=vIy4~ZpPI6LjiHB#+^YWH zCes$kde{-T#j4dS*?cav&@3xs>tnhZvro3MHpYRvt?%-jF@P!C50T#-zplv(guIZWbUD{gPT9k>PWY9AS)M29&ey zzHZWoLLT82A!bBn*loSAQcti}SejzTo0(;1ArG2!vY}0};oz4O&aul>SHeD?I8A68 zIF8JuWCiCAc_&md2_~U+9nXmKu-qyod!sbT=;br14^7Xb7{@@M!D&$~+*thn-PqU!;07vwpMQ zBDdht_ra~m941(iho~I(*8ziN`YP?;dVMF}9A)wbn7A3kDyn=~;dx0$F^=nSS@2vMKT-r zam-ch6ea4_mcat-^G(Ks(nP^{B~OA4shFPO1lxf;U5&N!aJpK+;2#+C$}zh1+x;;* z+u-wTVvP?H9=YZI8np>{l4W3K9q_Er`{BGz{9_6(*3Uk@|TH=nbQ%Hc;x;Y;|Q%F>n5 z++|8}6a;KhtlPjwT9&uK43*AXb=I#B#c*}z$#AHtaK8d_3=Lg)nDf(`L-eMDVt?1F z;HeeBayS>X!`2t&&JEQ?^Ziar|0SV%Wz806rj<>`;mjC_ZdI^O0zyvIB&9eNRL^Y1 zIdv)YbHKcOfRXhAQK&QgjwaDrKd*PzQL2zv6kuRuYV!cfs5HEAPOeGZD7g7$w1r9+ z;KY9lv(JPT?9eMjV5vAq&V7VGLY@|y(i-K8vzv>b>8*=|8i&>P7UU4)tSE7nb#y-p zNXYh>ufO=<)fq{yCAD~ww%4sp4tAIE(st4KI8rLBw{EwlJX*x;$qn)@QxJDB#sc-r z6ltttCY=DDXv2q&aS={cu(*+r+!m5H`Op$>sD;z{8ja9KOrC9}VShFv6JkdP(yQBy zW=}qT{^2O#O#JQd_7@cXecFahn2kU`mKmCLKN-Dlf#R2Bzuy_*!%~o5M+`PnO=&dn{L=H9UqVaZn22p`t zXN_N|L(7^|Na%zShC>oT4oblsiJuk*qcd@MjPx%-!h)BiUPX-^tweq0LhNxk2tD;T zE0k*J??aI!BN1Fz=3QHg;;eM1vmp>DLnDdH}+A=lC|FKt^!6@Me_@Z3uzT?iOpPTO-@HSW-kSEu!u z|7`MO{c*fUvttS?JlxW6hne)~*@qvY$y2mWtP{p7_Dq~er^-R6JbugSc_dKMm1$jA zM=B&PMNH)WAVbNH#qw9~yX)7av3t7@`xl;=zz_7N8QiuDGH|#GGv}XC{|(yHXU2iU zP5}yeuNsP!;%Omek6aQ-sOUs8aZHa(4C&#V6h%yn&$6dAWY?MOoKyLT(9***xD+C7 zxYSUW zz>TT^BCkxJtmW4Qm?k{Xlz|zd>nMguv``i`A{k$hPxL2p)~*)yD?O6PMuiZoL^F7D z-Io>^jv>S5|i2sNEgnr74Ll2S@A4)Z+j^kBz{VKNS zFsA3^&}8HvT9bzI)^(uFed5k9U(unXppTP&>(*(3+XgLPZki=<*ss8d@EhVd035vl zdr&Vq-H3F;7LqWP1k&=p0T=lk3tDg_|b2L!Hzre^)!@qFy zPA%Ws-_e^&ETXpiBlO|LOu8(*~N0h9#nGj`3clo~$!W7yS*z;+W(^5=-Vom(FLg$}&Vgk9o$&w-Wc?A#RKBc?A}BWlr} zM0^})Kf_%M?{I^QoOq}qwdoag8~tGu*`_bvcnxv7M27KZ$wy4$@cut8A1*jx?NOhG zaLA7+<$}o`lhXkrTuox8NHGz4Z3K?l6FHQ@!>935m9y9)4W^4X2K+~};~W1C@u6LL zUv?2O+jGWWT<-kU-(D(C;9{(mCUn&z(zOW;Y>X-D8z%i&xkHvX8y#??wBuaTjVkHC zlFWEAmDs|R728^lGt#ta{B4V!xkjD4q*lYn5+C6{jxYkmEp&K>ych6tER3d7n^j!3 zktOgp)XN{HB2(jo@K)(vwE9`Q)HV1kF?A=RSId7g88R$m=Qqrsc_W7HMZy?P7jNEA zX!0w*Uf`KAb?$BQ3Jpl|X>-z9Z1d3{Lnj(U6riZ`wp#1oEUw>>f! zI{D`IGIVv5AK02Krttq_M~)Xos>|H;4tOD{#LHPH&Cem`A#kbHsd23q#Y&9pg<~Ld zuU4sf#^XCGu+i$zP_=9)S#sHF+Zd_c{&Oqi>ZCxsJu%1w^uKc}M|P?PjJtH#$wd6p zHuU%i%Z*z7DhQWWY2r0EH-VlEY;37z_bCp7SetRsThXS~UXL<|>zEU-^bC_ja>pbo z;Q5M&vlg9Se7jQAE6OJew`v2oA+Z<@#TYM?310tn3h-Z_GMhex0~$9~e=?h7cjluk z0j;#WwXm*o*^cfjIDK2owvE)1;B1P)tw@(%v|cQqjQVMbzt`{YStHa{5wJqUNXx{T zfbv!RO~zN%R(C?bt}7D!9Mi}CaZw5)giwv<$oLC>mfiQUlBE}XQU1fMdstkZt@Lo6 z^~Mdz!565mX&AfL;^pkuZE1H`RO78dYII%m`7`>pbFb1jyg#ypxm9CPeSb8YIn41c z^CE*al5LawvnQNCdx_v^s_I0gFfPWtIXI5~!@2o6OKSLdHH9w{$<_-*I z%>Crc(vKjks$}WGB~gc1UVNzCD&mo3p?DQ3JvAP&hZixQGd$k3vjXpzFIZt#I2{GP zC?PG$&VKt3YETzt$ZO2r<`l%HL{7fZiO$^LUVZ+@h(up&e?N8@A5Bq@mj>`As@qpk zb@m;w$?|0kO_mmE-#2hwXEUoiJm5$?xQ2GJZgyxswCy(06!N0Ikh6s#p8Ad{b}zwb z{MgY)A>S6sUJa~=lcQ*m@g+FNB4CfF^hGLv7h`t|duW=RT$zwSmYEi3`4CC%a~@ys z=>MP!)xL9!&$C1itGHkmWr)wid7<1WIP2u73M+RL3i2;4o_nU^6-r@F&HNDd@qf>=Ezx)c&SLT6klJy$$`p#+_VvE_6c9XdMnf(7J9-Jvw1_~J- zp_&#^fu^<9>{u%4jyX8p{t|@?RQq1eCcbTSI8Glw&sU(5fM?f zxe2&%$Yw9Ix~GL!qvl3lub=%xX zsyv=yALO9nxPPIitZblD8$ij5`NAlL86izyXk~3`{^f@@eK+z4WS zS+)V`Yv#!Zj)))|PwWly#7BlHPYtfKNO!bryp8G07mEyj`2b9G0^{nV72yAb@h#4A zSmK$C%Cui{*cf@d170ZqP)=sk+UqPxL$lX9ltsO}mwT)J9WB~UW=g0jRG_E0W^H^` z7{Ez>CQf;VZ8kx1I45;u5~YP-$}o9&PWT+h|5JW^n!C>b5g^#P?e{a1mJp@=dW)rB zT6-?it}n?^x}8A{%%kDVvnI8K^!oGG^LVj%(X-XCcKh-5#~dmPC(8NKKQ@HL`6c2D z>n#+jGW*8rG^3@gha&n8zttC*J2?`fE21N1`7A%5qORVg6X^#~FUEzYh7gvMVQSc2l?;US@zrN^<McZa2({8<1#@hd8@|;HDG|Pvprh!pJ$WSuZvN|`^MPWg$ zwklBW_!;&mq4?=^P7P03`Q=)Cv1D`gw^3ie`^X(f&c+CPA1Vu|>ml^)C&9WAwBF;? zj*N{Y0szE12xlRGY~dH@hw=6t4MfIO1V;Kzdvk=>(aB}Lv2J{NFr>EHo%}j3JONLK zyVst9Npll`e@RZ9y+ElNqPb7LoQo|?*;wQ3BqV5|A$2Bc%4!1JKu2_YYrt+}GcFST)QS>`Pv zHLCtMo!}iqQ)P_rnO2Oe4jpxN_niu}D>3&U)-$SEA~X31w|{@p{GPzaE#z?vlRy&6 zA8-CYRFCq1O!fbS z#jKwig@6%7&71%CKdjUfV{wG8_jo37HO*npmq4%}lJG{bI20;RFvoQVM5x%*f!r+= zQ=;Bq%%T=VS`Vbd5*-;7`}OCv(K&e6&TMW{_@A)8dQ>hM%^Q=h+Rzkd<`<{jBqy)> z2*60o=98{M-Fvdt(22=jUXN=GEqTikimpeBLslXVA6~;}G!YL^7VrQll zyA@MkS3ymTwfKjZ23VPZiBuv(p}nu-%`|Nyn2#!xk(VyQXqKwN<@3eCo_cCq%6_|K ziC7bB)S`Kjy8L;h+)W$&o@c|LB+ml$>+YAhrFcR&Q^XMQ&pVn9JR$bhtcwka9mLnu zsz3c2xYHf=*T05)u#`DV)=>vNUJ(^5wq3{8eeoW?qq#QXKuf{~es*1PM-Jl&uXr>b zFJz!_vLug8gejeTTw@yk(SjCAL!pT-+3{V8Lw^0MEL_q|i+L-x)QRjMvpm11xXpcp${t=&P9kD7OA~w|36&Q$ zWW2{UDjxH*oT38{@M(D8Xs=3|GpLQ+aQ$l<%(DF|1}jnn9p)vQ_`{OFP2%`~1LyEL zYF(!trU-tJ;{6@Q_=g&=VKicCGo#Y)PO5N$J|9Y@p7hqm|mA0+W1YSbKDQ^KI~aZt_!GtHPq!?$wZ&c62BMh`=;?3@z8#ms-@sT`GsN3wx(p@CET zVI*;!*lRNZzoj*diKkxw4>@(NmqY$t1YEeW+26bFsnkKPiV7odKTn)(G}Ml6Hvje= z+%Y1r+lAhH_!Y$jeK?X0HQYpp+qy6*?(xWHQI$ZnSUyg zM0)*1*(?d)nTMuYPF$TK3{y^m8q=Bwx-VOe60096b(Mwtp_1Be>>ONkozWFojqw4sQ8e590R^60{=)ymXSM+8 z2wEi`FaKxc4UvD8xemP}kNX9ZO#S$Jj^PwBYl)4=OgmaF4g%Z5AsZ7ZTKcG5FUZMb z{RgjEM-QFzZKw+TFTVDQ3}gN;Ur!hIQ+cQtNw4mCs8j!!uY3N( z*O83QZ;xfm;6aniEZ=S2uH20~1l!b&`C^ar`-oKct-Sj{lOx@D(K8^j`|JUsfXvB6 znWwhX{er(@-l1ce`8aq;`CO;^PP1sfX2Idyr&<4{+@RJ6Aex_1ozF|V^5sJ6+$uX6 z^OR=OQ~_-If;6wPs@ITN_nO2^<6^)nK{3u(89iHZ?rxP?t-r@8tOaiLvJ~KSz_}@L3jSnzJ&vs1nQYM zRPvRLfTvYXf^U?Dh{FHV%3DWO^@MH1AOeyHLApCcT0jI3-60`Jcc>_cl+>m{5J^EK z1rZRC5EKPGbT^XH-Hiu0G4JvBz2Ecv{jK%=Gkey|zGv+VA@AHT#4e>r?WxJ2=-NSO;>KEB1ypGD->NYz( zh1;y7cs*0&Z+*Mp845V0kT7L(%MVJoire0eXgtg1r8niQ>{?CdE-h)75kXe1I4_>F%mU|yxyyJ0EsF}TWcUy1GOF}A@|0+o;%WqmW`<(pB^)>qx?u9)3 zB#VYc3D>7>Oo+#Ipz!N!3gb4u#CWB9{bknukxL4Az%Mb*uWt5l@;+0VPO z8yVA`kII_3h^>{j@PbuS=b=~MRa{M+tD=2#?6ET)cIVDt~;Z5F1&Q z9T_E-0|~_D1wNj>(ymBaMn`MM-$W7U37n9M)UTry>b;%IRT#8a$y~Ztc4F`MsDHT? zUA0=an(}7EMsMgwMNG*XmM`X<47=oS1^7&!7gQvN_fJV{l8v_!4B)$xP?rRDZ!-MN zYAK?=q*M~%&lwK-&~g3OpVbd$-#$(JG+B18Vpf0xl>6v(k2XEccsv4IQ>h2id$PR8 z^4wbDJJO4;ABtORzy+oA*%L}djMZPXS?l+3a1>B?w#J`sy=#t0FW99&EU39D$@=q8 ztPqRGg4`Ne*Uy*nF6k#PdI5Gd;Nlo6tmp-LIIf`G1)L6B`h`^rFCQO|0?!RkkpPbg zZy3T0CBplkw%i{?cJ|5;B?=)!PK;0Y=gl+p;?mfr@gmAis_`shixzL|7^;{l@57Z7 zyP;@=_QitdE@2wpR#Ms)p`G~}ail`2Cq*mX+nw`xm0z-J%;=7eFE7KG?~tjIvyepB z-bq2fiLUf@PbN7g$v9E_H3AVbN_Z_kmo@1m)GDl;{KhB6XWx^2-XEzyzq%PAXB;u; ztw=_VET~*~T9j0GUpv}6&UyM*PQA1im(+*Q@7=Zza~%*I>J7WNvm@c?=LpevGG7-GC?@Q#rBo;1zaq zX26$V69{d=@BTH{43XCs$}s3g{|BNeGEBSymRGQoaza?YnNR>H!V+9ou3(0&WYitu zhpkvQ*;1AmB?$7bUdW3aDQ^2H_*UMj8%p@J48}fBD1z_GXoI^O=u^c5(ll7E#96pV z0%aU2>2Q*kI|COH!Q&biAm8$-jh2nDL>n@H9KBX z)@&B;rDb&oJcOIqhN5MVC`9QwB=z}0Jy^n;IV&vUg%aRV;>qF-s|i2E!V!#rxx9jx z10~IYREZse6^!xu?pt3JyH&CuC3eZ_&T}9uq8( zS4RmV*H4)UKEh|gnm(P{b9PJ=gjSMS(69;P?0LDcE% zb6JDJfvLjf>*Z%I3|<+QhP+O}>u$mzVYiqEcYhx%^8i!^kZl6VVvyoQ{Esm?bVlSK zjz*Zia7++O`V?hYcD;fQrvJk9dru4>+Uq*ft#9E#+FrhE9}U4tPF^IhC9TIxSEv z_-A8FfMRE&2()?AJ&bXDKu0t=6CFJhr( zG)`eeIPxOHJm^gWy>qx*+9()V=#FVwT>Ey8AzsIv^nkJ!z&DH2y9fnOJV6(6jCqbj zBnJUtX1xP!E5<_6>;|mhWxe?=&e+r7(DROc@OBaUYVmJ|aWj?&p={+U1vQn_(Pm9H z#@mQmoO9fPZup?uhGBzExwZ@nHxX!MtYsW9zU*Rfbm@uw#CvIPL@J6g@{l6O*gs^- zNv*W;eEwn}IC0}vRN-i8-f;NWSlP2Sl8(KB*FA)R`SsySP=PMI=e=!iC7fj;vdAFg z9!5$|loguFkb~v|losx?p3{sb2dByrhxpTwYq5WtOruu~G;`reo0YdB|6Vz1r!Pq~SNy%QU0MxGrg6OklD^rnlTnEkw|3flP^CQA_}tKxk`u3>w{qj6w& z_q$0xc3)(FX~4s{r{YBe-u=Lm%Y^<& zl{4wZyX+zjw02>6Xg-InsjYT)h9-LjnT7})+m z6Jr-RK+8h!A+&akQ%=CUfc@)R^x(aOuY-|}FZeWhL6*6|gZLb=HVS^Az_I67J}hE{ z_z(=VfC>g+xd_Pqc?cyE+y(X$SzH1%AWIwVQA~2Vzz@9v49`sCkyArSveL`C%c75@ zpEY9z4*=~F{P6$%s}E3Z9qYiY*5?A=43Mu@z%O`Y)q<-hs3ZcG&42()fUVrcvY%F> zrnV7@py469vhL4&6gwKqI4;((4%)mSsj-2(Wc)f=pjGP%%d$cH1$X@xj=C0g4r!Y2 zVyUSP1!uv{F6a>8p<@uYlm3VPo(YUA0Oc7xPaYU75=n&KdwBt=ox_mue?H4J3l22= z;@7}V&YND~*9m+@ft)8liwsdiw!-L*j@X4e$Uy zF!Kdtdzj5Y*O03FSj5ER&27+vLAay4?AZPhFc%_EVGacBjVxbRZ5dlRvpX;D31qsPEW|s^ zzZFpgXlW%Mu?%RhoEppB$8z1lO zI7qMGM}#jlwIm>>xJUX~A|?k8$2POzyYNc`Rk_hZq0uy|Z z$$=zJ;P?46dZ84jDrbb{S>DOfZ*AV`C}p}$jSBg3DxN-;I?s3|a8y*s%RtU0_7J7I zh%LE3YtHySWhD<8bFVZ#rCF?EVflJYn51>}yN84PK4#J91Qqouw~5TY@RriO*o$h? z*$w9)=lJd3Q}JlH@r1>#-{cZP@Giqw|DOet!}22$-FqTZSj4>PGWatt)D88$1unEx zJQw0zRDSb|e~JxfPq`9yq^q`*>RfMM<~>+}DE{xV@BvAHNW|V6T#+Uq>3)qn1kPc9 zf%6KO#Q=fXaxKbGf7yJti+zBzq1lI;p=R)Q4nXCb#7x$XXPUC4z6$OPf$!c^J1s&D zpC=$UMQkPS3Ze&_<$`zb=TQW)nnU+3iOtZPF{OTwxu1$2HVwNPH=Pew1X}u&l|Nw~ z*X7c-_PE|289fw5CCavvgyFzWoDel-;ZKwhx$aQ4+WiD(2{@54vP1Smh;PWY!g3qZv`f25-WK_`IQ+*m_-M(OqOLbm%7QFA#irS!Z!sXYxn9ZbBQ8zTG(y zur+DaHZ>!Fv3Sg6nN?(~bIb8b+3Azgr|wTPLT>rIuE-g!ERyM*kim6Xc23-CWPWhs zn>43AEz)RvyDgeE#CtZeey++nhJ7}1qF80LqsoK*o-Yicj`iJSh`CI~|!pqsJq9z>=*;C?ocQe~}Nn8ZI zgDW*$L%zIe8BkmhAK}o11zP z0s1dbZIJTNy-Us%W5cYs6*Qk{_zQy(A-uOHijl2D{sooZuRcDVGuKZ=e|NwpZbpXT z_FvdJZaWP9-wKWiFZu<9RX#wEH1UU-7jl)`nPMv~EO89sCw91F8u0U3Exx``RuPAG z;Ese^_SapEI2OiasvG)@*LA=&|WgkoK%u@HkfobVSrd;RA~5`u+F0Zffn zcrG2!55iwIs3`~dp5q-9JBIj*3fYFO$aADj$zRDBE$+j9{0*^3hj{Lvp+^~e z#>E9+7R_G^B##PtL^bx|#PI&HwxhfvNh-(gjUeB#lOgS5n12bx-v$CtT(eChylC9L z_nCBlw+S_&60;TCI|vwx*VpHGW(jhwyZJ}S4z!kGe;bo7+Pd0k;x6F0*tO}<#!Ib} zP4{~tHZx_az3Cu^46>RCh6~qzo^80DE&zQQ|rp)=PYsgLU8hrA;3E^_B$E2BK8^)-W8cT^?9tr=SCm zsmSpeL1H|gJ;{wzr#37S%j7X2yZVYgcwJRE{#Np6U+c~Org(1QX4)Agu1nr0D}*g~ zcCD(N=a6m6WxWW?=L((=3Eqq~U;xou>NbGgXN7kP>h+)$T(#{bi#{zO^H&yA`9 z$?{{IQGGW~O<<^)L4o7vvD5R$^&rYDGk0^`WDbA+G)%158s3|))&0nl`cIeDZ$-ih zRc}ROQ9*dL7hjfxY%fJKmE!HI0|W-H<~KWkPiM#xepC0Y2*1Xx&~G1O$D2hqLubX_ zGTYjh6^(#a$%EAxb)Q|!bv4$DJ+T*eHGkgjNN0XX^PH&n4X{WCuvI_^*bi}$$N#^D zU^a*u2TTzMYXZMQD9P-LaFjsA9 zd0pvcV#SNIrLI)VKuq>4@=O#S7)1-+<*ZLT0trQQ3^!(})7F1GIgwxgXyTN5q&Snt z`olWNvB=y!c2osf^LRqihxgC>!dqoGXH18_ltb22-+V3mKJh-w(w7=+QK!4HW*aYA zk**_^C9MBl`ubP}W=B+n^AB)qXq1xIjx=iMC9Nu@$H7FfA zKICg3OX!BkB`ZGZKJdj%jt>K(RfyofKnFey#@E3|uivfPko4od^kuNHT|1))CZQT^ z-o7eZuli19LeKUcQ_{())k5EogE*)vc**EX6}$1XdjZAhGV^*v7767R{i~lYKUL%mr#p0^f)CWl z2a}aHM7YFRO3#$*#caQXwN=8_@2NMf9jxbCipu(K?5}|fwEU`(H-2Z zem>7ESexLjk7hLzu=;ISS5iDIcYIA!vG~6HXzD{t<4xA6e64rNdj*1bzCvcCJ%y?2 z5}Ksr@yJw3laEQw#Wo3#N#1_yY!NsO`~aD56W&YzL?OH%FHWCAtrIYbb)x=ZuYqWs zm6jOmJhouj&|Gs>mOYZY5UB94<7#SeBDYr`B~Ug{$*5e}Gg9q*ox|N5M@++|Q=qPG zXxo_9#!o$!|1nKow2M$RhX;~~3*656GaYb^H6f!t@R^47HIj~j#w>}aaRU60ZUjB= zaIv@bW@UWZp&~e6GV4|wzxu4lues{4(HXC^f4`!-A3l~Xew!#&i=jnx*IK)h4EXJ>pPV#b#atMuLAnm>C8kuMFEJI(TFzkRrt)Pc0 zeXyqRra!l|NUd*_Gc@5o0gFObC!S)P+s-P7Yc zOYutw?3kVb4)k8fQ=V#%7hJ)!f4v%R1WT~x2_7gaeSsFX z{4&KoD$^N1i0&{V>HW4{ZO#S1r^jU&SMi7bW<2CH>JwhnT#HC4fJ(IZv9o7ZG}+Of zOrr_4s=cIL&D>EbdHg#(B_iaAo0U^BbK|>TI5V%i9|e{EOu8r){^qE{-F4EqpXsLsh^V!8;ad{e-D;!GwLB9J-GdI?U9s( zqL750g{%n6>twj0P+#cJnn7x6bwh=3Cb4%f$uFdy0 zrd-!T`e#7sO{M1%8QhzX=#-Dt=S1^wcF%;D$QA_bC>^M~= zR+1kJy^>jso=NscyZFU7=6^-myKe5cBlob{O_Ukuh`m#ifGYU(hS0d zSjP(PNvPZ`>p=-Bt(VCmObxJyGOMM|MeWk#;q-;x!@#Y=jsWg(Da zQg{rPgZ*%W93nidCF*ANx1LF~sqTt3GLomb@(YPmug}xT6qjd)Sbk-OkkK$_DcPkh1pbsY$3S%6m~Vu9Us`UrMU<+%cGo zcQo%TPY~-jeu^MEgTk-`|58t=tOAtXoReQ8gp%|@nUC%c@8!Z50)U`AwdFwby)Unr zWV^mDpiB8ZmiE_M|J(V5I~|~c2{7YQR~)jmeE@w<&gR885yDO~C43SF<1=->w1-gX zxj_{R_vdVG&3-=~zm=i}79Q2NSBn%Y8xz-dMLX4;z0+HLYobjHbR^isuIjG(6L3OA z9EFI*irjHC@0P*M3c2s=j3G1o*^c-5gx%0v61j#!Z8eu!bLkSfCf2SJY*f3!nMu9b zG&K_+sz11XQI#Td&5P)CD1}On`$XXxA%ej_cMUZ0pnoBykyB|_nmjeL{GkfMJFLP5 zWAlosOM9a*fAwvre>OFrsw{2SG^95KZ)?lBmxj&hKRy`9({W0H?A)3}I= zIR2jB;`tq}_ujGZ_-eGy4>G8?@}cKP6*44&Q40zINxyFB0KO^Rq-5#7AKdYAttcbl`+Muz37Wof14 zBr?`WQh1ZMAW-fl!O~3b(D9?zSi*|D8pTbuB@dZ?@yubuyrom@T7~kuRQG6In`*3`lSX*rP)NhVm?_`lTChP zkG>{D+?86=IukT6_$tAA7{$Zocq~#wb*C74Uu{6GDOrvtP^Pf?E|mA(=~oL`z#Tq6 z{)3OP%O@{sskxf+zjG9LA75#-QxJXTPc8DAHaC)mJzGF%{VF_L&e4#nU9ZJX^Fd)h zSsa&hUn`y=d9M_bJ=)IBJ+zW$VBmnyh_IJ2T-)f1D**NiG&vTQL`|4=hvhkD^@V-K* z9SBcjQ`6roJ(Y?kU{d<{yX*7Z6No|II}^4nlxa%k50izpdQ{7KFXnYAPZq~i8;wbB z+FE_8lT?p3LARSzE&s6bFPGcck{)b0tVCQ}%h$RdqT>}=9e_t7X9p1_k-7FWJ<;pA z?~BdAfqzpFJ}%^(Ji-pWI=jHBUZx(g)8I9O;&@=}7<{@= { if (!spec.components) { return { @@ -93,7 +95,7 @@ export const generateTypeDefsAndResolversFromSwagger = ( const query = targetedOperationName const type = targetedOperationType - const source = targetedSwaggerName + const source = getSourceName(targetedSwaggerName, config) if ( targetedOperationType !== 'TYPE_NOT_FOUND' && @@ -124,6 +126,7 @@ export const generateTypeDefsAndResolversFromSwagger = ( root = { ...root, followLink: hateoasLink.href } } + // @TODO: Fix type checking for interger params if (paramsToSend.length) { paramsToSend.forEach((param, i) => { args[param] = root[param] || root[paramsFromLink[i]] || '' diff --git a/test/api/.gitignore b/test/api/.gitignore new file mode 100644 index 0000000..de4d1f0 --- /dev/null +++ b/test/api/.gitignore @@ -0,0 +1,2 @@ +dist +node_modules diff --git a/test/api/Dockerfile b/test/api/Dockerfile new file mode 100644 index 0000000..a66c25d --- /dev/null +++ b/test/api/Dockerfile @@ -0,0 +1,21 @@ + +# Use a base image with Node.js +FROM node:18-alpine + +# Set the working directory inside the container +WORKDIR /app + +# Copy package.json and package-lock.json to the working directory +COPY package*.json ./ + +# Install dependencies +RUN npm install + +# Copy the rest of the code to the working directory +COPY . . + +# Expose the port on which your API will run +#EXPOSE 5000 + +# Define the command to start your API +CMD ["npm", "run", "start"] diff --git a/test/api/api-docs/products.yml b/test/api/api-docs/products.yml new file mode 100644 index 0000000..1b8f5a4 --- /dev/null +++ b/test/api/api-docs/products.yml @@ -0,0 +1,82 @@ +openapi: 3.0.0 +info: + title: Products API + description: API to manage products with HATEOAS. + version: "1.0" +servers: + - url: http://localhost:3000/ +paths: + /products: + get: + operationId: getProducts + summary: List all products + responses: + "200": + description: A list of products with HATEOAS links + content: + application/json: + schema: + $ref: "#/components/schemas/Products" + /products/{id}: + get: + operationId: getProductById + summary: Get a product by ID + parameters: + - name: id + in: path + required: true + schema: + type: integer + description: The product ID + responses: + "200": + description: A list of products with HATEOAS links + content: + application/json: + schema: + $ref: "#/components/schemas/Product" +components: + schemas: + Product: + type: object + properties: + _links: + $ref: "#/components/schemas/ProductLinks" + id: + type: integer + name: + type: string + price: + type: number + supplierId: + type: integer + ProductLinks: + type: object + required: + - self + - supplier + properties: + self: + $ref: "#/components/schemas/Link" + supplier: + $ref: "#/components/schemas/Link" + x-links: + - rel: self + hrefPattern: "/products/{id}" + - rel: supplier + hrefPattern: "/suppliers/{id}" + + Products: + type: object + properties: + items: + type: array + items: + $ref: "#/components/schemas/Product" + Link: + type: object + required: + - href + properties: + href: + type: string diff --git a/test/api/api-docs/suppliers.yml b/test/api/api-docs/suppliers.yml new file mode 100644 index 0000000..7bfefa0 --- /dev/null +++ b/test/api/api-docs/suppliers.yml @@ -0,0 +1,60 @@ +openapi: 3.0.0 +info: + title: Suppliers API + description: API to manage suppliers with HATEOAS. + version: "1.0" +servers: + - url: http://localhost:3000/ +paths: + /suppliers: + get: + operationId: getSuppliers + summary: List all suppliers + responses: + "200": + description: A list of suppliers with HATEOAS links + content: + application/json: + schema: + $ref: "#/components/schemas/Suppliers" + /suppliers/{id}: + get: + operationId: getSupplierById + summary: Get a supplier by ID + parameters: + - name: id + in: path + required: true + schema: + type: integer + description: The supplier ID + responses: + "200": + description: A list of suppliers with HATEOAS links + content: + application/json: + schema: + $ref: "#/components/schemas/Supplier" +components: + schemas: + Supplier: + type: object + properties: + id: + type: integer + name: + type: string + Link: + type: object + required: + - href + properties: + href: + type: string + Suppliers: + type: object + properties: + items: + type: array + items: + $ref: "#/components/schemas/Supplier" diff --git a/test/api/models/products.ts b/test/api/models/products.ts new file mode 100644 index 0000000..745f6fa --- /dev/null +++ b/test/api/models/products.ts @@ -0,0 +1,39 @@ +type Product = { + id: number + name: string + price: number + supplierId: number + _links: { + supplier: { + href: string + } + self?: { + href: string + } + } +} + +function generateProducts() { + const _products: Product[] = [] + for (let i = 1; i <= 50; i++) { + const supplierId = Math.floor(Math.random() * 10) + 1 + const product: Product = { + id: i, + name: `Product ${i}`, + price: Math.floor(Math.random() * 100), + supplierId, + _links: { + self: { + href: `/products/${i}` + }, + supplier: { + href: `/suppliers/${supplierId}` + } + } + } + _products.push(product) + } + return _products +} + +export const products = generateProducts() diff --git a/test/api/models/suppliers.ts b/test/api/models/suppliers.ts new file mode 100644 index 0000000..938b255 --- /dev/null +++ b/test/api/models/suppliers.ts @@ -0,0 +1,18 @@ +export type Supplier = { + id: number + name: string +} + +const generateSuppliers = () => { + const _suppliers: Supplier[] = [] + for (let i = 1; i <= 10; i++) { + const supplier: Supplier = { + id: i, + name: `Supplier ${i}` + } + _suppliers.push(supplier) + } + return _suppliers +} + +export const suppliers = generateSuppliers() diff --git a/test/api/package-lock.json b/test/api/package-lock.json new file mode 100644 index 0000000..206267e --- /dev/null +++ b/test/api/package-lock.json @@ -0,0 +1,1102 @@ +{ + "name": "api", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "api", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@types/express": "^4.17.21", + "@types/node": "^20.12.7", + "express": "^4.19.2", + "swagger-ui-express": "^5.0.0", + "ts-node": "^10.9.2", + "typescript": "^5.4.5", + "yamljs": "^0.3.0" + }, + "devDependencies": { + "@types/swagger-ui-express": "^4.1.6", + "@types/yamljs": "^0.2.34" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==" + }, + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.0.tgz", + "integrity": "sha512-bGyep3JqPCRry1wq+O5n7oiBgGWmeIJXPjXXCo8EK0u8duZGSYar7cGqd3ML2JUsLGeB7fmc06KYo9fLGWqPvQ==", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==" + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" + }, + "node_modules/@types/node": { + "version": "20.12.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz", + "integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/qs": { + "version": "6.9.14", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.14.tgz", + "integrity": "sha512-5khscbd3SwWMhFqylJBLQ0zIu7c1K6Vz0uBIt915BI3zV0q1nfjRQD3RqSBcPaO6PHEF4ov/t9y89fSiyThlPA==" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==" + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, + "node_modules/@types/swagger-ui-express": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@types/swagger-ui-express/-/swagger-ui-express-4.1.6.tgz", + "integrity": "sha512-UVSiGYXa5IzdJJG3hrc86e8KdZWLYxyEsVoUI4iPXc7CO4VZ3AfNP8d/8+hrDRIqz+HAaSMtZSqAsF3Nq2X/Dg==", + "dev": true, + "dependencies": { + "@types/express": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/yamljs": { + "version": "0.2.34", + "resolved": "https://registry.npmjs.org/@types/yamljs/-/yamljs-0.2.34.tgz", + "integrity": "sha512-gJvfRlv9ErxdOv7ux7UsJVePtX54NAvQyd8ncoiFqK8G5aeHIfQfGH2fbruvjAQ9657HwAaO54waS+Dsk2QTUQ==", + "dev": true + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/body-parser": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.2", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.6.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/swagger-ui-dist": { + "version": "5.15.1", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.15.1.tgz", + "integrity": "sha512-Et/WY0NFdKj8sUBOyEx5P3VybsvGl7bo/y9JvgQ22TkH1a/KscQ0ZiQST2YeJ3cwCrIjYTbHbt165fkku0y1Ig==" + }, + "node_modules/swagger-ui-express": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-5.0.0.tgz", + "integrity": "sha512-tsU9tODVvhyfkNSvf03E6FAk+z+5cU3lXAzMy6Pv4av2Gt2xA0++fogwC4qo19XuFf6hdxevPuVCSKFuMHJhFA==", + "dependencies": { + "swagger-ui-dist": ">=5.0.0" + }, + "engines": { + "node": ">= v0.10.32" + }, + "peerDependencies": { + "express": ">=4.0.0 || >=5.0.0-beta" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typescript": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==" + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/yamljs": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/yamljs/-/yamljs-0.3.0.tgz", + "integrity": "sha512-C/FsVVhht4iPQYXOInoxUM/1ELSf9EsgKH34FofQOp6hwCPrW4vG4w5++TED3xRUo8gD7l0P1J1dLlDYzODsTQ==", + "dependencies": { + "argparse": "^1.0.7", + "glob": "^7.0.5" + }, + "bin": { + "json2yaml": "bin/json2yaml", + "yaml2json": "bin/yaml2json" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "engines": { + "node": ">=6" + } + } + } +} diff --git a/test/api/package.json b/test/api/package.json new file mode 100644 index 0000000..0aa13a7 --- /dev/null +++ b/test/api/package.json @@ -0,0 +1,28 @@ +{ + "name": "api", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "build:local:image": "docker build -t api-products .", + "build": "tsc", + "start": "ts-node server.ts", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "express": "^4.19.2", + "swagger-ui-express": "^5.0.0", + "ts-node": "^10.9.2", + "typescript": "^5.4.5", + "yamljs": "^0.3.0" + }, + "devDependencies": { + "@types/express": "^4.17.21", + "@types/node": "^20.12.7", + "@types/swagger-ui-express": "^4.1.6", + "@types/yamljs": "^0.2.34" + } +} diff --git a/test/api/server.ts b/test/api/server.ts new file mode 100644 index 0000000..047b36c --- /dev/null +++ b/test/api/server.ts @@ -0,0 +1,43 @@ +import express from 'express' +import { products } from './models/products' +import { suppliers } from './models/suppliers' +import swaggerUi from 'swagger-ui-express' +import Yaml from 'yamljs' + +const app = express() +const port = 5000 +app.listen(port, () => { + console.log(`🚀 Server is running at http://localhost:${port}`) +}) + +app.use(express.json()) + +// Serve the swagger.yml file +app.use('/api-docs/:file', (req, res) => { + const file = req.params.file + res.json(Yaml.load(`./api-docs/${file}`)) +}) + +// Redirect to the swagger UI +app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(Yaml.load('./api-docs/swagger.yml'))) + +// Serve the products +app.get('/products', (req, res) => { + res.json({ items: products }) +}) + +// Serve single product +app.get('/products/:id', (req, res) => { + const id = parseInt(req.params.id) + res.json({ ...products.find((product) => product.id === id) }) +}) +// Serve the suppliers +app.get('/suppliers', (req, res) => { + res.json({ items: suppliers }) +}) + +// Serve a single supplier +app.get('/suppliers/:id', (req, res) => { + const id = parseInt(req.params.id) + res.json({ ...suppliers.find((supplier) => supplier.id === id) }) +}) diff --git a/test/api/tsconfig.json b/test/api/tsconfig.json new file mode 100644 index 0000000..8996a90 --- /dev/null +++ b/test/api/tsconfig.json @@ -0,0 +1,109 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "ESNext" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + "module": "commonjs" /* Specify what module code is generated. */, + "rootDir": "./" /* Specify the root folder within your source files. */, + // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ + // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ + // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ + // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + "outDir": "./dist" /* Specify an output folder for all emitted files. */, + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, + + /* Type Checking */ + "strict": true /* Enable all strict type-checking options. */, + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + } +} diff --git a/test/integration/config.yaml b/test/integration/config.yaml new file mode 100644 index 0000000..78caaa0 --- /dev/null +++ b/test/integration/config.yaml @@ -0,0 +1,27 @@ +sources: + - name: Products + handler: + openapi: + source: http://api-products:5000/api-docs/products.yml + endpoint: http://api-products:5000 + - name: Suppliers + handler: + openapi: + source: http://api-products:5000/api-docs/suppliers.yml + endpoint: http://api-products:5000 + +additionalEnvelopPlugins: "./plugins" +additionalTransforms: [{ "./transforms/index.ts": {} }] +additionalTypeDefs: | + """ + This directive is used to convert the result to uppercase. + """ + directive @lower on FIELD +skipSSLValidation: true +serve: + hostname: 0.0.0.0 + port: 3000 + cors: + origin: "*" + playground: true + playgroundTitle: Console GraphQL diff --git a/test/integration/docker-compose.yaml b/test/integration/docker-compose.yaml new file mode 100644 index 0000000..b823ce3 --- /dev/null +++ b/test/integration/docker-compose.yaml @@ -0,0 +1,25 @@ +services: + # + api-products: + build: + context: ../api/ + dockerfile: ../api/Dockerfile + extra_hosts: + - "api-products:172.20.0.2" + ports: + - 45537:5000 + restart: unless-stopped + # + graphql-mesh: + depends_on: + - api-products + image: ${IMAGE_TAG:-bouyguestelecom/graphql-mesh:latest} + environment: + - DEBUG=1 + ports: + - 45538:3000 + volumes: + - ./transforms:/app/transforms + - ./plugins:/app/plugins + - ./config.yaml:/app/config.yaml + restart: unless-stopped diff --git a/test/integration/plugins/index.ts b/test/integration/plugins/index.ts new file mode 100644 index 0000000..829007d --- /dev/null +++ b/test/integration/plugins/index.ts @@ -0,0 +1,45 @@ +import { type Plugin } from '@envelop/core'; + +/** + * This plugin auto-populates the Server-Timing header to the response. + * + * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Server-Timing + */ +export default () => { + return { + onFetch({ context, info }) { + if (!info) { + return; + } + + const start = Date.now(); + + return () => { + const duration = Date.now() - start; + const timing = `${info.fieldName};desc="${info.fieldName} (${info.sourceName})";dur=${duration}`; + if (!context.timings) { + context.timings = []; + } + context.timings.push(timing); + }; + }, + + onExecute() { + return { + onExecuteDone({ args }) { + // @ts-ignore + const { timings } = args.contextValue; + if (!timings) { + return; + } + + // @ts-ignore + args.contextValue.res.setHeader?.( + 'Server-Timing', + timings.join(', ') + ); + }, + }; + }, + }; +}; diff --git a/test/integration/tests/.gitignore b/test/integration/tests/.gitignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/test/integration/tests/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/test/integration/tests/get-products.test.ts b/test/integration/tests/get-products.test.ts new file mode 100644 index 0000000..44cb64e --- /dev/null +++ b/test/integration/tests/get-products.test.ts @@ -0,0 +1,73 @@ +import { test, expect } from 'vitest' +import axios from 'axios' + +const url = 'http://127.0.0.1:45538/graphql' +const headers = { 'Content-Type': 'application/json' } + +/* Get all products */ +const getAllProductsQuery = /* GraphQL */ ` + query getAllProducts { + getProducts { + items { + name + id + price + supplierId + } + } + } +` + +test('getAllProducts query', async () => { + const response = await axios.post(url, { query: getAllProductsQuery }, { headers }) + + const result = response.data + expect(response.status).toBe(200) + expect(result).toHaveProperty('data') + expect(result.data).toHaveProperty('getProducts') + expect(result.data.getProducts.items.length).toEqual(50) +}) + +/* SPL filter */ +const getTenFirstProductsQuery = /* GraphQL */ ` + query getTenFirstProducts { + getProducts { + items @SPL(query: "id <= 10") { + name + id + price + supplierId + } + } + } +` + +test('getTenFirstProducts with SPL filter query', async () => { + const response = await axios.post(url, { query: getTenFirstProductsQuery }, { headers }) + + const result = response.data + expect(response.status).toBe(200) + expect(result.data.getProducts.items.length).toEqual(10) +}) + +/* Hateoas link */ +const getProductAndSupplierInfo = /* GraphQL */ ` + query getProductAndSupplierInfo { + getProductById(id: 1) { + id + name + price + supplier { + name + id + } + } + } +` + +test('Follow hateoas link to get Suppier info', async () => { + const response = await axios.post(url, { query: getProductAndSupplierInfo }, { headers }) + + const result = response.data + expect(result.data.getProductById.supplier.name).contains('Supplier') +}) diff --git a/test/integration/tests/package-lock.json b/test/integration/tests/package-lock.json new file mode 100644 index 0000000..3957168 --- /dev/null +++ b/test/integration/tests/package-lock.json @@ -0,0 +1,1515 @@ +{ + "name": "tests", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "tests", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "axios": "^1.6.8", + "vitest": "^1.5.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", + "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", + "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", + "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", + "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", + "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", + "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", + "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", + "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", + "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", + "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", + "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", + "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", + "cpu": [ + "loong64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", + "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", + "cpu": [ + "mips64el" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", + "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", + "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", + "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", + "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", + "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", + "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", + "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", + "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", + "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", + "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.2.tgz", + "integrity": "sha512-ahxSgCkAEk+P/AVO0vYr7DxOD3CwAQrT0Go9BJyGQ9Ef0QxVOfjDZMiF4Y2s3mLyPrjonchIMH/tbWHucJMykQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.2.tgz", + "integrity": "sha512-lAarIdxZWbFSHFSDao9+I/F5jDaKyCqAPMq5HqnfpBw8dKDiCaaqM0lq5h1pQTLeIqueeay4PieGR5jGZMWprw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.2.tgz", + "integrity": "sha512-SWsr8zEUk82KSqquIMgZEg2GE5mCSfr9sE/thDROkX6pb3QQWPp8Vw8zOq2GyxZ2t0XoSIUlvHDkrf5Gmf7x3Q==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.2.tgz", + "integrity": "sha512-o/HAIrQq0jIxJAhgtIvV5FWviYK4WB0WwV91SLUnsliw1lSAoLsmgEEgRWzDguAFeUEUUoIWXiJrPqU7vGiVkA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.2.tgz", + "integrity": "sha512-nwlJ65UY9eGq91cBi6VyDfArUJSKOYt5dJQBq8xyLhvS23qO+4Nr/RreibFHjP6t+5ap2ohZrUJcHv5zk5ju/g==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.2.tgz", + "integrity": "sha512-Pg5TxxO2IVlMj79+c/9G0LREC9SY3HM+pfAwX7zj5/cAuwrbfj2Wv9JbMHIdPCfQpYsI4g9mE+2Bw/3aeSs2rQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.2.tgz", + "integrity": "sha512-cAOTjGNm84gc6tS02D1EXtG7tDRsVSDTBVXOLbj31DkwfZwgTPYZ6aafSU7rD/4R2a34JOwlF9fQayuTSkoclA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.2.tgz", + "integrity": "sha512-4RyT6v1kXb7C0fn6zV33rvaX05P0zHoNzaXI/5oFHklfKm602j+N4mn2YvoezQViRLPnxP8M1NaY4s/5kXO5cw==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.2.tgz", + "integrity": "sha512-KNUH6jC/vRGAKSorySTyc/yRYlCwN/5pnMjXylfBniwtJx5O7X17KG/0efj8XM3TZU7raYRXJFFReOzNmL1n1w==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.2.tgz", + "integrity": "sha512-xPV4y73IBEXToNPa3h5lbgXOi/v0NcvKxU0xejiFw6DtIYQqOTMhZ2DN18/HrrP0PmiL3rGtRG9gz1QE8vFKXQ==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.2.tgz", + "integrity": "sha512-QBhtr07iFGmF9egrPOWyO5wciwgtzKkYPNLVCFZTmr4TWmY0oY2Dm/bmhHjKRwZoGiaKdNcKhFtUMBKvlchH+Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.2.tgz", + "integrity": "sha512-8zfsQRQGH23O6qazZSFY5jP5gt4cFvRuKTpuBsC1ZnSWxV8ZKQpPqOZIUtdfMOugCcBvFGRa1pDC/tkf19EgBw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.2.tgz", + "integrity": "sha512-H4s8UjgkPnlChl6JF5empNvFHp77Jx+Wfy2EtmYPe9G22XV+PMuCinZVHurNe8ggtwoaohxARJZbaH/3xjB/FA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.2.tgz", + "integrity": "sha512-djqpAjm/i8erWYF0K6UY4kRO3X5+T4TypIqw60Q8MTqSBaQNpNXDhxdjpZ3ikgb+wn99svA7jxcXpiyg9MUsdw==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.2.tgz", + "integrity": "sha512-teAqzLT0yTYZa8ZP7zhFKEx4cotS8Tkk5XiqNMJhD4CpaWB1BHARE4Qy+RzwnXvSAYv+Q3jAqCVBS+PS+Yee8Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==" + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" + }, + "node_modules/@vitest/expect": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.5.0.tgz", + "integrity": "sha512-0pzuCI6KYi2SIC3LQezmxujU9RK/vwC1U9R0rLuGlNGcOuDWxqWKu6nUdFsX9tH1WU0SXtAxToOsEjeUn1s3hA==", + "dependencies": { + "@vitest/spy": "1.5.0", + "@vitest/utils": "1.5.0", + "chai": "^4.3.10" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.5.0.tgz", + "integrity": "sha512-7HWwdxXP5yDoe7DTpbif9l6ZmDwCzcSIK38kTSIt6CFEpMjX4EpCgT6wUmS0xTXqMI6E/ONmfgRKmaujpabjZQ==", + "dependencies": { + "@vitest/utils": "1.5.0", + "p-limit": "^5.0.0", + "pathe": "^1.1.1" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.5.0.tgz", + "integrity": "sha512-qpv3fSEuNrhAO3FpH6YYRdaECnnRjg9VxbhdtPwPRnzSfHVXnNzzrpX4cJxqiwgRMo7uRMWDFBlsBq4Cr+rO3A==", + "dependencies": { + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.5.0.tgz", + "integrity": "sha512-vu6vi6ew5N5MMHJjD5PoakMRKYdmIrNJmyfkhRpQt5d9Ewhw9nZ5Aqynbi3N61bvk9UvZ5UysMT6ayIrZ8GA9w==", + "dependencies": { + "tinyspy": "^2.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.5.0.tgz", + "integrity": "sha512-BDU0GNL8MWkRkSRdNFvCUCAVOeHaUlVJ9Tx0TYBZyXaaOTmGtUFObzchCivIBrIwKzvZA7A9sCejVhXM2aY98A==", + "dependencies": { + "diff-sequences": "^29.6.3", + "estree-walker": "^3.0.3", + "loupe": "^2.3.7", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "engines": { + "node": "*" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", + "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/chai": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz", + "integrity": "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==", + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.0.8" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-eql": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", + "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/esbuild": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", + "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.20.2", + "@esbuild/android-arm": "0.20.2", + "@esbuild/android-arm64": "0.20.2", + "@esbuild/android-x64": "0.20.2", + "@esbuild/darwin-arm64": "0.20.2", + "@esbuild/darwin-x64": "0.20.2", + "@esbuild/freebsd-arm64": "0.20.2", + "@esbuild/freebsd-x64": "0.20.2", + "@esbuild/linux-arm": "0.20.2", + "@esbuild/linux-arm64": "0.20.2", + "@esbuild/linux-ia32": "0.20.2", + "@esbuild/linux-loong64": "0.20.2", + "@esbuild/linux-mips64el": "0.20.2", + "@esbuild/linux-ppc64": "0.20.2", + "@esbuild/linux-riscv64": "0.20.2", + "@esbuild/linux-s390x": "0.20.2", + "@esbuild/linux-x64": "0.20.2", + "@esbuild/netbsd-x64": "0.20.2", + "@esbuild/openbsd-x64": "0.20.2", + "@esbuild/sunos-x64": "0.20.2", + "@esbuild/win32-arm64": "0.20.2", + "@esbuild/win32-ia32": "0.20.2", + "@esbuild/win32-x64": "0.20.2" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "engines": { + "node": "*" + } + }, + "node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/js-tokens": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.0.tgz", + "integrity": "sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==" + }, + "node_modules/jsonc-parser": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", + "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==" + }, + "node_modules/local-pkg": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.0.tgz", + "integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==", + "dependencies": { + "mlly": "^1.4.2", + "pkg-types": "^1.0.3" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dependencies": { + "get-func-name": "^2.0.1" + } + }, + "node_modules/magic-string": { + "version": "0.30.9", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.9.tgz", + "integrity": "sha512-S1+hd+dIrC8EZqKyT9DstTH/0Z+f76kmmvZnkfQVmOpDEF9iVgdYif3Q/pIWHmCoo59bQVGW0kVL3e2nl+9+Sw==", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mlly": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.6.1.tgz", + "integrity": "sha512-vLgaHvaeunuOXHSmEbZ9izxPx3USsk8KCQ8iC+aTlp5sKRSoZvwhHh5L9VbKSaVC6sJDqbyohIS76E2VmHIPAA==", + "dependencies": { + "acorn": "^8.11.3", + "pathe": "^1.1.2", + "pkg-types": "^1.0.3", + "ufo": "^1.3.2" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz", + "integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==", + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==" + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "engines": { + "node": "*" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "node_modules/pkg-types": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.3.tgz", + "integrity": "sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==", + "dependencies": { + "jsonc-parser": "^3.2.0", + "mlly": "^1.2.0", + "pathe": "^1.1.0" + } + }, + "node_modules/postcss": { + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, + "node_modules/rollup": { + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.2.tgz", + "integrity": "sha512-WkeoTWvuBoFjFAhsEOHKRoZ3r9GfTyhh7Vff1zwebEFLEFjT1lG3784xEgKiTa7E+e70vsC81roVL2MP4tgEEQ==", + "dependencies": { + "@types/estree": "1.0.5" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.14.2", + "@rollup/rollup-android-arm64": "4.14.2", + "@rollup/rollup-darwin-arm64": "4.14.2", + "@rollup/rollup-darwin-x64": "4.14.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.14.2", + "@rollup/rollup-linux-arm64-gnu": "4.14.2", + "@rollup/rollup-linux-arm64-musl": "4.14.2", + "@rollup/rollup-linux-powerpc64le-gnu": "4.14.2", + "@rollup/rollup-linux-riscv64-gnu": "4.14.2", + "@rollup/rollup-linux-s390x-gnu": "4.14.2", + "@rollup/rollup-linux-x64-gnu": "4.14.2", + "@rollup/rollup-linux-x64-musl": "4.14.2", + "@rollup/rollup-win32-arm64-msvc": "4.14.2", + "@rollup/rollup-win32-ia32-msvc": "4.14.2", + "@rollup/rollup-win32-x64-msvc": "4.14.2", + "fsevents": "~2.3.2" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "engines": { + "node": ">=8" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==" + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/source-map-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==" + }, + "node_modules/std-env": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz", + "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==" + }, + "node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.1.0.tgz", + "integrity": "sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==", + "dependencies": { + "js-tokens": "^9.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/tinybench": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.7.0.tgz", + "integrity": "sha512-Qgayeb106x2o4hNzNjsZEfFziw8IbKqtbXBjVh7VIZfBxfD5M4gWtpyx5+YTae2gJ6Y6Dz/KLepiv16RFeQWNA==" + }, + "node_modules/tinypool": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.3.tgz", + "integrity": "sha512-Ud7uepAklqRH1bvwy22ynrliC7Dljz7Tm8M/0RBUW+YRa4YHhZ6e4PpgE+fu1zr/WqB1kbeuVrdfeuyIBpy4tw==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz", + "integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "engines": { + "node": ">=4" + } + }, + "node_modules/ufo": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.3.tgz", + "integrity": "sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==" + }, + "node_modules/vite": { + "version": "5.2.8", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.8.tgz", + "integrity": "sha512-OyZR+c1CE8yeHw5V5t59aXsUPPVTHMDjEZz8MgguLL/Q7NblxhZUlTu9xSPqlsUO/y+X7dlU05jdhvyycD55DA==", + "dependencies": { + "esbuild": "^0.20.1", + "postcss": "^8.4.38", + "rollup": "^4.13.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.5.0.tgz", + "integrity": "sha512-tV8h6gMj6vPzVCa7l+VGq9lwoJjW8Y79vst8QZZGiuRAfijU+EEWuc0kFpmndQrWhMMhet1jdSF+40KSZUqIIw==", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.4", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.5.0.tgz", + "integrity": "sha512-d8UKgR0m2kjdxDWX6911uwxout6GHS0XaGH1cksSIVVG8kRlE7G7aBw7myKQCvDI5dT4j7ZMa+l706BIORMDLw==", + "dependencies": { + "@vitest/expect": "1.5.0", + "@vitest/runner": "1.5.0", + "@vitest/snapshot": "1.5.0", + "@vitest/spy": "1.5.0", + "@vitest/utils": "1.5.0", + "acorn-walk": "^8.3.2", + "chai": "^4.3.10", + "debug": "^4.3.4", + "execa": "^8.0.1", + "local-pkg": "^0.5.0", + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "std-env": "^3.5.0", + "strip-literal": "^2.0.0", + "tinybench": "^2.5.1", + "tinypool": "^0.8.3", + "vite": "^5.0.0", + "vite-node": "1.5.0", + "why-is-node-running": "^2.2.2" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "1.5.0", + "@vitest/ui": "1.5.0", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/why-is-node-running": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz", + "integrity": "sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/test/integration/tests/package.json b/test/integration/tests/package.json new file mode 100644 index 0000000..702a1e7 --- /dev/null +++ b/test/integration/tests/package.json @@ -0,0 +1,16 @@ +{ + "name": "tests", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "vitest" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "axios": "^1.6.8", + "vitest": "^1.5.0" + } +} diff --git a/test/integration/tests/vite.config.ts b/test/integration/tests/vite.config.ts new file mode 100644 index 0000000..0324084 --- /dev/null +++ b/test/integration/tests/vite.config.ts @@ -0,0 +1,4 @@ +/// +// Configure Vitest (https://vitest.dev/config/) +import { defineConfig } from 'vite' +export default defineConfig({}) diff --git a/test/integration/transforms/index.ts b/test/integration/transforms/index.ts new file mode 100644 index 0000000..a4acf47 --- /dev/null +++ b/test/integration/transforms/index.ts @@ -0,0 +1,51 @@ +import { defaultFieldResolver, GraphQLSchema } from 'graphql' +import { MeshTransform } from '@graphql-mesh/types' +import { MapperKind, mapSchema } from '@graphql-tools/utils' + +export default class LowerDirectiveTransform implements MeshTransform { + noWrap = true + + transformSchema(schema: GraphQLSchema) { + return mapSchema(schema, { + [MapperKind.OBJECT_FIELD]: (fieldConfig) => { + const originalResolver = + fieldConfig.resolve != null ? fieldConfig.resolve : defaultFieldResolver + + const resolver = async (next: any, _source: any, _args: any, context: any, info: any) => { + const { directives } = info.fieldNodes[0] + const upperDirective = directives.find( + (directive: { name: { value: string } }) => directive.name.value === 'lower' + ) + + let result = await next(context) + + if (upperDirective) { + if (typeof result === 'string') { + result = result.toLowerCase() + } + } + + return result + } + + fieldConfig.resolve = (source, originalArgs, context, info) => { + return resolver( + (context: unknown) => + new Promise((resolve, reject) => { + const result = originalResolver(source, originalArgs, context, info) + if (result instanceof Error) { + reject(result) + } + resolve(result) + }), + source, + originalArgs, + context, + info + ) + } + return fieldConfig + } + }) + } +} From b5c3a684097ceee3eeddc5ba3d4a197d0e1c5d61 Mon Sep 17 00:00:00 2001 From: Mbaye THIAM Date: Mon, 15 Apr 2024 15:28:05 +0200 Subject: [PATCH 4/8] =?UTF-8?q?=F0=9F=91=B7=20run=20tests=20tasks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/integration-tests.yml | 17 +++++++++++++++++ test/api/Dockerfile | 3 ++- test/api/server.ts | 11 ++++++++++- test/integration/docker-compose.yaml | 12 ++++++++++-- test/integration/tests/get-products.test.ts | 2 +- 5 files changed, 40 insertions(+), 5 deletions(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index c354fd7..6a7f5c4 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -9,6 +9,9 @@ on: jobs: integration-tests: runs-on: ubuntu-latest + strategy: + matrix: + node-version: [18.x] services: registry: image: registry:2 @@ -41,3 +44,17 @@ jobs: - name: Setup services for testing purpose run: export IMAGE_TAG=localhost:5000/test/graphql-mesh:latest && cd ./test/integration && docker compose up -d + + - name: Inspect network + run: docker network inspect integration_default + + - name: Set up Node.js + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + + - name: Install dependencies + run: cd ./test/integration/tests && npm install + + - name: Run tests + run: cd ./test/integration/tests && npm test diff --git a/test/api/Dockerfile b/test/api/Dockerfile index a66c25d..ddc2416 100644 --- a/test/api/Dockerfile +++ b/test/api/Dockerfile @@ -2,6 +2,8 @@ # Use a base image with Node.js FROM node:18-alpine +RUN apk add curl + # Set the working directory inside the container WORKDIR /app @@ -10,7 +12,6 @@ COPY package*.json ./ # Install dependencies RUN npm install - # Copy the rest of the code to the working directory COPY . . diff --git a/test/api/server.ts b/test/api/server.ts index 047b36c..339d360 100644 --- a/test/api/server.ts +++ b/test/api/server.ts @@ -19,7 +19,16 @@ app.use('/api-docs/:file', (req, res) => { }) // Redirect to the swagger UI -app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(Yaml.load('./api-docs/swagger.yml'))) +app.use( + '/api-products-docs', + swaggerUi.serve, + swaggerUi.setup(Yaml.load('./api-docs/products.yml')) +) +app.use( + '/api-suppliers-docs', + swaggerUi.serve, + swaggerUi.setup(Yaml.load('./api-docs/suppliers.yml')) +) // Serve the products app.get('/products', (req, res) => { diff --git a/test/integration/docker-compose.yaml b/test/integration/docker-compose.yaml index b823ce3..28a6a80 100644 --- a/test/integration/docker-compose.yaml +++ b/test/integration/docker-compose.yaml @@ -4,14 +4,22 @@ services: build: context: ../api/ dockerfile: ../api/Dockerfile - extra_hosts: - - "api-products:172.20.0.2" + #image: api-products ports: - 45537:5000 + healthcheck: + test: curl --fail http://localhost:5000/products || exit 1 + interval: 10s + timeout: 10s + retries: 3 restart: unless-stopped + # graphql-mesh: depends_on: + api-products: + condition: service_healthy + links: - api-products image: ${IMAGE_TAG:-bouyguestelecom/graphql-mesh:latest} environment: diff --git a/test/integration/tests/get-products.test.ts b/test/integration/tests/get-products.test.ts index 44cb64e..26a03c3 100644 --- a/test/integration/tests/get-products.test.ts +++ b/test/integration/tests/get-products.test.ts @@ -1,7 +1,7 @@ import { test, expect } from 'vitest' import axios from 'axios' -const url = 'http://127.0.0.1:45538/graphql' +const url = 'http://0.0.0.0:45538/graphql' const headers = { 'Content-Type': 'application/json' } /* Get all products */ From 2835091b87d479ca0d4a895b2d4b3d2225020e32 Mon Sep 17 00:00:00 2001 From: Mbaye THIAM Date: Tue, 16 Apr 2024 13:56:23 +0200 Subject: [PATCH 5/8] =?UTF-8?q?=E2=9C=85=20add=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/integration-tests.yml | 3 - packages/graphql-mesh/utils/swaggers/index.ts | 4 +- test/api/models/products.ts | 2 +- .../tests/cases/directive-spl.test.ts | 25 ++++++++ .../graphql-mesh.test.ts} | 57 +++++++++++-------- .../inject-additionnal-transforms.test.ts | 22 +++++++ test/integration/tests/cases/plugins.test.ts | 20 +++++++ test/integration/tests/config.ts | 3 + 8 files changed, 105 insertions(+), 31 deletions(-) create mode 100644 test/integration/tests/cases/directive-spl.test.ts rename test/integration/tests/{get-products.test.ts => cases/graphql-mesh.test.ts} (64%) create mode 100644 test/integration/tests/cases/inject-additionnal-transforms.test.ts create mode 100644 test/integration/tests/cases/plugins.test.ts create mode 100644 test/integration/tests/config.ts diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 6a7f5c4..0826789 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -45,9 +45,6 @@ jobs: - name: Setup services for testing purpose run: export IMAGE_TAG=localhost:5000/test/graphql-mesh:latest && cd ./test/integration && docker compose up -d - - name: Inspect network - run: docker network inspect integration_default - - name: Set up Node.js uses: actions/setup-node@v3 with: diff --git a/packages/graphql-mesh/utils/swaggers/index.ts b/packages/graphql-mesh/utils/swaggers/index.ts index 3d833a0..66b0f40 100644 --- a/packages/graphql-mesh/utils/swaggers/index.ts +++ b/packages/graphql-mesh/utils/swaggers/index.ts @@ -126,10 +126,10 @@ export const generateTypeDefsAndResolversFromSwagger = ( root = { ...root, followLink: hateoasLink.href } } - // @TODO: Fix type checking for interger params if (paramsToSend.length) { paramsToSend.forEach((param, i) => { - args[param] = root[param] || root[paramsFromLink[i]] || '' + // To avoid params validation error in case of missing params or type mismatch we set default value to '0' + args[param] = root[param] || root[paramsFromLink[i]] || '0' }) } diff --git a/test/api/models/products.ts b/test/api/models/products.ts index 745f6fa..04a9637 100644 --- a/test/api/models/products.ts +++ b/test/api/models/products.ts @@ -16,7 +16,7 @@ type Product = { function generateProducts() { const _products: Product[] = [] for (let i = 1; i <= 50; i++) { - const supplierId = Math.floor(Math.random() * 10) + 1 + const supplierId = (i % 10) + 1 const product: Product = { id: i, name: `Product ${i}`, diff --git a/test/integration/tests/cases/directive-spl.test.ts b/test/integration/tests/cases/directive-spl.test.ts new file mode 100644 index 0000000..e1519b6 --- /dev/null +++ b/test/integration/tests/cases/directive-spl.test.ts @@ -0,0 +1,25 @@ +import { test, expect } from 'vitest' +import axios from 'axios' +import { url, headers } from '../config' + +/* SPL filter */ +const getTenFirstProductsQuery = /* GraphQL */ ` + query getTenFirstProducts { + getProducts { + items @SPL(query: "id <= 10") { + name + id + price + supplierId + } + } + } +` + +test('getTenFirstProducts with SPL filter query', async () => { + const response = await axios.post(url, { query: getTenFirstProductsQuery }, { headers }) + + const result = response.data + expect(response.status).toBe(200) + expect(result.data.getProducts.items.length).toEqual(10) +}) diff --git a/test/integration/tests/get-products.test.ts b/test/integration/tests/cases/graphql-mesh.test.ts similarity index 64% rename from test/integration/tests/get-products.test.ts rename to test/integration/tests/cases/graphql-mesh.test.ts index 26a03c3..c6c5c1a 100644 --- a/test/integration/tests/get-products.test.ts +++ b/test/integration/tests/cases/graphql-mesh.test.ts @@ -1,8 +1,6 @@ import { test, expect } from 'vitest' import axios from 'axios' - -const url = 'http://0.0.0.0:45538/graphql' -const headers = { 'Content-Type': 'application/json' } +import { url, headers } from '../config' /* Get all products */ const getAllProductsQuery = /* GraphQL */ ` @@ -28,46 +26,55 @@ test('getAllProducts query', async () => { expect(result.data.getProducts.items.length).toEqual(50) }) -/* SPL filter */ -const getTenFirstProductsQuery = /* GraphQL */ ` - query getTenFirstProducts { - getProducts { - items @SPL(query: "id <= 10") { +/* Hateoas link */ +const getProductAndSupplierInfo = /* GraphQL */ ` + query getProductAndSupplierInfo { + getProductById(id: 1) { + id + name + price + supplier { name id - price - supplierId } } } ` -test('getTenFirstProducts with SPL filter query', async () => { - const response = await axios.post(url, { query: getTenFirstProductsQuery }, { headers }) +test('Follow hateoas link to get Suppier info', async () => { + const response = await axios.post(url, { query: getProductAndSupplierInfo }, { headers }) const result = response.data - expect(response.status).toBe(200) - expect(result.data.getProducts.items.length).toEqual(10) + expect(result.data.getProductById.supplier.name).contains('Supplier 2') }) -/* Hateoas link */ -const getProductAndSupplierInfo = /* GraphQL */ ` - query getProductAndSupplierInfo { +/* Linklist property */ + +const getProductwithLinkList = /* GraphQL */ ` + query getProductWithLinkList { getProductById(id: 1) { - id - name - price - supplier { - name - id + supplierId + _linksList { + rel + href } } } ` test('Follow hateoas link to get Suppier info', async () => { - const response = await axios.post(url, { query: getProductAndSupplierInfo }, { headers }) + const response = await axios.post(url, { query: getProductwithLinkList }, { headers }) const result = response.data - expect(result.data.getProductById.supplier.name).contains('Supplier') + expect(result.data.getProductById._linksList.length).toEqual(2) + expect(result.data.getProductById._linksList).toEqual([ + { + rel: 'self', + href: '/products/1' + }, + { + rel: 'supplier', + href: '/suppliers/2' + } + ]) }) diff --git a/test/integration/tests/cases/inject-additionnal-transforms.test.ts b/test/integration/tests/cases/inject-additionnal-transforms.test.ts new file mode 100644 index 0000000..6f90cff --- /dev/null +++ b/test/integration/tests/cases/inject-additionnal-transforms.test.ts @@ -0,0 +1,22 @@ +import { test, expect } from 'vitest' +import axios from 'axios' +import { url, headers } from '../config' + +/* Get all products */ +const getProductById = /* GraphQL */ ` + query getProduct { + getProductById(id: 1) { + name @lower + price + supplierId + } + } +` + +test('Injection lower transform', async () => { + const response = await axios.post(url, { query: getProductById }, { headers }) + + const result = response.data + expect(result).toHaveProperty('data') + expect(result.data.getProductById.name).toEqual('product 1') +}) diff --git a/test/integration/tests/cases/plugins.test.ts b/test/integration/tests/cases/plugins.test.ts new file mode 100644 index 0000000..91f7dae --- /dev/null +++ b/test/integration/tests/cases/plugins.test.ts @@ -0,0 +1,20 @@ +import { test, expect } from 'vitest' +import axios from 'axios' +import { url, headers } from '../config' + +/* Get all products */ +const getProductById = /* GraphQL */ ` + query getProduct { + getProductById(id: 1) { + name + price + supplierId + } + } +` + +test('Server timing plugin', async () => { + const response = await axios.post(url, { query: getProductById }, { headers }) + const serverTiming = response.headers['server-timing'] + expect(serverTiming).contains('getProductById;desc="getProductById (Products)";dur') +}) diff --git a/test/integration/tests/config.ts b/test/integration/tests/config.ts new file mode 100644 index 0000000..48a36bc --- /dev/null +++ b/test/integration/tests/config.ts @@ -0,0 +1,3 @@ +export const url = 'http://0.0.0.0:45538/graphql' +// export const url = 'http://0.0.0.0:4000/graphql' +export const headers = { 'Content-Type': 'application/json' } From ebb46e76d21b2d562e7b572970b5d39871e9c6d1 Mon Sep 17 00:00:00 2001 From: Mbaye THIAM Date: Tue, 16 Apr 2024 17:57:29 +0200 Subject: [PATCH 6/8] =?UTF-8?q?=F0=9F=A4=A1=20Replace=20api=20with=20camou?= =?UTF-8?q?fage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/api/.gitignore | 2 - test/api/Dockerfile | 22 - test/api/models/products.ts | 39 - test/api/models/suppliers.ts | 18 - test/api/package-lock.json | 1102 ----------------- test/api/package.json | 28 - test/api/server.ts | 52 - test/api/tsconfig.json | 109 -- test/integration/camouflage.yaml | 65 + test/integration/compose.yaml | 35 + test/integration/config.yaml | 8 +- test/integration/docker-compose.yaml | 33 - .../mocks}/api-docs/products.yml | 0 .../mocks/api-docs/products/GET.mock | 136 ++ .../mocks}/api-docs/suppliers.yml | 0 .../mocks/api-docs/suppliers/GET.mock | 102 ++ test/integration/mocks/products/GET.mock | 707 +++++++++++ test/integration/mocks/products/__/GET.mock | 28 + test/integration/mocks/suppliers/GET.mock | 47 + test/integration/mocks/suppliers/__/GET.mock | 9 + .../tests/cases/directive-spl.test.ts | 1 + .../tests/cases/graphql-mesh.test.ts | 3 + .../inject-additionnal-transforms.test.ts | 1 + test/integration/tests/cases/plugins.test.ts | 2 + 24 files changed, 1140 insertions(+), 1409 deletions(-) delete mode 100644 test/api/.gitignore delete mode 100644 test/api/Dockerfile delete mode 100644 test/api/models/products.ts delete mode 100644 test/api/models/suppliers.ts delete mode 100644 test/api/package-lock.json delete mode 100644 test/api/package.json delete mode 100644 test/api/server.ts delete mode 100644 test/api/tsconfig.json create mode 100644 test/integration/camouflage.yaml create mode 100644 test/integration/compose.yaml delete mode 100644 test/integration/docker-compose.yaml rename test/{api => integration/mocks}/api-docs/products.yml (100%) create mode 100644 test/integration/mocks/api-docs/products/GET.mock rename test/{api => integration/mocks}/api-docs/suppliers.yml (100%) create mode 100644 test/integration/mocks/api-docs/suppliers/GET.mock create mode 100644 test/integration/mocks/products/GET.mock create mode 100644 test/integration/mocks/products/__/GET.mock create mode 100644 test/integration/mocks/suppliers/GET.mock create mode 100644 test/integration/mocks/suppliers/__/GET.mock diff --git a/test/api/.gitignore b/test/api/.gitignore deleted file mode 100644 index de4d1f0..0000000 --- a/test/api/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -dist -node_modules diff --git a/test/api/Dockerfile b/test/api/Dockerfile deleted file mode 100644 index ddc2416..0000000 --- a/test/api/Dockerfile +++ /dev/null @@ -1,22 +0,0 @@ - -# Use a base image with Node.js -FROM node:18-alpine - -RUN apk add curl - -# Set the working directory inside the container -WORKDIR /app - -# Copy package.json and package-lock.json to the working directory -COPY package*.json ./ - -# Install dependencies -RUN npm install -# Copy the rest of the code to the working directory -COPY . . - -# Expose the port on which your API will run -#EXPOSE 5000 - -# Define the command to start your API -CMD ["npm", "run", "start"] diff --git a/test/api/models/products.ts b/test/api/models/products.ts deleted file mode 100644 index 04a9637..0000000 --- a/test/api/models/products.ts +++ /dev/null @@ -1,39 +0,0 @@ -type Product = { - id: number - name: string - price: number - supplierId: number - _links: { - supplier: { - href: string - } - self?: { - href: string - } - } -} - -function generateProducts() { - const _products: Product[] = [] - for (let i = 1; i <= 50; i++) { - const supplierId = (i % 10) + 1 - const product: Product = { - id: i, - name: `Product ${i}`, - price: Math.floor(Math.random() * 100), - supplierId, - _links: { - self: { - href: `/products/${i}` - }, - supplier: { - href: `/suppliers/${supplierId}` - } - } - } - _products.push(product) - } - return _products -} - -export const products = generateProducts() diff --git a/test/api/models/suppliers.ts b/test/api/models/suppliers.ts deleted file mode 100644 index 938b255..0000000 --- a/test/api/models/suppliers.ts +++ /dev/null @@ -1,18 +0,0 @@ -export type Supplier = { - id: number - name: string -} - -const generateSuppliers = () => { - const _suppliers: Supplier[] = [] - for (let i = 1; i <= 10; i++) { - const supplier: Supplier = { - id: i, - name: `Supplier ${i}` - } - _suppliers.push(supplier) - } - return _suppliers -} - -export const suppliers = generateSuppliers() diff --git a/test/api/package-lock.json b/test/api/package-lock.json deleted file mode 100644 index 206267e..0000000 --- a/test/api/package-lock.json +++ /dev/null @@ -1,1102 +0,0 @@ -{ - "name": "api", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "api", - "version": "1.0.0", - "license": "ISC", - "dependencies": { - "@types/express": "^4.17.21", - "@types/node": "^20.12.7", - "express": "^4.19.2", - "swagger-ui-express": "^5.0.0", - "ts-node": "^10.9.2", - "typescript": "^5.4.5", - "yamljs": "^0.3.0" - }, - "devDependencies": { - "@types/swagger-ui-express": "^4.1.6", - "@types/yamljs": "^0.2.34" - } - }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", - "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==" - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==" - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==" - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==" - }, - "node_modules/@types/body-parser": { - "version": "1.19.5", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", - "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/express": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", - "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "4.19.0", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.0.tgz", - "integrity": "sha512-bGyep3JqPCRry1wq+O5n7oiBgGWmeIJXPjXXCo8EK0u8duZGSYar7cGqd3ML2JUsLGeB7fmc06KYo9fLGWqPvQ==", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/http-errors": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", - "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==" - }, - "node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" - }, - "node_modules/@types/node": { - "version": "20.12.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz", - "integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@types/qs": { - "version": "6.9.14", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.14.tgz", - "integrity": "sha512-5khscbd3SwWMhFqylJBLQ0zIu7c1K6Vz0uBIt915BI3zV0q1nfjRQD3RqSBcPaO6PHEF4ov/t9y89fSiyThlPA==" - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==" - }, - "node_modules/@types/send": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", - "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.15.7", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", - "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", - "dependencies": { - "@types/http-errors": "*", - "@types/node": "*", - "@types/send": "*" - } - }, - "node_modules/@types/swagger-ui-express": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@types/swagger-ui-express/-/swagger-ui-express-4.1.6.tgz", - "integrity": "sha512-UVSiGYXa5IzdJJG3hrc86e8KdZWLYxyEsVoUI4iPXc7CO4VZ3AfNP8d/8+hrDRIqz+HAaSMtZSqAsF3Nq2X/Dg==", - "dev": true, - "dependencies": { - "@types/express": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@types/yamljs": { - "version": "0.2.34", - "resolved": "https://registry.npmjs.org/@types/yamljs/-/yamljs-0.2.34.tgz", - "integrity": "sha512-gJvfRlv9ErxdOv7ux7UsJVePtX54NAvQyd8ncoiFqK8G5aeHIfQfGH2fbruvjAQ9657HwAaO54waS+Dsk2QTUQ==", - "dev": true - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", - "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "node_modules/body-parser": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", - "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" - }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" - }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "dependencies": { - "get-intrinsic": "^1.2.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express": { - "version": "4.19.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", - "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.2", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.6.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", - "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/swagger-ui-dist": { - "version": "5.15.1", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.15.1.tgz", - "integrity": "sha512-Et/WY0NFdKj8sUBOyEx5P3VybsvGl7bo/y9JvgQ22TkH1a/KscQ0ZiQST2YeJ3cwCrIjYTbHbt165fkku0y1Ig==" - }, - "node_modules/swagger-ui-express": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-5.0.0.tgz", - "integrity": "sha512-tsU9tODVvhyfkNSvf03E6FAk+z+5cU3lXAzMy6Pv4av2Gt2xA0++fogwC4qo19XuFf6hdxevPuVCSKFuMHJhFA==", - "dependencies": { - "swagger-ui-dist": ">=5.0.0" - }, - "engines": { - "node": ">= v0.10.32" - }, - "peerDependencies": { - "express": ">=4.0.0 || >=5.0.0-beta" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/ts-node": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typescript": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", - "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==" - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "node_modules/yamljs": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/yamljs/-/yamljs-0.3.0.tgz", - "integrity": "sha512-C/FsVVhht4iPQYXOInoxUM/1ELSf9EsgKH34FofQOp6hwCPrW4vG4w5++TED3xRUo8gD7l0P1J1dLlDYzODsTQ==", - "dependencies": { - "argparse": "^1.0.7", - "glob": "^7.0.5" - }, - "bin": { - "json2yaml": "bin/json2yaml", - "yaml2json": "bin/yaml2json" - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "engines": { - "node": ">=6" - } - } - } -} diff --git a/test/api/package.json b/test/api/package.json deleted file mode 100644 index 0aa13a7..0000000 --- a/test/api/package.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "name": "api", - "version": "1.0.0", - "description": "", - "main": "index.js", - "scripts": { - "build:local:image": "docker build -t api-products .", - "build": "tsc", - "start": "ts-node server.ts", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "keywords": [], - "author": "", - "license": "ISC", - "dependencies": { - "express": "^4.19.2", - "swagger-ui-express": "^5.0.0", - "ts-node": "^10.9.2", - "typescript": "^5.4.5", - "yamljs": "^0.3.0" - }, - "devDependencies": { - "@types/express": "^4.17.21", - "@types/node": "^20.12.7", - "@types/swagger-ui-express": "^4.1.6", - "@types/yamljs": "^0.2.34" - } -} diff --git a/test/api/server.ts b/test/api/server.ts deleted file mode 100644 index 339d360..0000000 --- a/test/api/server.ts +++ /dev/null @@ -1,52 +0,0 @@ -import express from 'express' -import { products } from './models/products' -import { suppliers } from './models/suppliers' -import swaggerUi from 'swagger-ui-express' -import Yaml from 'yamljs' - -const app = express() -const port = 5000 -app.listen(port, () => { - console.log(`🚀 Server is running at http://localhost:${port}`) -}) - -app.use(express.json()) - -// Serve the swagger.yml file -app.use('/api-docs/:file', (req, res) => { - const file = req.params.file - res.json(Yaml.load(`./api-docs/${file}`)) -}) - -// Redirect to the swagger UI -app.use( - '/api-products-docs', - swaggerUi.serve, - swaggerUi.setup(Yaml.load('./api-docs/products.yml')) -) -app.use( - '/api-suppliers-docs', - swaggerUi.serve, - swaggerUi.setup(Yaml.load('./api-docs/suppliers.yml')) -) - -// Serve the products -app.get('/products', (req, res) => { - res.json({ items: products }) -}) - -// Serve single product -app.get('/products/:id', (req, res) => { - const id = parseInt(req.params.id) - res.json({ ...products.find((product) => product.id === id) }) -}) -// Serve the suppliers -app.get('/suppliers', (req, res) => { - res.json({ items: suppliers }) -}) - -// Serve a single supplier -app.get('/suppliers/:id', (req, res) => { - const id = parseInt(req.params.id) - res.json({ ...suppliers.find((supplier) => supplier.id === id) }) -}) diff --git a/test/api/tsconfig.json b/test/api/tsconfig.json deleted file mode 100644 index 8996a90..0000000 --- a/test/api/tsconfig.json +++ /dev/null @@ -1,109 +0,0 @@ -{ - "compilerOptions": { - /* Visit https://aka.ms/tsconfig to read more about this file */ - - /* Projects */ - // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ - // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ - // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ - // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ - // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ - // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ - - /* Language and Environment */ - "target": "ESNext" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, - // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ - // "jsx": "preserve", /* Specify what JSX code is generated. */ - // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ - // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ - // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ - // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ - // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ - // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ - // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ - // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ - // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ - - /* Modules */ - "module": "commonjs" /* Specify what module code is generated. */, - "rootDir": "./" /* Specify the root folder within your source files. */, - // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ - // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ - // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ - // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ - // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ - // "types": [], /* Specify type package names to be included without being referenced in a source file. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ - // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ - // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ - // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ - // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ - // "resolveJsonModule": true, /* Enable importing .json files. */ - // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ - // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ - - /* JavaScript Support */ - // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ - // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ - // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ - - /* Emit */ - // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ - // "declarationMap": true, /* Create sourcemaps for d.ts files. */ - // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ - // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ - // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ - // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ - "outDir": "./dist" /* Specify an output folder for all emitted files. */, - // "removeComments": true, /* Disable emitting comments. */ - // "noEmit": true, /* Disable emitting files from a compilation. */ - // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ - // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ - // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ - // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ - // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ - // "newLine": "crlf", /* Set the newline character for emitting files. */ - // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ - // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ - // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ - // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ - // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ - // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ - - /* Interop Constraints */ - // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ - // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ - // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, - // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ - "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, - - /* Type Checking */ - "strict": true /* Enable all strict type-checking options. */, - // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ - // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ - // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ - // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ - // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ - // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ - // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ - // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ - // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ - // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ - // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ - // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ - // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ - // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ - // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ - // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ - // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ - - /* Completeness */ - // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ - } -} diff --git a/test/integration/camouflage.yaml b/test/integration/camouflage.yaml new file mode 100644 index 0000000..84df4c0 --- /dev/null +++ b/test/integration/camouflage.yaml @@ -0,0 +1,65 @@ +loglevel: info +cpus: 1 +monitoring: + port: 5555 +ssl: + cert: "./certs/server.cert" + key: "./certs/server.key" + root_cert: "./certs/root.cert" +protocols: + http: + enable: true + mocks_dir: "./mocks" + port: 8080 + https: + enable: false + port: 8443 + http2: + enable: false + port: 8081 + ws: + enable: false + mocks_dir: "./ws_mocks" + port: 8082 + grpc: + enable: false + host: 0.0.0.0 + port: 4312 + mocks_dir: "./grpc/mocks" + protos_dir: "./grpc/protos" + grpc_tls: false + thrift: + enable: false + mocks_dir: "./thrift/mocks" + services: + - port: 9999 + service: "/opt/gen-nodejs/Calculator" + handlers: + - add + - substract + - ping +backup: + enable: false + cron: "0 * * * *" # Hourly Backup +cache: + enable: false + ttl_seconds: 300 +injection: + enable: true +origins: + - http://localhost:3000 + - http://localhost:3001 + - http://localhost:5000 +validation: + enable: true + schemas: + - type: OpenApi + url: https://raw.githubusercontent.com/OAI/OpenAPI-Specification/main/examples/v3.0/petstore.json +# ext_helpers: "./custom_handlebar.json" +# ext_data_source: +# pg: +# host: localhost +# port: 5432 +# user: root +# password: password +# database: postgres diff --git a/test/integration/compose.yaml b/test/integration/compose.yaml new file mode 100644 index 0000000..ac9c740 --- /dev/null +++ b/test/integration/compose.yaml @@ -0,0 +1,35 @@ +services: + # + api-products: + image: shubhendumadhukar/camouflage + ports: + - 45537:8080 + volumes: + - ./camouflage.yaml:/app/config.yml:ro + - ./mocks:/app/mocks:ro + + healthcheck: + # https://stackoverflow.com/questions/72708667/use-wget-instead-of-curl-for-healthchecks-in-asp-net-core-docker-images + test: wget --spider --tries=1 --no-verbose http://localhost:8080/products || exit 1 + interval: 10s + timeout: 10s + retries: 3 + restart: unless-stopped + + # + graphql-mesh: + depends_on: + api-products: + condition: service_healthy + links: + - api-products + image: ${IMAGE_TAG:-graphql-mesh} + environment: + - DEBUG=1 + ports: + - 45538:3000 + volumes: + - ./transforms:/app/transforms:ro + - ./plugins:/app/plugins:ro + - ./config.yaml:/app/config.yaml:ro + restart: unless-stopped diff --git a/test/integration/config.yaml b/test/integration/config.yaml index 78caaa0..36661c3 100644 --- a/test/integration/config.yaml +++ b/test/integration/config.yaml @@ -2,13 +2,13 @@ sources: - name: Products handler: openapi: - source: http://api-products:5000/api-docs/products.yml - endpoint: http://api-products:5000 + source: http://api-products:8080/api-docs/products + endpoint: http://api-products:8080 - name: Suppliers handler: openapi: - source: http://api-products:5000/api-docs/suppliers.yml - endpoint: http://api-products:5000 + source: http://api-products:8080/api-docs/suppliers + endpoint: http://api-products:8080 additionalEnvelopPlugins: "./plugins" additionalTransforms: [{ "./transforms/index.ts": {} }] diff --git a/test/integration/docker-compose.yaml b/test/integration/docker-compose.yaml deleted file mode 100644 index 28a6a80..0000000 --- a/test/integration/docker-compose.yaml +++ /dev/null @@ -1,33 +0,0 @@ -services: - # - api-products: - build: - context: ../api/ - dockerfile: ../api/Dockerfile - #image: api-products - ports: - - 45537:5000 - healthcheck: - test: curl --fail http://localhost:5000/products || exit 1 - interval: 10s - timeout: 10s - retries: 3 - restart: unless-stopped - - # - graphql-mesh: - depends_on: - api-products: - condition: service_healthy - links: - - api-products - image: ${IMAGE_TAG:-bouyguestelecom/graphql-mesh:latest} - environment: - - DEBUG=1 - ports: - - 45538:3000 - volumes: - - ./transforms:/app/transforms - - ./plugins:/app/plugins - - ./config.yaml:/app/config.yaml - restart: unless-stopped diff --git a/test/api/api-docs/products.yml b/test/integration/mocks/api-docs/products.yml similarity index 100% rename from test/api/api-docs/products.yml rename to test/integration/mocks/api-docs/products.yml diff --git a/test/integration/mocks/api-docs/products/GET.mock b/test/integration/mocks/api-docs/products/GET.mock new file mode 100644 index 0000000..8648768 --- /dev/null +++ b/test/integration/mocks/api-docs/products/GET.mock @@ -0,0 +1,136 @@ +HTTP/1.1 200 OK +Content-Type: application/json + +{ + "openapi": "3.0.0", + "info": { + "title": "Products API", + "description": "API to manage products with HATEOAS.", + "version": "1.0" + }, + "servers": [ + { + "url": "http://localhost:3000/" + } + ], + "paths": { + "/products": { + "get": { + "operationId": "getProducts", + "summary": "List all products", + "responses": { + "200": { + "description": "A list of products with HATEOAS links", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Products" + } + } + } + } + } + } + }, + "/products/{id}": { + "get": { + "operationId": "getProductById", + "summary": "Get a product by ID", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + }, + "description": "The product ID" + } + ], + "responses": { + "200": { + "description": "A list of products with HATEOAS links", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Product" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Product": { + "type": "object", + "properties": { + "_links": { + "$ref": "#/components/schemas/ProductLinks" + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "price": { + "type": "number" + }, + "supplierId": { + "type": "integer" + } + } + }, + "ProductLinks": { + "type": "object", + "required": [ + "self", + "supplier" + ], + "properties": { + "self": { + "$ref": "#/components/schemas/Link" + }, + "supplier": { + "$ref": "#/components/schemas/Link" + } + }, + "x-links": [ + { + "rel": "self", + "hrefPattern": "/products/{id}" + }, + { + "rel": "supplier", + "hrefPattern": "/suppliers/{id}" + } + ] + }, + "Products": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Product" + } + } + } + }, + "Link": { + "type": "object", + "required": [ + "href" + ], + "properties": { + "href": { + "type": "string" + } + } + } + } + } +} diff --git a/test/api/api-docs/suppliers.yml b/test/integration/mocks/api-docs/suppliers.yml similarity index 100% rename from test/api/api-docs/suppliers.yml rename to test/integration/mocks/api-docs/suppliers.yml diff --git a/test/integration/mocks/api-docs/suppliers/GET.mock b/test/integration/mocks/api-docs/suppliers/GET.mock new file mode 100644 index 0000000..cdbdd65 --- /dev/null +++ b/test/integration/mocks/api-docs/suppliers/GET.mock @@ -0,0 +1,102 @@ +HTTP/1.1 200 OK +Content-Type: application/json + +{ + "openapi": "3.0.0", + "info": { + "title": "Suppliers API", + "description": "API to manage suppliers with HATEOAS.", + "version": "1.0" + }, + "servers": [ + { + "url": "http://localhost:3000/" + } + ], + "paths": { + "/suppliers": { + "get": { + "operationId": "getSuppliers", + "summary": "List all suppliers", + "responses": { + "200": { + "description": "A list of suppliers with HATEOAS links", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Suppliers" + } + } + } + } + } + } + }, + "/suppliers/{id}": { + "get": { + "operationId": "getSupplierById", + "summary": "Get a supplier by ID", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + }, + "description": "The supplier ID" + } + ], + "responses": { + "200": { + "description": "A list of suppliers with HATEOAS links", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Supplier" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Supplier": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + } + } + }, + "Link": { + "type": "object", + "required": [ + "href" + ], + "properties": { + "href": { + "type": "string" + } + } + }, + "Suppliers": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Supplier" + } + } + } + } + } + } +} diff --git a/test/integration/mocks/products/GET.mock b/test/integration/mocks/products/GET.mock new file mode 100644 index 0000000..ad9ac31 --- /dev/null +++ b/test/integration/mocks/products/GET.mock @@ -0,0 +1,707 @@ +HTTP/1.1 200 OK +Content-Type: application/json + +{ + "items": [ + { + "id": 1, + "name": "Product 1", + "price": 4, + "supplierId": 2, + "_links": { + "self": { + "href": "/products/1" + }, + "supplier": { + "href": "/suppliers/2" + } + } + }, + { + "id": 2, + "name": "Product 2", + "price": 74, + "supplierId": 3, + "_links": { + "self": { + "href": "/products/2" + }, + "supplier": { + "href": "/suppliers/3" + } + } + }, + { + "id": 3, + "name": "Product 3", + "price": 50, + "supplierId": 4, + "_links": { + "self": { + "href": "/products/3" + }, + "supplier": { + "href": "/suppliers/4" + } + } + }, + { + "id": 4, + "name": "Product 4", + "price": 1, + "supplierId": 5, + "_links": { + "self": { + "href": "/products/4" + }, + "supplier": { + "href": "/suppliers/5" + } + } + }, + { + "id": 5, + "name": "Product 5", + "price": 21, + "supplierId": 6, + "_links": { + "self": { + "href": "/products/5" + }, + "supplier": { + "href": "/suppliers/6" + } + } + }, + { + "id": 6, + "name": "Product 6", + "price": 29, + "supplierId": 7, + "_links": { + "self": { + "href": "/products/6" + }, + "supplier": { + "href": "/suppliers/7" + } + } + }, + { + "id": 7, + "name": "Product 7", + "price": 53, + "supplierId": 8, + "_links": { + "self": { + "href": "/products/7" + }, + "supplier": { + "href": "/suppliers/8" + } + } + }, + { + "id": 8, + "name": "Product 8", + "price": 41, + "supplierId": 9, + "_links": { + "self": { + "href": "/products/8" + }, + "supplier": { + "href": "/suppliers/9" + } + } + }, + { + "id": 9, + "name": "Product 9", + "price": 50, + "supplierId": 10, + "_links": { + "self": { + "href": "/products/9" + }, + "supplier": { + "href": "/suppliers/10" + } + } + }, + { + "id": 10, + "name": "Product 10", + "price": 93, + "supplierId": 1, + "_links": { + "self": { + "href": "/products/10" + }, + "supplier": { + "href": "/suppliers/1" + } + } + }, + { + "id": 11, + "name": "Product 11", + "price": 54, + "supplierId": 2, + "_links": { + "self": { + "href": "/products/11" + }, + "supplier": { + "href": "/suppliers/2" + } + } + }, + { + "id": 12, + "name": "Product 12", + "price": 15, + "supplierId": 3, + "_links": { + "self": { + "href": "/products/12" + }, + "supplier": { + "href": "/suppliers/3" + } + } + }, + { + "id": 13, + "name": "Product 13", + "price": 12, + "supplierId": 4, + "_links": { + "self": { + "href": "/products/13" + }, + "supplier": { + "href": "/suppliers/4" + } + } + }, + { + "id": 14, + "name": "Product 14", + "price": 82, + "supplierId": 5, + "_links": { + "self": { + "href": "/products/14" + }, + "supplier": { + "href": "/suppliers/5" + } + } + }, + { + "id": 15, + "name": "Product 15", + "price": 64, + "supplierId": 6, + "_links": { + "self": { + "href": "/products/15" + }, + "supplier": { + "href": "/suppliers/6" + } + } + }, + { + "id": 16, + "name": "Product 16", + "price": 53, + "supplierId": 7, + "_links": { + "self": { + "href": "/products/16" + }, + "supplier": { + "href": "/suppliers/7" + } + } + }, + { + "id": 17, + "name": "Product 17", + "price": 42, + "supplierId": 8, + "_links": { + "self": { + "href": "/products/17" + }, + "supplier": { + "href": "/suppliers/8" + } + } + }, + { + "id": 18, + "name": "Product 18", + "price": 7, + "supplierId": 9, + "_links": { + "self": { + "href": "/products/18" + }, + "supplier": { + "href": "/suppliers/9" + } + } + }, + { + "id": 19, + "name": "Product 19", + "price": 99, + "supplierId": 10, + "_links": { + "self": { + "href": "/products/19" + }, + "supplier": { + "href": "/suppliers/10" + } + } + }, + { + "id": 20, + "name": "Product 20", + "price": 49, + "supplierId": 1, + "_links": { + "self": { + "href": "/products/20" + }, + "supplier": { + "href": "/suppliers/1" + } + } + }, + { + "id": 21, + "name": "Product 21", + "price": 27, + "supplierId": 2, + "_links": { + "self": { + "href": "/products/21" + }, + "supplier": { + "href": "/suppliers/2" + } + } + }, + { + "id": 22, + "name": "Product 22", + "price": 4, + "supplierId": 3, + "_links": { + "self": { + "href": "/products/22" + }, + "supplier": { + "href": "/suppliers/3" + } + } + }, + { + "id": 23, + "name": "Product 23", + "price": 33, + "supplierId": 4, + "_links": { + "self": { + "href": "/products/23" + }, + "supplier": { + "href": "/suppliers/4" + } + } + }, + { + "id": 24, + "name": "Product 24", + "price": 14, + "supplierId": 5, + "_links": { + "self": { + "href": "/products/24" + }, + "supplier": { + "href": "/suppliers/5" + } + } + }, + { + "id": 25, + "name": "Product 25", + "price": 18, + "supplierId": 6, + "_links": { + "self": { + "href": "/products/25" + }, + "supplier": { + "href": "/suppliers/6" + } + } + }, + { + "id": 26, + "name": "Product 26", + "price": 1, + "supplierId": 7, + "_links": { + "self": { + "href": "/products/26" + }, + "supplier": { + "href": "/suppliers/7" + } + } + }, + { + "id": 27, + "name": "Product 27", + "price": 28, + "supplierId": 8, + "_links": { + "self": { + "href": "/products/27" + }, + "supplier": { + "href": "/suppliers/8" + } + } + }, + { + "id": 28, + "name": "Product 28", + "price": 55, + "supplierId": 9, + "_links": { + "self": { + "href": "/products/28" + }, + "supplier": { + "href": "/suppliers/9" + } + } + }, + { + "id": 29, + "name": "Product 29", + "price": 55, + "supplierId": 10, + "_links": { + "self": { + "href": "/products/29" + }, + "supplier": { + "href": "/suppliers/10" + } + } + }, + { + "id": 30, + "name": "Product 30", + "price": 33, + "supplierId": 1, + "_links": { + "self": { + "href": "/products/30" + }, + "supplier": { + "href": "/suppliers/1" + } + } + }, + { + "id": 31, + "name": "Product 31", + "price": 68, + "supplierId": 2, + "_links": { + "self": { + "href": "/products/31" + }, + "supplier": { + "href": "/suppliers/2" + } + } + }, + { + "id": 32, + "name": "Product 32", + "price": 14, + "supplierId": 3, + "_links": { + "self": { + "href": "/products/32" + }, + "supplier": { + "href": "/suppliers/3" + } + } + }, + { + "id": 33, + "name": "Product 33", + "price": 25, + "supplierId": 4, + "_links": { + "self": { + "href": "/products/33" + }, + "supplier": { + "href": "/suppliers/4" + } + } + }, + { + "id": 34, + "name": "Product 34", + "price": 12, + "supplierId": 5, + "_links": { + "self": { + "href": "/products/34" + }, + "supplier": { + "href": "/suppliers/5" + } + } + }, + { + "id": 35, + "name": "Product 35", + "price": 85, + "supplierId": 6, + "_links": { + "self": { + "href": "/products/35" + }, + "supplier": { + "href": "/suppliers/6" + } + } + }, + { + "id": 36, + "name": "Product 36", + "price": 54, + "supplierId": 7, + "_links": { + "self": { + "href": "/products/36" + }, + "supplier": { + "href": "/suppliers/7" + } + } + }, + { + "id": 37, + "name": "Product 37", + "price": 41, + "supplierId": 8, + "_links": { + "self": { + "href": "/products/37" + }, + "supplier": { + "href": "/suppliers/8" + } + } + }, + { + "id": 38, + "name": "Product 38", + "price": 85, + "supplierId": 9, + "_links": { + "self": { + "href": "/products/38" + }, + "supplier": { + "href": "/suppliers/9" + } + } + }, + { + "id": 39, + "name": "Product 39", + "price": 95, + "supplierId": 10, + "_links": { + "self": { + "href": "/products/39" + }, + "supplier": { + "href": "/suppliers/10" + } + } + }, + { + "id": 40, + "name": "Product 40", + "price": 37, + "supplierId": 1, + "_links": { + "self": { + "href": "/products/40" + }, + "supplier": { + "href": "/suppliers/1" + } + } + }, + { + "id": 41, + "name": "Product 41", + "price": 15, + "supplierId": 2, + "_links": { + "self": { + "href": "/products/41" + }, + "supplier": { + "href": "/suppliers/2" + } + } + }, + { + "id": 42, + "name": "Product 42", + "price": 38, + "supplierId": 3, + "_links": { + "self": { + "href": "/products/42" + }, + "supplier": { + "href": "/suppliers/3" + } + } + }, + { + "id": 43, + "name": "Product 43", + "price": 20, + "supplierId": 4, + "_links": { + "self": { + "href": "/products/43" + }, + "supplier": { + "href": "/suppliers/4" + } + } + }, + { + "id": 44, + "name": "Product 44", + "price": 77, + "supplierId": 5, + "_links": { + "self": { + "href": "/products/44" + }, + "supplier": { + "href": "/suppliers/5" + } + } + }, + { + "id": 45, + "name": "Product 45", + "price": 46, + "supplierId": 6, + "_links": { + "self": { + "href": "/products/45" + }, + "supplier": { + "href": "/suppliers/6" + } + } + }, + { + "id": 46, + "name": "Product 46", + "price": 92, + "supplierId": 7, + "_links": { + "self": { + "href": "/products/46" + }, + "supplier": { + "href": "/suppliers/7" + } + } + }, + { + "id": 47, + "name": "Product 47", + "price": 10, + "supplierId": 8, + "_links": { + "self": { + "href": "/products/47" + }, + "supplier": { + "href": "/suppliers/8" + } + } + }, + { + "id": 48, + "name": "Product 48", + "price": 56, + "supplierId": 9, + "_links": { + "self": { + "href": "/products/48" + }, + "supplier": { + "href": "/suppliers/9" + } + } + }, + { + "id": 49, + "name": "Product 49", + "price": 90, + "supplierId": 10, + "_links": { + "self": { + "href": "/products/49" + }, + "supplier": { + "href": "/suppliers/10" + } + } + }, + { + "id": 50, + "name": "Product 50", + "price": 93, + "supplierId": 1, + "_links": { + "self": { + "href": "/products/50" + }, + "supplier": { + "href": "/suppliers/1" + } + } + } + ] +} diff --git a/test/integration/mocks/products/__/GET.mock b/test/integration/mocks/products/__/GET.mock new file mode 100644 index 0000000..73f77cf --- /dev/null +++ b/test/integration/mocks/products/__/GET.mock @@ -0,0 +1,28 @@ +{{assign name='id' value=(capture from='path' regex='\/products\/(.+)?') }} + +HTTP/1.1 200 OK +Content-Type: application/json + +{{#code}} +((id) => { + const supplierId = (parseInt(id) % 10) + 1 + logger.info(request.params[0].split('/').reverse()) + return { + status: 200, + body: JSON.stringify({ + "id": 1, + "name": `Product ${id}`, + "price": `${Math.floor(Math.random() * 100)}`, + "supplierId": `${supplierId}`, + "_links": { + "self": { + "href": `/products/${id}` + }, + "supplier": { + "href": `/suppliers/${supplierId}` + } + } + }, null, 2) + } +})(request.params[0].split('/').reverse()[0]); +{{/code}} diff --git a/test/integration/mocks/suppliers/GET.mock b/test/integration/mocks/suppliers/GET.mock new file mode 100644 index 0000000..051df82 --- /dev/null +++ b/test/integration/mocks/suppliers/GET.mock @@ -0,0 +1,47 @@ +HTTP/1.1 200 OK +Content-Type: application/json + +{ + "items": [ + { + "id": 1, + "name": "Supplier 1" + }, + { + "id": 2, + "name": "Supplier 2" + }, + { + "id": 3, + "name": "Supplier 3" + }, + { + "id": 4, + "name": "Supplier 4" + }, + { + "id": 5, + "name": "Supplier 5" + }, + { + "id": 6, + "name": "Supplier 6" + }, + { + "id": 7, + "name": "Supplier 7" + }, + { + "id": 8, + "name": "Supplier 8" + }, + { + "id": 9, + "name": "Supplier 9" + }, + { + "id": 10, + "name": "Supplier 10" + } + ] +} diff --git a/test/integration/mocks/suppliers/__/GET.mock b/test/integration/mocks/suppliers/__/GET.mock new file mode 100644 index 0000000..d6afe99 --- /dev/null +++ b/test/integration/mocks/suppliers/__/GET.mock @@ -0,0 +1,9 @@ +{{assign name='id' value=(capture from='path' regex='\/suppliers\/(.+)?') }} + +HTTP/1.1 200 OK +Content-Type: application/json + +{ + "id": "{{id}}", + "name": "Supplier {{id}}" +} diff --git a/test/integration/tests/cases/directive-spl.test.ts b/test/integration/tests/cases/directive-spl.test.ts index e1519b6..27ac1c1 100644 --- a/test/integration/tests/cases/directive-spl.test.ts +++ b/test/integration/tests/cases/directive-spl.test.ts @@ -21,5 +21,6 @@ test('getTenFirstProducts with SPL filter query', async () => { const result = response.data expect(response.status).toBe(200) + expect(result.errors).toBeUndefined() expect(result.data.getProducts.items.length).toEqual(10) }) diff --git a/test/integration/tests/cases/graphql-mesh.test.ts b/test/integration/tests/cases/graphql-mesh.test.ts index c6c5c1a..100f9c1 100644 --- a/test/integration/tests/cases/graphql-mesh.test.ts +++ b/test/integration/tests/cases/graphql-mesh.test.ts @@ -22,6 +22,7 @@ test('getAllProducts query', async () => { const result = response.data expect(response.status).toBe(200) expect(result).toHaveProperty('data') + expect(result.errors).toBeUndefined() expect(result.data).toHaveProperty('getProducts') expect(result.data.getProducts.items.length).toEqual(50) }) @@ -45,6 +46,7 @@ test('Follow hateoas link to get Suppier info', async () => { const response = await axios.post(url, { query: getProductAndSupplierInfo }, { headers }) const result = response.data + expect(result.errors).toBeUndefined() expect(result.data.getProductById.supplier.name).contains('Supplier 2') }) @@ -66,6 +68,7 @@ test('Follow hateoas link to get Suppier info', async () => { const response = await axios.post(url, { query: getProductwithLinkList }, { headers }) const result = response.data + expect(result.errors).toBeUndefined() expect(result.data.getProductById._linksList.length).toEqual(2) expect(result.data.getProductById._linksList).toEqual([ { diff --git a/test/integration/tests/cases/inject-additionnal-transforms.test.ts b/test/integration/tests/cases/inject-additionnal-transforms.test.ts index 6f90cff..48b904e 100644 --- a/test/integration/tests/cases/inject-additionnal-transforms.test.ts +++ b/test/integration/tests/cases/inject-additionnal-transforms.test.ts @@ -17,6 +17,7 @@ test('Injection lower transform', async () => { const response = await axios.post(url, { query: getProductById }, { headers }) const result = response.data + expect(result.errors).toBeUndefined() expect(result).toHaveProperty('data') expect(result.data.getProductById.name).toEqual('product 1') }) diff --git a/test/integration/tests/cases/plugins.test.ts b/test/integration/tests/cases/plugins.test.ts index 91f7dae..cdf870b 100644 --- a/test/integration/tests/cases/plugins.test.ts +++ b/test/integration/tests/cases/plugins.test.ts @@ -15,6 +15,8 @@ const getProductById = /* GraphQL */ ` test('Server timing plugin', async () => { const response = await axios.post(url, { query: getProductById }, { headers }) + + expect(response.data.errors).toBeUndefined() const serverTiming = response.headers['server-timing'] expect(serverTiming).contains('getProductById;desc="getProductById (Products)";dur') }) From 65cce4583fcd67756abcb2523e96395221310586 Mon Sep 17 00:00:00 2001 From: Mbaye THIAM Date: Wed, 17 Apr 2024 14:26:47 +0200 Subject: [PATCH 7/8] =?UTF-8?q?=E2=9C=85=20Add=20tests=20for=20noAuth=20an?= =?UTF-8?q?d=20headers=20directives?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/integration/config.yaml | 6 + .../mocks/api-docs/authenticate.yml | 20 + .../mocks/api-docs/authenticate/GET.mock | 36 + test/integration/mocks/authenticate/POST.mock | 16 + test/integration/mocks/products/GET.mock | 733 +----------------- test/integration/mocks/products/__/GET.mock | 1 - test/integration/mocks/suppliers/GET.mock | 62 +- .../tests/cases/directive-headers.test.ts | 21 + .../tests/cases/directive-no-auth.test.ts | 26 + .../tests/cases/directive-spl.test.ts | 2 +- .../tests/cases/graphql-mesh.test.ts | 6 +- .../inject-additionnal-transforms.test.ts | 2 +- test/integration/tests/cases/plugins.test.ts | 2 +- 13 files changed, 178 insertions(+), 755 deletions(-) create mode 100644 test/integration/mocks/api-docs/authenticate.yml create mode 100644 test/integration/mocks/api-docs/authenticate/GET.mock create mode 100644 test/integration/mocks/authenticate/POST.mock create mode 100644 test/integration/tests/cases/directive-headers.test.ts create mode 100644 test/integration/tests/cases/directive-no-auth.test.ts diff --git a/test/integration/config.yaml b/test/integration/config.yaml index 36661c3..9bb03b7 100644 --- a/test/integration/config.yaml +++ b/test/integration/config.yaml @@ -10,6 +10,12 @@ sources: source: http://api-products:8080/api-docs/suppliers endpoint: http://api-products:8080 + - name: Authentication + handler: + openapi: + source: http://api-products:8080/api-docs/authenticate + endpoint: http://api-products:8080 + additionalEnvelopPlugins: "./plugins" additionalTransforms: [{ "./transforms/index.ts": {} }] additionalTypeDefs: | diff --git a/test/integration/mocks/api-docs/authenticate.yml b/test/integration/mocks/api-docs/authenticate.yml new file mode 100644 index 0000000..4198109 --- /dev/null +++ b/test/integration/mocks/api-docs/authenticate.yml @@ -0,0 +1,20 @@ +openapi: 3.0.0 +info: + title: Authentication API + version: 1.0.0 +paths: + /authenticate: + post: + summary: Authenticate user + operationId: isAuthenticated + responses: + '200': + description: Authentication successful + content: + application/json: + schema: + type: object + properties: + authenticate: + type: boolean + description: Yes or No based on authentication header diff --git a/test/integration/mocks/api-docs/authenticate/GET.mock b/test/integration/mocks/api-docs/authenticate/GET.mock new file mode 100644 index 0000000..a7adc8e --- /dev/null +++ b/test/integration/mocks/api-docs/authenticate/GET.mock @@ -0,0 +1,36 @@ +HTTP/1.1 200 OK +Content-Type: application/json + +{ + "openapi": "3.0.0", + "info": { + "title": "Authentication API", + "version": "1.0.0" + }, + "paths": { + "/authenticate": { + "post": { + "summary": "Authenticate user", + "operationId": "isAuthenticated", + "responses": { + "200": { + "description": "Authentication successful", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "authenticate": { + "type": "boolean", + "description": "Yes or No based on authentication header" + } + } + } + } + } + } + } + } + } + } +} diff --git a/test/integration/mocks/authenticate/POST.mock b/test/integration/mocks/authenticate/POST.mock new file mode 100644 index 0000000..deb6879 --- /dev/null +++ b/test/integration/mocks/authenticate/POST.mock @@ -0,0 +1,16 @@ +HTTP/1.1 200 OK +Content-Type: application/json + +{{#code}} +((request) => { + // logger.info(JSON.stringify(request.headers)) + console.log(request.headers['authorization']) + const supplierId = 1 + return { + status: 200, + body: JSON.stringify({ + "authenticate": request.headers['authorization'] === "Bearer token" + }, null, 2) + } +})(request); +{{/code}} diff --git a/test/integration/mocks/products/GET.mock b/test/integration/mocks/products/GET.mock index ad9ac31..1c317f8 100644 --- a/test/integration/mocks/products/GET.mock +++ b/test/integration/mocks/products/GET.mock @@ -1,707 +1,32 @@ HTTP/1.1 200 OK Content-Type: application/json -{ - "items": [ - { - "id": 1, - "name": "Product 1", - "price": 4, - "supplierId": 2, - "_links": { - "self": { - "href": "/products/1" - }, - "supplier": { - "href": "/suppliers/2" - } - } - }, - { - "id": 2, - "name": "Product 2", - "price": 74, - "supplierId": 3, - "_links": { - "self": { - "href": "/products/2" - }, - "supplier": { - "href": "/suppliers/3" - } - } - }, - { - "id": 3, - "name": "Product 3", - "price": 50, - "supplierId": 4, - "_links": { - "self": { - "href": "/products/3" - }, - "supplier": { - "href": "/suppliers/4" - } - } - }, - { - "id": 4, - "name": "Product 4", - "price": 1, - "supplierId": 5, - "_links": { - "self": { - "href": "/products/4" - }, - "supplier": { - "href": "/suppliers/5" - } - } - }, - { - "id": 5, - "name": "Product 5", - "price": 21, - "supplierId": 6, - "_links": { - "self": { - "href": "/products/5" - }, - "supplier": { - "href": "/suppliers/6" - } - } - }, - { - "id": 6, - "name": "Product 6", - "price": 29, - "supplierId": 7, - "_links": { - "self": { - "href": "/products/6" - }, - "supplier": { - "href": "/suppliers/7" - } - } - }, - { - "id": 7, - "name": "Product 7", - "price": 53, - "supplierId": 8, - "_links": { - "self": { - "href": "/products/7" - }, - "supplier": { - "href": "/suppliers/8" - } - } - }, - { - "id": 8, - "name": "Product 8", - "price": 41, - "supplierId": 9, - "_links": { - "self": { - "href": "/products/8" - }, - "supplier": { - "href": "/suppliers/9" - } - } - }, - { - "id": 9, - "name": "Product 9", - "price": 50, - "supplierId": 10, - "_links": { - "self": { - "href": "/products/9" - }, - "supplier": { - "href": "/suppliers/10" - } - } - }, - { - "id": 10, - "name": "Product 10", - "price": 93, - "supplierId": 1, - "_links": { - "self": { - "href": "/products/10" - }, - "supplier": { - "href": "/suppliers/1" - } - } - }, - { - "id": 11, - "name": "Product 11", - "price": 54, - "supplierId": 2, - "_links": { - "self": { - "href": "/products/11" - }, - "supplier": { - "href": "/suppliers/2" - } - } - }, - { - "id": 12, - "name": "Product 12", - "price": 15, - "supplierId": 3, - "_links": { - "self": { - "href": "/products/12" - }, - "supplier": { - "href": "/suppliers/3" - } - } - }, - { - "id": 13, - "name": "Product 13", - "price": 12, - "supplierId": 4, - "_links": { - "self": { - "href": "/products/13" - }, - "supplier": { - "href": "/suppliers/4" - } - } - }, - { - "id": 14, - "name": "Product 14", - "price": 82, - "supplierId": 5, - "_links": { - "self": { - "href": "/products/14" - }, - "supplier": { - "href": "/suppliers/5" - } - } - }, - { - "id": 15, - "name": "Product 15", - "price": 64, - "supplierId": 6, - "_links": { - "self": { - "href": "/products/15" - }, - "supplier": { - "href": "/suppliers/6" - } - } - }, - { - "id": 16, - "name": "Product 16", - "price": 53, - "supplierId": 7, - "_links": { - "self": { - "href": "/products/16" - }, - "supplier": { - "href": "/suppliers/7" - } - } - }, - { - "id": 17, - "name": "Product 17", - "price": 42, - "supplierId": 8, - "_links": { - "self": { - "href": "/products/17" - }, - "supplier": { - "href": "/suppliers/8" - } - } - }, - { - "id": 18, - "name": "Product 18", - "price": 7, - "supplierId": 9, - "_links": { - "self": { - "href": "/products/18" - }, - "supplier": { - "href": "/suppliers/9" - } - } - }, - { - "id": 19, - "name": "Product 19", - "price": 99, - "supplierId": 10, - "_links": { - "self": { - "href": "/products/19" - }, - "supplier": { - "href": "/suppliers/10" - } - } - }, - { - "id": 20, - "name": "Product 20", - "price": 49, - "supplierId": 1, - "_links": { - "self": { - "href": "/products/20" - }, - "supplier": { - "href": "/suppliers/1" - } - } - }, - { - "id": 21, - "name": "Product 21", - "price": 27, - "supplierId": 2, - "_links": { - "self": { - "href": "/products/21" - }, - "supplier": { - "href": "/suppliers/2" - } - } - }, - { - "id": 22, - "name": "Product 22", - "price": 4, - "supplierId": 3, - "_links": { - "self": { - "href": "/products/22" - }, - "supplier": { - "href": "/suppliers/3" - } - } - }, - { - "id": 23, - "name": "Product 23", - "price": 33, - "supplierId": 4, - "_links": { - "self": { - "href": "/products/23" - }, - "supplier": { - "href": "/suppliers/4" - } - } - }, - { - "id": 24, - "name": "Product 24", - "price": 14, - "supplierId": 5, - "_links": { - "self": { - "href": "/products/24" - }, - "supplier": { - "href": "/suppliers/5" - } - } - }, - { - "id": 25, - "name": "Product 25", - "price": 18, - "supplierId": 6, - "_links": { - "self": { - "href": "/products/25" - }, - "supplier": { - "href": "/suppliers/6" - } - } - }, - { - "id": 26, - "name": "Product 26", - "price": 1, - "supplierId": 7, - "_links": { - "self": { - "href": "/products/26" - }, - "supplier": { - "href": "/suppliers/7" - } - } - }, - { - "id": 27, - "name": "Product 27", - "price": 28, - "supplierId": 8, - "_links": { - "self": { - "href": "/products/27" - }, - "supplier": { - "href": "/suppliers/8" - } - } - }, - { - "id": 28, - "name": "Product 28", - "price": 55, - "supplierId": 9, - "_links": { - "self": { - "href": "/products/28" - }, - "supplier": { - "href": "/suppliers/9" - } - } - }, - { - "id": 29, - "name": "Product 29", - "price": 55, - "supplierId": 10, - "_links": { - "self": { - "href": "/products/29" - }, - "supplier": { - "href": "/suppliers/10" - } - } - }, - { - "id": 30, - "name": "Product 30", - "price": 33, - "supplierId": 1, - "_links": { - "self": { - "href": "/products/30" - }, - "supplier": { - "href": "/suppliers/1" - } - } - }, - { - "id": 31, - "name": "Product 31", - "price": 68, - "supplierId": 2, - "_links": { - "self": { - "href": "/products/31" - }, - "supplier": { - "href": "/suppliers/2" - } - } - }, - { - "id": 32, - "name": "Product 32", - "price": 14, - "supplierId": 3, - "_links": { - "self": { - "href": "/products/32" - }, - "supplier": { - "href": "/suppliers/3" - } - } - }, - { - "id": 33, - "name": "Product 33", - "price": 25, - "supplierId": 4, - "_links": { - "self": { - "href": "/products/33" - }, - "supplier": { - "href": "/suppliers/4" - } - } - }, - { - "id": 34, - "name": "Product 34", - "price": 12, - "supplierId": 5, - "_links": { - "self": { - "href": "/products/34" - }, - "supplier": { - "href": "/suppliers/5" - } - } - }, - { - "id": 35, - "name": "Product 35", - "price": 85, - "supplierId": 6, - "_links": { - "self": { - "href": "/products/35" - }, - "supplier": { - "href": "/suppliers/6" - } - } - }, - { - "id": 36, - "name": "Product 36", - "price": 54, - "supplierId": 7, - "_links": { - "self": { - "href": "/products/36" - }, - "supplier": { - "href": "/suppliers/7" - } - } - }, - { - "id": 37, - "name": "Product 37", - "price": 41, - "supplierId": 8, - "_links": { - "self": { - "href": "/products/37" - }, - "supplier": { - "href": "/suppliers/8" - } - } - }, - { - "id": 38, - "name": "Product 38", - "price": 85, - "supplierId": 9, - "_links": { - "self": { - "href": "/products/38" - }, - "supplier": { - "href": "/suppliers/9" - } - } - }, - { - "id": 39, - "name": "Product 39", - "price": 95, - "supplierId": 10, - "_links": { - "self": { - "href": "/products/39" - }, - "supplier": { - "href": "/suppliers/10" - } - } - }, - { - "id": 40, - "name": "Product 40", - "price": 37, - "supplierId": 1, - "_links": { - "self": { - "href": "/products/40" - }, - "supplier": { - "href": "/suppliers/1" - } - } - }, - { - "id": 41, - "name": "Product 41", - "price": 15, - "supplierId": 2, - "_links": { - "self": { - "href": "/products/41" - }, - "supplier": { - "href": "/suppliers/2" - } - } - }, - { - "id": 42, - "name": "Product 42", - "price": 38, - "supplierId": 3, - "_links": { - "self": { - "href": "/products/42" - }, - "supplier": { - "href": "/suppliers/3" - } - } - }, - { - "id": 43, - "name": "Product 43", - "price": 20, - "supplierId": 4, - "_links": { - "self": { - "href": "/products/43" - }, - "supplier": { - "href": "/suppliers/4" - } - } - }, - { - "id": 44, - "name": "Product 44", - "price": 77, - "supplierId": 5, - "_links": { - "self": { - "href": "/products/44" - }, - "supplier": { - "href": "/suppliers/5" - } - } - }, - { - "id": 45, - "name": "Product 45", - "price": 46, - "supplierId": 6, - "_links": { - "self": { - "href": "/products/45" - }, - "supplier": { - "href": "/suppliers/6" - } - } - }, - { - "id": 46, - "name": "Product 46", - "price": 92, - "supplierId": 7, - "_links": { - "self": { - "href": "/products/46" - }, - "supplier": { - "href": "/suppliers/7" - } - } - }, - { - "id": 47, - "name": "Product 47", - "price": 10, - "supplierId": 8, - "_links": { - "self": { - "href": "/products/47" - }, - "supplier": { - "href": "/suppliers/8" - } - } - }, - { - "id": 48, - "name": "Product 48", - "price": 56, - "supplierId": 9, - "_links": { - "self": { - "href": "/products/48" - }, - "supplier": { - "href": "/suppliers/9" - } - } - }, - { - "id": 49, - "name": "Product 49", - "price": 90, - "supplierId": 10, - "_links": { - "self": { - "href": "/products/49" - }, - "supplier": { - "href": "/suppliers/10" - } - } - }, - { - "id": 50, - "name": "Product 50", - "price": 93, - "supplierId": 1, - "_links": { - "self": { - "href": "/products/50" - }, - "supplier": { - "href": "/suppliers/1" - } - } - } - ] -} +{{#code}} +(() => { + const products = [] + for (let i = 1; i <= 50; i++) { + const supplierId = (i % 10) + 1 + products.push({ + id: i, + name: `Product ${i}`, + price: Math.floor(Math.random() * 100), + supplierId, + _links: { + self: { + href: `/products/${i}` + }, + supplier: { + href: `/suppliers/${supplierId}` + } + } + }) + } + + return { + status: 200, + body: JSON.stringify({ + "items": products + }, null, 2) + } +})(); +{{/code}} diff --git a/test/integration/mocks/products/__/GET.mock b/test/integration/mocks/products/__/GET.mock index 73f77cf..dec062d 100644 --- a/test/integration/mocks/products/__/GET.mock +++ b/test/integration/mocks/products/__/GET.mock @@ -6,7 +6,6 @@ Content-Type: application/json {{#code}} ((id) => { const supplierId = (parseInt(id) % 10) + 1 - logger.info(request.params[0].split('/').reverse()) return { status: 200, body: JSON.stringify({ diff --git a/test/integration/mocks/suppliers/GET.mock b/test/integration/mocks/suppliers/GET.mock index 051df82..56e6033 100644 --- a/test/integration/mocks/suppliers/GET.mock +++ b/test/integration/mocks/suppliers/GET.mock @@ -1,47 +1,21 @@ HTTP/1.1 200 OK Content-Type: application/json -{ - "items": [ - { - "id": 1, - "name": "Supplier 1" - }, - { - "id": 2, - "name": "Supplier 2" - }, - { - "id": 3, - "name": "Supplier 3" - }, - { - "id": 4, - "name": "Supplier 4" - }, - { - "id": 5, - "name": "Supplier 5" - }, - { - "id": 6, - "name": "Supplier 6" - }, - { - "id": 7, - "name": "Supplier 7" - }, - { - "id": 8, - "name": "Supplier 8" - }, - { - "id": 9, - "name": "Supplier 9" - }, - { - "id": 10, - "name": "Supplier 10" - } - ] -} +{{#code}} +(() => { + const suppliers = [] + for (let i = 1; i <= 10; i++) { + suppliers.push({ + id: i, + name: `Supplier ${i}` + }) + } + + return { + status: 200, + body: JSON.stringify({ + "items": suppliers + }, null, 2) + } +})(); +{{/code}} diff --git a/test/integration/tests/cases/directive-headers.test.ts b/test/integration/tests/cases/directive-headers.test.ts new file mode 100644 index 0000000..d3e6894 --- /dev/null +++ b/test/integration/tests/cases/directive-headers.test.ts @@ -0,0 +1,21 @@ +import { test, expect } from 'vitest' +import axios from 'axios' +import { url, headers } from '../config' + +/* SPL filter */ +const isAuthenticatedMutation = /* GraphQL */ ` + mutation isAuthenticated { + isAuthenticated @headers(input: [{ key: "Authorization", value: "Bearer token" }]) { + authenticate + } + } +` + +test('isAuthenticatedMutation: test directive @headers work properly', async () => { + const response = await axios.post(url, { query: isAuthenticatedMutation }, { headers }) + + const result = response.data + expect(response.status).toBe(200) + expect(result.errors).toBeUndefined() + expect(result.data.isAuthenticated.authenticate).toBe(true) +}) diff --git a/test/integration/tests/cases/directive-no-auth.test.ts b/test/integration/tests/cases/directive-no-auth.test.ts new file mode 100644 index 0000000..fd9b59e --- /dev/null +++ b/test/integration/tests/cases/directive-no-auth.test.ts @@ -0,0 +1,26 @@ +import { test, expect } from 'vitest' +import axios from 'axios' +import { url, headers } from '../config' + +/* SPL filter */ +const isAuthenticatedMutation = /* GraphQL */ ` + mutation isAuthenticated { + isAuthenticated @noAuth { + authenticate + } + } +` + +// Add Authorization header +const _headers = { + ...headers, + Authorization: 'Bearer token' +} +test('isAuthenticatedMutation: test directive @noAuth properly remove authorization header', async () => { + const response = await axios.post(url, { query: isAuthenticatedMutation }, { headers: _headers }) + + const result = response.data + expect(response.status).toBe(200) + expect(result.errors).toBeUndefined() + expect(result.data.isAuthenticated.authenticate).toBe(false) +}) diff --git a/test/integration/tests/cases/directive-spl.test.ts b/test/integration/tests/cases/directive-spl.test.ts index 27ac1c1..c6c62b2 100644 --- a/test/integration/tests/cases/directive-spl.test.ts +++ b/test/integration/tests/cases/directive-spl.test.ts @@ -16,7 +16,7 @@ const getTenFirstProductsQuery = /* GraphQL */ ` } ` -test('getTenFirstProducts with SPL filter query', async () => { +test('getTenFirstProductsQuery: test SPL filter inside query', async () => { const response = await axios.post(url, { query: getTenFirstProductsQuery }, { headers }) const result = response.data diff --git a/test/integration/tests/cases/graphql-mesh.test.ts b/test/integration/tests/cases/graphql-mesh.test.ts index 100f9c1..3e475be 100644 --- a/test/integration/tests/cases/graphql-mesh.test.ts +++ b/test/integration/tests/cases/graphql-mesh.test.ts @@ -16,7 +16,7 @@ const getAllProductsQuery = /* GraphQL */ ` } ` -test('getAllProducts query', async () => { +test('getAllProductsQuery: query working properly', async () => { const response = await axios.post(url, { query: getAllProductsQuery }, { headers }) const result = response.data @@ -42,7 +42,7 @@ const getProductAndSupplierInfo = /* GraphQL */ ` } ` -test('Follow hateoas link to get Suppier info', async () => { +test('getProductAndSupplierInfo: follow hateoas link', async () => { const response = await axios.post(url, { query: getProductAndSupplierInfo }, { headers }) const result = response.data @@ -64,7 +64,7 @@ const getProductwithLinkList = /* GraphQL */ ` } ` -test('Follow hateoas link to get Suppier info', async () => { +test('getProductAndSupplierInfo: Get "_linksList" attributes', async () => { const response = await axios.post(url, { query: getProductwithLinkList }, { headers }) const result = response.data diff --git a/test/integration/tests/cases/inject-additionnal-transforms.test.ts b/test/integration/tests/cases/inject-additionnal-transforms.test.ts index 48b904e..0617130 100644 --- a/test/integration/tests/cases/inject-additionnal-transforms.test.ts +++ b/test/integration/tests/cases/inject-additionnal-transforms.test.ts @@ -13,7 +13,7 @@ const getProductById = /* GraphQL */ ` } ` -test('Injection lower transform', async () => { +test('getProductById: @lower directive added throw transform', async () => { const response = await axios.post(url, { query: getProductById }, { headers }) const result = response.data diff --git a/test/integration/tests/cases/plugins.test.ts b/test/integration/tests/cases/plugins.test.ts index cdf870b..f2a756a 100644 --- a/test/integration/tests/cases/plugins.test.ts +++ b/test/integration/tests/cases/plugins.test.ts @@ -13,7 +13,7 @@ const getProductById = /* GraphQL */ ` } ` -test('Server timing plugin', async () => { +test('getProductById: Server timing plugin', async () => { const response = await axios.post(url, { query: getProductById }, { headers }) expect(response.data.errors).toBeUndefined() From 99530a4becb135388101eadd851d914f8f4fdf0f Mon Sep 17 00:00:00 2001 From: Mbaye THIAM Date: Wed, 17 Apr 2024 14:45:22 +0200 Subject: [PATCH 8/8] =?UTF-8?q?=F0=9F=91=B7=20wait=2030s=20for=20services?= =?UTF-8?q?=20to=20be=20ready=20before=20running=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/integration-tests.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 0826789..be0bac4 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -45,6 +45,9 @@ jobs: - name: Setup services for testing purpose run: export IMAGE_TAG=localhost:5000/test/graphql-mesh:latest && cd ./test/integration && docker compose up -d + - name: Wait for services to be ready + run: sleep 30 + - name: Set up Node.js uses: actions/setup-node@v3 with: