diff --git a/.changeset/chubby-hats-hope.md b/.changeset/chubby-hats-hope.md new file mode 100644 index 000000000000..0398329f03bd --- /dev/null +++ b/.changeset/chubby-hats-hope.md @@ -0,0 +1,7 @@ +--- +"@biomejs/biome": patch +--- + +Fixed an issue where Biome got stuck when analyzing some files. This is usually caused by a bug in the inference engine. Now Biome has some guards in place in case the number of types grows too much, and if that happens, a diagnostic is emitted and the inference is halted. + + diff --git a/.changeset/growing-gorillas-grunt.md b/.changeset/growing-gorillas-grunt.md new file mode 100644 index 000000000000..4d762068ef84 --- /dev/null +++ b/.changeset/growing-gorillas-grunt.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": patch +--- + +Fixed [#7020](https://github.com/biomejs/biome/issues/7020): Resolved an issue with analysing types of static member expressions involving unions. If the object type was a union that referenced nested unions, it would trigger an infinite loop as it tried to keep expanding nested unions, and the set of types would grow indefinitely. diff --git a/Cargo.lock b/Cargo.lock index ea46e03fb290..4aa1e0ede5e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1249,7 +1249,9 @@ dependencies = [ name = "biome_module_graph" version = "0.0.1" dependencies = [ + "biome_console", "biome_deserialize", + "biome_diagnostics", "biome_formatter", "biome_fs", "biome_js_formatter", diff --git a/crates/biome_cli/src/commands/mod.rs b/crates/biome_cli/src/commands/mod.rs index a2cdea067298..442944b119db 100644 --- a/crates/biome_cli/src/commands/mod.rs +++ b/crates/biome_cli/src/commands/mod.rs @@ -689,9 +689,11 @@ pub(crate) fn print_diagnostics_from_workspace_result( verbose: bool, ) -> Result<(), CliDiagnostic> { let mut has_errors = false; + let mut has_internal = false; for diagnostic in diagnostics { - if diagnostic.severity() >= Severity::Error { - has_errors = true; + has_errors = has_errors || diagnostic.severity() >= Severity::Error; + has_internal = has_internal || diagnostic.tags().is_internal(); + if has_internal || has_errors { if diagnostic.tags().is_verbose() && verbose { console.error(markup! {{PrintDiagnostic::verbose(diagnostic)}}) } else { diff --git a/crates/biome_diagnostics/src/diagnostic.rs b/crates/biome_diagnostics/src/diagnostic.rs index 894d595ce82a..e8e16239ce79 100644 --- a/crates/biome_diagnostics/src/diagnostic.rs +++ b/crates/biome_diagnostics/src/diagnostic.rs @@ -225,6 +225,9 @@ impl DiagnosticTags { pub fn is_verbose(&self) -> bool { self.contains(DiagnosticTag::Verbose) } + pub fn is_internal(&self) -> bool { + self.contains(DiagnosticTag::Internal) + } } impl BitOr for DiagnosticTags { diff --git a/crates/biome_js_analyze/src/lint/nursery/use_exhaustive_switch_cases.rs b/crates/biome_js_analyze/src/lint/nursery/use_exhaustive_switch_cases.rs index 0f5f6278891f..fcdb6fb2c187 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_exhaustive_switch_cases.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_exhaustive_switch_cases.rs @@ -136,36 +136,21 @@ impl Rule for UseExhaustiveSwitchCases { let discriminant = stmt.discriminant().ok()?; let discriminant_ty = flatten_type(&ctx.type_of_expression(&discriminant))?; - for union_part in match discriminant_ty.deref() { - TypeData::Union(union) => union - .types() - .iter() - .filter_map(|r| discriminant_ty.resolve(r)) - .collect(), - _ => vec![discriminant_ty], + for intersection_part in match discriminant_ty.is_union() { + true => discriminant_ty.flattened_union_variants().collect(), + false => vec![discriminant_ty], } { - let union_part = flatten_type(&union_part)?; - - for intersection_part in match union_part.deref() { - TypeData::Intersection(intersection) => intersection - .types() - .iter() - .filter_map(|r| union_part.resolve(r)) - .collect(), - _ => vec![union_part], - } { - let intersection_part = flatten_type(&intersection_part)?; - - if !matches!( - intersection_part.deref(), - TypeData::Literal(_) | TypeData::Null | TypeData::Undefined | TypeData::Symbol - ) || found_cases.contains(&intersection_part) - { - continue; - } - - missing_cases.push(intersection_part); + let intersection_part = flatten_type(&intersection_part)?; + + if !matches!( + intersection_part.deref(), + TypeData::Literal(_) | TypeData::Null | TypeData::Undefined | TypeData::Symbol + ) || found_cases.contains(&intersection_part) + { + continue; } + + missing_cases.push(intersection_part); } if missing_cases.is_empty() { diff --git a/crates/biome_js_type_info/src/conditionals.rs b/crates/biome_js_type_info/src/conditionals.rs index 3ee8a666b64a..a96587900e71 100644 --- a/crates/biome_js_type_info/src/conditionals.rs +++ b/crates/biome_js_type_info/src/conditionals.rs @@ -103,10 +103,10 @@ impl ConditionalType { } } TypeData::Reference(reference) => derive_from_reference(reference), - TypeData::Union(union) => { + TypeData::Union(_) => { let mut conditional = ConditionalType::Unknown; - for ty in union.types() { - let next = derive_from_reference(ty); + for ty in ty.flattened_union_variants(resolver) { + let next = derive_from_reference(&ty); conditional = if conditional == ConditionalType::Unknown { next } else { @@ -413,9 +413,9 @@ fn to_filtered_value( } } TypeData::Reference(reference) => reference_to_non_nullish_value(reference), - TypeData::Union(union) => { - let types = union - .types() + TypeData::Union(_) => { + let types: Vec<_> = resolved.flattened_union_variants(resolver).collect(); + let types = types .iter() .filter_map(|reference| { let ty = resolver diff --git a/crates/biome_js_type_info/src/flattening.rs b/crates/biome_js_type_info/src/flattening.rs index a765bbe6a709..535b6a2017f1 100644 --- a/crates/biome_js_type_info/src/flattening.rs +++ b/crates/biome_js_type_info/src/flattening.rs @@ -2,6 +2,7 @@ use crate::{TypeData, TypeInstance, TypeReference, TypeResolver}; mod expressions; mod intersections; +mod unions; use expressions::flattened_expression; use intersections::flattened_intersection; diff --git a/crates/biome_js_type_info/src/flattening/expressions.rs b/crates/biome_js_type_info/src/flattening/expressions.rs index dee8443b2527..3bdded9c163b 100644 --- a/crates/biome_js_type_info/src/flattening/expressions.rs +++ b/crates/biome_js_type_info/src/flattening/expressions.rs @@ -331,20 +331,14 @@ pub(super) fn flattened_expression( Some(TypeData::reference(member.ty().into_owned())) } - TypeData::Union(union) => { - let types: Vec<_> = union - .types() - .iter() - .map(|reference| object.apply_module_id_to_reference(reference)) - .map(|reference| reference.into_owned()) + TypeData::Union(_) => { + let types: Vec<_> = object + .flattened_union_variants(resolver) + .filter(|variant| *variant != GLOBAL_UNDEFINED_ID.into()) .collect(); let types = types .into_iter() - .filter_map(|variant| { - if variant == GLOBAL_UNDEFINED_ID.into() { - return None; - } - + .map(|variant| { // Resolve and flatten the type member for each variant. let variant = TypeData::TypeofExpression(Box::new( TypeofExpression::StaticMember(TypeofStaticMemberExpression { @@ -353,7 +347,7 @@ pub(super) fn flattened_expression( }), )); - Some(resolver.reference_to_owned_data(variant)) + resolver.reference_to_owned_data(variant) }) .collect(); diff --git a/crates/biome_js_type_info/src/flattening/unions.rs b/crates/biome_js_type_info/src/flattening/unions.rs new file mode 100644 index 000000000000..d01831be08c9 --- /dev/null +++ b/crates/biome_js_type_info/src/flattening/unions.rs @@ -0,0 +1,97 @@ +use rustc_hash::FxHashSet; + +use crate::{ResolvedTypeData, TypeData, TypeReference, TypeResolver}; + +impl TypeData { + /// Returns an iterator over the variants of this type, while deduplicating + /// variants and flattening nested unions in the process. + /// + /// Returns an iterator that yields no elements if the type is not a union. + pub fn flattened_union_variants( + &self, + resolver: &dyn TypeResolver, + ) -> impl Iterator { + if let Self::Union(union) = self { + UnionVariantIterator::new(union.types().to_vec(), resolver) + } else { + UnionVariantIterator::new_empty(resolver) + } + } +} + +impl ResolvedTypeData<'_> { + /// Returns an iterator over the variants of this resolved union, while + /// deduplicating variants and flattening nested unions in the process. + /// + /// Returns an iterator that yields no elements if the resolved data is not + /// a union. + pub fn flattened_union_variants( + self, + resolver: &dyn TypeResolver, + ) -> impl Iterator { + if let TypeData::Union(union) = self.to_data() { + UnionVariantIterator::new(union.into_types(), resolver) + } else { + UnionVariantIterator::new_empty(resolver) + } + } +} + +struct UnionVariantIterator<'a> { + resolver: &'a dyn TypeResolver, + unions: Vec>, + previous_references: FxHashSet, +} + +impl<'a> UnionVariantIterator<'a> { + fn new(mut variants: Vec, resolver: &'a dyn TypeResolver) -> Self { + // Reverse in-place so we can efficiently pop them later. + variants.reverse(); + + Self { + resolver, + unions: vec![variants], + previous_references: Default::default(), + } + } + + fn new_empty(resolver: &'a dyn TypeResolver) -> Self { + Self { + resolver, + unions: Vec::new(), + previous_references: Default::default(), + } + } +} + +impl<'a> Iterator for UnionVariantIterator<'a> { + type Item = TypeReference; + + fn next(&mut self) -> Option { + let mut union = self.unions.last_mut()?; + loop { + let reference = match union.pop() { + Some(reference) => reference, + None => { + self.unions.pop(); + union = self.unions.last_mut()?; + continue; + } + }; + + if self.previous_references.insert(reference.clone()) { + if let Some(ty) = self.resolver.resolve_and_get(&reference) + && ty.is_union() + && let TypeData::Union(nested) = ty.to_data() + { + self.unions + .push(nested.types().iter().rev().cloned().collect()); + union = self.unions.last_mut()?; + continue; + } + + return Some(reference); + } + } + } +} diff --git a/crates/biome_js_type_info/src/helpers.rs b/crates/biome_js_type_info/src/helpers.rs index b3e52189a243..420a91d07236 100644 --- a/crates/biome_js_type_info/src/helpers.rs +++ b/crates/biome_js_type_info/src/helpers.rs @@ -255,7 +255,7 @@ impl TypeData { /// remains, an instance of `Self::Reference` is returned instead of /// `Self::Union`. pub fn union_of(resolver: &dyn TypeResolver, types: Box<[TypeReference]>) -> Self { - // We use a hash table separately of a vector to quickly check for + // We use a hash table in addition to a vector to quickly check for // duplicates, without messing with the original order. let mut table: HashTable = HashTable::with_capacity(types.len()); let mut vec = Vec::with_capacity(types.len()); @@ -270,18 +270,17 @@ impl TypeData { // No point in adding `never` to the union. continue; } - Self::Union(union) => { + Self::Union(_) => { // Flatten existing union into the new one: - for ty in union.types() { - let ty = resolved.apply_module_id_to_reference(ty); + for ty in resolved.flattened_union_variants(resolver) { let entry = table.entry( hash_reference(&ty), - |i| &vec[*i] == ty.as_ref(), + |i| vec[*i] == ty, |i| hash_reference(&vec[*i]), ); if let Entry::Vacant(entry) = entry { let index = vec.len(); - vec.push(ty.into_owned()); + vec.push(ty); entry.insert(index); } } @@ -584,5 +583,6 @@ generate_matcher!(is_interface, Interface, _); generate_matcher!(is_null, Null); generate_matcher!(is_reference, Reference, _); generate_matcher!(is_never_keyword, NeverKeyword); +generate_matcher!(is_union, Union, _); generate_matcher!(is_unknown_keyword, UnknownKeyword); generate_matcher!(is_void_keyword, VoidKeyword); diff --git a/crates/biome_js_type_info/src/type.rs b/crates/biome_js_type_info/src/type.rs index 950e8fea53e9..11f8a1493110 100644 --- a/crates/biome_js_type_info/src/type.rs +++ b/crates/biome_js_type_info/src/type.rs @@ -86,18 +86,30 @@ impl Type { }) } + /// Returns an iterator over the variants of this type, while deduplicating + /// variants and flattening nested unions in the process. + /// + /// Returns an iterator that yields no elements if the type is not a union. + pub fn flattened_union_variants(&self) -> impl Iterator { + self.resolved_data() + .unwrap_or_else(|| ResolvedTypeData::from((GLOBAL_UNKNOWN_ID, &UNKNOWN_DATA))) + .flattened_union_variants(self.resolver.as_ref()) + .filter_map(|ty| { + self.resolver + .resolve_reference(&ty) + .map(|resolved_id| self.with_resolved_id(resolved_id)) + }) + } + /// Returns `true` if this type represents a **union type** that has a /// variant for which the given `predicate` returns `true`. /// /// Returns `false` otherwise. pub fn has_variant(&self, predicate: impl Fn(Self) -> bool) -> bool { - match self.as_raw_data() { - Some(TypeData::Union(union)) => union - .types() - .iter() - .filter_map(|ty| self.resolve(ty)) - .any(predicate), - _ => false, + if self.is_union() { + self.flattened_union_variants().any(predicate) + } else { + false } } diff --git a/crates/biome_js_type_info/src/type_data.rs b/crates/biome_js_type_info/src/type_data.rs index 010e24487366..ba8a09455a15 100644 --- a/crates/biome_js_type_info/src/type_data.rs +++ b/crates/biome_js_type_info/src/type_data.rs @@ -1479,6 +1479,10 @@ impl Union { self.0.contains(ty) } + pub fn into_types(self) -> Vec { + self.0.into_vec() + } + pub fn types(&self) -> &[TypeReference] { &self.0 } diff --git a/crates/biome_lsp/src/server.tests.rs b/crates/biome_lsp/src/server.tests.rs index 64a7ae9825c3..a730c8ce2e0c 100644 --- a/crates/biome_lsp/src/server.tests.rs +++ b/crates/biome_lsp/src/server.tests.rs @@ -13,8 +13,9 @@ use biome_fs::{BiomePath, MemoryFileSystem, TemporaryFs}; use biome_service::Watcher; use biome_service::workspace::{ FileContent, GetFileContentParams, GetModuleGraphParams, GetModuleGraphResult, - GetSyntaxTreeParams, GetSyntaxTreeResult, OpenFileParams, OpenProjectParams, OpenProjectResult, - PullDiagnosticsParams, PullDiagnosticsResult, ScanKind, ScanProjectParams, ScanProjectResult, + GetSyntaxTreeParams, GetSyntaxTreeResult, OpenFileParams, OpenFileResult, OpenProjectParams, + OpenProjectResult, PullDiagnosticsParams, PullDiagnosticsResult, ScanKind, ScanProjectParams, + ScanProjectResult, }; use camino::Utf8PathBuf; use futures::channel::mpsc::{Sender, channel}; @@ -3439,7 +3440,7 @@ export function bar() { .expect("scan_project returned an error"); assert_eq!(result.diagnostics.len(), 0); - let _: () = server + let _: OpenFileResult = server .request( "biome/open_file", "open_file", @@ -3649,7 +3650,7 @@ export function bar() { .expect("scan_project returned an error"); assert_eq!(result.diagnostics.len(), 0); - let _: () = server + let _: OpenFileResult = server .request( "biome/open_file", "open_file", diff --git a/crates/biome_module_graph/Cargo.toml b/crates/biome_module_graph/Cargo.toml index d92f5f05a153..0d46d3ae147b 100644 --- a/crates/biome_module_graph/Cargo.toml +++ b/crates/biome_module_graph/Cargo.toml @@ -14,6 +14,8 @@ version = "0.0.1" workspace = true [dependencies] +biome_console = { workspace = true } +biome_diagnostics = { workspace = true } biome_formatter = { workspace = true } biome_fs = { workspace = true } biome_js_semantic = { workspace = true } diff --git a/crates/biome_module_graph/benches/astro_server.mjs b/crates/biome_module_graph/benches/astro_server.mjs new file mode 100644 index 000000000000..3d380ed4d3de --- /dev/null +++ b/crates/biome_module_graph/benches/astro_server.mjs @@ -0,0 +1,7509 @@ +const ASTRO_VERSION = "5.9.4"; +const REROUTE_DIRECTIVE_HEADER = "X-Astro-Reroute"; +const REWRITE_DIRECTIVE_HEADER_KEY = "X-Astro-Rewrite"; +const REWRITE_DIRECTIVE_HEADER_VALUE = "yes"; +const NOOP_MIDDLEWARE_HEADER = "X-Astro-Noop"; +const ROUTE_TYPE_HEADER = "X-Astro-Route-Type"; +const DEFAULT_404_COMPONENT = "astro-default-404.astro"; +const REDIRECT_STATUS_CODES = [301, 302, 303, 307, 308, 300, 304]; +const REROUTABLE_STATUS_CODES = [404, 500]; +const clientAddressSymbol = Symbol.for("astro.clientAddress"); +const originPathnameSymbol = Symbol.for("astro.originPathname"); +const responseSentSymbol = Symbol.for("astro.responseSent"); + +const ClientAddressNotAvailable = { + name: "ClientAddressNotAvailable", + title: "`Astro.clientAddress` is not available in current adapter.", + message: (adapterName) => `\`Astro.clientAddress\` is not available in the \`${adapterName}\` adapter. File an issue with the adapter to add support.` +}; +const PrerenderClientAddressNotAvailable = { + name: "PrerenderClientAddressNotAvailable", + title: "`Astro.clientAddress` cannot be used inside prerendered routes.", + message: (name) => `\`Astro.clientAddress\` cannot be used inside prerendered route ${name}` +}; +const StaticClientAddressNotAvailable = { + name: "StaticClientAddressNotAvailable", + title: "`Astro.clientAddress` is not available in prerendered pages.", + message: "`Astro.clientAddress` is only available on pages that are server-rendered.", + hint: "See https://docs.astro.build/en/guides/on-demand-rendering/ for more information on how to enable SSR." +}; +const NoMatchingStaticPathFound = { + name: "NoMatchingStaticPathFound", + title: "No static path found for requested path.", + message: (pathName) => `A \`getStaticPaths()\` route pattern was matched, but no matching static path was found for requested path \`${pathName}\`.`, + hint: (possibleRoutes) => `Possible dynamic routes being matched: ${possibleRoutes.join(", ")}.` +}; +const OnlyResponseCanBeReturned = { + name: "OnlyResponseCanBeReturned", + title: "Invalid type returned by Astro page.", + message: (route, returnedValue) => `Route \`${route ? route : ""}\` returned a \`${returnedValue}\`. Only a [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) can be returned from Astro files.`, + hint: "See https://docs.astro.build/en/guides/on-demand-rendering/#response for more information." +}; +const MissingMediaQueryDirective = { + name: "MissingMediaQueryDirective", + title: "Missing value for `client:media` directive.", + message: 'Media query not provided for `client:media` directive. A media query similar to `client:media="(max-width: 600px)"` must be provided' +}; +const NoMatchingRenderer = { + name: "NoMatchingRenderer", + title: "No matching renderer found.", + message: (componentName, componentExtension, plural, validRenderersCount) => `Unable to render \`${componentName}\`. + +${validRenderersCount > 0 ? `There ${plural ? "are" : "is"} ${validRenderersCount} renderer${plural ? "s" : ""} configured in your \`astro.config.mjs\` file, +but ${plural ? "none were" : "it was not"} able to server-side render \`${componentName}\`.` : `No valid renderer was found ${componentExtension ? `for the \`.${componentExtension}\` file extension.` : `for this file extension.`}`}`, + hint: (probableRenderers) => `Did you mean to enable the ${probableRenderers} integration? + +See https://docs.astro.build/en/guides/framework-components/ for more information on how to install and configure integrations.` +}; +const NoClientOnlyHint = { + name: "NoClientOnlyHint", + title: "Missing hint on client:only directive.", + message: (componentName) => `Unable to render \`${componentName}\`. When using the \`client:only\` hydration strategy, Astro needs a hint to use the correct renderer.`, + hint: (probableRenderers) => `Did you mean to pass \`client:only="${probableRenderers}"\`? See https://docs.astro.build/en/reference/directives-reference/#clientonly for more information on client:only` +}; +const InvalidGetStaticPathsEntry = { + name: "InvalidGetStaticPathsEntry", + title: "Invalid entry inside getStaticPath's return value", + message: (entryType) => `Invalid entry returned by getStaticPaths. Expected an object, got \`${entryType}\``, + hint: "If you're using a `.map` call, you might be looking for `.flatMap()` instead. See https://docs.astro.build/en/reference/routing-reference/#getstaticpaths for more information on getStaticPaths." +}; +const InvalidGetStaticPathsReturn = { + name: "InvalidGetStaticPathsReturn", + title: "Invalid value returned by getStaticPaths.", + message: (returnType) => `Invalid type returned by \`getStaticPaths\`. Expected an \`array\`, got \`${returnType}\``, + hint: "See https://docs.astro.build/en/reference/routing-reference/#getstaticpaths for more information on getStaticPaths." +}; +const GetStaticPathsExpectedParams = { + name: "GetStaticPathsExpectedParams", + title: "Missing params property on `getStaticPaths` route.", + message: "Missing or empty required `params` property on `getStaticPaths` route.", + hint: "See https://docs.astro.build/en/reference/routing-reference/#getstaticpaths for more information on getStaticPaths." +}; +const GetStaticPathsInvalidRouteParam = { + name: "GetStaticPathsInvalidRouteParam", + title: "Invalid value for `getStaticPaths` route parameter.", + message: (key, value, valueType) => `Invalid getStaticPaths route parameter for \`${key}\`. Expected undefined, a string or a number, received \`${valueType}\` (\`${value}\`)`, + hint: "See https://docs.astro.build/en/reference/routing-reference/#getstaticpaths for more information on getStaticPaths." +}; +const GetStaticPathsRequired = { + name: "GetStaticPathsRequired", + title: "`getStaticPaths()` function required for dynamic routes.", + message: "`getStaticPaths()` function is required for dynamic routes. Make sure that you `export` a `getStaticPaths` function from your dynamic route.", + hint: `See https://docs.astro.build/en/guides/routing/#dynamic-routes for more information on dynamic routes. + + If you meant for this route to be server-rendered, set \`export const prerender = false;\` in the page.` +}; +const ReservedSlotName = { + name: "ReservedSlotName", + title: "Invalid slot name.", + message: (slotName) => `Unable to create a slot named \`${slotName}\`. \`${slotName}\` is a reserved slot name. Please update the name of this slot.` +}; +const NoMatchingImport = { + name: "NoMatchingImport", + title: "No import found for component.", + message: (componentName) => `Could not render \`${componentName}\`. No matching import has been found for \`${componentName}\`.`, + hint: "Please make sure the component is properly imported." +}; +const InvalidComponentArgs = { + name: "InvalidComponentArgs", + title: "Invalid component arguments.", + message: (name) => `Invalid arguments passed to${name ? ` <${name}>` : ""} component.`, + hint: "Astro components cannot be rendered directly via function call, such as `Component()` or `{items.map(Component)}`." +}; +const PageNumberParamNotFound = { + name: "PageNumberParamNotFound", + title: "Page number param not found.", + message: (paramName) => `[paginate()] page number param \`${paramName}\` not found in your filepath.`, + hint: "Rename your file to `[page].astro` or `[...page].astro`." +}; +const ImageMissingAlt = { + name: "ImageMissingAlt", + title: 'Image missing required "alt" property.', + message: 'Image missing "alt" property. "alt" text is required to describe important images on the page.', + hint: 'Use an empty string ("") for decorative images.' +}; +const InvalidImageService = { + name: "InvalidImageService", + title: "Error while loading image service.", + message: "There was an error loading the configured image service. Please see the stack trace for more information." +}; +const MissingImageDimension = { + name: "MissingImageDimension", + title: "Missing image dimensions", + message: (missingDimension, imageURL) => `Missing ${missingDimension === "both" ? "width and height attributes" : `${missingDimension} attribute`} for ${imageURL}. When using remote images, both dimensions are required in order to avoid CLS.`, + hint: "If your image is inside your `src` folder, you probably meant to import it instead. See [the Imports guide for more information](https://docs.astro.build/en/guides/imports/#other-assets). You can also use `inferSize={true}` for remote images to get the original dimensions." +}; +const FailedToFetchRemoteImageDimensions = { + name: "FailedToFetchRemoteImageDimensions", + title: "Failed to retrieve remote image dimensions", + message: (imageURL) => `Failed to get the dimensions for ${imageURL}.`, + hint: "Verify your remote image URL is accurate, and that you are not using `inferSize` with a file located in your `public/` folder." +}; +const UnsupportedImageFormat = { + name: "UnsupportedImageFormat", + title: "Unsupported image format", + message: (format, imagePath, supportedFormats) => `Received unsupported format \`${format}\` from \`${imagePath}\`. Currently only ${supportedFormats.join( + ", " + )} are supported by our image services.`, + hint: "Using an `img` tag directly instead of the `Image` component might be what you're looking for." +}; +const UnsupportedImageConversion = { + name: "UnsupportedImageConversion", + title: "Unsupported image conversion", + message: "Converting between vector (such as SVGs) and raster (such as PNGs and JPEGs) images is not currently supported." +}; +const PrerenderDynamicEndpointPathCollide = { + name: "PrerenderDynamicEndpointPathCollide", + title: "Prerendered dynamic endpoint has path collision.", + message: (pathname) => `Could not render \`${pathname}\` with an \`undefined\` param as the generated path will collide during prerendering. Prevent passing \`undefined\` as \`params\` for the endpoint's \`getStaticPaths()\` function, or add an additional extension to the endpoint's filename.`, + hint: (filename) => `Rename \`${filename}\` to \`${filename.replace(/\.(?:js|ts)/, (m) => `.json` + m)}\`` +}; +const ExpectedImage = { + name: "ExpectedImage", + title: "Expected src to be an image.", + message: (src, typeofOptions, fullOptions) => `Expected \`src\` property for \`getImage\` or \`\` to be either an ESM imported image or a string with the path of a remote image. Received \`${src}\` (type: \`${typeofOptions}\`). + +Full serialized options received: \`${fullOptions}\`.`, + hint: "This error can often happen because of a wrong path. Make sure the path to your image is correct. If you're passing an async function, make sure to call and await it." +}; +const ExpectedImageOptions = { + name: "ExpectedImageOptions", + title: "Expected image options.", + message: (options) => `Expected getImage() parameter to be an object. Received \`${options}\`.` +}; +const ExpectedNotESMImage = { + name: "ExpectedNotESMImage", + title: "Expected image options, not an ESM-imported image.", + message: "An ESM-imported image cannot be passed directly to `getImage()`. Instead, pass an object with the image in the `src` property.", + hint: "Try changing `getImage(myImage)` to `getImage({ src: myImage })`" +}; +const IncompatibleDescriptorOptions = { + name: "IncompatibleDescriptorOptions", + title: "Cannot set both `densities` and `widths`", + message: "Only one of `densities` or `widths` can be specified. In most cases, you'll probably want to use only `widths` if you require specific widths.", + hint: "Those attributes are used to construct a `srcset` attribute, which cannot have both `x` and `w` descriptors." +}; +const NoImageMetadata = { + name: "NoImageMetadata", + title: "Could not process image metadata.", + message: (imagePath) => `Could not process image metadata${imagePath ? ` for \`${imagePath}\`` : ""}.`, + hint: "This is often caused by a corrupted or malformed image. Re-exporting the image from your image editor may fix this issue." +}; +const ResponseSentError = { + name: "ResponseSentError", + title: "Unable to set response.", + message: "The response has already been sent to the browser and cannot be altered." +}; +const MiddlewareNoDataOrNextCalled = { + name: "MiddlewareNoDataOrNextCalled", + title: "The middleware didn't return a `Response`.", + message: "Make sure your middleware returns a `Response` object, either directly or by returning the `Response` from calling the `next` function." +}; +const MiddlewareNotAResponse = { + name: "MiddlewareNotAResponse", + title: "The middleware returned something that is not a `Response` object.", + message: "Any data returned from middleware must be a valid `Response` object." +}; +const EndpointDidNotReturnAResponse = { + name: "EndpointDidNotReturnAResponse", + title: "The endpoint did not return a `Response`.", + message: "An endpoint must return either a `Response`, or a `Promise` that resolves with a `Response`." +}; +const LocalsNotAnObject = { + name: "LocalsNotAnObject", + title: "Value assigned to `locals` is not accepted.", + message: "`locals` can only be assigned to an object. Other values like numbers, strings, etc. are not accepted.", + hint: "If you tried to remove some information from the `locals` object, try to use `delete` or set the property to `undefined`." +}; +const LocalsReassigned = { + name: "LocalsReassigned", + title: "`locals` must not be reassigned.", + message: "`locals` can not be assigned directly.", + hint: "Set a `locals` property instead." +}; +const AstroResponseHeadersReassigned = { + name: "AstroResponseHeadersReassigned", + title: "`Astro.response.headers` must not be reassigned.", + message: "Individual headers can be added to and removed from `Astro.response.headers`, but it must not be replaced with another instance of `Headers` altogether.", + hint: "Consider using `Astro.response.headers.add()`, and `Astro.response.headers.delete()`." +}; +const LocalImageUsedWrongly = { + name: "LocalImageUsedWrongly", + title: "Local images must be imported.", + message: (imageFilePath) => `\`Image\`'s and \`getImage\`'s \`src\` parameter must be an imported image or an URL, it cannot be a string filepath. Received \`${imageFilePath}\`.`, + hint: "If you want to use an image from your `src` folder, you need to either import it or if the image is coming from a content collection, use the [image() schema helper](https://docs.astro.build/en/guides/images/#images-in-content-collections). See https://docs.astro.build/en/guides/images/#src-required for more information on the `src` property." +}; +const AstroGlobUsedOutside = { + name: "AstroGlobUsedOutside", + title: "Astro.glob() used outside of an Astro file.", + message: (globStr) => `\`Astro.glob(${globStr})\` can only be used in \`.astro\` files. \`import.meta.glob(${globStr})\` can be used instead to achieve a similar result.`, + hint: "See Vite's documentation on `import.meta.glob` for more information: https://vite.dev/guide/features.html#glob-import" +}; +const AstroGlobNoMatch = { + name: "AstroGlobNoMatch", + title: "Astro.glob() did not match any files.", + message: (globStr) => `\`Astro.glob(${globStr})\` did not return any matching files.`, + hint: "Check the pattern for typos." +}; +const MissingSharp = { + name: "MissingSharp", + title: "Could not find Sharp.", + message: "Could not find Sharp. Please install Sharp (`sharp`) manually into your project or migrate to another image service.", + hint: "See Sharp's installation instructions for more information: https://sharp.pixelplumbing.com/install. If you are not relying on `astro:assets` to optimize, transform, or process any images, you can configure a passthrough image service instead of installing Sharp. See https://docs.astro.build/en/reference/errors/missing-sharp for more information.\n\nSee https://docs.astro.build/en/guides/images/#default-image-service for more information on how to migrate to another image service." +}; +const i18nNoLocaleFoundInPath = { + name: "i18nNoLocaleFoundInPath", + title: "The path doesn't contain any locale", + message: "You tried to use an i18n utility on a path that doesn't contain any locale. You can use `pathHasLocale` first to determine if the path has a locale." +}; +const RewriteWithBodyUsed = { + name: "RewriteWithBodyUsed", + title: "Cannot use Astro.rewrite after the request body has been read", + message: "Astro.rewrite() cannot be used if the request body has already been read. If you need to read the body, first clone the request." +}; +const ForbiddenRewrite = { + name: "ForbiddenRewrite", + title: "Forbidden rewrite to a static route.", + message: (from, to, component) => `You tried to rewrite the on-demand route '${from}' with the static route '${to}', when using the 'server' output. + +The static route '${to}' is rendered by the component +'${component}', which is marked as prerendered. This is a forbidden operation because during the build the component '${component}' is compiled to an +HTML file, which can't be retrieved at runtime by Astro.`, + hint: (component) => `Add \`export const prerender = false\` to the component '${component}', or use a Astro.redirect().` +}; +const ExperimentalFontsNotEnabled = { + name: "ExperimentalFontsNotEnabled", + title: "Experimental fonts are not enabled", + message: "The Font component is used but experimental fonts have not been registered in the config.", + hint: "Check that you have enabled experimental fonts and also configured your preferred fonts." +}; +const FontFamilyNotFound = { + name: "FontFamilyNotFound", + title: "Font family not found", + message: (family) => `No data was found for the \`"${family}"\` family passed to the \`\` component.`, + hint: "This is often caused by a typo. Check that your Font component is using a `cssVariable` specified in your config." +}; +const CspNotEnabled = { + name: "CspNotEnabled", + title: "CSP feature isn't enabled", + message: "The `experimental.csp` configuration isn't enabled." +}; +const ActionsReturnedInvalidDataError = { + name: "ActionsReturnedInvalidDataError", + title: "Action handler returned invalid data.", + message: (error) => `Action handler returned invalid data. Handlers should return serializable data types like objects, arrays, strings, and numbers. Parse error: ${error}`, + hint: "See the devalue library for all supported types: https://github.com/rich-harris/devalue" +}; +const ActionNotFoundError = { + name: "ActionNotFoundError", + title: "Action not found.", + message: (actionName) => `The server received a request for an action named \`${actionName}\` but could not find a match. If you renamed an action, check that you've updated your \`actions/index\` file and your calling code to match.`, + hint: "You can run `astro check` to detect type errors caused by mismatched action names." +}; +const SessionStorageInitError = { + name: "SessionStorageInitError", + title: "Session storage could not be initialized.", + message: (error, driver) => `Error when initializing session storage${driver ? ` with driver \`${driver}\`` : ""}. \`${error ?? ""}\``, + hint: "For more information, see https://docs.astro.build/en/guides/sessions/" +}; +const SessionStorageSaveError = { + name: "SessionStorageSaveError", + title: "Session data could not be saved.", + message: (error, driver) => `Error when saving session data${driver ? ` with driver \`${driver}\`` : ""}. \`${error ?? ""}\``, + hint: "For more information, see https://docs.astro.build/en/guides/sessions/" +}; + +function normalizeLF(code) { + return code.replace(/\r\n|\r(?!\n)|\n/g, "\n"); +} + +function codeFrame(src, loc) { + if (!loc || loc.line === void 0 || loc.column === void 0) { + return ""; + } + const lines = normalizeLF(src).split("\n").map((ln) => ln.replace(/\t/g, " ")); + const visibleLines = []; + for (let n = -2; n <= 2; n++) { + if (lines[loc.line + n]) visibleLines.push(loc.line + n); + } + let gutterWidth = 0; + for (const lineNo of visibleLines) { + let w = `> ${lineNo}`; + if (w.length > gutterWidth) gutterWidth = w.length; + } + let output = ""; + for (const lineNo of visibleLines) { + const isFocusedLine = lineNo === loc.line - 1; + output += isFocusedLine ? "> " : " "; + output += `${lineNo + 1} | ${lines[lineNo]} +`; + if (isFocusedLine) + output += `${Array.from({ length: gutterWidth }).join(" ")} | ${Array.from({ + length: loc.column + }).join(" ")}^ +`; + } + return output; +} + +class AstroError extends Error { + loc; + title; + hint; + frame; + type = "AstroError"; + constructor(props, options) { + const { name, title, message, stack, location, hint, frame } = props; + super(message, options); + this.title = title; + this.name = name; + if (message) this.message = message; + this.stack = stack ? stack : this.stack; + this.loc = location; + this.hint = hint; + this.frame = frame; + } + setLocation(location) { + this.loc = location; + } + setName(name) { + this.name = name; + } + setMessage(message) { + this.message = message; + } + setHint(hint) { + this.hint = hint; + } + setFrame(source, location) { + this.frame = codeFrame(source, location); + } + static is(err) { + return err.type === "AstroError"; + } +} + +function validateArgs(args) { + if (args.length !== 3) return false; + if (!args[0] || typeof args[0] !== "object") return false; + return true; +} +function baseCreateComponent(cb, moduleId, propagation) { + const name = moduleId?.split("/").pop()?.replace(".astro", "") ?? ""; + const fn = (...args) => { + if (!validateArgs(args)) { + throw new AstroError({ + ...InvalidComponentArgs, + message: InvalidComponentArgs.message(name) + }); + } + return cb(...args); + }; + Object.defineProperty(fn, "name", { value: name, writable: false }); + fn.isAstroComponentFactory = true; + fn.moduleId = moduleId; + fn.propagation = propagation; + return fn; +} +function createComponentWithOptions(opts) { + const cb = baseCreateComponent(opts.factory, opts.moduleId, opts.propagation); + return cb; +} +function createComponent(arg1, moduleId, propagation) { + if (typeof arg1 === "function") { + return baseCreateComponent(arg1, moduleId, propagation); + } else { + return createComponentWithOptions(arg1); + } +} + +function createAstroGlobFn() { + const globHandler = (importMetaGlobResult) => { + console.warn(`Astro.glob is deprecated and will be removed in a future major version of Astro. +Use import.meta.glob instead: https://vitejs.dev/guide/features.html#glob-import`); + if (typeof importMetaGlobResult === "string") { + throw new AstroError({ + ...AstroGlobUsedOutside, + message: AstroGlobUsedOutside.message(JSON.stringify(importMetaGlobResult)) + }); + } + let allEntries = [...Object.values(importMetaGlobResult)]; + if (allEntries.length === 0) { + throw new AstroError({ + ...AstroGlobNoMatch, + message: AstroGlobNoMatch.message(JSON.stringify(importMetaGlobResult)) + }); + } + return Promise.all(allEntries.map((fn) => fn())); + }; + return globHandler; +} +function createAstro(site) { + return { + // TODO: this is no longer necessary for `Astro.site` + // but it somehow allows working around caching issues in content collections for some tests + site: void 0, + generator: `Astro v${ASTRO_VERSION}`, + glob: createAstroGlobFn() + }; +} + +let FORCE_COLOR, NODE_DISABLE_COLORS, NO_COLOR, TERM, isTTY=true; +if (typeof process !== 'undefined') { + ({ FORCE_COLOR, NODE_DISABLE_COLORS, NO_COLOR, TERM } = process.env || {}); + isTTY = process.stdout && process.stdout.isTTY; +} + +const $ = { + enabled: !NODE_DISABLE_COLORS && NO_COLOR == null && TERM !== 'dumb' && ( + FORCE_COLOR != null && FORCE_COLOR !== '0' || isTTY + ) +}; + +function init(x, y) { + let rgx = new RegExp(`\\x1b\\[${y}m`, 'g'); + let open = `\x1b[${x}m`, close = `\x1b[${y}m`; + + return function (txt) { + if (!$.enabled || txt == null) return txt; + return open + (!!~(''+txt).indexOf(close) ? txt.replace(rgx, close + open) : txt) + close; + }; +} +const bold = init(1, 22); +const dim = init(2, 22); +const red = init(31, 39); +const green = init(32, 39); +const yellow = init(33, 39); +const blue = init(34, 39); + +async function renderEndpoint(mod, context, isPrerendered, logger) { + const { request, url } = context; + const method = request.method.toUpperCase(); + let handler = mod[method] ?? mod["ALL"]; + if (!handler && method === "HEAD" && mod["GET"]) { + handler = mod["GET"]; + } + if (isPrerendered && !["GET", "HEAD"].includes(method)) { + logger.warn( + "router", + `${url.pathname} ${bold( + method + )} requests are not available in static endpoints. Mark this page as server-rendered (\`export const prerender = false;\`) or update your config to \`output: 'server'\` to make all your pages server-rendered by default.` + ); + } + if (handler === void 0) { + logger.warn( + "router", + `No API Route handler exists for the method "${method}" for the route "${url.pathname}". +Found handlers: ${Object.keys(mod).map((exp) => JSON.stringify(exp)).join(", ")} +` + ("all" in mod ? `One of the exported handlers is "all" (lowercase), did you mean to export 'ALL'? +` : "") + ); + return new Response(null, { status: 404 }); + } + if (typeof handler !== "function") { + logger.error( + "router", + `The route "${url.pathname}" exports a value for the method "${method}", but it is of the type ${typeof handler} instead of a function.` + ); + return new Response(null, { status: 500 }); + } + let response = await handler.call(mod, context); + if (!response || response instanceof Response === false) { + throw new AstroError(EndpointDidNotReturnAResponse); + } + if (REROUTABLE_STATUS_CODES.includes(response.status)) { + try { + response.headers.set(REROUTE_DIRECTIVE_HEADER, "no"); + } catch (err) { + if (err.message?.includes("immutable")) { + response = new Response(response.body, response); + response.headers.set(REROUTE_DIRECTIVE_HEADER, "no"); + } else { + throw err; + } + } + } + if (method === "HEAD") { + return new Response(null, response); + } + return response; +} + +/** + * Copyright (C) 2017-present by Andrea Giammarchi - @WebReflection + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +const {replace} = ''; +const ca = /[&<>'"]/g; + +const esca = { + '&': '&', + '<': '<', + '>': '>', + "'": ''', + '"': '"' +}; +const pe = m => esca[m]; + +/** + * Safely escape HTML entities such as `&`, `<`, `>`, `"`, and `'`. + * @param {string} es the input to safely escape + * @returns {string} the escaped input, and it **throws** an error if + * the input type is unexpected, except for boolean and numbers, + * converted as string. + */ +const escape = es => replace.call(es, ca, pe); + +function isPromise(value) { + return !!value && typeof value === "object" && "then" in value && typeof value.then === "function"; +} +async function* streamAsyncIterator(stream) { + const reader = stream.getReader(); + try { + while (true) { + const { done, value } = await reader.read(); + if (done) return; + yield value; + } + } finally { + reader.releaseLock(); + } +} + +const escapeHTML = escape; +class HTMLBytes extends Uint8Array { +} +Object.defineProperty(HTMLBytes.prototype, Symbol.toStringTag, { + get() { + return "HTMLBytes"; + } +}); +class HTMLString extends String { + get [Symbol.toStringTag]() { + return "HTMLString"; + } +} +const markHTMLString = (value) => { + if (value instanceof HTMLString) { + return value; + } + if (typeof value === "string") { + return new HTMLString(value); + } + return value; +}; +function isHTMLString(value) { + return Object.prototype.toString.call(value) === "[object HTMLString]"; +} +function markHTMLBytes(bytes) { + return new HTMLBytes(bytes); +} +function hasGetReader(obj) { + return typeof obj.getReader === "function"; +} +async function* unescapeChunksAsync(iterable) { + if (hasGetReader(iterable)) { + for await (const chunk of streamAsyncIterator(iterable)) { + yield unescapeHTML(chunk); + } + } else { + for await (const chunk of iterable) { + yield unescapeHTML(chunk); + } + } +} +function* unescapeChunks(iterable) { + for (const chunk of iterable) { + yield unescapeHTML(chunk); + } +} +function unescapeHTML(str) { + if (!!str && typeof str === "object") { + if (str instanceof Uint8Array) { + return markHTMLBytes(str); + } else if (str instanceof Response && str.body) { + const body = str.body; + return unescapeChunksAsync(body); + } else if (typeof str.then === "function") { + return Promise.resolve(str).then((value) => { + return unescapeHTML(value); + }); + } else if (str[Symbol.for("astro:slot-string")]) { + return str; + } else if (Symbol.iterator in str) { + return unescapeChunks(str); + } else if (Symbol.asyncIterator in str || hasGetReader(str)) { + return unescapeChunksAsync(str); + } + } + return markHTMLString(str); +} + +const AstroJSX = "astro:jsx"; +function isVNode(vnode) { + return vnode && typeof vnode === "object" && vnode[AstroJSX]; +} + +function isAstroComponentFactory(obj) { + return obj == null ? false : obj.isAstroComponentFactory === true; +} +function isAPropagatingComponent(result, factory) { + const hint = getPropagationHint(result, factory); + return hint === "in-tree" || hint === "self"; +} +function getPropagationHint(result, factory) { + let hint = factory.propagation || "none"; + if (factory.moduleId && result.componentMetadata.has(factory.moduleId) && hint === "none") { + hint = result.componentMetadata.get(factory.moduleId).propagation; + } + return hint; +} + +const RenderInstructionSymbol = Symbol.for("astro:render"); +function createRenderInstruction(instruction) { + return Object.defineProperty(instruction, RenderInstructionSymbol, { + value: true + }); +} +function isRenderInstruction(chunk) { + return chunk && typeof chunk === "object" && chunk[RenderInstructionSymbol]; +} + +function r(e){var t,f,n="";if("string"==typeof e||"number"==typeof e)n+=e;else if("object"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t! + +Cyclic references cannot be safely serialized for client-side usage. Please remove the cyclic reference.`); + } + parents.add(value); + const serialized = value.map((v) => { + return convertToSerializedForm(v, metadata, parents); + }); + parents.delete(value); + return serialized; +} +function serializeObject(value, metadata = {}, parents = /* @__PURE__ */ new WeakSet()) { + if (parents.has(value)) { + throw new Error(`Cyclic reference detected while serializing props for <${metadata.displayName} client:${metadata.hydrate}>! + +Cyclic references cannot be safely serialized for client-side usage. Please remove the cyclic reference.`); + } + parents.add(value); + const serialized = Object.fromEntries( + Object.entries(value).map(([k, v]) => { + return [k, convertToSerializedForm(v, metadata, parents)]; + }) + ); + parents.delete(value); + return serialized; +} +function convertToSerializedForm(value, metadata = {}, parents = /* @__PURE__ */ new WeakSet()) { + const tag = Object.prototype.toString.call(value); + switch (tag) { + case "[object Date]": { + return [PROP_TYPE.Date, value.toISOString()]; + } + case "[object RegExp]": { + return [PROP_TYPE.RegExp, value.source]; + } + case "[object Map]": { + return [PROP_TYPE.Map, serializeArray(Array.from(value), metadata, parents)]; + } + case "[object Set]": { + return [PROP_TYPE.Set, serializeArray(Array.from(value), metadata, parents)]; + } + case "[object BigInt]": { + return [PROP_TYPE.BigInt, value.toString()]; + } + case "[object URL]": { + return [PROP_TYPE.URL, value.toString()]; + } + case "[object Array]": { + return [PROP_TYPE.JSON, serializeArray(value, metadata, parents)]; + } + case "[object Uint8Array]": { + return [PROP_TYPE.Uint8Array, Array.from(value)]; + } + case "[object Uint16Array]": { + return [PROP_TYPE.Uint16Array, Array.from(value)]; + } + case "[object Uint32Array]": { + return [PROP_TYPE.Uint32Array, Array.from(value)]; + } + default: { + if (value !== null && typeof value === "object") { + return [PROP_TYPE.Value, serializeObject(value, metadata, parents)]; + } + if (value === Infinity) { + return [PROP_TYPE.Infinity, 1]; + } + if (value === -Infinity) { + return [PROP_TYPE.Infinity, -1]; + } + if (value === void 0) { + return [PROP_TYPE.Value]; + } + return [PROP_TYPE.Value, value]; + } + } +} +function serializeProps(props, metadata) { + const serialized = JSON.stringify(serializeObject(props, metadata)); + return serialized; +} + +const transitionDirectivesToCopyOnIsland = Object.freeze([ + "data-astro-transition-scope", + "data-astro-transition-persist", + "data-astro-transition-persist-props" +]); +function extractDirectives(inputProps, clientDirectives) { + let extracted = { + isPage: false, + hydration: null, + props: {}, + propsWithoutTransitionAttributes: {} + }; + for (const [key, value] of Object.entries(inputProps)) { + if (key.startsWith("server:")) { + if (key === "server:root") { + extracted.isPage = true; + } + } + if (key.startsWith("client:")) { + if (!extracted.hydration) { + extracted.hydration = { + directive: "", + value: "", + componentUrl: "", + componentExport: { value: "" } + }; + } + switch (key) { + case "client:component-path": { + extracted.hydration.componentUrl = value; + break; + } + case "client:component-export": { + extracted.hydration.componentExport.value = value; + break; + } + // This is a special prop added to prove that the client hydration method + // was added statically. + case "client:component-hydration": { + break; + } + case "client:display-name": { + break; + } + default: { + extracted.hydration.directive = key.split(":")[1]; + extracted.hydration.value = value; + if (!clientDirectives.has(extracted.hydration.directive)) { + const hydrationMethods = Array.from(clientDirectives.keys()).map((d) => `client:${d}`).join(", "); + throw new Error( + `Error: invalid hydration directive "${key}". Supported hydration methods: ${hydrationMethods}` + ); + } + if (extracted.hydration.directive === "media" && typeof extracted.hydration.value !== "string") { + throw new AstroError(MissingMediaQueryDirective); + } + break; + } + } + } else { + extracted.props[key] = value; + if (!transitionDirectivesToCopyOnIsland.includes(key)) { + extracted.propsWithoutTransitionAttributes[key] = value; + } + } + } + for (const sym of Object.getOwnPropertySymbols(inputProps)) { + extracted.props[sym] = inputProps[sym]; + extracted.propsWithoutTransitionAttributes[sym] = inputProps[sym]; + } + return extracted; +} +async function generateHydrateScript(scriptOptions, metadata) { + const { renderer, result, astroId, props, attrs } = scriptOptions; + const { hydrate, componentUrl, componentExport } = metadata; + if (!componentExport.value) { + throw new AstroError({ + ...NoMatchingImport, + message: NoMatchingImport.message(metadata.displayName) + }); + } + const island = { + children: "", + props: { + // This is for HMR, probably can avoid it in prod + uid: astroId + } + }; + if (attrs) { + for (const [key, value] of Object.entries(attrs)) { + island.props[key] = escapeHTML(value); + } + } + island.props["component-url"] = await result.resolve(decodeURI(componentUrl)); + if (renderer.clientEntrypoint) { + island.props["component-export"] = componentExport.value; + island.props["renderer-url"] = await result.resolve( + decodeURI(renderer.clientEntrypoint.toString()) + ); + island.props["props"] = escapeHTML(serializeProps(props, metadata)); + } + island.props["ssr"] = ""; + island.props["client"] = hydrate; + let beforeHydrationUrl = await result.resolve("astro:scripts/before-hydration.js"); + if (beforeHydrationUrl.length) { + island.props["before-hydration-url"] = beforeHydrationUrl; + } + island.props["opts"] = escapeHTML( + JSON.stringify({ + name: metadata.displayName, + value: metadata.hydrateArgs || "" + }) + ); + transitionDirectivesToCopyOnIsland.forEach((name) => { + if (typeof props[name] !== "undefined") { + island.props[name] = props[name]; + } + }); + return island; +} + +/** + * shortdash - https://github.com/bibig/node-shorthash + * + * @license + * + * (The MIT License) + * + * Copyright (c) 2013 Bibig + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +const dictionary = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXY"; +const binary = dictionary.length; +function bitwise(str) { + let hash = 0; + if (str.length === 0) return hash; + for (let i = 0; i < str.length; i++) { + const ch = str.charCodeAt(i); + hash = (hash << 5) - hash + ch; + hash = hash & hash; + } + return hash; +} +function shorthash(text) { + let num; + let result = ""; + let integer = bitwise(text); + const sign = integer < 0 ? "Z" : ""; + integer = Math.abs(integer); + while (integer >= binary) { + num = integer % binary; + integer = Math.floor(integer / binary); + result = dictionary[num] + result; + } + if (integer > 0) { + result = dictionary[integer] + result; + } + return sign + result; +} + +const headAndContentSym = Symbol.for("astro.headAndContent"); +function isHeadAndContent(obj) { + return typeof obj === "object" && obj !== null && !!obj[headAndContentSym]; +} +function createThinHead() { + return { + [headAndContentSym]: true + }; +} + +const ISLAND_STYLES = "astro-island,astro-slot,astro-static-slot{display:contents}"; + +var astro_island_prebuilt_dev_default = `(()=>{var A=Object.defineProperty;var g=(i,o,a)=>o in i?A(i,o,{enumerable:!0,configurable:!0,writable:!0,value:a}):i[o]=a;var l=(i,o,a)=>g(i,typeof o!="symbol"?o+"":o,a);{let i={0:t=>y(t),1:t=>a(t),2:t=>new RegExp(t),3:t=>new Date(t),4:t=>new Map(a(t)),5:t=>new Set(a(t)),6:t=>BigInt(t),7:t=>new URL(t),8:t=>new Uint8Array(t),9:t=>new Uint16Array(t),10:t=>new Uint32Array(t),11:t=>1/0*t},o=t=>{let[h,e]=t;return h in i?i[h](e):void 0},a=t=>t.map(o),y=t=>typeof t!="object"||t===null?t:Object.fromEntries(Object.entries(t).map(([h,e])=>[h,o(e)]));class f extends HTMLElement{constructor(){super(...arguments);l(this,"Component");l(this,"hydrator");l(this,"hydrate",async()=>{var b;if(!this.hydrator||!this.isConnected)return;let e=(b=this.parentElement)==null?void 0:b.closest("astro-island[ssr]");if(e){e.addEventListener("astro:hydrate",this.hydrate,{once:!0});return}let c=this.querySelectorAll("astro-slot"),n={},p=this.querySelectorAll("template[data-astro-template]");for(let r of p){let s=r.closest(this.tagName);s!=null&&s.isSameNode(this)&&(n[r.getAttribute("data-astro-template")||"default"]=r.innerHTML,r.remove())}for(let r of c){let s=r.closest(this.tagName);s!=null&&s.isSameNode(this)&&(n[r.getAttribute("name")||"default"]=r.innerHTML)}let u;try{u=this.hasAttribute("props")?y(JSON.parse(this.getAttribute("props"))):{}}catch(r){let s=this.getAttribute("component-url")||"",v=this.getAttribute("component-export");throw v&&(s+=\` (export \${v})\`),console.error(\`[hydrate] Error parsing props for component \${s}\`,this.getAttribute("props"),r),r}let d,m=this.hydrator(this);d=performance.now(),await m(this.Component,u,n,{client:this.getAttribute("client")}),d&&this.setAttribute("client-render-time",(performance.now()-d).toString()),this.removeAttribute("ssr"),this.dispatchEvent(new CustomEvent("astro:hydrate"))});l(this,"unmount",()=>{this.isConnected||this.dispatchEvent(new CustomEvent("astro:unmount"))})}disconnectedCallback(){document.removeEventListener("astro:after-swap",this.unmount),document.addEventListener("astro:after-swap",this.unmount,{once:!0})}connectedCallback(){if(!this.hasAttribute("await-children")||document.readyState==="interactive"||document.readyState==="complete")this.childrenConnectedCallback();else{let e=()=>{document.removeEventListener("DOMContentLoaded",e),c.disconnect(),this.childrenConnectedCallback()},c=new MutationObserver(()=>{var n;((n=this.lastChild)==null?void 0:n.nodeType)===Node.COMMENT_NODE&&this.lastChild.nodeValue==="astro:end"&&(this.lastChild.remove(),e())});c.observe(this,{childList:!0}),document.addEventListener("DOMContentLoaded",e)}}async childrenConnectedCallback(){let e=this.getAttribute("before-hydration-url");e&&await import(e),this.start()}async start(){let e=JSON.parse(this.getAttribute("opts")),c=this.getAttribute("client");if(Astro[c]===void 0){window.addEventListener(\`astro:\${c}\`,()=>this.start(),{once:!0});return}try{await Astro[c](async()=>{let n=this.getAttribute("renderer-url"),[p,{default:u}]=await Promise.all([import(this.getAttribute("component-url")),n?import(n):()=>()=>{}]),d=this.getAttribute("component-export")||"default";if(!d.includes("."))this.Component=p[d];else{this.Component=p;for(let m of d.split("."))this.Component=this.Component[m]}return this.hydrator=u,this.hydrate},e,this)}catch(n){console.error(\`[astro-island] Error hydrating \${this.getAttribute("component-url")}\`,n)}}attributeChangedCallback(){this.hydrate()}}l(f,"observedAttributes",["props"]),customElements.get("astro-island")||customElements.define("astro-island",f)}})();`; + +var astro_island_prebuilt_default = `(()=>{var A=Object.defineProperty;var g=(i,o,a)=>o in i?A(i,o,{enumerable:!0,configurable:!0,writable:!0,value:a}):i[o]=a;var d=(i,o,a)=>g(i,typeof o!="symbol"?o+"":o,a);{let i={0:t=>m(t),1:t=>a(t),2:t=>new RegExp(t),3:t=>new Date(t),4:t=>new Map(a(t)),5:t=>new Set(a(t)),6:t=>BigInt(t),7:t=>new URL(t),8:t=>new Uint8Array(t),9:t=>new Uint16Array(t),10:t=>new Uint32Array(t),11:t=>1/0*t},o=t=>{let[l,e]=t;return l in i?i[l](e):void 0},a=t=>t.map(o),m=t=>typeof t!="object"||t===null?t:Object.fromEntries(Object.entries(t).map(([l,e])=>[l,o(e)]));class y extends HTMLElement{constructor(){super(...arguments);d(this,"Component");d(this,"hydrator");d(this,"hydrate",async()=>{var b;if(!this.hydrator||!this.isConnected)return;let e=(b=this.parentElement)==null?void 0:b.closest("astro-island[ssr]");if(e){e.addEventListener("astro:hydrate",this.hydrate,{once:!0});return}let c=this.querySelectorAll("astro-slot"),n={},h=this.querySelectorAll("template[data-astro-template]");for(let r of h){let s=r.closest(this.tagName);s!=null&&s.isSameNode(this)&&(n[r.getAttribute("data-astro-template")||"default"]=r.innerHTML,r.remove())}for(let r of c){let s=r.closest(this.tagName);s!=null&&s.isSameNode(this)&&(n[r.getAttribute("name")||"default"]=r.innerHTML)}let p;try{p=this.hasAttribute("props")?m(JSON.parse(this.getAttribute("props"))):{}}catch(r){let s=this.getAttribute("component-url")||"",v=this.getAttribute("component-export");throw v&&(s+=\` (export \${v})\`),console.error(\`[hydrate] Error parsing props for component \${s}\`,this.getAttribute("props"),r),r}let u;await this.hydrator(this)(this.Component,p,n,{client:this.getAttribute("client")}),this.removeAttribute("ssr"),this.dispatchEvent(new CustomEvent("astro:hydrate"))});d(this,"unmount",()=>{this.isConnected||this.dispatchEvent(new CustomEvent("astro:unmount"))})}disconnectedCallback(){document.removeEventListener("astro:after-swap",this.unmount),document.addEventListener("astro:after-swap",this.unmount,{once:!0})}connectedCallback(){if(!this.hasAttribute("await-children")||document.readyState==="interactive"||document.readyState==="complete")this.childrenConnectedCallback();else{let e=()=>{document.removeEventListener("DOMContentLoaded",e),c.disconnect(),this.childrenConnectedCallback()},c=new MutationObserver(()=>{var n;((n=this.lastChild)==null?void 0:n.nodeType)===Node.COMMENT_NODE&&this.lastChild.nodeValue==="astro:end"&&(this.lastChild.remove(),e())});c.observe(this,{childList:!0}),document.addEventListener("DOMContentLoaded",e)}}async childrenConnectedCallback(){let e=this.getAttribute("before-hydration-url");e&&await import(e),this.start()}async start(){let e=JSON.parse(this.getAttribute("opts")),c=this.getAttribute("client");if(Astro[c]===void 0){window.addEventListener(\`astro:\${c}\`,()=>this.start(),{once:!0});return}try{await Astro[c](async()=>{let n=this.getAttribute("renderer-url"),[h,{default:p}]=await Promise.all([import(this.getAttribute("component-url")),n?import(n):()=>()=>{}]),u=this.getAttribute("component-export")||"default";if(!u.includes("."))this.Component=h[u];else{this.Component=h;for(let f of u.split("."))this.Component=this.Component[f]}return this.hydrator=p,this.hydrate},e,this)}catch(n){console.error(\`[astro-island] Error hydrating \${this.getAttribute("component-url")}\`,n)}}attributeChangedCallback(){this.hydrate()}}d(y,"observedAttributes",["props"]),customElements.get("astro-island")||customElements.define("astro-island",y)}})();`; + +function determineIfNeedsHydrationScript(result) { + if (result._metadata.hasHydrationScript) { + return false; + } + return result._metadata.hasHydrationScript = true; +} +function determinesIfNeedsDirectiveScript(result, directive) { + if (result._metadata.hasDirectives.has(directive)) { + return false; + } + result._metadata.hasDirectives.add(directive); + return true; +} +function getDirectiveScriptText(result, directive) { + const clientDirectives = result.clientDirectives; + const clientDirective = clientDirectives.get(directive); + if (!clientDirective) { + throw new Error(`Unknown directive: ${directive}`); + } + return clientDirective; +} +function getPrescripts(result, type, directive) { + switch (type) { + case "both": + return ``; + case "directive": + return ``; + } +} + +function renderCspContent(result) { + const finalScriptHashes = /* @__PURE__ */ new Set(); + const finalStyleHashes = /* @__PURE__ */ new Set(); + for (const scriptHash of result.scriptHashes) { + finalScriptHashes.add(`'${scriptHash}'`); + } + for (const styleHash of result.styleHashes) { + finalStyleHashes.add(`'${styleHash}'`); + } + for (const styleHash of result._metadata.extraStyleHashes) { + finalStyleHashes.add(`'${styleHash}'`); + } + for (const scriptHash of result._metadata.extraScriptHashes) { + finalScriptHashes.add(`'${scriptHash}'`); + } + let directives = ""; + if (result.directives.length > 0) { + directives = result.directives.join(";") + ";"; + } + let scriptResources = "'self'"; + if (result.scriptResources.length > 0) { + scriptResources = result.scriptResources.map((r) => `${r}`).join(" "); + } + let styleResources = "'self'"; + if (result.styleResources.length > 0) { + styleResources = result.styleResources.map((r) => `${r}`).join(" "); + } + const strictDynamic = result.isStrictDynamic ? ` 'strict-dynamic'` : ""; + const scriptSrc = `script-src ${scriptResources} ${Array.from(finalScriptHashes).join(" ")}${strictDynamic};`; + const styleSrc = `style-src ${styleResources} ${Array.from(finalStyleHashes).join(" ")};`; + return `${directives} ${scriptSrc} ${styleSrc}`; +} + +const voidElementNames = /^(area|base|br|col|command|embed|hr|img|input|keygen|link|meta|param|source|track|wbr)$/i; +const htmlBooleanAttributes = /^(?:allowfullscreen|async|autofocus|autoplay|checked|controls|default|defer|disabled|disablepictureinpicture|disableremoteplayback|formnovalidate|hidden|inert|loop|nomodule|novalidate|open|playsinline|readonly|required|reversed|scoped|seamless|selected|itemscope)$/i; +const AMPERSAND_REGEX = /&/g; +const DOUBLE_QUOTE_REGEX = /"/g; +const STATIC_DIRECTIVES = /* @__PURE__ */ new Set(["set:html", "set:text"]); +const toIdent = (k) => k.trim().replace(/(?!^)\b\w|\s+|\W+/g, (match, index) => { + if (/\W/.test(match)) return ""; + return index === 0 ? match : match.toUpperCase(); +}); +const toAttributeString = (value, shouldEscape = true) => shouldEscape ? String(value).replace(AMPERSAND_REGEX, "&").replace(DOUBLE_QUOTE_REGEX, """) : value; +const kebab = (k) => k.toLowerCase() === k ? k : k.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`); +const toStyleString = (obj) => Object.entries(obj).filter(([_, v]) => typeof v === "string" && v.trim() || typeof v === "number").map(([k, v]) => { + if (k[0] !== "-" && k[1] !== "-") return `${kebab(k)}:${v}`; + return `${k}:${v}`; +}).join(";"); +function defineScriptVars(vars) { + let output = ""; + for (const [key, value] of Object.entries(vars)) { + output += `const ${toIdent(key)} = ${JSON.stringify(value)?.replace( + /<\/script>/g, + "\\x3C/script>" + )}; +`; + } + return markHTMLString(output); +} +function formatList(values) { + if (values.length === 1) { + return values[0]; + } + return `${values.slice(0, -1).join(", ")} or ${values[values.length - 1]}`; +} +function addAttribute(value, key, shouldEscape = true) { + if (value == null) { + return ""; + } + if (STATIC_DIRECTIVES.has(key)) { + console.warn(`[astro] The "${key}" directive cannot be applied dynamically at runtime. It will not be rendered as an attribute. + +Make sure to use the static attribute syntax (\`${key}={value}\`) instead of the dynamic spread syntax (\`{...{ "${key}": value }}\`).`); + return ""; + } + if (key === "class:list") { + const listValue = toAttributeString(clsx(value), shouldEscape); + if (listValue === "") { + return ""; + } + return markHTMLString(` ${key.slice(0, -5)}="${listValue}"`); + } + if (key === "style" && !(value instanceof HTMLString)) { + if (Array.isArray(value) && value.length === 2) { + return markHTMLString( + ` ${key}="${toAttributeString(`${toStyleString(value[0])};${value[1]}`, shouldEscape)}"` + ); + } + if (typeof value === "object") { + return markHTMLString(` ${key}="${toAttributeString(toStyleString(value), shouldEscape)}"`); + } + } + if (key === "className") { + return markHTMLString(` class="${toAttributeString(value, shouldEscape)}"`); + } + if (typeof value === "string" && value.includes("&") && isHttpUrl(value)) { + return markHTMLString(` ${key}="${toAttributeString(value, false)}"`); + } + if (htmlBooleanAttributes.test(key)) { + return markHTMLString(value ? ` ${key}` : ""); + } + if (value === "") { + return markHTMLString(` ${key}`); + } + if (key === "popover" && typeof value === "boolean") { + return markHTMLString(value ? ` popover` : ""); + } + if (key === "download" && typeof value === "boolean") { + return markHTMLString(value ? ` download` : ""); + } + return markHTMLString(` ${key}="${toAttributeString(value, shouldEscape)}"`); +} +function internalSpreadAttributes(values, shouldEscape = true) { + let output = ""; + for (const [key, value] of Object.entries(values)) { + output += addAttribute(value, key, shouldEscape); + } + return markHTMLString(output); +} +function renderElement$1(name, { props: _props, children = "" }, shouldEscape = true) { + const { lang: _, "data-astro-id": astroId, "define:vars": defineVars, ...props } = _props; + if (defineVars) { + if (name === "style") { + delete props["is:global"]; + delete props["is:scoped"]; + } + if (name === "script") { + delete props.hoist; + children = defineScriptVars(defineVars) + "\n" + children; + } + } + if ((children == null || children == "") && voidElementNames.test(name)) { + return `<${name}${internalSpreadAttributes(props, shouldEscape)}>`; + } + return `<${name}${internalSpreadAttributes(props, shouldEscape)}>${children}`; +} +const noop = () => { +}; +class BufferedRenderer { + chunks = []; + renderPromise; + destination; + /** + * Determines whether buffer has been flushed + * to the final destination. + */ + flushed = false; + constructor(destination, renderFunction) { + this.destination = destination; + this.renderPromise = renderFunction(this); + if (isPromise(this.renderPromise)) { + Promise.resolve(this.renderPromise).catch(noop); + } + } + write(chunk) { + if (this.flushed) { + this.destination.write(chunk); + } else { + this.chunks.push(chunk); + } + } + flush() { + if (this.flushed) { + throw new Error("The render buffer has already been flushed."); + } + this.flushed = true; + for (const chunk of this.chunks) { + this.destination.write(chunk); + } + return this.renderPromise; + } +} +function createBufferedRenderer(destination, renderFunction) { + return new BufferedRenderer(destination, renderFunction); +} +const isNode = typeof process !== "undefined" && Object.prototype.toString.call(process) === "[object process]"; +const isDeno = typeof Deno !== "undefined"; +function promiseWithResolvers() { + let resolve, reject; + const promise = new Promise((_resolve, _reject) => { + resolve = _resolve; + reject = _reject; + }); + return { + promise, + resolve, + reject + }; +} +const VALID_PROTOCOLS = ["http:", "https:"]; +function isHttpUrl(url) { + try { + const parsedUrl = new URL(url); + return VALID_PROTOCOLS.includes(parsedUrl.protocol); + } catch { + return false; + } +} + +const uniqueElements = (item, index, all) => { + const props = JSON.stringify(item.props); + const children = item.children; + return index === all.findIndex((i) => JSON.stringify(i.props) === props && i.children == children); +}; +function renderAllHeadContent(result) { + result._metadata.hasRenderedHead = true; + let content = ""; + if (result.shouldInjectCspMetaTags && result.cspDestination === "meta") { + content += renderElement$1( + "meta", + { + props: { + "http-equiv": "content-security-policy", + content: renderCspContent(result) + }, + children: "" + }, + false + ); + } + const styles = Array.from(result.styles).filter(uniqueElements).map( + (style) => style.props.rel === "stylesheet" ? renderElement$1("link", style) : renderElement$1("style", style) + ); + result.styles.clear(); + const scripts = Array.from(result.scripts).filter(uniqueElements).map((script) => { + if (result.userAssetsBase) { + script.props.src = (result.base === "/" ? "" : result.base) + result.userAssetsBase + script.props.src; + } + return renderElement$1("script", script, false); + }); + const links = Array.from(result.links).filter(uniqueElements).map((link) => renderElement$1("link", link, false)); + content += styles.join("\n") + links.join("\n") + scripts.join("\n"); + if (result._metadata.extraHead.length > 0) { + for (const part of result._metadata.extraHead) { + content += part; + } + } + return markHTMLString(content); +} +function maybeRenderHead() { + return createRenderInstruction({ type: "maybe-head" }); +} + +function encodeHexUpperCase(data) { + let result = ""; + for (let i = 0; i < data.length; i++) { + result += alphabetUpperCase[data[i] >> 4]; + result += alphabetUpperCase[data[i] & 0x0f]; + } + return result; +} +function decodeHex(data) { + if (data.length % 2 !== 0) { + throw new Error("Invalid hex string"); + } + const result = new Uint8Array(data.length / 2); + for (let i = 0; i < data.length; i += 2) { + if (!(data[i] in decodeMap)) { + throw new Error("Invalid character"); + } + if (!(data[i + 1] in decodeMap)) { + throw new Error("Invalid character"); + } + result[i / 2] |= decodeMap[data[i]] << 4; + result[i / 2] |= decodeMap[data[i + 1]]; + } + return result; +} +const alphabetUpperCase = "0123456789ABCDEF"; +const decodeMap = { + "0": 0, + "1": 1, + "2": 2, + "3": 3, + "4": 4, + "5": 5, + "6": 6, + "7": 7, + "8": 8, + "9": 9, + a: 10, + A: 10, + b: 11, + B: 11, + c: 12, + C: 12, + d: 13, + D: 13, + e: 14, + E: 14, + f: 15, + F: 15 +}; + +var EncodingPadding$1; +(function (EncodingPadding) { + EncodingPadding[EncodingPadding["Include"] = 0] = "Include"; + EncodingPadding[EncodingPadding["None"] = 1] = "None"; +})(EncodingPadding$1 || (EncodingPadding$1 = {})); +var DecodingPadding$1; +(function (DecodingPadding) { + DecodingPadding[DecodingPadding["Required"] = 0] = "Required"; + DecodingPadding[DecodingPadding["Ignore"] = 1] = "Ignore"; +})(DecodingPadding$1 || (DecodingPadding$1 = {})); + +function encodeBase64(bytes) { + return encodeBase64_internal(bytes, base64Alphabet, EncodingPadding.Include); +} +function encodeBase64_internal(bytes, alphabet, padding) { + let result = ""; + for (let i = 0; i < bytes.byteLength; i += 3) { + let buffer = 0; + let bufferBitSize = 0; + for (let j = 0; j < 3 && i + j < bytes.byteLength; j++) { + buffer = (buffer << 8) | bytes[i + j]; + bufferBitSize += 8; + } + for (let j = 0; j < 4; j++) { + if (bufferBitSize >= 6) { + result += alphabet[(buffer >> (bufferBitSize - 6)) & 0x3f]; + bufferBitSize -= 6; + } + else if (bufferBitSize > 0) { + result += alphabet[(buffer << (6 - bufferBitSize)) & 0x3f]; + bufferBitSize = 0; + } + else if (padding === EncodingPadding.Include) { + result += "="; + } + } + } + return result; +} +const base64Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +function decodeBase64(encoded) { + return decodeBase64_internal(encoded, base64DecodeMap, DecodingPadding.Required); +} +function decodeBase64_internal(encoded, decodeMap, padding) { + const result = new Uint8Array(Math.ceil(encoded.length / 4) * 3); + let totalBytes = 0; + for (let i = 0; i < encoded.length; i += 4) { + let chunk = 0; + let bitsRead = 0; + for (let j = 0; j < 4; j++) { + if (padding === DecodingPadding.Required && encoded[i + j] === "=") { + continue; + } + if (padding === DecodingPadding.Ignore && + (i + j >= encoded.length || encoded[i + j] === "=")) { + continue; + } + if (j > 0 && encoded[i + j - 1] === "=") { + throw new Error("Invalid padding"); + } + if (!(encoded[i + j] in decodeMap)) { + throw new Error("Invalid character"); + } + chunk |= decodeMap[encoded[i + j]] << ((3 - j) * 6); + bitsRead += 6; + } + if (bitsRead < 24) { + let unused; + if (bitsRead === 12) { + unused = chunk & 0xffff; + } + else if (bitsRead === 18) { + unused = chunk & 0xff; + } + else { + throw new Error("Invalid padding"); + } + if (unused !== 0) { + throw new Error("Invalid padding"); + } + } + const byteLength = Math.floor(bitsRead / 8); + for (let i = 0; i < byteLength; i++) { + result[totalBytes] = (chunk >> (16 - i * 8)) & 0xff; + totalBytes++; + } + } + return result.slice(0, totalBytes); +} +var EncodingPadding; +(function (EncodingPadding) { + EncodingPadding[EncodingPadding["Include"] = 0] = "Include"; + EncodingPadding[EncodingPadding["None"] = 1] = "None"; +})(EncodingPadding || (EncodingPadding = {})); +var DecodingPadding; +(function (DecodingPadding) { + DecodingPadding[DecodingPadding["Required"] = 0] = "Required"; + DecodingPadding[DecodingPadding["Ignore"] = 1] = "Ignore"; +})(DecodingPadding || (DecodingPadding = {})); +const base64DecodeMap = { + "0": 52, + "1": 53, + "2": 54, + "3": 55, + "4": 56, + "5": 57, + "6": 58, + "7": 59, + "8": 60, + "9": 61, + A: 0, + B: 1, + C: 2, + D: 3, + E: 4, + F: 5, + G: 6, + H: 7, + I: 8, + J: 9, + K: 10, + L: 11, + M: 12, + N: 13, + O: 14, + P: 15, + Q: 16, + R: 17, + S: 18, + T: 19, + U: 20, + V: 21, + W: 22, + X: 23, + Y: 24, + Z: 25, + a: 26, + b: 27, + c: 28, + d: 29, + e: 30, + f: 31, + g: 32, + h: 33, + i: 34, + j: 35, + k: 36, + l: 37, + m: 38, + n: 39, + o: 40, + p: 41, + q: 42, + r: 43, + s: 44, + t: 45, + u: 46, + v: 47, + w: 48, + x: 49, + y: 50, + z: 51, + "+": 62, + "/": 63 +}; + +var util; +(function (util) { + util.assertEqual = (val) => val; + function assertIs(_arg) { } + util.assertIs = assertIs; + function assertNever(_x) { + throw new Error(); + } + util.assertNever = assertNever; + util.arrayToEnum = (items) => { + const obj = {}; + for (const item of items) { + obj[item] = item; + } + return obj; + }; + util.getValidEnumValues = (obj) => { + const validKeys = util.objectKeys(obj).filter((k) => typeof obj[obj[k]] !== "number"); + const filtered = {}; + for (const k of validKeys) { + filtered[k] = obj[k]; + } + return util.objectValues(filtered); + }; + util.objectValues = (obj) => { + return util.objectKeys(obj).map(function (e) { + return obj[e]; + }); + }; + util.objectKeys = typeof Object.keys === "function" // eslint-disable-line ban/ban + ? (obj) => Object.keys(obj) // eslint-disable-line ban/ban + : (object) => { + const keys = []; + for (const key in object) { + if (Object.prototype.hasOwnProperty.call(object, key)) { + keys.push(key); + } + } + return keys; + }; + util.find = (arr, checker) => { + for (const item of arr) { + if (checker(item)) + return item; + } + return undefined; + }; + util.isInteger = typeof Number.isInteger === "function" + ? (val) => Number.isInteger(val) // eslint-disable-line ban/ban + : (val) => typeof val === "number" && isFinite(val) && Math.floor(val) === val; + function joinValues(array, separator = " | ") { + return array + .map((val) => (typeof val === "string" ? `'${val}'` : val)) + .join(separator); + } + util.joinValues = joinValues; + util.jsonStringifyReplacer = (_, value) => { + if (typeof value === "bigint") { + return value.toString(); + } + return value; + }; +})(util || (util = {})); +var objectUtil; +(function (objectUtil) { + objectUtil.mergeShapes = (first, second) => { + return { + ...first, + ...second, // second overwrites first + }; + }; +})(objectUtil || (objectUtil = {})); +const ZodParsedType = util.arrayToEnum([ + "string", + "nan", + "number", + "integer", + "float", + "boolean", + "date", + "bigint", + "symbol", + "function", + "undefined", + "null", + "array", + "object", + "unknown", + "promise", + "void", + "never", + "map", + "set", +]); +const getParsedType = (data) => { + const t = typeof data; + switch (t) { + case "undefined": + return ZodParsedType.undefined; + case "string": + return ZodParsedType.string; + case "number": + return isNaN(data) ? ZodParsedType.nan : ZodParsedType.number; + case "boolean": + return ZodParsedType.boolean; + case "function": + return ZodParsedType.function; + case "bigint": + return ZodParsedType.bigint; + case "symbol": + return ZodParsedType.symbol; + case "object": + if (Array.isArray(data)) { + return ZodParsedType.array; + } + if (data === null) { + return ZodParsedType.null; + } + if (data.then && + typeof data.then === "function" && + data.catch && + typeof data.catch === "function") { + return ZodParsedType.promise; + } + if (typeof Map !== "undefined" && data instanceof Map) { + return ZodParsedType.map; + } + if (typeof Set !== "undefined" && data instanceof Set) { + return ZodParsedType.set; + } + if (typeof Date !== "undefined" && data instanceof Date) { + return ZodParsedType.date; + } + return ZodParsedType.object; + default: + return ZodParsedType.unknown; + } +}; + +const ZodIssueCode = util.arrayToEnum([ + "invalid_type", + "invalid_literal", + "custom", + "invalid_union", + "invalid_union_discriminator", + "invalid_enum_value", + "unrecognized_keys", + "invalid_arguments", + "invalid_return_type", + "invalid_date", + "invalid_string", + "too_small", + "too_big", + "invalid_intersection_types", + "not_multiple_of", + "not_finite", +]); +const quotelessJson = (obj) => { + const json = JSON.stringify(obj, null, 2); + return json.replace(/"([^"]+)":/g, "$1:"); +}; +class ZodError extends Error { + get errors() { + return this.issues; + } + constructor(issues) { + super(); + this.issues = []; + this.addIssue = (sub) => { + this.issues = [...this.issues, sub]; + }; + this.addIssues = (subs = []) => { + this.issues = [...this.issues, ...subs]; + }; + const actualProto = new.target.prototype; + if (Object.setPrototypeOf) { + // eslint-disable-next-line ban/ban + Object.setPrototypeOf(this, actualProto); + } + else { + this.__proto__ = actualProto; + } + this.name = "ZodError"; + this.issues = issues; + } + format(_mapper) { + const mapper = _mapper || + function (issue) { + return issue.message; + }; + const fieldErrors = { _errors: [] }; + const processError = (error) => { + for (const issue of error.issues) { + if (issue.code === "invalid_union") { + issue.unionErrors.map(processError); + } + else if (issue.code === "invalid_return_type") { + processError(issue.returnTypeError); + } + else if (issue.code === "invalid_arguments") { + processError(issue.argumentsError); + } + else if (issue.path.length === 0) { + fieldErrors._errors.push(mapper(issue)); + } + else { + let curr = fieldErrors; + let i = 0; + while (i < issue.path.length) { + const el = issue.path[i]; + const terminal = i === issue.path.length - 1; + if (!terminal) { + curr[el] = curr[el] || { _errors: [] }; + // if (typeof el === "string") { + // curr[el] = curr[el] || { _errors: [] }; + // } else if (typeof el === "number") { + // const errorArray: any = []; + // errorArray._errors = []; + // curr[el] = curr[el] || errorArray; + // } + } + else { + curr[el] = curr[el] || { _errors: [] }; + curr[el]._errors.push(mapper(issue)); + } + curr = curr[el]; + i++; + } + } + } + }; + processError(this); + return fieldErrors; + } + static assert(value) { + if (!(value instanceof ZodError)) { + throw new Error(`Not a ZodError: ${value}`); + } + } + toString() { + return this.message; + } + get message() { + return JSON.stringify(this.issues, util.jsonStringifyReplacer, 2); + } + get isEmpty() { + return this.issues.length === 0; + } + flatten(mapper = (issue) => issue.message) { + const fieldErrors = {}; + const formErrors = []; + for (const sub of this.issues) { + if (sub.path.length > 0) { + fieldErrors[sub.path[0]] = fieldErrors[sub.path[0]] || []; + fieldErrors[sub.path[0]].push(mapper(sub)); + } + else { + formErrors.push(mapper(sub)); + } + } + return { formErrors, fieldErrors }; + } + get formErrors() { + return this.flatten(); + } +} +ZodError.create = (issues) => { + const error = new ZodError(issues); + return error; +}; + +const errorMap = (issue, _ctx) => { + let message; + switch (issue.code) { + case ZodIssueCode.invalid_type: + if (issue.received === ZodParsedType.undefined) { + message = "Required"; + } + else { + message = `Expected ${issue.expected}, received ${issue.received}`; + } + break; + case ZodIssueCode.invalid_literal: + message = `Invalid literal value, expected ${JSON.stringify(issue.expected, util.jsonStringifyReplacer)}`; + break; + case ZodIssueCode.unrecognized_keys: + message = `Unrecognized key(s) in object: ${util.joinValues(issue.keys, ", ")}`; + break; + case ZodIssueCode.invalid_union: + message = `Invalid input`; + break; + case ZodIssueCode.invalid_union_discriminator: + message = `Invalid discriminator value. Expected ${util.joinValues(issue.options)}`; + break; + case ZodIssueCode.invalid_enum_value: + message = `Invalid enum value. Expected ${util.joinValues(issue.options)}, received '${issue.received}'`; + break; + case ZodIssueCode.invalid_arguments: + message = `Invalid function arguments`; + break; + case ZodIssueCode.invalid_return_type: + message = `Invalid function return type`; + break; + case ZodIssueCode.invalid_date: + message = `Invalid date`; + break; + case ZodIssueCode.invalid_string: + if (typeof issue.validation === "object") { + if ("includes" in issue.validation) { + message = `Invalid input: must include "${issue.validation.includes}"`; + if (typeof issue.validation.position === "number") { + message = `${message} at one or more positions greater than or equal to ${issue.validation.position}`; + } + } + else if ("startsWith" in issue.validation) { + message = `Invalid input: must start with "${issue.validation.startsWith}"`; + } + else if ("endsWith" in issue.validation) { + message = `Invalid input: must end with "${issue.validation.endsWith}"`; + } + else { + util.assertNever(issue.validation); + } + } + else if (issue.validation !== "regex") { + message = `Invalid ${issue.validation}`; + } + else { + message = "Invalid"; + } + break; + case ZodIssueCode.too_small: + if (issue.type === "array") + message = `Array must contain ${issue.exact ? "exactly" : issue.inclusive ? `at least` : `more than`} ${issue.minimum} element(s)`; + else if (issue.type === "string") + message = `String must contain ${issue.exact ? "exactly" : issue.inclusive ? `at least` : `over`} ${issue.minimum} character(s)`; + else if (issue.type === "number") + message = `Number must be ${issue.exact + ? `exactly equal to ` + : issue.inclusive + ? `greater than or equal to ` + : `greater than `}${issue.minimum}`; + else if (issue.type === "date") + message = `Date must be ${issue.exact + ? `exactly equal to ` + : issue.inclusive + ? `greater than or equal to ` + : `greater than `}${new Date(Number(issue.minimum))}`; + else + message = "Invalid input"; + break; + case ZodIssueCode.too_big: + if (issue.type === "array") + message = `Array must contain ${issue.exact ? `exactly` : issue.inclusive ? `at most` : `less than`} ${issue.maximum} element(s)`; + else if (issue.type === "string") + message = `String must contain ${issue.exact ? `exactly` : issue.inclusive ? `at most` : `under`} ${issue.maximum} character(s)`; + else if (issue.type === "number") + message = `Number must be ${issue.exact + ? `exactly` + : issue.inclusive + ? `less than or equal to` + : `less than`} ${issue.maximum}`; + else if (issue.type === "bigint") + message = `BigInt must be ${issue.exact + ? `exactly` + : issue.inclusive + ? `less than or equal to` + : `less than`} ${issue.maximum}`; + else if (issue.type === "date") + message = `Date must be ${issue.exact + ? `exactly` + : issue.inclusive + ? `smaller than or equal to` + : `smaller than`} ${new Date(Number(issue.maximum))}`; + else + message = "Invalid input"; + break; + case ZodIssueCode.custom: + message = `Invalid input`; + break; + case ZodIssueCode.invalid_intersection_types: + message = `Intersection results could not be merged`; + break; + case ZodIssueCode.not_multiple_of: + message = `Number must be a multiple of ${issue.multipleOf}`; + break; + case ZodIssueCode.not_finite: + message = "Number must be finite"; + break; + default: + message = _ctx.defaultError; + util.assertNever(issue); + } + return { message }; +}; + +let overrideErrorMap = errorMap; +function setErrorMap(map) { + overrideErrorMap = map; +} +function getErrorMap() { + return overrideErrorMap; +} + +const makeIssue = (params) => { + const { data, path, errorMaps, issueData } = params; + const fullPath = [...path, ...(issueData.path || [])]; + const fullIssue = { + ...issueData, + path: fullPath, + }; + if (issueData.message !== undefined) { + return { + ...issueData, + path: fullPath, + message: issueData.message, + }; + } + let errorMessage = ""; + const maps = errorMaps + .filter((m) => !!m) + .slice() + .reverse(); + for (const map of maps) { + errorMessage = map(fullIssue, { data, defaultError: errorMessage }).message; + } + return { + ...issueData, + path: fullPath, + message: errorMessage, + }; +}; +const EMPTY_PATH = []; +function addIssueToContext(ctx, issueData) { + const overrideMap = getErrorMap(); + const issue = makeIssue({ + issueData: issueData, + data: ctx.data, + path: ctx.path, + errorMaps: [ + ctx.common.contextualErrorMap, // contextual error map is first priority + ctx.schemaErrorMap, // then schema-bound map if available + overrideMap, // then global override map + overrideMap === errorMap ? undefined : errorMap, // then global default map + ].filter((x) => !!x), + }); + ctx.common.issues.push(issue); +} +class ParseStatus { + constructor() { + this.value = "valid"; + } + dirty() { + if (this.value === "valid") + this.value = "dirty"; + } + abort() { + if (this.value !== "aborted") + this.value = "aborted"; + } + static mergeArray(status, results) { + const arrayValue = []; + for (const s of results) { + if (s.status === "aborted") + return INVALID; + if (s.status === "dirty") + status.dirty(); + arrayValue.push(s.value); + } + return { status: status.value, value: arrayValue }; + } + static async mergeObjectAsync(status, pairs) { + const syncPairs = []; + for (const pair of pairs) { + const key = await pair.key; + const value = await pair.value; + syncPairs.push({ + key, + value, + }); + } + return ParseStatus.mergeObjectSync(status, syncPairs); + } + static mergeObjectSync(status, pairs) { + const finalObject = {}; + for (const pair of pairs) { + const { key, value } = pair; + if (key.status === "aborted") + return INVALID; + if (value.status === "aborted") + return INVALID; + if (key.status === "dirty") + status.dirty(); + if (value.status === "dirty") + status.dirty(); + if (key.value !== "__proto__" && + (typeof value.value !== "undefined" || pair.alwaysSet)) { + finalObject[key.value] = value.value; + } + } + return { status: status.value, value: finalObject }; + } +} +const INVALID = Object.freeze({ + status: "aborted", +}); +const DIRTY = (value) => ({ status: "dirty", value }); +const OK = (value) => ({ status: "valid", value }); +const isAborted = (x) => x.status === "aborted"; +const isDirty = (x) => x.status === "dirty"; +const isValid = (x) => x.status === "valid"; +const isAsync = (x) => typeof Promise !== "undefined" && x instanceof Promise; + +/****************************************************************************** +Copyright (c) Microsoft Corporation. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. +***************************************************************************** */ + +function __classPrivateFieldGet(receiver, state, kind, f) { + if (typeof state === "function" ? receiver !== state || true : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); + return state.get(receiver); +} + +function __classPrivateFieldSet(receiver, state, value, kind, f) { + if (typeof state === "function" ? receiver !== state || true : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); + return (state.set(receiver, value)), value; +} + +typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { + var e = new Error(message); + return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; +}; + +var errorUtil; +(function (errorUtil) { + errorUtil.errToObj = (message) => typeof message === "string" ? { message } : message || {}; + errorUtil.toString = (message) => typeof message === "string" ? message : message === null || message === void 0 ? void 0 : message.message; +})(errorUtil || (errorUtil = {})); + +var _ZodEnum_cache, _ZodNativeEnum_cache; +class ParseInputLazyPath { + constructor(parent, value, path, key) { + this._cachedPath = []; + this.parent = parent; + this.data = value; + this._path = path; + this._key = key; + } + get path() { + if (!this._cachedPath.length) { + if (this._key instanceof Array) { + this._cachedPath.push(...this._path, ...this._key); + } + else { + this._cachedPath.push(...this._path, this._key); + } + } + return this._cachedPath; + } +} +const handleResult = (ctx, result) => { + if (isValid(result)) { + return { success: true, data: result.value }; + } + else { + if (!ctx.common.issues.length) { + throw new Error("Validation failed but no issues detected."); + } + return { + success: false, + get error() { + if (this._error) + return this._error; + const error = new ZodError(ctx.common.issues); + this._error = error; + return this._error; + }, + }; + } +}; +function processCreateParams(params) { + if (!params) + return {}; + const { errorMap, invalid_type_error, required_error, description } = params; + if (errorMap && (invalid_type_error || required_error)) { + throw new Error(`Can't use "invalid_type_error" or "required_error" in conjunction with custom error map.`); + } + if (errorMap) + return { errorMap: errorMap, description }; + const customMap = (iss, ctx) => { + var _a, _b; + const { message } = params; + if (iss.code === "invalid_enum_value") { + return { message: message !== null && message !== void 0 ? message : ctx.defaultError }; + } + if (typeof ctx.data === "undefined") { + return { message: (_a = message !== null && message !== void 0 ? message : required_error) !== null && _a !== void 0 ? _a : ctx.defaultError }; + } + if (iss.code !== "invalid_type") + return { message: ctx.defaultError }; + return { message: (_b = message !== null && message !== void 0 ? message : invalid_type_error) !== null && _b !== void 0 ? _b : ctx.defaultError }; + }; + return { errorMap: customMap, description }; +} +class ZodType { + get description() { + return this._def.description; + } + _getType(input) { + return getParsedType(input.data); + } + _getOrReturnCtx(input, ctx) { + return (ctx || { + common: input.parent.common, + data: input.data, + parsedType: getParsedType(input.data), + schemaErrorMap: this._def.errorMap, + path: input.path, + parent: input.parent, + }); + } + _processInputParams(input) { + return { + status: new ParseStatus(), + ctx: { + common: input.parent.common, + data: input.data, + parsedType: getParsedType(input.data), + schemaErrorMap: this._def.errorMap, + path: input.path, + parent: input.parent, + }, + }; + } + _parseSync(input) { + const result = this._parse(input); + if (isAsync(result)) { + throw new Error("Synchronous parse encountered promise."); + } + return result; + } + _parseAsync(input) { + const result = this._parse(input); + return Promise.resolve(result); + } + parse(data, params) { + const result = this.safeParse(data, params); + if (result.success) + return result.data; + throw result.error; + } + safeParse(data, params) { + var _a; + const ctx = { + common: { + issues: [], + async: (_a = params === null || params === void 0 ? void 0 : params.async) !== null && _a !== void 0 ? _a : false, + contextualErrorMap: params === null || params === void 0 ? void 0 : params.errorMap, + }, + path: (params === null || params === void 0 ? void 0 : params.path) || [], + schemaErrorMap: this._def.errorMap, + parent: null, + data, + parsedType: getParsedType(data), + }; + const result = this._parseSync({ data, path: ctx.path, parent: ctx }); + return handleResult(ctx, result); + } + "~validate"(data) { + var _a, _b; + const ctx = { + common: { + issues: [], + async: !!this["~standard"].async, + }, + path: [], + schemaErrorMap: this._def.errorMap, + parent: null, + data, + parsedType: getParsedType(data), + }; + if (!this["~standard"].async) { + try { + const result = this._parseSync({ data, path: [], parent: ctx }); + return isValid(result) + ? { + value: result.value, + } + : { + issues: ctx.common.issues, + }; + } + catch (err) { + if ((_b = (_a = err === null || err === void 0 ? void 0 : err.message) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === null || _b === void 0 ? void 0 : _b.includes("encountered")) { + this["~standard"].async = true; + } + ctx.common = { + issues: [], + async: true, + }; + } + } + return this._parseAsync({ data, path: [], parent: ctx }).then((result) => isValid(result) + ? { + value: result.value, + } + : { + issues: ctx.common.issues, + }); + } + async parseAsync(data, params) { + const result = await this.safeParseAsync(data, params); + if (result.success) + return result.data; + throw result.error; + } + async safeParseAsync(data, params) { + const ctx = { + common: { + issues: [], + contextualErrorMap: params === null || params === void 0 ? void 0 : params.errorMap, + async: true, + }, + path: (params === null || params === void 0 ? void 0 : params.path) || [], + schemaErrorMap: this._def.errorMap, + parent: null, + data, + parsedType: getParsedType(data), + }; + const maybeAsyncResult = this._parse({ data, path: ctx.path, parent: ctx }); + const result = await (isAsync(maybeAsyncResult) + ? maybeAsyncResult + : Promise.resolve(maybeAsyncResult)); + return handleResult(ctx, result); + } + refine(check, message) { + const getIssueProperties = (val) => { + if (typeof message === "string" || typeof message === "undefined") { + return { message }; + } + else if (typeof message === "function") { + return message(val); + } + else { + return message; + } + }; + return this._refinement((val, ctx) => { + const result = check(val); + const setError = () => ctx.addIssue({ + code: ZodIssueCode.custom, + ...getIssueProperties(val), + }); + if (typeof Promise !== "undefined" && result instanceof Promise) { + return result.then((data) => { + if (!data) { + setError(); + return false; + } + else { + return true; + } + }); + } + if (!result) { + setError(); + return false; + } + else { + return true; + } + }); + } + refinement(check, refinementData) { + return this._refinement((val, ctx) => { + if (!check(val)) { + ctx.addIssue(typeof refinementData === "function" + ? refinementData(val, ctx) + : refinementData); + return false; + } + else { + return true; + } + }); + } + _refinement(refinement) { + return new ZodEffects({ + schema: this, + typeName: ZodFirstPartyTypeKind.ZodEffects, + effect: { type: "refinement", refinement }, + }); + } + superRefine(refinement) { + return this._refinement(refinement); + } + constructor(def) { + /** Alias of safeParseAsync */ + this.spa = this.safeParseAsync; + this._def = def; + this.parse = this.parse.bind(this); + this.safeParse = this.safeParse.bind(this); + this.parseAsync = this.parseAsync.bind(this); + this.safeParseAsync = this.safeParseAsync.bind(this); + this.spa = this.spa.bind(this); + this.refine = this.refine.bind(this); + this.refinement = this.refinement.bind(this); + this.superRefine = this.superRefine.bind(this); + this.optional = this.optional.bind(this); + this.nullable = this.nullable.bind(this); + this.nullish = this.nullish.bind(this); + this.array = this.array.bind(this); + this.promise = this.promise.bind(this); + this.or = this.or.bind(this); + this.and = this.and.bind(this); + this.transform = this.transform.bind(this); + this.brand = this.brand.bind(this); + this.default = this.default.bind(this); + this.catch = this.catch.bind(this); + this.describe = this.describe.bind(this); + this.pipe = this.pipe.bind(this); + this.readonly = this.readonly.bind(this); + this.isNullable = this.isNullable.bind(this); + this.isOptional = this.isOptional.bind(this); + this["~standard"] = { + version: 1, + vendor: "zod", + validate: (data) => this["~validate"](data), + }; + } + optional() { + return ZodOptional.create(this, this._def); + } + nullable() { + return ZodNullable.create(this, this._def); + } + nullish() { + return this.nullable().optional(); + } + array() { + return ZodArray.create(this); + } + promise() { + return ZodPromise.create(this, this._def); + } + or(option) { + return ZodUnion.create([this, option], this._def); + } + and(incoming) { + return ZodIntersection.create(this, incoming, this._def); + } + transform(transform) { + return new ZodEffects({ + ...processCreateParams(this._def), + schema: this, + typeName: ZodFirstPartyTypeKind.ZodEffects, + effect: { type: "transform", transform }, + }); + } + default(def) { + const defaultValueFunc = typeof def === "function" ? def : () => def; + return new ZodDefault({ + ...processCreateParams(this._def), + innerType: this, + defaultValue: defaultValueFunc, + typeName: ZodFirstPartyTypeKind.ZodDefault, + }); + } + brand() { + return new ZodBranded({ + typeName: ZodFirstPartyTypeKind.ZodBranded, + type: this, + ...processCreateParams(this._def), + }); + } + catch(def) { + const catchValueFunc = typeof def === "function" ? def : () => def; + return new ZodCatch({ + ...processCreateParams(this._def), + innerType: this, + catchValue: catchValueFunc, + typeName: ZodFirstPartyTypeKind.ZodCatch, + }); + } + describe(description) { + const This = this.constructor; + return new This({ + ...this._def, + description, + }); + } + pipe(target) { + return ZodPipeline.create(this, target); + } + readonly() { + return ZodReadonly.create(this); + } + isOptional() { + return this.safeParse(undefined).success; + } + isNullable() { + return this.safeParse(null).success; + } +} +const cuidRegex = /^c[^\s-]{8,}$/i; +const cuid2Regex = /^[0-9a-z]+$/; +const ulidRegex = /^[0-9A-HJKMNP-TV-Z]{26}$/i; +// const uuidRegex = +// /^([a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[a-f0-9]{4}-[a-f0-9]{12}|00000000-0000-0000-0000-000000000000)$/i; +const uuidRegex = /^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/i; +const nanoidRegex = /^[a-z0-9_-]{21}$/i; +const jwtRegex = /^[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]*$/; +const durationRegex = /^[-+]?P(?!$)(?:(?:[-+]?\d+Y)|(?:[-+]?\d+[.,]\d+Y$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:(?:[-+]?\d+W)|(?:[-+]?\d+[.,]\d+W$))?(?:(?:[-+]?\d+D)|(?:[-+]?\d+[.,]\d+D$))?(?:T(?=[\d+-])(?:(?:[-+]?\d+H)|(?:[-+]?\d+[.,]\d+H$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:[-+]?\d+(?:[.,]\d+)?S)?)??$/; +// from https://stackoverflow.com/a/46181/1550155 +// old version: too slow, didn't support unicode +// const emailRegex = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i; +//old email regex +// const emailRegex = /^(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@((?!-)([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{1,})[^-<>()[\].,;:\s@"]$/i; +// eslint-disable-next-line +// const emailRegex = +// /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\])|(\[IPv6:(([a-f0-9]{1,4}:){7}|::([a-f0-9]{1,4}:){0,6}|([a-f0-9]{1,4}:){1}:([a-f0-9]{1,4}:){0,5}|([a-f0-9]{1,4}:){2}:([a-f0-9]{1,4}:){0,4}|([a-f0-9]{1,4}:){3}:([a-f0-9]{1,4}:){0,3}|([a-f0-9]{1,4}:){4}:([a-f0-9]{1,4}:){0,2}|([a-f0-9]{1,4}:){5}:([a-f0-9]{1,4}:){0,1})([a-f0-9]{1,4}|(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2})))\])|([A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])*(\.[A-Za-z]{2,})+))$/; +// const emailRegex = +// /^[a-zA-Z0-9\.\!\#\$\%\&\'\*\+\/\=\?\^\_\`\{\|\}\~\-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/; +// const emailRegex = +// /^(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])$/i; +const emailRegex = /^(?!\.)(?!.*\.\.)([A-Z0-9_'+\-\.]*)[A-Z0-9_+-]@([A-Z0-9][A-Z0-9\-]*\.)+[A-Z]{2,}$/i; +// const emailRegex = +// /^[a-z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-z0-9-]+(?:\.[a-z0-9\-]+)*$/i; +// from https://thekevinscott.com/emojis-in-javascript/#writing-a-regular-expression +const _emojiRegex = `^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$`; +let emojiRegex; +// faster, simpler, safer +const ipv4Regex = /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/; +const ipv4CidrRegex = /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/(3[0-2]|[12]?[0-9])$/; +// const ipv6Regex = +// /^(([a-f0-9]{1,4}:){7}|::([a-f0-9]{1,4}:){0,6}|([a-f0-9]{1,4}:){1}:([a-f0-9]{1,4}:){0,5}|([a-f0-9]{1,4}:){2}:([a-f0-9]{1,4}:){0,4}|([a-f0-9]{1,4}:){3}:([a-f0-9]{1,4}:){0,3}|([a-f0-9]{1,4}:){4}:([a-f0-9]{1,4}:){0,2}|([a-f0-9]{1,4}:){5}:([a-f0-9]{1,4}:){0,1})([a-f0-9]{1,4}|(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2})))$/; +const ipv6Regex = /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/; +const ipv6CidrRegex = /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$/; +// https://stackoverflow.com/questions/7860392/determine-if-string-is-in-base64-using-javascript +const base64Regex = /^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/; +// https://base64.guru/standards/base64url +const base64urlRegex = /^([0-9a-zA-Z-_]{4})*(([0-9a-zA-Z-_]{2}(==)?)|([0-9a-zA-Z-_]{3}(=)?))?$/; +// simple +// const dateRegexSource = `\\d{4}-\\d{2}-\\d{2}`; +// no leap year validation +// const dateRegexSource = `\\d{4}-((0[13578]|10|12)-31|(0[13-9]|1[0-2])-30|(0[1-9]|1[0-2])-(0[1-9]|1\\d|2\\d))`; +// with leap year validation +const dateRegexSource = `((\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-((0[13578]|1[02])-(0[1-9]|[12]\\d|3[01])|(0[469]|11)-(0[1-9]|[12]\\d|30)|(02)-(0[1-9]|1\\d|2[0-8])))`; +const dateRegex = new RegExp(`^${dateRegexSource}$`); +function timeRegexSource(args) { + // let regex = `\\d{2}:\\d{2}:\\d{2}`; + let regex = `([01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d`; + if (args.precision) { + regex = `${regex}\\.\\d{${args.precision}}`; + } + else if (args.precision == null) { + regex = `${regex}(\\.\\d+)?`; + } + return regex; +} +function timeRegex(args) { + return new RegExp(`^${timeRegexSource(args)}$`); +} +// Adapted from https://stackoverflow.com/a/3143231 +function datetimeRegex(args) { + let regex = `${dateRegexSource}T${timeRegexSource(args)}`; + const opts = []; + opts.push(args.local ? `Z?` : `Z`); + if (args.offset) + opts.push(`([+-]\\d{2}:?\\d{2})`); + regex = `${regex}(${opts.join("|")})`; + return new RegExp(`^${regex}$`); +} +function isValidIP(ip, version) { + if ((version === "v4" || !version) && ipv4Regex.test(ip)) { + return true; + } + if ((version === "v6" || !version) && ipv6Regex.test(ip)) { + return true; + } + return false; +} +function isValidJWT(jwt, alg) { + if (!jwtRegex.test(jwt)) + return false; + try { + const [header] = jwt.split("."); + // Convert base64url to base64 + const base64 = header + .replace(/-/g, "+") + .replace(/_/g, "/") + .padEnd(header.length + ((4 - (header.length % 4)) % 4), "="); + const decoded = JSON.parse(atob(base64)); + if (typeof decoded !== "object" || decoded === null) + return false; + if (!decoded.typ || !decoded.alg) + return false; + if (alg && decoded.alg !== alg) + return false; + return true; + } + catch (_a) { + return false; + } +} +function isValidCidr(ip, version) { + if ((version === "v4" || !version) && ipv4CidrRegex.test(ip)) { + return true; + } + if ((version === "v6" || !version) && ipv6CidrRegex.test(ip)) { + return true; + } + return false; +} +class ZodString extends ZodType { + _parse(input) { + if (this._def.coerce) { + input.data = String(input.data); + } + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.string) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.string, + received: ctx.parsedType, + }); + return INVALID; + } + const status = new ParseStatus(); + let ctx = undefined; + for (const check of this._def.checks) { + if (check.kind === "min") { + if (input.data.length < check.value) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: check.value, + type: "string", + inclusive: true, + exact: false, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "max") { + if (input.data.length > check.value) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: check.value, + type: "string", + inclusive: true, + exact: false, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "length") { + const tooBig = input.data.length > check.value; + const tooSmall = input.data.length < check.value; + if (tooBig || tooSmall) { + ctx = this._getOrReturnCtx(input, ctx); + if (tooBig) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: check.value, + type: "string", + inclusive: true, + exact: true, + message: check.message, + }); + } + else if (tooSmall) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: check.value, + type: "string", + inclusive: true, + exact: true, + message: check.message, + }); + } + status.dirty(); + } + } + else if (check.kind === "email") { + if (!emailRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "email", + code: ZodIssueCode.invalid_string, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "emoji") { + if (!emojiRegex) { + emojiRegex = new RegExp(_emojiRegex, "u"); + } + if (!emojiRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "emoji", + code: ZodIssueCode.invalid_string, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "uuid") { + if (!uuidRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "uuid", + code: ZodIssueCode.invalid_string, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "nanoid") { + if (!nanoidRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "nanoid", + code: ZodIssueCode.invalid_string, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "cuid") { + if (!cuidRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "cuid", + code: ZodIssueCode.invalid_string, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "cuid2") { + if (!cuid2Regex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "cuid2", + code: ZodIssueCode.invalid_string, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "ulid") { + if (!ulidRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "ulid", + code: ZodIssueCode.invalid_string, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "url") { + try { + new URL(input.data); + } + catch (_a) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "url", + code: ZodIssueCode.invalid_string, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "regex") { + check.regex.lastIndex = 0; + const testResult = check.regex.test(input.data); + if (!testResult) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "regex", + code: ZodIssueCode.invalid_string, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "trim") { + input.data = input.data.trim(); + } + else if (check.kind === "includes") { + if (!input.data.includes(check.value, check.position)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: { includes: check.value, position: check.position }, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "toLowerCase") { + input.data = input.data.toLowerCase(); + } + else if (check.kind === "toUpperCase") { + input.data = input.data.toUpperCase(); + } + else if (check.kind === "startsWith") { + if (!input.data.startsWith(check.value)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: { startsWith: check.value }, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "endsWith") { + if (!input.data.endsWith(check.value)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: { endsWith: check.value }, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "datetime") { + const regex = datetimeRegex(check); + if (!regex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: "datetime", + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "date") { + const regex = dateRegex; + if (!regex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: "date", + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "time") { + const regex = timeRegex(check); + if (!regex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: "time", + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "duration") { + if (!durationRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "duration", + code: ZodIssueCode.invalid_string, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "ip") { + if (!isValidIP(input.data, check.version)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "ip", + code: ZodIssueCode.invalid_string, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "jwt") { + if (!isValidJWT(input.data, check.alg)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "jwt", + code: ZodIssueCode.invalid_string, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "cidr") { + if (!isValidCidr(input.data, check.version)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "cidr", + code: ZodIssueCode.invalid_string, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "base64") { + if (!base64Regex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "base64", + code: ZodIssueCode.invalid_string, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "base64url") { + if (!base64urlRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "base64url", + code: ZodIssueCode.invalid_string, + message: check.message, + }); + status.dirty(); + } + } + else { + util.assertNever(check); + } + } + return { status: status.value, value: input.data }; + } + _regex(regex, validation, message) { + return this.refinement((data) => regex.test(data), { + validation, + code: ZodIssueCode.invalid_string, + ...errorUtil.errToObj(message), + }); + } + _addCheck(check) { + return new ZodString({ + ...this._def, + checks: [...this._def.checks, check], + }); + } + email(message) { + return this._addCheck({ kind: "email", ...errorUtil.errToObj(message) }); + } + url(message) { + return this._addCheck({ kind: "url", ...errorUtil.errToObj(message) }); + } + emoji(message) { + return this._addCheck({ kind: "emoji", ...errorUtil.errToObj(message) }); + } + uuid(message) { + return this._addCheck({ kind: "uuid", ...errorUtil.errToObj(message) }); + } + nanoid(message) { + return this._addCheck({ kind: "nanoid", ...errorUtil.errToObj(message) }); + } + cuid(message) { + return this._addCheck({ kind: "cuid", ...errorUtil.errToObj(message) }); + } + cuid2(message) { + return this._addCheck({ kind: "cuid2", ...errorUtil.errToObj(message) }); + } + ulid(message) { + return this._addCheck({ kind: "ulid", ...errorUtil.errToObj(message) }); + } + base64(message) { + return this._addCheck({ kind: "base64", ...errorUtil.errToObj(message) }); + } + base64url(message) { + // base64url encoding is a modification of base64 that can safely be used in URLs and filenames + return this._addCheck({ + kind: "base64url", + ...errorUtil.errToObj(message), + }); + } + jwt(options) { + return this._addCheck({ kind: "jwt", ...errorUtil.errToObj(options) }); + } + ip(options) { + return this._addCheck({ kind: "ip", ...errorUtil.errToObj(options) }); + } + cidr(options) { + return this._addCheck({ kind: "cidr", ...errorUtil.errToObj(options) }); + } + datetime(options) { + var _a, _b; + if (typeof options === "string") { + return this._addCheck({ + kind: "datetime", + precision: null, + offset: false, + local: false, + message: options, + }); + } + return this._addCheck({ + kind: "datetime", + precision: typeof (options === null || options === void 0 ? void 0 : options.precision) === "undefined" ? null : options === null || options === void 0 ? void 0 : options.precision, + offset: (_a = options === null || options === void 0 ? void 0 : options.offset) !== null && _a !== void 0 ? _a : false, + local: (_b = options === null || options === void 0 ? void 0 : options.local) !== null && _b !== void 0 ? _b : false, + ...errorUtil.errToObj(options === null || options === void 0 ? void 0 : options.message), + }); + } + date(message) { + return this._addCheck({ kind: "date", message }); + } + time(options) { + if (typeof options === "string") { + return this._addCheck({ + kind: "time", + precision: null, + message: options, + }); + } + return this._addCheck({ + kind: "time", + precision: typeof (options === null || options === void 0 ? void 0 : options.precision) === "undefined" ? null : options === null || options === void 0 ? void 0 : options.precision, + ...errorUtil.errToObj(options === null || options === void 0 ? void 0 : options.message), + }); + } + duration(message) { + return this._addCheck({ kind: "duration", ...errorUtil.errToObj(message) }); + } + regex(regex, message) { + return this._addCheck({ + kind: "regex", + regex: regex, + ...errorUtil.errToObj(message), + }); + } + includes(value, options) { + return this._addCheck({ + kind: "includes", + value: value, + position: options === null || options === void 0 ? void 0 : options.position, + ...errorUtil.errToObj(options === null || options === void 0 ? void 0 : options.message), + }); + } + startsWith(value, message) { + return this._addCheck({ + kind: "startsWith", + value: value, + ...errorUtil.errToObj(message), + }); + } + endsWith(value, message) { + return this._addCheck({ + kind: "endsWith", + value: value, + ...errorUtil.errToObj(message), + }); + } + min(minLength, message) { + return this._addCheck({ + kind: "min", + value: minLength, + ...errorUtil.errToObj(message), + }); + } + max(maxLength, message) { + return this._addCheck({ + kind: "max", + value: maxLength, + ...errorUtil.errToObj(message), + }); + } + length(len, message) { + return this._addCheck({ + kind: "length", + value: len, + ...errorUtil.errToObj(message), + }); + } + /** + * Equivalent to `.min(1)` + */ + nonempty(message) { + return this.min(1, errorUtil.errToObj(message)); + } + trim() { + return new ZodString({ + ...this._def, + checks: [...this._def.checks, { kind: "trim" }], + }); + } + toLowerCase() { + return new ZodString({ + ...this._def, + checks: [...this._def.checks, { kind: "toLowerCase" }], + }); + } + toUpperCase() { + return new ZodString({ + ...this._def, + checks: [...this._def.checks, { kind: "toUpperCase" }], + }); + } + get isDatetime() { + return !!this._def.checks.find((ch) => ch.kind === "datetime"); + } + get isDate() { + return !!this._def.checks.find((ch) => ch.kind === "date"); + } + get isTime() { + return !!this._def.checks.find((ch) => ch.kind === "time"); + } + get isDuration() { + return !!this._def.checks.find((ch) => ch.kind === "duration"); + } + get isEmail() { + return !!this._def.checks.find((ch) => ch.kind === "email"); + } + get isURL() { + return !!this._def.checks.find((ch) => ch.kind === "url"); + } + get isEmoji() { + return !!this._def.checks.find((ch) => ch.kind === "emoji"); + } + get isUUID() { + return !!this._def.checks.find((ch) => ch.kind === "uuid"); + } + get isNANOID() { + return !!this._def.checks.find((ch) => ch.kind === "nanoid"); + } + get isCUID() { + return !!this._def.checks.find((ch) => ch.kind === "cuid"); + } + get isCUID2() { + return !!this._def.checks.find((ch) => ch.kind === "cuid2"); + } + get isULID() { + return !!this._def.checks.find((ch) => ch.kind === "ulid"); + } + get isIP() { + return !!this._def.checks.find((ch) => ch.kind === "ip"); + } + get isCIDR() { + return !!this._def.checks.find((ch) => ch.kind === "cidr"); + } + get isBase64() { + return !!this._def.checks.find((ch) => ch.kind === "base64"); + } + get isBase64url() { + // base64url encoding is a modification of base64 that can safely be used in URLs and filenames + return !!this._def.checks.find((ch) => ch.kind === "base64url"); + } + get minLength() { + let min = null; + for (const ch of this._def.checks) { + if (ch.kind === "min") { + if (min === null || ch.value > min) + min = ch.value; + } + } + return min; + } + get maxLength() { + let max = null; + for (const ch of this._def.checks) { + if (ch.kind === "max") { + if (max === null || ch.value < max) + max = ch.value; + } + } + return max; + } +} +ZodString.create = (params) => { + var _a; + return new ZodString({ + checks: [], + typeName: ZodFirstPartyTypeKind.ZodString, + coerce: (_a = params === null || params === void 0 ? void 0 : params.coerce) !== null && _a !== void 0 ? _a : false, + ...processCreateParams(params), + }); +}; +// https://stackoverflow.com/questions/3966484/why-does-modulus-operator-return-fractional-number-in-javascript/31711034#31711034 +function floatSafeRemainder(val, step) { + const valDecCount = (val.toString().split(".")[1] || "").length; + const stepDecCount = (step.toString().split(".")[1] || "").length; + const decCount = valDecCount > stepDecCount ? valDecCount : stepDecCount; + const valInt = parseInt(val.toFixed(decCount).replace(".", "")); + const stepInt = parseInt(step.toFixed(decCount).replace(".", "")); + return (valInt % stepInt) / Math.pow(10, decCount); +} +class ZodNumber extends ZodType { + constructor() { + super(...arguments); + this.min = this.gte; + this.max = this.lte; + this.step = this.multipleOf; + } + _parse(input) { + if (this._def.coerce) { + input.data = Number(input.data); + } + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.number) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.number, + received: ctx.parsedType, + }); + return INVALID; + } + let ctx = undefined; + const status = new ParseStatus(); + for (const check of this._def.checks) { + if (check.kind === "int") { + if (!util.isInteger(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: "integer", + received: "float", + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "min") { + const tooSmall = check.inclusive + ? input.data < check.value + : input.data <= check.value; + if (tooSmall) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: check.value, + type: "number", + inclusive: check.inclusive, + exact: false, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "max") { + const tooBig = check.inclusive + ? input.data > check.value + : input.data >= check.value; + if (tooBig) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: check.value, + type: "number", + inclusive: check.inclusive, + exact: false, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "multipleOf") { + if (floatSafeRemainder(input.data, check.value) !== 0) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.not_multiple_of, + multipleOf: check.value, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "finite") { + if (!Number.isFinite(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.not_finite, + message: check.message, + }); + status.dirty(); + } + } + else { + util.assertNever(check); + } + } + return { status: status.value, value: input.data }; + } + gte(value, message) { + return this.setLimit("min", value, true, errorUtil.toString(message)); + } + gt(value, message) { + return this.setLimit("min", value, false, errorUtil.toString(message)); + } + lte(value, message) { + return this.setLimit("max", value, true, errorUtil.toString(message)); + } + lt(value, message) { + return this.setLimit("max", value, false, errorUtil.toString(message)); + } + setLimit(kind, value, inclusive, message) { + return new ZodNumber({ + ...this._def, + checks: [ + ...this._def.checks, + { + kind, + value, + inclusive, + message: errorUtil.toString(message), + }, + ], + }); + } + _addCheck(check) { + return new ZodNumber({ + ...this._def, + checks: [...this._def.checks, check], + }); + } + int(message) { + return this._addCheck({ + kind: "int", + message: errorUtil.toString(message), + }); + } + positive(message) { + return this._addCheck({ + kind: "min", + value: 0, + inclusive: false, + message: errorUtil.toString(message), + }); + } + negative(message) { + return this._addCheck({ + kind: "max", + value: 0, + inclusive: false, + message: errorUtil.toString(message), + }); + } + nonpositive(message) { + return this._addCheck({ + kind: "max", + value: 0, + inclusive: true, + message: errorUtil.toString(message), + }); + } + nonnegative(message) { + return this._addCheck({ + kind: "min", + value: 0, + inclusive: true, + message: errorUtil.toString(message), + }); + } + multipleOf(value, message) { + return this._addCheck({ + kind: "multipleOf", + value: value, + message: errorUtil.toString(message), + }); + } + finite(message) { + return this._addCheck({ + kind: "finite", + message: errorUtil.toString(message), + }); + } + safe(message) { + return this._addCheck({ + kind: "min", + inclusive: true, + value: Number.MIN_SAFE_INTEGER, + message: errorUtil.toString(message), + })._addCheck({ + kind: "max", + inclusive: true, + value: Number.MAX_SAFE_INTEGER, + message: errorUtil.toString(message), + }); + } + get minValue() { + let min = null; + for (const ch of this._def.checks) { + if (ch.kind === "min") { + if (min === null || ch.value > min) + min = ch.value; + } + } + return min; + } + get maxValue() { + let max = null; + for (const ch of this._def.checks) { + if (ch.kind === "max") { + if (max === null || ch.value < max) + max = ch.value; + } + } + return max; + } + get isInt() { + return !!this._def.checks.find((ch) => ch.kind === "int" || + (ch.kind === "multipleOf" && util.isInteger(ch.value))); + } + get isFinite() { + let max = null, min = null; + for (const ch of this._def.checks) { + if (ch.kind === "finite" || + ch.kind === "int" || + ch.kind === "multipleOf") { + return true; + } + else if (ch.kind === "min") { + if (min === null || ch.value > min) + min = ch.value; + } + else if (ch.kind === "max") { + if (max === null || ch.value < max) + max = ch.value; + } + } + return Number.isFinite(min) && Number.isFinite(max); + } +} +ZodNumber.create = (params) => { + return new ZodNumber({ + checks: [], + typeName: ZodFirstPartyTypeKind.ZodNumber, + coerce: (params === null || params === void 0 ? void 0 : params.coerce) || false, + ...processCreateParams(params), + }); +}; +class ZodBigInt extends ZodType { + constructor() { + super(...arguments); + this.min = this.gte; + this.max = this.lte; + } + _parse(input) { + if (this._def.coerce) { + try { + input.data = BigInt(input.data); + } + catch (_a) { + return this._getInvalidInput(input); + } + } + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.bigint) { + return this._getInvalidInput(input); + } + let ctx = undefined; + const status = new ParseStatus(); + for (const check of this._def.checks) { + if (check.kind === "min") { + const tooSmall = check.inclusive + ? input.data < check.value + : input.data <= check.value; + if (tooSmall) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + type: "bigint", + minimum: check.value, + inclusive: check.inclusive, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "max") { + const tooBig = check.inclusive + ? input.data > check.value + : input.data >= check.value; + if (tooBig) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + type: "bigint", + maximum: check.value, + inclusive: check.inclusive, + message: check.message, + }); + status.dirty(); + } + } + else if (check.kind === "multipleOf") { + if (input.data % check.value !== BigInt(0)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.not_multiple_of, + multipleOf: check.value, + message: check.message, + }); + status.dirty(); + } + } + else { + util.assertNever(check); + } + } + return { status: status.value, value: input.data }; + } + _getInvalidInput(input) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.bigint, + received: ctx.parsedType, + }); + return INVALID; + } + gte(value, message) { + return this.setLimit("min", value, true, errorUtil.toString(message)); + } + gt(value, message) { + return this.setLimit("min", value, false, errorUtil.toString(message)); + } + lte(value, message) { + return this.setLimit("max", value, true, errorUtil.toString(message)); + } + lt(value, message) { + return this.setLimit("max", value, false, errorUtil.toString(message)); + } + setLimit(kind, value, inclusive, message) { + return new ZodBigInt({ + ...this._def, + checks: [ + ...this._def.checks, + { + kind, + value, + inclusive, + message: errorUtil.toString(message), + }, + ], + }); + } + _addCheck(check) { + return new ZodBigInt({ + ...this._def, + checks: [...this._def.checks, check], + }); + } + positive(message) { + return this._addCheck({ + kind: "min", + value: BigInt(0), + inclusive: false, + message: errorUtil.toString(message), + }); + } + negative(message) { + return this._addCheck({ + kind: "max", + value: BigInt(0), + inclusive: false, + message: errorUtil.toString(message), + }); + } + nonpositive(message) { + return this._addCheck({ + kind: "max", + value: BigInt(0), + inclusive: true, + message: errorUtil.toString(message), + }); + } + nonnegative(message) { + return this._addCheck({ + kind: "min", + value: BigInt(0), + inclusive: true, + message: errorUtil.toString(message), + }); + } + multipleOf(value, message) { + return this._addCheck({ + kind: "multipleOf", + value, + message: errorUtil.toString(message), + }); + } + get minValue() { + let min = null; + for (const ch of this._def.checks) { + if (ch.kind === "min") { + if (min === null || ch.value > min) + min = ch.value; + } + } + return min; + } + get maxValue() { + let max = null; + for (const ch of this._def.checks) { + if (ch.kind === "max") { + if (max === null || ch.value < max) + max = ch.value; + } + } + return max; + } +} +ZodBigInt.create = (params) => { + var _a; + return new ZodBigInt({ + checks: [], + typeName: ZodFirstPartyTypeKind.ZodBigInt, + coerce: (_a = params === null || params === void 0 ? void 0 : params.coerce) !== null && _a !== void 0 ? _a : false, + ...processCreateParams(params), + }); +}; +class ZodBoolean extends ZodType { + _parse(input) { + if (this._def.coerce) { + input.data = Boolean(input.data); + } + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.boolean) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.boolean, + received: ctx.parsedType, + }); + return INVALID; + } + return OK(input.data); + } +} +ZodBoolean.create = (params) => { + return new ZodBoolean({ + typeName: ZodFirstPartyTypeKind.ZodBoolean, + coerce: (params === null || params === void 0 ? void 0 : params.coerce) || false, + ...processCreateParams(params), + }); +}; +class ZodDate extends ZodType { + _parse(input) { + if (this._def.coerce) { + input.data = new Date(input.data); + } + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.date) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.date, + received: ctx.parsedType, + }); + return INVALID; + } + if (isNaN(input.data.getTime())) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_date, + }); + return INVALID; + } + const status = new ParseStatus(); + let ctx = undefined; + for (const check of this._def.checks) { + if (check.kind === "min") { + if (input.data.getTime() < check.value) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + message: check.message, + inclusive: true, + exact: false, + minimum: check.value, + type: "date", + }); + status.dirty(); + } + } + else if (check.kind === "max") { + if (input.data.getTime() > check.value) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + message: check.message, + inclusive: true, + exact: false, + maximum: check.value, + type: "date", + }); + status.dirty(); + } + } + else { + util.assertNever(check); + } + } + return { + status: status.value, + value: new Date(input.data.getTime()), + }; + } + _addCheck(check) { + return new ZodDate({ + ...this._def, + checks: [...this._def.checks, check], + }); + } + min(minDate, message) { + return this._addCheck({ + kind: "min", + value: minDate.getTime(), + message: errorUtil.toString(message), + }); + } + max(maxDate, message) { + return this._addCheck({ + kind: "max", + value: maxDate.getTime(), + message: errorUtil.toString(message), + }); + } + get minDate() { + let min = null; + for (const ch of this._def.checks) { + if (ch.kind === "min") { + if (min === null || ch.value > min) + min = ch.value; + } + } + return min != null ? new Date(min) : null; + } + get maxDate() { + let max = null; + for (const ch of this._def.checks) { + if (ch.kind === "max") { + if (max === null || ch.value < max) + max = ch.value; + } + } + return max != null ? new Date(max) : null; + } +} +ZodDate.create = (params) => { + return new ZodDate({ + checks: [], + coerce: (params === null || params === void 0 ? void 0 : params.coerce) || false, + typeName: ZodFirstPartyTypeKind.ZodDate, + ...processCreateParams(params), + }); +}; +class ZodSymbol extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.symbol) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.symbol, + received: ctx.parsedType, + }); + return INVALID; + } + return OK(input.data); + } +} +ZodSymbol.create = (params) => { + return new ZodSymbol({ + typeName: ZodFirstPartyTypeKind.ZodSymbol, + ...processCreateParams(params), + }); +}; +class ZodUndefined extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.undefined) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.undefined, + received: ctx.parsedType, + }); + return INVALID; + } + return OK(input.data); + } +} +ZodUndefined.create = (params) => { + return new ZodUndefined({ + typeName: ZodFirstPartyTypeKind.ZodUndefined, + ...processCreateParams(params), + }); +}; +class ZodNull extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.null) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.null, + received: ctx.parsedType, + }); + return INVALID; + } + return OK(input.data); + } +} +ZodNull.create = (params) => { + return new ZodNull({ + typeName: ZodFirstPartyTypeKind.ZodNull, + ...processCreateParams(params), + }); +}; +class ZodAny extends ZodType { + constructor() { + super(...arguments); + // to prevent instances of other classes from extending ZodAny. this causes issues with catchall in ZodObject. + this._any = true; + } + _parse(input) { + return OK(input.data); + } +} +ZodAny.create = (params) => { + return new ZodAny({ + typeName: ZodFirstPartyTypeKind.ZodAny, + ...processCreateParams(params), + }); +}; +class ZodUnknown extends ZodType { + constructor() { + super(...arguments); + // required + this._unknown = true; + } + _parse(input) { + return OK(input.data); + } +} +ZodUnknown.create = (params) => { + return new ZodUnknown({ + typeName: ZodFirstPartyTypeKind.ZodUnknown, + ...processCreateParams(params), + }); +}; +class ZodNever extends ZodType { + _parse(input) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.never, + received: ctx.parsedType, + }); + return INVALID; + } +} +ZodNever.create = (params) => { + return new ZodNever({ + typeName: ZodFirstPartyTypeKind.ZodNever, + ...processCreateParams(params), + }); +}; +class ZodVoid extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.undefined) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.void, + received: ctx.parsedType, + }); + return INVALID; + } + return OK(input.data); + } +} +ZodVoid.create = (params) => { + return new ZodVoid({ + typeName: ZodFirstPartyTypeKind.ZodVoid, + ...processCreateParams(params), + }); +}; +class ZodArray extends ZodType { + _parse(input) { + const { ctx, status } = this._processInputParams(input); + const def = this._def; + if (ctx.parsedType !== ZodParsedType.array) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.array, + received: ctx.parsedType, + }); + return INVALID; + } + if (def.exactLength !== null) { + const tooBig = ctx.data.length > def.exactLength.value; + const tooSmall = ctx.data.length < def.exactLength.value; + if (tooBig || tooSmall) { + addIssueToContext(ctx, { + code: tooBig ? ZodIssueCode.too_big : ZodIssueCode.too_small, + minimum: (tooSmall ? def.exactLength.value : undefined), + maximum: (tooBig ? def.exactLength.value : undefined), + type: "array", + inclusive: true, + exact: true, + message: def.exactLength.message, + }); + status.dirty(); + } + } + if (def.minLength !== null) { + if (ctx.data.length < def.minLength.value) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: def.minLength.value, + type: "array", + inclusive: true, + exact: false, + message: def.minLength.message, + }); + status.dirty(); + } + } + if (def.maxLength !== null) { + if (ctx.data.length > def.maxLength.value) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: def.maxLength.value, + type: "array", + inclusive: true, + exact: false, + message: def.maxLength.message, + }); + status.dirty(); + } + } + if (ctx.common.async) { + return Promise.all([...ctx.data].map((item, i) => { + return def.type._parseAsync(new ParseInputLazyPath(ctx, item, ctx.path, i)); + })).then((result) => { + return ParseStatus.mergeArray(status, result); + }); + } + const result = [...ctx.data].map((item, i) => { + return def.type._parseSync(new ParseInputLazyPath(ctx, item, ctx.path, i)); + }); + return ParseStatus.mergeArray(status, result); + } + get element() { + return this._def.type; + } + min(minLength, message) { + return new ZodArray({ + ...this._def, + minLength: { value: minLength, message: errorUtil.toString(message) }, + }); + } + max(maxLength, message) { + return new ZodArray({ + ...this._def, + maxLength: { value: maxLength, message: errorUtil.toString(message) }, + }); + } + length(len, message) { + return new ZodArray({ + ...this._def, + exactLength: { value: len, message: errorUtil.toString(message) }, + }); + } + nonempty(message) { + return this.min(1, message); + } +} +ZodArray.create = (schema, params) => { + return new ZodArray({ + type: schema, + minLength: null, + maxLength: null, + exactLength: null, + typeName: ZodFirstPartyTypeKind.ZodArray, + ...processCreateParams(params), + }); +}; +function deepPartialify(schema) { + if (schema instanceof ZodObject) { + const newShape = {}; + for (const key in schema.shape) { + const fieldSchema = schema.shape[key]; + newShape[key] = ZodOptional.create(deepPartialify(fieldSchema)); + } + return new ZodObject({ + ...schema._def, + shape: () => newShape, + }); + } + else if (schema instanceof ZodArray) { + return new ZodArray({ + ...schema._def, + type: deepPartialify(schema.element), + }); + } + else if (schema instanceof ZodOptional) { + return ZodOptional.create(deepPartialify(schema.unwrap())); + } + else if (schema instanceof ZodNullable) { + return ZodNullable.create(deepPartialify(schema.unwrap())); + } + else if (schema instanceof ZodTuple) { + return ZodTuple.create(schema.items.map((item) => deepPartialify(item))); + } + else { + return schema; + } +} +class ZodObject extends ZodType { + constructor() { + super(...arguments); + this._cached = null; + /** + * @deprecated In most cases, this is no longer needed - unknown properties are now silently stripped. + * If you want to pass through unknown properties, use `.passthrough()` instead. + */ + this.nonstrict = this.passthrough; + // extend< + // Augmentation extends ZodRawShape, + // NewOutput extends util.flatten<{ + // [k in keyof Augmentation | keyof Output]: k extends keyof Augmentation + // ? Augmentation[k]["_output"] + // : k extends keyof Output + // ? Output[k] + // : never; + // }>, + // NewInput extends util.flatten<{ + // [k in keyof Augmentation | keyof Input]: k extends keyof Augmentation + // ? Augmentation[k]["_input"] + // : k extends keyof Input + // ? Input[k] + // : never; + // }> + // >( + // augmentation: Augmentation + // ): ZodObject< + // extendShape, + // UnknownKeys, + // Catchall, + // NewOutput, + // NewInput + // > { + // return new ZodObject({ + // ...this._def, + // shape: () => ({ + // ...this._def.shape(), + // ...augmentation, + // }), + // }) as any; + // } + /** + * @deprecated Use `.extend` instead + * */ + this.augment = this.extend; + } + _getCached() { + if (this._cached !== null) + return this._cached; + const shape = this._def.shape(); + const keys = util.objectKeys(shape); + return (this._cached = { shape, keys }); + } + _parse(input) { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.object) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.object, + received: ctx.parsedType, + }); + return INVALID; + } + const { status, ctx } = this._processInputParams(input); + const { shape, keys: shapeKeys } = this._getCached(); + const extraKeys = []; + if (!(this._def.catchall instanceof ZodNever && + this._def.unknownKeys === "strip")) { + for (const key in ctx.data) { + if (!shapeKeys.includes(key)) { + extraKeys.push(key); + } + } + } + const pairs = []; + for (const key of shapeKeys) { + const keyValidator = shape[key]; + const value = ctx.data[key]; + pairs.push({ + key: { status: "valid", value: key }, + value: keyValidator._parse(new ParseInputLazyPath(ctx, value, ctx.path, key)), + alwaysSet: key in ctx.data, + }); + } + if (this._def.catchall instanceof ZodNever) { + const unknownKeys = this._def.unknownKeys; + if (unknownKeys === "passthrough") { + for (const key of extraKeys) { + pairs.push({ + key: { status: "valid", value: key }, + value: { status: "valid", value: ctx.data[key] }, + }); + } + } + else if (unknownKeys === "strict") { + if (extraKeys.length > 0) { + addIssueToContext(ctx, { + code: ZodIssueCode.unrecognized_keys, + keys: extraKeys, + }); + status.dirty(); + } + } + else if (unknownKeys === "strip") ; + else { + throw new Error(`Internal ZodObject error: invalid unknownKeys value.`); + } + } + else { + // run catchall validation + const catchall = this._def.catchall; + for (const key of extraKeys) { + const value = ctx.data[key]; + pairs.push({ + key: { status: "valid", value: key }, + value: catchall._parse(new ParseInputLazyPath(ctx, value, ctx.path, key) //, ctx.child(key), value, getParsedType(value) + ), + alwaysSet: key in ctx.data, + }); + } + } + if (ctx.common.async) { + return Promise.resolve() + .then(async () => { + const syncPairs = []; + for (const pair of pairs) { + const key = await pair.key; + const value = await pair.value; + syncPairs.push({ + key, + value, + alwaysSet: pair.alwaysSet, + }); + } + return syncPairs; + }) + .then((syncPairs) => { + return ParseStatus.mergeObjectSync(status, syncPairs); + }); + } + else { + return ParseStatus.mergeObjectSync(status, pairs); + } + } + get shape() { + return this._def.shape(); + } + strict(message) { + errorUtil.errToObj; + return new ZodObject({ + ...this._def, + unknownKeys: "strict", + ...(message !== undefined + ? { + errorMap: (issue, ctx) => { + var _a, _b, _c, _d; + const defaultError = (_c = (_b = (_a = this._def).errorMap) === null || _b === void 0 ? void 0 : _b.call(_a, issue, ctx).message) !== null && _c !== void 0 ? _c : ctx.defaultError; + if (issue.code === "unrecognized_keys") + return { + message: (_d = errorUtil.errToObj(message).message) !== null && _d !== void 0 ? _d : defaultError, + }; + return { + message: defaultError, + }; + }, + } + : {}), + }); + } + strip() { + return new ZodObject({ + ...this._def, + unknownKeys: "strip", + }); + } + passthrough() { + return new ZodObject({ + ...this._def, + unknownKeys: "passthrough", + }); + } + // const AugmentFactory = + // (def: Def) => + // ( + // augmentation: Augmentation + // ): ZodObject< + // extendShape, Augmentation>, + // Def["unknownKeys"], + // Def["catchall"] + // > => { + // return new ZodObject({ + // ...def, + // shape: () => ({ + // ...def.shape(), + // ...augmentation, + // }), + // }) as any; + // }; + extend(augmentation) { + return new ZodObject({ + ...this._def, + shape: () => ({ + ...this._def.shape(), + ...augmentation, + }), + }); + } + /** + * Prior to zod@1.0.12 there was a bug in the + * inferred type of merged objects. Please + * upgrade if you are experiencing issues. + */ + merge(merging) { + const merged = new ZodObject({ + unknownKeys: merging._def.unknownKeys, + catchall: merging._def.catchall, + shape: () => ({ + ...this._def.shape(), + ...merging._def.shape(), + }), + typeName: ZodFirstPartyTypeKind.ZodObject, + }); + return merged; + } + // merge< + // Incoming extends AnyZodObject, + // Augmentation extends Incoming["shape"], + // NewOutput extends { + // [k in keyof Augmentation | keyof Output]: k extends keyof Augmentation + // ? Augmentation[k]["_output"] + // : k extends keyof Output + // ? Output[k] + // : never; + // }, + // NewInput extends { + // [k in keyof Augmentation | keyof Input]: k extends keyof Augmentation + // ? Augmentation[k]["_input"] + // : k extends keyof Input + // ? Input[k] + // : never; + // } + // >( + // merging: Incoming + // ): ZodObject< + // extendShape>, + // Incoming["_def"]["unknownKeys"], + // Incoming["_def"]["catchall"], + // NewOutput, + // NewInput + // > { + // const merged: any = new ZodObject({ + // unknownKeys: merging._def.unknownKeys, + // catchall: merging._def.catchall, + // shape: () => + // objectUtil.mergeShapes(this._def.shape(), merging._def.shape()), + // typeName: ZodFirstPartyTypeKind.ZodObject, + // }) as any; + // return merged; + // } + setKey(key, schema) { + return this.augment({ [key]: schema }); + } + // merge( + // merging: Incoming + // ): //ZodObject = (merging) => { + // ZodObject< + // extendShape>, + // Incoming["_def"]["unknownKeys"], + // Incoming["_def"]["catchall"] + // > { + // // const mergedShape = objectUtil.mergeShapes( + // // this._def.shape(), + // // merging._def.shape() + // // ); + // const merged: any = new ZodObject({ + // unknownKeys: merging._def.unknownKeys, + // catchall: merging._def.catchall, + // shape: () => + // objectUtil.mergeShapes(this._def.shape(), merging._def.shape()), + // typeName: ZodFirstPartyTypeKind.ZodObject, + // }) as any; + // return merged; + // } + catchall(index) { + return new ZodObject({ + ...this._def, + catchall: index, + }); + } + pick(mask) { + const shape = {}; + util.objectKeys(mask).forEach((key) => { + if (mask[key] && this.shape[key]) { + shape[key] = this.shape[key]; + } + }); + return new ZodObject({ + ...this._def, + shape: () => shape, + }); + } + omit(mask) { + const shape = {}; + util.objectKeys(this.shape).forEach((key) => { + if (!mask[key]) { + shape[key] = this.shape[key]; + } + }); + return new ZodObject({ + ...this._def, + shape: () => shape, + }); + } + /** + * @deprecated + */ + deepPartial() { + return deepPartialify(this); + } + partial(mask) { + const newShape = {}; + util.objectKeys(this.shape).forEach((key) => { + const fieldSchema = this.shape[key]; + if (mask && !mask[key]) { + newShape[key] = fieldSchema; + } + else { + newShape[key] = fieldSchema.optional(); + } + }); + return new ZodObject({ + ...this._def, + shape: () => newShape, + }); + } + required(mask) { + const newShape = {}; + util.objectKeys(this.shape).forEach((key) => { + if (mask && !mask[key]) { + newShape[key] = this.shape[key]; + } + else { + const fieldSchema = this.shape[key]; + let newField = fieldSchema; + while (newField instanceof ZodOptional) { + newField = newField._def.innerType; + } + newShape[key] = newField; + } + }); + return new ZodObject({ + ...this._def, + shape: () => newShape, + }); + } + keyof() { + return createZodEnum(util.objectKeys(this.shape)); + } +} +ZodObject.create = (shape, params) => { + return new ZodObject({ + shape: () => shape, + unknownKeys: "strip", + catchall: ZodNever.create(), + typeName: ZodFirstPartyTypeKind.ZodObject, + ...processCreateParams(params), + }); +}; +ZodObject.strictCreate = (shape, params) => { + return new ZodObject({ + shape: () => shape, + unknownKeys: "strict", + catchall: ZodNever.create(), + typeName: ZodFirstPartyTypeKind.ZodObject, + ...processCreateParams(params), + }); +}; +ZodObject.lazycreate = (shape, params) => { + return new ZodObject({ + shape, + unknownKeys: "strip", + catchall: ZodNever.create(), + typeName: ZodFirstPartyTypeKind.ZodObject, + ...processCreateParams(params), + }); +}; +class ZodUnion extends ZodType { + _parse(input) { + const { ctx } = this._processInputParams(input); + const options = this._def.options; + function handleResults(results) { + // return first issue-free validation if it exists + for (const result of results) { + if (result.result.status === "valid") { + return result.result; + } + } + for (const result of results) { + if (result.result.status === "dirty") { + // add issues from dirty option + ctx.common.issues.push(...result.ctx.common.issues); + return result.result; + } + } + // return invalid + const unionErrors = results.map((result) => new ZodError(result.ctx.common.issues)); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_union, + unionErrors, + }); + return INVALID; + } + if (ctx.common.async) { + return Promise.all(options.map(async (option) => { + const childCtx = { + ...ctx, + common: { + ...ctx.common, + issues: [], + }, + parent: null, + }; + return { + result: await option._parseAsync({ + data: ctx.data, + path: ctx.path, + parent: childCtx, + }), + ctx: childCtx, + }; + })).then(handleResults); + } + else { + let dirty = undefined; + const issues = []; + for (const option of options) { + const childCtx = { + ...ctx, + common: { + ...ctx.common, + issues: [], + }, + parent: null, + }; + const result = option._parseSync({ + data: ctx.data, + path: ctx.path, + parent: childCtx, + }); + if (result.status === "valid") { + return result; + } + else if (result.status === "dirty" && !dirty) { + dirty = { result, ctx: childCtx }; + } + if (childCtx.common.issues.length) { + issues.push(childCtx.common.issues); + } + } + if (dirty) { + ctx.common.issues.push(...dirty.ctx.common.issues); + return dirty.result; + } + const unionErrors = issues.map((issues) => new ZodError(issues)); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_union, + unionErrors, + }); + return INVALID; + } + } + get options() { + return this._def.options; + } +} +ZodUnion.create = (types, params) => { + return new ZodUnion({ + options: types, + typeName: ZodFirstPartyTypeKind.ZodUnion, + ...processCreateParams(params), + }); +}; +///////////////////////////////////////////////////// +///////////////////////////////////////////////////// +////////// ////////// +////////// ZodDiscriminatedUnion ////////// +////////// ////////// +///////////////////////////////////////////////////// +///////////////////////////////////////////////////// +const getDiscriminator = (type) => { + if (type instanceof ZodLazy) { + return getDiscriminator(type.schema); + } + else if (type instanceof ZodEffects) { + return getDiscriminator(type.innerType()); + } + else if (type instanceof ZodLiteral) { + return [type.value]; + } + else if (type instanceof ZodEnum) { + return type.options; + } + else if (type instanceof ZodNativeEnum) { + // eslint-disable-next-line ban/ban + return util.objectValues(type.enum); + } + else if (type instanceof ZodDefault) { + return getDiscriminator(type._def.innerType); + } + else if (type instanceof ZodUndefined) { + return [undefined]; + } + else if (type instanceof ZodNull) { + return [null]; + } + else if (type instanceof ZodOptional) { + return [undefined, ...getDiscriminator(type.unwrap())]; + } + else if (type instanceof ZodNullable) { + return [null, ...getDiscriminator(type.unwrap())]; + } + else if (type instanceof ZodBranded) { + return getDiscriminator(type.unwrap()); + } + else if (type instanceof ZodReadonly) { + return getDiscriminator(type.unwrap()); + } + else if (type instanceof ZodCatch) { + return getDiscriminator(type._def.innerType); + } + else { + return []; + } +}; +class ZodDiscriminatedUnion extends ZodType { + _parse(input) { + const { ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.object) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.object, + received: ctx.parsedType, + }); + return INVALID; + } + const discriminator = this.discriminator; + const discriminatorValue = ctx.data[discriminator]; + const option = this.optionsMap.get(discriminatorValue); + if (!option) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_union_discriminator, + options: Array.from(this.optionsMap.keys()), + path: [discriminator], + }); + return INVALID; + } + if (ctx.common.async) { + return option._parseAsync({ + data: ctx.data, + path: ctx.path, + parent: ctx, + }); + } + else { + return option._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx, + }); + } + } + get discriminator() { + return this._def.discriminator; + } + get options() { + return this._def.options; + } + get optionsMap() { + return this._def.optionsMap; + } + /** + * The constructor of the discriminated union schema. Its behaviour is very similar to that of the normal z.union() constructor. + * However, it only allows a union of objects, all of which need to share a discriminator property. This property must + * have a different value for each object in the union. + * @param discriminator the name of the discriminator property + * @param types an array of object schemas + * @param params + */ + static create(discriminator, options, params) { + // Get all the valid discriminator values + const optionsMap = new Map(); + // try { + for (const type of options) { + const discriminatorValues = getDiscriminator(type.shape[discriminator]); + if (!discriminatorValues.length) { + throw new Error(`A discriminator value for key \`${discriminator}\` could not be extracted from all schema options`); + } + for (const value of discriminatorValues) { + if (optionsMap.has(value)) { + throw new Error(`Discriminator property ${String(discriminator)} has duplicate value ${String(value)}`); + } + optionsMap.set(value, type); + } + } + return new ZodDiscriminatedUnion({ + typeName: ZodFirstPartyTypeKind.ZodDiscriminatedUnion, + discriminator, + options, + optionsMap, + ...processCreateParams(params), + }); + } +} +function mergeValues(a, b) { + const aType = getParsedType(a); + const bType = getParsedType(b); + if (a === b) { + return { valid: true, data: a }; + } + else if (aType === ZodParsedType.object && bType === ZodParsedType.object) { + const bKeys = util.objectKeys(b); + const sharedKeys = util + .objectKeys(a) + .filter((key) => bKeys.indexOf(key) !== -1); + const newObj = { ...a, ...b }; + for (const key of sharedKeys) { + const sharedValue = mergeValues(a[key], b[key]); + if (!sharedValue.valid) { + return { valid: false }; + } + newObj[key] = sharedValue.data; + } + return { valid: true, data: newObj }; + } + else if (aType === ZodParsedType.array && bType === ZodParsedType.array) { + if (a.length !== b.length) { + return { valid: false }; + } + const newArray = []; + for (let index = 0; index < a.length; index++) { + const itemA = a[index]; + const itemB = b[index]; + const sharedValue = mergeValues(itemA, itemB); + if (!sharedValue.valid) { + return { valid: false }; + } + newArray.push(sharedValue.data); + } + return { valid: true, data: newArray }; + } + else if (aType === ZodParsedType.date && + bType === ZodParsedType.date && + +a === +b) { + return { valid: true, data: a }; + } + else { + return { valid: false }; + } +} +class ZodIntersection extends ZodType { + _parse(input) { + const { status, ctx } = this._processInputParams(input); + const handleParsed = (parsedLeft, parsedRight) => { + if (isAborted(parsedLeft) || isAborted(parsedRight)) { + return INVALID; + } + const merged = mergeValues(parsedLeft.value, parsedRight.value); + if (!merged.valid) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_intersection_types, + }); + return INVALID; + } + if (isDirty(parsedLeft) || isDirty(parsedRight)) { + status.dirty(); + } + return { status: status.value, value: merged.data }; + }; + if (ctx.common.async) { + return Promise.all([ + this._def.left._parseAsync({ + data: ctx.data, + path: ctx.path, + parent: ctx, + }), + this._def.right._parseAsync({ + data: ctx.data, + path: ctx.path, + parent: ctx, + }), + ]).then(([left, right]) => handleParsed(left, right)); + } + else { + return handleParsed(this._def.left._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx, + }), this._def.right._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx, + })); + } + } +} +ZodIntersection.create = (left, right, params) => { + return new ZodIntersection({ + left: left, + right: right, + typeName: ZodFirstPartyTypeKind.ZodIntersection, + ...processCreateParams(params), + }); +}; +class ZodTuple extends ZodType { + _parse(input) { + const { status, ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.array) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.array, + received: ctx.parsedType, + }); + return INVALID; + } + if (ctx.data.length < this._def.items.length) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: this._def.items.length, + inclusive: true, + exact: false, + type: "array", + }); + return INVALID; + } + const rest = this._def.rest; + if (!rest && ctx.data.length > this._def.items.length) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: this._def.items.length, + inclusive: true, + exact: false, + type: "array", + }); + status.dirty(); + } + const items = [...ctx.data] + .map((item, itemIndex) => { + const schema = this._def.items[itemIndex] || this._def.rest; + if (!schema) + return null; + return schema._parse(new ParseInputLazyPath(ctx, item, ctx.path, itemIndex)); + }) + .filter((x) => !!x); // filter nulls + if (ctx.common.async) { + return Promise.all(items).then((results) => { + return ParseStatus.mergeArray(status, results); + }); + } + else { + return ParseStatus.mergeArray(status, items); + } + } + get items() { + return this._def.items; + } + rest(rest) { + return new ZodTuple({ + ...this._def, + rest, + }); + } +} +ZodTuple.create = (schemas, params) => { + if (!Array.isArray(schemas)) { + throw new Error("You must pass an array of schemas to z.tuple([ ... ])"); + } + return new ZodTuple({ + items: schemas, + typeName: ZodFirstPartyTypeKind.ZodTuple, + rest: null, + ...processCreateParams(params), + }); +}; +class ZodRecord extends ZodType { + get keySchema() { + return this._def.keyType; + } + get valueSchema() { + return this._def.valueType; + } + _parse(input) { + const { status, ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.object) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.object, + received: ctx.parsedType, + }); + return INVALID; + } + const pairs = []; + const keyType = this._def.keyType; + const valueType = this._def.valueType; + for (const key in ctx.data) { + pairs.push({ + key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, key)), + value: valueType._parse(new ParseInputLazyPath(ctx, ctx.data[key], ctx.path, key)), + alwaysSet: key in ctx.data, + }); + } + if (ctx.common.async) { + return ParseStatus.mergeObjectAsync(status, pairs); + } + else { + return ParseStatus.mergeObjectSync(status, pairs); + } + } + get element() { + return this._def.valueType; + } + static create(first, second, third) { + if (second instanceof ZodType) { + return new ZodRecord({ + keyType: first, + valueType: second, + typeName: ZodFirstPartyTypeKind.ZodRecord, + ...processCreateParams(third), + }); + } + return new ZodRecord({ + keyType: ZodString.create(), + valueType: first, + typeName: ZodFirstPartyTypeKind.ZodRecord, + ...processCreateParams(second), + }); + } +} +class ZodMap extends ZodType { + get keySchema() { + return this._def.keyType; + } + get valueSchema() { + return this._def.valueType; + } + _parse(input) { + const { status, ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.map) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.map, + received: ctx.parsedType, + }); + return INVALID; + } + const keyType = this._def.keyType; + const valueType = this._def.valueType; + const pairs = [...ctx.data.entries()].map(([key, value], index) => { + return { + key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, [index, "key"])), + value: valueType._parse(new ParseInputLazyPath(ctx, value, ctx.path, [index, "value"])), + }; + }); + if (ctx.common.async) { + const finalMap = new Map(); + return Promise.resolve().then(async () => { + for (const pair of pairs) { + const key = await pair.key; + const value = await pair.value; + if (key.status === "aborted" || value.status === "aborted") { + return INVALID; + } + if (key.status === "dirty" || value.status === "dirty") { + status.dirty(); + } + finalMap.set(key.value, value.value); + } + return { status: status.value, value: finalMap }; + }); + } + else { + const finalMap = new Map(); + for (const pair of pairs) { + const key = pair.key; + const value = pair.value; + if (key.status === "aborted" || value.status === "aborted") { + return INVALID; + } + if (key.status === "dirty" || value.status === "dirty") { + status.dirty(); + } + finalMap.set(key.value, value.value); + } + return { status: status.value, value: finalMap }; + } + } +} +ZodMap.create = (keyType, valueType, params) => { + return new ZodMap({ + valueType, + keyType, + typeName: ZodFirstPartyTypeKind.ZodMap, + ...processCreateParams(params), + }); +}; +class ZodSet extends ZodType { + _parse(input) { + const { status, ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.set) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.set, + received: ctx.parsedType, + }); + return INVALID; + } + const def = this._def; + if (def.minSize !== null) { + if (ctx.data.size < def.minSize.value) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: def.minSize.value, + type: "set", + inclusive: true, + exact: false, + message: def.minSize.message, + }); + status.dirty(); + } + } + if (def.maxSize !== null) { + if (ctx.data.size > def.maxSize.value) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: def.maxSize.value, + type: "set", + inclusive: true, + exact: false, + message: def.maxSize.message, + }); + status.dirty(); + } + } + const valueType = this._def.valueType; + function finalizeSet(elements) { + const parsedSet = new Set(); + for (const element of elements) { + if (element.status === "aborted") + return INVALID; + if (element.status === "dirty") + status.dirty(); + parsedSet.add(element.value); + } + return { status: status.value, value: parsedSet }; + } + const elements = [...ctx.data.values()].map((item, i) => valueType._parse(new ParseInputLazyPath(ctx, item, ctx.path, i))); + if (ctx.common.async) { + return Promise.all(elements).then((elements) => finalizeSet(elements)); + } + else { + return finalizeSet(elements); + } + } + min(minSize, message) { + return new ZodSet({ + ...this._def, + minSize: { value: minSize, message: errorUtil.toString(message) }, + }); + } + max(maxSize, message) { + return new ZodSet({ + ...this._def, + maxSize: { value: maxSize, message: errorUtil.toString(message) }, + }); + } + size(size, message) { + return this.min(size, message).max(size, message); + } + nonempty(message) { + return this.min(1, message); + } +} +ZodSet.create = (valueType, params) => { + return new ZodSet({ + valueType, + minSize: null, + maxSize: null, + typeName: ZodFirstPartyTypeKind.ZodSet, + ...processCreateParams(params), + }); +}; +class ZodFunction extends ZodType { + constructor() { + super(...arguments); + this.validate = this.implement; + } + _parse(input) { + const { ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.function) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.function, + received: ctx.parsedType, + }); + return INVALID; + } + function makeArgsIssue(args, error) { + return makeIssue({ + data: args, + path: ctx.path, + errorMaps: [ + ctx.common.contextualErrorMap, + ctx.schemaErrorMap, + getErrorMap(), + errorMap, + ].filter((x) => !!x), + issueData: { + code: ZodIssueCode.invalid_arguments, + argumentsError: error, + }, + }); + } + function makeReturnsIssue(returns, error) { + return makeIssue({ + data: returns, + path: ctx.path, + errorMaps: [ + ctx.common.contextualErrorMap, + ctx.schemaErrorMap, + getErrorMap(), + errorMap, + ].filter((x) => !!x), + issueData: { + code: ZodIssueCode.invalid_return_type, + returnTypeError: error, + }, + }); + } + const params = { errorMap: ctx.common.contextualErrorMap }; + const fn = ctx.data; + if (this._def.returns instanceof ZodPromise) { + // Would love a way to avoid disabling this rule, but we need + // an alias (using an arrow function was what caused 2651). + // eslint-disable-next-line @typescript-eslint/no-this-alias + const me = this; + return OK(async function (...args) { + const error = new ZodError([]); + const parsedArgs = await me._def.args + .parseAsync(args, params) + .catch((e) => { + error.addIssue(makeArgsIssue(args, e)); + throw error; + }); + const result = await Reflect.apply(fn, this, parsedArgs); + const parsedReturns = await me._def.returns._def.type + .parseAsync(result, params) + .catch((e) => { + error.addIssue(makeReturnsIssue(result, e)); + throw error; + }); + return parsedReturns; + }); + } + else { + // Would love a way to avoid disabling this rule, but we need + // an alias (using an arrow function was what caused 2651). + // eslint-disable-next-line @typescript-eslint/no-this-alias + const me = this; + return OK(function (...args) { + const parsedArgs = me._def.args.safeParse(args, params); + if (!parsedArgs.success) { + throw new ZodError([makeArgsIssue(args, parsedArgs.error)]); + } + const result = Reflect.apply(fn, this, parsedArgs.data); + const parsedReturns = me._def.returns.safeParse(result, params); + if (!parsedReturns.success) { + throw new ZodError([makeReturnsIssue(result, parsedReturns.error)]); + } + return parsedReturns.data; + }); + } + } + parameters() { + return this._def.args; + } + returnType() { + return this._def.returns; + } + args(...items) { + return new ZodFunction({ + ...this._def, + args: ZodTuple.create(items).rest(ZodUnknown.create()), + }); + } + returns(returnType) { + return new ZodFunction({ + ...this._def, + returns: returnType, + }); + } + implement(func) { + const validatedFunc = this.parse(func); + return validatedFunc; + } + strictImplement(func) { + const validatedFunc = this.parse(func); + return validatedFunc; + } + static create(args, returns, params) { + return new ZodFunction({ + args: (args + ? args + : ZodTuple.create([]).rest(ZodUnknown.create())), + returns: returns || ZodUnknown.create(), + typeName: ZodFirstPartyTypeKind.ZodFunction, + ...processCreateParams(params), + }); + } +} +class ZodLazy extends ZodType { + get schema() { + return this._def.getter(); + } + _parse(input) { + const { ctx } = this._processInputParams(input); + const lazySchema = this._def.getter(); + return lazySchema._parse({ data: ctx.data, path: ctx.path, parent: ctx }); + } +} +ZodLazy.create = (getter, params) => { + return new ZodLazy({ + getter: getter, + typeName: ZodFirstPartyTypeKind.ZodLazy, + ...processCreateParams(params), + }); +}; +class ZodLiteral extends ZodType { + _parse(input) { + if (input.data !== this._def.value) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + received: ctx.data, + code: ZodIssueCode.invalid_literal, + expected: this._def.value, + }); + return INVALID; + } + return { status: "valid", value: input.data }; + } + get value() { + return this._def.value; + } +} +ZodLiteral.create = (value, params) => { + return new ZodLiteral({ + value: value, + typeName: ZodFirstPartyTypeKind.ZodLiteral, + ...processCreateParams(params), + }); +}; +function createZodEnum(values, params) { + return new ZodEnum({ + values, + typeName: ZodFirstPartyTypeKind.ZodEnum, + ...processCreateParams(params), + }); +} +class ZodEnum extends ZodType { + constructor() { + super(...arguments); + _ZodEnum_cache.set(this, void 0); + } + _parse(input) { + if (typeof input.data !== "string") { + const ctx = this._getOrReturnCtx(input); + const expectedValues = this._def.values; + addIssueToContext(ctx, { + expected: util.joinValues(expectedValues), + received: ctx.parsedType, + code: ZodIssueCode.invalid_type, + }); + return INVALID; + } + if (!__classPrivateFieldGet(this, _ZodEnum_cache)) { + __classPrivateFieldSet(this, _ZodEnum_cache, new Set(this._def.values)); + } + if (!__classPrivateFieldGet(this, _ZodEnum_cache).has(input.data)) { + const ctx = this._getOrReturnCtx(input); + const expectedValues = this._def.values; + addIssueToContext(ctx, { + received: ctx.data, + code: ZodIssueCode.invalid_enum_value, + options: expectedValues, + }); + return INVALID; + } + return OK(input.data); + } + get options() { + return this._def.values; + } + get enum() { + const enumValues = {}; + for (const val of this._def.values) { + enumValues[val] = val; + } + return enumValues; + } + get Values() { + const enumValues = {}; + for (const val of this._def.values) { + enumValues[val] = val; + } + return enumValues; + } + get Enum() { + const enumValues = {}; + for (const val of this._def.values) { + enumValues[val] = val; + } + return enumValues; + } + extract(values, newDef = this._def) { + return ZodEnum.create(values, { + ...this._def, + ...newDef, + }); + } + exclude(values, newDef = this._def) { + return ZodEnum.create(this.options.filter((opt) => !values.includes(opt)), { + ...this._def, + ...newDef, + }); + } +} +_ZodEnum_cache = new WeakMap(); +ZodEnum.create = createZodEnum; +class ZodNativeEnum extends ZodType { + constructor() { + super(...arguments); + _ZodNativeEnum_cache.set(this, void 0); + } + _parse(input) { + const nativeEnumValues = util.getValidEnumValues(this._def.values); + const ctx = this._getOrReturnCtx(input); + if (ctx.parsedType !== ZodParsedType.string && + ctx.parsedType !== ZodParsedType.number) { + const expectedValues = util.objectValues(nativeEnumValues); + addIssueToContext(ctx, { + expected: util.joinValues(expectedValues), + received: ctx.parsedType, + code: ZodIssueCode.invalid_type, + }); + return INVALID; + } + if (!__classPrivateFieldGet(this, _ZodNativeEnum_cache)) { + __classPrivateFieldSet(this, _ZodNativeEnum_cache, new Set(util.getValidEnumValues(this._def.values))); + } + if (!__classPrivateFieldGet(this, _ZodNativeEnum_cache).has(input.data)) { + const expectedValues = util.objectValues(nativeEnumValues); + addIssueToContext(ctx, { + received: ctx.data, + code: ZodIssueCode.invalid_enum_value, + options: expectedValues, + }); + return INVALID; + } + return OK(input.data); + } + get enum() { + return this._def.values; + } +} +_ZodNativeEnum_cache = new WeakMap(); +ZodNativeEnum.create = (values, params) => { + return new ZodNativeEnum({ + values: values, + typeName: ZodFirstPartyTypeKind.ZodNativeEnum, + ...processCreateParams(params), + }); +}; +class ZodPromise extends ZodType { + unwrap() { + return this._def.type; + } + _parse(input) { + const { ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.promise && + ctx.common.async === false) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.promise, + received: ctx.parsedType, + }); + return INVALID; + } + const promisified = ctx.parsedType === ZodParsedType.promise + ? ctx.data + : Promise.resolve(ctx.data); + return OK(promisified.then((data) => { + return this._def.type.parseAsync(data, { + path: ctx.path, + errorMap: ctx.common.contextualErrorMap, + }); + })); + } +} +ZodPromise.create = (schema, params) => { + return new ZodPromise({ + type: schema, + typeName: ZodFirstPartyTypeKind.ZodPromise, + ...processCreateParams(params), + }); +}; +class ZodEffects extends ZodType { + innerType() { + return this._def.schema; + } + sourceType() { + return this._def.schema._def.typeName === ZodFirstPartyTypeKind.ZodEffects + ? this._def.schema.sourceType() + : this._def.schema; + } + _parse(input) { + const { status, ctx } = this._processInputParams(input); + const effect = this._def.effect || null; + const checkCtx = { + addIssue: (arg) => { + addIssueToContext(ctx, arg); + if (arg.fatal) { + status.abort(); + } + else { + status.dirty(); + } + }, + get path() { + return ctx.path; + }, + }; + checkCtx.addIssue = checkCtx.addIssue.bind(checkCtx); + if (effect.type === "preprocess") { + const processed = effect.transform(ctx.data, checkCtx); + if (ctx.common.async) { + return Promise.resolve(processed).then(async (processed) => { + if (status.value === "aborted") + return INVALID; + const result = await this._def.schema._parseAsync({ + data: processed, + path: ctx.path, + parent: ctx, + }); + if (result.status === "aborted") + return INVALID; + if (result.status === "dirty") + return DIRTY(result.value); + if (status.value === "dirty") + return DIRTY(result.value); + return result; + }); + } + else { + if (status.value === "aborted") + return INVALID; + const result = this._def.schema._parseSync({ + data: processed, + path: ctx.path, + parent: ctx, + }); + if (result.status === "aborted") + return INVALID; + if (result.status === "dirty") + return DIRTY(result.value); + if (status.value === "dirty") + return DIRTY(result.value); + return result; + } + } + if (effect.type === "refinement") { + const executeRefinement = (acc) => { + const result = effect.refinement(acc, checkCtx); + if (ctx.common.async) { + return Promise.resolve(result); + } + if (result instanceof Promise) { + throw new Error("Async refinement encountered during synchronous parse operation. Use .parseAsync instead."); + } + return acc; + }; + if (ctx.common.async === false) { + const inner = this._def.schema._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx, + }); + if (inner.status === "aborted") + return INVALID; + if (inner.status === "dirty") + status.dirty(); + // return value is ignored + executeRefinement(inner.value); + return { status: status.value, value: inner.value }; + } + else { + return this._def.schema + ._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx }) + .then((inner) => { + if (inner.status === "aborted") + return INVALID; + if (inner.status === "dirty") + status.dirty(); + return executeRefinement(inner.value).then(() => { + return { status: status.value, value: inner.value }; + }); + }); + } + } + if (effect.type === "transform") { + if (ctx.common.async === false) { + const base = this._def.schema._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx, + }); + if (!isValid(base)) + return base; + const result = effect.transform(base.value, checkCtx); + if (result instanceof Promise) { + throw new Error(`Asynchronous transform encountered during synchronous parse operation. Use .parseAsync instead.`); + } + return { status: status.value, value: result }; + } + else { + return this._def.schema + ._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx }) + .then((base) => { + if (!isValid(base)) + return base; + return Promise.resolve(effect.transform(base.value, checkCtx)).then((result) => ({ status: status.value, value: result })); + }); + } + } + util.assertNever(effect); + } +} +ZodEffects.create = (schema, effect, params) => { + return new ZodEffects({ + schema, + typeName: ZodFirstPartyTypeKind.ZodEffects, + effect, + ...processCreateParams(params), + }); +}; +ZodEffects.createWithPreprocess = (preprocess, schema, params) => { + return new ZodEffects({ + schema, + effect: { type: "preprocess", transform: preprocess }, + typeName: ZodFirstPartyTypeKind.ZodEffects, + ...processCreateParams(params), + }); +}; +class ZodOptional extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType === ZodParsedType.undefined) { + return OK(undefined); + } + return this._def.innerType._parse(input); + } + unwrap() { + return this._def.innerType; + } +} +ZodOptional.create = (type, params) => { + return new ZodOptional({ + innerType: type, + typeName: ZodFirstPartyTypeKind.ZodOptional, + ...processCreateParams(params), + }); +}; +class ZodNullable extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType === ZodParsedType.null) { + return OK(null); + } + return this._def.innerType._parse(input); + } + unwrap() { + return this._def.innerType; + } +} +ZodNullable.create = (type, params) => { + return new ZodNullable({ + innerType: type, + typeName: ZodFirstPartyTypeKind.ZodNullable, + ...processCreateParams(params), + }); +}; +class ZodDefault extends ZodType { + _parse(input) { + const { ctx } = this._processInputParams(input); + let data = ctx.data; + if (ctx.parsedType === ZodParsedType.undefined) { + data = this._def.defaultValue(); + } + return this._def.innerType._parse({ + data, + path: ctx.path, + parent: ctx, + }); + } + removeDefault() { + return this._def.innerType; + } +} +ZodDefault.create = (type, params) => { + return new ZodDefault({ + innerType: type, + typeName: ZodFirstPartyTypeKind.ZodDefault, + defaultValue: typeof params.default === "function" + ? params.default + : () => params.default, + ...processCreateParams(params), + }); +}; +class ZodCatch extends ZodType { + _parse(input) { + const { ctx } = this._processInputParams(input); + // newCtx is used to not collect issues from inner types in ctx + const newCtx = { + ...ctx, + common: { + ...ctx.common, + issues: [], + }, + }; + const result = this._def.innerType._parse({ + data: newCtx.data, + path: newCtx.path, + parent: { + ...newCtx, + }, + }); + if (isAsync(result)) { + return result.then((result) => { + return { + status: "valid", + value: result.status === "valid" + ? result.value + : this._def.catchValue({ + get error() { + return new ZodError(newCtx.common.issues); + }, + input: newCtx.data, + }), + }; + }); + } + else { + return { + status: "valid", + value: result.status === "valid" + ? result.value + : this._def.catchValue({ + get error() { + return new ZodError(newCtx.common.issues); + }, + input: newCtx.data, + }), + }; + } + } + removeCatch() { + return this._def.innerType; + } +} +ZodCatch.create = (type, params) => { + return new ZodCatch({ + innerType: type, + typeName: ZodFirstPartyTypeKind.ZodCatch, + catchValue: typeof params.catch === "function" ? params.catch : () => params.catch, + ...processCreateParams(params), + }); +}; +class ZodNaN extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.nan) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.nan, + received: ctx.parsedType, + }); + return INVALID; + } + return { status: "valid", value: input.data }; + } +} +ZodNaN.create = (params) => { + return new ZodNaN({ + typeName: ZodFirstPartyTypeKind.ZodNaN, + ...processCreateParams(params), + }); +}; +const BRAND = Symbol("zod_brand"); +class ZodBranded extends ZodType { + _parse(input) { + const { ctx } = this._processInputParams(input); + const data = ctx.data; + return this._def.type._parse({ + data, + path: ctx.path, + parent: ctx, + }); + } + unwrap() { + return this._def.type; + } +} +class ZodPipeline extends ZodType { + _parse(input) { + const { status, ctx } = this._processInputParams(input); + if (ctx.common.async) { + const handleAsync = async () => { + const inResult = await this._def.in._parseAsync({ + data: ctx.data, + path: ctx.path, + parent: ctx, + }); + if (inResult.status === "aborted") + return INVALID; + if (inResult.status === "dirty") { + status.dirty(); + return DIRTY(inResult.value); + } + else { + return this._def.out._parseAsync({ + data: inResult.value, + path: ctx.path, + parent: ctx, + }); + } + }; + return handleAsync(); + } + else { + const inResult = this._def.in._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx, + }); + if (inResult.status === "aborted") + return INVALID; + if (inResult.status === "dirty") { + status.dirty(); + return { + status: "dirty", + value: inResult.value, + }; + } + else { + return this._def.out._parseSync({ + data: inResult.value, + path: ctx.path, + parent: ctx, + }); + } + } + } + static create(a, b) { + return new ZodPipeline({ + in: a, + out: b, + typeName: ZodFirstPartyTypeKind.ZodPipeline, + }); + } +} +class ZodReadonly extends ZodType { + _parse(input) { + const result = this._def.innerType._parse(input); + const freeze = (data) => { + if (isValid(data)) { + data.value = Object.freeze(data.value); + } + return data; + }; + return isAsync(result) + ? result.then((data) => freeze(data)) + : freeze(result); + } + unwrap() { + return this._def.innerType; + } +} +ZodReadonly.create = (type, params) => { + return new ZodReadonly({ + innerType: type, + typeName: ZodFirstPartyTypeKind.ZodReadonly, + ...processCreateParams(params), + }); +}; +//////////////////////////////////////// +//////////////////////////////////////// +////////// ////////// +////////// z.custom ////////// +////////// ////////// +//////////////////////////////////////// +//////////////////////////////////////// +function cleanParams(params, data) { + const p = typeof params === "function" + ? params(data) + : typeof params === "string" + ? { message: params } + : params; + const p2 = typeof p === "string" ? { message: p } : p; + return p2; +} +function custom(check, _params = {}, + /** + * @deprecated + * + * Pass `fatal` into the params object instead: + * + * ```ts + * z.string().custom((val) => val.length > 5, { fatal: false }) + * ``` + * + */ + fatal) { + if (check) + return ZodAny.create().superRefine((data, ctx) => { + var _a, _b; + const r = check(data); + if (r instanceof Promise) { + return r.then((r) => { + var _a, _b; + if (!r) { + const params = cleanParams(_params, data); + const _fatal = (_b = (_a = params.fatal) !== null && _a !== void 0 ? _a : fatal) !== null && _b !== void 0 ? _b : true; + ctx.addIssue({ code: "custom", ...params, fatal: _fatal }); + } + }); + } + if (!r) { + const params = cleanParams(_params, data); + const _fatal = (_b = (_a = params.fatal) !== null && _a !== void 0 ? _a : fatal) !== null && _b !== void 0 ? _b : true; + ctx.addIssue({ code: "custom", ...params, fatal: _fatal }); + } + return; + }); + return ZodAny.create(); +} +const late = { + object: ZodObject.lazycreate, +}; +var ZodFirstPartyTypeKind; +(function (ZodFirstPartyTypeKind) { + ZodFirstPartyTypeKind["ZodString"] = "ZodString"; + ZodFirstPartyTypeKind["ZodNumber"] = "ZodNumber"; + ZodFirstPartyTypeKind["ZodNaN"] = "ZodNaN"; + ZodFirstPartyTypeKind["ZodBigInt"] = "ZodBigInt"; + ZodFirstPartyTypeKind["ZodBoolean"] = "ZodBoolean"; + ZodFirstPartyTypeKind["ZodDate"] = "ZodDate"; + ZodFirstPartyTypeKind["ZodSymbol"] = "ZodSymbol"; + ZodFirstPartyTypeKind["ZodUndefined"] = "ZodUndefined"; + ZodFirstPartyTypeKind["ZodNull"] = "ZodNull"; + ZodFirstPartyTypeKind["ZodAny"] = "ZodAny"; + ZodFirstPartyTypeKind["ZodUnknown"] = "ZodUnknown"; + ZodFirstPartyTypeKind["ZodNever"] = "ZodNever"; + ZodFirstPartyTypeKind["ZodVoid"] = "ZodVoid"; + ZodFirstPartyTypeKind["ZodArray"] = "ZodArray"; + ZodFirstPartyTypeKind["ZodObject"] = "ZodObject"; + ZodFirstPartyTypeKind["ZodUnion"] = "ZodUnion"; + ZodFirstPartyTypeKind["ZodDiscriminatedUnion"] = "ZodDiscriminatedUnion"; + ZodFirstPartyTypeKind["ZodIntersection"] = "ZodIntersection"; + ZodFirstPartyTypeKind["ZodTuple"] = "ZodTuple"; + ZodFirstPartyTypeKind["ZodRecord"] = "ZodRecord"; + ZodFirstPartyTypeKind["ZodMap"] = "ZodMap"; + ZodFirstPartyTypeKind["ZodSet"] = "ZodSet"; + ZodFirstPartyTypeKind["ZodFunction"] = "ZodFunction"; + ZodFirstPartyTypeKind["ZodLazy"] = "ZodLazy"; + ZodFirstPartyTypeKind["ZodLiteral"] = "ZodLiteral"; + ZodFirstPartyTypeKind["ZodEnum"] = "ZodEnum"; + ZodFirstPartyTypeKind["ZodEffects"] = "ZodEffects"; + ZodFirstPartyTypeKind["ZodNativeEnum"] = "ZodNativeEnum"; + ZodFirstPartyTypeKind["ZodOptional"] = "ZodOptional"; + ZodFirstPartyTypeKind["ZodNullable"] = "ZodNullable"; + ZodFirstPartyTypeKind["ZodDefault"] = "ZodDefault"; + ZodFirstPartyTypeKind["ZodCatch"] = "ZodCatch"; + ZodFirstPartyTypeKind["ZodPromise"] = "ZodPromise"; + ZodFirstPartyTypeKind["ZodBranded"] = "ZodBranded"; + ZodFirstPartyTypeKind["ZodPipeline"] = "ZodPipeline"; + ZodFirstPartyTypeKind["ZodReadonly"] = "ZodReadonly"; +})(ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {})); +const instanceOfType = ( +// const instanceOfType = any>( +cls, params = { + message: `Input not instance of ${cls.name}`, + }) => custom((data) => data instanceof cls, params); +const stringType = ZodString.create; +const numberType = ZodNumber.create; +const nanType = ZodNaN.create; +const bigIntType = ZodBigInt.create; +const booleanType = ZodBoolean.create; +const dateType = ZodDate.create; +const symbolType = ZodSymbol.create; +const undefinedType = ZodUndefined.create; +const nullType = ZodNull.create; +const anyType = ZodAny.create; +const unknownType = ZodUnknown.create; +const neverType = ZodNever.create; +const voidType = ZodVoid.create; +const arrayType = ZodArray.create; +const objectType = ZodObject.create; +const strictObjectType = ZodObject.strictCreate; +const unionType = ZodUnion.create; +const discriminatedUnionType = ZodDiscriminatedUnion.create; +const intersectionType = ZodIntersection.create; +const tupleType = ZodTuple.create; +const recordType = ZodRecord.create; +const mapType = ZodMap.create; +const setType = ZodSet.create; +const functionType = ZodFunction.create; +const lazyType = ZodLazy.create; +const literalType = ZodLiteral.create; +const enumType = ZodEnum.create; +const nativeEnumType = ZodNativeEnum.create; +const promiseType = ZodPromise.create; +const effectsType = ZodEffects.create; +const optionalType = ZodOptional.create; +const nullableType = ZodNullable.create; +const preprocessType = ZodEffects.createWithPreprocess; +const pipelineType = ZodPipeline.create; +const ostring = () => stringType().optional(); +const onumber = () => numberType().optional(); +const oboolean = () => booleanType().optional(); +const coerce = { + string: ((arg) => ZodString.create({ ...arg, coerce: true })), + number: ((arg) => ZodNumber.create({ ...arg, coerce: true })), + boolean: ((arg) => ZodBoolean.create({ + ...arg, + coerce: true, + })), + bigint: ((arg) => ZodBigInt.create({ ...arg, coerce: true })), + date: ((arg) => ZodDate.create({ ...arg, coerce: true })), +}; +const NEVER = INVALID; + +var z = /*#__PURE__*/Object.freeze({ + __proto__: null, + defaultErrorMap: errorMap, + setErrorMap: setErrorMap, + getErrorMap: getErrorMap, + makeIssue: makeIssue, + EMPTY_PATH: EMPTY_PATH, + addIssueToContext: addIssueToContext, + ParseStatus: ParseStatus, + INVALID: INVALID, + DIRTY: DIRTY, + OK: OK, + isAborted: isAborted, + isDirty: isDirty, + isValid: isValid, + isAsync: isAsync, + get util () { return util; }, + get objectUtil () { return objectUtil; }, + ZodParsedType: ZodParsedType, + getParsedType: getParsedType, + ZodType: ZodType, + datetimeRegex: datetimeRegex, + ZodString: ZodString, + ZodNumber: ZodNumber, + ZodBigInt: ZodBigInt, + ZodBoolean: ZodBoolean, + ZodDate: ZodDate, + ZodSymbol: ZodSymbol, + ZodUndefined: ZodUndefined, + ZodNull: ZodNull, + ZodAny: ZodAny, + ZodUnknown: ZodUnknown, + ZodNever: ZodNever, + ZodVoid: ZodVoid, + ZodArray: ZodArray, + ZodObject: ZodObject, + ZodUnion: ZodUnion, + ZodDiscriminatedUnion: ZodDiscriminatedUnion, + ZodIntersection: ZodIntersection, + ZodTuple: ZodTuple, + ZodRecord: ZodRecord, + ZodMap: ZodMap, + ZodSet: ZodSet, + ZodFunction: ZodFunction, + ZodLazy: ZodLazy, + ZodLiteral: ZodLiteral, + ZodEnum: ZodEnum, + ZodNativeEnum: ZodNativeEnum, + ZodPromise: ZodPromise, + ZodEffects: ZodEffects, + ZodTransformer: ZodEffects, + ZodOptional: ZodOptional, + ZodNullable: ZodNullable, + ZodDefault: ZodDefault, + ZodCatch: ZodCatch, + ZodNaN: ZodNaN, + BRAND: BRAND, + ZodBranded: ZodBranded, + ZodPipeline: ZodPipeline, + ZodReadonly: ZodReadonly, + custom: custom, + Schema: ZodType, + ZodSchema: ZodType, + late: late, + get ZodFirstPartyTypeKind () { return ZodFirstPartyTypeKind; }, + coerce: coerce, + any: anyType, + array: arrayType, + bigint: bigIntType, + boolean: booleanType, + date: dateType, + discriminatedUnion: discriminatedUnionType, + effect: effectsType, + 'enum': enumType, + 'function': functionType, + 'instanceof': instanceOfType, + intersection: intersectionType, + lazy: lazyType, + literal: literalType, + map: mapType, + nan: nanType, + nativeEnum: nativeEnumType, + never: neverType, + 'null': nullType, + nullable: nullableType, + number: numberType, + object: objectType, + oboolean: oboolean, + onumber: onumber, + optional: optionalType, + ostring: ostring, + pipeline: pipelineType, + preprocess: preprocessType, + promise: promiseType, + record: recordType, + set: setType, + strictObject: strictObjectType, + string: stringType, + symbol: symbolType, + transformer: effectsType, + tuple: tupleType, + 'undefined': undefinedType, + union: unionType, + unknown: unknownType, + 'void': voidType, + NEVER: NEVER, + ZodIssueCode: ZodIssueCode, + quotelessJson: quotelessJson, + ZodError: ZodError +}); + +const ALGORITHMS = { + "SHA-256": "sha256-", + "SHA-384": "sha384-", + "SHA-512": "sha512-" +}; +const ALGORITHM_VALUES = Object.values(ALGORITHMS); +z.enum(Object.keys(ALGORITHMS)).optional().default("SHA-256"); +z.custom((value) => { + if (typeof value !== "string") { + return false; + } + return ALGORITHM_VALUES.some((allowedValue) => { + return value.startsWith(allowedValue); + }); +}); +const ALLOWED_DIRECTIVES = [ + "base-uri", + "child-src", + "connect-src", + "default-src", + "fenced-frame-src", + "font-src", + "form-action", + "frame-ancestors", + "frame-src", + "img-src", + "manifest-src", + "media-src", + "object-src", + "referrer", + "report-to", + "report-uri", + "require-trusted-types-for", + "sandbox", + "trusted-types", + "upgrade-insecure-requests", + "worker-src" +]; +z.custom((value) => { + if (typeof value !== "string") { + return false; + } + return ALLOWED_DIRECTIVES.some((allowedValue) => { + return value.startsWith(allowedValue); + }); +}); + +const ALGORITHM = "AES-GCM"; +async function decodeKey(encoded) { + const bytes = decodeBase64(encoded); + return crypto.subtle.importKey("raw", bytes, ALGORITHM, true, ["encrypt", "decrypt"]); +} +const encoder$1 = new TextEncoder(); +const decoder$1 = new TextDecoder(); +const IV_LENGTH = 24; +async function encryptString(key, raw) { + const iv = crypto.getRandomValues(new Uint8Array(IV_LENGTH / 2)); + const data = encoder$1.encode(raw); + const buffer = await crypto.subtle.encrypt( + { + name: ALGORITHM, + iv + }, + key, + data + ); + return encodeHexUpperCase(iv) + encodeBase64(new Uint8Array(buffer)); +} +async function decryptString(key, encoded) { + const iv = decodeHex(encoded.slice(0, IV_LENGTH)); + const dataArray = decodeBase64(encoded.slice(IV_LENGTH)); + const decryptedBuffer = await crypto.subtle.decrypt( + { + name: ALGORITHM, + iv + }, + key, + dataArray + ); + const decryptedString = decoder$1.decode(decryptedBuffer); + return decryptedString; +} +async function generateCspDigest(data, algorithm) { + const hashBuffer = await crypto.subtle.digest(algorithm, encoder$1.encode(data)); + const hash = encodeBase64(new Uint8Array(hashBuffer)); + return `${ALGORITHMS[algorithm]}${hash}`; +} + +const renderTemplateResultSym = Symbol.for("astro.renderTemplateResult"); +class RenderTemplateResult { + [renderTemplateResultSym] = true; + htmlParts; + expressions; + error; + constructor(htmlParts, expressions) { + this.htmlParts = htmlParts; + this.error = void 0; + this.expressions = expressions.map((expression) => { + if (isPromise(expression)) { + return Promise.resolve(expression).catch((err) => { + if (!this.error) { + this.error = err; + throw err; + } + }); + } + return expression; + }); + } + render(destination) { + const flushers = this.expressions.map((exp) => { + return createBufferedRenderer(destination, (bufferDestination) => { + if (exp || exp === 0) { + return renderChild(bufferDestination, exp); + } + }); + }); + let i = 0; + const iterate = () => { + while (i < this.htmlParts.length) { + const html = this.htmlParts[i]; + const flusher = flushers[i]; + i++; + if (html) { + destination.write(markHTMLString(html)); + } + if (flusher) { + const result = flusher.flush(); + if (isPromise(result)) { + return result.then(iterate); + } + } + } + }; + return iterate(); + } +} +function isRenderTemplateResult(obj) { + return typeof obj === "object" && obj !== null && !!obj[renderTemplateResultSym]; +} +function renderTemplate(htmlParts, ...expressions) { + return new RenderTemplateResult(htmlParts, expressions); +} + +const slotString = Symbol.for("astro:slot-string"); +class SlotString extends HTMLString { + instructions; + [slotString]; + constructor(content, instructions) { + super(content); + this.instructions = instructions; + this[slotString] = true; + } +} +function isSlotString(str) { + return !!str[slotString]; +} +function renderSlot(result, slotted, fallback) { + return { + async render(destination) { + await renderChild(destination, typeof slotted === "function" ? slotted(result) : slotted); + } + }; +} +async function renderSlotToString(result, slotted, fallback) { + let content = ""; + let instructions = null; + const temporaryDestination = { + write(chunk) { + if (chunk instanceof SlotString) { + content += chunk; + if (chunk.instructions) { + instructions ??= []; + instructions.push(...chunk.instructions); + } + } else if (chunk instanceof Response) return; + else if (typeof chunk === "object" && "type" in chunk && typeof chunk.type === "string") { + if (instructions === null) { + instructions = []; + } + instructions.push(chunk); + } else { + content += chunkToString(result, chunk); + } + } + }; + const renderInstance = renderSlot(result, slotted); + await renderInstance.render(temporaryDestination); + return markHTMLString(new SlotString(content, instructions)); +} +async function renderSlots(result, slots = {}) { + let slotInstructions = null; + let children = {}; + if (slots) { + await Promise.all( + Object.entries(slots).map( + ([key, value]) => renderSlotToString(result, value).then((output) => { + if (output.instructions) { + if (slotInstructions === null) { + slotInstructions = []; + } + slotInstructions.push(...output.instructions); + } + children[key] = output; + }) + ) + ); + } + return { slotInstructions, children }; +} +function createSlotValueFromString(content) { + return function() { + return renderTemplate`${unescapeHTML(content)}`; + }; +} + +const internalProps = /* @__PURE__ */ new Set([ + "server:component-path", + "server:component-export", + "server:component-directive", + "server:defer" +]); +function containsServerDirective(props) { + return "server:component-directive" in props; +} +const SCRIPT_RE = /<\/script/giu; +const COMMENT_RE = /"); + for (const name in this.slots) { + if (name === "fallback") { + await renderChild(destination, this.slots.fallback(this.result)); + } + } + destination.write( + `` + ); + } +} +const renderServerIslandRuntime = () => { + return ``; +}; +const SERVER_ISLAND_REPLACER = markHTMLString( + `async function replaceServerIsland(id, r) { + let s = document.querySelector(\`script[data-island-id="\${id}"]\`); + // If there's no matching script, or the request fails then return + if (!s || r.status !== 200 || r.headers.get('content-type')?.split(';')[0].trim() !== 'text/html') return; + // Load the HTML before modifying the DOM in case of errors + let html = await r.text(); + // Remove any placeholder content before the island script + while (s.previousSibling && s.previousSibling.nodeType !== 8 && s.previousSibling.data !== '[if astro]>server-island-start line.trim()).filter((line) => line && !line.startsWith("//")).join(" ") +); + +const Fragment = Symbol.for("astro:fragment"); +const Renderer = Symbol.for("astro:renderer"); +const encoder = new TextEncoder(); +const decoder = new TextDecoder(); +function stringifyChunk(result, chunk) { + if (isRenderInstruction(chunk)) { + const instruction = chunk; + switch (instruction.type) { + case "directive": { + const { hydration } = instruction; + let needsHydrationScript = hydration && determineIfNeedsHydrationScript(result); + let needsDirectiveScript = hydration && determinesIfNeedsDirectiveScript(result, hydration.directive); + if (needsHydrationScript) { + let prescripts = getPrescripts(result, "both", hydration.directive); + return markHTMLString(prescripts); + } else if (needsDirectiveScript) { + let prescripts = getPrescripts(result, "directive", hydration.directive); + return markHTMLString(prescripts); + } else { + return ""; + } + } + case "head": { + if (result._metadata.hasRenderedHead || result.partial) { + return ""; + } + return renderAllHeadContent(result); + } + case "maybe-head": { + if (result._metadata.hasRenderedHead || result._metadata.headInTree || result.partial) { + return ""; + } + return renderAllHeadContent(result); + } + case "renderer-hydration-script": { + const { rendererSpecificHydrationScripts } = result._metadata; + const { rendererName } = instruction; + if (!rendererSpecificHydrationScripts.has(rendererName)) { + rendererSpecificHydrationScripts.add(rendererName); + return instruction.render(); + } + return ""; + } + case "server-island-runtime": { + if (result._metadata.hasRenderedServerIslandRuntime) { + return ""; + } + result._metadata.hasRenderedServerIslandRuntime = true; + return renderServerIslandRuntime(); + } + default: { + throw new Error(`Unknown chunk type: ${chunk.type}`); + } + } + } else if (chunk instanceof Response) { + return ""; + } else if (isSlotString(chunk)) { + let out = ""; + const c = chunk; + if (c.instructions) { + for (const instr of c.instructions) { + out += stringifyChunk(result, instr); + } + } + out += chunk.toString(); + return out; + } + return chunk.toString(); +} +function chunkToString(result, chunk) { + if (ArrayBuffer.isView(chunk)) { + return decoder.decode(chunk); + } else { + return stringifyChunk(result, chunk); + } +} +function chunkToByteArray(result, chunk) { + if (ArrayBuffer.isView(chunk)) { + return chunk; + } else { + const stringified = stringifyChunk(result, chunk); + return encoder.encode(stringified.toString()); + } +} +function isRenderInstance(obj) { + return !!obj && typeof obj === "object" && "render" in obj && typeof obj.render === "function"; +} + +function renderChild(destination, child) { + if (isPromise(child)) { + return child.then((x) => renderChild(destination, x)); + } + if (child instanceof SlotString) { + destination.write(child); + return; + } + if (isHTMLString(child)) { + destination.write(child); + return; + } + if (Array.isArray(child)) { + return renderArray(destination, child); + } + if (typeof child === "function") { + return renderChild(destination, child()); + } + if (!child && child !== 0) { + return; + } + if (typeof child === "string") { + destination.write(markHTMLString(escapeHTML(child))); + return; + } + if (isRenderInstance(child)) { + return child.render(destination); + } + if (isRenderTemplateResult(child)) { + return child.render(destination); + } + if (isAstroComponentInstance(child)) { + return child.render(destination); + } + if (ArrayBuffer.isView(child)) { + destination.write(child); + return; + } + if (typeof child === "object" && (Symbol.asyncIterator in child || Symbol.iterator in child)) { + if (Symbol.asyncIterator in child) { + return renderAsyncIterable(destination, child); + } + return renderIterable(destination, child); + } + destination.write(child); +} +function renderArray(destination, children) { + const flushers = children.map((c) => { + return createBufferedRenderer(destination, (bufferDestination) => { + return renderChild(bufferDestination, c); + }); + }); + const iterator = flushers[Symbol.iterator](); + const iterate = () => { + for (; ; ) { + const { value: flusher, done } = iterator.next(); + if (done) { + break; + } + const result = flusher.flush(); + if (isPromise(result)) { + return result.then(iterate); + } + } + }; + return iterate(); +} +function renderIterable(destination, children) { + const iterator = children[Symbol.iterator](); + const iterate = () => { + for (; ; ) { + const { value, done } = iterator.next(); + if (done) { + break; + } + const result = renderChild(destination, value); + if (isPromise(result)) { + return result.then(iterate); + } + } + }; + return iterate(); +} +async function renderAsyncIterable(destination, children) { + for await (const value of children) { + await renderChild(destination, value); + } +} + +const astroComponentInstanceSym = Symbol.for("astro.componentInstance"); +class AstroComponentInstance { + [astroComponentInstanceSym] = true; + result; + props; + slotValues; + factory; + returnValue; + constructor(result, props, slots, factory) { + this.result = result; + this.props = props; + this.factory = factory; + this.slotValues = {}; + for (const name in slots) { + let didRender = false; + let value = slots[name](result); + this.slotValues[name] = () => { + if (!didRender) { + didRender = true; + return value; + } + return slots[name](result); + }; + } + } + init(result) { + if (this.returnValue !== void 0) { + return this.returnValue; + } + this.returnValue = this.factory(result, this.props, this.slotValues); + if (isPromise(this.returnValue)) { + this.returnValue.then((resolved) => { + this.returnValue = resolved; + }).catch(() => { + }); + } + return this.returnValue; + } + render(destination) { + const returnValue = this.init(this.result); + if (isPromise(returnValue)) { + return returnValue.then((x) => this.renderImpl(destination, x)); + } + return this.renderImpl(destination, returnValue); + } + renderImpl(destination, returnValue) { + if (isHeadAndContent(returnValue)) { + return returnValue.content.render(destination); + } else { + return renderChild(destination, returnValue); + } + } +} +function validateComponentProps(props, clientDirectives, displayName) { + if (props != null) { + const directives = [...clientDirectives.keys()].map((directive) => `client:${directive}`); + for (const prop of Object.keys(props)) { + if (directives.includes(prop)) { + console.warn( + `You are attempting to render <${displayName} ${prop} />, but ${displayName} is an Astro component. Astro components do not render in the client and should not have a hydration directive. Please use a framework component for client rendering.` + ); + } + } + } +} +function createAstroComponentInstance(result, displayName, factory, props, slots = {}) { + validateComponentProps(props, result.clientDirectives, displayName); + const instance = new AstroComponentInstance(result, props, slots, factory); + if (isAPropagatingComponent(result, factory)) { + result._metadata.propagators.add(instance); + } + return instance; +} +function isAstroComponentInstance(obj) { + return typeof obj === "object" && obj !== null && !!obj[astroComponentInstanceSym]; +} + +const DOCTYPE_EXP = /" : "\n"; + str += doctype; + } + } + if (chunk instanceof Response) return; + str += chunkToString(result, chunk); + } + }; + await templateResult.render(destination); + return str; +} +async function renderToReadableStream(result, componentFactory, props, children, isPage = false, route) { + const templateResult = await callComponentAsTemplateResultOrResponse( + result, + componentFactory, + props, + children, + route + ); + if (templateResult instanceof Response) return templateResult; + let renderedFirstPageChunk = false; + if (isPage) { + await bufferHeadContent(result); + } + return new ReadableStream({ + start(controller) { + const destination = { + write(chunk) { + if (isPage && !renderedFirstPageChunk) { + renderedFirstPageChunk = true; + if (!result.partial && !DOCTYPE_EXP.test(String(chunk))) { + const doctype = result.compressHTML ? "" : "\n"; + controller.enqueue(encoder.encode(doctype)); + } + } + if (chunk instanceof Response) { + throw new AstroError({ + ...ResponseSentError + }); + } + const bytes = chunkToByteArray(result, chunk); + controller.enqueue(bytes); + } + }; + (async () => { + try { + await templateResult.render(destination); + controller.close(); + } catch (e) { + if (AstroError.is(e) && !e.loc) { + e.setLocation({ + file: route?.component + }); + } + setTimeout(() => controller.error(e), 0); + } + })(); + }, + cancel() { + result.cancelled = true; + } + }); +} +async function callComponentAsTemplateResultOrResponse(result, componentFactory, props, children, route) { + const factoryResult = await componentFactory(result, props, children); + if (factoryResult instanceof Response) { + return factoryResult; + } else if (isHeadAndContent(factoryResult)) { + if (!isRenderTemplateResult(factoryResult.content)) { + throw new AstroError({ + ...OnlyResponseCanBeReturned, + message: OnlyResponseCanBeReturned.message( + route?.route, + typeof factoryResult + ), + location: { + file: route?.component + } + }); + } + return factoryResult.content; + } else if (!isRenderTemplateResult(factoryResult)) { + throw new AstroError({ + ...OnlyResponseCanBeReturned, + message: OnlyResponseCanBeReturned.message(route?.route, typeof factoryResult), + location: { + file: route?.component + } + }); + } + return factoryResult; +} +async function bufferHeadContent(result) { + const iterator = result._metadata.propagators.values(); + while (true) { + const { value, done } = iterator.next(); + if (done) { + break; + } + const returnValue = await value.init(result); + if (isHeadAndContent(returnValue) && returnValue.head) { + result._metadata.extraHead.push(returnValue.head); + } + } +} +async function renderToAsyncIterable(result, componentFactory, props, children, isPage = false, route) { + const templateResult = await callComponentAsTemplateResultOrResponse( + result, + componentFactory, + props, + children, + route + ); + if (templateResult instanceof Response) return templateResult; + let renderedFirstPageChunk = false; + if (isPage) { + await bufferHeadContent(result); + } + let error = null; + let next = null; + const buffer = []; + let renderingComplete = false; + const iterator = { + async next() { + if (result.cancelled) return { done: true, value: void 0 }; + if (next !== null) { + await next.promise; + } else if (!renderingComplete && !buffer.length) { + next = promiseWithResolvers(); + await next.promise; + } + if (!renderingComplete) { + next = promiseWithResolvers(); + } + if (error) { + throw error; + } + let length = 0; + for (let i = 0, len = buffer.length; i < len; i++) { + length += buffer[i].length; + } + let mergedArray = new Uint8Array(length); + let offset = 0; + for (let i = 0, len = buffer.length; i < len; i++) { + const item = buffer[i]; + mergedArray.set(item, offset); + offset += item.length; + } + buffer.length = 0; + const returnValue = { + // The iterator is done when rendering has finished + // and there are no more chunks to return. + done: length === 0 && renderingComplete, + value: mergedArray + }; + return returnValue; + }, + async return() { + result.cancelled = true; + return { done: true, value: void 0 }; + } + }; + const destination = { + write(chunk) { + if (isPage && !renderedFirstPageChunk) { + renderedFirstPageChunk = true; + if (!result.partial && !DOCTYPE_EXP.test(String(chunk))) { + const doctype = result.compressHTML ? "" : "\n"; + buffer.push(encoder.encode(doctype)); + } + } + if (chunk instanceof Response) { + throw new AstroError(ResponseSentError); + } + const bytes = chunkToByteArray(result, chunk); + if (bytes.length > 0) { + buffer.push(bytes); + next?.resolve(); + } else if (buffer.length > 0) { + next?.resolve(); + } + } + }; + const renderResult = toPromise(() => templateResult.render(destination)); + renderResult.catch((err) => { + error = err; + }).finally(() => { + renderingComplete = true; + next?.resolve(); + }); + return { + [Symbol.asyncIterator]() { + return iterator; + } + }; +} +function toPromise(fn) { + try { + const result = fn(); + return isPromise(result) ? result : Promise.resolve(result); + } catch (err) { + return Promise.reject(err); + } +} + +function componentIsHTMLElement(Component) { + return typeof HTMLElement !== "undefined" && HTMLElement.isPrototypeOf(Component); +} +async function renderHTMLElement(result, constructor, props, slots) { + const name = getHTMLElementName(constructor); + let attrHTML = ""; + for (const attr in props) { + attrHTML += ` ${attr}="${toAttributeString(await props[attr])}"`; + } + return markHTMLString( + `<${name}${attrHTML}>${await renderSlotToString(result, slots?.default)}` + ); +} +function getHTMLElementName(constructor) { + const definedName = customElements.getName(constructor); + if (definedName) return definedName; + const assignedName = constructor.name.replace(/^HTML|Element$/g, "").replace(/[A-Z]/g, "-$&").toLowerCase().replace(/^-/, "html-"); + return assignedName; +} + +const needsHeadRenderingSymbol = Symbol.for("astro.needsHeadRendering"); +const rendererAliases = /* @__PURE__ */ new Map([["solid", "solid-js"]]); +const clientOnlyValues = /* @__PURE__ */ new Set(["solid-js", "react", "preact", "vue", "svelte"]); +function guessRenderers(componentUrl) { + const extname = componentUrl?.split(".").pop(); + switch (extname) { + case "svelte": + return ["@astrojs/svelte"]; + case "vue": + return ["@astrojs/vue"]; + case "jsx": + case "tsx": + return ["@astrojs/react", "@astrojs/preact", "@astrojs/solid-js", "@astrojs/vue (jsx)"]; + case void 0: + default: + return [ + "@astrojs/react", + "@astrojs/preact", + "@astrojs/solid-js", + "@astrojs/vue", + "@astrojs/svelte" + ]; + } +} +function isFragmentComponent(Component) { + return Component === Fragment; +} +function isHTMLComponent(Component) { + return Component && Component["astro:html"] === true; +} +const ASTRO_SLOT_EXP = /<\/?astro-slot\b[^>]*>/g; +const ASTRO_STATIC_SLOT_EXP = /<\/?astro-static-slot\b[^>]*>/g; +function removeStaticAstroSlot(html, supportsAstroStaticSlot = true) { + const exp = supportsAstroStaticSlot ? ASTRO_STATIC_SLOT_EXP : ASTRO_SLOT_EXP; + return html.replace(exp, ""); +} +async function renderFrameworkComponent(result, displayName, Component, _props, slots = {}) { + if (!Component && "client:only" in _props === false) { + throw new Error( + `Unable to render ${displayName} because it is ${Component}! +Did you forget to import the component or is it possible there is a typo?` + ); + } + const { renderers, clientDirectives } = result; + const metadata = { + astroStaticSlot: true, + displayName + }; + const { hydration, isPage, props, propsWithoutTransitionAttributes } = extractDirectives( + _props, + clientDirectives + ); + let html = ""; + let attrs = void 0; + if (hydration) { + metadata.hydrate = hydration.directive; + metadata.hydrateArgs = hydration.value; + metadata.componentExport = hydration.componentExport; + metadata.componentUrl = hydration.componentUrl; + } + const probableRendererNames = guessRenderers(metadata.componentUrl); + const validRenderers = renderers.filter((r) => r.name !== "astro:jsx"); + const { children, slotInstructions } = await renderSlots(result, slots); + let renderer; + if (metadata.hydrate !== "only") { + let isTagged = false; + try { + isTagged = Component && Component[Renderer]; + } catch { + } + if (isTagged) { + const rendererName = Component[Renderer]; + renderer = renderers.find(({ name }) => name === rendererName); + } + if (!renderer) { + let error; + for (const r of renderers) { + try { + if (await r.ssr.check.call({ result }, Component, props, children)) { + renderer = r; + break; + } + } catch (e) { + error ??= e; + } + } + if (!renderer && error) { + throw error; + } + } + if (!renderer && typeof HTMLElement === "function" && componentIsHTMLElement(Component)) { + const output = await renderHTMLElement( + result, + Component, + _props, + slots + ); + return { + render(destination) { + destination.write(output); + } + }; + } + } else { + if (metadata.hydrateArgs) { + const rendererName = rendererAliases.has(metadata.hydrateArgs) ? rendererAliases.get(metadata.hydrateArgs) : metadata.hydrateArgs; + if (clientOnlyValues.has(rendererName)) { + renderer = renderers.find( + ({ name }) => name === `@astrojs/${rendererName}` || name === rendererName + ); + } + } + if (!renderer && validRenderers.length === 1) { + renderer = validRenderers[0]; + } + if (!renderer) { + const extname = metadata.componentUrl?.split(".").pop(); + renderer = renderers.find(({ name }) => name === `@astrojs/${extname}` || name === extname); + } + } + let componentServerRenderEndTime; + if (!renderer) { + if (metadata.hydrate === "only") { + const rendererName = rendererAliases.has(metadata.hydrateArgs) ? rendererAliases.get(metadata.hydrateArgs) : metadata.hydrateArgs; + if (clientOnlyValues.has(rendererName)) { + const plural = validRenderers.length > 1; + throw new AstroError({ + ...NoMatchingRenderer, + message: NoMatchingRenderer.message( + metadata.displayName, + metadata?.componentUrl?.split(".").pop(), + plural, + validRenderers.length + ), + hint: NoMatchingRenderer.hint( + formatList(probableRendererNames.map((r) => "`" + r + "`")) + ) + }); + } else { + throw new AstroError({ + ...NoClientOnlyHint, + message: NoClientOnlyHint.message(metadata.displayName), + hint: NoClientOnlyHint.hint( + probableRendererNames.map((r) => r.replace("@astrojs/", "")).join("|") + ) + }); + } + } else if (typeof Component !== "string") { + const matchingRenderers = validRenderers.filter( + (r) => probableRendererNames.includes(r.name) + ); + const plural = validRenderers.length > 1; + if (matchingRenderers.length === 0) { + throw new AstroError({ + ...NoMatchingRenderer, + message: NoMatchingRenderer.message( + metadata.displayName, + metadata?.componentUrl?.split(".").pop(), + plural, + validRenderers.length + ), + hint: NoMatchingRenderer.hint( + formatList(probableRendererNames.map((r) => "`" + r + "`")) + ) + }); + } else if (matchingRenderers.length === 1) { + renderer = matchingRenderers[0]; + ({ html, attrs } = await renderer.ssr.renderToStaticMarkup.call( + { result }, + Component, + propsWithoutTransitionAttributes, + children, + metadata + )); + } else { + throw new Error(`Unable to render ${metadata.displayName}! + +This component likely uses ${formatList(probableRendererNames)}, +but Astro encountered an error during server-side rendering. + +Please ensure that ${metadata.displayName}: +1. Does not unconditionally access browser-specific globals like \`window\` or \`document\`. + If this is unavoidable, use the \`client:only\` hydration directive. +2. Does not conditionally return \`null\` or \`undefined\` when rendered on the server. + +If you're still stuck, please open an issue on GitHub or join us at https://astro.build/chat.`); + } + } + } else { + if (metadata.hydrate === "only") { + html = await renderSlotToString(result, slots?.fallback); + } else { + const componentRenderStartTime = performance.now(); + ({ html, attrs } = await renderer.ssr.renderToStaticMarkup.call( + { result }, + Component, + propsWithoutTransitionAttributes, + children, + metadata + )); + if (process.env.NODE_ENV === "development") + componentServerRenderEndTime = performance.now() - componentRenderStartTime; + } + } + if (!html && typeof Component === "string") { + const Tag = sanitizeElementName(Component); + const childSlots = Object.values(children).join(""); + const renderTemplateResult = renderTemplate`<${Tag}${internalSpreadAttributes( + props + )}${markHTMLString( + childSlots === "" && voidElementNames.test(Tag) ? `/>` : `>${childSlots}` + )}`; + html = ""; + const destination = { + write(chunk) { + if (chunk instanceof Response) return; + html += chunkToString(result, chunk); + } + }; + await renderTemplateResult.render(destination); + } + if (!hydration) { + return { + render(destination) { + if (slotInstructions) { + for (const instruction of slotInstructions) { + destination.write(instruction); + } + } + if (isPage || renderer?.name === "astro:jsx") { + destination.write(html); + } else if (html && html.length > 0) { + destination.write( + markHTMLString(removeStaticAstroSlot(html, renderer?.ssr?.supportsAstroStaticSlot)) + ); + } + } + }; + } + const astroId = shorthash( + ` +${html} +${serializeProps( + props, + metadata + )}` + ); + const island = await generateHydrateScript( + { renderer, result, astroId, props, attrs }, + metadata + ); + if (componentServerRenderEndTime && process.env.NODE_ENV === "development") + island.props["server-render-time"] = componentServerRenderEndTime; + let unrenderedSlots = []; + if (html) { + if (Object.keys(children).length > 0) { + for (const key of Object.keys(children)) { + let tagName = renderer?.ssr?.supportsAstroStaticSlot ? !!metadata.hydrate ? "astro-slot" : "astro-static-slot" : "astro-slot"; + let expectedHTML = key === "default" ? `<${tagName}>` : `<${tagName} name="${key}">`; + if (!html.includes(expectedHTML)) { + unrenderedSlots.push(key); + } + } + } + } else { + unrenderedSlots = Object.keys(children); + } + const template = unrenderedSlots.length > 0 ? unrenderedSlots.map( + (key) => `` + ).join("") : ""; + island.children = `${html ?? ""}${template}`; + if (island.children) { + island.props["await-children"] = ""; + island.children += ``; + } + return { + render(destination) { + if (slotInstructions) { + for (const instruction of slotInstructions) { + destination.write(instruction); + } + } + destination.write(createRenderInstruction({ type: "directive", hydration })); + if (hydration.directive !== "only" && renderer?.ssr.renderHydrationScript) { + destination.write( + createRenderInstruction({ + type: "renderer-hydration-script", + rendererName: renderer.name, + render: renderer.ssr.renderHydrationScript + }) + ); + } + const renderedElement = renderElement$1("astro-island", island, false); + destination.write(markHTMLString(renderedElement)); + } + }; +} +function sanitizeElementName(tag) { + const unsafe = /[&<>'"\s]+/; + if (!unsafe.test(tag)) return tag; + return tag.trim().split(unsafe)[0].trim(); +} +async function renderFragmentComponent(result, slots = {}) { + const children = await renderSlotToString(result, slots?.default); + return { + render(destination) { + if (children == null) return; + destination.write(children); + } + }; +} +async function renderHTMLComponent(result, Component, _props, slots = {}) { + const { slotInstructions, children } = await renderSlots(result, slots); + const html = Component({ slots: children }); + const hydrationHtml = slotInstructions ? slotInstructions.map((instr) => chunkToString(result, instr)).join("") : ""; + return { + render(destination) { + destination.write(markHTMLString(hydrationHtml + html)); + } + }; +} +function renderAstroComponent(result, displayName, Component, props, slots = {}) { + if (containsServerDirective(props)) { + const serverIslandComponent = new ServerIslandComponent(result, props, slots, displayName); + result._metadata.propagators.add(serverIslandComponent); + return serverIslandComponent; + } + const instance = createAstroComponentInstance(result, displayName, Component, props, slots); + return { + render(destination) { + return instance.render(destination); + } + }; +} +function renderComponent(result, displayName, Component, props, slots = {}) { + if (isPromise(Component)) { + return Component.catch(handleCancellation).then((x) => { + return renderComponent(result, displayName, x, props, slots); + }); + } + if (isFragmentComponent(Component)) { + return renderFragmentComponent(result, slots).catch(handleCancellation); + } + props = normalizeProps(props); + if (isHTMLComponent(Component)) { + return renderHTMLComponent(result, Component, props, slots).catch(handleCancellation); + } + if (isAstroComponentFactory(Component)) { + return renderAstroComponent(result, displayName, Component, props, slots); + } + return renderFrameworkComponent(result, displayName, Component, props, slots).catch( + handleCancellation + ); + function handleCancellation(e) { + if (result.cancelled) + return { + render() { + } + }; + throw e; + } +} +function normalizeProps(props) { + if (props["class:list"] !== void 0) { + const value = props["class:list"]; + delete props["class:list"]; + props["class"] = clsx(props["class"], value); + if (props["class"] === "") { + delete props["class"]; + } + } + return props; +} +async function renderComponentToString(result, displayName, Component, props, slots = {}, isPage = false, route) { + let str = ""; + let renderedFirstPageChunk = false; + let head = ""; + if (isPage && !result.partial && nonAstroPageNeedsHeadInjection(Component)) { + head += chunkToString(result, maybeRenderHead()); + } + try { + const destination = { + write(chunk) { + if (isPage && !result.partial && !renderedFirstPageChunk) { + renderedFirstPageChunk = true; + if (!/" : "\n"; + str += doctype + head; + } + } + if (chunk instanceof Response) return; + str += chunkToString(result, chunk); + } + }; + const renderInstance = await renderComponent(result, displayName, Component, props, slots); + if (containsServerDirective(props)) { + await bufferHeadContent(result); + } + await renderInstance.render(destination); + } catch (e) { + if (AstroError.is(e) && !e.loc) { + e.setLocation({ + file: route?.component + }); + } + throw e; + } + return str; +} +function nonAstroPageNeedsHeadInjection(pageComponent) { + return !!pageComponent?.[needsHeadRenderingSymbol]; +} + +const ClientOnlyPlaceholder = "astro-client-only"; +const hasTriedRenderComponentSymbol = Symbol("hasTriedRenderComponent"); +async function renderJSX(result, vnode) { + switch (true) { + case vnode instanceof HTMLString: + if (vnode.toString().trim() === "") { + return ""; + } + return vnode; + case typeof vnode === "string": + return markHTMLString(escapeHTML(vnode)); + case typeof vnode === "function": + return vnode; + case (!vnode && vnode !== 0): + return ""; + case Array.isArray(vnode): + return markHTMLString( + (await Promise.all(vnode.map((v) => renderJSX(result, v)))).join("") + ); + } + return renderJSXVNode(result, vnode); +} +async function renderJSXVNode(result, vnode) { + if (isVNode(vnode)) { + switch (true) { + case !vnode.type: { + throw new Error(`Unable to render ${result.pathname} because it contains an undefined Component! +Did you forget to import the component or is it possible there is a typo?`); + } + case vnode.type === Symbol.for("astro:fragment"): + return renderJSX(result, vnode.props.children); + case isAstroComponentFactory(vnode.type): { + let props = {}; + let slots = {}; + for (const [key, value] of Object.entries(vnode.props ?? {})) { + if (key === "children" || value && typeof value === "object" && value["$$slot"]) { + slots[key === "children" ? "default" : key] = () => renderJSX(result, value); + } else { + props[key] = value; + } + } + const str = await renderComponentToString( + result, + vnode.type.name, + vnode.type, + props, + slots + ); + const html = markHTMLString(str); + return html; + } + case (!vnode.type && vnode.type !== 0): + return ""; + case (typeof vnode.type === "string" && vnode.type !== ClientOnlyPlaceholder): + return markHTMLString(await renderElement(result, vnode.type, vnode.props ?? {})); + } + if (vnode.type) { + let extractSlots2 = function(child) { + if (Array.isArray(child)) { + return child.map((c) => extractSlots2(c)); + } + if (!isVNode(child)) { + _slots.default.push(child); + return; + } + if ("slot" in child.props) { + _slots[child.props.slot] = [..._slots[child.props.slot] ?? [], child]; + delete child.props.slot; + return; + } + _slots.default.push(child); + }; + if (typeof vnode.type === "function" && vnode.props["server:root"]) { + const output2 = await vnode.type(vnode.props ?? {}); + return await renderJSX(result, output2); + } + if (typeof vnode.type === "function") { + if (vnode.props[hasTriedRenderComponentSymbol]) { + delete vnode.props[hasTriedRenderComponentSymbol]; + const output2 = await vnode.type(vnode.props ?? {}); + if (output2?.[AstroJSX] || !output2) { + return await renderJSXVNode(result, output2); + } else { + return; + } + } else { + vnode.props[hasTriedRenderComponentSymbol] = true; + } + } + const { children = null, ...props } = vnode.props ?? {}; + const _slots = { + default: [] + }; + extractSlots2(children); + for (const [key, value] of Object.entries(props)) { + if (value?.["$$slot"]) { + _slots[key] = value; + delete props[key]; + } + } + const slotPromises = []; + const slots = {}; + for (const [key, value] of Object.entries(_slots)) { + slotPromises.push( + renderJSX(result, value).then((output2) => { + if (output2.toString().trim().length === 0) return; + slots[key] = () => output2; + }) + ); + } + await Promise.all(slotPromises); + let output; + if (vnode.type === ClientOnlyPlaceholder && vnode.props["client:only"]) { + output = await renderComponentToString( + result, + vnode.props["client:display-name"] ?? "", + null, + props, + slots + ); + } else { + output = await renderComponentToString( + result, + typeof vnode.type === "function" ? vnode.type.name : vnode.type, + vnode.type, + props, + slots + ); + } + return markHTMLString(output); + } + } + return markHTMLString(`${vnode}`); +} +async function renderElement(result, tag, { children, ...props }) { + return markHTMLString( + `<${tag}${spreadAttributes(props)}${markHTMLString( + (children == null || children == "") && voidElementNames.test(tag) ? `/>` : `>${children == null ? "" : await renderJSX(result, prerenderElementChildren(tag, children))}` + )}` + ); +} +function prerenderElementChildren(tag, children) { + if (typeof children === "string" && (tag === "style" || tag === "script")) { + return markHTMLString(children); + } else { + return children; + } +} + +async function renderPage(result, componentFactory, props, children, streaming, route) { + if (!isAstroComponentFactory(componentFactory)) { + result._metadata.headInTree = result.componentMetadata.get(componentFactory.moduleId)?.containsHead ?? false; + const pageProps = { ...props ?? {}, "server:root": true }; + const str = await renderComponentToString( + result, + componentFactory.name, + componentFactory, + pageProps, + {}, + true, + route + ); + const bytes = encoder.encode(str); + const headers2 = new Headers([ + ["Content-Type", "text/html"], + ["Content-Length", bytes.byteLength.toString()] + ]); + if (result.cspDestination === "header" || result.cspDestination === "adapter") { + headers2.set("content-security-policy", renderCspContent(result)); + } + return new Response(bytes, { + headers: headers2 + }); + } + result._metadata.headInTree = result.componentMetadata.get(componentFactory.moduleId)?.containsHead ?? false; + let body; + if (streaming) { + if (isNode && !isDeno) { + const nodeBody = await renderToAsyncIterable( + result, + componentFactory, + props, + children, + true, + route + ); + body = nodeBody; + } else { + body = await renderToReadableStream(result, componentFactory, props, children, true, route); + } + } else { + body = await renderToString(result, componentFactory, props, children, true, route); + } + if (body instanceof Response) return body; + const init = result.response; + const headers = new Headers(init.headers); + if (result.shouldInjectCspMetaTags && result.cspDestination === "header" || result.cspDestination === "adapter") { + headers.set("content-security-policy", renderCspContent(result)); + } + if (!streaming && typeof body === "string") { + body = encoder.encode(body); + headers.set("Content-Length", body.byteLength.toString()); + } + let status = init.status; + let statusText = init.statusText; + if (route?.route === "/404") { + status = 404; + if (statusText === "OK") { + statusText = "Not Found"; + } + } else if (route?.route === "/500") { + status = 500; + if (statusText === "OK") { + statusText = "Internal Server Error"; + } + } + if (status) { + return new Response(body, { ...init, headers, status, statusText }); + } else { + return new Response(body, { ...init, headers }); + } +} + +/*! https://mths.be/cssesc v3.0.0 by @mathias */ + +var cssesc_1; +var hasRequiredCssesc; + +function requireCssesc () { + if (hasRequiredCssesc) return cssesc_1; + hasRequiredCssesc = 1; + + var object = {}; + var hasOwnProperty = object.hasOwnProperty; + var merge = function merge(options, defaults) { + if (!options) { + return defaults; + } + var result = {}; + for (var key in defaults) { + // `if (defaults.hasOwnProperty(key) { … }` is not needed here, since + // only recognized option names are used. + result[key] = hasOwnProperty.call(options, key) ? options[key] : defaults[key]; + } + return result; + }; + + var regexAnySingleEscape = /[ -,\.\/:-@\[-\^`\{-~]/; + var regexSingleEscape = /[ -,\.\/:-@\[\]\^`\{-~]/; + var regexExcessiveSpaces = /(^|\\+)?(\\[A-F0-9]{1,6})\x20(?![a-fA-F0-9\x20])/g; + + // https://mathiasbynens.be/notes/css-escapes#css + var cssesc = function cssesc(string, options) { + options = merge(options, cssesc.options); + if (options.quotes != 'single' && options.quotes != 'double') { + options.quotes = 'single'; + } + var quote = options.quotes == 'double' ? '"' : '\''; + var isIdentifier = options.isIdentifier; + + var firstChar = string.charAt(0); + var output = ''; + var counter = 0; + var length = string.length; + while (counter < length) { + var character = string.charAt(counter++); + var codePoint = character.charCodeAt(); + var value = void 0; + // If it’s not a printable ASCII character… + if (codePoint < 0x20 || codePoint > 0x7E) { + if (codePoint >= 0xD800 && codePoint <= 0xDBFF && counter < length) { + // It’s a high surrogate, and there is a next character. + var extra = string.charCodeAt(counter++); + if ((extra & 0xFC00) == 0xDC00) { + // next character is low surrogate + codePoint = ((codePoint & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000; + } else { + // It’s an unmatched surrogate; only append this code unit, in case + // the next code unit is the high surrogate of a surrogate pair. + counter--; + } + } + value = '\\' + codePoint.toString(16).toUpperCase() + ' '; + } else { + if (options.escapeEverything) { + if (regexAnySingleEscape.test(character)) { + value = '\\' + character; + } else { + value = '\\' + codePoint.toString(16).toUpperCase() + ' '; + } + } else if (/[\t\n\f\r\x0B]/.test(character)) { + value = '\\' + codePoint.toString(16).toUpperCase() + ' '; + } else if (character == '\\' || !isIdentifier && (character == '"' && quote == character || character == '\'' && quote == character) || isIdentifier && regexSingleEscape.test(character)) { + value = '\\' + character; + } else { + value = character; + } + } + output += value; + } + + if (isIdentifier) { + if (/^-[-\d]/.test(output)) { + output = '\\-' + output.slice(1); + } else if (/\d/.test(firstChar)) { + output = '\\3' + firstChar + ' ' + output.slice(1); + } + } + + // Remove spaces after `\HEX` escapes that are not followed by a hex digit, + // since they’re redundant. Note that this is only possible if the escape + // sequence isn’t preceded by an odd number of backslashes. + output = output.replace(regexExcessiveSpaces, function ($0, $1, $2) { + if ($1 && $1.length % 2) { + // It’s not safe to remove the space, so don’t. + return $0; + } + // Strip the space. + return ($1 || '') + $2; + }); + + if (!isIdentifier && options.wrap) { + return quote + output + quote; + } + return output; + }; + + // Expose default options (so they can be overridden globally). + cssesc.options = { + 'escapeEverything': false, + 'isIdentifier': false, + 'quotes': 'single', + 'wrap': false + }; + + cssesc.version = '3.0.0'; + + cssesc_1 = cssesc; + return cssesc_1; +} + +requireCssesc(); + +"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_".split("").reduce((v, c) => (v[c.charCodeAt(0)] = c, v), []); +"-0123456789_".split("").reduce((v, c) => (v[c.charCodeAt(0)] = c, v), []); + +function spreadAttributes(values = {}, _name, { class: scopedClassName } = {}) { + let output = ""; + if (scopedClassName) { + if (typeof values.class !== "undefined") { + values.class += ` ${scopedClassName}`; + } else if (typeof values["class:list"] !== "undefined") { + values["class:list"] = [values["class:list"], scopedClassName]; + } else { + values.class = scopedClassName; + } + } + for (const [key, value] of Object.entries(values)) { + output += addAttribute(value, key, true); + } + return markHTMLString(output); +} + +export { renderJSX as $, AstroError as A, dim as B, blue as C, MiddlewareNoDataOrNextCalled as D, ExpectedImage as E, FailedToFetchRemoteImageDimensions as F, MiddlewareNotAResponse as G, originPathnameSymbol as H, IncompatibleDescriptorOptions as I, RewriteWithBodyUsed as J, GetStaticPathsRequired as K, LocalImageUsedWrongly as L, MissingImageDimension as M, NoImageMetadata as N, InvalidGetStaticPathsReturn as O, InvalidGetStaticPathsEntry as P, GetStaticPathsExpectedParams as Q, ROUTE_TYPE_HEADER as R, GetStaticPathsInvalidRouteParam as S, PageNumberParamNotFound as T, UnsupportedImageFormat as U, DEFAULT_404_COMPONENT as V, ActionNotFoundError as W, NoMatchingStaticPathFound as X, PrerenderDynamicEndpointPathCollide as Y, ReservedSlotName as Z, renderSlotToString as _, UnsupportedImageConversion as a, chunkToString as a0, isRenderInstruction as a1, ForbiddenRewrite as a2, SessionStorageInitError as a3, SessionStorageSaveError as a4, ASTRO_VERSION as a5, CspNotEnabled as a6, green as a7, LocalsReassigned as a8, PrerenderClientAddressNotAvailable as a9, clientAddressSymbol as aa, ClientAddressNotAvailable as ab, StaticClientAddressNotAvailable as ac, AstroResponseHeadersReassigned as ad, responseSentSymbol as ae, renderPage as af, REWRITE_DIRECTIVE_HEADER_KEY as ag, REWRITE_DIRECTIVE_HEADER_VALUE as ah, renderEndpoint as ai, LocalsNotAnObject as aj, REROUTABLE_STATUS_CODES as ak, NOOP_MIDDLEWARE_HEADER as al, REDIRECT_STATUS_CODES as am, ActionsReturnedInvalidDataError as an, escape as ao, MissingSharp as ap, ExpectedImageOptions as b, ExpectedNotESMImage as c, InvalidImageService as d, createComponent as e, createAstro as f, ImageMissingAlt as g, addAttribute as h, ExperimentalFontsNotEnabled as i, FontFamilyNotFound as j, decodeKey as k, decryptString as l, maybeRenderHead as m, createSlotValueFromString as n, isAstroComponentFactory as o, renderComponent as p, REROUTE_DIRECTIVE_HEADER as q, renderTemplate as r, spreadAttributes as s, toStyleString as t, unescapeHTML as u, i18nNoLocaleFoundInPath as v, ResponseSentError as w, bold as x, red as y, yellow as z }; diff --git a/crates/biome_module_graph/benches/module_graph.rs b/crates/biome_module_graph/benches/module_graph.rs index 4e4f03acdadf..11f64b7f2423 100644 --- a/crates/biome_module_graph/benches/module_graph.rs +++ b/crates/biome_module_graph/benches/module_graph.rs @@ -41,6 +41,10 @@ const INDEX_D_TS_CASES: &[(&str, &[u8])] = &[ "RedisCommander.d.ts", include_bytes!("./RedisCommander.d.ts") as &[u8], ), + ( + "astro_server.mjs", + include_bytes!("./astro_server.mjs") as &[u8], + ), ]; fn index_d_ts_cases() -> impl Iterator { diff --git a/crates/biome_module_graph/src/diagnostics.rs b/crates/biome_module_graph/src/diagnostics.rs new file mode 100644 index 000000000000..18d603eda8c5 --- /dev/null +++ b/crates/biome_module_graph/src/diagnostics.rs @@ -0,0 +1,13 @@ +use crate::js_module_info::JsModuleInfoDiagnostic; +use biome_diagnostics::Diagnostic; + +#[derive(Debug, Clone, Diagnostic)] +pub enum ModuleDiagnostic { + JsInfo(JsModuleInfoDiagnostic), +} + +impl From for ModuleDiagnostic { + fn from(d: JsModuleInfoDiagnostic) -> Self { + Self::JsInfo(d) + } +} diff --git a/crates/biome_module_graph/src/js_module_info.rs b/crates/biome_module_graph/src/js_module_info.rs index 4723465f96e9..c86b0ccecbe6 100644 --- a/crates/biome_module_graph/src/js_module_info.rs +++ b/crates/biome_module_graph/src/js_module_info.rs @@ -1,7 +1,9 @@ mod binding; mod collector; +mod diagnostics; mod module_resolver; mod scope; +mod utils; mod visitor; use biome_js_syntax::AnyJsImportLike; @@ -20,7 +22,9 @@ use crate::ModuleGraph; use scope::{JsScope, JsScopeData, TsBindingReference}; +use crate::diagnostics::ModuleDiagnostic; pub(super) use binding::JsBindingData; +pub use diagnostics::JsModuleInfoDiagnostic; pub use module_resolver::ModuleResolver; pub(crate) use visitor::JsModuleVisitor; @@ -46,6 +50,10 @@ impl JsModuleInfo { } } + pub fn diagnostics(&self) -> &[ModuleDiagnostic] { + self.diagnostics.as_slice() + } + /// Finds an exported symbol by `name`, using the `module_graph` to /// lookup re-exports if necessary. #[inline] @@ -196,6 +204,9 @@ pub struct JsModuleInfoInner { /// info is constructed, no new types can be registered in it, and we have /// no use for a hash table anymore. pub(crate) types: Vec>, + + /// Diagnostics emitted during the resolution of the module + pub(crate) diagnostics: Vec, } #[derive(Debug, Default)] diff --git a/crates/biome_module_graph/src/js_module_info/collector.rs b/crates/biome_module_graph/src/js_module_info/collector.rs index dcf769da7c9f..99f7e72b69f4 100644 --- a/crates/biome_module_graph/src/js_module_info/collector.rs +++ b/crates/biome_module_graph/src/js_module_info/collector.rs @@ -20,13 +20,15 @@ use rust_lapper::{Interval, Lapper}; use rustc_hash::FxHashMap; use super::{ - Exports, ImportSymbol, Imports, JsExport, JsImport, JsModuleInfo, JsModuleInfoInner, - JsOwnExport, JsReexport, ResolvedPath, binding::JsBindingData, scope::JsScopeData, + Exports, ImportSymbol, Imports, JsExport, JsImport, JsModuleInfo, JsModuleInfoDiagnostic, + JsModuleInfoInner, JsOwnExport, JsReexport, ResolvedPath, binding::JsBindingData, + scope::JsScopeData, }; use crate::js_module_info::{ binding::{JsBindingReference, JsBindingReferenceKind, JsDeclarationKind}, scope::TsBindingReference, scope_id_for_range, + utils::reached_too_many_types, }; use crate::{JsImportPath, JsImportPhase}; @@ -94,6 +96,9 @@ pub(super) struct JsModuleInfoCollector { /// Static imports mapped from the local name of the binding being imported. static_imports: IndexMap, + + /// Diagnostics emitted during the collection of module graph information + diagnostics: Vec, } /// Intermediary representation for an exported symbol. @@ -750,6 +755,11 @@ impl JsModuleInfoCollector { let mut i = 0; while i < self.types.len() { + if let Err(diagnostic) = reached_too_many_types(i) { + self.diagnostics.push(diagnostic); + return; + } + if let Some(ty) = self.types.get(i).flattened(self) { self.types.replace(i, ty); did_flatten = true; @@ -1049,6 +1059,7 @@ impl JsModuleInfo { scopes: collector.scopes, scope_by_range, types: collector.types.into(), + diagnostics: collector.diagnostics.into_iter().map(Into::into).collect(), })) } } diff --git a/crates/biome_module_graph/src/js_module_info/diagnostics.rs b/crates/biome_module_graph/src/js_module_info/diagnostics.rs new file mode 100644 index 000000000000..7ba52276a06a --- /dev/null +++ b/crates/biome_module_graph/src/js_module_info/diagnostics.rs @@ -0,0 +1,100 @@ +use crate::js_module_info::utils::MAX_NUM_TYPES; +use biome_console::fmt::Formatter; +use biome_console::markup; +use biome_diagnostics::{ + Category, Diagnostic, DiagnosticTags, LogCategory, Severity, Visit, category, +}; + +#[derive(Debug, Clone, Diagnostic)] +pub enum JsModuleInfoDiagnostic { + ExceededTypesLimit(ExceededTypesLimitDiagnostic), +} + +impl JsModuleInfoDiagnostic { + pub fn exceeded_types_limit() -> Self { + Self::ExceededTypesLimit(ExceededTypesLimitDiagnostic) + } +} + +#[derive(Debug, Clone)] +pub struct ExceededTypesLimitDiagnostic; + +impl Diagnostic for ExceededTypesLimitDiagnostic { + fn severity(&self) -> Severity { + Severity::Warning + } + + fn category(&self) -> Option<&'static Category> { + Some(category!("project")) + } + + fn tags(&self) -> DiagnosticTags { + DiagnosticTags::INTERNAL + } + + fn message(&self, fmt: &mut Formatter<'_>) -> std::io::Result<()> { + // format with a thousand separators for readability: + let num = MAX_NUM_TYPES + .to_string() + .as_bytes() + .rchunks(3) + .rev() + .map(|bytes| std::str::from_utf8(bytes).unwrap()) + .collect::>() + .join(","); + + fmt.write_markup(markup! { + "Biome encountered an unusually large amount of types which exceeded the limit of "{num}"." + })?; + + fmt.write_str("\n\n")?; + + fmt.write_markup(markup! { + "Either you are analyzing ""very"" large files (did you make sure to \ + exclude your ""build/"" or ""dist/"" folder?), or you've encountered a bug in \ + Biome." + })?; + + fmt.write_str("\n\n")?; + + fmt.write_markup(markup! { + "Please follow these instructions to discover if you are accidentally \ + analyzing large files and what to do about them in ""the relative guide." + })?; + + Ok(()) + } + + fn advices(&self, visitor: &mut dyn Visit) -> std::io::Result<()> { + visitor.record_log(LogCategory::Info, &markup!{ + "In the meantime, you can ignore this file by adding its name or folder in the ""files.experimentalScannerIgnores"" option in your configuration file." + })?; + visitor.record_log(LogCategory::Info, &markup!{ + "Refer to the ""documentation"" for more information." + })?; + + visitor.record_log( + LogCategory::Info, + &markup! { + "If you think this is a bug, please report it and include the following information:" + }, + )?; + visitor.record_list(&[ + &"source code of the file;", + &"how the file is imported in the project (by a test file, a dependency, etc.);", + &"if and how the file/folder is excluded.", + ])?; + visitor.record_log( + LogCategory::Warn, + &markup! {"Failing to provide this information won't allow the team to fix the issue."}, + )?; + + Ok(()) + } +} + +impl From for JsModuleInfoDiagnostic { + fn from(d: ExceededTypesLimitDiagnostic) -> Self { + Self::ExceededTypesLimit(d) + } +} diff --git a/crates/biome_module_graph/src/js_module_info/module_resolver.rs b/crates/biome_module_graph/src/js_module_info/module_resolver.rs index b3b6ddcff10c..15ec9bb06ab6 100644 --- a/crates/biome_module_graph/src/js_module_info/module_resolver.rs +++ b/crates/biome_module_graph/src/js_module_info/module_resolver.rs @@ -13,10 +13,10 @@ use rustc_hash::{FxHashMap, FxHashSet}; use crate::{ JsExport, JsImportPath, JsOwnExport, ModuleGraph, - js_module_info::{JsModuleInfoInner, scope::TsBindingReference}, + js_module_info::{JsModuleInfoInner, scope::TsBindingReference, utils::reached_too_many_types}, }; -use super::JsModuleInfo; +use super::{JsModuleInfo, JsModuleInfoDiagnostic}; const MAX_IMPORT_DEPTH: usize = 10; // Arbitrary depth, may require tweaking. @@ -67,6 +67,9 @@ pub struct ModuleResolver { /// Maps from `TypeId` indices in module 0 to indices in our own `types` /// store. type_id_map: Vec, + + /// Diagnostics emitted during the resolution of types + diagnostics: Vec, } impl ModuleResolver { @@ -80,6 +83,7 @@ impl ModuleResolver { expressions: Default::default(), types: TypeStore::with_capacity(num_initial_types), type_id_map: Default::default(), + diagnostics: Default::default(), }; resolver.run_inference(); @@ -214,6 +218,11 @@ impl ModuleResolver { let mut i = 0; while i < self.types.len() { + if let Err(diagnostic) = reached_too_many_types(i) { + self.diagnostics.push(diagnostic); + return; + } + if let Some(ty) = self.types.get(i).flattened(self) { self.types.replace(i, ty); did_flatten = true; diff --git a/crates/biome_module_graph/src/js_module_info/utils.rs b/crates/biome_module_graph/src/js_module_info/utils.rs new file mode 100644 index 000000000000..918880902ac1 --- /dev/null +++ b/crates/biome_module_graph/src/js_module_info/utils.rs @@ -0,0 +1,13 @@ +use crate::js_module_info::JsModuleInfoDiagnostic; + +pub(crate) const MAX_NUM_TYPES: usize = 200_000; + +/// Checks the given `num_types` against a threshold and returns a diagnostic +/// if the threshold is exceeded. +pub(crate) fn reached_too_many_types(num_types: usize) -> Result<(), JsModuleInfoDiagnostic> { + if num_types < MAX_NUM_TYPES { + return Ok(()); + } + + Err(JsModuleInfoDiagnostic::exceeded_types_limit()) +} diff --git a/crates/biome_module_graph/src/lib.rs b/crates/biome_module_graph/src/lib.rs index 3420bf7ac7f8..490d44a07da3 100644 --- a/crates/biome_module_graph/src/lib.rs +++ b/crates/biome_module_graph/src/lib.rs @@ -1,5 +1,6 @@ #![deny(clippy::use_self)] +mod diagnostics; mod format_module_graph; mod js_module_info; mod module_graph; @@ -7,8 +8,9 @@ mod module_graph; pub use biome_js_type_info::ImportSymbol; pub use biome_resolver::ResolvedPath; +pub use diagnostics::ModuleDiagnostic; pub use js_module_info::{ - JsExport, JsImport, JsImportPath, JsImportPhase, JsModuleInfo, JsOwnExport, JsReexport, - ModuleResolver, SerializedJsModuleInfo, + JsExport, JsImport, JsImportPath, JsImportPhase, JsModuleInfo, JsModuleInfoDiagnostic, + JsOwnExport, JsReexport, ModuleResolver, SerializedJsModuleInfo, }; pub use module_graph::{ModuleDependencies, ModuleGraph, SUPPORTED_EXTENSIONS}; diff --git a/crates/biome_module_graph/src/module_graph.rs b/crates/biome_module_graph/src/module_graph.rs index 3593c1b081fe..c15005f1a8fd 100644 --- a/crates/biome_module_graph/src/module_graph.rs +++ b/crates/biome_module_graph/src/module_graph.rs @@ -20,7 +20,9 @@ use camino::{Utf8Path, Utf8PathBuf}; use papaya::{HashMap, HashMapRef, LocalGuard}; use rustc_hash::{FxBuildHasher, FxHashSet}; -use crate::{JsExport, JsModuleInfo, JsOwnExport, js_module_info::JsModuleVisitor}; +use crate::{ + JsExport, JsModuleInfo, JsOwnExport, ModuleDiagnostic, js_module_info::JsModuleVisitor, +}; pub(crate) use fs_proxy::ModuleGraphFsProxy; @@ -80,7 +82,7 @@ impl ModuleGraph { project_layout: &ProjectLayout, added_or_updated_paths: &[(&BiomePath, AnyJsRoot)], removed_paths: &[&BiomePath], - ) -> ModuleDependencies { + ) -> (ModuleDependencies, Vec) { // Make sure all directories are registered for the added/updated paths. let path_info = self.path_info.pin(); for (path, _) in added_or_updated_paths { @@ -100,6 +102,7 @@ impl ModuleGraph { let fs_proxy = ModuleGraphFsProxy::new(fs, self, project_layout); let mut dependencies = ModuleDependencies::default(); + let mut diagnostics = Vec::new(); // Traverse all the added and updated paths and insert their module // info. @@ -115,6 +118,10 @@ impl ModuleGraph { } } + for diagnostic in module_info.diagnostics() { + diagnostics.push(diagnostic.clone()); + } + modules.insert(path.to_path_buf(), module_info); } @@ -124,7 +131,7 @@ impl ModuleGraph { path_info.remove(removed_path.as_path()); } - dependencies + (dependencies, diagnostics) } pub fn get_or_insert_path_info( diff --git a/crates/biome_service/src/diagnostics.rs b/crates/biome_service/src/diagnostics.rs index a6822c471fb7..a4a97db6df82 100644 --- a/crates/biome_service/src/diagnostics.rs +++ b/crates/biome_service/src/diagnostics.rs @@ -698,6 +698,7 @@ mod test { use biome_diagnostics::{DiagnosticExt, Error, print_diagnostic_to_string}; use biome_formatter::FormatError; use biome_fs::BiomePath; + use biome_module_graph::{JsModuleInfoDiagnostic, ModuleDiagnostic}; use std::ffi::OsString; fn snap_diagnostic(test_name: &str, diagnostic: Error) { @@ -805,4 +806,11 @@ mod test { Error::from(WorkspaceError::non_utf8_path(OsString::from("path.js"))), ) } + + #[test] + fn module_diagnostic() { + let diagnostics = ModuleDiagnostic::JsInfo(JsModuleInfoDiagnostic::exceeded_types_limit()) + .with_file_path("example.js"); + snap_diagnostic("module_diagnostic", diagnostics); + } } diff --git a/crates/biome_service/src/scanner.rs b/crates/biome_service/src/scanner.rs index dafb455a4582..785916b68327 100644 --- a/crates/biome_service/src/scanner.rs +++ b/crates/biome_service/src/scanner.rs @@ -10,12 +10,6 @@ mod workspace_bridges; #[cfg(test)] mod test_utils; -use std::collections::BTreeSet; -use std::panic::catch_unwind; -use std::sync::{Mutex, RwLock}; -use std::time::Duration; -use std::{mem, thread}; - use biome_diagnostics::serde::Diagnostic; use biome_diagnostics::{Diagnostic as _, DiagnosticExt, Error, Severity}; use biome_fs::{BiomePath, PathInterner, PathKind, TraversalContext, TraversalScope}; @@ -25,6 +19,11 @@ use crossbeam::channel::{Receiver, Sender, unbounded}; use papaya::{HashMap, HashSet}; use rayon::Scope; use rustc_hash::FxHashSet; +use std::collections::BTreeSet; +use std::panic::catch_unwind; +use std::sync::{Mutex, RwLock}; +use std::time::Duration; +use std::{mem, thread}; use tracing::instrument; use crate::diagnostics::Panic; @@ -197,7 +196,7 @@ impl Scanner { workspace: &W, project_key: ProjectKey, path: &Utf8Path, - ) -> Result<(), WorkspaceError> { + ) -> Result, WorkspaceError> { let (scan_kind, watch) = self .projects .pin() @@ -217,17 +216,17 @@ impl Scanner { watch, }; - self.scan( + let result = self.scan( workspace, project_key, path, IndexTrigger::Update, scan_options, - ) - .map(|_| ()) + )?; // No need to notify after this scan, because the individual file // updates will already trigger notifications. + Ok(result.diagnostics) } pub fn index_dependencies( @@ -237,7 +236,7 @@ impl Scanner { project_path: &Utf8Path, dependencies: ModuleDependencies, trigger: IndexTrigger, - ) -> Result<(), WorkspaceError> { + ) -> Result, WorkspaceError> { let scan_kind = self .get_scan_kind_for_project(project_key) .ok_or_else(WorkspaceError::no_project)?; @@ -247,7 +246,7 @@ impl Scanner { let (diagnostics_sender, diagnostics_receiver) = unbounded(); let collector = DiagnosticsCollector::new(false); - thread::scope(|scope| { + let diagnostics = thread::scope(|scope| { let handler = thread::Builder::new() .name("biome::scanner".to_string()) .spawn_scoped(scope, || collector.run(diagnostics_receiver)) @@ -281,10 +280,10 @@ impl Scanner { drop(ctx); // Wait for the collector thread to finish. - handler.join().unwrap(); + handler.join().unwrap() }); - Ok(()) + Ok(diagnostics) } /// Updates the index of the file with the given `path`. @@ -642,7 +641,7 @@ impl DiagnosticsCollector { /// Checks whether the given `diagnostic` should be collected or not. fn should_collect(&self, diagnostic: &Diagnostic) -> bool { - diagnostic.severity() >= self.diagnostic_level + diagnostic.severity() >= self.diagnostic_level || diagnostic.tags().is_internal() } fn run(&self, receiver: Receiver) -> Vec { @@ -814,13 +813,20 @@ fn open_file( ctx.workspace .index_file(ctx.project_key, path.clone(), trigger) }) { - Ok(Ok(dependencies)) => dependencies, + Ok(Ok((dependencies, diagnostics))) => { + for diagnostic in diagnostics { + ctx.push_diagnostic(diagnostic.with_file_path(path.as_str())); + } + dependencies + } Ok(Err(err)) => { let mut error: Error = err.into(); - if !path.is_config() && error.severity() == Severity::Error { - error = error.with_severity(Severity::Warning); + if !path.is_config() && error.severity() >= Severity::Error { + error = error + .with_severity(Severity::Warning) + .with_file_path(path.as_str()); } - ctx.send_diagnostic(Diagnostic::new(error)); + ctx.push_diagnostic(error); Default::default() } diff --git a/crates/biome_service/src/scanner/test_utils.rs b/crates/biome_service/src/scanner/test_utils.rs index b2a9df1fed84..7ed0db4c7887 100644 --- a/crates/biome_service/src/scanner/test_utils.rs +++ b/crates/biome_service/src/scanner/test_utils.rs @@ -1,13 +1,13 @@ use std::sync::mpsc; +use crate::projects::ProjectKey; +use crate::{WatcherInstruction, WorkspaceError}; +use biome_diagnostics::serde::Diagnostic; use biome_fs::{BiomePath, FileSystem}; use camino::{Utf8Path, Utf8PathBuf}; use papaya::HashSet; use rustc_hash::{FxBuildHasher, FxHashSet}; -use crate::projects::ProjectKey; -use crate::{WatcherInstruction, WorkspaceError}; - use super::watcher::WatcherInstructionChannel; use super::{ScanKind, WorkspaceWatcherBridge}; @@ -101,20 +101,20 @@ impl WorkspaceWatcherBridge for MockWorkspaceWatcherBridge<'_> { &self, project_key: ProjectKey, path: impl Into, - ) -> Result<(), WorkspaceError> { + ) -> Result, WorkspaceError> { assert_eq!(project_key, self.project_key); self.indexed_files.pin().insert(path.into().into()); self.tx.send(()).expect("can send notification"); - Ok(()) + Ok(vec![]) } - fn index_folder(&self, path: &Utf8Path) -> Result<(), WorkspaceError> { + fn index_folder(&self, path: &Utf8Path) -> Result, WorkspaceError> { self.indexed_folders.pin().insert(path.to_path_buf()); self.tx.send(()).expect("can send notification"); - Ok(()) + Ok(vec![]) } fn insert_watched_folder(&self, path: Utf8PathBuf) -> bool { @@ -128,19 +128,19 @@ impl WorkspaceWatcherBridge for MockWorkspaceWatcherBridge<'_> { self.tx.send(()).expect("can send notification"); } - fn unload_file(&self, path: &Utf8Path) -> Result<(), WorkspaceError> { + fn unload_file(&self, path: &Utf8Path) -> Result, WorkspaceError> { self.indexed_files.pin().remove(path); self.tx.send(()).expect("can send notification"); - Ok(()) + Ok(vec![]) } - fn unload_path(&self, path: &Utf8Path) -> Result<(), WorkspaceError> { + fn unload_path(&self, path: &Utf8Path) -> Result, WorkspaceError> { self.indexed_files.pin().remove(path); self.indexed_folders.pin().remove(path); self.tx.send(()).expect("can send notification"); - Ok(()) + Ok(vec![]) } fn notify_stopped(&self) {} diff --git a/crates/biome_service/src/scanner/watcher.rs b/crates/biome_service/src/scanner/watcher.rs index e2a681b6c1b7..44cbda95e05b 100644 --- a/crates/biome_service/src/scanner/watcher.rs +++ b/crates/biome_service/src/scanner/watcher.rs @@ -1,5 +1,11 @@ use std::path::PathBuf; +use super::WorkspaceWatcherBridge; +use crate::WorkspaceError; +use crate::diagnostics::WatchError; +use crate::workspace::ScanKind; +use biome_diagnostics::PrintDescription; +use biome_diagnostics::serde::Diagnostic; use camino::{Utf8Path, Utf8PathBuf}; use crossbeam::channel::{Receiver, Sender, bounded, unbounded}; use notify::event::{CreateKind, ModifyKind, RemoveKind, RenameMode}; @@ -8,13 +14,7 @@ use notify::{ Error as NotifyError, Event as NotifyEvent, EventKind, RecursiveMode, Result as NotifyResult, Watcher as NotifyWatcher, }; -use tracing::{debug, warn}; - -use crate::WorkspaceError; -use crate::diagnostics::WatchError; -use crate::workspace::ScanKind; - -use super::WorkspaceWatcherBridge; +use tracing::{debug, error, warn}; /// Instructions to let the watcher either watch or unwatch a given folder. #[derive(Debug, Eq, PartialEq)] @@ -113,7 +113,13 @@ impl Watcher { loop { crossbeam::channel::select! { recv(self.notify_rx) -> event => match event { - Ok(Ok(event)) => Self::handle_notify_event(workspace, event), + Ok(Ok(event)) => { + // TODO: Improve error propagation. + let diagnostics = Self::handle_notify_event(workspace, event); + for diagnostic in diagnostics { + error!("{}", PrintDescription(&diagnostic)); + } + } Ok(Err(error)) => { // TODO: Improve error propagation. warn!("Watcher error: {error}"); @@ -147,14 +153,17 @@ impl Watcher { } #[tracing::instrument(level = "trace", skip(workspace))] - fn handle_notify_event(workspace: &impl WorkspaceWatcherBridge, event: NotifyEvent) { + fn handle_notify_event( + workspace: &impl WorkspaceWatcherBridge, + event: NotifyEvent, + ) -> Vec { let paths = Self::watched_paths(workspace, event.paths); if paths.is_empty() { - return; + return vec![]; }; let result = match event.kind { - EventKind::Access(_) => Ok(()), + EventKind::Access(_) => Ok(vec![]), EventKind::Create(create_kind) => match create_kind { CreateKind::Folder => Self::index_folders(workspace, paths), _ => Self::index_paths(workspace, paths), @@ -177,25 +186,26 @@ impl Watcher { Self::unload_paths(workspace, paths) } } - _ => Ok(()), + _ => Ok(vec![]), }, // `RenameMode::Any` and `ModifyKind::Any` need to be included as a catch-all. // Without it, we'll miss events on Windows or macOS. ModifyKind::Data(_) | ModifyKind::Name(RenameMode::Any) | ModifyKind::Any => { Self::index_paths(workspace, paths) } - _ => Ok(()), + _ => Ok(vec![]), }, EventKind::Remove(remove_kind) => match remove_kind { RemoveKind::File => Self::unload_files(workspace, paths), _ => Self::unload_paths(workspace, paths), }, - EventKind::Any | EventKind::Other => Ok(()), + EventKind::Any | EventKind::Other => Ok(vec![]), }; - if let Err(error) = result { - // TODO: Improve error propagation. + result.unwrap_or_else(|error| { + // TODO: improve error propagation warn!("Error processing watch event: {error}"); - } + vec![] + }) } /// Filters the paths to make sure only paths within watched folders remain. @@ -222,12 +232,14 @@ impl Watcher { fn index_folders( workspace: &impl WorkspaceWatcherBridge, paths: Vec, - ) -> Result<(), WorkspaceError> { + ) -> Result, WorkspaceError> { + let mut diagnostics = vec![]; for path in paths { - workspace.index_folder(&path)?; + let result = workspace.index_folder(&path)?; + diagnostics.extend(result); } - Ok(()) + Ok(diagnostics) } /// Indexes open one or more files or folders. @@ -237,12 +249,14 @@ impl Watcher { fn index_paths( workspace: &impl WorkspaceWatcherBridge, paths: Vec, - ) -> Result<(), WorkspaceError> { + ) -> Result, WorkspaceError> { + let mut diagnostics = vec![]; for path in paths { - Self::index_path(workspace, &path)?; + let result = Self::index_path(workspace, &path)?; + diagnostics.extend(result); } - Ok(()) + Ok(diagnostics) } /// Indexes an individual file or folder. @@ -253,12 +267,12 @@ impl Watcher { fn index_path( workspace: &impl WorkspaceWatcherBridge, path: &Utf8Path, - ) -> Result<(), WorkspaceError> { + ) -> Result, WorkspaceError> { if workspace.fs().path_is_dir(path) { workspace.index_folder(path) } else { let Some(project_key) = workspace.find_project_for_path(path) else { - return Ok(()); // file events outside our projects can be safely ignored. + return Ok(vec![]); // file events outside our projects can be safely ignored. }; workspace.index_file(project_key, path) @@ -269,12 +283,13 @@ impl Watcher { fn unload_files( workspace: &impl WorkspaceWatcherBridge, paths: Vec, - ) -> Result<(), WorkspaceError> { + ) -> Result, WorkspaceError> { + let mut diagnostics = vec![]; for path in paths { - workspace.unload_file(&path)?; + diagnostics.extend(workspace.unload_file(&path)?); } - Ok(()) + Ok(diagnostics) } /// Unloads the given `paths` from the workspace index. @@ -284,21 +299,29 @@ impl Watcher { fn unload_paths( workspace: &impl WorkspaceWatcherBridge, paths: Vec, - ) -> Result<(), WorkspaceError> { + ) -> Result, WorkspaceError> { + let mut diagnostics = vec![]; for path in &paths { - workspace.unload_path(path)?; + let result = workspace.unload_path(path)?; + diagnostics.extend(result); } - Ok(()) + Ok(diagnostics) } fn rename_path( workspace: &impl WorkspaceWatcherBridge, from: &Utf8Path, to: &Utf8Path, - ) -> Result<(), WorkspaceError> { - workspace.unload_path(from)?; - Self::index_path(workspace, to) + ) -> Result, WorkspaceError> { + let mut diagnostics = vec![]; + if workspace.fs().path_is_file(from) { + diagnostics.extend(workspace.unload_file(from)?); + } else { + diagnostics.extend(workspace.unload_path(from)?); + } + diagnostics.extend(Self::index_path(workspace, to)?); + Ok(diagnostics) } /// Reindexes an individual file if the watcher (still) has interest in it. diff --git a/crates/biome_service/src/scanner/watcher.tests.rs b/crates/biome_service/src/scanner/watcher.tests.rs index 343d4e91a003..ecaf2ce0bbb1 100644 --- a/crates/biome_service/src/scanner/watcher.tests.rs +++ b/crates/biome_service/src/scanner/watcher.tests.rs @@ -146,7 +146,7 @@ fn should_index_on_create_and_unload_on_delete() { .recv_timeout(Duration::from_secs(1)) .expect("watcher should've sent notification"); - sleep(Duration::from_millis(10)); // Give the hash set a moment to clean up. + sleep(Duration::from_millis(30)); // Give the hash set a moment to clean up. assert!( mock_bridge.indexed_files.is_empty(), diff --git a/crates/biome_service/src/scanner/workspace_bridges.rs b/crates/biome_service/src/scanner/workspace_bridges.rs index d2bb629fb679..7e83dc3d483c 100644 --- a/crates/biome_service/src/scanner/workspace_bridges.rs +++ b/crates/biome_service/src/scanner/workspace_bridges.rs @@ -1,7 +1,7 @@ use std::panic::RefUnwindSafe; use biome_fs::FileSystem; -use biome_module_graph::ModuleDependencies; +use biome_module_graph::{ModuleDependencies, ModuleDiagnostic}; use camino::Utf8Path; use crate::{WorkspaceError, projects::ProjectKey, workspace::ServiceNotification}; @@ -45,7 +45,7 @@ pub(crate) trait WorkspaceScannerBridge: Send + Sync + RefUnwindSafe { project_key: ProjectKey, path: impl Into, trigger: IndexTrigger, - ) -> Result; + ) -> Result<(ModuleDependencies, Vec), WorkspaceError>; /// Informs the workspace of the list of nested config files. /// @@ -86,7 +86,7 @@ pub(crate) trait WorkspaceScannerBridge: Send + Sync + RefUnwindSafe { /// Unloads the index of the file with the given `path` within the /// workspace. - fn unload_file(&self, path: &Utf8Path) -> Result<(), WorkspaceError>; + fn unload_file(&self, path: &Utf8Path) -> Result, WorkspaceError>; /// Unloads the given `path` from the workspace index. /// @@ -97,7 +97,7 @@ pub(crate) trait WorkspaceScannerBridge: Send + Sync + RefUnwindSafe { /// /// If you already know the path is a file, you should use /// [`WorkspaceWatcherBridge::unload_file()`] directly instead. - fn unload_path(&self, path: &Utf8Path) -> Result<(), WorkspaceError>; + fn unload_path(&self, path: &Utf8Path) -> Result, WorkspaceError>; } /// Trait used to give access to workspace functionality required by the @@ -133,7 +133,7 @@ pub(crate) trait WorkspaceWatcherBridge { &self, project_key: ProjectKey, path: impl Into, - ) -> Result<(), WorkspaceError>; + ) -> Result, WorkspaceError>; /// Scans and watches the folder with the given `path` within the project /// with the given `project_key` in the workspace, while indexing all the @@ -141,7 +141,7 @@ pub(crate) trait WorkspaceWatcherBridge { /// /// If relevant to the given `scan_kind`, dependencies of the files in the /// folder are indexed as well. - fn index_folder(&self, path: &Utf8Path) -> Result<(), WorkspaceError>; + fn index_folder(&self, path: &Utf8Path) -> Result, WorkspaceError>; /// Informs the workspace the given folder is watched. /// @@ -157,7 +157,7 @@ pub(crate) trait WorkspaceWatcherBridge { /// Unloads the index of the file with the given `path` within the /// workspace. - fn unload_file(&self, path: &Utf8Path) -> Result<(), WorkspaceError>; + fn unload_file(&self, path: &Utf8Path) -> Result, WorkspaceError>; /// Unloads the given `path` from the workspace index. /// @@ -168,7 +168,7 @@ pub(crate) trait WorkspaceWatcherBridge { /// /// If you already know the path is a file, you should use /// [`WorkspaceWatcherBridge::unload_file()`] directly instead. - fn unload_path(&self, path: &Utf8Path) -> Result<(), WorkspaceError>; + fn unload_path(&self, path: &Utf8Path) -> Result, WorkspaceError>; /// Notifies service notification listeners that the watcher has stopped. fn notify_stopped(&self); @@ -239,16 +239,21 @@ where &self, project_key: ProjectKey, path: impl Into, - ) -> Result<(), WorkspaceError> { + ) -> Result, WorkspaceError> { self.workspace .index_file(project_key, path, IndexTrigger::Update) - .map(|_| ()) + .map(|(_, diagnostics)| { + diagnostics + .into_iter() + .map(biome_diagnostics::serde::Diagnostic::new) + .collect::>() + }) } #[inline] - fn index_folder(&self, path: &Utf8Path) -> Result<(), WorkspaceError> { + fn index_folder(&self, path: &Utf8Path) -> Result, WorkspaceError> { let Some(project_key) = self.find_project_for_path(path) else { - return Ok(()); // file events outside our projects can be safely ignored. + return Ok(vec![]); // file events outside our projects can be safely ignored. }; self.scanner.index_folder(self.workspace, project_key, path) @@ -265,12 +270,12 @@ where } #[inline] - fn unload_file(&self, path: &Utf8Path) -> Result<(), WorkspaceError> { + fn unload_file(&self, path: &Utf8Path) -> Result, WorkspaceError> { self.workspace.unload_file(path) } #[inline] - fn unload_path(&self, path: &Utf8Path) -> Result<(), WorkspaceError> { + fn unload_path(&self, path: &Utf8Path) -> Result, WorkspaceError> { self.workspace.unload_path(path) } diff --git a/crates/biome_service/src/snapshots/module_diagnostic.snap b/crates/biome_service/src/snapshots/module_diagnostic.snap new file mode 100644 index 000000000000..1daf617fe459 --- /dev/null +++ b/crates/biome_service/src/snapshots/module_diagnostic.snap @@ -0,0 +1,25 @@ +--- +source: crates/biome_service/src/diagnostics.rs +expression: content +--- +example.js project INTERNAL ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Biome encountered an unusually large amount of types which exceeded the limit of 200,000. + + Either you are analyzing very large files (did you make sure to exclude your build/ or dist/ folder?), or you've encountered a bug in Biome. + + Please follow these instructions to discover if you are accidentally analyzing large files and what to do about them in the relative guide. + + i In the meantime, you can ignore this file by adding its name or folder in the files.experimentalScannerIgnores option in your configuration file. + + i Refer to the documentation for more information. + + i If you think this is a bug, please report it and include the following information: + + - source code of the file; + - how the file is imported in the project (by a test file, a dependency, etc.); + - if and how the file/folder is excluded. + + ! Failing to provide this information won't allow the team to fix the issue. + + ! This diagnostic was derived from an internal Biome error. Potential bug, please report it if necessary. diff --git a/crates/biome_service/src/workspace.rs b/crates/biome_service/src/workspace.rs index 1dc6a8d1f1d2..9608866944a2 100644 --- a/crates/biome_service/src/workspace.rs +++ b/crates/biome_service/src/workspace.rs @@ -750,6 +750,12 @@ pub struct OpenFileParams { #[serde(default)] pub persist_node_cache: bool, } +#[derive(Debug, serde::Serialize, serde::Deserialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[serde(rename_all = "camelCase")] +pub struct OpenFileResult { + diagnostics: Vec, +} #[derive(Debug, serde::Serialize, serde::Deserialize)] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] @@ -873,6 +879,13 @@ pub struct ChangeFileParams { pub version: i32, } +#[derive(Debug, serde::Serialize, serde::Deserialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[serde(rename_all = "camelCase")] +pub struct ChangeFileResult { + diagnostics: Vec, +} + #[derive(Debug, serde::Serialize, serde::Deserialize)] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[serde(rename_all = "camelCase")] @@ -1365,7 +1378,7 @@ pub trait Workspace: Send + Sync + RefUnwindSafe { /// /// If the file path is under a folder that belongs to an opened project /// other than the current one, the current project is changed accordingly. - fn open_file(&self, params: OpenFileParams) -> Result<(), WorkspaceError>; + fn open_file(&self, params: OpenFileParams) -> Result; /// Checks if `file_path` exists in the workspace. /// @@ -1405,7 +1418,7 @@ pub trait Workspace: Send + Sync + RefUnwindSafe { ) -> Result; /// Changes the content of an open file. - fn change_file(&self, params: ChangeFileParams) -> Result<(), WorkspaceError>; + fn change_file(&self, params: ChangeFileParams) -> Result; /// Retrieves the list of diagnostics associated with a file. fn pull_diagnostics( @@ -1600,7 +1613,11 @@ impl<'app, W: Workspace + ?Sized> FileGuard<'app, W> { }) } - pub fn change_file(&self, version: i32, content: String) -> Result<(), WorkspaceError> { + pub fn change_file( + &self, + version: i32, + content: String, + ) -> Result { self.workspace.change_file(ChangeFileParams { project_key: self.project_key, path: self.path.clone(), diff --git a/crates/biome_service/src/workspace/client.rs b/crates/biome_service/src/workspace/client.rs index f40aef2145d5..3bebd3eb7f00 100644 --- a/crates/biome_service/src/workspace/client.rs +++ b/crates/biome_service/src/workspace/client.rs @@ -1,11 +1,12 @@ use super::{ - ChangeFileParams, CloseFileParams, FileExitsParams, FixFileParams, FixFileResult, - FormatFileParams, FormatOnTypeParams, FormatRangeParams, GetControlFlowGraphParams, - GetFormatterIRParams, GetModuleGraphParams, GetModuleGraphResult, GetSemanticModelParams, - GetSyntaxTreeParams, GetSyntaxTreeResult, OpenFileParams, PullActionsParams, PullActionsResult, - PullDiagnosticsParams, PullDiagnosticsResult, RenameParams, RenameResult, ScanProjectParams, - ScanProjectResult, SearchPatternParams, SearchResults, SupportsFeatureParams, - UpdateModuleGraphParams, UpdateSettingsParams, UpdateSettingsResult, + ChangeFileParams, ChangeFileResult, CloseFileParams, FileExitsParams, FixFileParams, + FixFileResult, FormatFileParams, FormatOnTypeParams, FormatRangeParams, + GetControlFlowGraphParams, GetFormatterIRParams, GetModuleGraphParams, GetModuleGraphResult, + GetSemanticModelParams, GetSyntaxTreeParams, GetSyntaxTreeResult, OpenFileParams, + OpenFileResult, PullActionsParams, PullActionsResult, PullDiagnosticsParams, + PullDiagnosticsResult, RenameParams, RenameResult, ScanProjectParams, ScanProjectResult, + SearchPatternParams, SearchResults, SupportsFeatureParams, UpdateModuleGraphParams, + UpdateSettingsParams, UpdateSettingsResult, }; use crate::workspace::{ CheckFileSizeParams, CheckFileSizeResult, CloseProjectParams, FileFeaturesResult, @@ -126,7 +127,7 @@ where self.request("biome/close_project", params) } - fn open_file(&self, params: OpenFileParams) -> Result<(), WorkspaceError> { + fn open_file(&self, params: OpenFileParams) -> Result { self.request("biome/open_file", params) } @@ -189,7 +190,7 @@ where self.request("biome/check_file_size", params) } - fn change_file(&self, params: ChangeFileParams) -> Result<(), WorkspaceError> { + fn change_file(&self, params: ChangeFileParams) -> Result { self.request("biome/change_file", params) } diff --git a/crates/biome_service/src/workspace/server.rs b/crates/biome_service/src/workspace/server.rs index 788869f3d1b4..66bd3e8eccda 100644 --- a/crates/biome_service/src/workspace/server.rs +++ b/crates/biome_service/src/workspace/server.rs @@ -20,7 +20,7 @@ use biome_grit_patterns::{CompilePatternOptions, GritQuery, compile_pattern_with use biome_js_syntax::{AnyJsRoot, ModuleKind}; use biome_json_parser::JsonParserOptions; use biome_json_syntax::JsonFileSource; -use biome_module_graph::{ModuleDependencies, ModuleGraph}; +use biome_module_graph::{ModuleDependencies, ModuleDiagnostic, ModuleGraph}; use biome_package::PackageType; use biome_parser::AnyParse; use biome_plugin_loader::{BiomePlugin, PluginCache, PluginDiagnostic}; @@ -416,9 +416,13 @@ impl WorkspaceServer { // Manifest files need to update the module graph if is_indexed && let Some(root) = syntax.and_then(Result::ok).map(AnyParse::into_root) { - let dependencies = + let (dependencies, diagnostics) = self.update_service_data(&path, UpdateKind::AddedOrChanged(reason, root))?; - Ok(InternalOpenFileResult { dependencies }) + + Ok(InternalOpenFileResult { + dependencies, + diagnostics, + }) } else { // If the document was never opened by the scanner, we don't care // about updating service data. @@ -705,7 +709,7 @@ impl WorkspaceServer { &self, path: &BiomePath, update_kind: &UpdateKind, - ) -> ModuleDependencies { + ) -> (ModuleDependencies, Vec) { let (added_or_changed_paths, removed_paths) = match update_kind { UpdateKind::AddedOrChanged(_, root) => { let Some(root) = SendNode::into_node(root.clone()).and_then(AnyJsRoot::cast) else { @@ -734,13 +738,13 @@ impl WorkspaceServer { &self, path: &Utf8Path, update_kind: UpdateKind, - ) -> Result { + ) -> Result<(ModuleDependencies, Vec), WorkspaceError> { let path = BiomePath::from(path); if path.is_manifest() { self.update_project_layout(&path, &update_kind)?; } - let dependencies = self.update_module_graph_internal(&path, &update_kind); + let result = self.update_module_graph_internal(&path, &update_kind); match update_kind { UpdateKind::AddedOrChanged(OpenFileReason::Index(IndexTrigger::InitialScan), _) => { @@ -751,7 +755,7 @@ impl WorkspaceServer { } } - Ok(dependencies) + Ok(result) } } @@ -779,6 +783,7 @@ impl Workspace for WorkspaceServer { verbose, }: ScanProjectParams, ) -> Result { + let mut diagnostics = Vec::new(); if scan_kind.is_none() { let path = self .projects @@ -788,10 +793,16 @@ impl Workspace for WorkspaceServer { let manifest = path.join("package.json"); if self.fs.path_exists(&manifest) { let trigger = IndexTrigger::InitialScan; - self.index_file(project_key, manifest.clone(), trigger)?; + let (_, _diagnostics) = self.index_file(project_key, manifest.clone(), trigger)?; + diagnostics.extend( + _diagnostics + .into_iter() + .map(biome_diagnostics::serde::Diagnostic::new) + .collect::>(), + ); } return Ok(ScanProjectResult { - diagnostics: Vec::new(), + diagnostics, duration: Duration::from_millis(0), configuration_files: vec![], }); @@ -804,7 +815,13 @@ impl Workspace for WorkspaceServer { watch, }; - self.scanner.index_project(self, project_key, scan_options) + let mut result = self + .scanner + .index_project(self, project_key, scan_options)?; + + result.diagnostics.extend(diagnostics); + + Ok(result) } /// Updates the global settings for this workspace. @@ -947,9 +964,17 @@ impl Workspace for WorkspaceServer { Ok(()) } - fn open_file(&self, params: OpenFileParams) -> Result<(), WorkspaceError> { - self.open_file_internal(OpenFileReason::ClientRequest, params) - .map(|_| ()) + fn open_file(&self, params: OpenFileParams) -> Result { + let diagnostics = self + .open_file_internal(OpenFileReason::ClientRequest, params) + .map(|result| { + result + .diagnostics + .into_iter() + .map(biome_diagnostics::serde::Diagnostic::new) + .collect::>() + })?; + Ok(OpenFileResult { diagnostics }) } fn file_exists(&self, params: FileExitsParams) -> Result { @@ -1117,7 +1142,7 @@ impl Workspace for WorkspaceServer { content, version, }: ChangeFileParams, - ) -> Result<(), WorkspaceError> { + ) -> Result { let documents = self.documents.pin(); let (index, existing_version) = documents .get(path.as_path()) @@ -1126,7 +1151,9 @@ impl Workspace for WorkspaceServer { if existing_version.is_some_and(|existing_version| existing_version >= version) { warn!(%version, %path, "outdated_file_change"); - return Ok(()); // Safely ignore older versions. + return Ok(ChangeFileResult { + diagnostics: vec![], + }); // Safely ignore older versions. } // We remove the node cache for the document, if it exists. @@ -1178,25 +1205,36 @@ impl Workspace for WorkspaceServer { .insert(path.clone().into(), document) .ok_or_else(WorkspaceError::not_found)?; + let mut final_diagnostics = vec![]; + if self.is_indexed(&path) { - let dependencies = self.update_service_data( + let (dependencies, diagnostics) = self.update_service_data( &path, UpdateKind::AddedOrChanged(OpenFileReason::ClientRequest, root), )?; + final_diagnostics.extend( + diagnostics + .into_iter() + .map(biome_diagnostics::serde::Diagnostic::new) + .collect::>(), + ); if !dependencies.is_empty() && let Some(project_path) = self.projects.get_project_path(project_key) { - let _ = self.scanner.index_dependencies( + let diagnostics = self.scanner.index_dependencies( self, project_key, &project_path, dependencies, IndexTrigger::Update, - ); + )?; + final_diagnostics.extend(diagnostics); } } - Ok(()) + Ok(ChangeFileResult { + diagnostics: final_diagnostics, + }) } /// Retrieves the list of diagnostics associated with a file @@ -1669,7 +1707,7 @@ impl WorkspaceScannerBridge for WorkspaceServer { project_key: ProjectKey, path: impl Into, trigger: IndexTrigger, - ) -> Result { + ) -> Result<(ModuleDependencies, Vec), WorkspaceError> { self.open_file_internal( OpenFileReason::Index(trigger), OpenFileParams { @@ -1680,7 +1718,7 @@ impl WorkspaceScannerBridge for WorkspaceServer { persist_node_cache: false, }, ) - .map(|result| result.dependencies) + .map(|result| (result.dependencies, result.diagnostics)) } fn update_project_config_files( @@ -1814,12 +1852,23 @@ impl WorkspaceScannerBridge for WorkspaceServer { let _ = self.notification_tx.send(notification); } - fn unload_file(&self, path: &Utf8Path) -> Result<(), WorkspaceError> { + fn unload_file( + &self, + path: &Utf8Path, + ) -> Result, WorkspaceError> { self.update_service_data(path, UpdateKind::Removed) - .map(|_| ()) + .map(|(_, diagnostics)| { + diagnostics + .into_iter() + .map(biome_diagnostics::serde::Diagnostic::new) + .collect() + }) } - fn unload_path(&self, path: &Utf8Path) -> Result<(), WorkspaceError> { + fn unload_path( + &self, + path: &Utf8Path, + ) -> Result, WorkspaceError> { // Note that we cannot check the kind of the path, because the watcher // would only attempt to unload a file or folder after it has been // removed. So asking the filesystem wouldn't work anymore. So we just @@ -1832,9 +1881,7 @@ impl WorkspaceScannerBridge for WorkspaceServer { self.project_layout.unload_folder(path); // Finally unloads the path itself. - self.unload_file(path)?; - - Ok(()) + self.unload_file(path) } } @@ -1842,6 +1889,9 @@ impl WorkspaceScannerBridge for WorkspaceServer { pub(super) struct InternalOpenFileResult { /// Dependencies we discovered of the opened file. pub dependencies: ModuleDependencies, + + /// + pub diagnostics: Vec, } /// Reports the reason why a file is being opened/indexed. diff --git a/crates/biome_wasm/src/lib.rs b/crates/biome_wasm/src/lib.rs index f9ac07eda4eb..dd32401ab732 100644 --- a/crates/biome_wasm/src/lib.rs +++ b/crates/biome_wasm/src/lib.rs @@ -125,10 +125,14 @@ impl Workspace { } #[wasm_bindgen(js_name = openFile)] - pub fn open_file(&self, params: IOpenFileParams) -> Result<(), Error> { + pub fn open_file(&self, params: IOpenFileParams) -> Result { let params: OpenFileParams = serde_wasm_bindgen::from_value(params.into()).map_err(into_error)?; - self.inner.open_file(params).map_err(into_error) + let result = self.inner.open_file(params).map_err(into_error)?; + + to_value(&result) + .map(IOpenFileResult::from) + .map_err(into_error) } #[wasm_bindgen(js_name = getFileContent)] @@ -192,10 +196,14 @@ impl Workspace { } #[wasm_bindgen(js_name = changeFile)] - pub fn change_file(&self, params: IChangeFileParams) -> Result<(), Error> { + pub fn change_file(&self, params: IChangeFileParams) -> Result { let params: ChangeFileParams = serde_wasm_bindgen::from_value(params.into()).map_err(into_error)?; - self.inner.change_file(params).map_err(into_error) + let result = self.inner.change_file(params).map_err(into_error)?; + + to_value(&result) + .map(IChangeFileResult::from) + .map_err(into_error) } #[wasm_bindgen(js_name = closeFile)] diff --git a/justfile b/justfile index 293b87bac5df..a41413e79917 100644 --- a/justfile +++ b/justfile @@ -28,8 +28,12 @@ gen-all: # Generates TypeScript types and JSON schema of the configuration gen-bindings: cargo codegen-schema + just gen-types + +gen-types: cargo run -p xtask_codegen --features schema -- bindings + # Generates code generated files for the linter gen-analyzer: just gen-rules diff --git a/packages/@biomejs/backend-jsonrpc/src/workspace.ts b/packages/@biomejs/backend-jsonrpc/src/workspace.ts index a54f20a54212..15bb9ec7a4ec 100644 --- a/packages/@biomejs/backend-jsonrpc/src/workspace.ts +++ b/packages/@biomejs/backend-jsonrpc/src/workspace.ts @@ -9202,12 +9202,18 @@ export type HtmlVariant = | "Svelte"; export type GritVariant = "Standard"; export type HtmlTextExpressions = "None" | "Single" | "Double"; +export interface OpenFileResult { + diagnostics: Diagnostic[]; +} export interface ChangeFileParams { content: string; path: BiomePath; projectKey: ProjectKey; version: number; } +export interface ChangeFileResult { + diagnostics: Diagnostic[]; +} export interface CloseFileParams { path: BiomePath; projectKey: ProjectKey; @@ -9522,8 +9528,8 @@ export interface Workspace { updateSettings(params: UpdateSettingsParams): Promise; openProject(params: OpenProjectParams): Promise; scanProject(params: ScanProjectParams): Promise; - openFile(params: OpenFileParams): Promise; - changeFile(params: ChangeFileParams): Promise; + openFile(params: OpenFileParams): Promise; + changeFile(params: ChangeFileParams): Promise; closeFile(params: CloseFileParams): Promise; fileExists(params: FileExitsParams): Promise; isPathIgnored(params: PathIsIgnoredParams): Promise;