From fbba29d8298d912d4a27395f30d273c0539fb530 Mon Sep 17 00:00:00 2001 From: yishai Date: Tue, 13 Jun 2023 12:50:15 +0300 Subject: [PATCH 001/756] feat((TextChunck): add new class TextRange and moving most of TextChunck methods to it. --- sefaria/model/text.py | 438 ++++++++++++++++++++++++------------------ 1 file changed, 248 insertions(+), 190 deletions(-) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index b6ab4c2765..87aa556be9 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -1686,28 +1686,12 @@ def __call__(cls, *args, **kwargs): else: return super(TextFamilyDelegator, cls).__call__(*args, **kwargs) - -class TextChunk(AbstractTextRecord, metaclass=TextFamilyDelegator): - """ - A chunk of text corresponding to the provided :class:`Ref`, language, and optional version name. - If it is possible to get a more complete text by merging multiple versions, a merged result will be returned. - - :param oref: :class:`Ref` - :param lang: "he" or "en". "he" means all rtl languages and "en" means all ltr languages - :param vtitle: optional. Title of the version desired. - :param actual_lang: optional. if vtitle isn't specified, prefer to find a version with ISO language `actual_lang`. As opposed to `lang` which can only be "he" or "en", `actual_lang` can be any valid 2 letter ISO language code. - """ +class TextRange(AbstractTextRecord, metaclass=TextFamilyDelegator): text_attr = "text" - def __init__(self, oref, lang="en", vtitle=None, exclude_copyrighted=False, actual_lang=None, fallback_on_default_version=False): - """ - :param oref: - :type oref: Ref - :param lang: "he" or "en" - :param vtitle: - :return: - """ + def __init__(self, oref, vtitle=None, actual_lang=None, **kwargs): + #kwargs are only for supporting old TextChunck. should be removed after merging if isinstance(oref.index_node, JaggedArrayNode): self._oref = oref else: @@ -1716,54 +1700,41 @@ def __init__(self, oref, lang="en", vtitle=None, exclude_copyrighted=False, actu raise InputError("Can not get TextChunk at this level, please provide a more precise reference") self._oref = child_ref self._ref_depth = len(self._oref.sections) + self.actual_lang = actual_lang self._versions = [] self._version_ids = None - self._saveable = False # Can this TextChunk be saved? - - self.lang = lang + self._saveable = False self.is_merged = False self.sources = [] self.text = self._original_text = self.empty_text() self.vtitle = vtitle - self.full_version = None self.versionSource = None # handling of source is hacky + self._choose_version(actual_lang=actual_lang, **kwargs) #kactual_lang and wargs are only for supporting old TextChunck. should be removed after merging - if lang and vtitle and not fallback_on_default_version: + def _choose_version(self, **kwargs): #kwargs are only for supporting old TextChunck. should be removed after merging + if self.actual_lang and self.vtitle: self._saveable = True - v = Version().load({"title": self._oref.index.title, "language": lang, "versionTitle": vtitle}, self._oref.part_projection()) - if exclude_copyrighted and v.is_copyrighted(): - raise InputError("Can not provision copyrighted text. {} ({}/{})".format(oref.normal(), vtitle, lang)) + v = Version().load({"title": self._oref.index.title, "actualLanguage": self.actual_lang, "versionTitle": self.vtitle}, self._oref.part_projection()) if v: self._versions += [v] self.text = self._original_text = self.trim_text(v.content_node(self._oref.index_node)) - elif lang: - if actual_lang is not None: - self._choose_version_by_lang(oref, lang, exclude_copyrighted, actual_lang, prioritized_vtitle=vtitle) - else: - self._choose_version_by_lang(oref, lang, exclude_copyrighted, prioritized_vtitle=vtitle) + elif self.actual_lang: + self._choose_version_by_lang() else: raise Exception("TextChunk requires a language.") + if not self._versions: + version_title_message = f" and version title '{self.vtile}'" if self.vtile else '' + raise NoVersionFoundError(f"No text record found for '{self._oref.index.title}' in {self.actual_lang}{version_title_message}") - def _choose_version_by_lang(self, oref, lang: str, exclude_copyrighted: bool, actual_lang: str = None, prioritized_vtitle: str = None) -> None: - if prioritized_vtitle: - actual_lang = None - vset = VersionSet(self._oref.condition_query(lang, actual_lang), proj=self._oref.part_projection()) - if len(vset) == 0: - if VersionSet({"title": self._oref.index.title}).count() == 0: - raise NoVersionFoundError("No text record found for '{}'".format(self._oref.index.title)) - return + def _choose_version_by_lang(self) -> None: + vset = VersionSet(self._oref.condition_query(actual_lang=self.actual_lang), proj=self._oref.part_projection()) if len(vset) == 1: v = vset[0] - if exclude_copyrighted and v.is_copyrighted(): - raise InputError("Can not provision copyrighted text. {} ({}/{})".format(oref.normal(), v.versionTitle, v.language)) self._versions += [v] self.text = self.trim_text(v.content_node(self._oref.index_node)) - #todo: Should this instance, and the non-merge below, be made saveable? - else: # multiple versions available, merge - if exclude_copyrighted: - vset.remove(Version.is_copyrighted) - merged_text, sources = vset.merge(self._oref.index_node, prioritized_vtitle=prioritized_vtitle) #todo: For commentaries, this merges the whole chapter. It may show up as merged, even if our part is not merged. + elif len(vset) > 1: # multiple versions available, merge + merged_text, sources = vset.merge(self._oref.index_node) #todo: For commentaries, this merges the whole chapter. It may show up as merged, even if our part is not merged. self.text = self.trim_text(merged_text) if len(set(sources)) == 1: for v in vset: @@ -1776,16 +1747,13 @@ def _choose_version_by_lang(self, oref, lang: str, exclude_copyrighted: bool, ac self._versions = vset.array() def __str__(self): - args = "{}, {}".format(self._oref, self.lang) + args = f"{self._oref}, {self.actual_lang}" if self.vtitle: - args += ", {}".format(self.vtitle) + args += f", {self.vtitle}" return args def __repr__(self): # Wanted to use orig_tref, but repr can not include Unicode - args = "{}, {}".format(self._oref, self.lang) - if self.vtitle: - args += ", {}".format(self.vtitle) - return "{}({})".format(self.__class__.__name__, args) + return f"{self.__class__.__name__}({self.__str__()})" def version_ids(self): if self._version_ids is None: @@ -1806,55 +1774,6 @@ def ja(self, remove_html=False): else: return JaggedTextArray(self.text) - def save(self, force_save=False): - """ - For editing in place (i.e. self.text[3] = "Some text"), it is necessary to set force_save to True. This is - because by editing in place, both the self.text and the self._original_text fields will get changed, - causing the save to abort. - :param force_save: If set to True, will force a save even if no change was detected in the text. - :return: - """ - assert self._saveable, "Tried to save a read-only text: {}".format(self._oref.normal()) - assert not self._oref.is_range(), "Only non-range references can be saved: {}".format(self._oref.normal()) - #may support simple ranges in the future. - #self._oref.is_range() and self._oref.range_index() == len(self._oref.sections) - 1 - if not force_save: - if self.text == self._original_text: - logger.warning("Aborted save of {}. No change in text.".format(self._oref.normal())) - return False - - self._validate() - self._sanitize() - self._trim_ending_whitespace() - - if not self.version(): - self.full_version = Version( - { - "chapter": self._oref.index.nodes.create_skeleton(), - "versionTitle": self.vtitle, - "versionSource": self.versionSource, - "language": self.lang, - "title": self._oref.index.title - } - ) - else: - self.full_version = Version().load({"title": self._oref.index.title, "language": self.lang, "versionTitle": self.vtitle}) - assert self.full_version, "Failed to load Version record for {}, {}".format(self._oref.normal(), self.vtitle) - if self.versionSource: - self.full_version.versionSource = self.versionSource # hack - - content = self.full_version.sub_content(self._oref.index_node.version_address()) - self._pad(content) - self.full_version.sub_content(self._oref.index_node.version_address(), [i - 1 for i in self._oref.sections], self.text) - - self._check_available_text_pre_save() - - self.full_version.save() - self._oref.recalibrate_next_prev_refs(len(self.text)) - self._update_link_language_availability() - - return self - def _pad(self, content): """ Pads the passed content to the dimension of self._oref. @@ -1880,61 +1799,6 @@ def _pad(self, content): if pos < self._ref_depth - 2 and isinstance(parent_content[val - 1], str): parent_content[val - 1] = [parent_content[val - 1]] - def _check_available_text_pre_save(self): - """ - Stores the availability of this text in before a save is made, - so that we can know if segments have been added or deleted overall. - """ - self._available_text_pre_save = {} - langs_checked = [self.lang] # swtich to ["en", "he"] when global availability checks are needed - for lang in langs_checked: - try: - self._available_text_pre_save[lang] = self._oref.text(lang=lang).text - except NoVersionFoundError: - self._available_text_pre_save[lang] = [] - - def _check_available_segments_changed_post_save(self, lang=None): - """ - Returns a list of tuples containing a Ref and a boolean availability - for each Ref that was either made available or unavailble for `lang`. - If `lang` is None, returns changed availability across all langauges. - """ - if lang: - old_refs_available = self._text_to_ref_available(self._available_text_pre_save[self.lang]) - else: - # Looking for availability of in all langauges, merge results of Hebrew and English - old_en_refs_available = self._text_to_ref_available(self._available_text_pre_save["en"]) - old_he_refs_available = self._text_to_ref_available(self._available_text_pre_save["he"]) - zipped = list(itertools.zip_longest(old_en_refs_available, old_he_refs_available)) - old_refs_available = [] - for item in zipped: - en, he = item[0], item[1] - ref = en[0] if en else he[0] - old_refs_available.append((ref, (en and en[1] or he and he[1]))) - - new_refs_available = self._text_to_ref_available(self.text) - - changed = [] - zipped = list(itertools.zip_longest(old_refs_available, new_refs_available)) - for item in zipped: - old_text, new_text = item[0], item[1] - had_previously = old_text and old_text[1] - have_now = new_text and new_text[1] - - if not had_previously and have_now: - changed.append(new_text) - elif had_previously and not have_now: - # Current save is deleting a line of text, but it could still be - # available in a different version for this language. Check again. - if lang: - text_still_available = bool(old_text[0].text(lang=lang).text) - else: - text_still_available = bool(old_text[0].text("en").text) or bool(old_text[0].text("he").text) - if not text_still_available: - changed.append([old_text[0], False]) - - return changed - def _text_to_ref_available(self, text): """Converts a JaggedArray of text to flat list of (Ref, bool) if text is availble""" flat = JaggedArray(text).flatten_to_array_with_indices() @@ -1948,18 +1812,6 @@ def _text_to_ref_available(self, text): refs_available += [[ref, available]] return refs_available - def _update_link_language_availability(self): - """ - Check if current save has changed the overall availabilty of text for refs - in this language, pass refs to update revelant links if so. - """ - changed = self._check_available_segments_changed_post_save(lang=self.lang) - - if len(changed): - from . import link - for change in changed: - link.update_link_language_availabiliy(change[0], self.lang, change[1]) - def _validate(self): """ validate that depth/breadth of the TextChunk.text matches depth/breadth of the Ref @@ -1970,8 +1822,8 @@ def _validate(self): implied_depth = ref_depth + posted_depth if implied_depth != self._oref.index_node.depth: raise InputError( - "Text Structure Mismatch. The stored depth of {} is {}, but the text posted to {} implies a depth of {}." - .format(self._oref.index_node.full_title(), self._oref.index_node.depth, self._oref.normal(), implied_depth) + f"Text Structure Mismatch. The stored depth of {self._oref.index_node.full_title()} is {self._oref.index_node.depth}, \ + but the text posted to {self._oref.normal()} implies a depth of {implied_depth}." ) #validate that length of the array matches length of the ref @@ -1980,20 +1832,18 @@ def _validate(self): span_size = self._oref.span_size() if posted_depth == 0: #possible? raise InputError( - "Text Structure Mismatch. {} implies a length of {} sections, but the text posted is a string." - .format(self._oref.normal(), span_size) + f"Text Structure Mismatch. {self._oref.normal()} implies a length of {span_size} sections, but the text posted is a string." ) elif posted_depth == 1: #possible? raise InputError( - "Text Structure Mismatch. {} implies a length of {} sections, but the text posted is a simple list." - .format(self._oref.normal(), span_size) + f"Text Structure Mismatch. {self._oref.normal()} implies a length of {span_size} sections, but the text posted is a simple list." ) else: posted_length = len(self.text) if posted_length != span_size: raise InputError( - "Text Structure Mismatch. {} implies a length of {} sections, but the text posted has {} elements." - .format(self._oref.normal(), span_size, posted_length) + f"Text Structure Mismatch. {self._oref.normal()} implies a length of {span_size} sections, \ + but the text posted has {posted_length} elements." ) #todo: validate last section size if provided @@ -2001,23 +1851,21 @@ def _validate(self): range_length = self._oref.range_size() if posted_depth == 0: raise InputError( - "Text Structure Mismatch. {} implies a length of {}, but the text posted is a string." - .format(self._oref.normal(), range_length) + f"Text Structure Mismatch. {self._oref.normal()} implies a length of {range_length}, but the text posted is a string." ) elif posted_depth == 1: posted_length = len(self.text) if posted_length != range_length: raise InputError( - "Text Structure Mismatch. {} implies a length of {}, but the text posted has {} elements." - .format(self._oref.normal(), range_length, posted_length) + f"Text Structure Mismatch. {self._oref.normal()} implies a length of {range_length}, \ + but the text posted has {posted_length} elements." ) else: # this should never happen. The depth check should catch it. raise InputError( - "Text Structure Mismatch. {} implies an simple array of length {}, but the text posted has depth {}." - .format(self._oref.normal(), range_length, posted_depth) + f"Text Structure Mismatch. {self._oref.normal()} implies an simple array of length {range_length}, \ + but the text posted has depth {posted_depth}." ) - #maybe use JaggedArray.subarray()? def trim_text(self, txt): """ Trims a text loaded from Version record with self._oref.part_projection() to the specifications of self._oref @@ -2086,9 +1934,13 @@ def has_manually_wrapped_refs(self): # merged version return False + def get_language(self): + #method for getting a different lang attribue in old TextChunck and RangeText + #can be removed after merging and all uses can be replaced by self.acutal_lang + return self.actual_lang + def nonempty_segment_refs(self): """ - :return: list of segment refs with content in this TextChunk """ r = self._oref @@ -2100,7 +1952,7 @@ def nonempty_segment_refs(self): else: input_refs = [r] for temp_ref in input_refs: - temp_tc = temp_ref.text(lang=self.lang, vtitle=self.vtitle) + temp_tc = temp_ref.text(lang=self.get_language(), vtitle=self.vtitle) ja = temp_tc.ja() jarray = ja.mask().array() @@ -2129,9 +1981,9 @@ def find_string(self, regex_str, cleaner=lambda x: x, strict=True): text_list = [x for x in self.ja().flatten_to_array() if len(x) > 0] if len(text_list) != len(ref_list): if strict: - raise ValueError("The number of refs doesn't match the number of starting words. len(refs)={} len(inds)={}".format(len(ref_list),len(ind_list))) + raise ValueError(f"The number of refs doesn't match the number of starting words. len(refs)={len(ref_list)} len(inds)={len(text_list)}") else: - print("Warning: The number of refs doesn't match the number of starting words. len(refs)={} len(inds)={} {}".format(len(ref_list),len(ind_list),str(self._oref))) + print(f"Warning: The number of refs doesn't match the number of starting words. len(refs)={len(ref_list)} len(inds)={len(text_list)}") matches = [] for r, t in zip(ref_list, text_list): @@ -2164,9 +2016,9 @@ def text_index_map(self, tokenizer=lambda x: re.split(r'\s+', x), strict=True, r if len(ind_list) != len(ref_list): if strict: - raise ValueError("The number of refs doesn't match the number of starting words. len(refs)={} len(inds)={}".format(len(ref_list),len(ind_list))) + raise ValueError(f"The number of refs doesn't match the number of starting words. len(refs)={len(ref_list)} len(inds)={len(ind_list)}") else: - print("Warning: The number of refs doesn't match the number of starting words. len(refs)={} len(inds)={} {}".format(len(ref_list),len(ind_list),str(self._oref))) + print(f"Warning: The number of refs doesn't match the number of starting words. len(refs)={len(ref_list)} len(inds)={len(ind_list)}") if len(ind_list) > len(ref_list): ind_list = ind_list[:len(ref_list)] else: @@ -2178,6 +2030,212 @@ def text_index_map(self, tokenizer=lambda x: re.split(r'\s+', x), strict=True, r return ind_list, ref_list, total_len +class TextChunk(TextRange, metaclass=TextFamilyDelegator): + """ + A chunk of text corresponding to the provided :class:`Ref`, language, and optional version name. + If it is possible to get a more complete text by merging multiple versions, a merged result will be returned. + + :param oref: :class:`Ref` + :param lang: "he" or "en". "he" means all rtl languages and "en" means all ltr languages + :param vtitle: optional. Title of the version desired. + :param actual_lang: optional. if vtitle isn't specified, prefer to find a version with ISO language `actual_lang`. As opposed to `lang` which can only be "he" or "en", `actual_lang` can be any valid 2 letter ISO language code. + """ + + text_attr = "text" + + def __init__(self, oref, lang="en", vtitle=None, exclude_copyrighted=False, actual_lang=None, fallback_on_default_version=False): + """ + :param oref: + :type oref: Ref + :param lang: "he" or "en" + :param vtitle: + :return: + """ + + self.lang = lang + super(TextChunk, self).__init__(oref=oref, vtitle=vtitle, exclude_copyrighted=exclude_copyrighted, actual_lang=actual_lang, fallback_on_default_version=fallback_on_default_version) + + def _choose_version(self, exclude_copyrighted, actual_lang, fallback_on_default_version): + if self.lang and self.vtitle and not fallback_on_default_version: + self._saveable = True + v = Version().load({"title": self._oref.index.title, "language": self.lang, "versionTitle": self.vtitle}, self._oref.part_projection()) + if exclude_copyrighted and v.is_copyrighted(): + raise InputError(f"Can not provision copyrighted text. {self._oref.normal()} ({self.vtitle}/{self.lang})") + if v: + self._versions += [v] + self.text = self._original_text = self.trim_text(v.content_node(self._oref.index_node)) + elif self.lang: + if actual_lang is not None: + self._choose_version_by_lang(self.lang, exclude_copyrighted, actual_lang, prioritized_vtitle=self.vtitle) + else: + self._choose_version_by_lang(self.lang, exclude_copyrighted, prioritized_vtitle=self.vtitle) + else: + raise Exception("TextChunk requires a language.") + + def _choose_version_by_lang(self, lang: str, exclude_copyrighted: bool, actual_lang: str = None, prioritized_vtitle: str = None) -> None: + if prioritized_vtitle: + actual_lang = None + vset = VersionSet(self._oref.condition_query(lang, actual_lang), proj=self._oref.part_projection()) + if len(vset) == 0: + if VersionSet({"title": self._oref.index.title}).count() == 0: + raise NoVersionFoundError("No text record found for '{}'".format(self._oref.index.title)) + return + if len(vset) == 1: + v = vset[0] + if exclude_copyrighted and v.is_copyrighted(): + raise InputError("Can not provision copyrighted text. {} ({}/{})".format(oref.normal(), v.versionTitle, v.language)) + self._versions += [v] + self.text = self.trim_text(v.content_node(self._oref.index_node)) + #todo: Should this instance, and the non-merge below, be made saveable? + else: # multiple versions available, merge + if exclude_copyrighted: + vset.remove(Version.is_copyrighted) + merged_text, sources = vset.merge(self._oref.index_node, prioritized_vtitle=prioritized_vtitle) #todo: For commentaries, this merges the whole chapter. It may show up as merged, even if our part is not merged. + self.text = self.trim_text(merged_text) + if len(set(sources)) == 1: + for v in vset: + if v.versionTitle == sources[0]: + self._versions += [v] + break + else: + self.sources = sources + self.is_merged = True + self._versions = vset.array() + + def __str__(self): + args = "{}, {}".format(self._oref, self.lang) + if self.vtitle: + args += ", {}".format(self.vtitle) + return args + + def __repr__(self): # Wanted to use orig_tref, but repr can not include Unicode + args = "{}, {}".format(self._oref, self.lang) + if self.vtitle: + args += ", {}".format(self.vtitle) + return "{}({})".format(self.__class__.__name__, args) + + def save(self, force_save=False): + """ + For editing in place (i.e. self.text[3] = "Some text"), it is necessary to set force_save to True. This is + because by editing in place, both the self.text and the self._original_text fields will get changed, + causing the save to abort. + :param force_save: If set to True, will force a save even if no change was detected in the text. + :return: + """ + assert self._saveable, "Tried to save a read-only text: {}".format(self._oref.normal()) + assert not self._oref.is_range(), "Only non-range references can be saved: {}".format(self._oref.normal()) + #may support simple ranges in the future. + #self._oref.is_range() and self._oref.range_index() == len(self._oref.sections) - 1 + if not force_save: + if self.text == self._original_text: + logger.warning("Aborted save of {}. No change in text.".format(self._oref.normal())) + return False + + self._validate() + self._sanitize() + self._trim_ending_whitespace() + + if not self.version(): + self.full_version = Version( + { + "chapter": self._oref.index.nodes.create_skeleton(), + "versionTitle": self.vtitle, + "versionSource": self.versionSource, + "language": self.lang, + "title": self._oref.index.title + } + ) + else: + self.full_version = Version().load({"title": self._oref.index.title, "language": self.lang, "versionTitle": self.vtitle}) + assert self.full_version, "Failed to load Version record for {}, {}".format(self._oref.normal(), self.vtitle) + if self.versionSource: + self.full_version.versionSource = self.versionSource # hack + + content = self.full_version.sub_content(self._oref.index_node.version_address()) + self._pad(content) + self.full_version.sub_content(self._oref.index_node.version_address(), [i - 1 for i in self._oref.sections], self.text) + + self._check_available_text_pre_save() + + self.full_version.save() + self._oref.recalibrate_next_prev_refs(len(self.text)) + self._update_link_language_availability() + + return self + + def _check_available_text_pre_save(self): + """ + Stores the availability of this text in before a save is made, + so that we can know if segments have been added or deleted overall. + """ + self._available_text_pre_save = {} + langs_checked = [self.lang] # swtich to ["en", "he"] when global availability checks are needed + for lang in langs_checked: + try: + self._available_text_pre_save[lang] = self._oref.text(lang=lang).text + except NoVersionFoundError: + self._available_text_pre_save[lang] = [] + + def _check_available_segments_changed_post_save(self, lang=None): + """ + Returns a list of tuples containing a Ref and a boolean availability + for each Ref that was either made available or unavailble for `lang`. + If `lang` is None, returns changed availability across all langauges. + """ + if lang: + old_refs_available = self._text_to_ref_available(self._available_text_pre_save[self.lang]) + else: + # Looking for availability of in all langauges, merge results of Hebrew and English + old_en_refs_available = self._text_to_ref_available(self._available_text_pre_save["en"]) + old_he_refs_available = self._text_to_ref_available(self._available_text_pre_save["he"]) + zipped = list(itertools.zip_longest(old_en_refs_available, old_he_refs_available)) + old_refs_available = [] + for item in zipped: + en, he = item[0], item[1] + ref = en[0] if en else he[0] + old_refs_available.append((ref, (en and en[1] or he and he[1]))) + + new_refs_available = self._text_to_ref_available(self.text) + + changed = [] + zipped = list(itertools.zip_longest(old_refs_available, new_refs_available)) + for item in zipped: + old_text, new_text = item[0], item[1] + had_previously = old_text and old_text[1] + have_now = new_text and new_text[1] + + if not had_previously and have_now: + changed.append(new_text) + elif had_previously and not have_now: + # Current save is deleting a line of text, but it could still be + # available in a different version for this language. Check again. + if lang: + text_still_available = bool(old_text[0].text(lang=lang).text) + else: + text_still_available = bool(old_text[0].text("en").text) or bool(old_text[0].text("he").text) + if not text_still_available: + changed.append([old_text[0], False]) + + return changed + + def _update_link_language_availability(self): + """ + Check if current save has changed the overall availabilty of text for refs + in this language, pass refs to update revelant links if so. + """ + changed = self._check_available_segments_changed_post_save(lang=self.lang) + + if len(changed): + from . import link + for change in changed: + link.update_link_language_availabiliy(change[0], self.lang, change[1]) + + def get_language(self): + #method for getting a different lang attribue in old TextChunck and RangeText + #can be removed after merging and all uses can be replaced by self.acutal_lang + return self.lang + + class VirtualTextChunk(AbstractTextRecord): """ Delegated from TextChunk From 3207f2f53733bd6a6b5283916536ac31360f8e0e Mon Sep 17 00:00:00 2001 From: yishai Date: Tue, 13 Jun 2023 14:37:50 +0300 Subject: [PATCH 002/756] fix((TextChunck): change params order of TextRange. --- sefaria/model/text.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index 87aa556be9..c731bce5a3 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -1690,7 +1690,7 @@ class TextRange(AbstractTextRecord, metaclass=TextFamilyDelegator): text_attr = "text" - def __init__(self, oref, vtitle=None, actual_lang=None, **kwargs): + def __init__(self, oref, actual_lang=None, vtitle=None, **kwargs): #kwargs are only for supporting old TextChunck. should be removed after merging if isinstance(oref.index_node, JaggedArrayNode): self._oref = oref From 379122bfdd1e8d0aa65fa9c92bd1b378d9772619 Mon Sep 17 00:00:00 2001 From: yishai Date: Wed, 14 Jun 2023 12:40:33 +0300 Subject: [PATCH 003/756] chore((TextChunck): add TextRange to __init__. --- sefaria/model/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sefaria/model/__init__.py b/sefaria/model/__init__.py index afed915d2f..2764249067 100644 --- a/sefaria/model/__init__.py +++ b/sefaria/model/__init__.py @@ -18,7 +18,7 @@ from .history import History, HistorySet, log_add, log_delete, log_update, log_text from .schema import deserialize_tree, Term, TermSet, TermScheme, TermSchemeSet, TitledTreeNode, SchemaNode, \ ArrayMapNode, JaggedArrayNode, NumberedTitledTreeNode, NonUniqueTerm, NonUniqueTermSet -from .text import library, Index, IndexSet, Version, VersionSet, TextChunk, TextFamily, Ref, merge_texts +from .text import library, Index, IndexSet, Version, VersionSet, TextChunk, TextRange, TextFamily, Ref, merge_texts from .link import Link, LinkSet, get_link_counts, get_book_link_collection, get_book_category_linkset from .note import Note, NoteSet from .layer import Layer, LayerSet From f2a0651ab5afd64731e855ce00b0163de87a0cfc Mon Sep 17 00:00:00 2001 From: yishai Date: Wed, 14 Jun 2023 12:43:47 +0300 Subject: [PATCH 004/756] feat((TextChunck): new texts api. --- api/texts_api.py | 178 +++++++++++++++++++++++++++++++++++++++++++++++ api/views.py | 60 ++++++++++++++++ sefaria/urls.py | 2 + 3 files changed, 240 insertions(+) create mode 100644 api/texts_api.py create mode 100644 api/views.py diff --git a/api/texts_api.py b/api/texts_api.py new file mode 100644 index 0000000000..245ef0ebee --- /dev/null +++ b/api/texts_api.py @@ -0,0 +1,178 @@ +import django +django.setup() +from sefaria.model import * +from sefaria.datatype.jagged_array import JaggedArray + +class APITextsHandler(): + + def __init__(self, oref, versions_params): + self.versions_params = versions_params + self.oref = oref + self.required_versions = [] + self.handled_version_params = [] + self.all_versions = self.oref.versionset() + self.return_obj = {'versions': []} + + def append_required_versions(self, lang, vtitle=None): + if vtitle: + versions = [v for v in self.all_versions if v.actualLanguage == lang and v.versionTitle == vtitle] + else: + versions = [v for v in self.all_versions if v.actualLanguage == lang] + for version in versions: + if version not in self.required_versions: + self.required_versions.append(version) + + def handle_version_params(self, version_params): + if version_params in self.handled_version_params: + return + if '|' not in version_params: + lang, vtitle = version_params, '' + else: + lang, vtitle = version_params.split('|', 1) + vtitle = vtitle.replace('_', ' ') + if vtitle == 'all': + self.append_required_versions(lang) + else: + self.append_required_versions(lang, vtitle) + self.handled_version_params.append(version_params) + + def add_versions_to_obj(self): + for version in self.required_versions: + self.return_obj['versions'].append({ + "versionTitle": version.versionTitle, + "versionSource": version.versionSource, + "language": version.actualLanguage, #TODO - why do we need this? why in the server? + "languageCode": version.actualLanguage, + #TODO - do we need also direction? + "status": getattr(version, 'status', None), + "license": getattr(version, 'license', None), + "versionNotes": getattr(version, 'versionNotes', None), + "digitizedBySefaria": getattr(version, 'digitizedBySefaria', None), + "priority":getattr(version, 'priority', None), + "versionTitleInHebrew": getattr(version, 'versionTitleInHebrew', None), + "versionNotesInHebrew": getattr(version, 'versionNotesInHebrew', None), + "extendedNotes": getattr(version, 'extendedNotes', None), + "extendedNotesHebrew": getattr(version, 'extendedNotesHebrew', None), + "purchaseInformationImage": getattr(version, 'purchaseInformationImage', None), + "purchaseInformationURL": getattr(version, 'purchaseInformationURL', None), + "shortVersionTitle": getattr(version, 'shortVersionTitle', None), + "shortVersionTitleInHebrew": getattr(version, 'shortVersionTitleInHebrew', None), + "isBaseText": getattr(version, 'isBaseText', False), + "formatAsPoetry": getattr(version, 'formatAsPoetry', False), + "text": TextRange(self.oref, version.actualLanguage, version.versionTitle).text #TODO - current api returns text for sections. dowe want it? + }) + + def add_ref_data(self): + oref = self.oref + self.return_obj.update({ + 'ref': oref.normal(), + #TODO - why do we need all of those? + 'heRef': oref.he_normal(), + 'sections': oref.sections, + 'toSections': oref.toSections, + 'sectionRef': oref.section_ref().normal(), + 'heSectionRef': oref.section_ref().he_normal(), + 'firstAvailableSectionRef': oref.first_available_section_ref().normal(), + 'isSpanning': oref.is_spanning(), + 'spanningRefs': [r.normal() for r in oref.split_spanning_ref()], + 'next': oref.next_section_ref().normal(), #TODO - change names like 'next_section_ref'? the question is how much cilent code use this name + 'prev': oref.prev_section_ref().normal(), + 'title': oref.context_ref().normal(), + 'book': oref.book, #TODO - isnt it the same as title? + 'heTitle': oref.context_ref().he_normal(), + 'primary_category': oref.primary_category, + 'type': oref.primary_category, #same as primary category + }) + + def reduce_alts_to_ref(self): #TODO - copied from TextFamily. if we won't remove it, we should find some place for that + oref = self.oref + # Set up empty Array that mirrors text structure + alts_ja = JaggedArray() + for key, struct in oref.index.get_alt_structures().items(): + # Assuming these are in order, continue if it is before ours, break if we see one after + for n in struct.get_leaf_nodes(): + wholeRef = Ref(n.wholeRef).default_child_ref().as_ranged_segment_ref() + if wholeRef.ending_ref().precedes(oref): + continue + if wholeRef.starting_ref().follows(oref): + break + + # It's in our territory + wholeRefStart = wholeRef.starting_ref() + if oref.contains(wholeRefStart) and not wholeRefStart.contains(oref): + indxs = [k - 1 for k in wholeRefStart.in_terms_of(oref)] + val = {"en": [], "he": []} + try: + val = alts_ja.get_element(indxs) or val + except IndexError: + pass + val["en"] += [n.primary_title("en")] + val["he"] += [n.primary_title("he")] + val["whole"] = True + alts_ja.set_element(indxs, val) + + if getattr(n, "refs", None): + for i, r in enumerate(n.refs): + # hack to skip Rishon, skip empty refs + if i == 0 or not r: + continue + subRef = Ref(r) + subRefStart = subRef.starting_ref() + if oref.contains(subRefStart) and not subRefStart.contains(oref): + indxs = [k - 1 for k in subRefStart.in_terms_of(oref)] + val = {"en": [], "he": []} + try: + val = alts_ja.get_element(indxs) or val + except IndexError: + pass + val["en"] += [n.sectionString([i + 1], "en", title=False)] + val["he"] += [n.sectionString([i + 1], "he", title=False)] + alts_ja.set_element(indxs, val) + elif subRefStart.follows(oref): + break + + return alts_ja.array() + + def add_index_data(self): + index = self.oref.index + self.return_obj.update({ + 'indexTitle': index.title, + #TODO - those are attrs of index. it seems client can take them from there + 'categories': index.categories, + 'heIndexTitle': index.get_title('he'), + 'isComplex': index.is_complex(), + 'isDependant': index.is_dependant_text(), + 'order': getattr(index, 'order', ''), + # TODO - alts are reduced fron the index. I guess here we can't give the client to do the job but have to check + 'alts': self.reduce_alts_to_ref(), + }) + + def add_node_data(self): + inode = self.oref.index_node + # TODO - all those are attrs of node, so they are available also in the index. can the client take them from there? + if getattr(inode, "lengths", None): + self.return_obj["lengths"] = getattr(inode, "lengths") + if len(self.return_obj["lengths"]): + self.return_obj["length"] = self.return_obj["lengths"][0] + elif getattr(inode, "length", None): + self.return_obj["length"] = getattr(inode, "length") + self.return_obj.update({ + 'textDepth': getattr(inode, "depth", None), + 'sectionNames': getattr(inode, "sectionNames", None), + 'addressTypes': getattr(inode, "addressTypes", None), + 'heTitle': inode.full_title("he"), #TODO - is there use in this and the nexts? + 'titleVariants': inode.all_tree_titles("en"), + 'heTitleVariants': inode.all_tree_titles("he"), + # TODO - offsets are reduced fron the node. I guess here we can't give the client to do the job but have to check + # TODO - also this them for sections while this code for now returns the text according to segment refs + 'index_offsets_by_depth': inode.trim_index_offsets_by_sections(self.oref.sections, self.oref.toSections), + }) + + def get_versions_for_query(self): + for version_params in self.versions_params: + self.handle_version_params(version_params) + self.add_versions_to_obj() + self.add_ref_data() + self.add_index_data() + self.add_node_data() + return self.return_obj diff --git a/api/views.py b/api/views.py new file mode 100644 index 0000000000..4520ed72ff --- /dev/null +++ b/api/views.py @@ -0,0 +1,60 @@ +from django.http import Http404, QueryDict, HttpResponseBadRequest + +from sefaria.model import * +from sefaria.system.multiserver.coordinator import server_coordinator +from sefaria.settings import DISABLE_AUTOCOMPLETER, ENABLE_LINKER +from .texts_api import APITextsHandler +from sefaria.client.util import jsonResponse + +import structlog +logger = structlog.get_logger(__name__) + +#TODO - i've copied it from reader.views. i'm not sure what it does + +# # # +# Initialized cache library objects that depend on sefaria.model being completely loaded. +logger.info("Initializing library objects.") +logger.info("Initializing TOC Tree") +library.get_toc_tree() + +logger.info("Initializing Shared Cache") +library.init_shared_cache() + +if not DISABLE_AUTOCOMPLETER: + logger.info("Initializing Full Auto Completer") + library.build_full_auto_completer() + + logger.info("Initializing Ref Auto Completer") + library.build_ref_auto_completer() + + logger.info("Initializing Lexicon Auto Completers") + library.build_lexicon_auto_completers() + + logger.info("Initializing Cross Lexicon Auto Completer") + library.build_cross_lexicon_auto_completer() + + logger.info("Initializing Topic Auto Completer") + library.build_topic_auto_completer() + +if ENABLE_LINKER: + logger.info("Initializing Linker") + library.build_ref_resolver() + +if server_coordinator: + server_coordinator.connect() +# # # + +def get_texts(request, tref): + try: + oref = Ref.instantiate_ref_with_legacy_parse_fallback(tref) + except Exception as e: + return HttpResponseBadRequest(e) + cb = request.GET.get("callback", None) + if request.method == "GET": + versions_params = request.GET.getlist('version', []) + if not versions_params: + versions_params = ['source'] #TODO - or base? + handler = APITextsHandler(oref, versions_params) + data = handler.get_versions_for_query() + return jsonResponse(data, cb) + return jsonResponse({"error": "Unsupported HTTP method."}, cb) diff --git a/sefaria/urls.py b/sefaria/urls.py index 2232b6f16e..02741bc089 100644 --- a/sefaria/urls.py +++ b/sefaria/urls.py @@ -13,6 +13,7 @@ import sourcesheets.views as sheets_views import sefaria.gauth.views as gauth_views import django.contrib.auth.views as django_auth_views +import api.views as v3_views from sefaria.site.urls import site_urlpatterns @@ -146,6 +147,7 @@ url(r'^api/texts/modify-bulk/(?P.+)$', reader_views.modify_bulk_text_api), url(r'^api/texts/(?P<tref>.+)/(?P<lang>\w\w)/(?P<version>.+)$', reader_views.old_text_versions_api_redirect), url(r'^api/texts/(?P<tref>.+)$', reader_views.texts_api), + url(r'^api/v3/texts/(?P<tref>.+)$', v3_views.get_texts), url(r'^api/index/?$', reader_views.table_of_contents_api), url(r'^api/opensearch-suggestions/?$', reader_views.opensearch_suggestions_api), url(r'^api/index/titles/?$', reader_views.text_titles_api), From b735640c0c4dc0c8cd128c8aba88f5b786af94b2 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Wed, 14 Jun 2023 15:22:55 +0300 Subject: [PATCH 005/756] fix((texts api): get best priority for default. --- api/texts_api.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/api/texts_api.py b/api/texts_api.py index 245ef0ebee..e7391ca8ee 100644 --- a/api/texts_api.py +++ b/api/texts_api.py @@ -14,10 +14,12 @@ def __init__(self, oref, versions_params): self.return_obj = {'versions': []} def append_required_versions(self, lang, vtitle=None): - if vtitle: + if vtitle and vtitle != 'all': versions = [v for v in self.all_versions if v.actualLanguage == lang and v.versionTitle == vtitle] else: versions = [v for v in self.all_versions if v.actualLanguage == lang] + if vtitle != 'all': + versions = [min(versions, key=lambda v: getattr(v, 'proiority', 100))] for version in versions: if version not in self.required_versions: self.required_versions.append(version) From 0fbf3df7dde33f9b66fa1a318649cbf6df0fdaec Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Wed, 14 Jun 2023 15:37:02 +0300 Subject: [PATCH 006/756] feat(texts api): handle lang 'base' and 'source'. --- api/texts_api.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/api/texts_api.py b/api/texts_api.py index e7391ca8ee..b43cb9a00a 100644 --- a/api/texts_api.py +++ b/api/texts_api.py @@ -6,7 +6,7 @@ class APITextsHandler(): def __init__(self, oref, versions_params): - self.versions_params = versions_params + self.versions_params = versions_params if versions_params else 'base' self.oref = oref self.required_versions = [] self.handled_version_params = [] @@ -14,10 +14,16 @@ def __init__(self, oref, versions_params): self.return_obj = {'versions': []} def append_required_versions(self, lang, vtitle=None): + if lang == 'base': + lang_condition = lambda v: getattr(v, 'isBaseText', False) == True + elif lang == 'source': + lang_condition = lambda v: getattr(v, 'isSource', False) == True + else: + lang_condition = lambda v: v.actualLanguage == lang if vtitle and vtitle != 'all': - versions = [v for v in self.all_versions if v.actualLanguage == lang and v.versionTitle == vtitle] + versions = [v for v in self.all_versions if lang_condition(v) and v.versionTitle == vtitle] else: - versions = [v for v in self.all_versions if v.actualLanguage == lang] + versions = [v for v in self.all_versions if lang_condition(v)] if vtitle != 'all': versions = [min(versions, key=lambda v: getattr(v, 'proiority', 100))] for version in versions: @@ -32,10 +38,7 @@ def handle_version_params(self, version_params): else: lang, vtitle = version_params.split('|', 1) vtitle = vtitle.replace('_', ' ') - if vtitle == 'all': - self.append_required_versions(lang) - else: - self.append_required_versions(lang, vtitle) + self.append_required_versions(lang, vtitle) self.handled_version_params.append(version_params) def add_versions_to_obj(self): From 820ea095ff56e51ce9f79b42c0ac682c333119c5 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Wed, 14 Jun 2023 15:47:00 +0300 Subject: [PATCH 007/756] feat(texts api): default lang is base. --- api/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/views.py b/api/views.py index 4520ed72ff..fec45b21e1 100644 --- a/api/views.py +++ b/api/views.py @@ -53,7 +53,7 @@ def get_texts(request, tref): if request.method == "GET": versions_params = request.GET.getlist('version', []) if not versions_params: - versions_params = ['source'] #TODO - or base? + versions_params = ['base'] #TODO - or base? handler = APITextsHandler(oref, versions_params) data = handler.get_versions_for_query() return jsonResponse(data, cb) From 13da51b42a51a99cbdef39d6c878207d343fd802 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Wed, 14 Jun 2023 15:47:06 +0300 Subject: [PATCH 008/756] fix(texts api): dont search for best priority if there are no versions. --- api/texts_api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/texts_api.py b/api/texts_api.py index b43cb9a00a..dd5c2a03ec 100644 --- a/api/texts_api.py +++ b/api/texts_api.py @@ -6,7 +6,7 @@ class APITextsHandler(): def __init__(self, oref, versions_params): - self.versions_params = versions_params if versions_params else 'base' + self.versions_params = versions_params self.oref = oref self.required_versions = [] self.handled_version_params = [] @@ -24,7 +24,7 @@ def append_required_versions(self, lang, vtitle=None): versions = [v for v in self.all_versions if lang_condition(v) and v.versionTitle == vtitle] else: versions = [v for v in self.all_versions if lang_condition(v)] - if vtitle != 'all': + if vtitle != 'all' and versions: versions = [min(versions, key=lambda v: getattr(v, 'proiority', 100))] for version in versions: if version not in self.required_versions: From acaed171ac541c7928f9578ad67185a9f99e93d8 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Wed, 14 Jun 2023 15:47:55 +0300 Subject: [PATCH 009/756] chore(texts api): remove unused imports. --- api/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/views.py b/api/views.py index fec45b21e1..544c559b6e 100644 --- a/api/views.py +++ b/api/views.py @@ -1,4 +1,4 @@ -from django.http import Http404, QueryDict, HttpResponseBadRequest +from django.http import HttpResponseBadRequest from sefaria.model import * from sefaria.system.multiserver.coordinator import server_coordinator From 8324212b1f4d9f3248e136c52b554456e0ba59a3 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Wed, 14 Jun 2023 18:26:00 +0300 Subject: [PATCH 010/756] chore(texts api): remove equal to True. --- api/texts_api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/texts_api.py b/api/texts_api.py index dd5c2a03ec..7bfa2f3ce4 100644 --- a/api/texts_api.py +++ b/api/texts_api.py @@ -15,9 +15,9 @@ def __init__(self, oref, versions_params): def append_required_versions(self, lang, vtitle=None): if lang == 'base': - lang_condition = lambda v: getattr(v, 'isBaseText', False) == True + lang_condition = lambda v: getattr(v, 'isBaseText', False) elif lang == 'source': - lang_condition = lambda v: getattr(v, 'isSource', False) == True + lang_condition = lambda v: getattr(v, 'isSource', False) else: lang_condition = lambda v: v.actualLanguage == lang if vtitle and vtitle != 'all': From 9ecddceea0668bd6c19f44cf3a44971c408abf72 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Wed, 14 Jun 2023 19:11:16 +0300 Subject: [PATCH 011/756] chore(texts api): use version_list instead of versionSet - saves memory and more simple. --- api/texts_api.py | 49 +++++++++++++------------------------------ sefaria/model/text.py | 4 ++-- 2 files changed, 16 insertions(+), 37 deletions(-) diff --git a/api/texts_api.py b/api/texts_api.py index 7bfa2f3ce4..d0a7adbc21 100644 --- a/api/texts_api.py +++ b/api/texts_api.py @@ -8,27 +8,26 @@ class APITextsHandler(): def __init__(self, oref, versions_params): self.versions_params = versions_params self.oref = oref - self.required_versions = [] self.handled_version_params = [] - self.all_versions = self.oref.versionset() + self.all_versions = self.oref.version_list() self.return_obj = {'versions': []} def append_required_versions(self, lang, vtitle=None): if lang == 'base': - lang_condition = lambda v: getattr(v, 'isBaseText', False) + lang_condition = lambda v: v['isBaseText'] elif lang == 'source': - lang_condition = lambda v: getattr(v, 'isSource', False) + lang_condition = lambda v: v['isSource'] else: - lang_condition = lambda v: v.actualLanguage == lang + lang_condition = lambda v: v['actualLanguage'] == lang if vtitle and vtitle != 'all': - versions = [v for v in self.all_versions if lang_condition(v) and v.versionTitle == vtitle] + versions = [v for v in self.all_versions if lang_condition(v) and v['versionTitle'] == vtitle] else: versions = [v for v in self.all_versions if lang_condition(v)] if vtitle != 'all' and versions: - versions = [min(versions, key=lambda v: getattr(v, 'proiority', 100))] + versions = [max(versions, key=lambda v: int(v['proiority'] or 0))] for version in versions: - if version not in self.required_versions: - self.required_versions.append(version) + if version not in self.return_obj['versions']: + self.return_obj['versions'].append(version) def handle_version_params(self, version_params): if version_params in self.handled_version_params: @@ -41,31 +40,10 @@ def handle_version_params(self, version_params): self.append_required_versions(lang, vtitle) self.handled_version_params.append(version_params) - def add_versions_to_obj(self): - for version in self.required_versions: - self.return_obj['versions'].append({ - "versionTitle": version.versionTitle, - "versionSource": version.versionSource, - "language": version.actualLanguage, #TODO - why do we need this? why in the server? - "languageCode": version.actualLanguage, - #TODO - do we need also direction? - "status": getattr(version, 'status', None), - "license": getattr(version, 'license', None), - "versionNotes": getattr(version, 'versionNotes', None), - "digitizedBySefaria": getattr(version, 'digitizedBySefaria', None), - "priority":getattr(version, 'priority', None), - "versionTitleInHebrew": getattr(version, 'versionTitleInHebrew', None), - "versionNotesInHebrew": getattr(version, 'versionNotesInHebrew', None), - "extendedNotes": getattr(version, 'extendedNotes', None), - "extendedNotesHebrew": getattr(version, 'extendedNotesHebrew', None), - "purchaseInformationImage": getattr(version, 'purchaseInformationImage', None), - "purchaseInformationURL": getattr(version, 'purchaseInformationURL', None), - "shortVersionTitle": getattr(version, 'shortVersionTitle', None), - "shortVersionTitleInHebrew": getattr(version, 'shortVersionTitleInHebrew', None), - "isBaseText": getattr(version, 'isBaseText', False), - "formatAsPoetry": getattr(version, 'formatAsPoetry', False), - "text": TextRange(self.oref, version.actualLanguage, version.versionTitle).text #TODO - current api returns text for sections. dowe want it? - }) + def add_text_to_versions(self): + for version in self.return_obj['versions']: + version['text'] = TextRange(self.oref, version.actualLanguage, version.versionTitle).text + #TODO - current api returns text for sections. do we want it? def add_ref_data(self): oref = self.oref @@ -149,6 +127,7 @@ def add_index_data(self): 'isDependant': index.is_dependant_text(), 'order': getattr(index, 'order', ''), # TODO - alts are reduced fron the index. I guess here we can't give the client to do the job but have to check + # 'alts': self.reduce_alts_to_ref(), }) @@ -176,7 +155,7 @@ def add_node_data(self): def get_versions_for_query(self): for version_params in self.versions_params: self.handle_version_params(version_params) - self.add_versions_to_obj() + self.add_text_to_versions() self.add_ref_data() self.add_index_data() self.add_node_data() diff --git a/sefaria/model/text.py b/sefaria/model/text.py index c731bce5a3..57e6a16e03 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -4604,10 +4604,10 @@ def version_list(self): :return list: each list element is an object with keys 'versionTitle' and 'language' """ - fields = ["title", "versionTitle", "versionSource", "language", "status", "license", "versionNotes", + fields = ["title", "versionTitle", "versionSource", "language", 'actualLanguage', "status", "license", "versionNotes", "digitizedBySefaria", "priority", "versionTitleInHebrew", "versionNotesInHebrew", "extendedNotes", "extendedNotesHebrew", "purchaseInformationImage", "purchaseInformationURL", "shortVersionTitle", - "shortVersionTitleInHebrew", "isBaseText"] + "shortVersionTitleInHebrew", "isBaseText", 'isSource', 'formatAsPoetry'] versions = VersionSet(self.condition_query()) version_list = [] if self.is_book_level(): From 8195b9bad995d4e9f08d3432bed5971266257ac2 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Wed, 14 Jun 2023 19:13:14 +0300 Subject: [PATCH 012/756] chore(texts api): add todo. --- api/texts_api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/texts_api.py b/api/texts_api.py index d0a7adbc21..602f29f607 100644 --- a/api/texts_api.py +++ b/api/texts_api.py @@ -127,7 +127,7 @@ def add_index_data(self): 'isDependant': index.is_dependant_text(), 'order': getattr(index, 'order', ''), # TODO - alts are reduced fron the index. I guess here we can't give the client to do the job but have to check - # + #TODO - also this did them for sections while this code for now returns the text according to segment refs 'alts': self.reduce_alts_to_ref(), }) @@ -148,7 +148,7 @@ def add_node_data(self): 'titleVariants': inode.all_tree_titles("en"), 'heTitleVariants': inode.all_tree_titles("he"), # TODO - offsets are reduced fron the node. I guess here we can't give the client to do the job but have to check - # TODO - also this them for sections while this code for now returns the text according to segment refs + # TODO - also this did them for sections while this code for now returns the text according to segment refs 'index_offsets_by_depth': inode.trim_index_offsets_by_sections(self.oref.sections, self.oref.toSections), }) From 79f18d2cd68ae2c847c83e413457c0139ce536e2 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Wed, 14 Jun 2023 19:25:30 +0300 Subject: [PATCH 013/756] chore(texts api): remove redundant int. --- api/texts_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/texts_api.py b/api/texts_api.py index 602f29f607..e28d675f51 100644 --- a/api/texts_api.py +++ b/api/texts_api.py @@ -24,7 +24,7 @@ def append_required_versions(self, lang, vtitle=None): else: versions = [v for v in self.all_versions if lang_condition(v)] if vtitle != 'all' and versions: - versions = [max(versions, key=lambda v: int(v['proiority'] or 0))] + versions = [max(versions, key=lambda v: v['proiority'] or 0)] for version in versions: if version not in self.return_obj['versions']: self.return_obj['versions'].append(version) From cd7f89cb783ee78d1ec8b8654b04ba764538af4a Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Wed, 14 Jun 2023 20:54:27 +0300 Subject: [PATCH 014/756] fix(texts api): pop title from version. --- api/texts_api.py | 1 + 1 file changed, 1 insertion(+) diff --git a/api/texts_api.py b/api/texts_api.py index e28d675f51..0b9b1aa716 100644 --- a/api/texts_api.py +++ b/api/texts_api.py @@ -42,6 +42,7 @@ def handle_version_params(self, version_params): def add_text_to_versions(self): for version in self.return_obj['versions']: + version.pop('title') version['text'] = TextRange(self.oref, version.actualLanguage, version.versionTitle).text #TODO - current api returns text for sections. do we want it? From a50c6bba76cb5a8439ec79a6748c07e4d156cee9 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Wed, 14 Jun 2023 20:57:44 +0300 Subject: [PATCH 015/756] chore(texts api): remove todo. --- api/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/views.py b/api/views.py index 544c559b6e..43faca7a1e 100644 --- a/api/views.py +++ b/api/views.py @@ -53,7 +53,7 @@ def get_texts(request, tref): if request.method == "GET": versions_params = request.GET.getlist('version', []) if not versions_params: - versions_params = ['base'] #TODO - or base? + versions_params = ['base'] handler = APITextsHandler(oref, versions_params) data = handler.get_versions_for_query() return jsonResponse(data, cb) From 9fee80bdc32f1dd2444efd2d39f258d934112390 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Thu, 15 Jun 2023 08:53:10 +0300 Subject: [PATCH 016/756] fix(texts api): default lang en in TextRange. --- sefaria/model/text.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index 57e6a16e03..3dd253a6a8 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -1690,7 +1690,7 @@ class TextRange(AbstractTextRecord, metaclass=TextFamilyDelegator): text_attr = "text" - def __init__(self, oref, actual_lang=None, vtitle=None, **kwargs): + def __init__(self, oref, actual_lang='en', vtitle=None, **kwargs): #kwargs are only for supporting old TextChunck. should be removed after merging if isinstance(oref.index_node, JaggedArrayNode): self._oref = oref From 92cae3daa12c044ee2d51af9e903bbba7735776e Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Thu, 15 Jun 2023 09:09:52 +0300 Subject: [PATCH 017/756] feat(texts api): add direction to version and pop the old language. --- api/texts_api.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/api/texts_api.py b/api/texts_api.py index 0b9b1aa716..3982d82904 100644 --- a/api/texts_api.py +++ b/api/texts_api.py @@ -43,6 +43,8 @@ def handle_version_params(self, version_params): def add_text_to_versions(self): for version in self.return_obj['versions']: version.pop('title') + version['direction'] = 'ltr' if version['language'] == 'en' else 'he' + version.pop('language') version['text'] = TextRange(self.oref, version.actualLanguage, version.versionTitle).text #TODO - current api returns text for sections. do we want it? From c6b7725817759d67fbcc4395b775fef1068230ed Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Thu, 15 Jun 2023 09:10:46 +0300 Subject: [PATCH 018/756] fix(texts api): typo. --- api/texts_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/texts_api.py b/api/texts_api.py index 3982d82904..2946e3bc0a 100644 --- a/api/texts_api.py +++ b/api/texts_api.py @@ -24,7 +24,7 @@ def append_required_versions(self, lang, vtitle=None): else: versions = [v for v in self.all_versions if lang_condition(v)] if vtitle != 'all' and versions: - versions = [max(versions, key=lambda v: v['proiority'] or 0)] + versions = [max(versions, key=lambda v: v['priority'] or 0)] for version in versions: if version not in self.return_obj['versions']: self.return_obj['versions'].append(version) From dd4cb1623b21041f5dcf1405ebaabb677cb1d3ce Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Thu, 15 Jun 2023 12:37:55 +0300 Subject: [PATCH 019/756] feat(texts api): add errors to api response. --- api/texts_api.py | 46 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/api/texts_api.py b/api/texts_api.py index 2946e3bc0a..51dd23d805 100644 --- a/api/texts_api.py +++ b/api/texts_api.py @@ -2,15 +2,38 @@ django.setup() from sefaria.model import * from sefaria.datatype.jagged_array import JaggedArray +from enum import Enum class APITextsHandler(): + Direction = Enum('direction', ['rtl', 'ltr']) + def __init__(self, oref, versions_params): self.versions_params = versions_params + self.current_params = '' self.oref = oref self.handled_version_params = [] self.all_versions = self.oref.version_list() - self.return_obj = {'versions': []} + self.return_obj = {'versions': [], 'errors': []} + + def handle_errors(self, lang, vtitle): + print(f'"{self.current_params}", "{lang}", "{vtitle}"') + if lang == 'source': + code = 103 + message = f'We do not have the source text for {self.oref}' + elif vtitle and vtitle != 'all': + code = 101 + message = f'We do not have version named {vtitle} with language code {lang} for {self.oref}' + else: + code = 102 + availabe_langs = {v['actualLanguage'] for v in self.all_versions} + message = f'We do not have the code language you asked for {self.oref}. Available codes are {availabe_langs}' + self.return_obj['errors'].append({ + self.current_params: { + 'error_code': code, + 'message': message, + } + }) def append_required_versions(self, lang, vtitle=None): if lang == 'base': @@ -28,6 +51,8 @@ def append_required_versions(self, lang, vtitle=None): for version in versions: if version not in self.return_obj['versions']: self.return_obj['versions'].append(version) + if not versions: + self.handle_errors(lang, vtitle) def handle_version_params(self, version_params): if version_params in self.handled_version_params: @@ -43,16 +68,14 @@ def handle_version_params(self, version_params): def add_text_to_versions(self): for version in self.return_obj['versions']: version.pop('title') - version['direction'] = 'ltr' if version['language'] == 'en' else 'he' + version['direction'] = self.Direction.ltr.value if version['language'] == 'en' else self.Direction.rtl.value version.pop('language') - version['text'] = TextRange(self.oref, version.actualLanguage, version.versionTitle).text - #TODO - current api returns text for sections. do we want it? + version['text'] = TextRange(self.oref, version['actualLanguage'], version['versionTitle']).text def add_ref_data(self): oref = self.oref self.return_obj.update({ 'ref': oref.normal(), - #TODO - why do we need all of those? 'heRef': oref.he_normal(), 'sections': oref.sections, 'toSections': oref.toSections, @@ -61,10 +84,10 @@ def add_ref_data(self): 'firstAvailableSectionRef': oref.first_available_section_ref().normal(), 'isSpanning': oref.is_spanning(), 'spanningRefs': [r.normal() for r in oref.split_spanning_ref()], - 'next': oref.next_section_ref().normal(), #TODO - change names like 'next_section_ref'? the question is how much cilent code use this name + 'next': oref.next_section_ref().normal(), 'prev': oref.prev_section_ref().normal(), 'title': oref.context_ref().normal(), - 'book': oref.book, #TODO - isnt it the same as title? + 'book': oref.book, 'heTitle': oref.context_ref().he_normal(), 'primary_category': oref.primary_category, 'type': oref.primary_category, #same as primary category @@ -123,20 +146,16 @@ def add_index_data(self): index = self.oref.index self.return_obj.update({ 'indexTitle': index.title, - #TODO - those are attrs of index. it seems client can take them from there 'categories': index.categories, 'heIndexTitle': index.get_title('he'), 'isComplex': index.is_complex(), 'isDependant': index.is_dependant_text(), 'order': getattr(index, 'order', ''), - # TODO - alts are reduced fron the index. I guess here we can't give the client to do the job but have to check - #TODO - also this did them for sections while this code for now returns the text according to segment refs 'alts': self.reduce_alts_to_ref(), }) def add_node_data(self): inode = self.oref.index_node - # TODO - all those are attrs of node, so they are available also in the index. can the client take them from there? if getattr(inode, "lengths", None): self.return_obj["lengths"] = getattr(inode, "lengths") if len(self.return_obj["lengths"]): @@ -147,16 +166,15 @@ def add_node_data(self): 'textDepth': getattr(inode, "depth", None), 'sectionNames': getattr(inode, "sectionNames", None), 'addressTypes': getattr(inode, "addressTypes", None), - 'heTitle': inode.full_title("he"), #TODO - is there use in this and the nexts? + 'heTitle': inode.full_title("he"), 'titleVariants': inode.all_tree_titles("en"), 'heTitleVariants': inode.all_tree_titles("he"), - # TODO - offsets are reduced fron the node. I guess here we can't give the client to do the job but have to check - # TODO - also this did them for sections while this code for now returns the text according to segment refs 'index_offsets_by_depth': inode.trim_index_offsets_by_sections(self.oref.sections, self.oref.toSections), }) def get_versions_for_query(self): for version_params in self.versions_params: + self.current_params = version_params self.handle_version_params(version_params) self.add_text_to_versions() self.add_ref_data() From 07847e59210401afb8be6ad1898f8bfc7e777340 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Sun, 18 Jun 2023 09:11:07 +0300 Subject: [PATCH 020/756] refactor(Version): get fields for version_list from Version class atributes rather than another list. --- sefaria/model/text.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index 3dd253a6a8..359b7e5365 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -1340,6 +1340,8 @@ class Version(AbstractTextRecord, abst.AbstractMongoRecord, AbstractSchemaConten "hasManuallyWrappedRefs", # true for texts where refs were manually wrapped in a-tags. no need to run linker at run-time. "actualLanguage", "isBaseText", + 'isSource', + 'isBaseText2' #temp ] def __str__(self): @@ -4604,10 +4606,8 @@ def version_list(self): :return list: each list element is an object with keys 'versionTitle' and 'language' """ - fields = ["title", "versionTitle", "versionSource", "language", 'actualLanguage', "status", "license", "versionNotes", - "digitizedBySefaria", "priority", "versionTitleInHebrew", "versionNotesInHebrew", "extendedNotes", - "extendedNotesHebrew", "purchaseInformationImage", "purchaseInformationURL", "shortVersionTitle", - "shortVersionTitleInHebrew", "isBaseText", 'isSource', 'formatAsPoetry'] + fields = Version.optional_attrs + Version.required_attrs + fields.remove('chapter') versions = VersionSet(self.condition_query()) version_list = [] if self.is_book_level(): From 1cd1f1ed2899ea29f5294e78fca789183f6aec36 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Sun, 18 Jun 2023 09:22:29 +0300 Subject: [PATCH 021/756] chore(api texts): change isBaseText to isBaseText2 (temporal name). --- api/texts_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/texts_api.py b/api/texts_api.py index 51dd23d805..4365a4a090 100644 --- a/api/texts_api.py +++ b/api/texts_api.py @@ -37,7 +37,7 @@ def handle_errors(self, lang, vtitle): def append_required_versions(self, lang, vtitle=None): if lang == 'base': - lang_condition = lambda v: v['isBaseText'] + lang_condition = lambda v: v['isBaseText2'] #temporal name elif lang == 'source': lang_condition = lambda v: v['isSource'] else: From 16852b0971eda9d52e88d058f88d1d35facaf278 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Sun, 18 Jun 2023 10:33:41 +0300 Subject: [PATCH 022/756] chore(api texts): add underscore to private functions. --- api/texts_api.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/api/texts_api.py b/api/texts_api.py index 4365a4a090..9b7b88644f 100644 --- a/api/texts_api.py +++ b/api/texts_api.py @@ -16,7 +16,7 @@ def __init__(self, oref, versions_params): self.all_versions = self.oref.version_list() self.return_obj = {'versions': [], 'errors': []} - def handle_errors(self, lang, vtitle): + def _handle_errors(self, lang, vtitle): print(f'"{self.current_params}", "{lang}", "{vtitle}"') if lang == 'source': code = 103 @@ -35,7 +35,7 @@ def handle_errors(self, lang, vtitle): } }) - def append_required_versions(self, lang, vtitle=None): + def _append_required_versions(self, lang, vtitle=None): if lang == 'base': lang_condition = lambda v: v['isBaseText2'] #temporal name elif lang == 'source': @@ -54,7 +54,7 @@ def append_required_versions(self, lang, vtitle=None): if not versions: self.handle_errors(lang, vtitle) - def handle_version_params(self, version_params): + def _handle_version_params(self, version_params): if version_params in self.handled_version_params: return if '|' not in version_params: @@ -65,14 +65,14 @@ def handle_version_params(self, version_params): self.append_required_versions(lang, vtitle) self.handled_version_params.append(version_params) - def add_text_to_versions(self): + def _add_text_to_versions(self): for version in self.return_obj['versions']: version.pop('title') version['direction'] = self.Direction.ltr.value if version['language'] == 'en' else self.Direction.rtl.value version.pop('language') version['text'] = TextRange(self.oref, version['actualLanguage'], version['versionTitle']).text - def add_ref_data(self): + def _add_ref_data(self): oref = self.oref self.return_obj.update({ 'ref': oref.normal(), @@ -93,7 +93,7 @@ def add_ref_data(self): 'type': oref.primary_category, #same as primary category }) - def reduce_alts_to_ref(self): #TODO - copied from TextFamily. if we won't remove it, we should find some place for that + def _reduce_alts_to_ref(self): #TODO - copied from TextFamily. if we won't remove it, we should find some place for that oref = self.oref # Set up empty Array that mirrors text structure alts_ja = JaggedArray() @@ -142,7 +142,7 @@ def reduce_alts_to_ref(self): #TODO - copied from TextFamily. if we won't remove return alts_ja.array() - def add_index_data(self): + def _add_index_data(self): index = self.oref.index self.return_obj.update({ 'indexTitle': index.title, @@ -154,7 +154,7 @@ def add_index_data(self): 'alts': self.reduce_alts_to_ref(), }) - def add_node_data(self): + def _add_node_data(self): inode = self.oref.index_node if getattr(inode, "lengths", None): self.return_obj["lengths"] = getattr(inode, "lengths") From 37d2459a0cd2ce771363198e4c587708fbd53eac Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Sun, 18 Jun 2023 14:55:54 +0300 Subject: [PATCH 023/756] fix(api texts): add underscore to calls. --- api/texts_api.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/api/texts_api.py b/api/texts_api.py index 9b7b88644f..98a2ffe617 100644 --- a/api/texts_api.py +++ b/api/texts_api.py @@ -52,7 +52,7 @@ def _append_required_versions(self, lang, vtitle=None): if version not in self.return_obj['versions']: self.return_obj['versions'].append(version) if not versions: - self.handle_errors(lang, vtitle) + self._handle_errors(lang, vtitle) def _handle_version_params(self, version_params): if version_params in self.handled_version_params: @@ -62,7 +62,7 @@ def _handle_version_params(self, version_params): else: lang, vtitle = version_params.split('|', 1) vtitle = vtitle.replace('_', ' ') - self.append_required_versions(lang, vtitle) + self._append_required_versions(lang, vtitle) self.handled_version_params.append(version_params) def _add_text_to_versions(self): @@ -151,7 +151,7 @@ def _add_index_data(self): 'isComplex': index.is_complex(), 'isDependant': index.is_dependant_text(), 'order': getattr(index, 'order', ''), - 'alts': self.reduce_alts_to_ref(), + 'alts': self._reduce_alts_to_ref(), }) def _add_node_data(self): @@ -175,9 +175,9 @@ def _add_node_data(self): def get_versions_for_query(self): for version_params in self.versions_params: self.current_params = version_params - self.handle_version_params(version_params) - self.add_text_to_versions() - self.add_ref_data() - self.add_index_data() - self.add_node_data() + self._handle_version_params(version_params) + self._add_text_to_versions() + self._add_ref_data() + self._add_index_data() + self._add_node_data() return self.return_obj From f1c881440637465286ba954fb3b957e5449a21f8 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Sun, 18 Jun 2023 19:25:32 +0300 Subject: [PATCH 024/756] fix(api texts): sections and toSections for Talmud addressType, add collective title. --- api/texts_api.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/api/texts_api.py b/api/texts_api.py index 98a2ffe617..fd8d0a4f74 100644 --- a/api/texts_api.py +++ b/api/texts_api.py @@ -2,6 +2,7 @@ django.setup() from sefaria.model import * from sefaria.datatype.jagged_array import JaggedArray +from sefaria.utils.hebrew import hebrew_term from enum import Enum class APITextsHandler(): @@ -77,16 +78,16 @@ def _add_ref_data(self): self.return_obj.update({ 'ref': oref.normal(), 'heRef': oref.he_normal(), - 'sections': oref.sections, - 'toSections': oref.toSections, + 'sections': oref.normal_sections(), #that means it will be string. in the previous api talmud sections were strings while integers remained integets. this is more consistent but we should check it works + 'toSections': oref.normal_toSections(), 'sectionRef': oref.section_ref().normal(), 'heSectionRef': oref.section_ref().he_normal(), 'firstAvailableSectionRef': oref.first_available_section_ref().normal(), 'isSpanning': oref.is_spanning(), 'spanningRefs': [r.normal() for r in oref.split_spanning_ref()], 'next': oref.next_section_ref().normal(), - 'prev': oref.prev_section_ref().normal(), - 'title': oref.context_ref().normal(), + 'prev': oref.prev_section_ref().normal() if oref.prev_section_ref() else None, + 'title': oref.context_ref().normal() if oref.next_section_ref() else None, 'book': oref.book, 'heTitle': oref.context_ref().he_normal(), 'primary_category': oref.primary_category, @@ -151,6 +152,8 @@ def _add_index_data(self): 'isComplex': index.is_complex(), 'isDependant': index.is_dependant_text(), 'order': getattr(index, 'order', ''), + 'collectiveTitle': getattr(index, 'collective_title', ''), + 'heCollectiveTitle': hebrew_term(getattr(index, 'collective_title', '')), 'alts': self._reduce_alts_to_ref(), }) From 1d8ae8d775256c16a79d8e3b15cdbd72be3762c5 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Mon, 19 Jun 2023 09:48:59 +0300 Subject: [PATCH 025/756] chore(api texts): return json in error 400. --- api/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/views.py b/api/views.py index 43faca7a1e..55e818aef5 100644 --- a/api/views.py +++ b/api/views.py @@ -1,5 +1,5 @@ +import json from django.http import HttpResponseBadRequest - from sefaria.model import * from sefaria.system.multiserver.coordinator import server_coordinator from sefaria.settings import DISABLE_AUTOCOMPLETER, ENABLE_LINKER @@ -48,7 +48,7 @@ def get_texts(request, tref): try: oref = Ref.instantiate_ref_with_legacy_parse_fallback(tref) except Exception as e: - return HttpResponseBadRequest(e) + return HttpResponseBadRequest(json.dumps({'error': getattr(e, 'message', str(e))}, ensure_ascii=False)) cb = request.GET.get("callback", None) if request.method == "GET": versions_params = request.GET.getlist('version', []) From 0ffbf93387a4d6604bfc7c5d15fb803549dcd11d Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Mon, 19 Jun 2023 11:20:36 +0300 Subject: [PATCH 026/756] chore(api texts): remove print. --- api/texts_api.py | 1 - 1 file changed, 1 deletion(-) diff --git a/api/texts_api.py b/api/texts_api.py index fd8d0a4f74..f2e43d4e51 100644 --- a/api/texts_api.py +++ b/api/texts_api.py @@ -18,7 +18,6 @@ def __init__(self, oref, versions_params): self.return_obj = {'versions': [], 'errors': []} def _handle_errors(self, lang, vtitle): - print(f'"{self.current_params}", "{lang}", "{vtitle}"') if lang == 'source': code = 103 message = f'We do not have the source text for {self.oref}' From f5d743278a21eb0bcae55e191b8aa5d1ffcf0d59 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Mon, 19 Jun 2023 12:17:40 +0300 Subject: [PATCH 027/756] chore(api texts): sort avaailabe langs in error message (for stable message). --- api/texts_api.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/api/texts_api.py b/api/texts_api.py index f2e43d4e51..c02281a63e 100644 --- a/api/texts_api.py +++ b/api/texts_api.py @@ -18,7 +18,10 @@ def __init__(self, oref, versions_params): self.return_obj = {'versions': [], 'errors': []} def _handle_errors(self, lang, vtitle): - if lang == 'source': + if self.oref.is_empty(): + code = 104 + message = f'The ref {self.oref} is empty' + elif lang == 'source': code = 103 message = f'We do not have the source text for {self.oref}' elif vtitle and vtitle != 'all': @@ -27,7 +30,7 @@ def _handle_errors(self, lang, vtitle): else: code = 102 availabe_langs = {v['actualLanguage'] for v in self.all_versions} - message = f'We do not have the code language you asked for {self.oref}. Available codes are {availabe_langs}' + message = f'We do not have the code language you asked for {self.oref}. Available codes are {sorted(availabe_langs)}' self.return_obj['errors'].append({ self.current_params: { 'error_code': code, From 681806fa2ffa4091efe22da7145e06a5e144d1af Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Mon, 19 Jun 2023 12:55:57 +0300 Subject: [PATCH 028/756] chore(api texts): tests. --- api/tests.py | 132 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 api/tests.py diff --git a/api/tests.py b/api/tests.py new file mode 100644 index 0000000000..643689df5e --- /dev/null +++ b/api/tests.py @@ -0,0 +1,132 @@ +from django.test.client import Client +import django +django.setup() +from reader.tests import SefariaTestCase +import json + +c = Client() + +class APITextsTests(SefariaTestCase): + + def test_api_get_text_default(self): + response = c.get('/api/v3/texts/Genesis.1') + self.assertEqual(200, response.status_code) + data = json.loads(response.content) + self.assertTrue(len(data["versions"]) == 1) + self.assertTrue(data["versions"][0]['actualLanguage'] == 'he') + self.assertEqual(data["book"], "Genesis") + self.assertEqual(data["categories"], ["Tanakh", "Torah"]) + self.assertEqual(data["sections"], ['1']) + self.assertEqual(data["toSections"], ['1']) + + def test_api_get_text_source_all(self): + response = c.get('/api/v3/texts/Shabbat.22a?version=source|all') + self.assertEqual(200, response.status_code) + data = json.loads(response.content) + self.assertTrue(len(data["versions"]) > 1) + self.assertTrue(all(v['actualLanguage'] == 'he' for v in data["versions"])) + self.assertEqual(data["book"], "Shabbat") + self.assertEqual(data["categories"], ["Talmud", "Bavli", "Seder Moed"]) + self.assertEqual(data["sections"], ["22a"]) + self.assertEqual(data["toSections"], ["22a"]) + + def test_api_get_text_lang_all(self): + response = c.get('/api/v3/texts/Rashi_on_Genesis.2.3?version=en|all') + self.assertEqual(200, response.status_code) + data = json.loads(response.content) + self.assertTrue(len(data["versions"]) > 1) + self.assertTrue(all(v['actualLanguage'] == 'en' for v in data["versions"])) + self.assertEqual(data["book"], "Rashi on Genesis") + self.assertEqual(data["collectiveTitle"], "Rashi") + self.assertEqual(data["categories"], ["Tanakh", "Rishonim on Tanakh", "Rashi", "Torah"]) + self.assertEqual(data["sections"], ['2', '3']) + self.assertEqual(data["toSections"], ['2', '3']) + + def test_api_get_text_specific(self): + response = c.get('/api/v3/texts/Tosafot_on_Sukkah.2a.4.1?version=he|Vilna_Edition') + self.assertEqual(200, response.status_code) + data = json.loads(response.content) + self.assertEqual(len(data["versions"]), 1) + self.assertEqual(data["versions"][0]['actualLanguage'], 'he') + self.assertEqual(data["versions"][0]['versionTitle'], 'Vilna Edition') + self.assertEqual(data["book"], "Tosafot on Sukkah") + self.assertEqual(data["collectiveTitle"], "Tosafot") + self.assertEqual(data["categories"], ["Talmud", "Bavli", "Rishonim on Talmud", "Tosafot", "Seder Moed"]) + self.assertEqual(data["sections"], ["2a", '4', '1']) + self.assertEqual(data["toSections"], ["2a", '4', '1']) + + def test_api_get_text_base_all(self): + response = c.get('/api/v3/texts/Genesis.1?version=base|all') + data = json.loads(response.content) + self.assertTrue(len(data["versions"]) > 3) + self.assertTrue(all(v['actualLanguage'] == 'he' for v in data["versions"])) + + def test_api_get_text_two_params(self): + response = c.get('/api/v3/texts/Genesis.1?version=he|Tanach with Nikkud&version=en|all') + data = json.loads(response.content) + self.assertTrue(len(data["versions"]) > 7) + self.assertEqual(data["versions"][0]['actualLanguage'], 'he') + self.assertTrue(all(v['actualLanguage'] == 'en' for v in data["versions"][1:])) + + def test_api_get_text_range(self): + response = c.get('/api/v3/texts/Job.5.2-4') + self.assertEqual(200, response.status_code) + data = json.loads(response.content) + self.assertEqual(data["sections"], ['5', '2']) + self.assertEqual(data["toSections"], ['5', '4']) + + def test_api_get_text_bad_text(self): + response = c.get('/api/v3/texts/Life_of_Pi.13.13') + self.assertEqual(400, response.status_code) + data = json.loads(response.content) + self.assertEqual(data["error"], "Could not find title in reference: Life of Pi.13.13") + + def test_api_get_text_out_of_bound(self): + response = c.get('/api/v3/texts/Genesis.999') + data = json.loads(response.content) + self.assertEqual(data["error"], "Genesis ends at Chapter 50.") + + def test_api_get_text_too_many_hyphens(self): + response = c.get('/api/v3/texts/Genesis.9-4-5') + data = json.loads(response.content) + self.assertEqual(data["error"], "Couldn't understand ref 'Genesis.9-4-5' (too many -'s).") + + def test_api_get_text_bad_sections(self): + response = c.get('/api/v3/texts/Job.6-X') + self.assertEqual(400, response.status_code) + data = json.loads(response.content) + self.assertEqual(data["error"], "Couldn't understand text sections: 'Job.6-X'.") + + def test_api_get_text_no_source(self): + response = c.get("/api/v3/texts/The_Book_of_Maccabees_I.1?version=en|Brenton's_Septuagint&version=source") + self.assertEqual(200, response.status_code) + data = json.loads(response.content) + self.assertEqual(len(data["versions"]), 1) + self.assertEqual(data['errors'][0]['source']['error_code'], 103) + self.assertEqual(data['errors'][0]['source']['message'], 'We do not have the source text for The Book of Maccabees I 1') + + def test_api_get_text_no_language(self): + response = c.get("/api/v3/texts/The_Book_of_Maccabees_I.1?version=en|Brenton's_Septuagint&version=sgrg|all") + self.assertEqual(200, response.status_code) + data = json.loads(response.content) + self.assertEqual(len(data["versions"]), 1) + self.assertEqual(data['errors'][0]['sgrg|all']['error_code'], 102) + self.assertEqual(data['errors'][0]['sgrg|all']['message'], + "We do not have the code language you asked for The Book of Maccabees I 1. Available codes are ['en', 'he']") + + def test_api_get_text_no_version(self): + response = c.get("/api/v3/texts/The_Book_of_Maccabees_I.1?version=en|Brenton's_Septuagint&version=he|Kishkoosh") + self.assertEqual(200, response.status_code) + data = json.loads(response.content) + self.assertEqual(len(data["versions"]), 1) + self.assertEqual(data['errors'][0]['he|Kishkoosh']['error_code'], 101) + self.assertEqual(data['errors'][0]['he|Kishkoosh']['message'], + 'We do not have version named Kishkoosh with language code he for The Book of Maccabees I 1') + + + def test_api_get_text_empty_ref(self): + response = c.get("/api/v3/texts/Berakhot.1a") + self.assertEqual(200, response.status_code) + data = json.loads(response.content) + self.assertEqual(data['errors'][0]['base']['error_code'], 104) + self.assertEqual(data['errors'][0]['base']['message'], 'The ref Berakhot 1a is empty') From d3ed3a50b01edceccfb023ad030e4a22789465a9 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Mon, 19 Jun 2023 16:24:06 +0300 Subject: [PATCH 029/756] refactor(api texts): class for errors. --- api/api_errors.py | 44 ++++++++++++++++++++++++++++++++++++++++++++ api/texts_api.py | 18 ++++++------------ 2 files changed, 50 insertions(+), 12 deletions(-) create mode 100644 api/api_errors.py diff --git a/api/api_errors.py b/api/api_errors.py new file mode 100644 index 0000000000..04572a24b0 --- /dev/null +++ b/api/api_errors.py @@ -0,0 +1,44 @@ +import django +django.setup() +from sefaria.model import * +from typing import List + + +class APIError(): + + def __init__(self, error_code=0, message=''): + self.error_code = error_code + self.message = message + + def get_dict(self): + return {'error_code': self.error_code, + 'message': self.message} + + +class NoVersionError(APIError): + + def __init__(self, oref: Ref, vtitle: str, lang: str): + self.error_code = 101 + self.message = f'We do not have version named {vtitle} with language code {lang} for {oref}' + + +class NoLanguageVersionError(APIError): + + def __init__(self, oref: Ref, langs: List[str]): + self.error_code = 102 + self.message = f'We do not have the code language you asked for {oref}. Available codes are {langs}' + + +class NoSourceTextError(APIError): + + def __init__(self, oref: Ref): + self.error_code = 103 + self.message = f'We do not have the source text for {oref}' + + + +class RefIsEmptyError(APIError): + + def __init__(self, oref: Ref): + self.error_code = 104 + self.message = f'The ref {oref} is empty' diff --git a/api/texts_api.py b/api/texts_api.py index c02281a63e..cef866454e 100644 --- a/api/texts_api.py +++ b/api/texts_api.py @@ -4,6 +4,7 @@ from sefaria.datatype.jagged_array import JaggedArray from sefaria.utils.hebrew import hebrew_term from enum import Enum +from .api_errors import * class APITextsHandler(): @@ -19,23 +20,16 @@ def __init__(self, oref, versions_params): def _handle_errors(self, lang, vtitle): if self.oref.is_empty(): - code = 104 - message = f'The ref {self.oref} is empty' + error = RefIsEmptyError(self.oref) elif lang == 'source': - code = 103 - message = f'We do not have the source text for {self.oref}' + error = NoSourceTextError(self.oref) elif vtitle and vtitle != 'all': - code = 101 - message = f'We do not have version named {vtitle} with language code {lang} for {self.oref}' + error = NoVersionError(self.oref, vtitle, lang) else: - code = 102 availabe_langs = {v['actualLanguage'] for v in self.all_versions} - message = f'We do not have the code language you asked for {self.oref}. Available codes are {sorted(availabe_langs)}' + error = NoLanguageVersionError(self.oref, sorted(availabe_langs)) self.return_obj['errors'].append({ - self.current_params: { - 'error_code': code, - 'message': message, - } + self.current_params: error.get_dict() }) def _append_required_versions(self, lang, vtitle=None): From d22baa8b164cb8baf840ca1914e8afdd6e85488e Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Mon, 19 Jun 2023 16:53:12 +0300 Subject: [PATCH 030/756] refactor(api texts): change magic string to vars. --- api/texts_api.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/api/texts_api.py b/api/texts_api.py index cef866454e..fc1e381137 100644 --- a/api/texts_api.py +++ b/api/texts_api.py @@ -9,6 +9,9 @@ class APITextsHandler(): Direction = Enum('direction', ['rtl', 'ltr']) + ALL = 'all' + BASE = 'base' + SOURCE = 'source' def __init__(self, oref, versions_params): self.versions_params = versions_params @@ -21,9 +24,9 @@ def __init__(self, oref, versions_params): def _handle_errors(self, lang, vtitle): if self.oref.is_empty(): error = RefIsEmptyError(self.oref) - elif lang == 'source': + elif lang == self.SOURCE: error = NoSourceTextError(self.oref) - elif vtitle and vtitle != 'all': + elif vtitle and vtitle != self.ALL: error = NoVersionError(self.oref, vtitle, lang) else: availabe_langs = {v['actualLanguage'] for v in self.all_versions} @@ -33,17 +36,17 @@ def _handle_errors(self, lang, vtitle): }) def _append_required_versions(self, lang, vtitle=None): - if lang == 'base': + if lang == self.BASE: lang_condition = lambda v: v['isBaseText2'] #temporal name - elif lang == 'source': + elif lang == self.SOURCE: lang_condition = lambda v: v['isSource'] else: lang_condition = lambda v: v['actualLanguage'] == lang - if vtitle and vtitle != 'all': + if vtitle and vtitle != self.ALL: versions = [v for v in self.all_versions if lang_condition(v) and v['versionTitle'] == vtitle] else: versions = [v for v in self.all_versions if lang_condition(v)] - if vtitle != 'all' and versions: + if vtitle != self.ALL and versions: versions = [max(versions, key=lambda v: v['priority'] or 0)] for version in versions: if version not in self.return_obj['versions']: From 8c63e33bd1605d47357bb18575c3fc7eee3c1615 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Mon, 19 Jun 2023 16:53:55 +0300 Subject: [PATCH 031/756] chore(api texts): remove import. --- api/texts_api.py | 1 - 1 file changed, 1 deletion(-) diff --git a/api/texts_api.py b/api/texts_api.py index fc1e381137..595a68e3d5 100644 --- a/api/texts_api.py +++ b/api/texts_api.py @@ -1,6 +1,5 @@ import django django.setup() -from sefaria.model import * from sefaria.datatype.jagged_array import JaggedArray from sefaria.utils.hebrew import hebrew_term from enum import Enum From a0a6e208d13a9d3ca50dd3755e4158c34d441f50 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Mon, 19 Jun 2023 17:17:30 +0300 Subject: [PATCH 032/756] chore(api texts): splitting params by a helper function. --- api/helper.py | 8 ++++++++ api/texts_api.py | 8 +++----- 2 files changed, 11 insertions(+), 5 deletions(-) create mode 100644 api/helper.py diff --git a/api/helper.py b/api/helper.py new file mode 100644 index 0000000000..aa9b781263 --- /dev/null +++ b/api/helper.py @@ -0,0 +1,8 @@ +from typing import List + +def split_at_pipe_with_default(string: str, list_length: int, defaults: List[str]) -> List[str]: + #length of defaults should be one less than list_length + substrings = string.split('|', list_length-1) + while len(substrings) < list_length: + substrings.append(defaults.pop()) + return substrings diff --git a/api/texts_api.py b/api/texts_api.py index 595a68e3d5..f17c6cda78 100644 --- a/api/texts_api.py +++ b/api/texts_api.py @@ -4,6 +4,7 @@ from sefaria.utils.hebrew import hebrew_term from enum import Enum from .api_errors import * +from .helper import split_at_pipe_with_default class APITextsHandler(): @@ -56,11 +57,8 @@ def _append_required_versions(self, lang, vtitle=None): def _handle_version_params(self, version_params): if version_params in self.handled_version_params: return - if '|' not in version_params: - lang, vtitle = version_params, '' - else: - lang, vtitle = version_params.split('|', 1) - vtitle = vtitle.replace('_', ' ') + lang, vtitle = split_at_pipe_with_default(version_params, 2, ['']) + vtitle = vtitle.replace('_', ' ') self._append_required_versions(lang, vtitle) self.handled_version_params.append(version_params) From 082d2b5b3ccd6d0dfef052c68ae84091b9cfc1da Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Mon, 19 Jun 2023 17:27:56 +0300 Subject: [PATCH 033/756] chore(api texts): add type hints. --- api/api_errors.py | 2 +- api/texts_api.py | 22 ++++++++++++---------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/api/api_errors.py b/api/api_errors.py index 04572a24b0..c1b7b40af7 100644 --- a/api/api_errors.py +++ b/api/api_errors.py @@ -10,7 +10,7 @@ def __init__(self, error_code=0, message=''): self.error_code = error_code self.message = message - def get_dict(self): + def get_dict(self) -> dict: return {'error_code': self.error_code, 'message': self.message} diff --git a/api/texts_api.py b/api/texts_api.py index f17c6cda78..7b513f5fa3 100644 --- a/api/texts_api.py +++ b/api/texts_api.py @@ -1,10 +1,12 @@ import django django.setup() +from sefaria.model import * from sefaria.datatype.jagged_array import JaggedArray from sefaria.utils.hebrew import hebrew_term from enum import Enum from .api_errors import * from .helper import split_at_pipe_with_default +from typing import List class APITextsHandler(): @@ -13,7 +15,7 @@ class APITextsHandler(): BASE = 'base' SOURCE = 'source' - def __init__(self, oref, versions_params): + def __init__(self, oref: Ref, versions_params: List[str]): self.versions_params = versions_params self.current_params = '' self.oref = oref @@ -21,7 +23,7 @@ def __init__(self, oref, versions_params): self.all_versions = self.oref.version_list() self.return_obj = {'versions': [], 'errors': []} - def _handle_errors(self, lang, vtitle): + def _handle_errors(self, lang: str, vtitle: str) -> None: if self.oref.is_empty(): error = RefIsEmptyError(self.oref) elif lang == self.SOURCE: @@ -35,7 +37,7 @@ def _handle_errors(self, lang, vtitle): self.current_params: error.get_dict() }) - def _append_required_versions(self, lang, vtitle=None): + def _append_required_versions(self, lang: str, vtitle=None) -> None: if lang == self.BASE: lang_condition = lambda v: v['isBaseText2'] #temporal name elif lang == self.SOURCE: @@ -54,7 +56,7 @@ def _append_required_versions(self, lang, vtitle=None): if not versions: self._handle_errors(lang, vtitle) - def _handle_version_params(self, version_params): + def _handle_version_params(self, version_params: str) -> None: if version_params in self.handled_version_params: return lang, vtitle = split_at_pipe_with_default(version_params, 2, ['']) @@ -62,14 +64,14 @@ def _handle_version_params(self, version_params): self._append_required_versions(lang, vtitle) self.handled_version_params.append(version_params) - def _add_text_to_versions(self): + def _add_text_to_versions(self) -> None: for version in self.return_obj['versions']: version.pop('title') version['direction'] = self.Direction.ltr.value if version['language'] == 'en' else self.Direction.rtl.value version.pop('language') version['text'] = TextRange(self.oref, version['actualLanguage'], version['versionTitle']).text - def _add_ref_data(self): + def _add_ref_data(self) -> None: oref = self.oref self.return_obj.update({ 'ref': oref.normal(), @@ -90,7 +92,7 @@ def _add_ref_data(self): 'type': oref.primary_category, #same as primary category }) - def _reduce_alts_to_ref(self): #TODO - copied from TextFamily. if we won't remove it, we should find some place for that + def _reduce_alts_to_ref(self) -> dict: #TODO - copied from TextFamily. if we won't remove it, we should find some place for that oref = self.oref # Set up empty Array that mirrors text structure alts_ja = JaggedArray() @@ -139,7 +141,7 @@ def _reduce_alts_to_ref(self): #TODO - copied from TextFamily. if we won't remov return alts_ja.array() - def _add_index_data(self): + def _add_index_data(self) -> None: index = self.oref.index self.return_obj.update({ 'indexTitle': index.title, @@ -153,7 +155,7 @@ def _add_index_data(self): 'alts': self._reduce_alts_to_ref(), }) - def _add_node_data(self): + def _add_node_data(self) -> None: inode = self.oref.index_node if getattr(inode, "lengths", None): self.return_obj["lengths"] = getattr(inode, "lengths") @@ -171,7 +173,7 @@ def _add_node_data(self): 'index_offsets_by_depth': inode.trim_index_offsets_by_sections(self.oref.sections, self.oref.toSections), }) - def get_versions_for_query(self): + def get_versions_for_query(self) -> dict: for version_params in self.versions_params: self.current_params = version_params self._handle_version_params(version_params) From e702160432358b0f4ef53814d85fe05c0c401756 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Mon, 19 Jun 2023 17:42:42 +0300 Subject: [PATCH 034/756] docs(api texts): documentation for APITextsHandler and _reduce_alts_to_ref. --- api/texts_api.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/api/texts_api.py b/api/texts_api.py index 7b513f5fa3..b47732ef8a 100644 --- a/api/texts_api.py +++ b/api/texts_api.py @@ -9,6 +9,21 @@ from typing import List class APITextsHandler(): + """ + process api calls for text + an api call contains ref and list of version_params + a version params is string divided by pipe as 'lang|vtitle' + lang is our code language. special values are: + source - for versions in the source language of the text + base - as source but with falling to the 'nearest' to source, or what we have defined as such + vtitle is the exact versionTitle. special values are: + no vtitle - the version with the max priority attr of the specified language + all - all versions of the specified language + return_obj is dict that includes in its root: + ref and index data + 'versions' - list of versions details and text + 'errors' - for any version_params that had an error + """ Direction = Enum('direction', ['rtl', 'ltr']) ALL = 'all' @@ -93,6 +108,10 @@ def _add_ref_data(self) -> None: }) def _reduce_alts_to_ref(self) -> dict: #TODO - copied from TextFamily. if we won't remove it, we should find some place for that + """ + this function takes the index's alt_structs and reduce it to the relevant ref + it is necessary for the client side + """ oref = self.oref # Set up empty Array that mirrors text structure alts_ja = JaggedArray() From e796bf2540617807851001e4e70a8d1ba70af72e Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Mon, 19 Jun 2023 17:52:13 +0300 Subject: [PATCH 035/756] chore(api texts): remove all redundant code from views beginning. --- api/views.py | 39 --------------------------------------- 1 file changed, 39 deletions(-) diff --git a/api/views.py b/api/views.py index 55e818aef5..c7709a4abb 100644 --- a/api/views.py +++ b/api/views.py @@ -1,48 +1,9 @@ import json from django.http import HttpResponseBadRequest from sefaria.model import * -from sefaria.system.multiserver.coordinator import server_coordinator -from sefaria.settings import DISABLE_AUTOCOMPLETER, ENABLE_LINKER from .texts_api import APITextsHandler from sefaria.client.util import jsonResponse -import structlog -logger = structlog.get_logger(__name__) - -#TODO - i've copied it from reader.views. i'm not sure what it does - -# # # -# Initialized cache library objects that depend on sefaria.model being completely loaded. -logger.info("Initializing library objects.") -logger.info("Initializing TOC Tree") -library.get_toc_tree() - -logger.info("Initializing Shared Cache") -library.init_shared_cache() - -if not DISABLE_AUTOCOMPLETER: - logger.info("Initializing Full Auto Completer") - library.build_full_auto_completer() - - logger.info("Initializing Ref Auto Completer") - library.build_ref_auto_completer() - - logger.info("Initializing Lexicon Auto Completers") - library.build_lexicon_auto_completers() - - logger.info("Initializing Cross Lexicon Auto Completer") - library.build_cross_lexicon_auto_completer() - - logger.info("Initializing Topic Auto Completer") - library.build_topic_auto_completer() - -if ENABLE_LINKER: - logger.info("Initializing Linker") - library.build_ref_resolver() - -if server_coordinator: - server_coordinator.connect() -# # # def get_texts(request, tref): try: From 6cc9fb117cfd0f5233d6739db617af1f4cd9016b Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Tue, 20 Jun 2023 10:34:55 +0300 Subject: [PATCH 036/756] chore(api texts): remove all redundant code from views beginning. --- api/texts_api.py | 4 +--- sefaria/model/text.py | 3 ++- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/api/texts_api.py b/api/texts_api.py index b47732ef8a..86aa281c63 100644 --- a/api/texts_api.py +++ b/api/texts_api.py @@ -25,7 +25,6 @@ class APITextsHandler(): 'errors' - for any version_params that had an error """ - Direction = Enum('direction', ['rtl', 'ltr']) ALL = 'all' BASE = 'base' SOURCE = 'source' @@ -82,8 +81,7 @@ def _handle_version_params(self, version_params: str) -> None: def _add_text_to_versions(self) -> None: for version in self.return_obj['versions']: version.pop('title') - version['direction'] = self.Direction.ltr.value if version['language'] == 'en' else self.Direction.rtl.value - version.pop('language') + version.pop('language') #should be removed after language is removed from attrs version['text'] = TextRange(self.oref, version['actualLanguage'], version['versionTitle']).text def _add_ref_data(self) -> None: diff --git a/sefaria/model/text.py b/sefaria/model/text.py index 359b7e5365..d012ce47d9 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -1341,7 +1341,8 @@ class Version(AbstractTextRecord, abst.AbstractMongoRecord, AbstractSchemaConten "actualLanguage", "isBaseText", 'isSource', - 'isBaseText2' #temp + 'isBaseText2', #temp + 'direction', # 1 for rtl, 2 for ltr ] def __str__(self): From f3e8466d83e1cc5b778b7f0fda651ebe496e2f50 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Tue, 20 Jun 2023 13:55:43 +0300 Subject: [PATCH 037/756] fix(Ref): do not try to recognize book in ref initialization in the middle of a word. --- sefaria/model/text.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index d012ce47d9..8659b4750a 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -2870,6 +2870,9 @@ def __init_tref(self): # Remove letter from end of base reference until TitleNode matched, set `title` variable with matched title for l in range(len(base), 0, -1): + if l != len(base) and base[l] not in ' ,.:_': + continue #do not stop in the middle of a word + self.index_node = tndict.get(base[0:l]) if self.index_node: From 243f6cc772f85431abdcfac1c5b29a5e760795b8 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Tue, 20 Jun 2023 14:56:46 +0300 Subject: [PATCH 038/756] feat(api texts): return 400 for empty ref. --- api/api_errors.py | 8 -------- api/tests.py | 14 ++++++-------- api/texts_api.py | 6 +----- api/views.py | 2 ++ 4 files changed, 9 insertions(+), 21 deletions(-) diff --git a/api/api_errors.py b/api/api_errors.py index c1b7b40af7..3bf38296e2 100644 --- a/api/api_errors.py +++ b/api/api_errors.py @@ -34,11 +34,3 @@ class NoSourceTextError(APIError): def __init__(self, oref: Ref): self.error_code = 103 self.message = f'We do not have the source text for {oref}' - - - -class RefIsEmptyError(APIError): - - def __init__(self, oref: Ref): - self.error_code = 104 - self.message = f'The ref {oref} is empty' diff --git a/api/tests.py b/api/tests.py index 643689df5e..748583c993 100644 --- a/api/tests.py +++ b/api/tests.py @@ -97,6 +97,12 @@ def test_api_get_text_bad_sections(self): data = json.loads(response.content) self.assertEqual(data["error"], "Couldn't understand text sections: 'Job.6-X'.") + def test_api_get_text_empty_ref(self): + response = c.get("/api/v3/texts/Berakhot.1a") + self.assertEqual(400, response.status_code) + data = json.loads(response.content) + self.assertEqual(data["error"], "We have no text for Berakhot 1a.") + def test_api_get_text_no_source(self): response = c.get("/api/v3/texts/The_Book_of_Maccabees_I.1?version=en|Brenton's_Septuagint&version=source") self.assertEqual(200, response.status_code) @@ -122,11 +128,3 @@ def test_api_get_text_no_version(self): self.assertEqual(data['errors'][0]['he|Kishkoosh']['error_code'], 101) self.assertEqual(data['errors'][0]['he|Kishkoosh']['message'], 'We do not have version named Kishkoosh with language code he for The Book of Maccabees I 1') - - - def test_api_get_text_empty_ref(self): - response = c.get("/api/v3/texts/Berakhot.1a") - self.assertEqual(200, response.status_code) - data = json.loads(response.content) - self.assertEqual(data['errors'][0]['base']['error_code'], 104) - self.assertEqual(data['errors'][0]['base']['message'], 'The ref Berakhot 1a is empty') diff --git a/api/texts_api.py b/api/texts_api.py index 86aa281c63..f5485a18f2 100644 --- a/api/texts_api.py +++ b/api/texts_api.py @@ -1,9 +1,7 @@ import django django.setup() -from sefaria.model import * from sefaria.datatype.jagged_array import JaggedArray from sefaria.utils.hebrew import hebrew_term -from enum import Enum from .api_errors import * from .helper import split_at_pipe_with_default from typing import List @@ -38,9 +36,7 @@ def __init__(self, oref: Ref, versions_params: List[str]): self.return_obj = {'versions': [], 'errors': []} def _handle_errors(self, lang: str, vtitle: str) -> None: - if self.oref.is_empty(): - error = RefIsEmptyError(self.oref) - elif lang == self.SOURCE: + if lang == self.SOURCE: error = NoSourceTextError(self.oref) elif vtitle and vtitle != self.ALL: error = NoVersionError(self.oref, vtitle, lang) diff --git a/api/views.py b/api/views.py index c7709a4abb..66e66de424 100644 --- a/api/views.py +++ b/api/views.py @@ -10,6 +10,8 @@ def get_texts(request, tref): oref = Ref.instantiate_ref_with_legacy_parse_fallback(tref) except Exception as e: return HttpResponseBadRequest(json.dumps({'error': getattr(e, 'message', str(e))}, ensure_ascii=False)) + if oref.is_empty(): + return HttpResponseBadRequest(json.dumps({'error': f'We have no text for {oref}.'}, ensure_ascii=False)) cb = request.GET.get("callback", None) if request.method == "GET": versions_params = request.GET.getlist('version', []) From 30d7ef1503ac8c4fbbbb48436f8c4c62381703fb Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Wed, 21 Jun 2023 10:38:55 +0300 Subject: [PATCH 039/756] refactor(api texts): change names for errors. --- api/api_errors.py | 11 +++++------ api/texts_api.py | 6 +++--- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/api/api_errors.py b/api/api_errors.py index 3bf38296e2..65ce8a576e 100644 --- a/api/api_errors.py +++ b/api/api_errors.py @@ -6,30 +6,29 @@ class APIError(): - def __init__(self, error_code=0, message=''): - self.error_code = error_code - self.message = message + def __init__(self): + pass def get_dict(self) -> dict: return {'error_code': self.error_code, 'message': self.message} -class NoVersionError(APIError): +class APINoVersion(APIError): def __init__(self, oref: Ref, vtitle: str, lang: str): self.error_code = 101 self.message = f'We do not have version named {vtitle} with language code {lang} for {oref}' -class NoLanguageVersionError(APIError): +class APINoLanguageVersion(APIError): def __init__(self, oref: Ref, langs: List[str]): self.error_code = 102 self.message = f'We do not have the code language you asked for {oref}. Available codes are {langs}' -class NoSourceTextError(APIError): +class APINoSourceText(APIError): def __init__(self, oref: Ref): self.error_code = 103 diff --git a/api/texts_api.py b/api/texts_api.py index f5485a18f2..47f29b57ce 100644 --- a/api/texts_api.py +++ b/api/texts_api.py @@ -37,12 +37,12 @@ def __init__(self, oref: Ref, versions_params: List[str]): def _handle_errors(self, lang: str, vtitle: str) -> None: if lang == self.SOURCE: - error = NoSourceTextError(self.oref) + error = APINoSourceText(self.oref) elif vtitle and vtitle != self.ALL: - error = NoVersionError(self.oref, vtitle, lang) + error = APINoVersion(self.oref, vtitle, lang) else: availabe_langs = {v['actualLanguage'] for v in self.all_versions} - error = NoLanguageVersionError(self.oref, sorted(availabe_langs)) + error = APINoLanguageVersion(self.oref, sorted(availabe_langs)) self.return_obj['errors'].append({ self.current_params: error.get_dict() }) From 7f9eeef6477444c5effbe1ced9c37c71687c748e Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Wed, 21 Jun 2023 10:46:29 +0300 Subject: [PATCH 040/756] docs(api texts): explain why check before append. --- api/texts_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/texts_api.py b/api/texts_api.py index 47f29b57ce..10ac81b08d 100644 --- a/api/texts_api.py +++ b/api/texts_api.py @@ -61,7 +61,7 @@ def _append_required_versions(self, lang: str, vtitle=None) -> None: if vtitle != self.ALL and versions: versions = [max(versions, key=lambda v: v['priority'] or 0)] for version in versions: - if version not in self.return_obj['versions']: + if version not in self.return_obj['versions']: #do not return the same version even if included in two different version params self.return_obj['versions'].append(version) if not versions: self._handle_errors(lang, vtitle) From 3268846cccfb7f1ff4c04d1014f3429283015353 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Wed, 21 Jun 2023 10:50:43 +0300 Subject: [PATCH 041/756] refactor(api texts): change names of adding data functions. --- api/texts_api.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/api/texts_api.py b/api/texts_api.py index 10ac81b08d..f67e88efa5 100644 --- a/api/texts_api.py +++ b/api/texts_api.py @@ -80,7 +80,7 @@ def _add_text_to_versions(self) -> None: version.pop('language') #should be removed after language is removed from attrs version['text'] = TextRange(self.oref, version['actualLanguage'], version['versionTitle']).text - def _add_ref_data(self) -> None: + def _add_ref_data_to_return_obj(self) -> None: oref = self.oref self.return_obj.update({ 'ref': oref.normal(), @@ -154,7 +154,7 @@ def _reduce_alts_to_ref(self) -> dict: #TODO - copied from TextFamily. if we won return alts_ja.array() - def _add_index_data(self) -> None: + def _add_index_data_to_return_obj(self) -> None: index = self.oref.index self.return_obj.update({ 'indexTitle': index.title, @@ -168,7 +168,7 @@ def _add_index_data(self) -> None: 'alts': self._reduce_alts_to_ref(), }) - def _add_node_data(self) -> None: + def _add_node_data_to_return_obj(self) -> None: inode = self.oref.index_node if getattr(inode, "lengths", None): self.return_obj["lengths"] = getattr(inode, "lengths") @@ -191,7 +191,7 @@ def get_versions_for_query(self) -> dict: self.current_params = version_params self._handle_version_params(version_params) self._add_text_to_versions() - self._add_ref_data() - self._add_index_data() - self._add_node_data() + self._add_ref_data_to_return_obj() + self._add_index_data_to_return_obj() + self._add_node_data_to_return_obj() return self.return_obj From a40741fa8515ea025ae417ab5edf4ff4840e7842 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Wed, 21 Jun 2023 10:56:40 +0300 Subject: [PATCH 042/756] docs(api texts): status codes of get_texts. --- api/views.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/api/views.py b/api/views.py index 66e66de424..aee1cbd2c7 100644 --- a/api/views.py +++ b/api/views.py @@ -6,6 +6,13 @@ def get_texts(request, tref): + """ + handle text request based on ref and query params + status codes: + 400 - for invalid ref or empty ref (i.e. has no text in any language) + 405 - unsuppored method + 200 - any other case. when requested version doesn't exist the returned object will include the message + """ try: oref = Ref.instantiate_ref_with_legacy_parse_fallback(tref) except Exception as e: @@ -20,4 +27,4 @@ def get_texts(request, tref): handler = APITextsHandler(oref, versions_params) data = handler.get_versions_for_query() return jsonResponse(data, cb) - return jsonResponse({"error": "Unsupported HTTP method."}, cb) + return jsonResponse({"error": "Unsupported HTTP method."}, cbdocs) From f3f2b525e85064cb426bf465d3d1f2365bb50828 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Wed, 21 Jun 2023 10:58:13 +0300 Subject: [PATCH 043/756] fix(api texts): status code 405. --- api/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/views.py b/api/views.py index aee1cbd2c7..4895d1446f 100644 --- a/api/views.py +++ b/api/views.py @@ -27,4 +27,4 @@ def get_texts(request, tref): handler = APITextsHandler(oref, versions_params) data = handler.get_versions_for_query() return jsonResponse(data, cb) - return jsonResponse({"error": "Unsupported HTTP method."}, cbdocs) + return jsonResponse({"error": "Unsupported HTTP method."}, cb, 405) From 1031eb1bddf7c148433dd9012d8cb49f132e4a16 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Wed, 21 Jun 2023 10:59:08 +0300 Subject: [PATCH 044/756] refactor(api texts): use jsonResponse rather than HttpResponseBadRequest. --- api/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/views.py b/api/views.py index 4895d1446f..c9802aebda 100644 --- a/api/views.py +++ b/api/views.py @@ -16,9 +16,9 @@ def get_texts(request, tref): try: oref = Ref.instantiate_ref_with_legacy_parse_fallback(tref) except Exception as e: - return HttpResponseBadRequest(json.dumps({'error': getattr(e, 'message', str(e))}, ensure_ascii=False)) + return jsonResponse({'error': getattr(e, 'message', str(e))}, 400) if oref.is_empty(): - return HttpResponseBadRequest(json.dumps({'error': f'We have no text for {oref}.'}, ensure_ascii=False)) + return jsonResponse({'error': f'We have no text for {oref}.'}, 400) cb = request.GET.get("callback", None) if request.method == "GET": versions_params = request.GET.getlist('version', []) From 227b2217c01d28478ad4d15391ee4b6fa09bb54d Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Wed, 21 Jun 2023 11:04:02 +0300 Subject: [PATCH 045/756] docs(Ref): version_list returns nore than what was documented. --- sefaria/model/text.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index 8659b4750a..c79e941829 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -4605,13 +4605,13 @@ def versionset(self, lang=None): def version_list(self): """ - A list of available text versions titles and languages matching this ref. + A list of available text versions metadata matching this ref. If this ref is book level, decorate with the first available section of content per version. :return list: each list element is an object with keys 'versionTitle' and 'language' """ fields = Version.optional_attrs + Version.required_attrs - fields.remove('chapter') + fields.remove('chapter') # not metadata versions = VersionSet(self.condition_query()) version_list = [] if self.is_book_level(): From 9b96175527714a0185b98da17bff29fb126f9982 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Wed, 21 Jun 2023 11:12:04 +0300 Subject: [PATCH 046/756] fix(api texts): passing params to jsonResponse correctly. --- api/views.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/views.py b/api/views.py index c9802aebda..dc07ea7a91 100644 --- a/api/views.py +++ b/api/views.py @@ -16,9 +16,9 @@ def get_texts(request, tref): try: oref = Ref.instantiate_ref_with_legacy_parse_fallback(tref) except Exception as e: - return jsonResponse({'error': getattr(e, 'message', str(e))}, 400) + return jsonResponse({'error': getattr(e, 'message', str(e))}, status=400) if oref.is_empty(): - return jsonResponse({'error': f'We have no text for {oref}.'}, 400) + return jsonResponse({'error': f'We have no text for {oref}.'}, status=400) cb = request.GET.get("callback", None) if request.method == "GET": versions_params = request.GET.getlist('version', []) @@ -27,4 +27,4 @@ def get_texts(request, tref): handler = APITextsHandler(oref, versions_params) data = handler.get_versions_for_query() return jsonResponse(data, cb) - return jsonResponse({"error": "Unsupported HTTP method."}, cb, 405) + return jsonResponse({"error": "Unsupported HTTP method."}, cb, status=405) From 1b655c1925c6ba2a7ade63802cd324b9479a0e38 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Wed, 21 Jun 2023 11:17:37 +0300 Subject: [PATCH 047/756] fix(api texts): split_at_pipe_with_default for more than 1 pipe. --- api/helper.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/helper.py b/api/helper.py index aa9b781263..ea93aae014 100644 --- a/api/helper.py +++ b/api/helper.py @@ -3,6 +3,6 @@ def split_at_pipe_with_default(string: str, list_length: int, defaults: List[str]) -> List[str]: #length of defaults should be one less than list_length substrings = string.split('|', list_length-1) - while len(substrings) < list_length: - substrings.append(defaults.pop()) + if len(substrings) < list_length: + substrings += defaults[len(substrings)-list_length:] return substrings From 5b2a018ba2e1e3a13c890299faab9ee7057d1fb6 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Wed, 21 Jun 2023 11:20:50 +0300 Subject: [PATCH 048/756] docs(api texts): docs for split_at_pipe_with_default. --- api/helper.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/api/helper.py b/api/helper.py index ea93aae014..29551c806e 100644 --- a/api/helper.py +++ b/api/helper.py @@ -1,7 +1,13 @@ from typing import List def split_at_pipe_with_default(string: str, list_length: int, defaults: List[str]) -> List[str]: - #length of defaults should be one less than list_length + """ + split a string of query params into list of params by pipe. filling the list with defaults when there are not enough + :param string: + :param list_length: the required length of a parameters list + :param defaults: a list of default strings for potentially missing parameters + :return: list of parematers + """ substrings = string.split('|', list_length-1) if len(substrings) < list_length: substrings += defaults[len(substrings)-list_length:] From 76430e22d868903683a9a35ed3cbd9c0e98e81f7 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Wed, 21 Jun 2023 11:40:02 +0300 Subject: [PATCH 049/756] refacot(api texts): move the logics that trims alt_structs from TextFamily and new texts_api to Index. --- api/texts_api.py | 55 +--------------------- sefaria/model/text.py | 105 ++++++++++++++++++++---------------------- 2 files changed, 52 insertions(+), 108 deletions(-) diff --git a/api/texts_api.py b/api/texts_api.py index f67e88efa5..9417e26c11 100644 --- a/api/texts_api.py +++ b/api/texts_api.py @@ -101,59 +101,6 @@ def _add_ref_data_to_return_obj(self) -> None: 'type': oref.primary_category, #same as primary category }) - def _reduce_alts_to_ref(self) -> dict: #TODO - copied from TextFamily. if we won't remove it, we should find some place for that - """ - this function takes the index's alt_structs and reduce it to the relevant ref - it is necessary for the client side - """ - oref = self.oref - # Set up empty Array that mirrors text structure - alts_ja = JaggedArray() - for key, struct in oref.index.get_alt_structures().items(): - # Assuming these are in order, continue if it is before ours, break if we see one after - for n in struct.get_leaf_nodes(): - wholeRef = Ref(n.wholeRef).default_child_ref().as_ranged_segment_ref() - if wholeRef.ending_ref().precedes(oref): - continue - if wholeRef.starting_ref().follows(oref): - break - - # It's in our territory - wholeRefStart = wholeRef.starting_ref() - if oref.contains(wholeRefStart) and not wholeRefStart.contains(oref): - indxs = [k - 1 for k in wholeRefStart.in_terms_of(oref)] - val = {"en": [], "he": []} - try: - val = alts_ja.get_element(indxs) or val - except IndexError: - pass - val["en"] += [n.primary_title("en")] - val["he"] += [n.primary_title("he")] - val["whole"] = True - alts_ja.set_element(indxs, val) - - if getattr(n, "refs", None): - for i, r in enumerate(n.refs): - # hack to skip Rishon, skip empty refs - if i == 0 or not r: - continue - subRef = Ref(r) - subRefStart = subRef.starting_ref() - if oref.contains(subRefStart) and not subRefStart.contains(oref): - indxs = [k - 1 for k in subRefStart.in_terms_of(oref)] - val = {"en": [], "he": []} - try: - val = alts_ja.get_element(indxs) or val - except IndexError: - pass - val["en"] += [n.sectionString([i + 1], "en", title=False)] - val["he"] += [n.sectionString([i + 1], "he", title=False)] - alts_ja.set_element(indxs, val) - elif subRefStart.follows(oref): - break - - return alts_ja.array() - def _add_index_data_to_return_obj(self) -> None: index = self.oref.index self.return_obj.update({ @@ -165,7 +112,7 @@ def _add_index_data_to_return_obj(self) -> None: 'order': getattr(index, 'order', ''), 'collectiveTitle': getattr(index, 'collective_title', ''), 'heCollectiveTitle': hebrew_term(getattr(index, 'collective_title', '')), - 'alts': self._reduce_alts_to_ref(), + 'alts': index.get_trimmed_alt_structs_for_ref(self.oref), }) def _add_node_data_to_return_obj(self) -> None: diff --git a/sefaria/model/text.py b/sefaria/model/text.py index c79e941829..058124575d 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -420,6 +420,56 @@ def alt_struct_nodes_helper(node, nodes): alt_struct_nodes_helper(node, nodes) return nodes + def get_trimmed_alt_structs_for_ref(self, oref) -> dict: + """ + this function takes the index's alt_structs and reduce it to the relevant ref + """ + # Set up empty Array that mirrors text structure + alts_ja = JaggedArray() + for key, struct in self.get_alt_structures().items(): + # Assuming these are in order, continue if it is before ours, break if we see one after + for n in struct.get_leaf_nodes(): + wholeRef = Ref(n.wholeRef).default_child_ref().as_ranged_segment_ref() + if wholeRef.ending_ref().precedes(oref): + continue + if wholeRef.starting_ref().follows(oref): + break + + # It's in our territory + wholeRefStart = wholeRef.starting_ref() + if oref.contains(wholeRefStart) and not wholeRefStart.contains(oref): + indxs = [k - 1 for k in wholeRefStart.in_terms_of(oref)] + val = {"en": [], "he": []} + try: + val = alts_ja.get_element(indxs) or val + except IndexError: + pass + val["en"] += [n.primary_title("en")] + val["he"] += [n.primary_title("he")] + val["whole"] = True + alts_ja.set_element(indxs, val) + + if getattr(n, "refs", None): + for i, r in enumerate(n.refs): + # hack to skip Rishon, skip empty refs + if i == 0 or not r: + continue + subRef = Ref(r) + subRefStart = subRef.starting_ref() + if oref.contains(subRefStart) and not subRefStart.contains(oref): + indxs = [k - 1 for k in subRefStart.in_terms_of(oref)] + val = {"en": [], "he": []} + try: + val = alts_ja.get_element(indxs) or val + except IndexError: + pass + val["en"] += [n.sectionString([i + 1], "en", title=False)] + val["he"] += [n.sectionString([i + 1], "he", title=False)] + alts_ja.set_element(indxs, val) + elif subRefStart.follows(oref): + break + + return alts_ja.array() def composition_place(self): from . import place @@ -2479,60 +2529,7 @@ def __init__(self, oref, context=1, commentary=True, version=None, lang=None, ve # Adds decoration for the start of each alt structure reference if alts: - # Set up empty Array that mirrors text structure - alts_ja = JaggedArray() - - for key, struct in oref.index.get_alt_structures().items(): - # Assuming these are in order, continue if it is before ours, break if we see one after - for n in struct.get_leaf_nodes(): - wholeRef = Ref(n.wholeRef).default_child_ref().as_ranged_segment_ref() - if wholeRef.ending_ref().precedes(oref): - continue - if wholeRef.starting_ref().follows(oref): - break - - #It's in our territory - wholeRefStart = wholeRef.starting_ref() - if oref.contains(wholeRefStart) and not wholeRefStart.contains(oref): - indxs = [k - 1 for k in wholeRefStart.in_terms_of(oref)] - val = {"en":[], "he":[]} - - try: - val = alts_ja.get_element(indxs) or val - except IndexError: - pass - - val["en"] += [n.primary_title("en")] - val["he"] += [n.primary_title("he")] - val["whole"] = True - - alts_ja.set_element(indxs, val) - - if getattr(n, "refs", None): - for i, r in enumerate(n.refs): - # hack to skip Rishon, skip empty refs - if i == 0 or not r: - continue - subRef = Ref(r) - subRefStart = subRef.starting_ref() - if oref.contains(subRefStart) and not subRefStart.contains(oref): - indxs = [k - 1 for k in subRefStart.in_terms_of(oref)] - val = {"en":[], "he":[]} - - try: - val = alts_ja.get_element(indxs) or val - except IndexError: - pass - - val["en"] += [n.sectionString([i + 1], "en", title=False)] - val["he"] += [n.sectionString([i + 1], "he", title=False)] - - alts_ja.set_element(indxs, val) - - elif subRefStart.follows(oref): - break - - self._alts = alts_ja.array() + self._alts = oref.index.get_trimmed_alt_structs_for_ref(oref) if self._inode.is_virtual: self._index_offsets_by_depth = None else: From ffb86466a02af28f6f594cf9337590a935e4f1ce Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Wed, 21 Jun 2023 11:55:33 +0300 Subject: [PATCH 050/756] chore(api texts): tests for split_at_pipe_with_default. --- api/tests.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/api/tests.py b/api/tests.py index 748583c993..089329ec99 100644 --- a/api/tests.py +++ b/api/tests.py @@ -3,9 +3,26 @@ django.setup() from reader.tests import SefariaTestCase import json +from api.helper import split_at_pipe_with_default + + +class HelperTests(SefariaTestCase): + def test_split_at_pipe_with_default(self): + for string, list_length, default, expected in [ + ('he|foo bar', 2, [], ['he', 'foo bar']), + ('he|foo bar', 2, ['baz'], ['he', 'foo bar']), + ('he', 2, ['baz'], ['he', 'baz']), + ('he|foo bar|baz', 3, [], ['he', 'foo bar', 'baz']), + ('he|foo bar|baz', 3, ['blue'], ['he', 'foo bar', 'baz']), + ('he|foo bar', 3, ['baz'], ['he', 'foo bar', 'baz']), + ('he', 3, ['foo', 'baz'], ['he', 'foo', 'baz']), + ]: + self.assertEqual(expected, split_at_pipe_with_default(string, list_length, default)) + c = Client() + class APITextsTests(SefariaTestCase): def test_api_get_text_default(self): From bb35e34fdad27eaf466082b91f1b05da1824cb60 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Tue, 18 Jul 2023 12:18:08 +0300 Subject: [PATCH 051/756] refactor(api texts): change names in split_at_pipe_with_default --- api/helper.py | 12 ++++++------ api/tests.py | 4 ++-- api/texts_api.py | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/api/helper.py b/api/helper.py index 29551c806e..070e39e81a 100644 --- a/api/helper.py +++ b/api/helper.py @@ -1,14 +1,14 @@ from typing import List -def split_at_pipe_with_default(string: str, list_length: int, defaults: List[str]) -> List[str]: +def split_query_param_and_add_defaults(query_string: str, list_length: int, defaults: List[str]) -> List[str]: """ split a string of query params into list of params by pipe. filling the list with defaults when there are not enough - :param string: + :param query_string: :param list_length: the required length of a parameters list :param defaults: a list of default strings for potentially missing parameters :return: list of parematers """ - substrings = string.split('|', list_length-1) - if len(substrings) < list_length: - substrings += defaults[len(substrings)-list_length:] - return substrings + params = query_string.split('|', list_length - 1) + if len(params) < list_length: + params += defaults[len(params)-list_length:] + return params diff --git a/api/tests.py b/api/tests.py index 089329ec99..b04d269ac6 100644 --- a/api/tests.py +++ b/api/tests.py @@ -3,7 +3,7 @@ django.setup() from reader.tests import SefariaTestCase import json -from api.helper import split_at_pipe_with_default +from api.helper import split_query_param_and_add_defaults class HelperTests(SefariaTestCase): @@ -17,7 +17,7 @@ def test_split_at_pipe_with_default(self): ('he|foo bar', 3, ['baz'], ['he', 'foo bar', 'baz']), ('he', 3, ['foo', 'baz'], ['he', 'foo', 'baz']), ]: - self.assertEqual(expected, split_at_pipe_with_default(string, list_length, default)) + self.assertEqual(expected, split_query_param_and_add_defaults(string, list_length, default)) c = Client() diff --git a/api/texts_api.py b/api/texts_api.py index 9417e26c11..890d0da3e6 100644 --- a/api/texts_api.py +++ b/api/texts_api.py @@ -3,7 +3,7 @@ from sefaria.datatype.jagged_array import JaggedArray from sefaria.utils.hebrew import hebrew_term from .api_errors import * -from .helper import split_at_pipe_with_default +from .helper import split_query_param_and_add_defaults from typing import List class APITextsHandler(): @@ -69,7 +69,7 @@ def _append_required_versions(self, lang: str, vtitle=None) -> None: def _handle_version_params(self, version_params: str) -> None: if version_params in self.handled_version_params: return - lang, vtitle = split_at_pipe_with_default(version_params, 2, ['']) + lang, vtitle = split_query_param_and_add_defaults(version_params, 2, ['']) vtitle = vtitle.replace('_', ' ') self._append_required_versions(lang, vtitle) self.handled_version_params.append(version_params) From f947614a04cd301f33171a3d77b0206dc9da0daa Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Tue, 18 Jul 2023 14:45:55 +0300 Subject: [PATCH 052/756] fix(search): update to use total.value based on new datastructure returned from ES 8 --- static/js/SearchResultList.jsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/static/js/SearchResultList.jsx b/static/js/SearchResultList.jsx index fcce157f83..6b3f15c70b 100644 --- a/static/js/SearchResultList.jsx +++ b/static/js/SearchResultList.jsx @@ -104,7 +104,7 @@ class SearchResultList extends Component { //console.log("Loaded cached query for") //console.log(args); this.state.hits[t] = this.state.hits[t].concat(cachedQuery.hits.hits); - this.state.totals[t] = cachedQuery.hits.total; + this.state.totals[t] = cachedQuery.hits.total.value; this.state.pagesLoaded[t] += 1; args.start = this.state.pagesLoaded[t] * this.querySize[t]; if (t === "text") { @@ -326,9 +326,9 @@ class SearchResultList extends Component { if (this.state.pagesLoaded[type] === 0) { // Skip if pages have already been loaded from cache, but let aggregation processing below occur let state = { hits: extend(this.state.hits, {[type]: data.hits.hits}), - totals: extend(this.state.totals, {[type]: data.hits.total}), + totals: extend(this.state.totals, {[type]: data.hits.total.value}), pagesLoaded: extend(this.state.pagesLoaded, {[type]: 1}), - moreToLoad: extend(this.state.moreToLoad, {[type]: data.hits.total > this.querySize[type]}) + moreToLoad: extend(this.state.moreToLoad, {[type]: data.hits.total.value > this.querySize[type]}) }; this.setState(state, () => { this.updateTotalResults(); @@ -336,7 +336,7 @@ class SearchResultList extends Component { }); const filter_label = (request_applied && request_applied.length > 0) ? (' - ' + request_applied.join('|')) : ''; const query_label = props.query + filter_label; - Sefaria.track.event("Search", `${this.props.searchInBook? "SidebarSearch ": ""}Query: ${type}`, query_label, data.hits.total); + Sefaria.track.event("Search", `${this.props.searchInBook? "SidebarSearch ": ""}Query: ${type}`, query_label, data.hits.total.value); } if (data.aggregations) { From 00671fa5956c329de81b1e4017535cd06a571c1a Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Tue, 18 Jul 2023 14:47:51 +0300 Subject: [PATCH 053/756] chore(search): update elasticsearch to 8 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 171b058cca..a2563e400b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -22,7 +22,7 @@ django==1.11.* djangorestframework @ https://github.com/encode/django-rest-framework/archive/3.11.1.tar.gz djangorestframework_simplejwt==3.3.0 PyJWT==1.7.1 # pinned b/c current version 2.0.0 breaks simplejwt. waiting for 2.0.1 -elasticsearch==7.9.1 +elasticsearch==8.8.2 elasticsearch_dsl==7.3.0 geojson==2.5.0 gevent==20.9.0 From 80d47469ba1f8bba9d09e61293abd45910a1907c Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Tue, 18 Jul 2023 14:49:35 +0300 Subject: [PATCH 054/756] chore(search): make search.py compatible with ES 7 --- sefaria/search.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/sefaria/search.py b/sefaria/search.py index ecffc85a31..e98e488e13 100644 --- a/sefaria/search.py +++ b/sefaria/search.py @@ -52,7 +52,7 @@ def delete_text(oref, version, lang): curr_index = get_new_and_current_index_names('text')['current'] id = make_text_doc_id(oref.normal(), version, lang) - es_client.delete(index=curr_index, doc_type='text', id=id) + es_client.delete(index=curr_index, id=id) except Exception as e: logger.error("ERROR deleting {} / {} / {} : {}".format(oref.normal(), version, lang, e)) @@ -76,7 +76,7 @@ def delete_version(index, version, lang): def delete_sheet(index_name, id): try: - es_client.delete(index=index_name, doc_type='sheet', id=id) + es_client.delete(index=index_name, id=id) except Exception as e: logger.error("ERROR deleting sheet {}".format(id)) @@ -147,7 +147,7 @@ def index_sheet(index_name, id): "dateModified": sheet.get("dateModified", None), "views": sheet.get("views", 0) } - es_client.create(index=index_name, doc_type='sheet', id=id, body=doc) + es_client.create(index=index_name, id=id, body=doc) global doc_count doc_count += 1 return True @@ -220,7 +220,6 @@ def get_exact_english_analyzer(): "icu_normalizer", ], "filter": [ - "standard", "lowercase", "icu_folding", ], @@ -259,7 +258,7 @@ def create_index(index_name, type): } } print('Creating index {}'.format(index_name)) - index_client.create(index=index_name, body=settings) + index_client.create(index=index_name, settings=settings) if type == 'text': put_text_mapping(index_name) @@ -326,7 +325,7 @@ def put_text_mapping(index_name): } } } - index_client.put_mapping(doc_type='text', body=text_mapping, index=index_name) + index_client.put_mapping(body=text_mapping, index=index_name) def put_sheet_mapping(index_name): @@ -392,7 +391,7 @@ def put_sheet_mapping(index_name): } } } - index_client.put_mapping(doc_type='sheet', body=sheet_mapping, index=index_name) + index_client.put_mapping(body=sheet_mapping, index=index_name) def get_search_categories(oref, categories): toc_tree = library.get_toc_tree() @@ -593,7 +592,6 @@ def _cache_action(cls, segment_str, tref, heTref, version): cls._bulk_actions += [ { "_index": cls.index_name, - "_type": "text", "_id": make_text_doc_id(tref, vtitle, vlang), "_source": doc } From aa112e61acd12558256d3e2b5248b5b1ee5cd437 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Tue, 18 Jul 2023 14:50:13 +0300 Subject: [PATCH 055/756] chore(search): make search_wrapper_api compatible with ES 8 --- reader/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reader/views.py b/reader/views.py index 88847aed47..e80dbb868b 100644 --- a/reader/views.py +++ b/reader/views.py @@ -4228,7 +4228,7 @@ def search_wrapper_api(request): search_obj = get_query_obj(search_obj=search_obj, **j) response = search_obj.execute() if response.success(): - return jsonResponse(response.to_dict(), callback=request.GET.get("callback", None)) + return jsonResponse(response.to_dict().body, callback=request.GET.get("callback", None)) return jsonResponse({"error": "Error with connection to Elasticsearch. Total shards: {}, Shards successful: {}, Timed out: {}".format(response._shards.total, response._shards.successful, response.timed_out)}, callback=request.GET.get("callback", None)) return jsonResponse({"error": "Unsupported HTTP method."}, callback=request.GET.get("callback", None)) From 95d3d8773a98582e54f1ead2987067e316c3a3b5 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Sun, 23 Jul 2023 09:36:25 +0300 Subject: [PATCH 056/756] refactor(api texts): change api_v3 to bi_directional_translation_views. --- sefaria/urls.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sefaria/urls.py b/sefaria/urls.py index 02741bc089..85a3cbf398 100644 --- a/sefaria/urls.py +++ b/sefaria/urls.py @@ -13,7 +13,7 @@ import sourcesheets.views as sheets_views import sefaria.gauth.views as gauth_views import django.contrib.auth.views as django_auth_views -import api.views as v3_views +import api.views as bi_directional_translation_views from sefaria.site.urls import site_urlpatterns @@ -147,7 +147,7 @@ url(r'^api/texts/modify-bulk/(?P<title>.+)$', reader_views.modify_bulk_text_api), url(r'^api/texts/(?P<tref>.+)/(?P<lang>\w\w)/(?P<version>.+)$', reader_views.old_text_versions_api_redirect), url(r'^api/texts/(?P<tref>.+)$', reader_views.texts_api), - url(r'^api/v3/texts/(?P<tref>.+)$', v3_views.get_texts), + url(r'^api/v3/texts/(?P<tref>.+)$', bi_directional_translation_views.get_texts), url(r'^api/index/?$', reader_views.table_of_contents_api), url(r'^api/opensearch-suggestions/?$', reader_views.opensearch_suggestions_api), url(r'^api/index/titles/?$', reader_views.text_titles_api), From f5832daa5958d6c44a6a400d1c54e360b2ade6bf Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 24 Aug 2022 20:24:16 +0300 Subject: [PATCH 057/756] feat(search): connect to ES using a password --- reader/views.py | 4 +++- sefaria/helper/search.py | 20 ++++++++++++++++++++ sefaria/local_settings_example.py | 6 +++++- sefaria/search.py | 6 +++--- 4 files changed, 31 insertions(+), 5 deletions(-) diff --git a/reader/views.py b/reader/views.py index e80dbb868b..c6fbcae3a6 100644 --- a/reader/views.py +++ b/reader/views.py @@ -4217,13 +4217,15 @@ def dummy_search_api(request): @csrf_exempt def search_wrapper_api(request): + from sefaria.helper.search import get_elasticsearch_client + if request.method == "POST": if "json" in request.POST: j = request.POST.get("json") # using form-urlencoded else: j = request.body # using content-type: application/json j = json.loads(j) - es_client = Elasticsearch(SEARCH_ADMIN) + es_client = get_elasticsearch_client(admin=False) search_obj = Search(using=es_client, index=j.get("type")).params(request_timeout=5) search_obj = get_query_obj(search_obj=search_obj, **j) response = search_obj.execute() diff --git a/sefaria/helper/search.py b/sefaria/helper/search.py index 36aef9eb3e..351af78060 100644 --- a/sefaria/helper/search.py +++ b/sefaria/helper/search.py @@ -144,3 +144,23 @@ def make_filter(type, agg_type, agg_key): return Regexp(path=reg) elif type == "sheet": return Term(**{agg_type: agg_key}) + + +def _get_es_server_url(admin=False): + from sefaria.settings import SEARCH_ADMIN, SEARCH_ADMIN_PW, SEARCH_ADMIN_USER, SEARCH_NON_ADMIN + base_url = SEARCH_ADMIN if admin else SEARCH_NON_ADMIN + if SEARCH_ADMIN_USER: + match = re.search(r'^(https?://)(.*)$', base_url) + if match: + http, base_url = match.group(1), match.group(2) + else: + http, base_url = "http", base_url + es_url = f"{http}{SEARCH_ADMIN_USER}:{SEARCH_ADMIN_PW}@{base_url}" + else: + es_url = base_url + return es_url + + +def get_elasticsearch_client(admin=False): + from elasticsearch import Elasticsearch + return Elasticsearch(_get_es_server_url(admin=admin)) diff --git a/sefaria/local_settings_example.py b/sefaria/local_settings_example.py index d9ffc34589..e5e18a6b2e 100644 --- a/sefaria/local_settings_example.py +++ b/sefaria/local_settings_example.py @@ -158,7 +158,11 @@ APSCHEDULER_NAME = "apscheduler" # ElasticSearch server -SEARCH_ADMIN = "http://localhost:9200" +SEARCH_PASSWORD = "" +SEARCH_ADMIN = "localhost:9200" # URL to connect to internal ES server for admin access. Leave off https:// +SEARCH_NON_ADMIN = "http://localhost:9200/api/search" # URL to connect to ES for searching. Can be /api/search Django endpoint which gets proxied to ES server. +SEARCH_ADMIN_PW = None # Optional password to connect to ES server. If no password, leave as `None` +SEARCH_ADMIN_USER = None # Optional user to connect to ES server. If no user, leave as `None` SEARCH_INDEX_ON_SAVE = False # Whether to send texts and source sheet to Search Host for indexing after save SEARCH_INDEX_NAME_TEXT = 'text' # name of the ElasticSearch index to use SEARCH_INDEX_NAME_SHEET = 'sheet' diff --git a/sefaria/search.py b/sefaria/search.py index e98e488e13..b464f365de 100644 --- a/sefaria/search.py +++ b/sefaria/search.py @@ -20,7 +20,6 @@ import time as pytime logger = structlog.get_logger(__name__) -from elasticsearch import Elasticsearch from elasticsearch.client import IndicesClient from elasticsearch.helpers import bulk from elasticsearch.exceptions import NotFoundError @@ -31,12 +30,13 @@ from sefaria.system.database import db from sefaria.system.exceptions import InputError from sefaria.utils.util import strip_tags -from .settings import SEARCH_ADMIN, SEARCH_INDEX_NAME_TEXT, SEARCH_INDEX_NAME_SHEET, STATICFILES_DIRS +from .settings import SEARCH_INDEX_NAME_TEXT, SEARCH_INDEX_NAME_SHEET +from sefaria.helper.search import get_elasticsearch_client from sefaria.site.site_settings import SITE_SETTINGS from sefaria.utils.hebrew import strip_cantillation import sefaria.model.queue as qu -es_client = Elasticsearch(SEARCH_ADMIN) +es_client = get_elasticsearch_client(admin=True) index_client = IndicesClient(es_client) tracer = structlog.get_logger(__name__) From aa3dfc6c9df1d9f7767a3686a36c7146a64e4adb Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Tue, 25 Jul 2023 11:46:02 +0300 Subject: [PATCH 058/756] refactor(api texts): a new class for handling versions params. --- api/texts_api.py | 66 +++++++++++++++++++++++++++++++++--------------- api/views.py | 7 +++-- 2 files changed, 48 insertions(+), 25 deletions(-) diff --git a/api/texts_api.py b/api/texts_api.py index 890d0da3e6..5e149ffc15 100644 --- a/api/texts_api.py +++ b/api/texts_api.py @@ -1,22 +1,54 @@ import django django.setup() -from sefaria.datatype.jagged_array import JaggedArray from sefaria.utils.hebrew import hebrew_term from .api_errors import * from .helper import split_query_param_and_add_defaults from typing import List -class APITextsHandler(): + +class VersionsParams(): """ - process api calls for text - an api call contains ref and list of version_params - a version params is string divided by pipe as 'lang|vtitle' + an object for managing the versions params for TextsHandler + params can come from an API request or internal (sever side rendering) lang is our code language. special values are: source - for versions in the source language of the text base - as source but with falling to the 'nearest' to source, or what we have defined as such vtitle is the exact versionTitle. special values are: no vtitle - the version with the max priority attr of the specified language all - all versions of the specified language + representing_string is the original string that came from an API call + """ + + def __init__(self, lang: str, vtitle: str, representing_string=''): + self.lang = lang + self.vtitle = vtitle + self.representing_string = representing_string + if not self.representing_string: + self.representing_string = f'{self.lang}|{self.representing_string}' + + def __eq__(self, other): + return isinstance(other, VersionsParams) and self.lang == other.lang and self.vtitle == other.vtitle + + @staticmethod + def parse_api_params(version_params): + """ + an api call contains ref and list of version_params + a version params is string divided by pipe as 'lang|vtitle' + this function takes the list of version_params and returns list of VersionsParams + """ + version_params_list = [] + for params_string in version_params: + lang, vtitle = split_query_param_and_add_defaults(params_string, 2, ['']) + vtitle = vtitle.replace('_', ' ') + version_params = VersionsParams(lang, vtitle, params_string) + if version_params not in version_params_list: + version_params_list.append(version_params) + return version_params_list + + +class TextsForClientHandler(): + """ + process api calls for text return_obj is dict that includes in its root: ref and index data 'versions' - list of versions details and text @@ -27,15 +59,15 @@ class APITextsHandler(): BASE = 'base' SOURCE = 'source' - def __init__(self, oref: Ref, versions_params: List[str]): + def __init__(self, oref: Ref, versions_params: List[VersionsParams]): self.versions_params = versions_params - self.current_params = '' self.oref = oref self.handled_version_params = [] self.all_versions = self.oref.version_list() self.return_obj = {'versions': [], 'errors': []} - def _handle_errors(self, lang: str, vtitle: str) -> None: + def _handle_errors(self, version_params: VersionsParams) -> None: + lang, vtitle = version_params.lang, version_params.vtitle if lang == self.SOURCE: error = APINoSourceText(self.oref) elif vtitle and vtitle != self.ALL: @@ -44,10 +76,11 @@ def _handle_errors(self, lang: str, vtitle: str) -> None: availabe_langs = {v['actualLanguage'] for v in self.all_versions} error = APINoLanguageVersion(self.oref, sorted(availabe_langs)) self.return_obj['errors'].append({ - self.current_params: error.get_dict() + version_params.representing_string: error.get_dict() }) - def _append_required_versions(self, lang: str, vtitle=None) -> None: + def _append_required_versions(self, version_params: VersionsParams) -> None: + lang, vtitle = version_params.lang, version_params.vtitle if lang == self.BASE: lang_condition = lambda v: v['isBaseText2'] #temporal name elif lang == self.SOURCE: @@ -64,15 +97,7 @@ def _append_required_versions(self, lang: str, vtitle=None) -> None: if version not in self.return_obj['versions']: #do not return the same version even if included in two different version params self.return_obj['versions'].append(version) if not versions: - self._handle_errors(lang, vtitle) - - def _handle_version_params(self, version_params: str) -> None: - if version_params in self.handled_version_params: - return - lang, vtitle = split_query_param_and_add_defaults(version_params, 2, ['']) - vtitle = vtitle.replace('_', ' ') - self._append_required_versions(lang, vtitle) - self.handled_version_params.append(version_params) + self._handle_errors(version_params) def _add_text_to_versions(self) -> None: for version in self.return_obj['versions']: @@ -135,8 +160,7 @@ def _add_node_data_to_return_obj(self) -> None: def get_versions_for_query(self) -> dict: for version_params in self.versions_params: - self.current_params = version_params - self._handle_version_params(version_params) + self._append_required_versions(version_params) self._add_text_to_versions() self._add_ref_data_to_return_obj() self._add_index_data_to_return_obj() diff --git a/api/views.py b/api/views.py index dc07ea7a91..fa12e610d5 100644 --- a/api/views.py +++ b/api/views.py @@ -1,7 +1,5 @@ -import json -from django.http import HttpResponseBadRequest from sefaria.model import * -from .texts_api import APITextsHandler +from .texts_api import TextsForClientHandler, VersionsParams from sefaria.client.util import jsonResponse @@ -24,7 +22,8 @@ def get_texts(request, tref): versions_params = request.GET.getlist('version', []) if not versions_params: versions_params = ['base'] - handler = APITextsHandler(oref, versions_params) + versions_params = VersionsParams.parse_api_params(versions_params) + handler = TextsForClientHandler(oref, versions_params) data = handler.get_versions_for_query() return jsonResponse(data, cb) return jsonResponse({"error": "Unsupported HTTP method."}, cb, status=405) From 554c82dda8cbd47d850c170644c64d0a2bd1ae8b Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Tue, 25 Jul 2023 12:15:24 +0300 Subject: [PATCH 059/756] chore(search): change to a fork of elasticsearch_dsl which adds support for ES 8 while we wait for the ES team to release a new version --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index a2563e400b..80b4f847a1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -23,7 +23,7 @@ djangorestframework @ https://github.com/encode/django-rest-framework/archive/3. djangorestframework_simplejwt==3.3.0 PyJWT==1.7.1 # pinned b/c current version 2.0.0 breaks simplejwt. waiting for 2.0.1 elasticsearch==8.8.2 -elasticsearch_dsl==7.3.0 +git+https://github.com/Sefaria/elasticsearch-dsl-py@v8.0.0#egg=elasticsearch-dsl geojson==2.5.0 gevent==20.9.0 google-api-python-client==1.12.5 From ed09ab74d4f89eae47f5abc92e14e94b3e074c62 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Tue, 25 Jul 2023 12:18:32 +0300 Subject: [PATCH 060/756] feat(api texts): option for getting version by versionTitle only (without lang). --- api/texts_api.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/api/texts_api.py b/api/texts_api.py index 5e149ffc15..d7ce9db8f0 100644 --- a/api/texts_api.py +++ b/api/texts_api.py @@ -85,8 +85,10 @@ def _append_required_versions(self, version_params: VersionsParams) -> None: lang_condition = lambda v: v['isBaseText2'] #temporal name elif lang == self.SOURCE: lang_condition = lambda v: v['isSource'] - else: + elif lang: lang_condition = lambda v: v['actualLanguage'] == lang + else: + lang_condition = lambda v: True if vtitle and vtitle != self.ALL: versions = [v for v in self.all_versions if lang_condition(v) and v['versionTitle'] == vtitle] else: From 3ffcacfde1894651dc676b32a54e1b4f0d2f9000 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 26 Jul 2023 14:52:47 +0300 Subject: [PATCH 061/756] refactor: reduce number of local settings required to connect to elastic search. Allows possibility of connecting to SEARCH_NON_ADMIN via a password --- sefaria/helper/search.py | 19 +++---------------- sefaria/local_settings_example.py | 12 +++++++----- 2 files changed, 10 insertions(+), 21 deletions(-) diff --git a/sefaria/helper/search.py b/sefaria/helper/search.py index 351af78060..f5a16300e8 100644 --- a/sefaria/helper/search.py +++ b/sefaria/helper/search.py @@ -146,21 +146,8 @@ def make_filter(type, agg_type, agg_key): return Term(**{agg_type: agg_key}) -def _get_es_server_url(admin=False): - from sefaria.settings import SEARCH_ADMIN, SEARCH_ADMIN_PW, SEARCH_ADMIN_USER, SEARCH_NON_ADMIN - base_url = SEARCH_ADMIN if admin else SEARCH_NON_ADMIN - if SEARCH_ADMIN_USER: - match = re.search(r'^(https?://)(.*)$', base_url) - if match: - http, base_url = match.group(1), match.group(2) - else: - http, base_url = "http", base_url - es_url = f"{http}{SEARCH_ADMIN_USER}:{SEARCH_ADMIN_PW}@{base_url}" - else: - es_url = base_url - return es_url - - def get_elasticsearch_client(admin=False): from elasticsearch import Elasticsearch - return Elasticsearch(_get_es_server_url(admin=admin)) + from sefaria.settings import SEARCH_ADMIN, SEARCH_NON_ADMIN + es_url = SEARCH_ADMIN if admin else SEARCH_NON_ADMIN + return Elasticsearch(es_url) diff --git a/sefaria/local_settings_example.py b/sefaria/local_settings_example.py index 8ddfd6efa2..1112abd926 100644 --- a/sefaria/local_settings_example.py +++ b/sefaria/local_settings_example.py @@ -158,11 +158,13 @@ APSCHEDULER_NAME = "apscheduler" # ElasticSearch server -SEARCH_PASSWORD = "" -SEARCH_ADMIN = "localhost:9200" # URL to connect to internal ES server for admin access. Leave off https:// -SEARCH_NON_ADMIN = "http://localhost:9200/api/search" # URL to connect to ES for searching. Can be /api/search Django endpoint which gets proxied to ES server. -SEARCH_ADMIN_PW = None # Optional password to connect to ES server. If no password, leave as `None` -SEARCH_ADMIN_USER = None # Optional user to connect to ES server. If no user, leave as `None` +# URL to connect to internal ES server for admin access. This URL is used by indexing jobs only +# If ElasticSearch server has a password use the following format: http(s)://{username}:{password}@{base_url} +SEARCH_ADMIN = "http://localhost:9200" +# URL to connect to ES for searching. Can point to /api/search Django endpoint which gets proxied to ES server. +# Set this to https://sefaria.org/api/search to connect to production search. +SEARCH_NON_ADMIN = "http://localhost:8000/api/search" + SEARCH_INDEX_ON_SAVE = False # Whether to send texts and source sheet to Search Host for indexing after save SEARCH_INDEX_NAME_TEXT = 'text' # name of the ElasticSearch index to use SEARCH_INDEX_NAME_SHEET = 'sheet' From 400ccce327171d611bead15be81fecad9a37aad6 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 26 Jul 2023 17:47:43 +0300 Subject: [PATCH 062/756] helm(search): update search local settings for elasticsearch 8 --- .../templates/configmap/local-settings-file.yaml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml b/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml index b14c650fc9..9060961403 100644 --- a/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml +++ b/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml @@ -136,14 +136,10 @@ data: SEARCH_HOST = "/api/search" SEARCH_ADMIN = os.getenv("SEARCH_ADMIN") - SEARCH_ADMIN_USER = os.getenv("SEARCH_ADMIN_USER") - SEARCH_ADMIN_PW = os.getenv("SEARCH_ADMIN_PW") - SEARCH_ADMIN_K8S = os.getenv("SEARCH_ADMIN_K8S") + SEARCH_NON_ADMIN = os.getenv("SEARCH_NON_ADMIN") SEARCH_INDEX_ON_SAVE = True - SEARCH_INDEX_NAME = "sefaria" SEARCH_INDEX_NAME_TEXT = 'text' # name of the ElasticSearch index to use SEARCH_INDEX_NAME_SHEET = 'sheet' - SEARCH_INDEX_NAME_MERGED = 'merged' TURN_SERVER = os.getenv("TURN_SERVER") #coturn.cauldron.sefaria.org TURN_SECRET= os.getenv("TURN_SECRET") From d72124de48c2e2763983e3bec2676d2324e1e06b Mon Sep 17 00:00:00 2001 From: Brendan Galloway <brendan@flanksource.com> Date: Mon, 31 Jul 2023 16:17:56 +0200 Subject: [PATCH 063/756] helm: reorder env priority for reindexing job --- .../templates/cronjob/reindex-elasticsearch.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml b/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml index 9cefe302fc..448dc84c03 100644 --- a/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml +++ b/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml @@ -47,11 +47,11 @@ spec: - secretRef: name: {{ .Values.secrets.localSettings.ref }} optional: true + - configMapRef: + name: local-settings-{{ .Values.deployEnv }} - secretRef: name: local-settings-secrets-{{ .Values.deployEnv }} optional: true - - configMapRef: - name: local-settings-{{ .Values.deployEnv }} volumeMounts: - mountPath: /app/sefaria/local_settings.py name: local-settings From 8474203de3cef38714f53e12c78e06fb21e0b444 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Wed, 2 Aug 2023 09:09:37 +0300 Subject: [PATCH 064/756] fix(text api): move condition on next ref to next ref. --- api/texts_api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/texts_api.py b/api/texts_api.py index d7ce9db8f0..43b92008b3 100644 --- a/api/texts_api.py +++ b/api/texts_api.py @@ -119,9 +119,9 @@ def _add_ref_data_to_return_obj(self) -> None: 'firstAvailableSectionRef': oref.first_available_section_ref().normal(), 'isSpanning': oref.is_spanning(), 'spanningRefs': [r.normal() for r in oref.split_spanning_ref()], - 'next': oref.next_section_ref().normal(), + 'next': oref.next_section_ref().normal() if oref.next_section_ref() else None, 'prev': oref.prev_section_ref().normal() if oref.prev_section_ref() else None, - 'title': oref.context_ref().normal() if oref.next_section_ref() else None, + 'title': oref.context_ref().normal(), 'book': oref.book, 'heTitle': oref.context_ref().he_normal(), 'primary_category': oref.primary_category, From 2e6ae41241fb220aa47a736eb8d2cbe5d8e0e56e Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 2 Aug 2023 13:02:37 +0300 Subject: [PATCH 065/756] helm: remove unused and confusing SEARCH_HOST declarations. --- .../sefaria-project/templates/configmap/local-settings-file.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml b/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml index 9060961403..82877cbacc 100644 --- a/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml +++ b/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml @@ -134,7 +134,6 @@ data: } SERVER_EMAIL = os.getenv("SERVER_EMAIL") - SEARCH_HOST = "/api/search" SEARCH_ADMIN = os.getenv("SEARCH_ADMIN") SEARCH_NON_ADMIN = os.getenv("SEARCH_NON_ADMIN") SEARCH_INDEX_ON_SAVE = True From 126a8c45abae0bb2ea5ac2a9d6230e83702fb991 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 2 Aug 2023 13:03:00 +0300 Subject: [PATCH 066/756] ci: remove unused and confusing SEARCH_HOST declarations. --- build/ci/production-values.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/build/ci/production-values.yaml b/build/ci/production-values.yaml index 1185ea0ce5..9839c73dfa 100644 --- a/build/ci/production-values.yaml +++ b/build/ci/production-values.yaml @@ -211,7 +211,6 @@ localSettings: GLOBAL_WARNING: false GLOBAL_WARNING_MESSAGE: "Sefaria will be in <b>Read-Only</b> mode for scheduled maintenance from 4:45pm-6:45pm Pacific time. Edits will <b>not</b> be saved during that time." SITE_PACKAGE: "sites.sefaria" - SEARCH_HOST: elasticsearch.data DEFAULT_FROM_EMAIL: "Sefaria <hello@sefaria.org>" SERVER_EMAIL: "dev@sefaria.org" MULTISERVER_ENABLED: "True" From 6beea252457cb4e1a13cff86083d86616819be54 Mon Sep 17 00:00:00 2001 From: Brendan Galloway <brendan@flanksource.com> Date: Wed, 2 Aug 2023 14:14:45 +0200 Subject: [PATCH 067/756] helm(feat): add dedicated elastic secrets --- .../sefaria-project/templates/_helpers.tpl | 16 ++++++++++++++++ .../templates/cronjob/reindex-elasticsearch.yaml | 2 ++ .../sefaria-project/templates/rollout/nginx.yaml | 2 ++ .../templates/secret/elastic-admin.yaml | 11 +++++++++++ .../templates/secret/elastic-user.yaml | 11 +++++++++++ helm-chart/sefaria-project/values.yaml | 10 ++++++++++ 6 files changed, 52 insertions(+) create mode 100644 helm-chart/sefaria-project/templates/secret/elastic-admin.yaml create mode 100644 helm-chart/sefaria-project/templates/secret/elastic-user.yaml diff --git a/helm-chart/sefaria-project/templates/_helpers.tpl b/helm-chart/sefaria-project/templates/_helpers.tpl index 0e7c9ecb57..881445d3af 100644 --- a/helm-chart/sefaria-project/templates/_helpers.tpl +++ b/helm-chart/sefaria-project/templates/_helpers.tpl @@ -54,6 +54,22 @@ elastic-certificate-{{ .Values.deployEnv }} {{- end }} {{- end }} +{{- define "sefaria.secrets.elasticUser" }} +{{- if .Values.secrets.elasticUser.ref -}} +{{- .Values.web.secrets.elasticUser.ref }} +{{- else -}} +elastic-user-{{ .Values.deployEnv }} +{{- end }} +{{- end }} + +{{- define "sefaria.secrets.elasticAdmin" }} +{{- if .Values.secrets.elasticAdmin.ref -}} +{{- .Values.web.secrets.elasticAdmin.ref }} +{{- else -}} +elastic-admin-{{ .Values.deployEnv }} +{{- end }} +{{- end }} + {{- define "sefaria.secrets.originTls" }} {{- if .Values.ingress.secrets.originTls.ref -}} diff --git a/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml b/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml index 448dc84c03..6c5aa13784 100644 --- a/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml +++ b/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml @@ -44,6 +44,8 @@ spec: name: {{ template "sefaria.secrets.slackWebhook" . }} key: slack-webhook envFrom: + - secretRef: + name: {{ template "sefaria.secrets.elasticAdmin" . }} - secretRef: name: {{ .Values.secrets.localSettings.ref }} optional: true diff --git a/helm-chart/sefaria-project/templates/rollout/nginx.yaml b/helm-chart/sefaria-project/templates/rollout/nginx.yaml index 94b1e007ed..f338a5d7a3 100644 --- a/helm-chart/sefaria-project/templates/rollout/nginx.yaml +++ b/helm-chart/sefaria-project/templates/rollout/nginx.yaml @@ -101,6 +101,8 @@ spec: value: "linker-{{ .Values.deployEnv }}-{{ .Release.Revision }}" {{- end }} envFrom: + - secretRef: + name: {{ template "sefaria.secrets.elasticUser" . }} - configMapRef: name: local-settings-nginx-{{ .Values.deployEnv }} optional: true diff --git a/helm-chart/sefaria-project/templates/secret/elastic-admin.yaml b/helm-chart/sefaria-project/templates/secret/elastic-admin.yaml new file mode 100644 index 0000000000..d6a3266af9 --- /dev/null +++ b/helm-chart/sefaria-project/templates/secret/elastic-admin.yaml @@ -0,0 +1,11 @@ +{{- if .Values.secrets.elasticAdmin.data }} +apiVersion: v1 +kind: Secret +metadata: + name: elastic-admin-{{ .Values.deployEnv }} + labels: + deployEnv: "{{ .Values.deployEnv }}" + {{- include "sefaria.labels" . | nindent 4 }} +type: Opaque +stringData: {{ .Values.secrets.elasticAdmin.data | toYaml | nindent 2 }} +{{- end }} diff --git a/helm-chart/sefaria-project/templates/secret/elastic-user.yaml b/helm-chart/sefaria-project/templates/secret/elastic-user.yaml new file mode 100644 index 0000000000..511d271a26 --- /dev/null +++ b/helm-chart/sefaria-project/templates/secret/elastic-user.yaml @@ -0,0 +1,11 @@ +{{- if .Values.secrets.elasticUser.data }} +apiVersion: v1 +kind: Secret +metadata: + name: elastic-user-{{ .Values.deployEnv }} + labels: + deployEnv: "{{ .Values.deployEnv }}" + {{- include "sefaria.labels" . | nindent 4 }} +type: Opaque +stringData: {{ .Values.secrets.elasticUser.data | toYaml | nindent 2 }} +{{- end }} diff --git a/helm-chart/sefaria-project/values.yaml b/helm-chart/sefaria-project/values.yaml index ffbe0353e5..c3cbe8b118 100644 --- a/helm-chart/sefaria-project/values.yaml +++ b/helm-chart/sefaria-project/values.yaml @@ -369,6 +369,16 @@ secrets: # should be commented out and vice-versa. ref: trello-secret # data: + elasticUser: + # If you're using a reference to an existing secret then the data: section + # should be commented out and vice-versa. + ref: + # data: + elasticAdmin: + # If you're using a reference to an existing secret then the data: section + # should be commented out and vice-versa. + ref: + # data: # Settings for various cronjobs From e7249ae6168d4251a088008d05795b122f50433b Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Thu, 3 Aug 2023 14:21:17 +0300 Subject: [PATCH 068/756] fix(search): make code backwards compatible with elasticsearch 6. This commit is meant as a temporary patch to make the deployment of ES 8 smoother --- reader/views.py | 3 ++- static/js/SearchResultList.jsx | 15 +++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/reader/views.py b/reader/views.py index 7295254069..a4929cc8c9 100644 --- a/reader/views.py +++ b/reader/views.py @@ -4209,7 +4209,8 @@ def search_wrapper_api(request): search_obj = get_query_obj(search_obj=search_obj, **j) response = search_obj.execute() if response.success(): - return jsonResponse(response.to_dict().body, callback=request.GET.get("callback", None)) + response_json = getattr(response.to_dict(), 'body', response.to_dict()) + return jsonResponse(response_json, callback=request.GET.get("callback", None)) return jsonResponse({"error": "Error with connection to Elasticsearch. Total shards: {}, Shards successful: {}, Timed out: {}".format(response._shards.total, response._shards.successful, response.timed_out)}, callback=request.GET.get("callback", None)) return jsonResponse({"error": "Unsupported HTTP method."}, callback=request.GET.get("callback", None)) diff --git a/static/js/SearchResultList.jsx b/static/js/SearchResultList.jsx index 6b3f15c70b..a05afe1f59 100644 --- a/static/js/SearchResultList.jsx +++ b/static/js/SearchResultList.jsx @@ -104,7 +104,7 @@ class SearchResultList extends Component { //console.log("Loaded cached query for") //console.log(args); this.state.hits[t] = this.state.hits[t].concat(cachedQuery.hits.hits); - this.state.totals[t] = cachedQuery.hits.total.value; + this.state.totals[t] = this._get_hits_total(cachedQuery.hits.total); this.state.pagesLoaded[t] += 1; args.start = this.state.pagesLoaded[t] * this.querySize[t]; if (t === "text") { @@ -302,6 +302,13 @@ class SearchResultList extends Component { .zip(aggregation_field_array, aggregation_field_lang_suffix_array) .map(([agg, suffix_map]) => `${agg}${suffix_map ? suffix_map[Sefaria.interfaceLang] : ''}`); // add suffix based on interfaceLang to filter, if present in suffix_map } + _get_hits_total(totalObj) { + /** + * this function ensures backwards compatibility between the way elasticsearch formats the total pre-v8 and post-v8 + */ + if (typeof(totalObj) === 'number') { return totalObj; } + return totalObj.value; + } _executeQuery(props, type) { //This takes a props object, so as to be able to handle being called from componentWillReceiveProps with newProps props = props || this.props; @@ -326,9 +333,9 @@ class SearchResultList extends Component { if (this.state.pagesLoaded[type] === 0) { // Skip if pages have already been loaded from cache, but let aggregation processing below occur let state = { hits: extend(this.state.hits, {[type]: data.hits.hits}), - totals: extend(this.state.totals, {[type]: data.hits.total.value}), + totals: extend(this.state.totals, {[type]: this._get_hits_total(data.hits.total)}), pagesLoaded: extend(this.state.pagesLoaded, {[type]: 1}), - moreToLoad: extend(this.state.moreToLoad, {[type]: data.hits.total.value > this.querySize[type]}) + moreToLoad: extend(this.state.moreToLoad, {[type]: this._get_hits_total(data.hits.total) > this.querySize[type]}) }; this.setState(state, () => { this.updateTotalResults(); @@ -336,7 +343,7 @@ class SearchResultList extends Component { }); const filter_label = (request_applied && request_applied.length > 0) ? (' - ' + request_applied.join('|')) : ''; const query_label = props.query + filter_label; - Sefaria.track.event("Search", `${this.props.searchInBook? "SidebarSearch ": ""}Query: ${type}`, query_label, data.hits.total.value); + Sefaria.track.event("Search", `${this.props.searchInBook? "SidebarSearch ": ""}Query: ${type}`, query_label, this._get_hits_total(data.hits.total)); } if (data.aggregations) { From 7e32bdcd23702128f486cfbd2eba65ee64724d66 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Mon, 7 Aug 2023 11:07:31 +0300 Subject: [PATCH 069/756] fix(reader): Temporarily have Django connect to ES using admin creds to ease upgrade. --- reader/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reader/views.py b/reader/views.py index a4929cc8c9..7dbf8554a7 100644 --- a/reader/views.py +++ b/reader/views.py @@ -4204,7 +4204,7 @@ def search_wrapper_api(request): else: j = request.body # using content-type: application/json j = json.loads(j) - es_client = get_elasticsearch_client(admin=False) + es_client = get_elasticsearch_client(admin=True) search_obj = Search(using=es_client, index=j.get("type")).params(request_timeout=5) search_obj = get_query_obj(search_obj=search_obj, **j) response = search_obj.execute() From 1846327e2d683b2579b8d9fd47d7793a97027b49 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Mon, 7 Aug 2023 12:15:10 +0300 Subject: [PATCH 070/756] fix(reader): Temporarily use fallback to SEARCH_ADMIN if SEARCH_NON_ADMIN isn't defined --- reader/views.py | 2 +- sefaria/helper/search.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/reader/views.py b/reader/views.py index 7dbf8554a7..a4929cc8c9 100644 --- a/reader/views.py +++ b/reader/views.py @@ -4204,7 +4204,7 @@ def search_wrapper_api(request): else: j = request.body # using content-type: application/json j = json.loads(j) - es_client = get_elasticsearch_client(admin=True) + es_client = get_elasticsearch_client(admin=False) search_obj = Search(using=es_client, index=j.get("type")).params(request_timeout=5) search_obj = get_query_obj(search_obj=search_obj, **j) response = search_obj.execute() diff --git a/sefaria/helper/search.py b/sefaria/helper/search.py index f5a16300e8..7d71ccce32 100644 --- a/sefaria/helper/search.py +++ b/sefaria/helper/search.py @@ -150,4 +150,7 @@ def get_elasticsearch_client(admin=False): from elasticsearch import Elasticsearch from sefaria.settings import SEARCH_ADMIN, SEARCH_NON_ADMIN es_url = SEARCH_ADMIN if admin else SEARCH_NON_ADMIN + if not es_url: + # try flipping values in case the other is defined + es_url = SEARCH_NON_ADMIN if admin else SEARCH_ADMIN return Elasticsearch(es_url) From 1b2920de244e3a69e6de4b403c86955c6d46371e Mon Sep 17 00:00:00 2001 From: Brendan Galloway <brendan@flanksource.com> Date: Mon, 7 Aug 2023 11:48:38 +0200 Subject: [PATCH 071/756] helm(feat): add elastic user to web podd --- helm-chart/sefaria-project/templates/rollout/web.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/helm-chart/sefaria-project/templates/rollout/web.yaml b/helm-chart/sefaria-project/templates/rollout/web.yaml index 513572b76b..e1696aae72 100644 --- a/helm-chart/sefaria-project/templates/rollout/web.yaml +++ b/helm-chart/sefaria-project/templates/rollout/web.yaml @@ -116,6 +116,8 @@ spec: value: k8s.container.name=app,k8s.deployment.name={{ .Values.deployEnv }}-web,k8s.namespace.name={{ .Release.Namespace }},k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME) {{- end }} envFrom: + - secretRef: + name: {{ template "sefaria.secrets.elasticUser" . }} - secretRef: name: {{ .Values.secrets.localSettings.ref }} optional: true From 7f7e16182fd113a1c76759915d885571202e2e22 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Mon, 7 Aug 2023 14:18:10 +0300 Subject: [PATCH 072/756] chore: remove SEARCH_NON_ADMIN which is no longer necessary --- .../templates/configmap/local-settings-file.yaml | 1 - reader/views.py | 2 +- sefaria/helper/search.py | 10 +++------- sefaria/local_settings_example.py | 6 ++---- sefaria/search.py | 2 +- 5 files changed, 7 insertions(+), 14 deletions(-) diff --git a/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml b/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml index 82877cbacc..a1b0e80d58 100644 --- a/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml +++ b/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml @@ -135,7 +135,6 @@ data: SERVER_EMAIL = os.getenv("SERVER_EMAIL") SEARCH_ADMIN = os.getenv("SEARCH_ADMIN") - SEARCH_NON_ADMIN = os.getenv("SEARCH_NON_ADMIN") SEARCH_INDEX_ON_SAVE = True SEARCH_INDEX_NAME_TEXT = 'text' # name of the ElasticSearch index to use SEARCH_INDEX_NAME_SHEET = 'sheet' diff --git a/reader/views.py b/reader/views.py index a4929cc8c9..6293febe60 100644 --- a/reader/views.py +++ b/reader/views.py @@ -4204,7 +4204,7 @@ def search_wrapper_api(request): else: j = request.body # using content-type: application/json j = json.loads(j) - es_client = get_elasticsearch_client(admin=False) + es_client = get_elasticsearch_client() search_obj = Search(using=es_client, index=j.get("type")).params(request_timeout=5) search_obj = get_query_obj(search_obj=search_obj, **j) response = search_obj.execute() diff --git a/sefaria/helper/search.py b/sefaria/helper/search.py index 7d71ccce32..9dd542910e 100644 --- a/sefaria/helper/search.py +++ b/sefaria/helper/search.py @@ -146,11 +146,7 @@ def make_filter(type, agg_type, agg_key): return Term(**{agg_type: agg_key}) -def get_elasticsearch_client(admin=False): +def get_elasticsearch_client(): from elasticsearch import Elasticsearch - from sefaria.settings import SEARCH_ADMIN, SEARCH_NON_ADMIN - es_url = SEARCH_ADMIN if admin else SEARCH_NON_ADMIN - if not es_url: - # try flipping values in case the other is defined - es_url = SEARCH_NON_ADMIN if admin else SEARCH_ADMIN - return Elasticsearch(es_url) + from sefaria.settings import SEARCH_ADMIN + return Elasticsearch(SEARCH_ADMIN) diff --git a/sefaria/local_settings_example.py b/sefaria/local_settings_example.py index 1112abd926..dfdc28cadb 100644 --- a/sefaria/local_settings_example.py +++ b/sefaria/local_settings_example.py @@ -158,12 +158,10 @@ APSCHEDULER_NAME = "apscheduler" # ElasticSearch server -# URL to connect to internal ES server for admin access. This URL is used by indexing jobs only +# URL to connect to ES server. +# Set this to https://sefaria.org/api/search to connect to production search. # If ElasticSearch server has a password use the following format: http(s)://{username}:{password}@{base_url} SEARCH_ADMIN = "http://localhost:9200" -# URL to connect to ES for searching. Can point to /api/search Django endpoint which gets proxied to ES server. -# Set this to https://sefaria.org/api/search to connect to production search. -SEARCH_NON_ADMIN = "http://localhost:8000/api/search" SEARCH_INDEX_ON_SAVE = False # Whether to send texts and source sheet to Search Host for indexing after save SEARCH_INDEX_NAME_TEXT = 'text' # name of the ElasticSearch index to use diff --git a/sefaria/search.py b/sefaria/search.py index f31382b8e5..a349074769 100644 --- a/sefaria/search.py +++ b/sefaria/search.py @@ -36,7 +36,7 @@ from sefaria.utils.hebrew import strip_cantillation import sefaria.model.queue as qu -es_client = get_elasticsearch_client(admin=True) +es_client = get_elasticsearch_client() index_client = IndicesClient(es_client) tracer = structlog.get_logger(__name__) From 6a3c5618ce655a99de2582b04a25714ddea06d53 Mon Sep 17 00:00:00 2001 From: Brendan Galloway <brendan@flanksource.com> Date: Tue, 8 Aug 2023 14:12:28 +0200 Subject: [PATCH 073/756] helm(feat): move nginx entrypoint to file to support elastic input --- .../sefaria-project/templates/configmap/nginx.yaml | 10 ++++++++++ .../sefaria-project/templates/rollout/nginx.yaml | 9 ++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/helm-chart/sefaria-project/templates/configmap/nginx.yaml b/helm-chart/sefaria-project/templates/configmap/nginx.yaml index bda59de65d..9126b3fa0b 100644 --- a/helm-chart/sefaria-project/templates/configmap/nginx.yaml +++ b/helm-chart/sefaria-project/templates/configmap/nginx.yaml @@ -28,6 +28,16 @@ data: } } {{- end }} + entrpoint.sh: | + #!/bin/bash + + set -e + + export ELASTIC_AUTH_HEADER=$(echo -n $ELASTIC_USER:$ELASTIC_PASSWORD | base64) + envsubst '${ENV_NAME},${VARNISH_HOST},${SEARCH_HOST},${RELEASE_TAG},${ELASTIC_AUTH_HEADER}{{- if .Values.linker.enabled }},${LINKER_HOST}{{- end }}{{- if .Values.instrumentation.enabled }},${NGINX_VERSION}{{- end }}' < /conf/nginx.template.conf > /nginx.conf + + nginx -c /nginx.conf -g 'daemon off;' + nginx.template.conf: |- {{- if .Values.instrumentation.enabled }} load_module /etc/nginx/modules/ngx_http_opentracing_module.so; diff --git a/helm-chart/sefaria-project/templates/rollout/nginx.yaml b/helm-chart/sefaria-project/templates/rollout/nginx.yaml index f338a5d7a3..df2f1e61f4 100644 --- a/helm-chart/sefaria-project/templates/rollout/nginx.yaml +++ b/helm-chart/sefaria-project/templates/rollout/nginx.yaml @@ -52,9 +52,8 @@ spec: - name: nginx image: "{{ .Values.nginx.containerImage.imageRegistry }}:{{ .Values.nginx.containerImage.tag }}" imagePullPolicy: Always - command: ["bash", "-c"] - # https://serverfault.com/questions/577370/how-can-i-use-environment-variables-in-nginx-conf - args: [ "envsubst '${ENV_NAME},${VARNISH_HOST},${SEARCH_HOST},${RELEASE_TAG}{{- if .Values.linker.enabled }},${LINKER_HOST}{{- end }}{{- if .Values.instrumentation.enabled }},${NGINX_VERSION}{{- end }}' < /conf/nginx.template.conf > /nginx.conf && exec nginx -c /nginx.conf -g 'daemon off;'" ] + command: + - /entrypoint.sh ports: - containerPort: 80 - containerPort: 443 @@ -76,6 +75,10 @@ spec: name: nginx-conf subPath: nginx.template.conf readOnly: true + - mountPath: /entrypoint.sh + name: nginx-conf + subPath: entrypoint.sh + defaultMode: 0755 {{- if .Values.instrumentation.enabled }} - mountPath: /etc/nginx/opentracing.json name: nginx-conf From 9a532cc85fd12f67a45a4233bc8163db52cd698f Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Tue, 8 Aug 2023 15:30:27 +0300 Subject: [PATCH 074/756] chore: add templating to access elastic auth in nginx and local settings --- .../sefaria-project/templates/configmap/local-settings.yaml | 2 +- helm-chart/sefaria-project/templates/configmap/nginx.yaml | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/helm-chart/sefaria-project/templates/configmap/local-settings.yaml b/helm-chart/sefaria-project/templates/configmap/local-settings.yaml index 643d5a5547..0ef0080cd7 100644 --- a/helm-chart/sefaria-project/templates/configmap/local-settings.yaml +++ b/helm-chart/sefaria-project/templates/configmap/local-settings.yaml @@ -10,7 +10,7 @@ data: DOMAIN_LANGUAGE: {{ .Values.localSettings.DOMAIN_LANGUAGE | toJson | quote }} MONGO_HOST: {{ .Values.localSettings.MONGO_HOST | quote }} APSCHEDULER_NAME: {{ tpl .Values.localSettings.APSCHEDULER_NAME . | quote }} - SEARCH_ADMIN: "http://{{ .Values.nginx.SEARCH_HOST }}:9200" + SEARCH_ADMIN: "http://{{ .Values.nginx.ELASTIC_USERNAME }}:{{ .Values.nginx.ELASTIC_PASSWORD }}@{{ .Values.nginx.SEARCH_HOST }}:9200" TURN_SERVER: {{ .Values.localSettings.TURN_SERVER | quote }} USE_CLOUDFLARE: "{{ .Values.localSettings.USE_CLOUDFLARE }}" FRONT_END_URL: {{ .Values.localSettings.FRONT_END_URL | quote }} diff --git a/helm-chart/sefaria-project/templates/configmap/nginx.yaml b/helm-chart/sefaria-project/templates/configmap/nginx.yaml index 9126b3fa0b..4e015fd53f 100644 --- a/helm-chart/sefaria-project/templates/configmap/nginx.yaml +++ b/helm-chart/sefaria-project/templates/configmap/nginx.yaml @@ -34,7 +34,7 @@ data: set -e export ELASTIC_AUTH_HEADER=$(echo -n $ELASTIC_USER:$ELASTIC_PASSWORD | base64) - envsubst '${ENV_NAME},${VARNISH_HOST},${SEARCH_HOST},${RELEASE_TAG},${ELASTIC_AUTH_HEADER}{{- if .Values.linker.enabled }},${LINKER_HOST}{{- end }}{{- if .Values.instrumentation.enabled }},${NGINX_VERSION}{{- end }}' < /conf/nginx.template.conf > /nginx.conf + envsubst '${ENV_NAME},${VARNISH_HOST},${SEARCH_HOST},${ELASTIC_USERNAME},${ELASTIC_PASSWORD},${RELEASE_TAG},${ELASTIC_AUTH_HEADER}{{- if .Values.linker.enabled }},${LINKER_HOST}{{- end }}{{- if .Values.instrumentation.enabled }},${NGINX_VERSION}{{- end }}' < /conf/nginx.template.conf > /nginx.conf nginx -c /nginx.conf -g 'daemon off;' @@ -118,6 +118,7 @@ data: location /api/search/ { rewrite ^/(?:api/search)/(.*)$ /$1 break; proxy_set_header Content-Type application/json; # es 6.0 requires this header + proxy_set_header Authorization "Basic ${ELASTIC_AUTH_HEADER}"; add_header 'Access-Control-Allow-Origin' ''; proxy_pass http://elasticsearch_upstream/; } From a75b4938507c9543a66d0f91dc48fdfcc87c4843 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Tue, 8 Aug 2023 15:30:44 +0300 Subject: [PATCH 075/756] helm: fix typo in entrypoint filename --- helm-chart/sefaria-project/templates/configmap/nginx.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helm-chart/sefaria-project/templates/configmap/nginx.yaml b/helm-chart/sefaria-project/templates/configmap/nginx.yaml index 4e015fd53f..44808678d4 100644 --- a/helm-chart/sefaria-project/templates/configmap/nginx.yaml +++ b/helm-chart/sefaria-project/templates/configmap/nginx.yaml @@ -28,7 +28,7 @@ data: } } {{- end }} - entrpoint.sh: | + entrypoint.sh: | #!/bin/bash set -e From f9c07e96f07a460388bfbd13b3c93f73ef013657 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Fri, 23 Jun 2023 12:37:00 -0400 Subject: [PATCH 076/756] feat(strapi-integration): Proof of concept: adds support for getting sidebar ads data from Strapi and displaying them in the right context --- static/js/Promotions.jsx | 415 +++++++++++++++++++++-------------- static/js/ReaderApp.jsx | 28 ++- static/js/context.js | 72 +++++- static/js/sefaria/sefaria.js | 1 + 4 files changed, 335 insertions(+), 181 deletions(-) diff --git a/static/js/Promotions.jsx b/static/js/Promotions.jsx index 77e6640961..d132e0cdce 100644 --- a/static/js/Promotions.jsx +++ b/static/js/Promotions.jsx @@ -1,145 +1,211 @@ -import React, {useState, useContext, useEffect} from 'react'; -import { AdContext } from './context'; -import classNames from 'classnames'; -import { InterruptingMessage } from './Misc'; -import Sefaria from './sefaria/sefaria'; - -const Promotions = ({adType, rerender}) => { - const [inAppAds, setInAppAds] = useState(Sefaria._inAppAds); - const [matchingAds, setMatchingAds] = useState(null); - const [prevMatchingAdIds, setPrevMatchingAdIds] = useState([]) - const context = useContext(AdContext); - useEffect(() => { - google.charts.load("current"); - google.charts.setOnLoadCallback(getAds) - }, []); - useEffect(() => { - if(inAppAds) { - setMatchingAds(getCurrentMatchingAds()); - } - }, [context, inAppAds]); - useEffect(() => { - if (!matchingAds) {return} - const matchingAdIds = matchingAds.map(x => x.campaignId).sort(); - const newIds = matchingAdIds.filter(value=> !(prevMatchingAdIds.includes(value))); - - if (newIds.length > 0) { - for (const matchingAd of matchingAds) { - if (newIds.includes(matchingAd.campaignId)) { - gtag("event", "promo_viewed", { - campaignID: matchingAd.campaignId, - adType:matchingAd.adType - }) - } - } - setPrevMatchingAdIds(newIds) - } - }, [matchingAds]) - - function getAds() { - const url = - 'https://docs.google.com/spreadsheets/d/1UJw2Akyv3lbLqBoZaFVWhaAp-FUQ-YZfhprL_iNhhQc/edit#gid=0' - const query = new google.visualization.Query(url); - query.setQuery('select A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q'); - query.send(processSheetsData); +import React, { useState, useContext, useEffect } from "react"; +import { AdContext, StrapiDataProvider, StrapiDataContext } from "./context"; +import classNames from "classnames"; +import { InterruptingMessage } from "./Misc"; +import Sefaria from "./sefaria/sefaria"; + +const Promotions = ({ adType, rerender }) => { + const [inAppAds, setInAppAds] = useState(Sefaria._inAppAds); // local cache + const [matchingAds, setMatchingAds] = useState(null); // match the ads to what comes from the google doc + const [prevMatchingAdIds, setPrevMatchingAdIds] = useState([]); + const context = useContext(AdContext); + const strapi = useContext(StrapiDataContext); + useEffect(() => { + if (strapi.dataFromStrapiHasBeenReceived) { + Sefaria._inAppAds = []; + console.log("we got some data"); + console.log(JSON.stringify(strapi.strapiData, null, 2)); + + const sidebarAds = strapi.strapiData.sidebarAds.data; + + sidebarAds.forEach((sidebarAd) => { + sidebarAd = sidebarAd.attributes; + console.log(JSON.stringify(sidebarAd, null, 2)); + let keywordTargetsArray = sidebarAd.keywords + .split(",") + .map((x) => x.trim().toLowerCase()); + Sefaria._inAppAds.push({ + campaignId: sidebarAd.internalCampaignId, + title: sidebarAd.Title, + bodyText: sidebarAd.bodyText, + buttonText: sidebarAd.buttonText, + buttonUrl: sidebarAd.buttonUrl, + buttonIcon: "", + buttonLocation: sidebarAd.buttonUrl, + adType: "sidebar", + hasBlueBackground: sidebarAd.hasBlueBackground, + repetition: 5, + buttonStyle: "", + trigger: { + showTo: sidebarAd.showTo, + interfaceLang: Sefaria.translateISOLanguageCode( + sidebarAd.locale + ).toLowerCase(), + dt_start: Date.parse(sidebarAd.startTime), + dt_end: Date.parse(sidebarAd.endTime), + keywordTargets: keywordTargetsArray, + excludeKeywordTargets: [], + }, + debug: sidebarAd.debug, + }); + }); + setInAppAds(Sefaria._inAppAds); + } + }, [strapi.dataFromStrapiHasBeenReceived]); + // empty array happens when the page loads, equivalent of didcomponentmount + // dataFromStrapiHasBeenReceived will originally be null until that part is scheduled and executed + useEffect(() => { + if (inAppAds) { + setMatchingAds(getCurrentMatchingAds()); + } + }, [context, inAppAds]); // when state changes, the effect will run + useEffect(() => { + if (!matchingAds) { + return; } + const matchingAdIds = matchingAds.map((x) => x.campaignId).sort(); + const newIds = matchingAdIds.filter( + (value) => !prevMatchingAdIds.includes(value) + ); - function showToUser(ad) { - if (ad.trigger.showTo === "all") { - return true; - } else if (ad.trigger.showTo === "loggedIn" && context.isLoggedIn) { - return true; - } else if (ad.trigger.showTo === "loggedOut" && !context.isLoggedIn) { - return true; - } else { - return false; + if (newIds.length > 0) { + for (const matchingAd of matchingAds) { + if (newIds.includes(matchingAd.campaignId)) { + gtag("event", "promo_viewed", { + campaignID: matchingAd.campaignId, + adType: matchingAd.adType, + }); } + } + setPrevMatchingAdIds(newIds); } + }, [matchingAds]); // when state of matching ads changes, which changes in previous useEffect - function showGivenDebugMode(ad) { - if (!ad.debug) { - return true; - } else if (context.isDebug == true) { - return true; - } else { - return false - } + // function getAds() { + // const url = + // 'https://docs.google.com/spreadsheets/d/1UJw2Akyv3lbLqBoZaFVWhaAp-FUQ-YZfhprL_iNhhQc/edit#gid=0' + // const query = new google.visualization.Query(url); + // query.setQuery('select A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q'); + // query.send(processSheetsData); + // + // + // } + + function showToUser(ad) { + if (ad.trigger.showTo === "all") { + return true; + } else if (ad.trigger.showTo === "loggedIn" && context.isLoggedIn) { + return true; + } else if (ad.trigger.showTo === "loggedOut" && !context.isLoggedIn) { + return true; + } else { + return false; + } + } + + function showGivenDebugMode(ad) { + if (!ad.debug) { + return true; + } else if (context.isDebug == true) { + return true; + } else { + return false; } - - + } + function getCurrentMatchingAds() { // TODO: refine matching algorithm to order by matchingness? - return inAppAds.filter(ad => { + return inAppAds.filter((ad) => { return ( showToUser(ad) && showGivenDebugMode(ad) && ad.trigger.interfaceLang === context.interfaceLang && ad.adType === adType && - context.dt > ad.trigger.dt_start && context.dt < ad.trigger.dt_end && - (context.keywordTargets.some(kw => ad.trigger.keywordTargets.includes(kw)) || - (ad.trigger.excludeKeywordTargets.length !== 0 && !context.keywordTargets.some(kw => ad.trigger.excludeKeywordTargets.includes(kw)))) && + context.dt > ad.trigger.dt_start && + context.dt < ad.trigger.dt_end && + (context.keywordTargets.some((kw) => + ad.trigger.keywordTargets.includes(kw) + ) || + (ad.trigger.excludeKeywordTargets.length !== 0 && + !context.keywordTargets.some((kw) => + ad.trigger.excludeKeywordTargets.includes(kw) + ))) && /* line below checks if ad with particular repetition number has been seen before and is a banner */ - (Sefaria._inBrowser && !document.cookie.includes(`${ad.campaignId}_${ad.repetition}`) || ad.adType === "sidebar") - ) - }) + ((Sefaria._inBrowser && + !document.cookie.includes(`${ad.campaignId}_${ad.repetition}`)) || + ad.adType === "sidebar") + ); + }); } - function processSheetsData(response) { - if (response.isError()) { - alert('Error in query: ' + response.getMessage() + ' ' + response.getDetailedMessage()); - return; - } - const data = response.getDataTable(); - const columns = data.getNumberOfColumns(); - const rows = data.getNumberOfRows(); - Sefaria._inAppAds = []; - for (let r = 0; r < rows; r++) { - let row = []; - for (let c = 0; c < columns; c++) { - row.push(data.getFormattedValue(r, c)); - } - let keywordTargetsArray = row[5].split(",").map(x => x.trim().toLowerCase()); - let excludeKeywordTargets = keywordTargetsArray.filter(x => x.indexOf("!") === 0); - excludeKeywordTargets = excludeKeywordTargets.map(x => x.slice(1)); - keywordTargetsArray = keywordTargetsArray.filter(x => x.indexOf("!") !== 0) - Sefaria._inAppAds.push( - { - campaignId: row[0], - title: row[6], - bodyText: row[7], - buttonText: row[8], - buttonUrl: row[9], - buttonIcon: row[10], - buttonLocation: row[11], - adType: row[12], - hasBlueBackground: parseInt(row[13]), - repetition: row[14], - buttonStyle: row[15], - trigger: { - showTo: row[4] , - interfaceLang: row[3], - dt_start: Date.parse(row[1]), - dt_end: Date.parse(row[2]), - keywordTargets: keywordTargetsArray, - excludeKeywordTargets: excludeKeywordTargets - }, - debug: parseInt(row[16]) - } - ) + function processSheetsData(response) { + if (response.isError()) { + alert( + "Error in query: " + + response.getMessage() + + " " + + response.getDetailedMessage() + ); + return; + } + const data = response.getDataTable(); + const columns = data.getNumberOfColumns(); + const rows = data.getNumberOfRows(); + Sefaria._inAppAds = []; + for (let r = 0; r < rows; r++) { + let row = []; + for (let c = 0; c < columns; c++) { + row.push(data.getFormattedValue(r, c)); } - setInAppAds(Sefaria._inAppAds); - + let keywordTargetsArray = row[5] + .split(",") + .map((x) => x.trim().toLowerCase()); + let excludeKeywordTargets = keywordTargetsArray.filter( + (x) => x.indexOf("!") === 0 + ); + excludeKeywordTargets = excludeKeywordTargets.map((x) => x.slice(1)); + keywordTargetsArray = keywordTargetsArray.filter( + (x) => x.indexOf("!") !== 0 + ); + Sefaria._inAppAds.push({ + campaignId: row[0], + title: row[6], + bodyText: row[7], + buttonText: row[8], + buttonUrl: row[9], + buttonIcon: row[10], + buttonLocation: row[11], + adType: row[12], + hasBlueBackground: parseInt(row[13]), + repetition: row[14], + buttonStyle: row[15], + trigger: { + showTo: row[4], + interfaceLang: row[3], + dt_start: Date.parse(row[1]), + dt_end: Date.parse(row[2]), + keywordTargets: keywordTargetsArray, + excludeKeywordTargets: excludeKeywordTargets, + }, + debug: parseInt(row[16]), + }); } + setInAppAds(Sefaria._inAppAds); + } - // TODO: refactor once old InterruptingMessage pattern is retired - function createBannerHtml(matchingAd) { - return `<div id="bannerTextBox"> - <span class="${context.interfaceLang === "hebrew" ? "int-he" : "int-en" }" style="font-weight: bold"> + // TODO: refactor once old InterruptingMessage pattern is retired + function createBannerHtml(matchingAd) { + return `<div id="bannerTextBox"> + <span class="${ + context.interfaceLang === "hebrew" ? "int-he" : "int-en" + }" style="font-weight: bold"> ${matchingAd.bodyText} </span> </div> <div id="bannerButtonBox"> - <a class="button white ${context.interfaceLang === "hebrew" ? "int-he" : "int-en" }" + <a class="button white ${ + context.interfaceLang === "hebrew" ? "int-he" : "int-en" + }" href="${matchingAd.buttonUrl}" onclick='gtag("event", "promo_clicked", { campaignID: matchingAd.campaignId, adType:matchingAd.adType @@ -147,54 +213,79 @@ const Promotions = ({adType, rerender}) => { target="_blank"> <span>${matchingAd.buttonText}</span> </a> -</div>` - } +</div>`; + } - function styleAds() { - if (adType === "banner") { - const matchingAd = matchingAds[0] // Only allow a single banner - if (!matchingAd) {return null} - const bannerHtml = createBannerHtml(matchingAd); - return <InterruptingMessage - messageName={matchingAd.campaignId} - messageHTML={bannerHtml} - style="banner" - repetition={matchingAd.repetition} - onClose={rerender} /> - } else { - const sidebarAds = matchingAds.map(ad => <SidebarAd matchingAd={ad} key={ad.campaignId} />); - return (sidebarAds) - } + function styleAds() { + if (adType === "banner") { + const matchingAd = matchingAds[0]; // Only allow a single banner + if (!matchingAd) { + return null; + } + const bannerHtml = createBannerHtml(matchingAd); + return ( + <InterruptingMessage + messageName={matchingAd.campaignId} + messageHTML={bannerHtml} + style="banner" + repetition={matchingAd.repetition} + onClose={rerender} + /> + ); + } else { + const sidebarAds = matchingAds.map((ad) => ( + <SidebarAd matchingAd={ad} key={ad.campaignId} /> + )); + return sidebarAds; } + } - return matchingAds ? styleAds() : null - -} + return matchingAds ? styleAds() : null; +}; -const SidebarAd = ({matchingAd}) => { - const classes = classNames({ - sidebarPromo: 1, - blue: matchingAd.hasBlueBackground, - }) +const SidebarAd = ({ matchingAd }) => { + const classes = classNames({ + sidebarPromo: 1, + blue: matchingAd.hasBlueBackground, + }); - function getButton() { - return <a className={matchingAd.buttonStyle} href={matchingAd.buttonUrl} - onClick={() => gtag("event", "promo_clicked", { - campaignID: matchingAd.campaignId, - adType:matchingAd.adType - })}> - <img src={`/static/icons/${matchingAd.buttonIcon}`} aria-hidden="true" /> - {matchingAd.buttonText}</a> - } + function getButton() { + return ( + <a + className={matchingAd.buttonStyle} + href={matchingAd.buttonUrl} + onClick={() => + gtag("event", "promo_clicked", { + campaignID: matchingAd.campaignId, + adType: matchingAd.adType, + }) + } + > + <img + src={`/static/icons/${matchingAd.buttonIcon}`} + aria-hidden="true" + /> + {matchingAd.buttonText} + </a> + ); + } - return <div className={classes}> - <h3>{matchingAd.title}</h3> - {matchingAd.buttonLocation === "below" ? - <><p>{matchingAd.bodyText}</p>{getButton()}</> : - <>{getButton()}<p>{matchingAd.bodyText}</p></>} + return ( + <div className={classes}> + <h3>{matchingAd.title}</h3> + {matchingAd.buttonLocation === "below" ? ( + <> + <p>{matchingAd.bodyText}</p> + {getButton()} + </> + ) : ( + <> + {getButton()} + <p>{matchingAd.bodyText}</p> + </> + )} </div> -} + ); +}; -export { - Promotions -} +export { Promotions }; diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index 02ec1aa695..1ea540c751 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -9,7 +9,7 @@ import $ from './sefaria/sefariaJquery'; import EditCollectionPage from './EditCollectionPage'; import Footer from './Footer'; import SearchState from './sefaria/searchState'; -import {ContentLanguageContext, AdContext} from './context'; +import {ContentLanguageContext, AdContext, StrapiDataProvider, ExampleComponent} from './context'; import { ContestLandingPage, RemoteLearningPage, @@ -2175,18 +2175,22 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { var classes = classNames(classDict); return ( - <AdContext.Provider value={this.getUserContext()}> - <div id="readerAppWrap"> - {interruptingMessage} - <div className={classes} onClick={this.handleInAppLinkClick}> - {header} - {panels} - {sefariaModal} - {communityPagePreviewControls} - <CookiesNotification /> + <StrapiDataProvider> + <AdContext.Provider value={this.getUserContext()}> + <div id="readerAppWrap"> + {interruptingMessage} + <div className={classes} onClick={this.handleInAppLinkClick}> + {header} + {panels} + {sefariaModal} + {communityPagePreviewControls} + {beitMidrashPanel} + <CookiesNotification /> + {/* <ExampleComponent /> */} + </div> </div> - </div> - </AdContext.Provider> + </AdContext.Provider> + </StrapiDataProvider> ); } } diff --git a/static/js/context.js b/static/js/context.js index d21b68b8d4..4387d1ac2c 100644 --- a/static/js/context.js +++ b/static/js/context.js @@ -1,14 +1,72 @@ -import React from 'react'; +import React, { useContext, useEffect, useState } from "react"; const ContentLanguageContext = React.createContext({ language: "english", }); -ContentLanguageContext.displayName = 'ContentLanguageContext'; //This lets us see this name in the devtools +ContentLanguageContext.displayName = "ContentLanguageContext"; //This lets us see this name in the devtools -const AdContext = React.createContext({ -}); -AdContext.displayName = 'AdContext'; +const AdContext = React.createContext({}); +AdContext.displayName = "AdContext"; + +const StrapiDataContext = React.createContext({}); +StrapiDataContext.displayName = "StrapiDataContext"; + +function StrapiDataProvider({ children }) { + const [dataFromStrapiHasBeenReceived, setDataFromStrapiHasBeenReceived] = + useState(false); + const [strapiData, setStrapiData] = useState(null); + useEffect(() => { + const getStrapiData = async () => { + try { + const result = fetch("http://localhost:1337/graphql", { + method: "POST", // *GET, POST, PUT, DELETE, etc. + mode: "cors", // no-cors, *cors, same-origin + cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached + credentials: "same-origin", // include, *same-origin, omit + headers: { + "Content-Type": "application/json", + }, + redirect: "follow", // manual, *follow, error + referrerPolicy: "no-referrer", // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url + body: '{"query":"# Write your query or mutation here\\nquery {\\n sidebarAds(\\n filters: {\\n startTime: { gte: \\"2023-06-19T04:00:00.000Z\\" }\\n and: [{ endTime: { lte: \\"2023-06-29T04:00:00.000Z\\" } }]\\n }\\n ) {\\n data {\\n id\\n attributes {\\n ButtonAboveOrBelow\\n Title\\n bodyText\\n buttonText\\n buttonUrl\\n createdAt\\n debug\\n endTime\\n hasBlueBackground\\n internalCampaignId\\n keywords\\n locale\\n publishedAt\\n showTo\\n startTime\\n updatedAt\\n }\\n }\\n }\\n}\\n"}', + }) + .then((response) => response.json()) + .then((result) => { + setStrapiData(result.data); + setDataFromStrapiHasBeenReceived(true); + }); + } catch (error) { + console.error("Failed to get strapi data", error); + } + }; + getStrapiData(); + }, []); + + return ( + <StrapiDataContext.Provider + value={{ dataFromStrapiHasBeenReceived, strapiData }} + > + {children} + </StrapiDataContext.Provider> + ); +} + +// function ExampleComponent() { +// const strapi = useContext(StrapiDataContext); +// if (strapi.dataFromStrapiHasBeenReceived) { +// return ( +// <div className="test"> +// <dialog open>{strapi.strapiData}</dialog> +// </div> +// ); +// } else { +// return null; +// } +// } export { ContentLanguageContext, - AdContext -}; \ No newline at end of file + AdContext, + StrapiDataProvider, + // ExampleComponent, + StrapiDataContext, +}; diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index 9698478df8..2cd4fee1b2 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -2318,6 +2318,7 @@ _media: {}, }, _tableOfContentsDedications: {}, + _strapiContent: null, _inAppAds: null, _stories: { stories: [], From b78902ea92ce0fb17b161ade7db38f160d02433e Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Fri, 7 Jul 2023 11:17:08 -0400 Subject: [PATCH 077/756] First rough draft for updating the interruptingmessage component to support using Strapi for modals --- sefaria/settings.py | 20 +- static/js/Misc.jsx | 411 +++++++++++++++++++++++++++++++--------- static/js/ReaderApp.jsx | 58 +++--- static/js/context.js | 38 +++- 4 files changed, 406 insertions(+), 121 deletions(-) diff --git a/sefaria/settings.py b/sefaria/settings.py index 38229af2ca..40d54c4278 100644 --- a/sefaria/settings.py +++ b/sefaria/settings.py @@ -299,20 +299,32 @@ +# GLOBAL_INTERRUPTING_MESSAGE = { +# "name": "2023-06-16-help-center", +# "style": "banner", # "modal" or "banner" +# "repetition": 1, +# "is_fundraising": False, +# "condition": { +# "returning_only": False, +# "english_only": False, +# "desktop_only": True, +# "debug": False, +# } +# } + GLOBAL_INTERRUPTING_MESSAGE = { - "name": "2023-08-08-anniversary", + "name": "2022-04-07-passover-donate-modal", "style": "modal", # "modal" or "banner" "repetition": 1, "is_fundraising": False, "condition": { "returning_only": False, "english_only": False, - "desktop_only": False, - "debug": False, + "desktop_only": True, + "debug": True, } } - # GLOBAL_INTERRUPTING_MESSAGE = None diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index e20562c887..45d5a2c845 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -7,7 +7,8 @@ import Sefaria from './sefaria/sefaria'; import classNames from 'classnames'; import PropTypes from 'prop-types'; import Component from 'react-class'; -import {usePaginatedDisplay} from './Hooks'; +import { usePaginatedDisplay } from './Hooks'; +import {ContentLanguageContext, AdContext, StrapiDataContext} from './context'; import ReactCrop from 'react-image-crop'; import 'react-image-crop/dist/ReactCrop.css'; import {ContentText} from "./ContentText"; @@ -2057,109 +2058,337 @@ SignUpModal.propTypes = { }; -class InterruptingMessage extends Component { - constructor(props) { - super(props); - this.displayName = 'InterruptingMessage'; - this.state = { - timesUp: false, - animationStarted: false +// class InterruptingMessage extends Component { +// constructor(props) { +// super(props); +// this.displayName = 'InterruptingMessage'; +// this.state = { +// timesUp: false, +// animationStarted: false +// }; +// this.settings = { +// "modal": { +// "trackingName": "Interrupting Message", +// "showDelay": 10000, +// }, +// "banner": { +// "trackingName": "Banner Message", +// "showDelay": 1, +// } +// }[this.props.style]; +// } +// componentDidMount() { +// if (this.shouldShow()) { +// this.delayedShow(); +// } +// } +// shouldShow() { +// const excludedPaths = ["/donate", "/mobile", "/app", "/ways-to-give"]; +// return excludedPaths.indexOf(window.location.pathname) === -1; +// } +// delayedShow() { +// setTimeout(function() { +// this.setState({timesUp: true}); +// $("#interruptingMessage .button").click(this.close); +// $("#interruptingMessage .trackedAction").click(this.trackAction); +// this.showAorB(); +// this.animateOpen(); +// }.bind(this), this.settings.showDelay); +// } +// animateOpen() { +// setTimeout(function() { +// if (this.props.style === "banner" && $("#s2").hasClass("headerOnly")) { $("body").addClass("hasBannerMessage"); } +// this.setState({animationStarted: true}); +// this.trackOpen(); +// }.bind(this), 50); +// } +// showAorB() { +// // Allow random A/B testing if items are tagged ".optionA", ".optionB" +// const $message = $(ReactDOM.findDOMNode(this)); +// if ($message.find(".optionA").length) { +// console.log("rand show") +// Math.random() > 0.5 ? $(".optionA").show() : $(".optionB").show(); +// } +// } +// close() { +// this.markAsRead(); +// this.props.onClose(); +// // if (this.props.style === "banner" && $("#s2").hasClass("headerOnly")) { $("body").removeClass("hasBannerMessage"); } +// } +// trackOpen() { +// Sefaria.track.event(this.settings.trackingName, "open", this.props.messageName, { nonInteraction: true }); +// } +// trackAction() { +// Sefaria.track.event(this.settings.trackingName, "action", this.props.messageName, { nonInteraction: true }); +// } +// markAsRead() { +// Sefaria._api("/api/interrupting-messages/read/" + this.props.messageName, function (data) {}); +// var cookieName = this.props.messageName + "_" + this.props.repetition; +// $.cookie(cookieName, true, { path: "/", expires: 14 }); +// Sefaria.track.event(this.settings.trackingName, "read", this.props.messageName, { nonInteraction: true }); +// Sefaria.interruptingMessage = null; +// } +// render() { +// if (!this.state.timesUp) { return null; } + +// if (this.props.style === "banner") { +// return <div id="bannerMessage" className={this.state.animationStarted ? "" : "hidden"}> +// <div id="bannerMessageContent" dangerouslySetInnerHTML={ {__html: this.props.messageHTML} }></div> +// <div id="bannerMessageClose" onClick={this.close}>×</div> +// </div>; + +// } else if (this.props.style === "modal") { +// return <div id="interruptingMessageBox" className={this.state.animationStarted ? "" : "hidden"}> +// <div id="interruptingMessageOverlay"></div> +// <div id="interruptingMessage"> +// <div className="colorLine"></div> +// <div id="interruptingMessageContentBox" className="hasColorLine"> +// <div id="interruptingMessageClose" onClick={this.close}>×</div> +// <div id="interruptingMessageContent" dangerouslySetInnerHTML={ {__html: this.props.messageHTML} }></div> +// {/* <div id="interruptingMessageContent" dangerouslySetInnerHTML={ {__html: this.strapi.interruptingMessageModal ? interruptingMessageModal.modalText : null } }></div> */} +// <div className="colorLine"></div> +// </div> +// </div> +// </div>; +// } +// return null; +// } +// } +// InterruptingMessage.propTypes = { +// messageName: PropTypes.string.isRequired, +// messageHTML: PropTypes.string.isRequired, +// style: PropTypes.string.isRequired, +// repetition: PropTypes.number.isRequired, // manual toggle to refresh an existing message +// onClose: PropTypes.func.isRequired +// }; + +export function useIsVisible(ref) { + const [isIntersecting, setIntersecting] = useState(false); + console.log(ref); + + useEffect(() => { + const observer = new IntersectionObserver( + ([entry]) => setIntersecting(entry.isIntersecting), + { threshhold: 1.0 } + ); + + if (ref.current) observer.observe(ref.current); + return () => { + observer.disconnect(); }; - this.settings = { - "modal": { - "trackingName": "Interrupting Message", - "showDelay": 1000, + }, [ref]); + + return isIntersecting; +} + +function OnInView({ children, onVisible }) { + const elementRef = useRef(); + + useEffect(() => { + const observer = new IntersectionObserver( + (entries) => { + const [entry] = entries; + if (entry.isIntersecting) { + onVisible(); + } }, - "banner": { - "trackingName": "Banner Message", - "showDelay": 1, - } - }[this.props.style]; - } - componentDidMount() { - if (this.shouldShow()) { - this.delayedShow(); + { threshold: 1 } + ); + + if (elementRef.current) { + observer.observe(elementRef.current); } - } - shouldShow() { + + return () => { + if (elementRef.current) { + observer.unobserve(elementRef.current); + } + }; + }, [onVisible]); + + return <div ref={elementRef}>{children}</div>; +} + + +const InterruptingMessage = ({ + messageName, + messageHTML, + style, + repetition, + onClose, +}) => { + const [timesUp, setTimesUp] = useState(false); + const [animationStarted, setAnimationStarted] = useState(false); + const [hasSeenModal, setHasSeenModal] = useState(false); + const strapi = useContext(StrapiDataContext); + + const ref = useRef(); + const isVisible = useIsVisible(ref); + + const settings = { + trackingName: "Interrupting Message", + showDelay: 5000, + }; + + // Need to figure out caching for Strapi so multiple queries aren't made on different page loads + // Use user context to determine whether this is valid for a user? + // Maybe user context should be used to find if there's a compatible modal + const shouldShow = () => { + if (!strapi.interruptingMessageModal) return false; const excludedPaths = ["/donate", "/mobile", "/app", "/ways-to-give"]; return excludedPaths.indexOf(window.location.pathname) === -1; + }; + + const closeModal = () => { + if (onClose) onClose(); + // Mark as read with cookies and track closing actions + setHasSeenModal(true); // should be acknolwedge instead of seen because there was an interaction + // Sefaria.interruptingMessageModal = null; } - delayedShow() { - setTimeout(function() { - this.setState({timesUp: true}); - $("#interruptingMessage .button").click(this.close); - $("#interruptingMessage .trackedAction").click(this.trackAction); - this.showAorB(); - this.animateOpen(); - }.bind(this), this.settings.showDelay); - } - animateOpen() { - setTimeout(function() { - if (this.props.style === "banner" && $("#s2").hasClass("headerOnly")) { $("body").addClass("hasBannerMessage"); } - this.setState({animationStarted: true}); - this.trackOpen(); - }.bind(this), 50); - } - showAorB() { - // Allow random A/B testing if items are tagged ".optionA", ".optionB" - const $message = $(ReactDOM.findDOMNode(this)); - if ($message.find(".optionA").length) { - console.log("rand show") - Math.random() > 0.5 ? $(".optionA").show() : $(".optionB").show(); - } - } - close() { - this.markAsRead(); - this.props.onClose(); - if (this.props.style === "banner" && $("#s2").hasClass("headerOnly")) { $("body").removeClass("hasBannerMessage"); } - } - trackOpen() { - Sefaria.track.event(this.settings.trackingName, "open", this.props.messageName, { nonInteraction: true }); - } - trackAction() { - Sefaria.track.event(this.settings.trackingName, "action", this.props.messageName, { nonInteraction: true }); - } - markAsRead() { - Sefaria._api("/api/interrupting-messages/read/" + this.props.messageName, function (data) {}); - var cookieName = this.props.messageName + "_" + this.props.repetition; - $.cookie(cookieName, true, { path: "/", expires: 14 }); - Sefaria.track.event(this.settings.trackingName, "read", this.props.messageName, { nonInteraction: true }); - Sefaria.interruptingMessage = null; + + const trackImpression = () => { + console.log("We've got visibility!"); + // track impression here } - render() { - if (!this.state.timesUp) { return null; } - - if (this.props.style === "banner") { - return <div id="bannerMessage" className={this.state.animationStarted ? "" : "hidden"}> - <div id="bannerMessageContent" dangerouslySetInnerHTML={ {__html: this.props.messageHTML} }></div> - <div id="bannerMessageClose" onClick={this.close}>×</div> - </div>; - - } else if (this.props.style === "modal") { - return <div id="interruptingMessageBox" className={this.state.animationStarted ? "" : "hidden"}> - <div id="interruptingMessageOverlay"></div> - <div id="interruptingMessage"> - <div className="colorLine"></div> - <div id="interruptingMessageContentBox" className="hasColorLine"> - <div id="interruptingMessageClose" onClick={this.close}>×</div> - <div id="interruptingMessageContent" dangerouslySetInnerHTML={ {__html: this.props.messageHTML} }></div> - <div className="colorLine"></div> + + useEffect(() => { + if (shouldShow()) { + const timeoutId = setTimeout(() => { + setTimesUp(true); + // Other stuff here + }, settings.showDelay); + return () => clearTimeout(timeoutId); // clearTimeout on component unmount + } + }, [strapi.interruptingMessageModal, settings.showDelay]); // execute useEffect when the modal or showDelay changes + + // useEffect(() => { + // if (timesUp) { + // const timeoutId = setTimeout(() => { + // // Track open action + // setAnimationStarted(true); + // }, 50); + // return () => clearTimeout(timeoutId); // clearTimeout on component unmount + // } + // }, [timesUp]); // execute useEffect when timesUp changes + + if (!timesUp) return null; + console.log("data for the component"); + console.log(strapi.interruptingMessageModal); + + if (!hasSeenModal) { + console.log("rendering component") + return ( + <OnInView onVisible={trackImpression}> + <div + id="interruptingMessageBox" + className={timesUp ? "" : "hidden"} + > + <div id="interruptingMessageOverlay"></div> + <div id="interruptingMessage"> + <div className="colorLine"></div> + <div id="interruptingMessageContentBox" className="hasColorLine"> + <div id="interruptingMessageClose" onClick={closeModal}> + × </div> + {/* <div id="interruptingMessageContent" dangerouslySetInnerHTML={ {__html: strapi.interruptingMessageModal ? strapi.interruptingMessageModal.modalText : null } }></div> */} + <div id="interruptingMessageContent"> + <style> + { + `#highHolidayDonation { + width: 410px; + max-height: 100%; + max-width: 100%; + } + .interface-english #highHolidayDonation { + text-align: left; + } + .interface-hebrew #highHolidayDonation { + text-align: right; + direction: rtl; + } + #highHolidayDonation p { + color: #555; + } + .interface-hebrew p.int-en { + display: none; + } + #highHolidayDonation p .int-en { + font-family: "adobe-garamond-pro", Georgia, serif; + } + #highHolidayDonation p .int-he { + font-family: "adobe-garamond-pro", Georgia, serif; + /* font-family: "Heebo", sans-serif; */ + } + #highHolidayDonation p.sub { + color: #999; + font-size: 12px; + font-family: "Roboto", "Helvetica Neue", Helvetica, sans-serif; + } + #highHolidayDonation p { + margin-top: 0; + } + #highHolidayDonation .button { + margin-bottom: 20px; + } + #highHolidayDonation img { + max-width: 100%; + } + #highHolidayDonation .buttons{ + text-align: right; + } + .leader { + font-weight: bold; + } + .center{ + text-align: center; + } + #email-input-wrapper { + display: flex; + align-items: flex-start; + flex-direction: column; + } + .newsletterInput#email-input { + width: 300px; + padding: 10px; + margin-bottom: 20px; + border-radius: 7px; + border: 1px solid #EEE; + color: #333; + }` + } + </style> + <div id="highHolidayDonation"> + <p> + <span className="int-en"> + {strapi.interruptingMessageModal.modalText} + </span> + </p> + <div className="buttons"> + <a className="button int-en" target="_blank" href={strapi.interruptingMessageModal.buttonURL} onClick={closeModal}> + <span className="int-en">{strapi.interruptingMessageModal.buttonText}</span> + </a> + </div> + </div> + </div> + <div className="colorLine"></div> </div> - </div>; - } + </div> + </div> + </OnInView> + ); + } else { return null; } -} -InterruptingMessage.propTypes = { - messageName: PropTypes.string.isRequired, - messageHTML: PropTypes.string.isRequired, - style: PropTypes.string.isRequired, - repetition: PropTypes.number.isRequired, // manual toggle to refresh an existing message - onClose: PropTypes.func.isRequired }; +// InterruptingMessage.propTypes = { +// messageName: PropTypes.string.isRequired, +// messageHTML: PropTypes.string.isRequired, +// style: PropTypes.string.isRequired, +// repetition: PropTypes.number.isRequired, +// onClose: PropTypes.func.isRequired +// }; + const NBox = ({ content, n, stretch, gap=0 }) => { // Wrap a list of elements into an n-column flexbox diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index 1ea540c751..6982956b97 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -9,7 +9,7 @@ import $ from './sefaria/sefariaJquery'; import EditCollectionPage from './EditCollectionPage'; import Footer from './Footer'; import SearchState from './sefaria/searchState'; -import {ContentLanguageContext, AdContext, StrapiDataProvider, ExampleComponent} from './context'; +import {ContentLanguageContext, AdContext, StrapiDataProvider, ExampleComponent, StrapiDataContext} from './context'; import { ContestLandingPage, RemoteLearningPage, @@ -1961,6 +1961,7 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { .flat() .filter(ref => !!ref); const deDupedTriggers = [...new Set(triggers.map(JSON.stringify))].map(JSON.parse).map(x => x.toLowerCase()); + // How do I get the user type? const context = { isDebug: this.props._debug, isLoggedIn: Sefaria._uid, @@ -2151,13 +2152,13 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { {panels} </div>) : null; - var interruptingMessage = Sefaria.interruptingMessage ? - (<InterruptingMessage - messageName={Sefaria.interruptingMessage.name} - messageHTML={Sefaria.interruptingMessage.html} - style={Sefaria.interruptingMessage.style} - repetition={Sefaria.interruptingMessage.repetition} - onClose={this.rerender} />) : <Promotions rerender={this.rerender} adType="banner"/>; + // var interruptingMessage = Sefaria.interruptingMessage ? + // (<InterruptingMessage + // messageName={Sefaria.interruptingMessage.name} + // messageHTML={Sefaria.interruptingMessage.html} + // style={Sefaria.interruptingMessage.style} + // repetition={Sefaria.interruptingMessage.repetition} + // onClose={this.rerender} />) : <Promotions rerender={this.rerender} adType="banner"/>; const sefariaModal = ( <SignUpModal onClose={this.toggleSignUpModal} @@ -2174,23 +2175,32 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { classDict[interfaceLangClass] = true; var classes = classNames(classDict); +// const strapi = useContext(StrapiDataContext); +// const { interruptingMessageModal } = useContext(StrapiDataContext); + return ( - <StrapiDataProvider> - <AdContext.Provider value={this.getUserContext()}> - <div id="readerAppWrap"> - {interruptingMessage} - <div className={classes} onClick={this.handleInAppLinkClick}> - {header} - {panels} - {sefariaModal} - {communityPagePreviewControls} - {beitMidrashPanel} - <CookiesNotification /> - {/* <ExampleComponent /> */} - </div> - </div> - </AdContext.Provider> - </StrapiDataProvider> + <StrapiDataProvider> + <AdContext.Provider value={this.getUserContext()}> + <div id="readerAppWrap"> + <InterruptingMessage + messageName={Sefaria.interruptingMessage.name} + messageHTML={Sefaria.interruptingMessage.html} + style={Sefaria.interruptingMessage.style} + repetition={Sefaria.interruptingMessage.repetition} + // onClose={this.rerender} + /> + <div className={classes} onClick={this.handleInAppLinkClick}> + {header} + {panels} + {sefariaModal} + {communityPagePreviewControls} + {beitMidrashPanel} + <CookiesNotification /> + {/* <ExampleComponent /> */} + </div> + </div> + </AdContext.Provider> + </StrapiDataProvider> ); } } diff --git a/static/js/context.js b/static/js/context.js index 4387d1ac2c..d2e4fe4276 100644 --- a/static/js/context.js +++ b/static/js/context.js @@ -14,9 +14,25 @@ function StrapiDataProvider({ children }) { const [dataFromStrapiHasBeenReceived, setDataFromStrapiHasBeenReceived] = useState(false); const [strapiData, setStrapiData] = useState(null); + const [interruptingMessageModal, setInterruptingMessageModal] = useState(null); useEffect(() => { const getStrapiData = async () => { try { + let getDateWithoutTime = (date) => date.toISOString().split("T")[0]; + let getJSONDateStringInLocalTimeZone = (date) => { + let parts = getDateWithoutTime(date).split("-"); + return new Date(parts[0], parts[1] - 1, parts[2]).toJSON(); + }; + let currentDate = new Date(); + let oneWeekFromNow = new Date(); + oneWeekFromNow.setDate(currentDate.getDate() + 7); + currentDate.setDate(currentDate.getDate() - 2); // Fix time management, previous code got time 1 hour in the future in UTC + let startDate = getJSONDateStringInLocalTimeZone(currentDate); + let endDate = getJSONDateStringInLocalTimeZone(oneWeekFromNow); + console.log(startDate); + console.log(endDate); + const query = `{"query":"# Write your query or mutation here\\nquery {\\n banners(filters: {\\n bannerStartDate: { gte: \\"${startDate}\\" }\\n and: [{ bannerEndDate: { lte: \\"${endDate}\\" } }]\\n } ) {\\n data {\\n id\\n attributes {\\n bannerEndDate\\n bannerStartDate\\n bannerText\\n buttonText\\n buttonURL\\n createdAt\\n locale\\n publishedAt\\n shouldDeployOnMobile\\n showToNewVisitors\\n showToNonSustainers\\n showToReturningVisitors\\n showToSustainers\\n updatedAt\\n }\\n }\\n }\\n modals(filters: {\\n modalStartDate: { gte: \\"${startDate}\\" }\\n and: [{ modalEndDate: { lte: \\"${endDate}\\" } }]\\n } ) {\\n data {\\n id\\n attributes {\\n buttonText\\n buttonURL\\n createdAt\\n locale\\n modalEndDate\\n modalStartDate\\n modalText\\n publishedAt\\n shouldDeployOnMobile\\n showToNewVisitors\\n showToNonSustainers\\n showToReturningVisitors\\n showToSustainers\\n updatedAt\\n }\\n }\\n }\\n sidebarAds(filters: {\\n startTime: { gte: \\"${startDate}\\" }\\n and: [{ endTime: { lte: \\"${endDate}\\" } }]\\n } ){\\n data {\\n id\\n attributes {\\n ButtonAboveOrBelow\\n Title\\n bodyText\\n buttonText\\n buttonUrl\\n createdAt\\n debug\\n endTime\\n hasBlueBackground\\n internalCampaignId\\n keywords\\n locale\\n publishedAt\\n showTo\\n startTime\\n updatedAt\\n }\\n }\\n }\\n}"}`; + console.log(query); const result = fetch("http://localhost:1337/graphql", { method: "POST", // *GET, POST, PUT, DELETE, etc. mode: "cors", // no-cors, *cors, same-origin @@ -27,12 +43,30 @@ function StrapiDataProvider({ children }) { }, redirect: "follow", // manual, *follow, error referrerPolicy: "no-referrer", // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url - body: '{"query":"# Write your query or mutation here\\nquery {\\n sidebarAds(\\n filters: {\\n startTime: { gte: \\"2023-06-19T04:00:00.000Z\\" }\\n and: [{ endTime: { lte: \\"2023-06-29T04:00:00.000Z\\" } }]\\n }\\n ) {\\n data {\\n id\\n attributes {\\n ButtonAboveOrBelow\\n Title\\n bodyText\\n buttonText\\n buttonUrl\\n createdAt\\n debug\\n endTime\\n hasBlueBackground\\n internalCampaignId\\n keywords\\n locale\\n publishedAt\\n showTo\\n startTime\\n updatedAt\\n }\\n }\\n }\\n}\\n"}', + body: query, }) .then((response) => response.json()) .then((result) => { setStrapiData(result.data); setDataFromStrapiHasBeenReceived(true); + // maybe sort by start date to choose which one should have a greater priority if more than one compatible one exists + // e.g. there are modals with overlapping time frames + let modals = result.data?.modals?.data; + console.log(modals); + const currentDate = new Date(); + if (modals?.length) { + // if they end up being sorted, the first one will be the compatible one + let modal = modals.find(modal => + currentDate >= new Date(modal.attributes.modalStartDate) && + currentDate <= new Date(modal.attributes.modalEndDate) + ); + console.log("found acceptable modal:"); + console.log(modal); + if (modal) { + console.log("setting the modal"); + setInterruptingMessageModal(modal.attributes); + } + } }); } catch (error) { console.error("Failed to get strapi data", error); @@ -43,7 +77,7 @@ function StrapiDataProvider({ children }) { return ( <StrapiDataContext.Provider - value={{ dataFromStrapiHasBeenReceived, strapiData }} + value={{ dataFromStrapiHasBeenReceived, strapiData, interruptingMessageModal }} > {children} </StrapiDataContext.Provider> From 46fb9648381ca4e9bbb1d5630b1ceb6338cb7476 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Mon, 10 Jul 2023 11:30:12 -0400 Subject: [PATCH 078/756] Start of rough draft for handling new visitors and returning visitors and determining whether the modal has been seen or not --- static/js/Misc.jsx | 166 +++++++++++++++++++++++++------------------ static/js/context.js | 2 +- 2 files changed, 98 insertions(+), 70 deletions(-) diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 45d5a2c845..0142a4c4b8 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -2162,24 +2162,6 @@ SignUpModal.propTypes = { // onClose: PropTypes.func.isRequired // }; -export function useIsVisible(ref) { - const [isIntersecting, setIntersecting] = useState(false); - console.log(ref); - - useEffect(() => { - const observer = new IntersectionObserver( - ([entry]) => setIntersecting(entry.isIntersecting), - { threshhold: 1.0 } - ); - - if (ref.current) observer.observe(ref.current); - return () => { - observer.disconnect(); - }; - }, [ref]); - - return isIntersecting; -} function OnInView({ children, onVisible }) { const elementRef = useRef(); @@ -2209,6 +2191,24 @@ function OnInView({ children, onVisible }) { return <div ref={elementRef}>{children}</div>; } +// let isNewVisitor = JSON.parse(localStorage.getItem("isNewVisitor")); +function isNewVisitor() { + return ( + "isNewVisitor" in sessionStorage || + ("isNewVisitor" in localStorage && + JSON.parse(localStorage.getItem("isNewVisitor"))) + ); +} + +function isReturningVisitor() { + return ( + !isNewVisitor() && + "isReturningVisitor" in localStorage && + JSON.parse(localStorage.getItem("isReturningVisitor")) + ); +} +let shouldShowModal = false; + const InterruptingMessage = ({ messageName, @@ -2218,16 +2218,11 @@ const InterruptingMessage = ({ onClose, }) => { const [timesUp, setTimesUp] = useState(false); - const [animationStarted, setAnimationStarted] = useState(false); const [hasSeenModal, setHasSeenModal] = useState(false); const strapi = useContext(StrapiDataContext); - - const ref = useRef(); - const isVisible = useIsVisible(ref); - const settings = { trackingName: "Interrupting Message", - showDelay: 5000, + showDelay: 500, }; // Need to figure out caching for Strapi so multiple queries aren't made on different page loads @@ -2235,23 +2230,64 @@ const InterruptingMessage = ({ // Maybe user context should be used to find if there's a compatible modal const shouldShow = () => { if (!strapi.interruptingMessageModal) return false; + if (!shouldShowModal) return false; const excludedPaths = ["/donate", "/mobile", "/app", "/ways-to-give"]; - return excludedPaths.indexOf(window.location.pathname) === -1; + return !JSON.parse(sessionStorage.getItem('modal_' + strapi.interruptingMessageModal.internalModalName)) && excludedPaths.indexOf(window.location.pathname) === -1; }; const closeModal = () => { if (onClose) onClose(); // Mark as read with cookies and track closing actions + sessionStorage.setItem('modal_' + strapi.interruptingMessageModal.internalModalName, 'true'); // maybe use modals as key and manipulate object entered setHasSeenModal(true); // should be acknolwedge instead of seen because there was an interaction // Sefaria.interruptingMessageModal = null; - } + }; const trackImpression = () => { console.log("We've got visibility!"); // track impression here - } + }; useEffect(() => { + if (Sefaria._uid) { + console.log("hitting logged in user"); + try { + localStorage.setItem("isNewVisitor", "false"); + sessionStorage.setItem("isNewVisitor", "false"); + localStorage.setItem("isReturningVisitor", "true"); + } catch { + shouldShowModal = true; + } + if (strapi.interruptingMessageModal?.showToReturningVisitors) { + shouldShowModal = true; + } + } else { + if (!isNewVisitor() && !isReturningVisitor()) { + console.log("not new visitor or returning visitor"); // first time here + try { + localStorage.setItem("isNewVisitor", "false"); + sessionStorage.setItem("isNewVisitor", "true"); + localStorage.setItem("isReturningVisitor", "true"); // This will make the current visitor a returning one once their session is cleared + // sessionStorage.setItem("isReturningVisitor", "false"); + } catch { + shouldShowModal = true; + } + } else if (isReturningVisitor()) { + console.log("returning visitor"); + if (strapi.interruptingMessageModal?.showToReturningVisitors) { + shouldShowModal = true; + } + } else if (isNewVisitor()) { + console.log("new visitor"); + if (strapi.interruptingMessageModal?.showToNewVisitors) { + shouldShowModal = true; + } + } + } + if (strapi.interruptingMessageModal && !strapi.interruptingMessageModal.showToNewVisitors && !strapi.interruptingMessageModal.showToReturningVisitors) { // Show to everyone if there is no state passed from Strapi + shouldShowModal = true; + } + if (shouldShow()) { const timeoutId = setTimeout(() => { setTimesUp(true); @@ -2261,40 +2297,26 @@ const InterruptingMessage = ({ } }, [strapi.interruptingMessageModal, settings.showDelay]); // execute useEffect when the modal or showDelay changes - // useEffect(() => { - // if (timesUp) { - // const timeoutId = setTimeout(() => { - // // Track open action - // setAnimationStarted(true); - // }, 50); - // return () => clearTimeout(timeoutId); // clearTimeout on component unmount - // } - // }, [timesUp]); // execute useEffect when timesUp changes - if (!timesUp) return null; console.log("data for the component"); console.log(strapi.interruptingMessageModal); if (!hasSeenModal) { - console.log("rendering component") + console.log("rendering component"); return ( <OnInView onVisible={trackImpression}> - <div - id="interruptingMessageBox" - className={timesUp ? "" : "hidden"} - > - <div id="interruptingMessageOverlay"></div> - <div id="interruptingMessage"> - <div className="colorLine"></div> - <div id="interruptingMessageContentBox" className="hasColorLine"> - <div id="interruptingMessageClose" onClick={closeModal}> - × - </div> - {/* <div id="interruptingMessageContent" dangerouslySetInnerHTML={ {__html: strapi.interruptingMessageModal ? strapi.interruptingMessageModal.modalText : null } }></div> */} - <div id="interruptingMessageContent"> - <style> - { - `#highHolidayDonation { + <div id="interruptingMessageBox" className={timesUp ? "" : "hidden"}> + <div id="interruptingMessageOverlay"></div> + <div id="interruptingMessage"> + <div className="colorLine"></div> + <div id="interruptingMessageContentBox" className="hasColorLine"> + <div id="interruptingMessageClose" onClick={closeModal}> + × + </div> + {/* <div id="interruptingMessageContent" dangerouslySetInnerHTML={ {__html: strapi.interruptingMessageModal ? strapi.interruptingMessageModal.modalText : null } }></div> */} + <div id="interruptingMessageContent"> + <style> + {`#highHolidayDonation { width: 410px; max-height: 100%; max-width: 100%; @@ -2354,26 +2376,32 @@ const InterruptingMessage = ({ border-radius: 7px; border: 1px solid #EEE; color: #333; - }` - } - </style> - <div id="highHolidayDonation"> - <p> - <span className="int-en"> - {strapi.interruptingMessageModal.modalText} - </span> - </p> - <div className="buttons"> - <a className="button int-en" target="_blank" href={strapi.interruptingMessageModal.buttonURL} onClick={closeModal}> - <span className="int-en">{strapi.interruptingMessageModal.buttonText}</span> - </a> + }`} + </style> + <div id="highHolidayDonation"> + <p> + <span className="int-en"> + {strapi.interruptingMessageModal.modalText} + </span> + </p> + <div className="buttons"> + <a + className="button int-en" + target="_blank" + href={strapi.interruptingMessageModal.buttonURL} + onClick={closeModal} + > + <span className="int-en"> + {strapi.interruptingMessageModal.buttonText} + </span> + </a> + </div> </div> </div> + <div className="colorLine"></div> </div> - <div className="colorLine"></div> </div> </div> - </div> </OnInView> ); } else { diff --git a/static/js/context.js b/static/js/context.js index d2e4fe4276..4a0f6e599c 100644 --- a/static/js/context.js +++ b/static/js/context.js @@ -31,7 +31,7 @@ function StrapiDataProvider({ children }) { let endDate = getJSONDateStringInLocalTimeZone(oneWeekFromNow); console.log(startDate); console.log(endDate); - const query = `{"query":"# Write your query or mutation here\\nquery {\\n banners(filters: {\\n bannerStartDate: { gte: \\"${startDate}\\" }\\n and: [{ bannerEndDate: { lte: \\"${endDate}\\" } }]\\n } ) {\\n data {\\n id\\n attributes {\\n bannerEndDate\\n bannerStartDate\\n bannerText\\n buttonText\\n buttonURL\\n createdAt\\n locale\\n publishedAt\\n shouldDeployOnMobile\\n showToNewVisitors\\n showToNonSustainers\\n showToReturningVisitors\\n showToSustainers\\n updatedAt\\n }\\n }\\n }\\n modals(filters: {\\n modalStartDate: { gte: \\"${startDate}\\" }\\n and: [{ modalEndDate: { lte: \\"${endDate}\\" } }]\\n } ) {\\n data {\\n id\\n attributes {\\n buttonText\\n buttonURL\\n createdAt\\n locale\\n modalEndDate\\n modalStartDate\\n modalText\\n publishedAt\\n shouldDeployOnMobile\\n showToNewVisitors\\n showToNonSustainers\\n showToReturningVisitors\\n showToSustainers\\n updatedAt\\n }\\n }\\n }\\n sidebarAds(filters: {\\n startTime: { gte: \\"${startDate}\\" }\\n and: [{ endTime: { lte: \\"${endDate}\\" } }]\\n } ){\\n data {\\n id\\n attributes {\\n ButtonAboveOrBelow\\n Title\\n bodyText\\n buttonText\\n buttonUrl\\n createdAt\\n debug\\n endTime\\n hasBlueBackground\\n internalCampaignId\\n keywords\\n locale\\n publishedAt\\n showTo\\n startTime\\n updatedAt\\n }\\n }\\n }\\n}"}`; + const query = `{"query":"# Write your query or mutation here\\nquery {\\n banners(filters: {\\n bannerStartDate: { gte: \\"${startDate}\\" }\\n and: [{ bannerEndDate: { lte: \\"${endDate}\\" } }]\\n } ) {\\n data {\\n id\\n attributes {\\n bannerEndDate\\n bannerStartDate\\n bannerText\\n buttonText\\n buttonURL\\n createdAt\\n locale\\n publishedAt\\n shouldDeployOnMobile\\n showToNewVisitors\\n showToNonSustainers\\n showToReturningVisitors\\n showToSustainers\\n updatedAt\\n }\\n }\\n }\\n modals(filters: {\\n modalStartDate: { gte: \\"${startDate}\\" }\\n and: [{ modalEndDate: { lte: \\"${endDate}\\" } }]\\n } ) {\\n data {\\n id\\n attributes {\\n internalModalName\\n buttonText\\n buttonURL\\n createdAt\\n locale\\n modalEndDate\\n modalStartDate\\n modalText\\n publishedAt\\n shouldDeployOnMobile\\n showToNewVisitors\\n showToNonSustainers\\n showToReturningVisitors\\n showToSustainers\\n updatedAt\\n }\\n }\\n }\\n sidebarAds(filters: {\\n startTime: { gte: \\"${startDate}\\" }\\n and: [{ endTime: { lte: \\"${endDate}\\" } }]\\n } ){\\n data {\\n id\\n attributes {\\n ButtonAboveOrBelow\\n Title\\n bodyText\\n buttonText\\n buttonUrl\\n createdAt\\n debug\\n endTime\\n hasBlueBackground\\n internalCampaignId\\n keywords\\n locale\\n publishedAt\\n showTo\\n startTime\\n updatedAt\\n }\\n }\\n }\\n}"}`; console.log(query); const result = fetch("http://localhost:1337/graphql", { method: "POST", // *GET, POST, PUT, DELETE, etc. From a0ddafaf0ee1a358a60dffa8af3372b64427213f Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Mon, 10 Jul 2023 16:01:47 -0400 Subject: [PATCH 079/756] Adds event tracking for when a modal is viewed, when the close button is clicked, and when the donate button is clicked --- static/js/Misc.jsx | 51 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 0142a4c4b8..252e0ccbcc 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -2175,7 +2175,7 @@ function OnInView({ children, onVisible }) { } }, { threshold: 1 } - ); + ); if (elementRef.current) { observer.observe(elementRef.current); @@ -2186,7 +2186,7 @@ function OnInView({ children, onVisible }) { observer.unobserve(elementRef.current); } }; - }, [onVisible]); + }, [onVisible]); return <div ref={elementRef}>{children}</div>; } @@ -2209,7 +2209,6 @@ function isReturningVisitor() { } let shouldShowModal = false; - const InterruptingMessage = ({ messageName, messageHTML, @@ -2222,7 +2221,7 @@ const InterruptingMessage = ({ const strapi = useContext(StrapiDataContext); const settings = { trackingName: "Interrupting Message", - showDelay: 500, + showDelay: 2000, }; // Need to figure out caching for Strapi so multiple queries aren't made on different page loads @@ -2232,20 +2231,36 @@ const InterruptingMessage = ({ if (!strapi.interruptingMessageModal) return false; if (!shouldShowModal) return false; const excludedPaths = ["/donate", "/mobile", "/app", "/ways-to-give"]; - return !JSON.parse(sessionStorage.getItem('modal_' + strapi.interruptingMessageModal.internalModalName)) && excludedPaths.indexOf(window.location.pathname) === -1; + return ( + !JSON.parse( + sessionStorage.getItem( + "modal_" + strapi.interruptingMessageModal.internalModalName + ) + ) && excludedPaths.indexOf(window.location.pathname) === -1 + ); }; - const closeModal = () => { + const closeModal = (eventDescription) => { if (onClose) onClose(); - // Mark as read with cookies and track closing actions - sessionStorage.setItem('modal_' + strapi.interruptingMessageModal.internalModalName, 'true'); // maybe use modals as key and manipulate object entered + sessionStorage.setItem( + "modal_" + strapi.interruptingMessageModal.internalModalName, + "true" + ); // maybe use modals as key and manipulate object entered + console.log(eventDescription); + gtag("event", "modal_interacted_with_" + eventDescription, { + campaignID: strapi.interruptingMessageModal.internalModalName, + adType: "modal", + }); setHasSeenModal(true); // should be acknolwedge instead of seen because there was an interaction // Sefaria.interruptingMessageModal = null; }; const trackImpression = () => { console.log("We've got visibility!"); - // track impression here + gtag("event", "modal_viewed", { + campaignID: strapi.interruptingMessageModal.internalModalName, + adType: "modal", + }); }; useEffect(() => { @@ -2284,7 +2299,12 @@ const InterruptingMessage = ({ } } } - if (strapi.interruptingMessageModal && !strapi.interruptingMessageModal.showToNewVisitors && !strapi.interruptingMessageModal.showToReturningVisitors) { // Show to everyone if there is no state passed from Strapi + if ( + strapi.interruptingMessageModal && + !strapi.interruptingMessageModal.showToNewVisitors && + !strapi.interruptingMessageModal.showToReturningVisitors + ) { + // Show to everyone if there is no state passed from Strapi shouldShowModal = true; } @@ -2310,7 +2330,12 @@ const InterruptingMessage = ({ <div id="interruptingMessage"> <div className="colorLine"></div> <div id="interruptingMessageContentBox" className="hasColorLine"> - <div id="interruptingMessageClose" onClick={closeModal}> + <div + id="interruptingMessageClose" + onClick={() => { + closeModal("close_clicked"); + }} + > × </div> {/* <div id="interruptingMessageContent" dangerouslySetInnerHTML={ {__html: strapi.interruptingMessageModal ? strapi.interruptingMessageModal.modalText : null } }></div> */} @@ -2389,7 +2414,9 @@ const InterruptingMessage = ({ className="button int-en" target="_blank" href={strapi.interruptingMessageModal.buttonURL} - onClick={closeModal} + onClick={() => { + closeModal("donate_button_clicked"); + }} > <span className="int-en"> {strapi.interruptingMessageModal.buttonText} From 1a8941236a145641583adae8bfe70da4cd29711b Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Tue, 11 Jul 2023 10:45:55 -0400 Subject: [PATCH 080/756] Adds support for determining whether a user is a sustainer in the front end and a demo for using it to determine whether a modal should be shown --- reader/views.py | 2 ++ static/js/Misc.jsx | 11 ++++++++++- static/js/sefaria/sefaria.js | 1 + 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/reader/views.py b/reader/views.py index 00b42b4a22..c90003d34f 100644 --- a/reader/views.py +++ b/reader/views.py @@ -205,6 +205,7 @@ def base_props(request): "slug": profile.slug if profile else "", "is_moderator": request.user.is_staff, "is_editor": UserWrapper(user_obj=request.user).has_permission_group("Editors"), + "is_sustainer": profile.is_sustainer, "full_name": profile.full_name, "profile_pic_url": profile.profile_pic_url, "is_history_enabled": profile.settings.get("reading_history", True), @@ -226,6 +227,7 @@ def base_props(request): "slug": "", "is_moderator": False, "is_editor": False, + "is_sustainer": False, "full_name": "", "profile_pic_url": "", "is_history_enabled": True, diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 252e0ccbcc..b19e8963e7 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -2251,7 +2251,7 @@ const InterruptingMessage = ({ campaignID: strapi.interruptingMessageModal.internalModalName, adType: "modal", }); - setHasSeenModal(true); // should be acknolwedge instead of seen because there was an interaction + setHasSeenModal(true); // should be interacted instead of seen because there was an interaction // Sefaria.interruptingMessageModal = null; }; @@ -2276,6 +2276,15 @@ const InterruptingMessage = ({ if (strapi.interruptingMessageModal?.showToReturningVisitors) { shouldShowModal = true; } + if (Sefaria.is_sustainer) { + console.log("we got ourselves a beautiful sustainer!"); + } + if (Sefaria.is_sustainer && strapi.interruptingMessageModal?.showToSustainers) { + shouldShowModal = true + } + else if (!Sefaria.is_sustainer && strapi.interruptingMessageModal?.showToNonSustainers) { + shouldShowModal = true; + } } else { if (!isNewVisitor() && !isReturningVisitor()) { console.log("not new visitor or returning visitor"); // first time here diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index 2cd4fee1b2..e9e17cdc6b 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -2959,6 +2959,7 @@ Sefaria.unpackBaseProps = function(props){ "slug", "is_moderator", "is_editor", + "is_sustainer", "full_name", "profile_pic_url", "is_history_enabled", From 98a7f2ea621e8aa78133b9232bc185d525e0ca65 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Wed, 12 Jul 2023 10:49:52 -0400 Subject: [PATCH 081/756] Reorganizes and refactors code to be cleaner. Also fixes a bug with determining whether a user is a new visitor or not --- static/js/Misc.jsx | 158 ++++++++++++++++++---------------------- static/js/ReaderApp.jsx | 11 +++ static/js/context.js | 16 ++-- 3 files changed, 92 insertions(+), 93 deletions(-) diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index b19e8963e7..8573801002 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -2162,7 +2162,7 @@ SignUpModal.propTypes = { // onClose: PropTypes.func.isRequired // }; - +// Write comments explaining how this works function OnInView({ children, onVisible }) { const elementRef = useRef(); @@ -2194,7 +2194,8 @@ function OnInView({ children, onVisible }) { // let isNewVisitor = JSON.parse(localStorage.getItem("isNewVisitor")); function isNewVisitor() { return ( - "isNewVisitor" in sessionStorage || + ("isNewVisitor" in sessionStorage && + JSON.parse(sessionStorage.getItem("isNewVisitor"))) || ("isNewVisitor" in localStorage && JSON.parse(localStorage.getItem("isNewVisitor"))) ); @@ -2207,7 +2208,6 @@ function isReturningVisitor() { JSON.parse(localStorage.getItem("isReturningVisitor")) ); } -let shouldShowModal = false; const InterruptingMessage = ({ messageName, @@ -2217,45 +2217,26 @@ const InterruptingMessage = ({ onClose, }) => { const [timesUp, setTimesUp] = useState(false); - const [hasSeenModal, setHasSeenModal] = useState(false); + const [hasInteractedWithModal, setHasInteractedWithModal] = useState(false); const strapi = useContext(StrapiDataContext); - const settings = { - trackingName: "Interrupting Message", - showDelay: 2000, + const showDelay = 5000; + + const markModalAsHasBeenInteractedWith = (modalName) => { + sessionStorage.setItem("modal_" + modalName, "true"); }; - // Need to figure out caching for Strapi so multiple queries aren't made on different page loads - // Use user context to determine whether this is valid for a user? - // Maybe user context should be used to find if there's a compatible modal - const shouldShow = () => { - if (!strapi.interruptingMessageModal) return false; - if (!shouldShowModal) return false; - const excludedPaths = ["/donate", "/mobile", "/app", "/ways-to-give"]; - return ( - !JSON.parse( - sessionStorage.getItem( - "modal_" + strapi.interruptingMessageModal.internalModalName - ) - ) && excludedPaths.indexOf(window.location.pathname) === -1 - ); + const hasModalBeenInteractedWith = (modalName) => { + return JSON.parse(sessionStorage.getItem("modal_" + modalName)); }; - const closeModal = (eventDescription) => { - if (onClose) onClose(); - sessionStorage.setItem( - "modal_" + strapi.interruptingMessageModal.internalModalName, - "true" - ); // maybe use modals as key and manipulate object entered - console.log(eventDescription); + const trackModalInteraction = (modalName, eventDescription) => { gtag("event", "modal_interacted_with_" + eventDescription, { - campaignID: strapi.interruptingMessageModal.internalModalName, + campaignID: modalName, adType: "modal", }); - setHasSeenModal(true); // should be interacted instead of seen because there was an interaction - // Sefaria.interruptingMessageModal = null; }; - const trackImpression = () => { + const trackModalImpression = () => { console.log("We've got visibility!"); gtag("event", "modal_viewed", { campaignID: strapi.interruptingMessageModal.internalModalName, @@ -2263,77 +2244,77 @@ const InterruptingMessage = ({ }); }; - useEffect(() => { - if (Sefaria._uid) { - console.log("hitting logged in user"); - try { - localStorage.setItem("isNewVisitor", "false"); - sessionStorage.setItem("isNewVisitor", "false"); - localStorage.setItem("isReturningVisitor", "true"); - } catch { - shouldShowModal = true; - } - if (strapi.interruptingMessageModal?.showToReturningVisitors) { - shouldShowModal = true; - } - if (Sefaria.is_sustainer) { - console.log("we got ourselves a beautiful sustainer!"); - } - if (Sefaria.is_sustainer && strapi.interruptingMessageModal?.showToSustainers) { - shouldShowModal = true - } - else if (!Sefaria.is_sustainer && strapi.interruptingMessageModal?.showToNonSustainers) { - shouldShowModal = true; - } - } else { - if (!isNewVisitor() && !isReturningVisitor()) { - console.log("not new visitor or returning visitor"); // first time here - try { - localStorage.setItem("isNewVisitor", "false"); - sessionStorage.setItem("isNewVisitor", "true"); - localStorage.setItem("isReturningVisitor", "true"); // This will make the current visitor a returning one once their session is cleared - // sessionStorage.setItem("isReturningVisitor", "false"); - } catch { - shouldShowModal = true; - } - } else if (isReturningVisitor()) { - console.log("returning visitor"); - if (strapi.interruptingMessageModal?.showToReturningVisitors) { - shouldShowModal = true; - } - } else if (isNewVisitor()) { - console.log("new visitor"); - if (strapi.interruptingMessageModal?.showToNewVisitors) { - shouldShowModal = true; - } - } - } + // Need to figure out caching for Strapi so multiple queries aren't made on different page loads + // Use user context to determine whether this is valid for a user? + // Maybe user context should be used to find if there's a compatible modal + const shouldShow = () => { + if (!strapi.interruptingMessageModal) return false; if ( - strapi.interruptingMessageModal && - !strapi.interruptingMessageModal.showToNewVisitors && - !strapi.interruptingMessageModal.showToReturningVisitors - ) { - // Show to everyone if there is no state passed from Strapi + hasModalBeenInteractedWith( + strapi.interruptingMessageModal.internalModalName + ) + ) + return false; + + let shouldShowModal = false; + + let noUserKindIsSet = ![ + strapi.interruptingMessageModal.showToReturningVisitors, + strapi.interruptingMessageModal.showToNewVisitors, + strapi.interruptingMessageModal.showToSustainers, + strapi.interruptingMessageModal.showToNonSustainers, + ].some((p) => p); + if ( + Sefaria._uid && + ((Sefaria.is_sustainer && + strapi.interruptingMessageModal.showToSustainers) || + (!Sefaria.is_sustainer && + strapi.interruptingMessageModal.showToNonSustainers)) + ) shouldShowModal = true; - } + else if ( + (isReturningVisitor() && + strapi.interruptingMessageModal.showToReturningVisitors) || + (isNewVisitor() && strapi.interruptingMessageModal.showToNewVisitors) + ) + shouldShowModal = true; + else if (noUserKindIsSet) shouldShowModal = true; + if (!shouldShowModal) return false; + const excludedPaths = ["/donate", "/mobile", "/app", "/ways-to-give"]; + return excludedPaths.indexOf(window.location.pathname) === -1; + }; + + const closeModal = (eventDescription) => { + if (onClose) onClose(); + console.log(eventDescription); + markModalAsHasBeenInteractedWith( + strapi.interruptingMessageModal.internalModalName + ); + setHasInteractedWithModal(true); + trackModalInteraction( + strapi.interruptingMessageModal.internalModalName, + eventDescription + ); + }; + + useEffect(() => { if (shouldShow()) { const timeoutId = setTimeout(() => { setTimesUp(true); - // Other stuff here - }, settings.showDelay); + }, showDelay); return () => clearTimeout(timeoutId); // clearTimeout on component unmount } - }, [strapi.interruptingMessageModal, settings.showDelay]); // execute useEffect when the modal or showDelay changes + }, [strapi.interruptingMessageModal]); // execute useEffect when the modal changes if (!timesUp) return null; console.log("data for the component"); console.log(strapi.interruptingMessageModal); - if (!hasSeenModal) { + if (!hasInteractedWithModal) { console.log("rendering component"); return ( - <OnInView onVisible={trackImpression}> + <OnInView onVisible={trackModalImpression}> <div id="interruptingMessageBox" className={timesUp ? "" : "hidden"}> <div id="interruptingMessageOverlay"></div> <div id="interruptingMessage"> @@ -2444,6 +2425,7 @@ const InterruptingMessage = ({ return null; } }; +InterruptingMessage.displayName = "InterruptingMessage"; // InterruptingMessage.propTypes = { // messageName: PropTypes.string.isRequired, diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index 6982956b97..7aadfded42 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -199,6 +199,17 @@ class ReaderApp extends Component { document.addEventListener('click', this.handleInAppClickWithModifiers, {capture: true}); // Save all initial panels to recently viewed this.state.panels.map(this.saveLastPlace); + // Initialize entries for first-time visitors to determine if they are new or returning + if (!("isNewVisitor" in localStorage) && !("isReturningVisitor" in localStorage)) { + sessionStorage.setItem("isNewVisitor", "true"); + // Setting these at this time will make the current new visitor a returning one once their session is cleared + localStorage.setItem("isNewVisitor", "false"); + localStorage.setItem("isReturningVisitor", "true"); + } else if (Sefaria._uid) { + localStorage.setItem("isNewVisitor", "false"); + sessionStorage.setItem("isNewVisitor", "false"); + localStorage.setItem("isReturningVisitor", "true"); + } } componentWillUnmount() { window.removeEventListener("popstate", this.handlePopState); diff --git a/static/js/context.js b/static/js/context.js index 4a0f6e599c..790cf305fa 100644 --- a/static/js/context.js +++ b/static/js/context.js @@ -14,7 +14,8 @@ function StrapiDataProvider({ children }) { const [dataFromStrapiHasBeenReceived, setDataFromStrapiHasBeenReceived] = useState(false); const [strapiData, setStrapiData] = useState(null); - const [interruptingMessageModal, setInterruptingMessageModal] = useState(null); + const [interruptingMessageModal, setInterruptingMessageModal] = + useState(null); useEffect(() => { const getStrapiData = async () => { try { @@ -56,9 +57,10 @@ function StrapiDataProvider({ children }) { const currentDate = new Date(); if (modals?.length) { // if they end up being sorted, the first one will be the compatible one - let modal = modals.find(modal => - currentDate >= new Date(modal.attributes.modalStartDate) && - currentDate <= new Date(modal.attributes.modalEndDate) + let modal = modals.find( + (modal) => + currentDate >= new Date(modal.attributes.modalStartDate) && + currentDate <= new Date(modal.attributes.modalEndDate) ); console.log("found acceptable modal:"); console.log(modal); @@ -77,7 +79,11 @@ function StrapiDataProvider({ children }) { return ( <StrapiDataContext.Provider - value={{ dataFromStrapiHasBeenReceived, strapiData, interruptingMessageModal }} + value={{ + dataFromStrapiHasBeenReceived, + strapiData, + interruptingMessageModal, + }} > {children} </StrapiDataContext.Provider> From 56b69c8e1a83419e56a588dcb092ec90c62b93f6 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Thu, 13 Jul 2023 14:02:05 -0400 Subject: [PATCH 082/756] Remove cruft of props passed to InterruptingMessage component since it will be using the Strapi context as parameters by default --- static/js/ReaderApp.jsx | 38 ++++++++++++++++---------------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index 7aadfded42..2e5b55db6e 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -2190,28 +2190,22 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { // const { interruptingMessageModal } = useContext(StrapiDataContext); return ( - <StrapiDataProvider> - <AdContext.Provider value={this.getUserContext()}> - <div id="readerAppWrap"> - <InterruptingMessage - messageName={Sefaria.interruptingMessage.name} - messageHTML={Sefaria.interruptingMessage.html} - style={Sefaria.interruptingMessage.style} - repetition={Sefaria.interruptingMessage.repetition} - // onClose={this.rerender} - /> - <div className={classes} onClick={this.handleInAppLinkClick}> - {header} - {panels} - {sefariaModal} - {communityPagePreviewControls} - {beitMidrashPanel} - <CookiesNotification /> - {/* <ExampleComponent /> */} - </div> - </div> - </AdContext.Provider> - </StrapiDataProvider> + <StrapiDataProvider> + <AdContext.Provider value={this.getUserContext()}> + <div id="readerAppWrap"> + <InterruptingMessage /> + <div className={classes} onClick={this.handleInAppLinkClick}> + {header} + {panels} + {sefariaModal} + {communityPagePreviewControls} + {beitMidrashPanel} + <CookiesNotification /> + {/* <ExampleComponent /> */} + </div> + </div> + </AdContext.Provider> + </StrapiDataProvider> ); } } From 408bd688911ae1bbc342d8e575d3d807f41fa3f9 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Thu, 13 Jul 2023 14:33:35 -0400 Subject: [PATCH 083/756] Simplifies determining who is returning and new visitor code --- static/js/Misc.jsx | 7 ++----- static/js/ReaderApp.jsx | 9 ++++----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 8573801002..5acca87a1b 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -2191,13 +2191,10 @@ function OnInView({ children, onVisible }) { return <div ref={elementRef}>{children}</div>; } -// let isNewVisitor = JSON.parse(localStorage.getItem("isNewVisitor")); function isNewVisitor() { return ( - ("isNewVisitor" in sessionStorage && - JSON.parse(sessionStorage.getItem("isNewVisitor"))) || - ("isNewVisitor" in localStorage && - JSON.parse(localStorage.getItem("isNewVisitor"))) + "isNewVisitor" in sessionStorage && + JSON.parse(sessionStorage.getItem("isNewVisitor")) ); } diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index 2e5b55db6e..6b1e4091c6 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -199,14 +199,13 @@ class ReaderApp extends Component { document.addEventListener('click', this.handleInAppClickWithModifiers, {capture: true}); // Save all initial panels to recently viewed this.state.panels.map(this.saveLastPlace); - // Initialize entries for first-time visitors to determine if they are new or returning - if (!("isNewVisitor" in localStorage) && !("isReturningVisitor" in localStorage)) { + // Initialize entries for first-time visitors to determine if they are new or returning presently or in the future + if (!("isNewVisitor" in sessionStorage) && !("isReturningVisitor" in localStorage)) { sessionStorage.setItem("isNewVisitor", "true"); - // Setting these at this time will make the current new visitor a returning one once their session is cleared - localStorage.setItem("isNewVisitor", "false"); + // Setting this at this time will make the current new visitor a returning one once their session is cleared localStorage.setItem("isReturningVisitor", "true"); } else if (Sefaria._uid) { - localStorage.setItem("isNewVisitor", "false"); + // A logged in user is automatically a returning visitor sessionStorage.setItem("isNewVisitor", "false"); localStorage.setItem("isReturningVisitor", "true"); } From 5784dcb1c757774357f12eb613c5fdfecb598668 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Fri, 14 Jul 2023 16:12:58 -0400 Subject: [PATCH 084/756] Adds initial support for banners that work with Strapi --- package-lock.json | 8 +-- package.json | 2 +- static/js/Misc.jsx | 139 +++++++++++++++++++++++++++++++++++++++ static/js/Promotions.jsx | 64 +++++++++--------- static/js/ReaderApp.jsx | 4 ++ static/js/context.js | 22 ++++++- 6 files changed, 202 insertions(+), 37 deletions(-) diff --git a/package-lock.json b/package-lock.json index 26f5d4edc1..0a2bd0d49b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "cheerio": "^0.22.0", "classnames": "^2.2.5", "cookie-parser": "^1.4.2", - "core-js": "^3.15.2", + "core-js": "^3.31.1", "css-modules-require-hook": "^4.2.3", "deep-merge": "^1.0.0", "diff-match-patch": "^1.0.0", @@ -4613,9 +4613,9 @@ } }, "node_modules/core-js": { - "version": "3.30.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.30.1.tgz", - "integrity": "sha512-ZNS5nbiSwDTq4hFosEDqm65izl2CWmLz0hARJMyNQBgkUZMIF51cQiMvIQKA6hvuaeWxQDP3hEedM1JZIgTldQ==", + "version": "3.31.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.31.1.tgz", + "integrity": "sha512-2sKLtfq1eFST7l7v62zaqXacPc7uG8ZAya8ogijLhTtaKNcpzpB4TMoTw2Si+8GYKRwFPMMtUT0263QFWFfqyQ==", "hasInstallScript": true, "funding": { "type": "opencollective", diff --git a/package.json b/package.json index ffd4e225b3..9097316243 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "cheerio": "^0.22.0", "classnames": "^2.2.5", "cookie-parser": "^1.4.2", - "core-js": "^3.15.2", + "core-js": "^3.31.1", "css-modules-require-hook": "^4.2.3", "deep-merge": "^1.0.0", "diff-match-patch": "^1.0.0", diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 5acca87a1b..4b24883758 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -2191,6 +2191,8 @@ function OnInView({ children, onVisible }) { return <div ref={elementRef}>{children}</div>; } +// User could be new visitor when there isn't anything in sessionStorage either... +// Maybe don't check if it's in there or have extra conditional function isNewVisitor() { return ( "isNewVisitor" in sessionStorage && @@ -2245,6 +2247,7 @@ const InterruptingMessage = ({ // Use user context to determine whether this is valid for a user? // Maybe user context should be used to find if there's a compatible modal const shouldShow = () => { + console.log("checking whether to show modal or not"); if (!strapi.interruptingMessageModal) return false; if ( hasModalBeenInteractedWith( @@ -2424,6 +2427,141 @@ const InterruptingMessage = ({ }; InterruptingMessage.displayName = "InterruptingMessage"; +const Banner = ({ messageName, messageHTML, style, repetition, onClose }) => { + const [timesUp, setTimesUp] = useState(false); + const [hasInteractedWithBanner, setHasInteractedWithBanner] = useState(false); + const strapi = useContext(StrapiDataContext); + const showDelay = 5000; + + const markBannerAsHasBeenInteractedWith = (bannerName) => { + sessionStorage.setItem("banner_" + bannerName, "true"); + }; + + const hasBannerBeenInteractedWith = (bannerName) => { + return JSON.parse(sessionStorage.getItem("banner_" + bannerName)); + }; + + const trackBannerInteraction = (bannerName, eventDescription) => { + gtag("event", "banner_interacted_with_" + eventDescription, { + campaignID: bannerName, + adType: "banner", + }); + }; + + const trackBannerImpression = () => { + console.log("We've got visibility!"); + gtag("event", "banner_viewed", { + campaignID: strapi.banner.internalBannerName, + adType: "banner", + }); + }; + + const shouldShow = () => { + console.log("checking whether to show banner or not"); + if (!strapi.banner) return false; + if (hasBannerBeenInteractedWith(strapi.banner.internalBannerName)) + return false; + + let shouldShowBanner = false; + + let noUserKindIsSet = ![ + strapi.banner.showToReturningVisitors, + strapi.banner.showToNewVisitors, + strapi.banner.showToSustainers, + strapi.banner.showToNonSustainers, + ].some((p) => p); + if ( + Sefaria._uid && + ((Sefaria.is_sustainer && strapi.banner.showToSustainers) || + (!Sefaria.is_sustainer && strapi.banner.showToNonSustainers)) + ) + shouldShowBanner = true; + else if ( + (isReturningVisitor() && strapi.banner.showToReturningVisitors) || + (isNewVisitor() && strapi.banner.showToNewVisitors) + ) + shouldShowBanner = true; + else if (noUserKindIsSet) shouldShowBanner = true; + if (!shouldShowBanner) return false; + + const excludedPaths = ["/donate", "/mobile", "/app", "/ways-to-give"]; + return excludedPaths.indexOf(window.location.pathname) === -1; + }; + + const closeBanner = (eventDescription) => { + if (onClose) onClose(); + console.log(eventDescription); + markBannerAsHasBeenInteractedWith(strapi.banner.internalBannerName); + setHasInteractedWithBanner(true); + trackBannerInteraction( + strapi.banner.internalBannerName, + eventDescription + ); + }; + + useEffect(() => { + if (shouldShow()) { + console.log("reaching here..."); + + const timeoutId = setTimeout(() => { + // s2 is the div that contains the React root and needs to be manipulated by traditional DOM methods + if (document.getElementById("s2").classList.contains("headerOnly")) { + document.body.classList.add("hasBannerMessage"); + } + setTimesUp(true); + }, showDelay); + return () => clearTimeout(timeoutId); // clearTimeout on component unmount + } + }, [strapi.banner]); // execute useEffect when the modal changes + + if (!timesUp) return null; + console.log("data for the component"); + console.log(strapi.banner); + + if (!hasInteractedWithBanner) { + console.log("rendering component"); + return ( + <OnInView onVisible={trackBannerImpression}> + <div id="bannerMessage" className={timesUp ? "" : "hidden"}> + <div id="bannerMessageContent"> + <div id="bannerTextBox"> + <span className="int-en">{strapi.banner.bannerText}</span> + <span className="int-he"> + ספריית ספריא מנגישה יותר מ-300 מיליון מלים של טקסטים יהודיים + ברחבי העולם. לכבוד שבועות, אנא תמכו היום בספריה שמסייעת ללימוד + שלכם על-ידי קבלת מעמד של ידידי ספריא. + </span> + </div> + <div id="bannerButtonBox"> + <a + className="button white int-en" + href="https://sefaria.nationbuilder.com/sustainers_e" + > + <span>Sustain Sefaria</span> + </a> + <a + className="button white int-he" + href="https://sefaria.nationbuilder.com/sustainers_e" + > + <span>לתרומה חודשית</span> + </a> + </div> + </div> + <div id="bannerMessageClose">×</div> + <div id="bannerMessageClose" onClick={closeBanner}> + × + </div> + </div> + </OnInView> + ); + } else { + return null; + } +}; + +Banner.displayName = "Banner"; + + // InterruptingMessage.propTypes = { // messageName: PropTypes.string.isRequired, // messageHTML: PropTypes.string.isRequired, @@ -3272,6 +3410,7 @@ export { FollowButton, GlobalWarningMessage, InterruptingMessage, + Banner, InterfaceText, EnglishText, HebrewText, diff --git a/static/js/Promotions.jsx b/static/js/Promotions.jsx index d132e0cdce..c76a61aadb 100644 --- a/static/js/Promotions.jsx +++ b/static/js/Promotions.jsx @@ -18,38 +18,40 @@ const Promotions = ({ adType, rerender }) => { const sidebarAds = strapi.strapiData.sidebarAds.data; - sidebarAds.forEach((sidebarAd) => { - sidebarAd = sidebarAd.attributes; - console.log(JSON.stringify(sidebarAd, null, 2)); - let keywordTargetsArray = sidebarAd.keywords - .split(",") - .map((x) => x.trim().toLowerCase()); - Sefaria._inAppAds.push({ - campaignId: sidebarAd.internalCampaignId, - title: sidebarAd.Title, - bodyText: sidebarAd.bodyText, - buttonText: sidebarAd.buttonText, - buttonUrl: sidebarAd.buttonUrl, - buttonIcon: "", - buttonLocation: sidebarAd.buttonUrl, - adType: "sidebar", - hasBlueBackground: sidebarAd.hasBlueBackground, - repetition: 5, - buttonStyle: "", - trigger: { - showTo: sidebarAd.showTo, - interfaceLang: Sefaria.translateISOLanguageCode( - sidebarAd.locale - ).toLowerCase(), - dt_start: Date.parse(sidebarAd.startTime), - dt_end: Date.parse(sidebarAd.endTime), - keywordTargets: keywordTargetsArray, - excludeKeywordTargets: [], - }, - debug: sidebarAd.debug, + if (sidebarAds) { + sidebarAds.forEach((sidebarAd) => { + sidebarAd = sidebarAd.attributes; + console.log(JSON.stringify(sidebarAd, null, 2)); + let keywordTargetsArray = sidebarAd.keywords + .split(",") + .map((x) => x.trim().toLowerCase()); + Sefaria._inAppAds.push({ + campaignId: sidebarAd.internalCampaignId, + title: sidebarAd.Title, + bodyText: sidebarAd.bodyText, + buttonText: sidebarAd.buttonText, + buttonUrl: sidebarAd.buttonUrl, + buttonIcon: "", + buttonLocation: sidebarAd.buttonUrl, + adType: "sidebar", + hasBlueBackground: sidebarAd.hasBlueBackground, + repetition: 5, + buttonStyle: "", + trigger: { + showTo: sidebarAd.showTo, + interfaceLang: Sefaria.translateISOLanguageCode( + sidebarAd.locale + ).toLowerCase(), + dt_start: Date.parse(sidebarAd.startTime), + dt_end: Date.parse(sidebarAd.endTime), + keywordTargets: keywordTargetsArray, + excludeKeywordTargets: [], + }, + debug: sidebarAd.debug, + }); }); - }); - setInAppAds(Sefaria._inAppAds); + setInAppAds(Sefaria._inAppAds); + } } }, [strapi.dataFromStrapiHasBeenReceived]); // empty array happens when the page loads, equivalent of didcomponentmount diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index 6b1e4091c6..dc2d7ecdf5 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -26,6 +26,7 @@ import { import { SignUpModal, InterruptingMessage, + Banner, CookiesNotification, CommunityPagePreviewControls } from './Misc'; @@ -2176,6 +2177,7 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { modalContentKind={this.state.modalContentKind} /> ); + const communityPagePreviewControls = this.props.communityPreview ? <CommunityPagePreviewControls date={this.props.communityPreview} /> : null; @@ -2193,6 +2195,8 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { <AdContext.Provider value={this.getUserContext()}> <div id="readerAppWrap"> <InterruptingMessage /> + {/* <Banner onClose={this.setContainerMode} /> */} + <Banner /> <div className={classes} onClick={this.handleInAppLinkClick}> {header} {panels} diff --git a/static/js/context.js b/static/js/context.js index 790cf305fa..62a9f45639 100644 --- a/static/js/context.js +++ b/static/js/context.js @@ -16,6 +16,7 @@ function StrapiDataProvider({ children }) { const [strapiData, setStrapiData] = useState(null); const [interruptingMessageModal, setInterruptingMessageModal] = useState(null); + const [banner, setBanner] = useState(null); useEffect(() => { const getStrapiData = async () => { try { @@ -32,7 +33,7 @@ function StrapiDataProvider({ children }) { let endDate = getJSONDateStringInLocalTimeZone(oneWeekFromNow); console.log(startDate); console.log(endDate); - const query = `{"query":"# Write your query or mutation here\\nquery {\\n banners(filters: {\\n bannerStartDate: { gte: \\"${startDate}\\" }\\n and: [{ bannerEndDate: { lte: \\"${endDate}\\" } }]\\n } ) {\\n data {\\n id\\n attributes {\\n bannerEndDate\\n bannerStartDate\\n bannerText\\n buttonText\\n buttonURL\\n createdAt\\n locale\\n publishedAt\\n shouldDeployOnMobile\\n showToNewVisitors\\n showToNonSustainers\\n showToReturningVisitors\\n showToSustainers\\n updatedAt\\n }\\n }\\n }\\n modals(filters: {\\n modalStartDate: { gte: \\"${startDate}\\" }\\n and: [{ modalEndDate: { lte: \\"${endDate}\\" } }]\\n } ) {\\n data {\\n id\\n attributes {\\n internalModalName\\n buttonText\\n buttonURL\\n createdAt\\n locale\\n modalEndDate\\n modalStartDate\\n modalText\\n publishedAt\\n shouldDeployOnMobile\\n showToNewVisitors\\n showToNonSustainers\\n showToReturningVisitors\\n showToSustainers\\n updatedAt\\n }\\n }\\n }\\n sidebarAds(filters: {\\n startTime: { gte: \\"${startDate}\\" }\\n and: [{ endTime: { lte: \\"${endDate}\\" } }]\\n } ){\\n data {\\n id\\n attributes {\\n ButtonAboveOrBelow\\n Title\\n bodyText\\n buttonText\\n buttonUrl\\n createdAt\\n debug\\n endTime\\n hasBlueBackground\\n internalCampaignId\\n keywords\\n locale\\n publishedAt\\n showTo\\n startTime\\n updatedAt\\n }\\n }\\n }\\n}"}`; + const query = `{"query":"# Write your query or mutation here\\nquery {\\n banners(filters: {\\n bannerStartDate: { gte: \\"${startDate}\\" }\\n and: [{ bannerEndDate: { lte: \\"${endDate}\\" } }]\\n } ) {\\n data {\\n id\\n attributes {\\n internalBannerName\\n bannerEndDate\\n bannerStartDate\\n bannerText\\n buttonText\\n buttonURL\\n createdAt\\n locale\\n publishedAt\\n shouldDeployOnMobile\\n showToNewVisitors\\n showToNonSustainers\\n showToReturningVisitors\\n showToSustainers\\n updatedAt\\n }\\n }\\n }\\n modals(filters: {\\n modalStartDate: { gte: \\"${startDate}\\" }\\n and: [{ modalEndDate: { lte: \\"${endDate}\\" } }]\\n } ) {\\n data {\\n id\\n attributes {\\n internalModalName\\n buttonText\\n buttonURL\\n createdAt\\n locale\\n modalEndDate\\n modalStartDate\\n modalText\\n publishedAt\\n shouldDeployOnMobile\\n showToNewVisitors\\n showToNonSustainers\\n showToReturningVisitors\\n showToSustainers\\n updatedAt\\n }\\n }\\n }\\n sidebarAds(filters: {\\n startTime: { gte: \\"${startDate}\\" }\\n and: [{ endTime: { lte: \\"${endDate}\\" } }]\\n } ){\\n data {\\n id\\n attributes {\\n ButtonAboveOrBelow\\n Title\\n bodyText\\n buttonText\\n buttonUrl\\n createdAt\\n debug\\n endTime\\n hasBlueBackground\\n internalCampaignId\\n keywords\\n locale\\n publishedAt\\n showTo\\n startTime\\n updatedAt\\n }\\n }\\n }\\n}"}`; console.log(query); const result = fetch("http://localhost:1337/graphql", { method: "POST", // *GET, POST, PUT, DELETE, etc. @@ -54,6 +55,9 @@ function StrapiDataProvider({ children }) { // e.g. there are modals with overlapping time frames let modals = result.data?.modals?.data; console.log(modals); + let banners = result.data?.banners?.data; + console.log(banners); + const currentDate = new Date(); if (modals?.length) { // if they end up being sorted, the first one will be the compatible one @@ -69,6 +73,21 @@ function StrapiDataProvider({ children }) { setInterruptingMessageModal(modal.attributes); } } + + if (banners?.length) { + let b = banners.find( + (b) => + currentDate >= new Date(b.attributes.bannerStartDate) && + currentDate <= new Date(b.attributes.bannerEndDate) + ); + console.log("found acceptable banner:"); + console.log(b); + if (b) { + console.log("setting the banner"); + setBanner(b.attributes); + console.log(b.attributes); + } + } }); } catch (error) { console.error("Failed to get strapi data", error); @@ -83,6 +102,7 @@ function StrapiDataProvider({ children }) { dataFromStrapiHasBeenReceived, strapiData, interruptingMessageModal, + banner, }} > {children} From 64cc7ddf4bc70ee607fccd0c602ad4c76cf6436a Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Fri, 14 Jul 2023 16:19:14 -0400 Subject: [PATCH 085/756] Adds button text and URL for Strapi banners --- static/js/Misc.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 4b24883758..31ba607c6a 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -2535,9 +2535,9 @@ const Banner = ({ messageName, messageHTML, style, repetition, onClose }) => { <div id="bannerButtonBox"> <a className="button white int-en" - href="https://sefaria.nationbuilder.com/sustainers_e" + href={strapi.banner.buttonURL} > - <span>Sustain Sefaria</span> + <span>{strapi.banner.buttonText}</span> </a> <a className="button white int-he" From 5dc47ca976b4f219212306f4dfd1795333d54659 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Wed, 19 Jul 2023 17:33:23 -0400 Subject: [PATCH 086/756] Renames state variable interruptingMessageModal to simply be modal to make a possible refactor easier and FINALLY gets a working literal GraphQL query as a multiline template string --- static/js/context.js | 98 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 92 insertions(+), 6 deletions(-) diff --git a/static/js/context.js b/static/js/context.js index 62a9f45639..955d77d7d8 100644 --- a/static/js/context.js +++ b/static/js/context.js @@ -14,7 +14,7 @@ function StrapiDataProvider({ children }) { const [dataFromStrapiHasBeenReceived, setDataFromStrapiHasBeenReceived] = useState(false); const [strapiData, setStrapiData] = useState(null); - const [interruptingMessageModal, setInterruptingMessageModal] = + const [modal, setModal] = useState(null); const [banner, setBanner] = useState(null); useEffect(() => { @@ -33,8 +33,94 @@ function StrapiDataProvider({ children }) { let endDate = getJSONDateStringInLocalTimeZone(oneWeekFromNow); console.log(startDate); console.log(endDate); - const query = `{"query":"# Write your query or mutation here\\nquery {\\n banners(filters: {\\n bannerStartDate: { gte: \\"${startDate}\\" }\\n and: [{ bannerEndDate: { lte: \\"${endDate}\\" } }]\\n } ) {\\n data {\\n id\\n attributes {\\n internalBannerName\\n bannerEndDate\\n bannerStartDate\\n bannerText\\n buttonText\\n buttonURL\\n createdAt\\n locale\\n publishedAt\\n shouldDeployOnMobile\\n showToNewVisitors\\n showToNonSustainers\\n showToReturningVisitors\\n showToSustainers\\n updatedAt\\n }\\n }\\n }\\n modals(filters: {\\n modalStartDate: { gte: \\"${startDate}\\" }\\n and: [{ modalEndDate: { lte: \\"${endDate}\\" } }]\\n } ) {\\n data {\\n id\\n attributes {\\n internalModalName\\n buttonText\\n buttonURL\\n createdAt\\n locale\\n modalEndDate\\n modalStartDate\\n modalText\\n publishedAt\\n shouldDeployOnMobile\\n showToNewVisitors\\n showToNonSustainers\\n showToReturningVisitors\\n showToSustainers\\n updatedAt\\n }\\n }\\n }\\n sidebarAds(filters: {\\n startTime: { gte: \\"${startDate}\\" }\\n and: [{ endTime: { lte: \\"${endDate}\\" } }]\\n } ){\\n data {\\n id\\n attributes {\\n ButtonAboveOrBelow\\n Title\\n bodyText\\n buttonText\\n buttonUrl\\n createdAt\\n debug\\n endTime\\n hasBlueBackground\\n internalCampaignId\\n keywords\\n locale\\n publishedAt\\n showTo\\n startTime\\n updatedAt\\n }\\n }\\n }\\n}"}`; - console.log(query); + const query = + ` + query { + banners( + filters: { + bannerStartDate: { gte: \"${startDate}\" } + and: [{ bannerEndDate: { lte: \"${endDate}\" } }] + } + ) { + data { + id + attributes { + internalBannerName + bannerEndDate + bannerStartDate + bannerText + buttonText + buttonURL + createdAt + locale + publishedAt + shouldDeployOnMobile + showToNewVisitors + showToNonSustainers + showToReturningVisitors + showToSustainers + updatedAt + } + } + } + modals( + filters: { + modalStartDate: { gte: \"${startDate}\" } + and: [{ modalEndDate: { lte: \"${endDate}\" } }] + } + ) { + data { + id + attributes { + internalModalName + buttonText + buttonURL + createdAt + locale + modalEndDate + modalStartDate + modalText + publishedAt + shouldDeployOnMobile + showToNewVisitors + showToNonSustainers + showToReturningVisitors + showToSustainers + updatedAt + } + } + } + sidebarAds( + filters: { + startTime: { gte: \"${startDate}\" } + and: [{ endTime: { lte: \"${endDate}\" } }] + } + ) { + data { + id + attributes { + ButtonAboveOrBelow + Title + bodyText + buttonText + buttonUrl + createdAt + debug + endTime + hasBlueBackground + internalCampaignId + keywords + locale + publishedAt + showTo + startTime + updatedAt + } + } + } + } + `; + const result = fetch("http://localhost:1337/graphql", { method: "POST", // *GET, POST, PUT, DELETE, etc. mode: "cors", // no-cors, *cors, same-origin @@ -45,7 +131,7 @@ function StrapiDataProvider({ children }) { }, redirect: "follow", // manual, *follow, error referrerPolicy: "no-referrer", // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url - body: query, + body: JSON.stringify({query}), }) .then((response) => response.json()) .then((result) => { @@ -70,7 +156,7 @@ function StrapiDataProvider({ children }) { console.log(modal); if (modal) { console.log("setting the modal"); - setInterruptingMessageModal(modal.attributes); + setModal(modal.attributes); } } @@ -101,7 +187,7 @@ function StrapiDataProvider({ children }) { value={{ dataFromStrapiHasBeenReceived, strapiData, - interruptingMessageModal, + modal, banner, }} > From c44ba40d91b8c3797017ef9fea9d061967cdda20 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Wed, 19 Jul 2023 17:50:39 -0400 Subject: [PATCH 087/756] Adds localization support for Hebrew to the GraphQL query for banners and modals --- static/js/context.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/static/js/context.js b/static/js/context.js index 955d77d7d8..bc8eed86b8 100644 --- a/static/js/context.js +++ b/static/js/context.js @@ -53,6 +53,16 @@ function StrapiDataProvider({ children }) { buttonURL createdAt locale + localizations { + data { + attributes { + locale + buttonText + buttonURL + bannerText + } + } + } publishedAt shouldDeployOnMobile showToNewVisitors @@ -77,6 +87,16 @@ function StrapiDataProvider({ children }) { buttonURL createdAt locale + localizations { + data { + attributes { + locale + buttonText + buttonURL + modalText + } + } + } modalEndDate modalStartDate modalText From 3c3dc25a816dd3192021f8b29cf2987d656a0593 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Wed, 19 Jul 2023 17:53:14 -0400 Subject: [PATCH 088/756] Changes the name of the strapi modal object to be just modal for easier future refactoring. Also adds commented out start of a refactored component for both modals and banners --- static/js/Misc.jsx | 452 ++++++++++++++++++++++++++++----------- static/js/Promotions.jsx | 2 +- 2 files changed, 324 insertions(+), 130 deletions(-) diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 31ba607c6a..bab90df5e7 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -2057,111 +2057,6 @@ SignUpModal.propTypes = { modalContent: PropTypes.object.isRequired, }; - -// class InterruptingMessage extends Component { -// constructor(props) { -// super(props); -// this.displayName = 'InterruptingMessage'; -// this.state = { -// timesUp: false, -// animationStarted: false -// }; -// this.settings = { -// "modal": { -// "trackingName": "Interrupting Message", -// "showDelay": 10000, -// }, -// "banner": { -// "trackingName": "Banner Message", -// "showDelay": 1, -// } -// }[this.props.style]; -// } -// componentDidMount() { -// if (this.shouldShow()) { -// this.delayedShow(); -// } -// } -// shouldShow() { -// const excludedPaths = ["/donate", "/mobile", "/app", "/ways-to-give"]; -// return excludedPaths.indexOf(window.location.pathname) === -1; -// } -// delayedShow() { -// setTimeout(function() { -// this.setState({timesUp: true}); -// $("#interruptingMessage .button").click(this.close); -// $("#interruptingMessage .trackedAction").click(this.trackAction); -// this.showAorB(); -// this.animateOpen(); -// }.bind(this), this.settings.showDelay); -// } -// animateOpen() { -// setTimeout(function() { -// if (this.props.style === "banner" && $("#s2").hasClass("headerOnly")) { $("body").addClass("hasBannerMessage"); } -// this.setState({animationStarted: true}); -// this.trackOpen(); -// }.bind(this), 50); -// } -// showAorB() { -// // Allow random A/B testing if items are tagged ".optionA", ".optionB" -// const $message = $(ReactDOM.findDOMNode(this)); -// if ($message.find(".optionA").length) { -// console.log("rand show") -// Math.random() > 0.5 ? $(".optionA").show() : $(".optionB").show(); -// } -// } -// close() { -// this.markAsRead(); -// this.props.onClose(); -// // if (this.props.style === "banner" && $("#s2").hasClass("headerOnly")) { $("body").removeClass("hasBannerMessage"); } -// } -// trackOpen() { -// Sefaria.track.event(this.settings.trackingName, "open", this.props.messageName, { nonInteraction: true }); -// } -// trackAction() { -// Sefaria.track.event(this.settings.trackingName, "action", this.props.messageName, { nonInteraction: true }); -// } -// markAsRead() { -// Sefaria._api("/api/interrupting-messages/read/" + this.props.messageName, function (data) {}); -// var cookieName = this.props.messageName + "_" + this.props.repetition; -// $.cookie(cookieName, true, { path: "/", expires: 14 }); -// Sefaria.track.event(this.settings.trackingName, "read", this.props.messageName, { nonInteraction: true }); -// Sefaria.interruptingMessage = null; -// } -// render() { -// if (!this.state.timesUp) { return null; } - -// if (this.props.style === "banner") { -// return <div id="bannerMessage" className={this.state.animationStarted ? "" : "hidden"}> -// <div id="bannerMessageContent" dangerouslySetInnerHTML={ {__html: this.props.messageHTML} }></div> -// <div id="bannerMessageClose" onClick={this.close}>×</div> -// </div>; - -// } else if (this.props.style === "modal") { -// return <div id="interruptingMessageBox" className={this.state.animationStarted ? "" : "hidden"}> -// <div id="interruptingMessageOverlay"></div> -// <div id="interruptingMessage"> -// <div className="colorLine"></div> -// <div id="interruptingMessageContentBox" className="hasColorLine"> -// <div id="interruptingMessageClose" onClick={this.close}>×</div> -// <div id="interruptingMessageContent" dangerouslySetInnerHTML={ {__html: this.props.messageHTML} }></div> -// {/* <div id="interruptingMessageContent" dangerouslySetInnerHTML={ {__html: this.strapi.interruptingMessageModal ? interruptingMessageModal.modalText : null } }></div> */} -// <div className="colorLine"></div> -// </div> -// </div> -// </div>; -// } -// return null; -// } -// } -// InterruptingMessage.propTypes = { -// messageName: PropTypes.string.isRequired, -// messageHTML: PropTypes.string.isRequired, -// style: PropTypes.string.isRequired, -// repetition: PropTypes.number.isRequired, // manual toggle to refresh an existing message -// onClose: PropTypes.func.isRequired -// }; - // Write comments explaining how this works function OnInView({ children, onVisible }) { const elementRef = useRef(); @@ -2209,10 +2104,6 @@ function isReturningVisitor() { } const InterruptingMessage = ({ - messageName, - messageHTML, - style, - repetition, onClose, }) => { const [timesUp, setTimesUp] = useState(false); @@ -2238,7 +2129,7 @@ const InterruptingMessage = ({ const trackModalImpression = () => { console.log("We've got visibility!"); gtag("event", "modal_viewed", { - campaignID: strapi.interruptingMessageModal.internalModalName, + campaignID: strapi.modal.internalModalName, adType: "modal", }); }; @@ -2248,34 +2139,35 @@ const InterruptingMessage = ({ // Maybe user context should be used to find if there's a compatible modal const shouldShow = () => { console.log("checking whether to show modal or not"); - if (!strapi.interruptingMessageModal) return false; + if (!strapi.modal) return false; if ( hasModalBeenInteractedWith( - strapi.interruptingMessageModal.internalModalName + strapi.modal.internalModalName ) ) return false; + console.log('lets check a modal'); let shouldShowModal = false; let noUserKindIsSet = ![ - strapi.interruptingMessageModal.showToReturningVisitors, - strapi.interruptingMessageModal.showToNewVisitors, - strapi.interruptingMessageModal.showToSustainers, - strapi.interruptingMessageModal.showToNonSustainers, + strapi.modal.showToReturningVisitors, + strapi.modal.showToNewVisitors, + strapi.modal.showToSustainers, + strapi.modal.showToNonSustainers, ].some((p) => p); if ( Sefaria._uid && ((Sefaria.is_sustainer && - strapi.interruptingMessageModal.showToSustainers) || + strapi.modal.showToSustainers) || (!Sefaria.is_sustainer && - strapi.interruptingMessageModal.showToNonSustainers)) + strapi.modal.showToNonSustainers)) ) shouldShowModal = true; else if ( (isReturningVisitor() && - strapi.interruptingMessageModal.showToReturningVisitors) || - (isNewVisitor() && strapi.interruptingMessageModal.showToNewVisitors) + strapi.modal.showToReturningVisitors) || + (isNewVisitor() && strapi.modal.showToNewVisitors) ) shouldShowModal = true; else if (noUserKindIsSet) shouldShowModal = true; @@ -2289,11 +2181,11 @@ const InterruptingMessage = ({ if (onClose) onClose(); console.log(eventDescription); markModalAsHasBeenInteractedWith( - strapi.interruptingMessageModal.internalModalName + strapi.modal.internalModalName ); setHasInteractedWithModal(true); trackModalInteraction( - strapi.interruptingMessageModal.internalModalName, + strapi.modal.internalModalName, eventDescription ); }; @@ -2305,11 +2197,11 @@ const InterruptingMessage = ({ }, showDelay); return () => clearTimeout(timeoutId); // clearTimeout on component unmount } - }, [strapi.interruptingMessageModal]); // execute useEffect when the modal changes + }, [strapi.modal]); // execute useEffect when the modal changes if (!timesUp) return null; console.log("data for the component"); - console.log(strapi.interruptingMessageModal); + console.log(strapi.modal); if (!hasInteractedWithModal) { console.log("rendering component"); @@ -2328,7 +2220,7 @@ const InterruptingMessage = ({ > × </div> - {/* <div id="interruptingMessageContent" dangerouslySetInnerHTML={ {__html: strapi.interruptingMessageModal ? strapi.interruptingMessageModal.modalText : null } }></div> */} + {/* <div id="interruptingMessageContent" dangerouslySetInnerHTML={ {__html: strapi.modal ? strapi.modal.modalText : null } }></div> */} <div id="interruptingMessageContent"> <style> {`#highHolidayDonation { @@ -2396,20 +2288,20 @@ const InterruptingMessage = ({ <div id="highHolidayDonation"> <p> <span className="int-en"> - {strapi.interruptingMessageModal.modalText} + {strapi.modal.modalText} </span> </p> <div className="buttons"> <a className="button int-en" target="_blank" - href={strapi.interruptingMessageModal.buttonURL} + href={strapi.modal.buttonURL} onClick={() => { closeModal("donate_button_clicked"); }} > <span className="int-en"> - {strapi.interruptingMessageModal.buttonText} + {strapi.modal.buttonText} </span> </a> </div> @@ -2427,7 +2319,7 @@ const InterruptingMessage = ({ }; InterruptingMessage.displayName = "InterruptingMessage"; -const Banner = ({ messageName, messageHTML, style, repetition, onClose }) => { +const Banner = ({ onClose }) => { const [timesUp, setTimesUp] = useState(false); const [hasInteractedWithBanner, setHasInteractedWithBanner] = useState(false); const strapi = useContext(StrapiDataContext); @@ -2561,6 +2453,308 @@ const Banner = ({ messageName, messageHTML, style, repetition, onClose }) => { Banner.displayName = "Banner"; +// const useInterruptingComponent = (componentKind, showDelay, onClose, nameId) => { +// const [timesUp, setTimesUp] = useState(false); +// const [hasComponentBeenInteractedWith, setComponentHasBeenInteractedWith] = useState(false); +// const strapi = useContext(StrapiDataContext); + +// const markComponentAsHasBeenInteractedWith = (name) => { +// sessionStorage.setItem(componentKind + "_" + name, "true"); +// } + +// const hasBeenInteractedWith = (name) => { +// return JSON.parse(sessionStorage.getItem(componentKind + "_" + name)); +// } + +// const trackInteraction = (name, eventDescription) => { +// gtag("event", componentKind + "_interacted_with_" + eventDescription, { +// campaignID: name, +// adType: componentKind, +// }); +// } + +// const trackImpression = (name) => { +// console.log("We've got visibility!"); +// gtag("event", componentKind + "_viewed", { +// campaignID: name, +// adType: componentKind, +// }); +// } + +// const closeComponent = (eventDescription) => { +// if (onClose) onClose(); +// markComponentAsHasBeenInteractedWith(strapiData[nameId]); +// setComponentHasBeenInteractedWith(true); +// trackInteraction(strapiData[nameId], eventDescription); +// } + +// useEffect(() => { +// if (shouldShow()) { +// const timeoutId = setTimeout(() => { +// setTimesUp(true); +// }, showDelay); +// return () => clearTimeout(timeoutId); +// } +// }, [strapi[componentKind]]); + +// return {timesUp, hasComponentBeenInteractedWith, closeComponent, trackImpression, strapiData: strapi[componentKind]}; +// }; + +// const InterruptingMessage = ({ onClose }) => { +// const { timesUp, hasComponentBeenInteractedWith, closeComponent, trackImpression, strapiData } = useInterruptingComponent('modal', 5000, onClose, 'internalModalName'); + +// if (!timesUp) return null; + +// if (!hasBeenInteractedWith) { +// if (!hasInteractedWithModal) { +// console.log("rendering component"); +// return ( +// <OnInView onVisible={trackModalImpression}> +// <div id="interruptingMessageBox" className={timesUp ? "" : "hidden"}> +// <div id="interruptingMessageOverlay"></div> +// <div id="interruptingMessage"> +// <div className="colorLine"></div> +// <div id="interruptingMessageContentBox" className="hasColorLine"> +// <div +// id="interruptingMessageClose" +// onClick={() => { +// closeModal("close_clicked"); +// }} +// > +// × +// </div> +// {/* <div id="interruptingMessageContent" dangerouslySetInnerHTML={ {__html: strapi.modal ? strapi.modal.modalText : null } }></div> */} +// <div id="interruptingMessageContent"> +// <style> +// {`#highHolidayDonation { +// width: 410px; +// max-height: 100%; +// max-width: 100%; +// } +// .interface-english #highHolidayDonation { +// text-align: left; +// } +// .interface-hebrew #highHolidayDonation { +// text-align: right; +// direction: rtl; +// } +// #highHolidayDonation p { +// color: #555; +// } +// .interface-hebrew p.int-en { +// display: none; +// } +// #highHolidayDonation p .int-en { +// font-family: "adobe-garamond-pro", Georgia, serif; +// } +// #highHolidayDonation p .int-he { +// font-family: "adobe-garamond-pro", Georgia, serif; +// /* font-family: "Heebo", sans-serif; */ +// } +// #highHolidayDonation p.sub { +// color: #999; +// font-size: 12px; +// font-family: "Roboto", "Helvetica Neue", Helvetica, sans-serif; +// } +// #highHolidayDonation p { +// margin-top: 0; +// } +// #highHolidayDonation .button { +// margin-bottom: 20px; +// } +// #highHolidayDonation img { +// max-width: 100%; +// } +// #highHolidayDonation .buttons{ +// text-align: right; +// } +// .leader { +// font-weight: bold; +// } +// .center{ +// text-align: center; +// } +// #email-input-wrapper { +// display: flex; +// align-items: flex-start; +// flex-direction: column; +// } +// .newsletterInput#email-input { +// width: 300px; +// padding: 10px; +// margin-bottom: 20px; +// border-radius: 7px; +// border: 1px solid #EEE; +// color: #333; +// }`} +// </style> +// <div id="highHolidayDonation"> +// <p> +// <span className="int-en"> +// {strapi.modal.modalText} +// </span> +// </p> +// <div className="buttons"> +// <a +// className="button int-en" +// target="_blank" +// href={strapi.modal.buttonURL} +// onClick={() => { +// closeModal("donate_button_clicked"); +// }} +// > +// <span className="int-en"> +// {strapi.modal.buttonText} +// </span> +// </a> +// </div> +// </div> +// </div> +// <div className="colorLine"></div> +// </div> +// </div> +// </div> +// </OnInView> +// ); +// } else { +// return null; +// } +// } +// }; + + + +// const InterruptingComponent = ({ componentKind, componentName, showDelay, beforeShowingUp, onClose }) => { +// const [timesUp, setTimesUp] = useState(false); +// const [hasInteractedWithComponent, setHasInteractedWithComponent] = useState(false); +// const strapi = useContext(StrapiDataContext); + +// const markComponentAsHasBeenInteractedWith = (componentName) => { +// sessionStorage.setItem(componentKind + "_" + componentName, "true"); +// }; + +// const hasComponentBeenInteractedWith = (bannerName) => { +// return JSON.parse(sessionStorage.getItem("banner_" + bannerName)); +// }; + +// const trackBannerInteraction = (bannerName, eventDescription) => { +// gtag("event", "banner_interacted_with_" + eventDescription, { +// campaignID: bannerName, +// adType: "banner", +// }); +// }; + +// const trackBannerImpression = () => { +// console.log("We've got visibility!"); +// gtag("event", "banner_viewed", { +// campaignID: strapi.banner.internalBannerName, +// adType: "banner", +// }); +// }; + +// const shouldShow = () => { +// console.log("checking whether to show banner or not"); +// if (!strapi.banner) return false; +// if (hasBannerBeenInteractedWith(strapi.banner.internalBannerName)) +// return false; + +// let shouldShowBanner = false; + +// let noUserKindIsSet = ![ +// strapi.banner.showToReturningVisitors, +// strapi.banner.showToNewVisitors, +// strapi.banner.showToSustainers, +// strapi.banner.showToNonSustainers, +// ].some((p) => p); +// if ( +// Sefaria._uid && +// ((Sefaria.is_sustainer && strapi.banner.showToSustainers) || +// (!Sefaria.is_sustainer && strapi.banner.showToNonSustainers)) +// ) +// shouldShowBanner = true; +// else if ( +// (isReturningVisitor() && strapi.banner.showToReturningVisitors) || +// (isNewVisitor() && strapi.banner.showToNewVisitors) +// ) +// shouldShowBanner = true; +// else if (noUserKindIsSet) shouldShowBanner = true; +// if (!shouldShowBanner) return false; + +// const excludedPaths = ["/donate", "/mobile", "/app", "/ways-to-give"]; +// return excludedPaths.indexOf(window.location.pathname) === -1; +// }; + +// const closeBanner = (eventDescription) => { +// if (onClose) onClose(); +// console.log(eventDescription); +// markBannerAsHasBeenInteractedWith(strapi.banner.internalBannerName); +// setHasInteractedWithBanner(true); +// trackBannerInteraction( +// strapi.banner.internalBannerName, +// eventDescription +// ); +// }; + +// useEffect(() => { +// if (shouldShow()) { +// console.log("reaching here..."); + +// const timeoutId = setTimeout(() => { +// // s2 is the div that contains the React root and needs to be manipulated by traditional DOM methods +// if (document.getElementById("s2").classList.contains("headerOnly")) { +// document.body.classList.add("hasBannerMessage"); +// } +// setTimesUp(true); +// }, showDelay); +// return () => clearTimeout(timeoutId); // clearTimeout on component unmount +// } +// }, [strapi.banner]); // execute useEffect when the modal changes + +// if (!timesUp) return null; +// console.log("data for the component"); +// console.log(strapi.banner); + +// if (!hasInteractedWithBanner) { +// console.log("rendering component"); +// return ( +// <OnInView onVisible={trackBannerImpression}> +// <div id="bannerMessage" className={timesUp ? "" : "hidden"}> +// <div id="bannerMessageContent"> +// <div id="bannerTextBox"> +// <span className="int-en">{strapi.banner.bannerText}</span> +// <span className="int-he"> +// ספריית ספריא מנגישה יותר מ-300 מיליון מלים של טקסטים יהודיים +// ברחבי העולם. לכבוד שבועות, אנא תמכו היום בספריה שמסייעת ללימוד +// שלכם על-ידי קבלת מעמד של ידידי ספריא. +// </span> +// </div> +// <div id="bannerButtonBox"> +// <a +// className="button white int-en" +// href={strapi.banner.buttonURL} +// > +// <span>{strapi.banner.buttonText}</span> +// </a> +// <a +// className="button white int-he" +// href="https://sefaria.nationbuilder.com/sustainers_e" +// > +// <span>לתרומה חודשית</span> +// </a> +// </div> +// </div> +// <div id="bannerMessageClose">×</div> +// <div id="bannerMessageClose" onClick={closeBanner}> +// × +// </div> +// </div> +// </OnInView> +// ); +// } else { +// return null; +// } +// }; + // InterruptingMessage.propTypes = { // messageName: PropTypes.string.isRequired, diff --git a/static/js/Promotions.jsx b/static/js/Promotions.jsx index c76a61aadb..d1f39d97dd 100644 --- a/static/js/Promotions.jsx +++ b/static/js/Promotions.jsx @@ -16,7 +16,7 @@ const Promotions = ({ adType, rerender }) => { console.log("we got some data"); console.log(JSON.stringify(strapi.strapiData, null, 2)); - const sidebarAds = strapi.strapiData.sidebarAds.data; + const sidebarAds = strapi.strapiData?.sidebarAds?.data; if (sidebarAds) { sidebarAds.forEach((sidebarAd) => { From fcb5abd4b6418a51b44128abe17172a11518ed4a Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Thu, 20 Jul 2023 13:47:41 -0400 Subject: [PATCH 089/756] Adds Hebrew/Markdown support for banners and modals --- static/js/Misc.jsx | 41 +++++++++++++++++++++++----------------- static/js/Promotions.jsx | 1 + static/js/context.js | 39 +++++++++++++++++++++++++++++++++----- 3 files changed, 59 insertions(+), 22 deletions(-) diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index bab90df5e7..432f5d7796 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -29,7 +29,7 @@ import TrackG4 from "./sefaria/trackG4"; * <InterfaceText> * <EnglishText>lorem ipsum</EnglishText> * <HebrewText>lorem ipsum</HebrewText> - * </InterfaceText> + * </LText> * ``` * @param children * @returns {JSX.Element} @@ -2287,21 +2287,31 @@ const InterruptingMessage = ({ </style> <div id="highHolidayDonation"> <p> - <span className="int-en"> - {strapi.modal.modalText} - </span> + <InterfaceText markdown={strapi.modal.modalText} /> </p> <div className="buttons"> <a className="button int-en" target="_blank" - href={strapi.modal.buttonURL} + href={strapi.modal.buttonURL.en} onClick={() => { closeModal("donate_button_clicked"); }} > <span className="int-en"> - {strapi.modal.buttonText} + {strapi.modal.buttonText.en} + </span> + </a> + <a + className="button int-he" + target="_blank" + href={strapi.modal.buttonURL.he} + onClick={() => { + closeModal("donate_button_clicked"); + }} + > + <span className="int-he"> + {strapi.modal.buttonText.he} </span> </a> </div> @@ -2351,6 +2361,7 @@ const Banner = ({ onClose }) => { const shouldShow = () => { console.log("checking whether to show banner or not"); if (!strapi.banner) return false; + if (Sefaria.interfaceLang === 'hebrew' && !strapi.banner.locales.includes('he')) return false; if (hasBannerBeenInteractedWith(strapi.banner.internalBannerName)) return false; @@ -2411,31 +2422,27 @@ const Banner = ({ onClose }) => { console.log(strapi.banner); if (!hasInteractedWithBanner) { - console.log("rendering component"); + console.log("rendering banner"); + console.log(strapi.banner.bannerText); return ( <OnInView onVisible={trackBannerImpression}> <div id="bannerMessage" className={timesUp ? "" : "hidden"}> <div id="bannerMessageContent"> <div id="bannerTextBox"> - <span className="int-en">{strapi.banner.bannerText}</span> - <span className="int-he"> - ספריית ספריא מנגישה יותר מ-300 מיליון מלים של טקסטים יהודיים - ברחבי העולם. לכבוד שבועות, אנא תמכו היום בספריה שמסייעת ללימוד - שלכם על-ידי קבלת מעמד של ידידי ספריא. - </span> + <InterfaceText markdown={strapi.banner.bannerText} /> </div> <div id="bannerButtonBox"> <a className="button white int-en" - href={strapi.banner.buttonURL} + href={strapi.banner.buttonURL.en} > - <span>{strapi.banner.buttonText}</span> + <span>{strapi.banner.buttonText.en}</span> </a> <a className="button white int-he" - href="https://sefaria.nationbuilder.com/sustainers_e" + href={strapi.banner.buttonURL.he} > - <span>לתרומה חודשית</span> + <span>{strapi.banner.buttonText.he}</span> </a> </div> </div> diff --git a/static/js/Promotions.jsx b/static/js/Promotions.jsx index d1f39d97dd..794b8b85a2 100644 --- a/static/js/Promotions.jsx +++ b/static/js/Promotions.jsx @@ -224,6 +224,7 @@ const Promotions = ({ adType, rerender }) => { if (!matchingAd) { return null; } + // TODO: change this to use new InterruptingMessage const bannerHtml = createBannerHtml(matchingAd); return ( <InterruptingMessage diff --git a/static/js/context.js b/static/js/context.js index bc8eed86b8..6be5ee62f2 100644 --- a/static/js/context.js +++ b/static/js/context.js @@ -176,22 +176,51 @@ function StrapiDataProvider({ children }) { console.log(modal); if (modal) { console.log("setting the modal"); + if (modal.attributes.localizations?.data?.length) { + let localization_attributes = modal.attributes.localizations.data[0].attributes; + let {locale, ...hebrew_attributes} = localization_attributes; + Object.keys(hebrew_attributes).forEach((attribute) => { + modal.attributes[attribute] = { en: modal.attributes[attribute], he: hebrew_attributes[attribute] } + }); + modal.attributes.locales = ['en', 'he']; + } + else { + ['modalText', 'buttonText', 'buttonURL'].forEach((attribute) => { + modal.attributes[attribute] = { en: modal.attributes[attribute], he: null }; + }); + modal.attributes.locales = ['en']; + } setModal(modal.attributes); } } if (banners?.length) { - let b = banners.find( + let banner = banners.find( (b) => currentDate >= new Date(b.attributes.bannerStartDate) && currentDate <= new Date(b.attributes.bannerEndDate) ); + console.log("found acceptable banner:"); - console.log(b); - if (b) { + console.log(banner); + if (banner) { console.log("setting the banner"); - setBanner(b.attributes); - console.log(b.attributes); + if (banner.attributes.localizations?.data?.length) { + let localization_attributes = banner.attributes.localizations.data[0].attributes; + let {locale, ...hebrew_attributes} = localization_attributes; + Object.keys(hebrew_attributes).forEach((attribute) => { + banner.attributes[attribute] = { en: banner.attributes[attribute], he: hebrew_attributes[attribute] } + }); + banner.attributes.locales = ['en', 'he']; + } else { + // Maybe have the GraphQL return null entries for each key so the same technique can be used from above? + ['bannerText', 'buttonText', 'buttonURL'].forEach((attribute) => { + banner.attributes[attribute] = { en: banner.attributes[attribute], he: null }; + }); + banner.attributes.locales = ['en']; + } + setBanner(banner.attributes); + console.log(banner.attributes); } } }); From 7c7ff4e4d11a5d68aff336f7a8334f707281660b Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Thu, 20 Jul 2023 13:50:03 -0400 Subject: [PATCH 090/756] Fixes unintentional typo --- static/js/Misc.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 432f5d7796..a8faece559 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -29,7 +29,7 @@ import TrackG4 from "./sefaria/trackG4"; * <InterfaceText> * <EnglishText>lorem ipsum</EnglishText> * <HebrewText>lorem ipsum</HebrewText> - * </LText> + * </InterfaceText> * ``` * @param children * @returns {JSX.Element} From 10c4773baa58da6609d89a1498b3e424674fbf2f Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Mon, 24 Jul 2023 01:47:18 -0400 Subject: [PATCH 091/756] Reformats code --- static/js/context.js | 266 ++++++++++++++++++++++--------------------- 1 file changed, 136 insertions(+), 130 deletions(-) diff --git a/static/js/context.js b/static/js/context.js index 6be5ee62f2..0cb6f9e2f0 100644 --- a/static/js/context.js +++ b/static/js/context.js @@ -1,6 +1,7 @@ -import React, { useContext, useEffect, useState } from "react"; +import React, {useContext, useEffect, useState} from "react"; + const ContentLanguageContext = React.createContext({ - language: "english", + language: "english", }); ContentLanguageContext.displayName = "ContentLanguageContext"; //This lets us see this name in the devtools @@ -10,31 +11,31 @@ AdContext.displayName = "AdContext"; const StrapiDataContext = React.createContext({}); StrapiDataContext.displayName = "StrapiDataContext"; -function StrapiDataProvider({ children }) { - const [dataFromStrapiHasBeenReceived, setDataFromStrapiHasBeenReceived] = - useState(false); - const [strapiData, setStrapiData] = useState(null); - const [modal, setModal] = - useState(null); - const [banner, setBanner] = useState(null); - useEffect(() => { - const getStrapiData = async () => { - try { - let getDateWithoutTime = (date) => date.toISOString().split("T")[0]; - let getJSONDateStringInLocalTimeZone = (date) => { - let parts = getDateWithoutTime(date).split("-"); - return new Date(parts[0], parts[1] - 1, parts[2]).toJSON(); - }; - let currentDate = new Date(); - let oneWeekFromNow = new Date(); - oneWeekFromNow.setDate(currentDate.getDate() + 7); - currentDate.setDate(currentDate.getDate() - 2); // Fix time management, previous code got time 1 hour in the future in UTC - let startDate = getJSONDateStringInLocalTimeZone(currentDate); - let endDate = getJSONDateStringInLocalTimeZone(oneWeekFromNow); - console.log(startDate); - console.log(endDate); - const query = - ` +function StrapiDataProvider({children}) { + const [dataFromStrapiHasBeenReceived, setDataFromStrapiHasBeenReceived] = + useState(false); + const [strapiData, setStrapiData] = useState(null); + const [modal, setModal] = + useState(null); + const [banner, setBanner] = useState(null); + useEffect(() => { + const getStrapiData = async () => { + try { + let getDateWithoutTime = (date) => date.toISOString().split("T")[0]; + let getJSONDateStringInLocalTimeZone = (date) => { + let parts = getDateWithoutTime(date).split("-"); + return new Date(parts[0], parts[1] - 1, parts[2]).toJSON(); + }; + let currentDate = new Date(); + let oneWeekFromNow = new Date(); + oneWeekFromNow.setDate(currentDate.getDate() + 7); + currentDate.setDate(currentDate.getDate() - 2); // Fix time management, previous code got time 1 hour in the future in UTC + let startDate = getJSONDateStringInLocalTimeZone(currentDate); + let endDate = getJSONDateStringInLocalTimeZone(oneWeekFromNow); + console.log(startDate); + console.log(endDate); + const query = + ` query { banners( filters: { @@ -140,109 +141,114 @@ function StrapiDataProvider({ children }) { } } `; - - const result = fetch("http://localhost:1337/graphql", { - method: "POST", // *GET, POST, PUT, DELETE, etc. - mode: "cors", // no-cors, *cors, same-origin - cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached - credentials: "same-origin", // include, *same-origin, omit - headers: { - "Content-Type": "application/json", - }, - redirect: "follow", // manual, *follow, error - referrerPolicy: "no-referrer", // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url - body: JSON.stringify({query}), - }) - .then((response) => response.json()) - .then((result) => { - setStrapiData(result.data); - setDataFromStrapiHasBeenReceived(true); - // maybe sort by start date to choose which one should have a greater priority if more than one compatible one exists - // e.g. there are modals with overlapping time frames - let modals = result.data?.modals?.data; - console.log(modals); - let banners = result.data?.banners?.data; - console.log(banners); - const currentDate = new Date(); - if (modals?.length) { - // if they end up being sorted, the first one will be the compatible one - let modal = modals.find( - (modal) => - currentDate >= new Date(modal.attributes.modalStartDate) && - currentDate <= new Date(modal.attributes.modalEndDate) - ); - console.log("found acceptable modal:"); - console.log(modal); - if (modal) { - console.log("setting the modal"); - if (modal.attributes.localizations?.data?.length) { - let localization_attributes = modal.attributes.localizations.data[0].attributes; - let {locale, ...hebrew_attributes} = localization_attributes; - Object.keys(hebrew_attributes).forEach((attribute) => { - modal.attributes[attribute] = { en: modal.attributes[attribute], he: hebrew_attributes[attribute] } - }); - modal.attributes.locales = ['en', 'he']; - } - else { - ['modalText', 'buttonText', 'buttonURL'].forEach((attribute) => { - modal.attributes[attribute] = { en: modal.attributes[attribute], he: null }; - }); - modal.attributes.locales = ['en']; - } - setModal(modal.attributes); - } - } + const result = fetch("http://localhost:1337/graphql", { + method: "POST", // *GET, POST, PUT, DELETE, etc. + mode: "cors", // no-cors, *cors, same-origin + cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached + credentials: "same-origin", // include, *same-origin, omit + headers: { + "Content-Type": "application/json", + }, + redirect: "follow", // manual, *follow, error + referrerPolicy: "no-referrer", // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url + body: JSON.stringify({query}), + }) + .then((response) => response.json()) + .then((result) => { + setStrapiData(result.data); + setDataFromStrapiHasBeenReceived(true); + // maybe sort by start date to choose which one should have a greater priority if more than one compatible one exists + // e.g. there are modals with overlapping time frames + let modals = result.data?.modals?.data; + console.log(modals); + let banners = result.data?.banners?.data; + console.log(banners); - if (banners?.length) { - let banner = banners.find( - (b) => - currentDate >= new Date(b.attributes.bannerStartDate) && - currentDate <= new Date(b.attributes.bannerEndDate) - ); + const currentDate = new Date(); + if (modals?.length) { + // if they end up being sorted, the first one will be the compatible one + let modal = modals.find( + (modal) => + currentDate >= new Date(modal.attributes.modalStartDate) && + currentDate <= new Date(modal.attributes.modalEndDate) + ); + console.log("found acceptable modal:"); + console.log(modal); + if (modal) { + console.log("setting the modal"); + if (modal.attributes.localizations?.data?.length) { + let localization_attributes = modal.attributes.localizations.data[0].attributes; + let {locale, ...hebrew_attributes} = localization_attributes; + Object.keys(hebrew_attributes).forEach((attribute) => { + modal.attributes[attribute] = { + en: modal.attributes[attribute], + he: hebrew_attributes[attribute] + } + }); + modal.attributes.locales = ['en', 'he']; + } else { + ['modalText', 'buttonText', 'buttonURL'].forEach((attribute) => { + modal.attributes[attribute] = {en: modal.attributes[attribute], he: null}; + }); + modal.attributes.locales = ['en']; + } + setModal(modal.attributes); + } + } - console.log("found acceptable banner:"); - console.log(banner); - if (banner) { - console.log("setting the banner"); - if (banner.attributes.localizations?.data?.length) { - let localization_attributes = banner.attributes.localizations.data[0].attributes; - let {locale, ...hebrew_attributes} = localization_attributes; - Object.keys(hebrew_attributes).forEach((attribute) => { - banner.attributes[attribute] = { en: banner.attributes[attribute], he: hebrew_attributes[attribute] } - }); - banner.attributes.locales = ['en', 'he']; - } else { - // Maybe have the GraphQL return null entries for each key so the same technique can be used from above? - ['bannerText', 'buttonText', 'buttonURL'].forEach((attribute) => { - banner.attributes[attribute] = { en: banner.attributes[attribute], he: null }; - }); - banner.attributes.locales = ['en']; - } - setBanner(banner.attributes); - console.log(banner.attributes); - } + if (banners?.length) { + let banner = banners.find( + (b) => + currentDate >= new Date(b.attributes.bannerStartDate) && + currentDate <= new Date(b.attributes.bannerEndDate) + ); + + console.log("found acceptable banner:"); + console.log(banner); + if (banner) { + console.log("setting the banner"); + if (banner.attributes.localizations?.data?.length) { + let localization_attributes = banner.attributes.localizations.data[0].attributes; + let {locale, ...hebrew_attributes} = localization_attributes; + Object.keys(hebrew_attributes).forEach((attribute) => { + banner.attributes[attribute] = { + en: banner.attributes[attribute], + he: hebrew_attributes[attribute] + } + }); + banner.attributes.locales = ['en', 'he']; + } else { + // Maybe have the GraphQL return null entries for each key so the same technique can be used from above? + ['bannerText', 'buttonText', 'buttonURL'].forEach((attribute) => { + banner.attributes[attribute] = {en: banner.attributes[attribute], he: null}; + }); + banner.attributes.locales = ['en']; + } + setBanner(banner.attributes); + console.log(banner.attributes); + } + } + }); + } catch (error) { + console.error("Failed to get strapi data", error); } - }); - } catch (error) { - console.error("Failed to get strapi data", error); - } - }; - getStrapiData(); - }, []); + }; + getStrapiData(); + }, []); - return ( - <StrapiDataContext.Provider - value={{ - dataFromStrapiHasBeenReceived, - strapiData, - modal, - banner, - }} - > - {children} - </StrapiDataContext.Provider> - ); + return ( + <StrapiDataContext.Provider + value={{ + dataFromStrapiHasBeenReceived, + strapiData, + modal, + banner, + }} + > + {children} + </StrapiDataContext.Provider> + ); } // function ExampleComponent() { @@ -259,9 +265,9 @@ function StrapiDataProvider({ children }) { // } export { - ContentLanguageContext, - AdContext, - StrapiDataProvider, - // ExampleComponent, - StrapiDataContext, + ContentLanguageContext, + AdContext, + StrapiDataProvider, + // ExampleComponent, + StrapiDataContext, }; From ae5645056e8fab157875fc8684d83f087636a8f6 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Tue, 25 Jul 2023 20:59:51 -0400 Subject: [PATCH 092/756] Adds environment variables for pointing to a Strapi instance --- sefaria/system/context_processors.py | 2 ++ static/js/context.js | 5 +++-- templates/base.html | 4 ++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/sefaria/system/context_processors.py b/sefaria/system/context_processors.py index d482e49bf9..cc7a71afa6 100644 --- a/sefaria/system/context_processors.py +++ b/sefaria/system/context_processors.py @@ -68,6 +68,8 @@ def global_settings(request): return { "SEARCH_INDEX_NAME_TEXT": SEARCH_INDEX_NAME_TEXT, "SEARCH_INDEX_NAME_SHEET":SEARCH_INDEX_NAME_SHEET, + "STRAPI_LOCATION": STRAPI_LOCATION, + "STRAPI_PORT": STRAPI_PORT, "GOOGLE_TAG_MANAGER_CODE":GOOGLE_TAG_MANAGER_CODE, "GOOGLE_GTAG": GOOGLE_GTAG, "HOTJAR_ID": HOTJAR_ID, diff --git a/static/js/context.js b/static/js/context.js index 0cb6f9e2f0..c6de85fee1 100644 --- a/static/js/context.js +++ b/static/js/context.js @@ -19,6 +19,7 @@ function StrapiDataProvider({children}) { useState(null); const [banner, setBanner] = useState(null); useEffect(() => { + if (STRAPI_INSTANCE) { const getStrapiData = async () => { try { let getDateWithoutTime = (date) => date.toISOString().split("T")[0]; @@ -141,8 +142,7 @@ function StrapiDataProvider({children}) { } } `; - - const result = fetch("http://localhost:1337/graphql", { + const result = fetch(STRAPI_INSTANCE + "/graphql", { method: "POST", // *GET, POST, PUT, DELETE, etc. mode: "cors", // no-cors, *cors, same-origin cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached @@ -235,6 +235,7 @@ function StrapiDataProvider({children}) { } }; getStrapiData(); + } }, []); return ( diff --git a/templates/base.html b/templates/base.html index 913d708e7c..a8f6417b17 100644 --- a/templates/base.html +++ b/templates/base.html @@ -237,6 +237,10 @@ static_url: {{ STATIC_PREFIX }}, }; + {% if STRAPI_LOCATION and STRAPI_PORT %} + var STRAPI_INSTANCE = "{{ STRAPI_LOCATION }}:{{ STRAPI_PORT }}"; + {% endif %} + {% endautoescape %} </script> From 64dfbc61bef1420929de670fae5f1388b5c4e687 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Tue, 25 Jul 2023 21:02:39 -0400 Subject: [PATCH 093/756] Adds environment variables to example local settings with an explanation --- sefaria/local_settings_example.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sefaria/local_settings_example.py b/sefaria/local_settings_example.py index f6b1fbf201..3037ee7981 100644 --- a/sefaria/local_settings_example.py +++ b/sefaria/local_settings_example.py @@ -132,6 +132,11 @@ } """ +# Location of Strapi CMS instance +# For local development, Strapi is located at http://localhost:1337 by default +STRAPI_LOCATION = None +STRAPI_PORT = None + MANAGERS = ADMINS From 861faf4744d62d4d64316a257fff7bed76fa77e4 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Wed, 26 Jul 2023 11:25:26 -0400 Subject: [PATCH 094/756] Fixes bug for when there is no hebrew translation for a modal --- static/js/Misc.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index a8faece559..f3cf7bf585 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -2140,6 +2140,7 @@ const InterruptingMessage = ({ const shouldShow = () => { console.log("checking whether to show modal or not"); if (!strapi.modal) return false; + if (Sefaria.interfaceLang === 'hebrew' && !strapi.modal.locales.includes('he')) return false; if ( hasModalBeenInteractedWith( strapi.modal.internalModalName From 81a6ec577fdc9e1e43d204ae5f2ce0db9b2e30f7 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Wed, 26 Jul 2023 15:19:22 -0400 Subject: [PATCH 095/756] Removes commented out code and moves styles from modal component to css file --- static/css/s2.css | 80 ++++++++++ static/js/Misc.jsx | 366 --------------------------------------------- 2 files changed, 80 insertions(+), 366 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index 1f147ac52c..d7927bde39 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -1217,6 +1217,86 @@ div.interfaceLinks-row a { .interface-hebrew #interruptingMessage h1{ font-style: normal; } + +/* Styles used from previously existing modals */ + +#highHolidayDonation { + width: 410px; + max-height: 100%; + max-width: 100%; +} + +.interface-english #highHolidayDonation { + text-align: left; +} + +.interface-hebrew #highHolidayDonation { + text-align: right; + direction: rtl; +} + +#highHolidayDonation p { + color: #555; +} + +.interface-hebrew p.int-en { + display: none; +} + +#highHolidayDonation p .int-en { + font-family: "adobe-garamond-pro", Georgia, serif; +} + +#highHolidayDonation p .int-he { + font-family: "adobe-garamond-pro", Georgia, serif; + /* font-family: "Heebo", sans-serif; */ +} + +#highHolidayDonation p.sub { + color: #999; + font-size: 12px; + font-family: "Roboto", "Helvetica Neue", Helvetica, sans-serif; +} + +#highHolidayDonation p { + margin-top: 0; +} + +#highHolidayDonation .button { + margin-bottom: 20px; +} + +#highHolidayDonation img { + max-width: 100%; +} + +#highHolidayDonation .buttons { + text-align: right; +} + +.leader { + font-weight: bold; +} + +.center { + text-align: center; +} + +#email-input-wrapper { + display: flex; + align-items: flex-start; + flex-direction: column; +} + +.newsletterInput#email-input { + width: 300px; + padding: 10px; + margin-bottom: 20px; + border-radius: 7px; + border: 1px solid #EEE; + color: #333; +} + .header .my-profile img { height: 24px; width: 24px; diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index f3cf7bf585..ec2c263a99 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -2221,71 +2221,7 @@ const InterruptingMessage = ({ > × </div> - {/* <div id="interruptingMessageContent" dangerouslySetInnerHTML={ {__html: strapi.modal ? strapi.modal.modalText : null } }></div> */} <div id="interruptingMessageContent"> - <style> - {`#highHolidayDonation { - width: 410px; - max-height: 100%; - max-width: 100%; - } - .interface-english #highHolidayDonation { - text-align: left; - } - .interface-hebrew #highHolidayDonation { - text-align: right; - direction: rtl; - } - #highHolidayDonation p { - color: #555; - } - .interface-hebrew p.int-en { - display: none; - } - #highHolidayDonation p .int-en { - font-family: "adobe-garamond-pro", Georgia, serif; - } - #highHolidayDonation p .int-he { - font-family: "adobe-garamond-pro", Georgia, serif; - /* font-family: "Heebo", sans-serif; */ - } - #highHolidayDonation p.sub { - color: #999; - font-size: 12px; - font-family: "Roboto", "Helvetica Neue", Helvetica, sans-serif; - } - #highHolidayDonation p { - margin-top: 0; - } - #highHolidayDonation .button { - margin-bottom: 20px; - } - #highHolidayDonation img { - max-width: 100%; - } - #highHolidayDonation .buttons{ - text-align: right; - } - .leader { - font-weight: bold; - } - .center{ - text-align: center; - } - #email-input-wrapper { - display: flex; - align-items: flex-start; - flex-direction: column; - } - .newsletterInput#email-input { - width: 300px; - padding: 10px; - margin-bottom: 20px; - border-radius: 7px; - border: 1px solid #EEE; - color: #333; - }`} - </style> <div id="highHolidayDonation"> <p> <InterfaceText markdown={strapi.modal.modalText} /> @@ -2461,308 +2397,6 @@ const Banner = ({ onClose }) => { Banner.displayName = "Banner"; -// const useInterruptingComponent = (componentKind, showDelay, onClose, nameId) => { -// const [timesUp, setTimesUp] = useState(false); -// const [hasComponentBeenInteractedWith, setComponentHasBeenInteractedWith] = useState(false); -// const strapi = useContext(StrapiDataContext); - -// const markComponentAsHasBeenInteractedWith = (name) => { -// sessionStorage.setItem(componentKind + "_" + name, "true"); -// } - -// const hasBeenInteractedWith = (name) => { -// return JSON.parse(sessionStorage.getItem(componentKind + "_" + name)); -// } - -// const trackInteraction = (name, eventDescription) => { -// gtag("event", componentKind + "_interacted_with_" + eventDescription, { -// campaignID: name, -// adType: componentKind, -// }); -// } - -// const trackImpression = (name) => { -// console.log("We've got visibility!"); -// gtag("event", componentKind + "_viewed", { -// campaignID: name, -// adType: componentKind, -// }); -// } - -// const closeComponent = (eventDescription) => { -// if (onClose) onClose(); -// markComponentAsHasBeenInteractedWith(strapiData[nameId]); -// setComponentHasBeenInteractedWith(true); -// trackInteraction(strapiData[nameId], eventDescription); -// } - -// useEffect(() => { -// if (shouldShow()) { -// const timeoutId = setTimeout(() => { -// setTimesUp(true); -// }, showDelay); -// return () => clearTimeout(timeoutId); -// } -// }, [strapi[componentKind]]); - -// return {timesUp, hasComponentBeenInteractedWith, closeComponent, trackImpression, strapiData: strapi[componentKind]}; -// }; - -// const InterruptingMessage = ({ onClose }) => { -// const { timesUp, hasComponentBeenInteractedWith, closeComponent, trackImpression, strapiData } = useInterruptingComponent('modal', 5000, onClose, 'internalModalName'); - -// if (!timesUp) return null; - -// if (!hasBeenInteractedWith) { -// if (!hasInteractedWithModal) { -// console.log("rendering component"); -// return ( -// <OnInView onVisible={trackModalImpression}> -// <div id="interruptingMessageBox" className={timesUp ? "" : "hidden"}> -// <div id="interruptingMessageOverlay"></div> -// <div id="interruptingMessage"> -// <div className="colorLine"></div> -// <div id="interruptingMessageContentBox" className="hasColorLine"> -// <div -// id="interruptingMessageClose" -// onClick={() => { -// closeModal("close_clicked"); -// }} -// > -// × -// </div> -// {/* <div id="interruptingMessageContent" dangerouslySetInnerHTML={ {__html: strapi.modal ? strapi.modal.modalText : null } }></div> */} -// <div id="interruptingMessageContent"> -// <style> -// {`#highHolidayDonation { -// width: 410px; -// max-height: 100%; -// max-width: 100%; -// } -// .interface-english #highHolidayDonation { -// text-align: left; -// } -// .interface-hebrew #highHolidayDonation { -// text-align: right; -// direction: rtl; -// } -// #highHolidayDonation p { -// color: #555; -// } -// .interface-hebrew p.int-en { -// display: none; -// } -// #highHolidayDonation p .int-en { -// font-family: "adobe-garamond-pro", Georgia, serif; -// } -// #highHolidayDonation p .int-he { -// font-family: "adobe-garamond-pro", Georgia, serif; -// /* font-family: "Heebo", sans-serif; */ -// } -// #highHolidayDonation p.sub { -// color: #999; -// font-size: 12px; -// font-family: "Roboto", "Helvetica Neue", Helvetica, sans-serif; -// } -// #highHolidayDonation p { -// margin-top: 0; -// } -// #highHolidayDonation .button { -// margin-bottom: 20px; -// } -// #highHolidayDonation img { -// max-width: 100%; -// } -// #highHolidayDonation .buttons{ -// text-align: right; -// } -// .leader { -// font-weight: bold; -// } -// .center{ -// text-align: center; -// } -// #email-input-wrapper { -// display: flex; -// align-items: flex-start; -// flex-direction: column; -// } -// .newsletterInput#email-input { -// width: 300px; -// padding: 10px; -// margin-bottom: 20px; -// border-radius: 7px; -// border: 1px solid #EEE; -// color: #333; -// }`} -// </style> -// <div id="highHolidayDonation"> -// <p> -// <span className="int-en"> -// {strapi.modal.modalText} -// </span> -// </p> -// <div className="buttons"> -// <a -// className="button int-en" -// target="_blank" -// href={strapi.modal.buttonURL} -// onClick={() => { -// closeModal("donate_button_clicked"); -// }} -// > -// <span className="int-en"> -// {strapi.modal.buttonText} -// </span> -// </a> -// </div> -// </div> -// </div> -// <div className="colorLine"></div> -// </div> -// </div> -// </div> -// </OnInView> -// ); -// } else { -// return null; -// } -// } -// }; - - - -// const InterruptingComponent = ({ componentKind, componentName, showDelay, beforeShowingUp, onClose }) => { -// const [timesUp, setTimesUp] = useState(false); -// const [hasInteractedWithComponent, setHasInteractedWithComponent] = useState(false); -// const strapi = useContext(StrapiDataContext); - -// const markComponentAsHasBeenInteractedWith = (componentName) => { -// sessionStorage.setItem(componentKind + "_" + componentName, "true"); -// }; - -// const hasComponentBeenInteractedWith = (bannerName) => { -// return JSON.parse(sessionStorage.getItem("banner_" + bannerName)); -// }; - -// const trackBannerInteraction = (bannerName, eventDescription) => { -// gtag("event", "banner_interacted_with_" + eventDescription, { -// campaignID: bannerName, -// adType: "banner", -// }); -// }; - -// const trackBannerImpression = () => { -// console.log("We've got visibility!"); -// gtag("event", "banner_viewed", { -// campaignID: strapi.banner.internalBannerName, -// adType: "banner", -// }); -// }; - -// const shouldShow = () => { -// console.log("checking whether to show banner or not"); -// if (!strapi.banner) return false; -// if (hasBannerBeenInteractedWith(strapi.banner.internalBannerName)) -// return false; - -// let shouldShowBanner = false; - -// let noUserKindIsSet = ![ -// strapi.banner.showToReturningVisitors, -// strapi.banner.showToNewVisitors, -// strapi.banner.showToSustainers, -// strapi.banner.showToNonSustainers, -// ].some((p) => p); -// if ( -// Sefaria._uid && -// ((Sefaria.is_sustainer && strapi.banner.showToSustainers) || -// (!Sefaria.is_sustainer && strapi.banner.showToNonSustainers)) -// ) -// shouldShowBanner = true; -// else if ( -// (isReturningVisitor() && strapi.banner.showToReturningVisitors) || -// (isNewVisitor() && strapi.banner.showToNewVisitors) -// ) -// shouldShowBanner = true; -// else if (noUserKindIsSet) shouldShowBanner = true; -// if (!shouldShowBanner) return false; - -// const excludedPaths = ["/donate", "/mobile", "/app", "/ways-to-give"]; -// return excludedPaths.indexOf(window.location.pathname) === -1; -// }; - -// const closeBanner = (eventDescription) => { -// if (onClose) onClose(); -// console.log(eventDescription); -// markBannerAsHasBeenInteractedWith(strapi.banner.internalBannerName); -// setHasInteractedWithBanner(true); -// trackBannerInteraction( -// strapi.banner.internalBannerName, -// eventDescription -// ); -// }; - -// useEffect(() => { -// if (shouldShow()) { -// console.log("reaching here..."); - -// const timeoutId = setTimeout(() => { -// // s2 is the div that contains the React root and needs to be manipulated by traditional DOM methods -// if (document.getElementById("s2").classList.contains("headerOnly")) { -// document.body.classList.add("hasBannerMessage"); -// } -// setTimesUp(true); -// }, showDelay); -// return () => clearTimeout(timeoutId); // clearTimeout on component unmount -// } -// }, [strapi.banner]); // execute useEffect when the modal changes - -// if (!timesUp) return null; -// console.log("data for the component"); -// console.log(strapi.banner); - -// if (!hasInteractedWithBanner) { -// console.log("rendering component"); -// return ( -// <OnInView onVisible={trackBannerImpression}> -// <div id="bannerMessage" className={timesUp ? "" : "hidden"}> -// <div id="bannerMessageContent"> -// <div id="bannerTextBox"> -// <span className="int-en">{strapi.banner.bannerText}</span> -// <span className="int-he"> -// ספריית ספריא מנגישה יותר מ-300 מיליון מלים של טקסטים יהודיים -// ברחבי העולם. לכבוד שבועות, אנא תמכו היום בספריה שמסייעת ללימוד -// שלכם על-ידי קבלת מעמד של ידידי ספריא. -// </span> -// </div> -// <div id="bannerButtonBox"> -// <a -// className="button white int-en" -// href={strapi.banner.buttonURL} -// > -// <span>{strapi.banner.buttonText}</span> -// </a> -// <a -// className="button white int-he" -// href="https://sefaria.nationbuilder.com/sustainers_e" -// > -// <span>לתרומה חודשית</span> -// </a> -// </div> -// </div> -// <div id="bannerMessageClose">×</div> -// <div id="bannerMessageClose" onClick={closeBanner}> -// × -// </div> -// </div> -// </OnInView> -// ); -// } else { -// return null; -// } -// }; - // InterruptingMessage.propTypes = { // messageName: PropTypes.string.isRequired, From c8200dac13d553f438c9ab490df48d87bf1a10f8 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Wed, 26 Jul 2023 15:29:58 -0400 Subject: [PATCH 096/756] Removes paragraph tag from JSX for the modal component --- static/js/Misc.jsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index ec2c263a99..1b871ec25e 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -2223,9 +2223,7 @@ const InterruptingMessage = ({ </div> <div id="interruptingMessageContent"> <div id="highHolidayDonation"> - <p> <InterfaceText markdown={strapi.modal.modalText} /> - </p> <div className="buttons"> <a className="button int-en" From 9db867a43cd27ba1f78fbcf480b69f7616d6dc2d Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Wed, 26 Jul 2023 16:05:25 -0400 Subject: [PATCH 097/756] Moves isReturningVisitor and isNewVisitor functions etc. to be utility functions under the Sefaria object. Rewrites isNewVisitor function to have an extra condition that will be considered on mounting of ReaderApp --- static/js/Misc.jsx | 24 ++++-------------------- static/js/ReaderApp.jsx | 9 +++------ static/js/sefaria/sefaria.js | 23 +++++++++++++++++++++++ 3 files changed, 30 insertions(+), 26 deletions(-) diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 1b871ec25e..25c5734d22 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -2086,22 +2086,6 @@ function OnInView({ children, onVisible }) { return <div ref={elementRef}>{children}</div>; } -// User could be new visitor when there isn't anything in sessionStorage either... -// Maybe don't check if it's in there or have extra conditional -function isNewVisitor() { - return ( - "isNewVisitor" in sessionStorage && - JSON.parse(sessionStorage.getItem("isNewVisitor")) - ); -} - -function isReturningVisitor() { - return ( - !isNewVisitor() && - "isReturningVisitor" in localStorage && - JSON.parse(localStorage.getItem("isReturningVisitor")) - ); -} const InterruptingMessage = ({ onClose, @@ -2166,9 +2150,9 @@ const InterruptingMessage = ({ ) shouldShowModal = true; else if ( - (isReturningVisitor() && + (Sefaria.isReturningVisitor() && strapi.modal.showToReturningVisitors) || - (isNewVisitor() && strapi.modal.showToNewVisitors) + (Sefaria.isNewVisitor() && strapi.modal.showToNewVisitors) ) shouldShowModal = true; else if (noUserKindIsSet) shouldShowModal = true; @@ -2315,8 +2299,8 @@ const Banner = ({ onClose }) => { ) shouldShowBanner = true; else if ( - (isReturningVisitor() && strapi.banner.showToReturningVisitors) || - (isNewVisitor() && strapi.banner.showToNewVisitors) + (Sefaria.isReturningVisitor() && strapi.banner.showToReturningVisitors) || + (Sefaria.isNewVisitor() && strapi.banner.showToNewVisitors) ) shouldShowBanner = true; else if (noUserKindIsSet) shouldShowBanner = true; diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index dc2d7ecdf5..71b43c965d 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -201,14 +201,11 @@ class ReaderApp extends Component { // Save all initial panels to recently viewed this.state.panels.map(this.saveLastPlace); // Initialize entries for first-time visitors to determine if they are new or returning presently or in the future - if (!("isNewVisitor" in sessionStorage) && !("isReturningVisitor" in localStorage)) { - sessionStorage.setItem("isNewVisitor", "true"); - // Setting this at this time will make the current new visitor a returning one once their session is cleared - localStorage.setItem("isReturningVisitor", "true"); + if (Sefaria.isNewVisitor()) { + Sefaria.markUserAsNewVisitor(); } else if (Sefaria._uid) { // A logged in user is automatically a returning visitor - sessionStorage.setItem("isNewVisitor", "false"); - localStorage.setItem("isReturningVisitor", "true"); + Sefaria.markUserAsReturningVisitor(); } } componentWillUnmount() { diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index e9e17cdc6b..4f2e983932 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -2254,6 +2254,29 @@ _media: {}, } Sefaria.last_place = history_item_array.filter(x=>!x.secondary).concat(Sefaria.last_place); // while technically we should remove dup. books, this list is only used on client }, + isNewVisitor: () => { + return ( + ("isNewVisitor" in sessionStorage && + JSON.parse(sessionStorage.getItem("isNewVisitor"))) || + (!("isNewVisitor" in sessionStorage) && !("isReturningVisitor" in localStorage)) + ); + }, + isReturningVisitor: () => { + return ( + !Sefaria.isNewVisitor() && + "isReturningVisitor" in localStorage && + JSON.parse(localStorage.getItem("isReturningVisitor")) + ); + }, + markUserAsNewVisitor: () => { + sessionStorage.setItem("isNewVisitor", "true"); + // Setting this at this time will make the current new visitor a returning one once their session is cleared + localStorage.setItem("isReturningVisitor", "true"); + }, + markUserAsReturningVisitor: () => { + sessionStorage.setItem("isNewVisitor", "false"); + localStorage.setItem("isReturningVisitor", "true"); + }, uploadProfilePhoto: (formData) => { return new Promise((resolve, reject) => { if (Sefaria._uid) { From e8d4dc2e5414e1e6c3c968fa289ce05739f1e5e0 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Thu, 27 Jul 2023 17:40:16 -0400 Subject: [PATCH 098/756] Revises Promotions component to create Sidebar Ads that have full support with Strapi. Component is currently being rendered too many times and the OnInView component in unable to handle that situation --- static/js/Misc.jsx | 3 +- static/js/Promotions.jsx | 257 +++++++++++-------------------- static/js/ReaderApp.jsx | 1 - static/js/context.js | 321 +++++++++++++++++++++------------------ 4 files changed, 257 insertions(+), 325 deletions(-) diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 25c5734d22..30bdde7def 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -3269,5 +3269,6 @@ export { AdminToolHeader, CategoryChooser, TitleVariants, - requestWithCallBack + requestWithCallBack, + OnInView }; diff --git a/static/js/Promotions.jsx b/static/js/Promotions.jsx index 794b8b85a2..2bd3142314 100644 --- a/static/js/Promotions.jsx +++ b/static/js/Promotions.jsx @@ -1,13 +1,12 @@ import React, { useState, useContext, useEffect } from "react"; import { AdContext, StrapiDataProvider, StrapiDataContext } from "./context"; import classNames from "classnames"; -import { InterruptingMessage } from "./Misc"; import Sefaria from "./sefaria/sefaria"; +import { OnInView } from "./Misc"; -const Promotions = ({ adType, rerender }) => { +const Promotions = () => { const [inAppAds, setInAppAds] = useState(Sefaria._inAppAds); // local cache - const [matchingAds, setMatchingAds] = useState(null); // match the ads to what comes from the google doc - const [prevMatchingAdIds, setPrevMatchingAdIds] = useState([]); + const [matchingAds, setMatchingAds] = useState(null); // match the ads to what comes from Strapi const context = useContext(AdContext); const strapi = useContext(StrapiDataContext); useEffect(() => { @@ -25,30 +24,54 @@ const Promotions = ({ adType, rerender }) => { let keywordTargetsArray = sidebarAd.keywords .split(",") .map((x) => x.trim().toLowerCase()); + let excludeKeywordTargets = keywordTargetsArray + .filter((x) => x[0] === "!") + .map((x) => x.slice(1)); + keywordTargetsArray = keywordTargetsArray.filter( + (x) => x[0] !== "!" + ); Sefaria._inAppAds.push({ campaignId: sidebarAd.internalCampaignId, - title: sidebarAd.Title, + title: sidebarAd.title, bodyText: sidebarAd.bodyText, buttonText: sidebarAd.buttonText, - buttonUrl: sidebarAd.buttonUrl, - buttonIcon: "", - buttonLocation: sidebarAd.buttonUrl, - adType: "sidebar", + buttonURL: sidebarAd.buttonURL, + buttonIcon: sidebarAd.buttonIcon, + buttonLocation: sidebarAd.buttonAboveOrBelow, hasBlueBackground: sidebarAd.hasBlueBackground, - repetition: 5, - buttonStyle: "", trigger: { showTo: sidebarAd.showTo, - interfaceLang: Sefaria.translateISOLanguageCode( - sidebarAd.locale - ).toLowerCase(), + interfaceLang: "english", dt_start: Date.parse(sidebarAd.startTime), dt_end: Date.parse(sidebarAd.endTime), keywordTargets: keywordTargetsArray, - excludeKeywordTargets: [], + excludeKeywordTargets: excludeKeywordTargets, }, debug: sidebarAd.debug, }); + if (sidebarAd.localizations?.data?.length) { + const hebrewAttributes = sidebarAd.localizations.data[0].attributes; + const [buttonText, bodyText, buttonURL, title] = [hebrewAttributes.buttonText, hebrewAttributes.bodyText, hebrewAttributes.buttonURL, hebrewAttributes.title]; + Sefaria._inAppAds.push({ + campaignId: sidebarAd.internalCampaignId, + title: title, + bodyText: bodyText, + buttonText: buttonText, + buttonUrl: buttonURL, + buttonIcon: sidebarAd.buttonIcon, + buttonLocation: sidebarAd.buttonAboveOrBelow, + hasBlueBackground: sidebarAd.hasBlueBackground, + trigger: { + showTo: sidebarAd.showTo, + interfaceLang: "hebrew", + dt_start: Date.parse(sidebarAd.startTime), + dt_end: Date.parse(sidebarAd.endTime), + keywordTargets: keywordTargetsArray, + excludeKeywordTargets: excludeKeywordTargets, + }, + debug: sidebarAd.debug, + }); + } }); setInAppAds(Sefaria._inAppAds); } @@ -61,27 +84,6 @@ const Promotions = ({ adType, rerender }) => { setMatchingAds(getCurrentMatchingAds()); } }, [context, inAppAds]); // when state changes, the effect will run - useEffect(() => { - if (!matchingAds) { - return; - } - const matchingAdIds = matchingAds.map((x) => x.campaignId).sort(); - const newIds = matchingAdIds.filter( - (value) => !prevMatchingAdIds.includes(value) - ); - - if (newIds.length > 0) { - for (const matchingAd of matchingAds) { - if (newIds.includes(matchingAd.campaignId)) { - gtag("event", "promo_viewed", { - campaignID: matchingAd.campaignId, - adType: matchingAd.adType, - }); - } - } - setPrevMatchingAdIds(newIds); - } - }, [matchingAds]); // when state of matching ads changes, which changes in previous useEffect // function getAds() { // const url = @@ -122,7 +124,6 @@ const Promotions = ({ adType, rerender }) => { showToUser(ad) && showGivenDebugMode(ad) && ad.trigger.interfaceLang === context.interfaceLang && - ad.adType === adType && context.dt > ad.trigger.dt_start && context.dt < ad.trigger.dt_end && (context.keywordTargets.some((kw) => @@ -131,163 +132,77 @@ const Promotions = ({ adType, rerender }) => { (ad.trigger.excludeKeywordTargets.length !== 0 && !context.keywordTargets.some((kw) => ad.trigger.excludeKeywordTargets.includes(kw) - ))) && - /* line below checks if ad with particular repetition number has been seen before and is a banner */ - ((Sefaria._inBrowser && - !document.cookie.includes(`${ad.campaignId}_${ad.repetition}`)) || - ad.adType === "sidebar") + ))) ); }); } - function processSheetsData(response) { - if (response.isError()) { - alert( - "Error in query: " + - response.getMessage() + - " " + - response.getDetailedMessage() - ); - return; - } - const data = response.getDataTable(); - const columns = data.getNumberOfColumns(); - const rows = data.getNumberOfRows(); - Sefaria._inAppAds = []; - for (let r = 0; r < rows; r++) { - let row = []; - for (let c = 0; c < columns; c++) { - row.push(data.getFormattedValue(r, c)); - } - let keywordTargetsArray = row[5] - .split(",") - .map((x) => x.trim().toLowerCase()); - let excludeKeywordTargets = keywordTargetsArray.filter( - (x) => x.indexOf("!") === 0 - ); - excludeKeywordTargets = excludeKeywordTargets.map((x) => x.slice(1)); - keywordTargetsArray = keywordTargetsArray.filter( - (x) => x.indexOf("!") !== 0 - ); - Sefaria._inAppAds.push({ - campaignId: row[0], - title: row[6], - bodyText: row[7], - buttonText: row[8], - buttonUrl: row[9], - buttonIcon: row[10], - buttonLocation: row[11], - adType: row[12], - hasBlueBackground: parseInt(row[13]), - repetition: row[14], - buttonStyle: row[15], - trigger: { - showTo: row[4], - interfaceLang: row[3], - dt_start: Date.parse(row[1]), - dt_end: Date.parse(row[2]), - keywordTargets: keywordTargetsArray, - excludeKeywordTargets: excludeKeywordTargets, - }, - debug: parseInt(row[16]), - }); - } - setInAppAds(Sefaria._inAppAds); - } + console.log("promotions component is being rerendered"); - // TODO: refactor once old InterruptingMessage pattern is retired - function createBannerHtml(matchingAd) { - return `<div id="bannerTextBox"> - <span class="${ - context.interfaceLang === "hebrew" ? "int-he" : "int-en" - }" style="font-weight: bold"> - ${matchingAd.bodyText} - </span> -</div> -<div id="bannerButtonBox"> - <a class="button white ${ - context.interfaceLang === "hebrew" ? "int-he" : "int-en" - }" - href="${matchingAd.buttonUrl}" - onclick='gtag("event", "promo_clicked", { - campaignID: matchingAd.campaignId, adType:matchingAd.adType - })' - target="_blank"> - <span>${matchingAd.buttonText}</span> - </a> -</div>`; - } + return matchingAds + ? matchingAds.map((ad) => <SidebarAd context={context} matchingAd={ad} key={ad.campaignId} />) + : null; +}; - function styleAds() { - if (adType === "banner") { - const matchingAd = matchingAds[0]; // Only allow a single banner - if (!matchingAd) { - return null; - } - // TODO: change this to use new InterruptingMessage - const bannerHtml = createBannerHtml(matchingAd); - return ( - <InterruptingMessage - messageName={matchingAd.campaignId} - messageHTML={bannerHtml} - style="banner" - repetition={matchingAd.repetition} - onClose={rerender} - /> - ); - } else { - const sidebarAds = matchingAds.map((ad) => ( - <SidebarAd matchingAd={ad} key={ad.campaignId} /> - )); - return sidebarAds; - } - } +function trackSidebarAdImpression(ad) { + console.log(ad.campaignId + " has been seen"); + gtag("event", "promo_viewed", { + campaignID: ad.campaignId, + adType: "sidebar", + }); +} - return matchingAds ? styleAds() : null; -}; +function trackSidebarAdClick(ad) { + gtag("event", "promo_clicked", { + campaignID: ad.campaignId, + adType: "sidebar", + }); +} -const SidebarAd = ({ matchingAd }) => { +const SidebarAd = ({ context, matchingAd }) => { const classes = classNames({ sidebarPromo: 1, blue: matchingAd.hasBlueBackground, }); + console.log("is this being rerendered?"); + function getButton() { return ( <a - className={matchingAd.buttonStyle} - href={matchingAd.buttonUrl} - onClick={() => - gtag("event", "promo_clicked", { - campaignID: matchingAd.campaignId, - adType: matchingAd.adType, - }) - } + className="button small" + href={matchingAd.buttonURL} + onClick={() => trackSidebarAdClick(matchingAd)} > - <img - src={`/static/icons/${matchingAd.buttonIcon}`} - aria-hidden="true" - /> + {matchingAd.buttonIcon?.data ? ( + <img + src={STRAPI_INSTANCE + matchingAd.buttonIcon.data.attributes.url} + alt={matchingAd.buttonIcon.data.attributes.alternativeText} + aria-hidden="true" + /> + ) : null} {matchingAd.buttonText} </a> ); } return ( - <div className={classes}> - <h3>{matchingAd.title}</h3> - {matchingAd.buttonLocation === "below" ? ( - <> - <p>{matchingAd.bodyText}</p> - {getButton()} - </> - ) : ( - <> - {getButton()} - <p>{matchingAd.bodyText}</p> - </> - )} - </div> + <OnInView onVisible={() => trackSidebarAdImpression(matchingAd)}> + <div className={classes}> + <h3 className={context.interfaceLang === "hebrew" ? "int-he" : "int-en" }>{matchingAd.title}</h3> + {matchingAd.buttonLocation === "below" ? ( + <> + <p className={context.interfaceLang === "hebrew" ? "int-he" : "int-en" }>{matchingAd.bodyText}</p> + {getButton()} + </> + ) : ( + <> + {getButton()} + <p className={context.interfaceLang === "hebrew" ? "int-he" : "int-en" }>{matchingAd.bodyText}</p> + </> + )} + </div> + </OnInView> ); }; diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index 71b43c965d..fe8737afd6 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -1969,7 +1969,6 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { .flat() .filter(ref => !!ref); const deDupedTriggers = [...new Set(triggers.map(JSON.stringify))].map(JSON.parse).map(x => x.toLowerCase()); - // How do I get the user type? const context = { isDebug: this.props._debug, isLoggedIn: Sefaria._uid, diff --git a/static/js/context.js b/static/js/context.js index c6de85fee1..687aa8283e 100644 --- a/static/js/context.js +++ b/static/js/context.js @@ -1,7 +1,7 @@ -import React, {useContext, useEffect, useState} from "react"; +import React, { useContext, useEffect, useState } from "react"; const ContentLanguageContext = React.createContext({ - language: "english", + language: "english", }); ContentLanguageContext.displayName = "ContentLanguageContext"; //This lets us see this name in the devtools @@ -11,32 +11,30 @@ AdContext.displayName = "AdContext"; const StrapiDataContext = React.createContext({}); StrapiDataContext.displayName = "StrapiDataContext"; -function StrapiDataProvider({children}) { - const [dataFromStrapiHasBeenReceived, setDataFromStrapiHasBeenReceived] = - useState(false); - const [strapiData, setStrapiData] = useState(null); - const [modal, setModal] = - useState(null); - const [banner, setBanner] = useState(null); - useEffect(() => { - if (STRAPI_INSTANCE) { - const getStrapiData = async () => { - try { - let getDateWithoutTime = (date) => date.toISOString().split("T")[0]; - let getJSONDateStringInLocalTimeZone = (date) => { - let parts = getDateWithoutTime(date).split("-"); - return new Date(parts[0], parts[1] - 1, parts[2]).toJSON(); - }; - let currentDate = new Date(); - let oneWeekFromNow = new Date(); - oneWeekFromNow.setDate(currentDate.getDate() + 7); - currentDate.setDate(currentDate.getDate() - 2); // Fix time management, previous code got time 1 hour in the future in UTC - let startDate = getJSONDateStringInLocalTimeZone(currentDate); - let endDate = getJSONDateStringInLocalTimeZone(oneWeekFromNow); - console.log(startDate); - console.log(endDate); - const query = - ` +function StrapiDataProvider({ children }) { + const [dataFromStrapiHasBeenReceived, setDataFromStrapiHasBeenReceived] = + useState(false); + const [strapiData, setStrapiData] = useState(null); + const [modal, setModal] = useState(null); + const [banner, setBanner] = useState(null); + useEffect(() => { + if (STRAPI_INSTANCE) { + const getStrapiData = async () => { + try { + let getDateWithoutTime = (date) => date.toISOString().split("T")[0]; + let getJSONDateStringInLocalTimeZone = (date) => { + let parts = getDateWithoutTime(date).split("-"); + return new Date(parts[0], parts[1] - 1, parts[2]).toJSON(); + }; + let currentDate = new Date(); + let oneWeekFromNow = new Date(); + oneWeekFromNow.setDate(currentDate.getDate() + 7); + currentDate.setDate(currentDate.getDate() - 2); // Fix time management, previous code got time 1 hour in the future in UTC + let startDate = getJSONDateStringInLocalTimeZone(currentDate); + let endDate = getJSONDateStringInLocalTimeZone(oneWeekFromNow); + console.log(startDate); + console.log(endDate); + const query = ` query { banners( filters: { @@ -121,11 +119,19 @@ function StrapiDataProvider({children}) { data { id attributes { - ButtonAboveOrBelow - Title + buttonAboveOrBelow + title bodyText buttonText - buttonUrl + buttonURL + buttonIcon { + data { + attributes { + url + alternativeText + } + } + } createdAt debug endTime @@ -133,6 +139,17 @@ function StrapiDataProvider({children}) { internalCampaignId keywords locale + localizations { + data { + attributes { + locale + title + bodyText + buttonText + buttonURL + } + } + } publishedAt showTo startTime @@ -142,133 +159,133 @@ function StrapiDataProvider({children}) { } } `; - const result = fetch(STRAPI_INSTANCE + "/graphql", { - method: "POST", // *GET, POST, PUT, DELETE, etc. - mode: "cors", // no-cors, *cors, same-origin - cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached - credentials: "same-origin", // include, *same-origin, omit - headers: { - "Content-Type": "application/json", - }, - redirect: "follow", // manual, *follow, error - referrerPolicy: "no-referrer", // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url - body: JSON.stringify({query}), - }) - .then((response) => response.json()) - .then((result) => { - setStrapiData(result.data); - setDataFromStrapiHasBeenReceived(true); - // maybe sort by start date to choose which one should have a greater priority if more than one compatible one exists - // e.g. there are modals with overlapping time frames - let modals = result.data?.modals?.data; - console.log(modals); - let banners = result.data?.banners?.data; - console.log(banners); + const result = fetch(STRAPI_INSTANCE + "/graphql", { + method: "POST", // *GET, POST, PUT, DELETE, etc. + mode: "cors", // no-cors, *cors, same-origin + cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached + credentials: "same-origin", + headers: { + "Content-Type": "application/json", + }, + redirect: "follow", + referrerPolicy: "no-referrer", + body: JSON.stringify({ query }), + }) + .then((response) => response.json()) + .then((result) => { + setStrapiData(result.data); + setDataFromStrapiHasBeenReceived(true); + // maybe sort by start date to choose which one should have a greater priority if more than one compatible one exists + // e.g. there are modals with overlapping time frames + let modals = result.data?.modals?.data; + console.log(modals); + let banners = result.data?.banners?.data; + console.log(banners); - const currentDate = new Date(); - if (modals?.length) { - // if they end up being sorted, the first one will be the compatible one - let modal = modals.find( - (modal) => - currentDate >= new Date(modal.attributes.modalStartDate) && - currentDate <= new Date(modal.attributes.modalEndDate) - ); - console.log("found acceptable modal:"); - console.log(modal); - if (modal) { - console.log("setting the modal"); - if (modal.attributes.localizations?.data?.length) { - let localization_attributes = modal.attributes.localizations.data[0].attributes; - let {locale, ...hebrew_attributes} = localization_attributes; - Object.keys(hebrew_attributes).forEach((attribute) => { - modal.attributes[attribute] = { - en: modal.attributes[attribute], - he: hebrew_attributes[attribute] - } - }); - modal.attributes.locales = ['en', 'he']; - } else { - ['modalText', 'buttonText', 'buttonURL'].forEach((attribute) => { - modal.attributes[attribute] = {en: modal.attributes[attribute], he: null}; - }); - modal.attributes.locales = ['en']; - } - setModal(modal.attributes); - } - } + const currentDate = new Date(); + if (modals?.length) { + // if they end up being sorted, the first one will be the compatible one + let modal = modals.find( + (modal) => + currentDate >= new Date(modal.attributes.modalStartDate) && + currentDate <= new Date(modal.attributes.modalEndDate) + ); + console.log("found acceptable modal:"); + console.log(modal); + if (modal) { + console.log("setting the modal"); + if (modal.attributes.localizations?.data?.length) { + let localization_attributes = + modal.attributes.localizations.data[0].attributes; + let { locale, ...hebrew_attributes } = + localization_attributes; + Object.keys(hebrew_attributes).forEach((attribute) => { + modal.attributes[attribute] = { + en: modal.attributes[attribute], + he: hebrew_attributes[attribute], + }; + }); + modal.attributes.locales = ["en", "he"]; + } else { + ["modalText", "buttonText", "buttonURL"].forEach( + (attribute) => { + modal.attributes[attribute] = { + en: modal.attributes[attribute], + he: null, + }; + } + ); + modal.attributes.locales = ["en"]; + } + setModal(modal.attributes); + } + } - if (banners?.length) { - let banner = banners.find( - (b) => - currentDate >= new Date(b.attributes.bannerStartDate) && - currentDate <= new Date(b.attributes.bannerEndDate) - ); + if (banners?.length) { + let banner = banners.find( + (b) => + currentDate >= new Date(b.attributes.bannerStartDate) && + currentDate <= new Date(b.attributes.bannerEndDate) + ); - console.log("found acceptable banner:"); - console.log(banner); - if (banner) { - console.log("setting the banner"); - if (banner.attributes.localizations?.data?.length) { - let localization_attributes = banner.attributes.localizations.data[0].attributes; - let {locale, ...hebrew_attributes} = localization_attributes; - Object.keys(hebrew_attributes).forEach((attribute) => { - banner.attributes[attribute] = { - en: banner.attributes[attribute], - he: hebrew_attributes[attribute] - } - }); - banner.attributes.locales = ['en', 'he']; - } else { - // Maybe have the GraphQL return null entries for each key so the same technique can be used from above? - ['bannerText', 'buttonText', 'buttonURL'].forEach((attribute) => { - banner.attributes[attribute] = {en: banner.attributes[attribute], he: null}; - }); - banner.attributes.locales = ['en']; - } - setBanner(banner.attributes); - console.log(banner.attributes); - } - } + console.log("found acceptable banner:"); + console.log(banner); + if (banner) { + console.log("setting the banner"); + if (banner.attributes.localizations?.data?.length) { + let localization_attributes = + banner.attributes.localizations.data[0].attributes; + let { locale, ...hebrew_attributes } = + localization_attributes; + Object.keys(hebrew_attributes).forEach((attribute) => { + banner.attributes[attribute] = { + en: banner.attributes[attribute], + he: hebrew_attributes[attribute], + }; }); - } catch (error) { - console.error("Failed to get strapi data", error); - } - }; - getStrapiData(); - } - }, []); + banner.attributes.locales = ["en", "he"]; + } else { + // Maybe have the GraphQL return null entries for each key so the same technique can be used from above? + ["bannerText", "buttonText", "buttonURL"].forEach( + (attribute) => { + banner.attributes[attribute] = { + en: banner.attributes[attribute], + he: null, + }; + } + ); + banner.attributes.locales = ["en"]; + } + setBanner(banner.attributes); + console.log(banner.attributes); + } + } + }); + } catch (error) { + console.error("Failed to get strapi data", error); + } + }; + getStrapiData(); + } + }, []); - return ( - <StrapiDataContext.Provider - value={{ - dataFromStrapiHasBeenReceived, - strapiData, - modal, - banner, - }} - > - {children} - </StrapiDataContext.Provider> - ); + return ( + <StrapiDataContext.Provider + value={{ + dataFromStrapiHasBeenReceived, + strapiData, + modal, + banner, + }} + > + {children} + </StrapiDataContext.Provider> + ); } -// function ExampleComponent() { -// const strapi = useContext(StrapiDataContext); -// if (strapi.dataFromStrapiHasBeenReceived) { -// return ( -// <div className="test"> -// <dialog open>{strapi.strapiData}</dialog> -// </div> -// ); -// } else { -// return null; -// } -// } - export { - ContentLanguageContext, - AdContext, - StrapiDataProvider, - // ExampleComponent, - StrapiDataContext, + ContentLanguageContext, + AdContext, + StrapiDataProvider, + StrapiDataContext, }; From ea4b26dfda3528aca51c9d1c812692f77b121a2f Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Fri, 28 Jul 2023 10:50:51 -0400 Subject: [PATCH 099/756] Prevents sidebar ads from unnecessarily rerendering --- static/js/Promotions.jsx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/static/js/Promotions.jsx b/static/js/Promotions.jsx index 2bd3142314..834cfd3f0b 100644 --- a/static/js/Promotions.jsx +++ b/static/js/Promotions.jsx @@ -137,7 +137,6 @@ const Promotions = () => { }); } - console.log("promotions component is being rerendered"); return matchingAds ? matchingAds.map((ad) => <SidebarAd context={context} matchingAd={ad} key={ad.campaignId} />) @@ -159,14 +158,12 @@ function trackSidebarAdClick(ad) { }); } -const SidebarAd = ({ context, matchingAd }) => { +const SidebarAd = React.memo(({ context, matchingAd }) => { const classes = classNames({ sidebarPromo: 1, blue: matchingAd.hasBlueBackground, }); - console.log("is this being rerendered?"); - function getButton() { return ( <a @@ -204,6 +201,6 @@ const SidebarAd = ({ context, matchingAd }) => { </div> </OnInView> ); -}; +}); export { Promotions }; From bbec399bb10090b8bc06e3ee6e3dc02fb962f7e7 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Fri, 28 Jul 2023 12:34:01 -0400 Subject: [PATCH 100/756] Prevents sidebar ads from rendering until the topic data is fully loaded so impressions are accurate. Sidebar ads render too fast relative to the rest of the page. They were rendered and an impression was registered before when there was still content to be loaded. This considers the proper placement of when the sidebar ads might be outside of the viewport --- static/js/Promotions.jsx | 42 +++++++++++++++++++++++++++++----------- static/js/TopicPage.jsx | 29 ++++++++++++++------------- 2 files changed, 47 insertions(+), 24 deletions(-) diff --git a/static/js/Promotions.jsx b/static/js/Promotions.jsx index 834cfd3f0b..b0a0e0ebd7 100644 --- a/static/js/Promotions.jsx +++ b/static/js/Promotions.jsx @@ -4,7 +4,7 @@ import classNames from "classnames"; import Sefaria from "./sefaria/sefaria"; import { OnInView } from "./Misc"; -const Promotions = () => { +const Promotions = ({ topicDataHasLoaded }) => { const [inAppAds, setInAppAds] = useState(Sefaria._inAppAds); // local cache const [matchingAds, setMatchingAds] = useState(null); // match the ads to what comes from Strapi const context = useContext(AdContext); @@ -27,9 +27,7 @@ const Promotions = () => { let excludeKeywordTargets = keywordTargetsArray .filter((x) => x[0] === "!") .map((x) => x.slice(1)); - keywordTargetsArray = keywordTargetsArray.filter( - (x) => x[0] !== "!" - ); + keywordTargetsArray = keywordTargetsArray.filter((x) => x[0] !== "!"); Sefaria._inAppAds.push({ campaignId: sidebarAd.internalCampaignId, title: sidebarAd.title, @@ -51,7 +49,12 @@ const Promotions = () => { }); if (sidebarAd.localizations?.data?.length) { const hebrewAttributes = sidebarAd.localizations.data[0].attributes; - const [buttonText, bodyText, buttonURL, title] = [hebrewAttributes.buttonText, hebrewAttributes.bodyText, hebrewAttributes.buttonURL, hebrewAttributes.title]; + const [buttonText, bodyText, buttonURL, title] = [ + hebrewAttributes.buttonText, + hebrewAttributes.bodyText, + hebrewAttributes.buttonURL, + hebrewAttributes.title, + ]; Sefaria._inAppAds.push({ campaignId: sidebarAd.internalCampaignId, title: title, @@ -137,9 +140,10 @@ const Promotions = () => { }); } - - return matchingAds - ? matchingAds.map((ad) => <SidebarAd context={context} matchingAd={ad} key={ad.campaignId} />) + return matchingAds && topicDataHasLoaded + ? matchingAds.map((ad) => ( + <SidebarAd context={context} matchingAd={ad} key={ad.campaignId} /> + )) : null; }; @@ -186,16 +190,32 @@ const SidebarAd = React.memo(({ context, matchingAd }) => { return ( <OnInView onVisible={() => trackSidebarAdImpression(matchingAd)}> <div className={classes}> - <h3 className={context.interfaceLang === "hebrew" ? "int-he" : "int-en" }>{matchingAd.title}</h3> + <h3 + className={context.interfaceLang === "hebrew" ? "int-he" : "int-en"} + > + {matchingAd.title} + </h3> {matchingAd.buttonLocation === "below" ? ( <> - <p className={context.interfaceLang === "hebrew" ? "int-he" : "int-en" }>{matchingAd.bodyText}</p> + <p + className={ + context.interfaceLang === "hebrew" ? "int-he" : "int-en" + } + > + {matchingAd.bodyText} + </p> {getButton()} </> ) : ( <> {getButton()} - <p className={context.interfaceLang === "hebrew" ? "int-he" : "int-en" }>{matchingAd.bodyText}</p> + <p + className={ + context.interfaceLang === "hebrew" ? "int-he" : "int-en" + } + > + {matchingAd.bodyText} + </p> </> )} </div> diff --git a/static/js/TopicPage.jsx b/static/js/TopicPage.jsx index 67c150aba8..34011c1dee 100644 --- a/static/js/TopicPage.jsx +++ b/static/js/TopicPage.jsx @@ -513,19 +513,22 @@ const TopicPage = ({ : (topicData.isLoading ? <LoadingMessage /> : null) } </div> <div className="sideColumn"> - { topicData ? - <TopicSideColumn - key={topic} - slug={topic} - links={topicData.links} - clearAndSetTopic={clearAndSetTopic} - setNavTopic={setNavTopic} - parashaData={parashaData} - tref={topicData.ref} - timePeriod={topicData.timePeriod} - properties={topicData.properties} /> - : null } - <Promotions adType="sidebar"/> + {topicData ? ( + <> + <TopicSideColumn + key={topic} + slug={topic} + links={topicData.links} + clearAndSetTopic={clearAndSetTopic} + setNavTopic={setNavTopic} + parashaData={parashaData} + tref={topicData.ref} + timePeriod={topicData.timePeriod} + properties={topicData.properties} + /> + <Promotions topicDataHasLoaded={!topicData.isLoading} /> + </> + ) : null} </div> </div> <Footer /> From abbfe01c5eaa06af4384214734c96fad3a24516b Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Mon, 31 Jul 2023 14:40:58 -0400 Subject: [PATCH 101/756] Taking an alternative approach to showing the promotions component after the topicData has loaded --- static/js/Promotions.jsx | 4 ++-- static/js/TopicPage.jsx | 9 ++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/static/js/Promotions.jsx b/static/js/Promotions.jsx index b0a0e0ebd7..be91f75537 100644 --- a/static/js/Promotions.jsx +++ b/static/js/Promotions.jsx @@ -4,7 +4,7 @@ import classNames from "classnames"; import Sefaria from "./sefaria/sefaria"; import { OnInView } from "./Misc"; -const Promotions = ({ topicDataHasLoaded }) => { +const Promotions = () => { const [inAppAds, setInAppAds] = useState(Sefaria._inAppAds); // local cache const [matchingAds, setMatchingAds] = useState(null); // match the ads to what comes from Strapi const context = useContext(AdContext); @@ -140,7 +140,7 @@ const Promotions = ({ topicDataHasLoaded }) => { }); } - return matchingAds && topicDataHasLoaded + return matchingAds ? matchingAds.map((ad) => ( <SidebarAd context={context} matchingAd={ad} key={ad.campaignId} /> )) diff --git a/static/js/TopicPage.jsx b/static/js/TopicPage.jsx index 34011c1dee..528701d19e 100644 --- a/static/js/TopicPage.jsx +++ b/static/js/TopicPage.jsx @@ -399,6 +399,7 @@ const TopicPage = ({ const [parashaData, setParashaData] = useState(null); const [showFilterHeader, setShowFilterHeader] = useState(false); const tabDisplayData = useTabDisplayData(translationLanguagePreference, versionPref); + const [isTopicSideColumnRendered, setIsTopicSideColumnRendered] = useState(false); const scrollableElement = useRef(); @@ -430,6 +431,12 @@ const TopicPage = ({ } }, [topic]); + useEffect(() => { + if (!topicData.isLoading) { + setIsTopicSideColumnRendered(true); + } + }, [topicData]); + // Set up tabs and register incremental load hooks const displayTabs = []; let onClickFilterIndex = 2; @@ -526,7 +533,7 @@ const TopicPage = ({ timePeriod={topicData.timePeriod} properties={topicData.properties} /> - <Promotions topicDataHasLoaded={!topicData.isLoading} /> + {isTopicSideColumnRendered && <Promotions/>} </> ) : null} </div> From 1a3f49a9398aa0a61e1b4414f5bfdfc84343c8d5 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Mon, 31 Jul 2023 15:57:40 -0400 Subject: [PATCH 102/756] Prevent promotions component from rendering until topics data is loaded so that sidebar ad impressions are valid --- static/js/TopicPage.jsx | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/static/js/TopicPage.jsx b/static/js/TopicPage.jsx index 528701d19e..6970b947c7 100644 --- a/static/js/TopicPage.jsx +++ b/static/js/TopicPage.jsx @@ -399,7 +399,6 @@ const TopicPage = ({ const [parashaData, setParashaData] = useState(null); const [showFilterHeader, setShowFilterHeader] = useState(false); const tabDisplayData = useTabDisplayData(translationLanguagePreference, versionPref); - const [isTopicSideColumnRendered, setIsTopicSideColumnRendered] = useState(false); const scrollableElement = useRef(); @@ -431,12 +430,6 @@ const TopicPage = ({ } }, [topic]); - useEffect(() => { - if (!topicData.isLoading) { - setIsTopicSideColumnRendered(true); - } - }, [topicData]); - // Set up tabs and register incremental load hooks const displayTabs = []; let onClickFilterIndex = 2; @@ -533,7 +526,7 @@ const TopicPage = ({ timePeriod={topicData.timePeriod} properties={topicData.properties} /> - {isTopicSideColumnRendered && <Promotions/>} + {!topicData.isLoading && <Promotions/>} </> ) : null} </div> From d6a56a974ada600423c2164010ad16e779f22969 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Mon, 31 Jul 2023 22:40:07 -0400 Subject: [PATCH 103/756] Fixes time query range in GraphQL. Relevant content will be found that has a start date 2 weeks ago and an end date 2 weeks into the future --- static/js/context.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/static/js/context.js b/static/js/context.js index 687aa8283e..a4b2b3f537 100644 --- a/static/js/context.js +++ b/static/js/context.js @@ -27,13 +27,12 @@ function StrapiDataProvider({ children }) { return new Date(parts[0], parts[1] - 1, parts[2]).toJSON(); }; let currentDate = new Date(); - let oneWeekFromNow = new Date(); - oneWeekFromNow.setDate(currentDate.getDate() + 7); - currentDate.setDate(currentDate.getDate() - 2); // Fix time management, previous code got time 1 hour in the future in UTC - let startDate = getJSONDateStringInLocalTimeZone(currentDate); - let endDate = getJSONDateStringInLocalTimeZone(oneWeekFromNow); - console.log(startDate); - console.log(endDate); + let twoWeeksAgo = new Date(); + let twoWeeksFromNow = new Date(); + twoWeeksFromNow.setDate(currentDate.getDate() + 14); + twoWeeksAgo.setDate(currentDate.getDate() - 14); + let startDate = getJSONDateStringInLocalTimeZone(twoWeeksAgo); + let endDate = getJSONDateStringInLocalTimeZone(twoWeeksFromNow); const query = ` query { banners( From 8cf73a07d6fb13c5f42be8530ff02007f8a57339 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Tue, 1 Aug 2023 17:04:26 -0400 Subject: [PATCH 104/756] Adds linebreaks to be properly interpreted from the content that comes from Strapi --- static/css/s2.css | 2 +- static/js/Misc.jsx | 11 ++++++----- static/js/context.js | 5 +++++ 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index d7927bde39..0a5f9d7c17 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -5797,7 +5797,7 @@ But not to use a display block directive that might break continuous mode for ot margin-bottom: 5px; color: #999; } -.textList.singlePanel .versionsTextList .topFiltersInner { +.textList.singlePanel .versionsTextList .topFiltersInner, .line-break { white-space: pre-wrap; } .showMoreFilters { diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 30bdde7def..8c6f825349 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -8,7 +8,7 @@ import classNames from 'classnames'; import PropTypes from 'prop-types'; import Component from 'react-class'; import { usePaginatedDisplay } from './Hooks'; -import {ContentLanguageContext, AdContext, StrapiDataContext} from './context'; +import {ContentLanguageContext, AdContext, StrapiDataContext, replaceNewLinesWithLinebreaks} from './context'; import ReactCrop from 'react-image-crop'; import 'react-image-crop/dist/ReactCrop.css'; import {ContentText} from "./ContentText"; @@ -60,7 +60,7 @@ const __filterChildrenByLanguage = (children, language) => { return newChildren; }; -const InterfaceText = ({text, html, markdown, children, context}) => { +const InterfaceText = ({text, html, markdown, children, context, styleClasses}) => { /** * Renders a single span for interface string with either class `int-en`` or `int-he` depending on Sefaria.interfaceLang. * If passed explicit text or html objects as props with "en" and/or "he", will only use those to determine correct text or fallback text to display. @@ -68,11 +68,12 @@ const InterfaceText = ({text, html, markdown, children, context}) => { * `children` can be the English string, which will be translated with Sefaria._ if needed. * `children` can also take the form of <LangText> components above, so they can be used for longer paragrpahs or paragraphs containing html, if needed. * `context` is passed to Sefaria._ for additional translation context + * `styleClasses` are CSS classes that you want applied to all the interface languages */ const contentVariable = html ? html : markdown ? markdown : text; // assumption is `markdown` or `html` are preferred over `text` if they are present const isHebrew = Sefaria.interfaceLang === "hebrew"; - let elemclasses = classNames({"int-en": !isHebrew, "int-he": isHebrew}); + let elemclasses = classNames(styleClasses, {"int-en": !isHebrew, "int-he": isHebrew}); let textResponse = null; if (contentVariable) {// Prioritize explicit props passed in for text of the element, does not attempt to use Sefaria._() for this case. let {he, en} = contentVariable; @@ -2207,7 +2208,7 @@ const InterruptingMessage = ({ </div> <div id="interruptingMessageContent"> <div id="highHolidayDonation"> - <InterfaceText markdown={strapi.modal.modalText} /> + <InterfaceText markdown={replaceNewLinesWithLinebreaks(strapi.modal.modalText)} styleClasses={['line-break']} /> <div className="buttons"> <a className="button int-en" @@ -2348,7 +2349,7 @@ const Banner = ({ onClose }) => { <div id="bannerMessage" className={timesUp ? "" : "hidden"}> <div id="bannerMessageContent"> <div id="bannerTextBox"> - <InterfaceText markdown={strapi.banner.bannerText} /> + <InterfaceText markdown={replaceNewLinesWithLinebreaks(strapi.banner.bannerText)} styleClasses={['line-break']} /> </div> <div id="bannerButtonBox"> <a diff --git a/static/js/context.js b/static/js/context.js index a4b2b3f537..390f01ac68 100644 --- a/static/js/context.js +++ b/static/js/context.js @@ -11,6 +11,11 @@ AdContext.displayName = "AdContext"; const StrapiDataContext = React.createContext({}); StrapiDataContext.displayName = "StrapiDataContext"; +export function replaceNewLinesWithLinebreaks(string) +{ + return string.replace(/\n/gi, "  \n") +} + function StrapiDataProvider({ children }) { const [dataFromStrapiHasBeenReceived, setDataFromStrapiHasBeenReceived] = useState(false); From 37a51181382c87d53ab8a625edc9a5dc59ecf1f5 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Wed, 2 Aug 2023 12:38:22 -0400 Subject: [PATCH 105/756] fix(strapi-cms): Fixes showing linebreaks for Strapi content in banners/modals rendered from Markdown --- static/js/context.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/static/js/context.js b/static/js/context.js index 390f01ac68..9a16396325 100644 --- a/static/js/context.js +++ b/static/js/context.js @@ -11,9 +11,16 @@ AdContext.displayName = "AdContext"; const StrapiDataContext = React.createContext({}); StrapiDataContext.displayName = "StrapiDataContext"; -export function replaceNewLinesWithLinebreaks(string) -{ - return string.replace(/\n/gi, "  \n") +const transformValues = (obj, callback) => { + const newObj = {}; + for (let key in obj) { + newObj[key] = obj[key] !== null ? callback(obj[key]) : null; + } + return newObj; +}; + +export function replaceNewLinesWithLinebreaks(content) { + return transformValues(content, s => s.replace(/\n/gi, "  \n") + "  \n"); } function StrapiDataProvider({ children }) { From 40d7d3039f18c986e01d28f44d5a8d7afba0d5d2 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Wed, 2 Aug 2023 14:48:59 -0400 Subject: [PATCH 106/756] chore(strapi-cms): Removes old InterruptingMessage system --- reader/browsertest/framework/elements.py | 38 ----------- reader/views.py | 18 +----- sefaria/local_settings_ci.py | 1 - sefaria/local_settings_example.py | 11 ---- sefaria/model/__init__.py | 1 - sefaria/model/interrupting_message.py | 81 ------------------------ sefaria/model/user_profile.py | 18 ------ sefaria/settings.py | 30 --------- sefaria/system/context_processors.py | 1 - sefaria/urls.py | 1 - static/js/sefaria/sefaria.js | 2 - 11 files changed, 3 insertions(+), 199 deletions(-) delete mode 100644 sefaria/model/interrupting_message.py diff --git a/reader/browsertest/framework/elements.py b/reader/browsertest/framework/elements.py index 25b2f24bb3..3895328aa3 100644 --- a/reader/browsertest/framework/elements.py +++ b/reader/browsertest/framework/elements.py @@ -344,13 +344,6 @@ def scroll_nav_panel_to_bottom(self): # Initial Setup ################ - def set_modal_cookie(self): - # # set cookie to avoid popup interruption - # # We now longer set the welcomeToS2LoggedOut message by default. - # # TODO is this method still needed? YES - # pass - self.driver.add_cookie({"name": "welcomeToS2LoggedOut", "value": "true"}) - def set_cookies_cookie(self): # set cookie to avoid popup interruption # We now longer set the welcomeToS2LoggedOut message by default. @@ -364,37 +357,6 @@ def click_accept_cookies(self): except NoSuchElementException: pass - def close_modal_popup(self): - """ - :return: Boolean - did we manage to close the popup? - """ - message = self.driver.execute_script('return Sefaria.interruptingMessage') - if not message: - return True - - time.sleep(3) - try: - self.driver.find_element_by_css_selector('#interruptingMessage #interruptingMessageClose') - self.click('#interruptingMessage #interruptingMessageClose') - return True - except NoSuchElementException: - pass - - try: - self.driver.find_element_by_css_selector('#bannerMessageClose') - self.click('#bannerMessageClose') - return True - except NoSuchElementException: - return False - - def close_popup_with_accept(self): - try: - alert = self.driver.switch_to.alert - alert.accept() - except NoAlertPresentException: - print('A <<NoAlertPresentException>> was thrown') - pass - # Login ######### diff --git a/reader/views.py b/reader/views.py index c90003d34f..64d5fb1a8b 100644 --- a/reader/views.py +++ b/reader/views.py @@ -68,7 +68,7 @@ from sefaria.image_generator import make_img_http_response import sefaria.tracker as tracker -from sefaria.settings import NODE_TIMEOUT, DEBUG, GLOBAL_INTERRUPTING_MESSAGE +from sefaria.settings import NODE_TIMEOUT, DEBUG from sefaria.model.category import TocCollectionNode from sefaria.model.abstract import SluggedAbstractMongoRecord from sefaria.utils.calendars import parashat_hashavua_and_haftara @@ -197,7 +197,6 @@ def base_props(request): if request.user.is_authenticated: profile = UserProfile(user_obj=request.user) - interrupting_message_dict = GLOBAL_INTERRUPTING_MESSAGE or {"name": profile.interrupting_message()} user_data = { "_uid": request.user.id, "_email": request.user.email, @@ -217,8 +216,7 @@ def base_props(request): "notificationCount": profile.unread_notification_count(), "notifications": profile.recent_notifications().client_contents(), "saved": {"loaded": False, "items": profile.get_history(saved=True, secondary=False, serialized=True, annotate=False)}, # saved is initially loaded without text annotations so it can quickly immediately mark any texts/sheets as saved, but marks as `loaded: false` so the full annotated data will be requested if the user visits the saved/history page - "last_place": profile.get_history(last_place=True, secondary=False, sheets=False, serialized=True), - "interruptingMessage": InterruptingMessage(attrs=interrupting_message_dict, request=request).contents(), + "last_place": profile.get_history(last_place=True, secondary=False, sheets=False, serialized=True) } else: user_data = { @@ -239,8 +237,7 @@ def base_props(request): "notificationCount": 0, "notifications": [], "saved": {"loaded": False, "items": []}, - "last_place": [], - "interruptingMessage": InterruptingMessage(attrs=GLOBAL_INTERRUPTING_MESSAGE, request=request).contents(), + "last_place": [] } user_data.update({ "last_cached": library.get_last_cached_time(), @@ -3769,15 +3766,6 @@ def my_profile(request): url += "?tab=" + request.GET.get("tab") return redirect(url) - -def interrupting_messages_read_api(request, message): - if not request.user.is_authenticated: - return jsonResponse({"error": "You must be logged in to use this API."}) - profile = UserProfile(id=request.user.id) - profile.mark_interrupting_message_read(message) - return jsonResponse({"status": "ok"}) - - @login_required @ensure_csrf_cookie def edit_profile(request): diff --git a/sefaria/local_settings_ci.py b/sefaria/local_settings_ci.py index 971c415b7c..1368222c38 100644 --- a/sefaria/local_settings_ci.py +++ b/sefaria/local_settings_ci.py @@ -48,7 +48,6 @@ MAINTENANCE_MESSAGE = "" GLOBAL_WARNING = False GLOBAL_WARNING_MESSAGE = "" -# GLOBAL_INTERRUPTING_MESSAGE = None SECRET_KEY = 'insert your long random secret key here !' diff --git a/sefaria/local_settings_example.py b/sefaria/local_settings_example.py index 3037ee7981..33103905c5 100644 --- a/sefaria/local_settings_example.py +++ b/sefaria/local_settings_example.py @@ -121,17 +121,6 @@ DOWN_FOR_MAINTENANCE = False MAINTENANCE_MESSAGE = "" -# GLOBAL_INTERRUPTING_MESSAGE = None -""" -GLOBAL_INTERRUPTING_MESSAGE = { - "name": "messageName", - "repetition": 1, - "is_fundraising": True, - "style": "modal" # "modal" or "banner" - "condition": {"returning_only": True} -} -""" - # Location of Strapi CMS instance # For local development, Strapi is located at http://localhost:1337 by default STRAPI_LOCATION = None diff --git a/sefaria/model/__init__.py b/sefaria/model/__init__.py index aec7537cf7..b5acc4cc97 100644 --- a/sefaria/model/__init__.py +++ b/sefaria/model/__init__.py @@ -24,7 +24,6 @@ from .layer import Layer, LayerSet from .notification import Notification, NotificationSet, GlobalNotification, GlobalNotificationSet from .trend import get_session_traits -from .interrupting_message import InterruptingMessage from .queue import IndexQueue, IndexQueueSet from .lock import Lock, LockSet, set_lock, release_lock, check_lock, expire_locks from .following import FollowRelationship, FollowersSet, FolloweesSet diff --git a/sefaria/model/interrupting_message.py b/sefaria/model/interrupting_message.py deleted file mode 100644 index 0daa75f920..0000000000 --- a/sefaria/model/interrupting_message.py +++ /dev/null @@ -1,81 +0,0 @@ -import json -from django.template.loader import render_to_string -from sefaria.model.user_profile import UserProfile - -class InterruptingMessage(object): - def __init__(self, attrs={}, request=None): - if attrs is None: - attrs = {} - self.name = attrs.get("name", None) - self.style = attrs.get("style", "modal") - self.repetition = attrs.get("repetition", 0) - self.is_fundraising = attrs.get("is_fundraising", False) - self.condition = attrs.get("condition", {}) - self.request = request - self.cookie_name = "%s_%d" % (self.name, self.repetition) - self.should_show = self.check_condition() - - def check_condition(self): - """Returns true if this interrupting message should be shown given its conditions""" - - # Always show to debug - if self.condition.get("debug", False): - return True - - # Nameless is useless - if not self.name: - return False - - # Don't show this name/repetiion pair more than once - if self.request.COOKIES.get(self.cookie_name, False): - return False - - # Limit to returning visitors only - if self.condition.get("returning_only", False): - if not self.request.COOKIES.get("_ga", False): - return False - - # Filter mobile traffic - if self.condition.get("desktop_only", True): - if self.request.user_agent.is_mobile: - return False - - # Filter non English interface traffic - if self.condition.get("english_only", True): - if self.request.LANGUAGE_CODE != "en": - return False - - # Filter non Hebrew interface traffic - if self.condition.get("hebrew_only", False): - if self.request.LANGUAGE_CODE != 'he': - return False - - # Filter logged out users - if self.condition.get("logged_in_only", False): - if not self.request.user.is_authenticated: - return False - - if self.is_fundraising: - if self.request.user.is_authenticated: - profile = UserProfile(id=self.request.user.id) - if(profile.is_sustainer): - return False - - return True - - def contents(self): - """ - Returns JSON for this interrupting message which may be just `null` if the - message should not be shown. - """ - - return { - "name": self.name, - "style": self.style, - "html": render_to_string("messages/%s.html" % self.name), - "repetition": self.repetition - } if self.should_show else None - - - def json(self): - return json.dumps(self.contents()) diff --git a/sefaria/model/user_profile.py b/sefaria/model/user_profile.py index e21f0784de..5571c0cfb2 100644 --- a/sefaria/model/user_profile.py +++ b/sefaria/model/user_profile.py @@ -355,7 +355,6 @@ def __init__(self, user_obj=None, id=None, slug=None, email=None, user_registrat self.twitter = "" self.linkedin = "" self.pinned_sheets = [] - self.interrupting_messages = ["newUserWelcome"] self.last_sync_web = 0 # epoch time for last sync of web app self.profile_pic_url = "" self.profile_pic_url_small = "" @@ -413,7 +412,6 @@ def __init__(self, user_obj=None, id=None, slug=None, email=None, user_registrat # create a profile for them. This allows two enviornments to share a user database, # while maintaining separate profiles (e.g. Sefaria and S4D). self.assign_slug() - self.interrupting_messages = [] self.save() @property @@ -602,21 +600,6 @@ def unread_notification_count(self): from sefaria.model.notification import NotificationSet return NotificationSet().unread_for_user(self.id).count() - def interrupting_message(self): - """ - Returns the next message to interupt the user with, if any are queued up. - """ - messages = self.interrupting_messages - return messages[0] if len(messages) > 0 else None - - def mark_interrupting_message_read(self, message): - """ - Removes `message` from the users list of queued interrupting_messages. - """ - if message in self.interrupting_messages: - self.interrupting_messages.remove(message) - self.save() - def process_history_item(self, hist, time_stamp): action = hist.pop("action", None) if self.settings.get("reading_history", True) or action == "add_saved": # regular case where history enabled, save/unsave saved item etc. or save history in either case @@ -680,7 +663,6 @@ def to_mongo_dict(self): "settings": self.settings, "version_preferences_by_corpus": self.version_preferences_by_corpus, "attr_time_stamps": self.attr_time_stamps, - "interrupting_messages": getattr(self, "interrupting_messages", []), "is_sustainer": self.is_sustainer, "tag_order": getattr(self, "tag_order", None), "last_sync_web": self.last_sync_web, diff --git a/sefaria/settings.py b/sefaria/settings.py index 40d54c4278..7eea25ff70 100644 --- a/sefaria/settings.py +++ b/sefaria/settings.py @@ -298,36 +298,6 @@ } - -# GLOBAL_INTERRUPTING_MESSAGE = { -# "name": "2023-06-16-help-center", -# "style": "banner", # "modal" or "banner" -# "repetition": 1, -# "is_fundraising": False, -# "condition": { -# "returning_only": False, -# "english_only": False, -# "desktop_only": True, -# "debug": False, -# } -# } - -GLOBAL_INTERRUPTING_MESSAGE = { - "name": "2022-04-07-passover-donate-modal", - "style": "modal", # "modal" or "banner" - "repetition": 1, - "is_fundraising": False, - "condition": { - "returning_only": False, - "english_only": False, - "desktop_only": True, - "debug": True, - } -} - -# GLOBAL_INTERRUPTING_MESSAGE = None - - # Grab environment specific settings from a file which # is left out of the repo. try: diff --git a/sefaria/system/context_processors.py b/sefaria/system/context_processors.py index cc7a71afa6..d4268c6d6e 100644 --- a/sefaria/system/context_processors.py +++ b/sefaria/system/context_processors.py @@ -13,7 +13,6 @@ from sefaria.site.site_settings import SITE_SETTINGS from sefaria.model import library from sefaria.model.user_profile import UserProfile, UserHistorySet, UserWrapper -from sefaria.model.interrupting_message import InterruptingMessage from sefaria.utils import calendars from sefaria.utils.util import short_to_long_lang_code from sefaria.utils.hebrew import hebrew_parasha_name diff --git a/sefaria/urls.py b/sefaria/urls.py index e3e6d30a75..b97e18771a 100644 --- a/sefaria/urls.py +++ b/sefaria/urls.py @@ -93,7 +93,6 @@ url(r'^api/profile/(?P<slug>[^/]+)$', reader_views.profile_get_api), url(r'^api/profile/(?P<slug>[^/]+)/(?P<ftype>followers|following)$', reader_views.profile_follow_api), url(r'^api/user_history/saved$', reader_views.saved_history_for_ref), - url(r'^api/interrupting-messages/read/(?P<message>.+)$', reader_views.interrupting_messages_read_api), ] # Topics diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index 4f2e983932..5b1c6578da 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -2997,8 +2997,6 @@ Sefaria.unpackBaseProps = function(props){ "last_place", "interfaceLang", "multiPanel", - "interruptingMessage", - "community", "followRecommendations", "trendingTopics", From 05f1d34a92fa3f9f5742bf38b56063bbe3e3992b Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Fri, 4 Aug 2023 11:31:48 -0400 Subject: [PATCH 107/756] feat(strapi-cms): Adds support for using a modal header --- static/js/Misc.jsx | 19 +++++++++++++++---- static/js/ReaderApp.jsx | 3 --- static/js/context.js | 21 +++++++++++++-------- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 8c6f825349..4262d0c544 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -2208,7 +2208,18 @@ const InterruptingMessage = ({ </div> <div id="interruptingMessageContent"> <div id="highHolidayDonation"> - <InterfaceText markdown={replaceNewLinesWithLinebreaks(strapi.modal.modalText)} styleClasses={['line-break']} /> + {strapi.modal.modalHeader.en && ( + <h4 className="int-en">{strapi.modal.modalHeader.en}</h4> + )} + {strapi.modal.modalHeader.he && ( + <h4 className="int-he">{strapi.modal.modalHeader.he}</h4> + )} + <InterfaceText + markdown={replaceNewLinesWithLinebreaks( + strapi.modal.modalText + )} + styleClasses={["line-break"]} + /> <div className="buttons"> <a className="button int-en" @@ -2227,9 +2238,9 @@ const InterruptingMessage = ({ target="_blank" href={strapi.modal.buttonURL.he} onClick={() => { - closeModal("donate_button_clicked"); - }} - > + closeModal("donate_button_clicked"); + }} + > <span className="int-he"> {strapi.modal.buttonText.he} </span> diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index fe8737afd6..e101d9aa1a 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -2183,9 +2183,6 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { classDict[interfaceLangClass] = true; var classes = classNames(classDict); -// const strapi = useContext(StrapiDataContext); -// const { interruptingMessageModal } = useContext(StrapiDataContext); - return ( <StrapiDataProvider> <AdContext.Provider value={this.getUserContext()}> diff --git a/static/js/context.js b/static/js/context.js index 9a16396325..090ff39ef0 100644 --- a/static/js/context.js +++ b/static/js/context.js @@ -104,12 +104,14 @@ function StrapiDataProvider({ children }) { locale buttonText buttonURL + modalHeader modalText } } } modalEndDate modalStartDate + modalHeader modalText publishedAt shouldDeployOnMobile @@ -218,14 +220,17 @@ function StrapiDataProvider({ children }) { }); modal.attributes.locales = ["en", "he"]; } else { - ["modalText", "buttonText", "buttonURL"].forEach( - (attribute) => { - modal.attributes[attribute] = { - en: modal.attributes[attribute], - he: null, - }; - } - ); + [ + "modalHeader", + "modalText", + "buttonText", + "buttonURL", + ].forEach((attribute) => { + modal.attributes[attribute] = { + en: modal.attributes[attribute], + he: null, + }; + }); modal.attributes.locales = ["en"]; } setModal(modal.attributes); From a5286efa802e3255fb7077f4cc86afd62ea1d53e Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Mon, 7 Aug 2023 13:49:36 -0400 Subject: [PATCH 108/756] fix(strapi-cms): Enables working with Strapi Cloud on local setup --- templates/base.html | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/templates/base.html b/templates/base.html index a8f6417b17..6b6453810e 100644 --- a/templates/base.html +++ b/templates/base.html @@ -238,7 +238,11 @@ }; {% if STRAPI_LOCATION and STRAPI_PORT %} - var STRAPI_INSTANCE = "{{ STRAPI_LOCATION }}:{{ STRAPI_PORT }}"; + {% if STRAPI_PORT == 80 %} + var STRAPI_INSTANCE = "{{ STRAPI_LOCATION }}"; + {% else %} + var STRAPI_INSTANCE = "{{ STRAPI_LOCATION }}:{{ STRAPI_PORT }}"; + {% endif %} {% endif %} {% endautoescape %} From 982494268f75931005f3809a3d3effe70596b8ae Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Tue, 8 Aug 2023 23:33:04 -0400 Subject: [PATCH 109/756] fix(strapi-cms): Fixes errors from cherry-picking --- static/js/Misc.jsx | 2 +- static/js/ReaderApp.jsx | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 4262d0c544..9cf2aa3db7 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -1,5 +1,5 @@ //const React = require('react'); -import React, {useEffect, useRef, useState} from 'react'; +import React, {useEffect, useRef, useState, useContext} from 'react'; import ReactDOM from 'react-dom'; import $ from './sefaria/sefariaJquery'; import {CollectionsModal} from "./CollectionsWidget"; diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index e101d9aa1a..92fe1e1f46 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -2195,7 +2195,6 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { {panels} {sefariaModal} {communityPagePreviewControls} - {beitMidrashPanel} <CookiesNotification /> {/* <ExampleComponent /> */} </div> From f26d1753eb400451b2850a09fe12749387b477b6 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 9 Aug 2023 10:12:36 +0300 Subject: [PATCH 110/756] helm: fix inconsistency in location of elastic secrets --- helm-chart/sefaria-project/templates/_helpers.tpl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/helm-chart/sefaria-project/templates/_helpers.tpl b/helm-chart/sefaria-project/templates/_helpers.tpl index 881445d3af..473afbf653 100644 --- a/helm-chart/sefaria-project/templates/_helpers.tpl +++ b/helm-chart/sefaria-project/templates/_helpers.tpl @@ -47,8 +47,8 @@ slack-webhook-{{ .Values.deployEnv }} {{- end }} {{- define "sefaria.secrets.elasticCertificate" }} -{{- if .Values.web.secrets.elasticCertificate.ref -}} -{{- .Values.web.secrets.elasticCertificate.ref }} +{{- if .Values.secrets.elasticCertificate.ref -}} +{{- .Values.secrets.elasticCertificate.ref }} {{- else -}} elastic-certificate-{{ .Values.deployEnv }} {{- end }} @@ -56,7 +56,7 @@ elastic-certificate-{{ .Values.deployEnv }} {{- define "sefaria.secrets.elasticUser" }} {{- if .Values.secrets.elasticUser.ref -}} -{{- .Values.web.secrets.elasticUser.ref }} +{{- .Values.secrets.elasticUser.ref }} {{- else -}} elastic-user-{{ .Values.deployEnv }} {{- end }} @@ -64,7 +64,7 @@ elastic-user-{{ .Values.deployEnv }} {{- define "sefaria.secrets.elasticAdmin" }} {{- if .Values.secrets.elasticAdmin.ref -}} -{{- .Values.web.secrets.elasticAdmin.ref }} +{{- .Values.secrets.elasticAdmin.ref }} {{- else -}} elastic-admin-{{ .Values.deployEnv }} {{- end }} From d568dda2656b1b00a2c3a501a7c7e63788d2cc5c Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 9 Aug 2023 10:32:59 +0300 Subject: [PATCH 111/756] helm: revert change to location of elasticCertificate location which was incorrect --- helm-chart/sefaria-project/templates/_helpers.tpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/helm-chart/sefaria-project/templates/_helpers.tpl b/helm-chart/sefaria-project/templates/_helpers.tpl index 473afbf653..ce020c7bb7 100644 --- a/helm-chart/sefaria-project/templates/_helpers.tpl +++ b/helm-chart/sefaria-project/templates/_helpers.tpl @@ -47,8 +47,8 @@ slack-webhook-{{ .Values.deployEnv }} {{- end }} {{- define "sefaria.secrets.elasticCertificate" }} -{{- if .Values.secrets.elasticCertificate.ref -}} -{{- .Values.secrets.elasticCertificate.ref }} +{{- if .Values.web.secrets.elasticCertificate.ref -}} +{{- .Values.web.secrets.elasticCertificate.ref }} {{- else -}} elastic-certificate-{{ .Values.deployEnv }} {{- end }} From c635e1acaaf340f2837326ae48b9e20ecf878b3b Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Wed, 9 Aug 2023 11:00:11 -0400 Subject: [PATCH 112/756] helm: Add environment variables for Strapi --- .../templates/configmap/local-settings-file.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml b/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml index 88111bcac1..fe44da8ced 100644 --- a/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml +++ b/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml @@ -311,6 +311,9 @@ data: # GLOBAL_INTERRUPTING_MESSAGE = None + STRAPI_LOCATION = os.getenv("STRAPI_LOCATION") + STRAPI_PORT = os.getenv("STRAPI_PORT") + structlog.configure( processors=[ structlog.stdlib.filter_by_level, From ec90e087c60ec50fadb9973ac6796d780194715f Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Wed, 9 Aug 2023 11:48:02 -0400 Subject: [PATCH 113/756] chore(strapi-cms): Add Strapi environment variables for CI and testing --- sefaria/local_settings_ci.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sefaria/local_settings_ci.py b/sefaria/local_settings_ci.py index 1368222c38..de5d56d849 100644 --- a/sefaria/local_settings_ci.py +++ b/sefaria/local_settings_ci.py @@ -193,6 +193,9 @@ } } +STRAPI_LOCATION = None +STRAPI_PORT = None + structlog.configure( processors=[ structlog.stdlib.filter_by_level, From 23195bc6f7198e8d110367179e88ffd0344edc94 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Wed, 9 Aug 2023 17:40:23 -0400 Subject: [PATCH 114/756] Fix error with wrong URL being used for making GraphQL requests to Strapi Cloud. Environment variable is being set as a string instead of an integer --- templates/base.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/base.html b/templates/base.html index 6b6453810e..6e0f0c820c 100644 --- a/templates/base.html +++ b/templates/base.html @@ -238,7 +238,7 @@ }; {% if STRAPI_LOCATION and STRAPI_PORT %} - {% if STRAPI_PORT == 80 %} + {% if STRAPI_PORT == "80" %} var STRAPI_INSTANCE = "{{ STRAPI_LOCATION }}"; {% else %} var STRAPI_INSTANCE = "{{ STRAPI_LOCATION }}:{{ STRAPI_PORT }}"; From f2d1f1ff87b97768a7b0afcfffdf5f570189c641 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Thu, 10 Aug 2023 10:28:17 +0300 Subject: [PATCH 115/756] fix: use . to run entrypoint to solve permission issue --- helm-chart/sefaria-project/templates/rollout/nginx.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helm-chart/sefaria-project/templates/rollout/nginx.yaml b/helm-chart/sefaria-project/templates/rollout/nginx.yaml index df2f1e61f4..bbf3d266af 100644 --- a/helm-chart/sefaria-project/templates/rollout/nginx.yaml +++ b/helm-chart/sefaria-project/templates/rollout/nginx.yaml @@ -53,7 +53,7 @@ spec: image: "{{ .Values.nginx.containerImage.imageRegistry }}:{{ .Values.nginx.containerImage.tag }}" imagePullPolicy: Always command: - - /entrypoint.sh + - "./entrypoint.sh" ports: - containerPort: 80 - containerPort: 443 From cfa5590545e61b5370b20804eaf597ad762085c5 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Thu, 10 Aug 2023 10:28:17 +0300 Subject: [PATCH 116/756] helm: use . to run entrypoint to solve permission issue --- helm-chart/sefaria-project/templates/rollout/nginx.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helm-chart/sefaria-project/templates/rollout/nginx.yaml b/helm-chart/sefaria-project/templates/rollout/nginx.yaml index df2f1e61f4..bbf3d266af 100644 --- a/helm-chart/sefaria-project/templates/rollout/nginx.yaml +++ b/helm-chart/sefaria-project/templates/rollout/nginx.yaml @@ -53,7 +53,7 @@ spec: image: "{{ .Values.nginx.containerImage.imageRegistry }}:{{ .Values.nginx.containerImage.tag }}" imagePullPolicy: Always command: - - /entrypoint.sh + - "./entrypoint.sh" ports: - containerPort: 80 - containerPort: 443 From 83908f7a9f50be31953f2aa24c497e25074b8559 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Thu, 10 Aug 2023 10:35:31 +0300 Subject: [PATCH 117/756] helm: remove quotations --- helm-chart/sefaria-project/templates/rollout/nginx.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helm-chart/sefaria-project/templates/rollout/nginx.yaml b/helm-chart/sefaria-project/templates/rollout/nginx.yaml index bbf3d266af..d708da27f1 100644 --- a/helm-chart/sefaria-project/templates/rollout/nginx.yaml +++ b/helm-chart/sefaria-project/templates/rollout/nginx.yaml @@ -53,7 +53,7 @@ spec: image: "{{ .Values.nginx.containerImage.imageRegistry }}:{{ .Values.nginx.containerImage.tag }}" imagePullPolicy: Always command: - - "./entrypoint.sh" + - ./entrypoint.sh ports: - containerPort: 80 - containerPort: 443 From 4d5e88f35e806ea7da48ba678a0f62a51bee402a Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Thu, 10 Aug 2023 12:06:30 +0300 Subject: [PATCH 118/756] helm: fix entrypoint location --- helm-chart/sefaria-project/templates/rollout/nginx.yaml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/helm-chart/sefaria-project/templates/rollout/nginx.yaml b/helm-chart/sefaria-project/templates/rollout/nginx.yaml index d708da27f1..d043427174 100644 --- a/helm-chart/sefaria-project/templates/rollout/nginx.yaml +++ b/helm-chart/sefaria-project/templates/rollout/nginx.yaml @@ -52,8 +52,7 @@ spec: - name: nginx image: "{{ .Values.nginx.containerImage.imageRegistry }}:{{ .Values.nginx.containerImage.tag }}" imagePullPolicy: Always - command: - - ./entrypoint.sh + command: ["/usr/src/entrypoint.sh"] ports: - containerPort: 80 - containerPort: 443 @@ -75,7 +74,7 @@ spec: name: nginx-conf subPath: nginx.template.conf readOnly: true - - mountPath: /entrypoint.sh + - mountPath: /usr/src/entrypoint.sh name: nginx-conf subPath: entrypoint.sh defaultMode: 0755 From b6aea2d5d8bd54556ef8d52cabdfed9f83067963 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Sun, 13 Aug 2023 13:01:12 +0300 Subject: [PATCH 119/756] helm: fix entrypoint not running --- helm-chart/sefaria-project/templates/rollout/nginx.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helm-chart/sefaria-project/templates/rollout/nginx.yaml b/helm-chart/sefaria-project/templates/rollout/nginx.yaml index d043427174..3e08cf09d9 100644 --- a/helm-chart/sefaria-project/templates/rollout/nginx.yaml +++ b/helm-chart/sefaria-project/templates/rollout/nginx.yaml @@ -52,7 +52,7 @@ spec: - name: nginx image: "{{ .Values.nginx.containerImage.imageRegistry }}:{{ .Values.nginx.containerImage.tag }}" imagePullPolicy: Always - command: ["/usr/src/entrypoint.sh"] + command: ["bash -c /usr/src/entrypoint.sh"] ports: - containerPort: 80 - containerPort: 443 From fa87c7484e9813fdca619d590823917fb16de4d2 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Sun, 13 Aug 2023 13:11:13 +0300 Subject: [PATCH 120/756] helm: fix entrypoint not running --- helm-chart/sefaria-project/templates/rollout/nginx.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/helm-chart/sefaria-project/templates/rollout/nginx.yaml b/helm-chart/sefaria-project/templates/rollout/nginx.yaml index 3e08cf09d9..306c1da3ec 100644 --- a/helm-chart/sefaria-project/templates/rollout/nginx.yaml +++ b/helm-chart/sefaria-project/templates/rollout/nginx.yaml @@ -52,7 +52,7 @@ spec: - name: nginx image: "{{ .Values.nginx.containerImage.imageRegistry }}:{{ .Values.nginx.containerImage.tag }}" imagePullPolicy: Always - command: ["bash -c /usr/src/entrypoint.sh"] + command: ["bash -c /entrypoint.sh"] ports: - containerPort: 80 - containerPort: 443 @@ -74,7 +74,7 @@ spec: name: nginx-conf subPath: nginx.template.conf readOnly: true - - mountPath: /usr/src/entrypoint.sh + - mountPath: /entrypoint.sh name: nginx-conf subPath: entrypoint.sh defaultMode: 0755 From 8bef91e2d572dfa4f331591a57dbcc2c40649917 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Mon, 14 Aug 2023 09:56:28 +0300 Subject: [PATCH 121/756] helm: fix entrypoint not running --- helm-chart/sefaria-project/templates/rollout/nginx.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helm-chart/sefaria-project/templates/rollout/nginx.yaml b/helm-chart/sefaria-project/templates/rollout/nginx.yaml index 306c1da3ec..23f1dba132 100644 --- a/helm-chart/sefaria-project/templates/rollout/nginx.yaml +++ b/helm-chart/sefaria-project/templates/rollout/nginx.yaml @@ -52,7 +52,7 @@ spec: - name: nginx image: "{{ .Values.nginx.containerImage.imageRegistry }}:{{ .Values.nginx.containerImage.tag }}" imagePullPolicy: Always - command: ["bash -c /entrypoint.sh"] + command: ["bash", "-c", "/entrypoint"] ports: - containerPort: 80 - containerPort: 443 From 3ea83053dfa5848e64629971ebdec7b8aca83fa1 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Mon, 14 Aug 2023 10:09:01 +0300 Subject: [PATCH 122/756] helm: fix entrypoint not running --- helm-chart/sefaria-project/templates/rollout/nginx.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helm-chart/sefaria-project/templates/rollout/nginx.yaml b/helm-chart/sefaria-project/templates/rollout/nginx.yaml index 23f1dba132..9a0c039a7c 100644 --- a/helm-chart/sefaria-project/templates/rollout/nginx.yaml +++ b/helm-chart/sefaria-project/templates/rollout/nginx.yaml @@ -52,7 +52,7 @@ spec: - name: nginx image: "{{ .Values.nginx.containerImage.imageRegistry }}:{{ .Values.nginx.containerImage.tag }}" imagePullPolicy: Always - command: ["bash", "-c", "/entrypoint"] + command: ["bash", "-c", "/entrypoint.sh"] ports: - containerPort: 80 - containerPort: 443 From bae0e27ab4ed9c70aef564c0084e6c2359af5a0e Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Mon, 14 Aug 2023 10:20:15 +0300 Subject: [PATCH 123/756] helm: fix entrypoint not running --- helm-chart/sefaria-project/templates/rollout/nginx.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/helm-chart/sefaria-project/templates/rollout/nginx.yaml b/helm-chart/sefaria-project/templates/rollout/nginx.yaml index 9a0c039a7c..d5aa211321 100644 --- a/helm-chart/sefaria-project/templates/rollout/nginx.yaml +++ b/helm-chart/sefaria-project/templates/rollout/nginx.yaml @@ -52,7 +52,7 @@ spec: - name: nginx image: "{{ .Values.nginx.containerImage.imageRegistry }}:{{ .Values.nginx.containerImage.tag }}" imagePullPolicy: Always - command: ["bash", "-c", "/entrypoint.sh"] + command: ["bash", "-c", "/usr/src/entrypoint.sh"] ports: - containerPort: 80 - containerPort: 443 @@ -74,7 +74,7 @@ spec: name: nginx-conf subPath: nginx.template.conf readOnly: true - - mountPath: /entrypoint.sh + - mountPath: /usr/src/entrypoint.sh name: nginx-conf subPath: entrypoint.sh defaultMode: 0755 From dc24bd60d72a151165e26f693f160a6f95baaf74 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Mon, 14 Aug 2023 10:36:01 +0300 Subject: [PATCH 124/756] helm: fix entrypoint not running --- helm-chart/sefaria-project/templates/rollout/nginx.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helm-chart/sefaria-project/templates/rollout/nginx.yaml b/helm-chart/sefaria-project/templates/rollout/nginx.yaml index d5aa211321..456fc70433 100644 --- a/helm-chart/sefaria-project/templates/rollout/nginx.yaml +++ b/helm-chart/sefaria-project/templates/rollout/nginx.yaml @@ -77,7 +77,7 @@ spec: - mountPath: /usr/src/entrypoint.sh name: nginx-conf subPath: entrypoint.sh - defaultMode: 0755 + defaultMode: 0777 {{- if .Values.instrumentation.enabled }} - mountPath: /etc/nginx/opentracing.json name: nginx-conf From 44cb129fb22845f59529b02de9ef05a1965055c4 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Mon, 14 Aug 2023 11:04:12 +0300 Subject: [PATCH 125/756] helm: fix entrypoint not running --- helm-chart/sefaria-project/templates/rollout/nginx.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helm-chart/sefaria-project/templates/rollout/nginx.yaml b/helm-chart/sefaria-project/templates/rollout/nginx.yaml index 456fc70433..de958d4de1 100644 --- a/helm-chart/sefaria-project/templates/rollout/nginx.yaml +++ b/helm-chart/sefaria-project/templates/rollout/nginx.yaml @@ -77,7 +77,6 @@ spec: - mountPath: /usr/src/entrypoint.sh name: nginx-conf subPath: entrypoint.sh - defaultMode: 0777 {{- if .Values.instrumentation.enabled }} - mountPath: /etc/nginx/opentracing.json name: nginx-conf @@ -115,6 +114,7 @@ spec: - name: nginx-conf configMap: name: nginx-conf-{{ .Values.deployEnv }} + defaultMode: 0755 - name: robots-txt configMap: name: robots-txt-{{ .Values.deployEnv }} From 5d2754351db6d379a12ef1550a22339282797f8a Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Mon, 14 Aug 2023 11:52:09 +0300 Subject: [PATCH 126/756] helm: use envvars located in each deployment for creating SEARCH_ADMIN --- .../templates/configmap/local-settings-file.yaml | 2 +- .../sefaria-project/templates/configmap/local-settings.yaml | 1 - helm-chart/sefaria-project/templates/configmap/nginx.yaml | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml b/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml index a1b0e80d58..b65dca26b0 100644 --- a/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml +++ b/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml @@ -134,7 +134,7 @@ data: } SERVER_EMAIL = os.getenv("SERVER_EMAIL") - SEARCH_ADMIN = os.getenv("SEARCH_ADMIN") + SEARCH_ADMIN: f"http://{os.getenv("ELASTIC_USERNAME")}:{os.getenv("ELASTIC_PASSWORD")}@" + {{ .Values.nginx.SEARCH_HOST | quote }} + ":9200" SEARCH_INDEX_ON_SAVE = True SEARCH_INDEX_NAME_TEXT = 'text' # name of the ElasticSearch index to use SEARCH_INDEX_NAME_SHEET = 'sheet' diff --git a/helm-chart/sefaria-project/templates/configmap/local-settings.yaml b/helm-chart/sefaria-project/templates/configmap/local-settings.yaml index 0ef0080cd7..2d8dd5c030 100644 --- a/helm-chart/sefaria-project/templates/configmap/local-settings.yaml +++ b/helm-chart/sefaria-project/templates/configmap/local-settings.yaml @@ -10,7 +10,6 @@ data: DOMAIN_LANGUAGE: {{ .Values.localSettings.DOMAIN_LANGUAGE | toJson | quote }} MONGO_HOST: {{ .Values.localSettings.MONGO_HOST | quote }} APSCHEDULER_NAME: {{ tpl .Values.localSettings.APSCHEDULER_NAME . | quote }} - SEARCH_ADMIN: "http://{{ .Values.nginx.ELASTIC_USERNAME }}:{{ .Values.nginx.ELASTIC_PASSWORD }}@{{ .Values.nginx.SEARCH_HOST }}:9200" TURN_SERVER: {{ .Values.localSettings.TURN_SERVER | quote }} USE_CLOUDFLARE: "{{ .Values.localSettings.USE_CLOUDFLARE }}" FRONT_END_URL: {{ .Values.localSettings.FRONT_END_URL | quote }} diff --git a/helm-chart/sefaria-project/templates/configmap/nginx.yaml b/helm-chart/sefaria-project/templates/configmap/nginx.yaml index 44808678d4..7c6b82cd66 100644 --- a/helm-chart/sefaria-project/templates/configmap/nginx.yaml +++ b/helm-chart/sefaria-project/templates/configmap/nginx.yaml @@ -34,7 +34,7 @@ data: set -e export ELASTIC_AUTH_HEADER=$(echo -n $ELASTIC_USER:$ELASTIC_PASSWORD | base64) - envsubst '${ENV_NAME},${VARNISH_HOST},${SEARCH_HOST},${ELASTIC_USERNAME},${ELASTIC_PASSWORD},${RELEASE_TAG},${ELASTIC_AUTH_HEADER}{{- if .Values.linker.enabled }},${LINKER_HOST}{{- end }}{{- if .Values.instrumentation.enabled }},${NGINX_VERSION}{{- end }}' < /conf/nginx.template.conf > /nginx.conf + envsubst '${ENV_NAME},${VARNISH_HOST},${SEARCH_HOST},${RELEASE_TAG},${ELASTIC_AUTH_HEADER}{{- if .Values.linker.enabled }},${LINKER_HOST}{{- end }}{{- if .Values.instrumentation.enabled }},${NGINX_VERSION}{{- end }}' < /conf/nginx.template.conf > /nginx.conf nginx -c /nginx.conf -g 'daemon off;' From 95fcee80987f07fbdb4bccc4e7e18f144cbaa45f Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Mon, 14 Aug 2023 13:19:02 +0300 Subject: [PATCH 127/756] helm: fix quotation marks --- .../templates/configmap/local-settings-file.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml b/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml index b65dca26b0..a221c5f95a 100644 --- a/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml +++ b/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml @@ -134,7 +134,7 @@ data: } SERVER_EMAIL = os.getenv("SERVER_EMAIL") - SEARCH_ADMIN: f"http://{os.getenv("ELASTIC_USERNAME")}:{os.getenv("ELASTIC_PASSWORD")}@" + {{ .Values.nginx.SEARCH_HOST | quote }} + ":9200" + SEARCH_ADMIN: f'http://{os.getenv("ELASTIC_USERNAME")}:{os.getenv("ELASTIC_PASSWORD")}@' + {{ .Values.nginx.SEARCH_HOST | quote }} + ":9200" SEARCH_INDEX_ON_SAVE = True SEARCH_INDEX_NAME_TEXT = 'text' # name of the ElasticSearch index to use SEARCH_INDEX_NAME_SHEET = 'sheet' From c0cf8d0536be3026cd458d6ff823edd619cfbb93 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Mon, 14 Aug 2023 13:49:10 +0300 Subject: [PATCH 128/756] helm: fix equals sign --- .../templates/configmap/local-settings-file.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml b/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml index a221c5f95a..2c1901aeb2 100644 --- a/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml +++ b/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml @@ -134,7 +134,7 @@ data: } SERVER_EMAIL = os.getenv("SERVER_EMAIL") - SEARCH_ADMIN: f'http://{os.getenv("ELASTIC_USERNAME")}:{os.getenv("ELASTIC_PASSWORD")}@' + {{ .Values.nginx.SEARCH_HOST | quote }} + ":9200" + SEARCH_ADMIN = f'http://{os.getenv("ELASTIC_USERNAME")}:{os.getenv("ELASTIC_PASSWORD")}@' + {{ .Values.nginx.SEARCH_HOST | quote }} + ":9200" SEARCH_INDEX_ON_SAVE = True SEARCH_INDEX_NAME_TEXT = 'text' # name of the ElasticSearch index to use SEARCH_INDEX_NAME_SHEET = 'sheet' From 0c642e3e54db791bc459e27f090ba9214ca3ee36 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Mon, 14 Aug 2023 15:11:20 +0300 Subject: [PATCH 129/756] chore: downgrade es so es client is compatible with elasticsearch 6 server --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index e10ed629d9..0c7399793b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -22,8 +22,8 @@ django==1.11.* djangorestframework @ https://github.com/encode/django-rest-framework/archive/3.11.1.tar.gz djangorestframework_simplejwt==3.3.0 PyJWT==1.7.1 # pinned b/c current version 2.0.0 breaks simplejwt. waiting for 2.0.1 -elasticsearch==8.8.2 -git+https://github.com/Sefaria/elasticsearch-dsl-py@v8.0.0#egg=elasticsearch-dsl +elasticsearch==7.9.1 +elasticsearch_dsl==7.3.0 geojson==2.5.0 gevent==20.12.0 google-api-python-client==1.12.5 From 76ff1a0cb35bda54e5496451ac9565dfa8f1c3e5 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Mon, 21 Aug 2023 11:34:25 +0300 Subject: [PATCH 130/756] refactor(api errors): rename A|PIError to APIDataError. --- api/api_errors.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/api/api_errors.py b/api/api_errors.py index 65ce8a576e..a1c7a8260f 100644 --- a/api/api_errors.py +++ b/api/api_errors.py @@ -4,7 +4,7 @@ from typing import List -class APIError(): +class APIDataError(): def __init__(self): pass @@ -14,21 +14,21 @@ def get_dict(self) -> dict: 'message': self.message} -class APINoVersion(APIError): +class APINoVersion(APIDataError): def __init__(self, oref: Ref, vtitle: str, lang: str): self.error_code = 101 self.message = f'We do not have version named {vtitle} with language code {lang} for {oref}' -class APINoLanguageVersion(APIError): +class APINoLanguageVersion(APIDataError): def __init__(self, oref: Ref, langs: List[str]): self.error_code = 102 self.message = f'We do not have the code language you asked for {oref}. Available codes are {langs}' -class APINoSourceText(APIError): +class APINoSourceText(APIDataError): def __init__(self, oref: Ref): self.error_code = 103 From f9492e0de0fa39fcacc2b298e19db0f75ffb47be Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Mon, 21 Aug 2023 11:40:30 +0300 Subject: [PATCH 131/756] refactor(api errors): add interim class TextsAPIResponseMessage. --- api/api_errors.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/api/api_errors.py b/api/api_errors.py index a1c7a8260f..3ff1dbedb5 100644 --- a/api/api_errors.py +++ b/api/api_errors.py @@ -3,32 +3,39 @@ from sefaria.model import * from typing import List +""" +classes for data errors in API calls. +used when part of the data that was requested exists and returned, and part is missing. +""" class APIDataError(): def __init__(self): pass + +class TextsAPIResponseMessage(APIDataError): + def get_dict(self) -> dict: return {'error_code': self.error_code, 'message': self.message} -class APINoVersion(APIDataError): +class APINoVersion(TextsAPIResponseMessage): def __init__(self, oref: Ref, vtitle: str, lang: str): self.error_code = 101 self.message = f'We do not have version named {vtitle} with language code {lang} for {oref}' -class APINoLanguageVersion(APIDataError): +class APINoLanguageVersion(TextsAPIResponseMessage): def __init__(self, oref: Ref, langs: List[str]): self.error_code = 102 self.message = f'We do not have the code language you asked for {oref}. Available codes are {langs}' -class APINoSourceText(APIDataError): +class APINoSourceText(TextsAPIResponseMessage): def __init__(self, oref: Ref): self.error_code = 103 From 830143094359632c7bb7f3a975f03a21e27c851e Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Mon, 21 Aug 2023 11:43:09 +0300 Subject: [PATCH 132/756] docs(api errors): doc strings for the parent classes. --- api/api_errors.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/api/api_errors.py b/api/api_errors.py index 3ff1dbedb5..972c5a1ad8 100644 --- a/api/api_errors.py +++ b/api/api_errors.py @@ -9,12 +9,18 @@ """ class APIDataError(): + """ + general class + """ def __init__(self): pass class TextsAPIResponseMessage(APIDataError): + """ + class for returning a message and an error code + """ def get_dict(self) -> dict: return {'error_code': self.error_code, From 260902879e597ec9349811900eb7eb28a39fb4e9 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Mon, 21 Aug 2023 11:52:07 +0300 Subject: [PATCH 133/756] refactor(api errors): rename get_dict to get_message. --- api/api_errors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/api_errors.py b/api/api_errors.py index 972c5a1ad8..dd170d8162 100644 --- a/api/api_errors.py +++ b/api/api_errors.py @@ -22,7 +22,7 @@ class TextsAPIResponseMessage(APIDataError): class for returning a message and an error code """ - def get_dict(self) -> dict: + def get_message(self) -> dict: return {'error_code': self.error_code, 'message': self.message} From 67e86a5ce08cad3c2054b27f6a54bd20bdd04586 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Mon, 21 Aug 2023 11:53:10 +0300 Subject: [PATCH 134/756] fix(api errors): rename get_dict to get_message when called. --- api/texts_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/texts_api.py b/api/texts_api.py index 43b92008b3..81d430aaf6 100644 --- a/api/texts_api.py +++ b/api/texts_api.py @@ -76,7 +76,7 @@ def _handle_errors(self, version_params: VersionsParams) -> None: availabe_langs = {v['actualLanguage'] for v in self.all_versions} error = APINoLanguageVersion(self.oref, sorted(availabe_langs)) self.return_obj['errors'].append({ - version_params.representing_string: error.get_dict() + version_params.representing_string: error.get_message() }) def _append_required_versions(self, version_params: VersionsParams) -> None: From 7c6404702401ba9821c26f2d9ca8785cf96a4313 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Mon, 21 Aug 2023 13:57:42 +0300 Subject: [PATCH 135/756] refactor(api tests): unit test without inheriting SefariaTestCase. --- api/tests.py | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/api/tests.py b/api/tests.py index b04d269ac6..b4dfbc787c 100644 --- a/api/tests.py +++ b/api/tests.py @@ -6,18 +6,17 @@ from api.helper import split_query_param_and_add_defaults -class HelperTests(SefariaTestCase): - def test_split_at_pipe_with_default(self): - for string, list_length, default, expected in [ - ('he|foo bar', 2, [], ['he', 'foo bar']), - ('he|foo bar', 2, ['baz'], ['he', 'foo bar']), - ('he', 2, ['baz'], ['he', 'baz']), - ('he|foo bar|baz', 3, [], ['he', 'foo bar', 'baz']), - ('he|foo bar|baz', 3, ['blue'], ['he', 'foo bar', 'baz']), - ('he|foo bar', 3, ['baz'], ['he', 'foo bar', 'baz']), - ('he', 3, ['foo', 'baz'], ['he', 'foo', 'baz']), - ]: - self.assertEqual(expected, split_query_param_and_add_defaults(string, list_length, default)) +def test_split_at_pipe_with_default(): + for string, list_length, default, expected in [ + ('he|foo bar', 2, [], ['he', 'foo bar']), + ('he|foo bar', 2, ['baz'], ['he', 'foo bar']), + ('he', 2, ['baz'], ['he', 'baz']), + ('he|foo bar|baz', 3, [], ['he', 'foo bar', 'baz']), + ('he|foo bar|baz', 3, ['blue'], ['he', 'foo bar', 'baz']), + ('he|foo bar', 3, ['baz'], ['he', 'foo bar', 'baz']), + ('he', 3, ['foo', 'baz'], ['he', 'foo', 'baz']), + ]: + assert expected == split_query_param_and_add_defaults(string, list_length, default) c = Client() From 719525b313cb2dfed4026243e72766f58ce4acaa Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Mon, 21 Aug 2023 15:22:00 +0300 Subject: [PATCH 136/756] refactor(api tests): create a representing_string when needed rather than in init. --- api/texts_api.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/api/texts_api.py b/api/texts_api.py index 81d430aaf6..5e3899fcfa 100644 --- a/api/texts_api.py +++ b/api/texts_api.py @@ -23,8 +23,6 @@ def __init__(self, lang: str, vtitle: str, representing_string=''): self.lang = lang self.vtitle = vtitle self.representing_string = representing_string - if not self.representing_string: - self.representing_string = f'{self.lang}|{self.representing_string}' def __eq__(self, other): return isinstance(other, VersionsParams) and self.lang == other.lang and self.vtitle == other.vtitle @@ -75,6 +73,7 @@ def _handle_errors(self, version_params: VersionsParams) -> None: else: availabe_langs = {v['actualLanguage'] for v in self.all_versions} error = APINoLanguageVersion(self.oref, sorted(availabe_langs)) + representing_string = version_params.representing_string or f'{version_params.lang}|{version_params.representing_string}' self.return_obj['errors'].append({ version_params.representing_string: error.get_message() }) From 7262985bfab692e55599a2cab9504bd41fbaef25 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Mon, 21 Aug 2023 21:18:28 -0400 Subject: [PATCH 137/756] Fix error handling for POST API request to GraphQL --- static/js/Promotions.jsx | 9 -- static/js/context.js | 233 ++++++++++++++++++++------------------- 2 files changed, 121 insertions(+), 121 deletions(-) diff --git a/static/js/Promotions.jsx b/static/js/Promotions.jsx index be91f75537..aec8d21c36 100644 --- a/static/js/Promotions.jsx +++ b/static/js/Promotions.jsx @@ -88,15 +88,6 @@ const Promotions = () => { } }, [context, inAppAds]); // when state changes, the effect will run - // function getAds() { - // const url = - // 'https://docs.google.com/spreadsheets/d/1UJw2Akyv3lbLqBoZaFVWhaAp-FUQ-YZfhprL_iNhhQc/edit#gid=0' - // const query = new google.visualization.Query(url); - // query.setQuery('select A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q'); - // query.send(processSheetsData); - // - // - // } function showToUser(ad) { if (ad.trigger.showTo === "all") { diff --git a/static/js/context.js b/static/js/context.js index 090ff39ef0..3d53a9a520 100644 --- a/static/js/context.js +++ b/static/js/context.js @@ -20,7 +20,10 @@ const transformValues = (obj, callback) => { }; export function replaceNewLinesWithLinebreaks(content) { - return transformValues(content, s => s.replace(/\n/gi, "  \n") + "  \n"); + return transformValues( + content, + (s) => s.replace(/\n/gi, "  \n") + "  \n" + ); } function StrapiDataProvider({ children }) { @@ -32,20 +35,21 @@ function StrapiDataProvider({ children }) { useEffect(() => { if (STRAPI_INSTANCE) { const getStrapiData = async () => { - try { - let getDateWithoutTime = (date) => date.toISOString().split("T")[0]; - let getJSONDateStringInLocalTimeZone = (date) => { - let parts = getDateWithoutTime(date).split("-"); - return new Date(parts[0], parts[1] - 1, parts[2]).toJSON(); - }; - let currentDate = new Date(); - let twoWeeksAgo = new Date(); - let twoWeeksFromNow = new Date(); - twoWeeksFromNow.setDate(currentDate.getDate() + 14); - twoWeeksAgo.setDate(currentDate.getDate() - 14); - let startDate = getJSONDateStringInLocalTimeZone(twoWeeksAgo); - let endDate = getJSONDateStringInLocalTimeZone(twoWeeksFromNow); - const query = ` + let getDateWithoutTime = (date) => date.toISOString().split("T")[0]; + let getJSONDateStringInLocalTimeZone = (date) => { + let parts = getDateWithoutTime(date).split("-"); + return new Date(parts[0], parts[1] - 1, parts[2]).toJSON(); + }; + let [currentDate, twoWeeksAgo, twoWeeksFromNow] = Array(3) + .fill() + .map(() => { + return new Date(); + }); + twoWeeksFromNow.setDate(currentDate.getDate() + 14); + twoWeeksAgo.setDate(currentDate.getDate() - 14); + let startDate = getJSONDateStringInLocalTimeZone(twoWeeksAgo); + let endDate = getJSONDateStringInLocalTimeZone(twoWeeksFromNow); + const query = ` query { banners( filters: { @@ -172,114 +176,119 @@ function StrapiDataProvider({ children }) { } } `; - const result = fetch(STRAPI_INSTANCE + "/graphql", { - method: "POST", // *GET, POST, PUT, DELETE, etc. - mode: "cors", // no-cors, *cors, same-origin - cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached - credentials: "same-origin", - headers: { - "Content-Type": "application/json", - }, - redirect: "follow", - referrerPolicy: "no-referrer", - body: JSON.stringify({ query }), + const result = fetch(STRAPI_INSTANCE + "/graphql", { + method: "POST", + mode: "cors", + cache: "no-cache", + credentials: "omit", + headers: { + "Content-Type": "application/json", + }, + redirect: "follow", + referrerPolicy: "no-referrer", + body: JSON.stringify({ query }), + }) + .then((response) => { + if (!response.ok) { + throw new Error(`HTTP Error: ${response.statusText}`); + } + return response.json(); }) - .then((response) => response.json()) - .then((result) => { - setStrapiData(result.data); - setDataFromStrapiHasBeenReceived(true); - // maybe sort by start date to choose which one should have a greater priority if more than one compatible one exists - // e.g. there are modals with overlapping time frames - let modals = result.data?.modals?.data; - console.log(modals); - let banners = result.data?.banners?.data; - console.log(banners); + .then((result) => { + setStrapiData(result.data); + setDataFromStrapiHasBeenReceived(true); + // maybe sort by start date to choose which one should have a greater priority if more than one compatible one exists + // e.g. there are modals with overlapping time frames + let modals = result.data?.modals?.data; + console.log(modals); + let banners = result.data?.banners?.data; + console.log(banners); - const currentDate = new Date(); - if (modals?.length) { - // if they end up being sorted, the first one will be the compatible one - let modal = modals.find( - (modal) => - currentDate >= new Date(modal.attributes.modalStartDate) && - currentDate <= new Date(modal.attributes.modalEndDate) - ); - console.log("found acceptable modal:"); - console.log(modal); - if (modal) { - console.log("setting the modal"); - if (modal.attributes.localizations?.data?.length) { - let localization_attributes = - modal.attributes.localizations.data[0].attributes; - let { locale, ...hebrew_attributes } = - localization_attributes; - Object.keys(hebrew_attributes).forEach((attribute) => { - modal.attributes[attribute] = { - en: modal.attributes[attribute], - he: hebrew_attributes[attribute], - }; - }); - modal.attributes.locales = ["en", "he"]; - } else { - [ - "modalHeader", - "modalText", - "buttonText", - "buttonURL", - ].forEach((attribute) => { - modal.attributes[attribute] = { - en: modal.attributes[attribute], - he: null, - }; - }); - modal.attributes.locales = ["en"]; - } - setModal(modal.attributes); + const currentDate = new Date(); + if (modals?.length) { + // if they end up being sorted, the first one will be the compatible one + let modal = modals.find( + (modal) => + currentDate >= new Date(modal.attributes.modalStartDate) && + currentDate <= new Date(modal.attributes.modalEndDate) + ); + console.log("found acceptable modal:"); + console.log(modal); + if (modal) { + console.log("setting the modal"); + if (modal.attributes.localizations?.data?.length) { + let localization_attributes = + modal.attributes.localizations.data[0].attributes; + let { locale, ...hebrew_attributes } = + localization_attributes; + Object.keys(hebrew_attributes).forEach((attribute) => { + modal.attributes[attribute] = { + en: modal.attributes[attribute], + he: hebrew_attributes[attribute], + }; + }); + modal.attributes.locales = ["en", "he"]; + } else { + [ + "modalHeader", + "modalText", + "buttonText", + "buttonURL", + ].forEach((attribute) => { + modal.attributes[attribute] = { + en: modal.attributes[attribute], + he: null, + }; + }); + modal.attributes.locales = ["en"]; } + setModal(modal.attributes); } + } - if (banners?.length) { - let banner = banners.find( - (b) => - currentDate >= new Date(b.attributes.bannerStartDate) && - currentDate <= new Date(b.attributes.bannerEndDate) - ); + if (banners?.length) { + let banner = banners.find( + (b) => + currentDate >= new Date(b.attributes.bannerStartDate) && + currentDate <= new Date(b.attributes.bannerEndDate) + ); - console.log("found acceptable banner:"); - console.log(banner); - if (banner) { - console.log("setting the banner"); - if (banner.attributes.localizations?.data?.length) { - let localization_attributes = - banner.attributes.localizations.data[0].attributes; - let { locale, ...hebrew_attributes } = - localization_attributes; - Object.keys(hebrew_attributes).forEach((attribute) => { + console.log("found acceptable banner:"); + console.log(banner); + if (banner) { + console.log("setting the banner"); + if (banner.attributes.localizations?.data?.length) { + let localization_attributes = + banner.attributes.localizations.data[0].attributes; + let { locale, ...hebrew_attributes } = + localization_attributes; + Object.keys(hebrew_attributes).forEach((attribute) => { + banner.attributes[attribute] = { + en: banner.attributes[attribute], + he: hebrew_attributes[attribute], + }; + }); + banner.attributes.locales = ["en", "he"]; + } else { + // Maybe have the GraphQL return null entries for each key so the same technique can be used from above? + ["bannerText", "buttonText", "buttonURL"].forEach( + (attribute) => { banner.attributes[attribute] = { en: banner.attributes[attribute], - he: hebrew_attributes[attribute], + he: null, }; - }); - banner.attributes.locales = ["en", "he"]; - } else { - // Maybe have the GraphQL return null entries for each key so the same technique can be used from above? - ["bannerText", "buttonText", "buttonURL"].forEach( - (attribute) => { - banner.attributes[attribute] = { - en: banner.attributes[attribute], - he: null, - }; - } - ); - banner.attributes.locales = ["en"]; - } - setBanner(banner.attributes); - console.log(banner.attributes); + } + ); + banner.attributes.locales = ["en"]; } + setBanner(banner.attributes); + console.log(banner.attributes); } - }); - } catch (error) { - console.error("Failed to get strapi data", error); - } + } + }) + .catch((error) => { + console.error("Failed to get strapi data: ", error); + }); }; getStrapiData(); } From af4a9810f76d4f8f89710f9e2f52531cf1bac2db Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Tue, 22 Aug 2023 11:26:05 +0300 Subject: [PATCH 138/756] refactor(api tests): use enums for error codes. --- api/api_errors.py | 12 +++++++++--- api/tests.py | 7 ++++--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/api/api_errors.py b/api/api_errors.py index dd170d8162..a7ed763b6b 100644 --- a/api/api_errors.py +++ b/api/api_errors.py @@ -2,6 +2,12 @@ django.setup() from sefaria.model import * from typing import List +from enum import Enum + +class APIWarningCode(Enum): + APINoVersion = 101 + APINoLanguageVersion = 102 + APINoSourceText = 103 """ classes for data errors in API calls. @@ -30,19 +36,19 @@ def get_message(self) -> dict: class APINoVersion(TextsAPIResponseMessage): def __init__(self, oref: Ref, vtitle: str, lang: str): - self.error_code = 101 + self.error_code = APIWarningCode.APINoVersion.value self.message = f'We do not have version named {vtitle} with language code {lang} for {oref}' class APINoLanguageVersion(TextsAPIResponseMessage): def __init__(self, oref: Ref, langs: List[str]): - self.error_code = 102 + self.error_code = APIWarningCode.APINoLanguageVersion.value self.message = f'We do not have the code language you asked for {oref}. Available codes are {langs}' class APINoSourceText(TextsAPIResponseMessage): def __init__(self, oref: Ref): - self.error_code = 103 + self.error_code = APIWarningCode.APINoSourceText.value self.message = f'We do not have the source text for {oref}' diff --git a/api/tests.py b/api/tests.py index b4dfbc787c..9887e7017d 100644 --- a/api/tests.py +++ b/api/tests.py @@ -4,6 +4,7 @@ from reader.tests import SefariaTestCase import json from api.helper import split_query_param_and_add_defaults +from api.api_errors import APIWarningCode def test_split_at_pipe_with_default(): @@ -124,7 +125,7 @@ def test_api_get_text_no_source(self): self.assertEqual(200, response.status_code) data = json.loads(response.content) self.assertEqual(len(data["versions"]), 1) - self.assertEqual(data['errors'][0]['source']['error_code'], 103) + self.assertEqual(data['errors'][0]['source']['error_code'], APIWarningCode.APINoSourceText.value) self.assertEqual(data['errors'][0]['source']['message'], 'We do not have the source text for The Book of Maccabees I 1') def test_api_get_text_no_language(self): @@ -132,7 +133,7 @@ def test_api_get_text_no_language(self): self.assertEqual(200, response.status_code) data = json.loads(response.content) self.assertEqual(len(data["versions"]), 1) - self.assertEqual(data['errors'][0]['sgrg|all']['error_code'], 102) + self.assertEqual(data['errors'][0]['sgrg|all']['error_code'], APIWarningCode.APINoLanguageVersion.value) self.assertEqual(data['errors'][0]['sgrg|all']['message'], "We do not have the code language you asked for The Book of Maccabees I 1. Available codes are ['en', 'he']") @@ -141,6 +142,6 @@ def test_api_get_text_no_version(self): self.assertEqual(200, response.status_code) data = json.loads(response.content) self.assertEqual(len(data["versions"]), 1) - self.assertEqual(data['errors'][0]['he|Kishkoosh']['error_code'], 101) + self.assertEqual(data['errors'][0]['he|Kishkoosh']['error_code'], APIWarningCode.APINoVersion.value) self.assertEqual(data['errors'][0]['he|Kishkoosh']['message'], 'We do not have version named Kishkoosh with language code he for The Book of Maccabees I 1') From 9e0739f9802bd8ec15aec44b3a90cbe49667f6c0 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Tue, 22 Aug 2023 11:33:20 +0300 Subject: [PATCH 139/756] refactor(api tests): change errors to warnings. --- api/{api_errors.py => api_warnings.py} | 16 ++++++++-------- api/tests.py | 14 +++++++------- api/texts_api.py | 20 ++++++++++---------- 3 files changed, 25 insertions(+), 25 deletions(-) rename api/{api_errors.py => api_warnings.py} (71%) diff --git a/api/api_errors.py b/api/api_warnings.py similarity index 71% rename from api/api_errors.py rename to api/api_warnings.py index a7ed763b6b..40cb37b1cc 100644 --- a/api/api_errors.py +++ b/api/api_warnings.py @@ -10,11 +10,11 @@ class APIWarningCode(Enum): APINoSourceText = 103 """ -classes for data errors in API calls. +classes for data warnings in API calls. used when part of the data that was requested exists and returned, and part is missing. """ -class APIDataError(): +class APIDatawarning(): """ general class """ @@ -23,32 +23,32 @@ def __init__(self): pass -class TextsAPIResponseMessage(APIDataError): +class TextsAPIResponseMessage(APIDatawarning): """ - class for returning a message and an error code + class for returning a message and an warning code """ def get_message(self) -> dict: - return {'error_code': self.error_code, + return {'warning_code': self.warning_code, 'message': self.message} class APINoVersion(TextsAPIResponseMessage): def __init__(self, oref: Ref, vtitle: str, lang: str): - self.error_code = APIWarningCode.APINoVersion.value + self.warning_code = APIWarningCode.APINoVersion.value self.message = f'We do not have version named {vtitle} with language code {lang} for {oref}' class APINoLanguageVersion(TextsAPIResponseMessage): def __init__(self, oref: Ref, langs: List[str]): - self.error_code = APIWarningCode.APINoLanguageVersion.value + self.warning_code = APIWarningCode.APINoLanguageVersion.value self.message = f'We do not have the code language you asked for {oref}. Available codes are {langs}' class APINoSourceText(TextsAPIResponseMessage): def __init__(self, oref: Ref): - self.error_code = APIWarningCode.APINoSourceText.value + self.warning_code = APIWarningCode.APINoSourceText.value self.message = f'We do not have the source text for {oref}' diff --git a/api/tests.py b/api/tests.py index 9887e7017d..4eb6da02cc 100644 --- a/api/tests.py +++ b/api/tests.py @@ -4,7 +4,7 @@ from reader.tests import SefariaTestCase import json from api.helper import split_query_param_and_add_defaults -from api.api_errors import APIWarningCode +from api.api_warnings import APIWarningCode def test_split_at_pipe_with_default(): @@ -125,16 +125,16 @@ def test_api_get_text_no_source(self): self.assertEqual(200, response.status_code) data = json.loads(response.content) self.assertEqual(len(data["versions"]), 1) - self.assertEqual(data['errors'][0]['source']['error_code'], APIWarningCode.APINoSourceText.value) - self.assertEqual(data['errors'][0]['source']['message'], 'We do not have the source text for The Book of Maccabees I 1') + self.assertEqual(data['warnings'][0]['source']['warning_code'], APIWarningCode.APINoSourceText.value) + self.assertEqual(data['warnings'][0]['source']['message'], 'We do not have the source text for The Book of Maccabees I 1') def test_api_get_text_no_language(self): response = c.get("/api/v3/texts/The_Book_of_Maccabees_I.1?version=en|Brenton's_Septuagint&version=sgrg|all") self.assertEqual(200, response.status_code) data = json.loads(response.content) self.assertEqual(len(data["versions"]), 1) - self.assertEqual(data['errors'][0]['sgrg|all']['error_code'], APIWarningCode.APINoLanguageVersion.value) - self.assertEqual(data['errors'][0]['sgrg|all']['message'], + self.assertEqual(data['warnings'][0]['sgrg|all']['warning_code'], APIWarningCode.APINoLanguageVersion.value) + self.assertEqual(data['warnings'][0]['sgrg|all']['message'], "We do not have the code language you asked for The Book of Maccabees I 1. Available codes are ['en', 'he']") def test_api_get_text_no_version(self): @@ -142,6 +142,6 @@ def test_api_get_text_no_version(self): self.assertEqual(200, response.status_code) data = json.loads(response.content) self.assertEqual(len(data["versions"]), 1) - self.assertEqual(data['errors'][0]['he|Kishkoosh']['error_code'], APIWarningCode.APINoVersion.value) - self.assertEqual(data['errors'][0]['he|Kishkoosh']['message'], + self.assertEqual(data['warnings'][0]['he|Kishkoosh']['warning_code'], APIWarningCode.APINoVersion.value) + self.assertEqual(data['warnings'][0]['he|Kishkoosh']['message'], 'We do not have version named Kishkoosh with language code he for The Book of Maccabees I 1') diff --git a/api/texts_api.py b/api/texts_api.py index 5e3899fcfa..6fa516decf 100644 --- a/api/texts_api.py +++ b/api/texts_api.py @@ -1,7 +1,7 @@ import django django.setup() from sefaria.utils.hebrew import hebrew_term -from .api_errors import * +from .api_warnings import * from .helper import split_query_param_and_add_defaults from typing import List @@ -50,7 +50,7 @@ class TextsForClientHandler(): return_obj is dict that includes in its root: ref and index data 'versions' - list of versions details and text - 'errors' - for any version_params that had an error + 'warning' - for any version_params that has an warning """ ALL = 'all' @@ -62,20 +62,20 @@ def __init__(self, oref: Ref, versions_params: List[VersionsParams]): self.oref = oref self.handled_version_params = [] self.all_versions = self.oref.version_list() - self.return_obj = {'versions': [], 'errors': []} + self.return_obj = {'versions': [], 'warnings': []} - def _handle_errors(self, version_params: VersionsParams) -> None: + def _handle_warnings(self, version_params: VersionsParams) -> None: lang, vtitle = version_params.lang, version_params.vtitle if lang == self.SOURCE: - error = APINoSourceText(self.oref) + warning = APINoSourceText(self.oref) elif vtitle and vtitle != self.ALL: - error = APINoVersion(self.oref, vtitle, lang) + warning = APINoVersion(self.oref, vtitle, lang) else: availabe_langs = {v['actualLanguage'] for v in self.all_versions} - error = APINoLanguageVersion(self.oref, sorted(availabe_langs)) + warning = APINoLanguageVersion(self.oref, sorted(availabe_langs)) representing_string = version_params.representing_string or f'{version_params.lang}|{version_params.representing_string}' - self.return_obj['errors'].append({ - version_params.representing_string: error.get_message() + self.return_obj['warnings'].append({ + version_params.representing_string: warning.get_message() }) def _append_required_versions(self, version_params: VersionsParams) -> None: @@ -98,7 +98,7 @@ def _append_required_versions(self, version_params: VersionsParams) -> None: if version not in self.return_obj['versions']: #do not return the same version even if included in two different version params self.return_obj['versions'].append(version) if not versions: - self._handle_errors(version_params) + self._handle_warnings(version_params) def _add_text_to_versions(self) -> None: for version in self.return_obj['versions']: From 769f2f619fb29a44ade6f091b8d46dd0d0ac2fc2 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Tue, 22 Aug 2023 11:54:26 +0300 Subject: [PATCH 140/756] refactor(api tests): remove callback from jsonResponse calls. --- api/views.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/api/views.py b/api/views.py index fa12e610d5..66c0542208 100644 --- a/api/views.py +++ b/api/views.py @@ -17,7 +17,6 @@ def get_texts(request, tref): return jsonResponse({'error': getattr(e, 'message', str(e))}, status=400) if oref.is_empty(): return jsonResponse({'error': f'We have no text for {oref}.'}, status=400) - cb = request.GET.get("callback", None) if request.method == "GET": versions_params = request.GET.getlist('version', []) if not versions_params: @@ -25,5 +24,5 @@ def get_texts(request, tref): versions_params = VersionsParams.parse_api_params(versions_params) handler = TextsForClientHandler(oref, versions_params) data = handler.get_versions_for_query() - return jsonResponse(data, cb) - return jsonResponse({"error": "Unsupported HTTP method."}, cb, status=405) + return jsonResponse(data) + return jsonResponse({"error": "Unsupported HTTP method."}, status=405) From af8436e62102d4ca7c24dcd5e41b9a7b8768d417 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Tue, 22 Aug 2023 15:55:38 +0300 Subject: [PATCH 141/756] feat(Topic Editor): added birth + death + alttitles for authors --- sefaria/helper/descriptions.py | 1 + static/css/s2.css | 18 ++--- static/js/AdminEditor.jsx | 129 +++++++++++++++++++++++---------- static/js/Misc.jsx | 16 +++- static/js/TopicEditor.jsx | 20 ++++- 5 files changed, 131 insertions(+), 53 deletions(-) diff --git a/sefaria/helper/descriptions.py b/sefaria/helper/descriptions.py index 6795236453..3ee6946a78 100644 --- a/sefaria/helper/descriptions.py +++ b/sefaria/helper/descriptions.py @@ -41,6 +41,7 @@ def update_authors_data(): "Achronim": "AH", "Contemporary": "CO" } + era_slug_map = { "GN": "geon-person", "RI": "rishon-person", diff --git a/static/css/s2.css b/static/css/s2.css index 25773f7575..ba91f8b8ae 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -11208,7 +11208,7 @@ body .homeFeedWrapper.userStats { float: right; margin-right: 30px; } -#categoryChooserMenu { +.categoryChooserMenu { overflow: hidden; background: url("/static/img/arrow-down.png") 98% 20px/10px 10px no-repeat #ffffff; width: 100%; @@ -11239,7 +11239,7 @@ body .homeFeedWrapper.userStats { display: inline-block; } -#categoryChooserMenu select { +.categoryChooserMenu select { background: transparent; font-size: 16px; width: 680px; @@ -11248,14 +11248,14 @@ body .homeFeedWrapper.userStats { padding: 4px 25px 4px 10px; } -.connectionsPanel .editTextInfo #categoryChooserMenu { +.connectionsPanel .editTextInfo .categoryChooserMenu { width: 102%; } .connectionsPanel .editTextInfo .collectionsWidget { width: 70%; } -.connectionsPanel .editTextInfo #categoryChooserMenu select { +.connectionsPanel .editTextInfo .categoryChooserMenu select { width: 98.5%; } @@ -11266,11 +11266,11 @@ body .homeFeedWrapper.userStats { .searchBox .editTextInfo #newIndex { margin: 50px auto; } -.searchBox #categoryChooserMenu { +.searchBox .categoryChooserMenu { width: 97%; background: url("/static/img/arrow-down.png") 99% 20px/10px 10px no-repeat #ffffff; } -.searchBox #categoryChooserMenu select { +.searchBox .categoryChooserMenu select { width: 100%; } .searchBox .editTextInfo #newIndex input { @@ -11279,7 +11279,7 @@ body .homeFeedWrapper.userStats { .searchBox .editTextInfo #newIndex #topicDesc { width: 92%; } -#categoryChooserMenu img { +.categoryChooserMenu img { opacity: 0.43; padding: 0 5px; height: 10px; @@ -11363,14 +11363,14 @@ body .homeFeedWrapper.userStats { } @media screen and (max-width: 680px) { - #categoryChooserMenu { + .categoryChooserMenu { background: url("/static/img/arrow-down.png") 300px 20px/10px 10px no-repeat #ffffff; width: 320px; } .editTextInfo .static .headerWithButtons h1 { margin: 30px; } - #categoryChooserMenu select { + .categoryChooserMenu select { width: 340px; } .editTextInfo #newIndex input { diff --git a/static/js/AdminEditor.jsx b/static/js/AdminEditor.jsx index 0658c862cf..964f5c65b7 100644 --- a/static/js/AdminEditor.jsx +++ b/static/js/AdminEditor.jsx @@ -1,9 +1,58 @@ import React, {useRef, useState} from "react"; import Sefaria from "./sefaria/sefaria"; -import {AdminToolHeader, InterfaceText} from "./Misc"; +import {AdminToolHeader, InterfaceText, TitleVariants} from "./Misc"; import sanitizeHtml from 'sanitize-html'; import classNames from "classnames"; - +const options_for_form = { + "Title": {label: "Title", field: "enTitle", placeholder: "Add a title."}, + "Hebrew Title": {label: "Hebrew Title", field: "heTitle", placeholder: "Add a title."}, + "English Description": { + label: "English Description", + field: "enDescription", + placeholder: "Add a description.", + type: 'textarea' + }, + "Hebrew Description": { + label: "Hebrew Description", + field: "heDescription", + placeholder: "Add a description.", + type: 'textarea' + }, + "Prompt": {label: "Prompt", field: "prompt", placeholder: "Add a prompt.", textarea: true}, + "English Short Description": { + label: "English Short Description for Table of Contents", field: "enCategoryDescription", + placeholder: "Add a short description.", type: 'input' + }, + "Hebrew Short Description": { + label: "Hebrew Short Description for Table of Contents", field: "heCategoryDescription", + placeholder: "Add a short description.", type: 'input' + }, + "English Alternate Titles": { + label: "English Alternate Titles", field: "enAltTitles", + placeholder: "Add a title.", type: 'title variants' + }, + "Hebrew Alternate Titles": { + label: "Hebrew Alternate Titles", field: "heAltTitles", + placeholder: "Add a title.", type: 'title variants' + }, + "Birth Place": { + label: "Place of Birth", field: "birthPlace", placeholder: "Place of birth", type: 'input' + }, + "Death Place": { + label: "Place of Death", field: "deathPlace", placeholder: "Place of death", type: 'input' + }, + "Birth Year": { + label: "Year of Birth", field: "birthYear", placeholder: "Year of birth", type: 'input' + }, + "Death Year": { + label: "Year of Death", field: "deathYear", placeholder: "Year of death", type: 'input' + }, + "Era": { + label: "Era (GN/Gaonim, RI/Rishonim, AH/Achronim, CO/Contemporary)", field: "era", placeholder: "Choose an era", type: 'dropdown', + dropdown_data: ["GN", "RI", "AH", "CO"] + } +} + const AdminEditorButton = ({toggleAddingTopics, text, top=false, bottom=false}) => { const classes = classNames({button: 1, extraSmall: 1, topic: 1, top, bottom}); return <div onClick={toggleAddingTopics} @@ -70,6 +119,10 @@ const AdminEditor = ({title, data, close, catMenu, updateData, savingStatus, } updateData({...data}); } + const handleTitleVariants = (newTitles, field) => { + data[field] = newTitles; + updateData({...data}); + } const preprocess = async () => { setValidatingLinks(true); for (const x of items) { @@ -85,44 +138,44 @@ const AdminEditor = ({title, data, close, catMenu, updateData, savingStatus, validate(); setValidatingLinks(false); } - const item = ({label, field, placeholder, is_textarea}) => { - return <div className="section"> - <label><InterfaceText>{label}</InterfaceText></label> - {is_textarea ? - // <MDEditor textareaProps={{id: field, placeholder: Sefaria._(placeholder)}} - // commands={[commands.bold, commands.italic, commands.link]} - // value={data[field]} onChange={setTextareaValue}/> - <textarea className="default" id={field} onBlur={setInputValue} defaultValue={data[field]} - placeholder={Sefaria._(placeholder)}/> - : <input type='text' id={field} onBlur={setInputValue} defaultValue={data[field]} - placeholder={Sefaria._(placeholder)}/>} - </div>; + const getDropdown = (field, dropdown_data, placeholder) => { + const chooseCat = <option key="chooseCategory" value={data.origEra} + selected={!dropdown_data.includes(data[field])}>{placeholder}</option>; + return <div id={`dropdown_${field}`} className="categoryChooserMenu"> + <select key={field} id={field} onChange={setInputValue}> + {chooseCat} + {dropdown_data.map(x => + <option key={`${field}_${x}`} + value={x} + selected={data[field] === x}>{x}</option>)} + </select> + </div>; } - const options_for_form = { - "Title": {label: "Title", field: "enTitle", placeholder: "Add a title."}, - "Hebrew Title": {label: "Hebrew Title", field: "heTitle", placeholder: "Add a title."}, - "English Description": { - label: "English Description", - field: "enDescription", - placeholder: "Add a description.", - is_textarea: true - }, - "Hebrew Description": { - label: "Hebrew Description", - field: "heDescription", - placeholder: "Add a description.", - is_textarea: true - }, - "Prompt": {label: "Prompt", field: "prompt", placeholder: "Add a prompt.", textarea: true}, - "English Short Description": { - label: "English Short Description for Table of Contents", field: "enCategoryDescription", - placeholder: "Add a short description.", is_textarea: false - }, - "Hebrew Short Description": { - label: "Hebrew Short Description for Table of Contents", field: "heCategoryDescription", - placeholder: "Add a short description.", is_textarea: false - }, + const item = ({label, field, placeholder, type, dropdown_data}) => { + let obj; + switch(type) { + case 'dropdown': + obj = getDropdown(field, dropdown_data, placeholder); + break; + case 'title variants': + const titles = data[field]; + obj = <TitleVariants update={(newTitles) => handleTitleVariants(newTitles, field)} titles={titles} id={field}/>; + break; + case 'textarea': + obj = <textarea className="default" id={field} onBlur={setInputValue} defaultValue={data[field]} + placeholder={Sefaria._(placeholder)}/>; + break; + default: + obj = <input type='text' id={field} onBlur={setInputValue} defaultValue={data[field]} + placeholder={Sefaria._(placeholder)}/>; + } + + return <div className="section"> + <label><InterfaceText>{label}</InterfaceText></label> + {obj} + </div>; } + return <div className="editTextInfo"> <div className="static"> <div className="inner"> diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 5d083ad7d6..0cf27654e3 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -1193,6 +1193,13 @@ const EditorForExistingTopic = ({ toggle, data }) => { origHe: data.primaryTitle.he || "", origDesc: data.description || {"en": "", "he": ""}, origCategoryDesc: data.categoryDescription || {"en": "", "he": ""}, + origEnAltTitles: data.titles.filter(x => !x.primary && x.lang === 'en').map(x => x.text), + origHeAltTitles: data.titles.filter(x => !x.primary && x.lang === 'he').map(x => x.text), + origBirthPlace: data?.properties?.birthPlace.value, + origBirthYear: data?.properties?.birthYear.value, + origDeathPlace: data?.properties?.deathPlace.value, + origDeathYear: data?.properties?.deathYear.value, + origEra: data?.properties?.era.value }; const origWasCat = "displays-above" in data?.links; @@ -2684,7 +2691,7 @@ const CategoryChooser = function({categories, update}) { } return <div ref={categoryMenu}> {menus.map((menu, index) => - <div id="categoryChooserMenu"> + <div className="categoryChooserMenu"> <select key={`subcats-${index}`} id={`subcats-${index}`} onChange={handleChange}> <option key="chooseCategory" value="Choose a category">Table of Contents</option> {menu} @@ -2696,15 +2703,20 @@ const CategoryChooser = function({categories, update}) { const TitleVariants = function({titles, update, options}) { /* - Wrapper for ReactTags component. `titles` is initial list of strings to populate ReactTags component + Wrapper for ReactTags component. `titles` is initial list of objects to populate ReactTags component. + each item in `titles` should have an 'id' and 'name' field and can have others as well and `update` is method to call after deleting or adding to titles. `options` is an object that can have the fields `onTitleDelete`, `onTitleAddition`, and `onTitleValidate` allowing overloading of TitleVariant's methods */ + if (titles.length > 0 && typeof titles[0] === 'string') { // normalize titles + titles = titles.map((item, i) => ({["name"]: item, ["id"]: i})); + } const onTitleDelete = function(i) { const newTitles = titles.filter(t => t !== titles[i]); update(newTitles); } const onTitleAddition = function(title) { + title.id = Math.max(titles.map(x => x.id)) + 1; // assign unique id const newTitles = [].concat(titles, title); update(newTitles); } diff --git a/static/js/TopicEditor.jsx b/static/js/TopicEditor.jsx index 121934f7b7..c2e734b9a3 100644 --- a/static/js/TopicEditor.jsx +++ b/static/js/TopicEditor.jsx @@ -1,5 +1,5 @@ import Sefaria from "./sefaria/sefaria"; -import {InterfaceText, requestWithCallBack, ToggleSet} from "./Misc"; +import {InterfaceText, requestWithCallBack, TitleVariants, ToggleSet} from "./Misc"; import $ from "./sefaria/sefariaJquery"; import {AdminEditor} from "./AdminEditor"; import {Reorder} from "./CategoryEditor"; @@ -12,9 +12,13 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { enDescription: origData?.origDesc?.en || "", enCategoryDescription: origData?.origCategoryDesc?.en, heCategoryDescription: origData?.origCategoryDesc?.he, + enAltTitles: origData.origEnAltTitles, heAltTitles: origData.origHeAltTitles, + birthPlace: origData.origBirthPlace, birthYear: origData.origBirthYear, + deathYear: origData.origDeathYear, era: origData.origEra, deathPlace: origData.origDeathPlace }); const isNew = !('origSlug' in origData); const [savingStatus, setSavingStatus] = useState(false); + const [isAuthor, setIsAuthor] = useState(origData.origCategorySlug === 'authors'); const [isCategory, setIsCategory] = useState(origWasCat); // initialize to True if the topic originally was a category // isCategory determines whether user can edit categoryDescriptions of topic const subtopics = Sefaria.topicTocPage(origData.origSlug); @@ -33,6 +37,7 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { //logic is: if it starts out originally a category, isCategory should always be true, otherwise, it should depend solely on 'Main Menu' const newIsCategory = origWasCat || e.target.value === Sefaria._("Main Menu"); setIsCategory(newIsCategory); + setIsAuthor(data.catSlug === 'authors'); setData(data); } @@ -44,7 +49,7 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { slugsToTitles = Object.assign(specialCases, slugsToTitles); const [catMenu, setCatMenu] = useState(<div className="section"> <label><InterfaceText>Parent Topic</InterfaceText></label> - <div id="categoryChooserMenu"> + <div className="categoryChooserMenu"> <select key="topicCats" id="topicCats" onChange={handleCatChange}> {Object.keys(slugsToTitles).map(function (tempSlug, i) { const tempTitle = Sefaria.interfaceLang === 'english' ? slugsToTitles[tempSlug].en : slugsToTitles[tempSlug].he; @@ -122,20 +127,27 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { requestWithCallBack({url, type: "DELETE", redirect: () => window.location.href = "/topics"}); } - let items = ["Title", "Hebrew Title", "Category Menu", "English Description", "Hebrew Description"]; + let items = ["Title", "Hebrew Title", "English Alternate Titles", "Hebrew Alternate Titles", "Category Menu", "English Description", "Hebrew Description"]; if (isCategory) { items.push("English Short Description"); items.push("Hebrew Short Description"); } + if (isAuthor) { + ["Birth Place", "Birth Year", "Death Place", "Death Year"].forEach(x => items.push(x)); + if (Sefaria._siteSettings.TORAH_SPECIFIC) { + items.push("Era"); + } + } return <AdminEditor title="Topic Editor" close={close} catMenu={catMenu} data={data} savingStatus={savingStatus} validate={validate} deleteObj={deleteObj} updateData={updateData} isNew={isNew} items={items} extras={ [isNew ? null : <Reorder subcategoriesAndBooks={sortedSubtopics} updateOrder={setSortedSubtopics} - displayType="topics"/>, + displayType="topics"/> ] } />; } + export {TopicEditor}; \ No newline at end of file From db2f8eee67ee4d2f0646ce0fa7f45a8d35bac40f Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Wed, 23 Aug 2023 07:20:12 +0300 Subject: [PATCH 142/756] refactor(api tests): view class rather than function. --- api/texts_api.py | 2 +- api/views.py | 31 ++++++++++++++----------------- sefaria/urls.py | 2 +- 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/api/texts_api.py b/api/texts_api.py index 6fa516decf..2ee0588b29 100644 --- a/api/texts_api.py +++ b/api/texts_api.py @@ -75,7 +75,7 @@ def _handle_warnings(self, version_params: VersionsParams) -> None: warning = APINoLanguageVersion(self.oref, sorted(availabe_langs)) representing_string = version_params.representing_string or f'{version_params.lang}|{version_params.representing_string}' self.return_obj['warnings'].append({ - version_params.representing_string: warning.get_message() + representing_string: warning.get_message() }) def _append_required_versions(self, version_params: VersionsParams) -> None: diff --git a/api/views.py b/api/views.py index 66c0542208..fa55f9f615 100644 --- a/api/views.py +++ b/api/views.py @@ -1,28 +1,25 @@ from sefaria.model import * from .texts_api import TextsForClientHandler, VersionsParams from sefaria.client.util import jsonResponse +from django.views import View -def get_texts(request, tref): - """ - handle text request based on ref and query params - status codes: - 400 - for invalid ref or empty ref (i.e. has no text in any language) - 405 - unsuppored method - 200 - any other case. when requested version doesn't exist the returned object will include the message - """ - try: - oref = Ref.instantiate_ref_with_legacy_parse_fallback(tref) - except Exception as e: - return jsonResponse({'error': getattr(e, 'message', str(e))}, status=400) - if oref.is_empty(): - return jsonResponse({'error': f'We have no text for {oref}.'}, status=400) - if request.method == "GET": +class Text(View): + + def dispatch(self, request, *args, **kwargs): + try: + self.oref = Ref.instantiate_ref_with_legacy_parse_fallback(kwargs['tref']) + except Exception as e: + return jsonResponse({'error': getattr(e, 'message', str(e))}, status=400) + return super().dispatch(request, *args, **kwargs) + + def get(self, request, *args, **kwargs): + if self.oref.is_empty(): + return jsonResponse({'error': f'We have no text for {self.oref}.'}, status=400) versions_params = request.GET.getlist('version', []) if not versions_params: versions_params = ['base'] versions_params = VersionsParams.parse_api_params(versions_params) - handler = TextsForClientHandler(oref, versions_params) + handler = TextsForClientHandler(self.oref, versions_params) data = handler.get_versions_for_query() return jsonResponse(data) - return jsonResponse({"error": "Unsupported HTTP method."}, status=405) diff --git a/sefaria/urls.py b/sefaria/urls.py index ee1c1f9235..f670e3e7ec 100644 --- a/sefaria/urls.py +++ b/sefaria/urls.py @@ -150,7 +150,7 @@ url(r'^api/texts/modify-bulk/(?P<title>.+)$', reader_views.modify_bulk_text_api), url(r'^api/texts/(?P<tref>.+)/(?P<lang>\w\w)/(?P<version>.+)$', reader_views.old_text_versions_api_redirect), url(r'^api/texts/(?P<tref>.+)$', reader_views.texts_api), - url(r'^api/v3/texts/(?P<tref>.+)$', bi_directional_translation_views.get_texts), + url(r'^api/v3/texts/(?P<tref>.+)$', bi_directional_translation_views.Text.as_view()), url(r'^api/index/?$', reader_views.table_of_contents_api), url(r'^api/opensearch-suggestions/?$', reader_views.opensearch_suggestions_api), url(r'^api/index/titles/?$', reader_views.text_titles_api), From 3b7bbf42e0f2003b6e0b4ffe290895414e130d81 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Wed, 23 Aug 2023 07:27:21 +0300 Subject: [PATCH 143/756] refactor(api tests): rename as api_views. --- sefaria/urls.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sefaria/urls.py b/sefaria/urls.py index f670e3e7ec..8a760a50af 100644 --- a/sefaria/urls.py +++ b/sefaria/urls.py @@ -14,7 +14,7 @@ import sourcesheets.views as sheets_views import sefaria.gauth.views as gauth_views import django.contrib.auth.views as django_auth_views -import api.views as bi_directional_translation_views +import api.views as api_views from sefaria.site.urls import site_urlpatterns @@ -150,7 +150,7 @@ url(r'^api/texts/modify-bulk/(?P<title>.+)$', reader_views.modify_bulk_text_api), url(r'^api/texts/(?P<tref>.+)/(?P<lang>\w\w)/(?P<version>.+)$', reader_views.old_text_versions_api_redirect), url(r'^api/texts/(?P<tref>.+)$', reader_views.texts_api), - url(r'^api/v3/texts/(?P<tref>.+)$', bi_directional_translation_views.Text.as_view()), + url(r'^api/v3/texts/(?P<tref>.+)$', api_views.Text.as_view()), url(r'^api/index/?$', reader_views.table_of_contents_api), url(r'^api/opensearch-suggestions/?$', reader_views.opensearch_suggestions_api), url(r'^api/index/titles/?$', reader_views.text_titles_api), From fc061ec0b620c42dcf69024fa047b0dc41ced2b5 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Wed, 23 Aug 2023 12:30:21 +0300 Subject: [PATCH 144/756] chore: added backend for topics birthPlace and deathPlace --- sefaria/helper/descriptions.py | 57 ++++++++++++++----------- sefaria/helper/topic.py | 52 ++++++++++++++++++++--- sefaria/model/place.py | 6 ++- sefaria/model/topic.py | 14 +++++++ static/js/AdminEditor.jsx | 10 ++++- static/js/Misc.jsx | 2 + static/js/TopicEditor.jsx | 77 +++++++++++++++++++++++++--------- static/js/sefaria/sefaria.js | 1 + 8 files changed, 168 insertions(+), 51 deletions(-) diff --git a/sefaria/helper/descriptions.py b/sefaria/helper/descriptions.py index 3ee6946a78..7b09ee93ae 100644 --- a/sefaria/helper/descriptions.py +++ b/sefaria/helper/descriptions.py @@ -12,6 +12,38 @@ from sefaria.system.exceptions import DuplicateRecordError from sefaria.model.abstract import SluggedAbstractMongoRecord +def create_era_link(topic, prev_era_to_delete=None): + era_slug_map = { + "GN": "geon-person", + "RI": "rishon-person", + "AH": "achron-person", + "CO": "modern-person", + "KG": "mishnaic-people", + "PT": "mishnaic-people", + "T": "mishnaic-people", + "A": "talmudic-people", + } + + isa_object_aggregate_map = { + 'tosafot': 'group-of-rishon-people' + } + + if prev_era_to_delete: + to_topic = isa_object_aggregate_map.get(topic.slug, era_slug_map[prev_era_to_delete]) + prev_link = IntraTopicLink().load({'toTopic': to_topic, 'fromTopic': topic.slug, 'linkType': 'is-a'}) + if prev_link: + prev_link.delete() + + to_topic = isa_object_aggregate_map.get(topic.slug, era_slug_map[topic.get_property('era')]) + itl = IntraTopicLink({ + "toTopic": to_topic, + "fromTopic": topic.slug, + "linkType": "is-a", + "dataSource": "sefaria", + "generatedBy": "update_authors_data" + }) + itl.save() + def update_authors_data(): @@ -42,21 +74,6 @@ def update_authors_data(): "Contemporary": "CO" } - era_slug_map = { - "GN": "geon-person", - "RI": "rishon-person", - "AH": "achron-person", - "CO": "modern-person", - "KG": "mishnaic-people", - "PT": "mishnaic-people", - "T": "mishnaic-people", - "A": "talmudic-people", - } - - isa_object_aggregate_map = { - 'tosafot': 'group-of-rishon-people' - } - url = 'https://docs.google.com/spreadsheets/d/e/2PACX-1vSx60DLNs8Dp0l2xpsPjrxD3dBpIKASXSBiE-zjq74SvUIc-hD-mHwCxsuJpQYNVHIh7FDBwx7Pp9zR/pub?gid=0&single=true&output=csv' response = requests.get(url) data = response.content.decode("utf-8") @@ -167,16 +184,8 @@ def _(p: Topic, attr, value): error_texts.append(str(e)) if p.get_property('era'): - to_topic = isa_object_aggregate_map.get(p.slug, era_slug_map[p.get_property('era')]) - itl = IntraTopicLink({ - "toTopic": to_topic, - "fromTopic": p.slug, - "linkType": "is-a", - "dataSource": "sefaria", - "generatedBy": "update_authors_data" - }) try: - itl.save() + create_era_link(p) except DuplicateRecordError as e: error_texts.append(str(e)) diff --git a/sefaria/helper/topic.py b/sefaria/helper/topic.py index 0b3b3fb444..3b0c9ac99c 100644 --- a/sefaria/helper/topic.py +++ b/sefaria/helper/topic.py @@ -12,6 +12,7 @@ import structlog from sefaria import tracker +from sefaria.helper.descriptions import create_era_link logger = structlog.get_logger(__name__) def get_topic(v2, topic, with_html=True, with_links=True, annotate_links=True, with_refs=True, group_related=True, annotate_time_period=False, ref_link_type_filters=None, with_indexes=True): @@ -1037,7 +1038,49 @@ def topic_change_category(topic_obj, new_category, old_category="", rebuild=Fals rebuild_topic_toc(topic_obj, category_changed=True) return topic_obj +def update_topic_titles(topic_obj, **kwargs): + for lang in ['en', 'he']: + for title in topic_obj.get_titles(lang): + topic_obj.remove_title(title, lang) + for title in kwargs['altTitles'][lang]: + topic_obj.add_title(title, lang) + topic_obj.add_title(kwargs['title'], 'en', True, True) + topic_obj.add_title(kwargs['heTitle'], 'he', True, True) + return topic_obj + + +def update_authors_era(topic_obj, **kwargs): + for k in ["birthYear", "deathYear"]: + assert kwargs[k].is_digit(), f"'{k}' must be a number but is {kwargs[k]}" + kwargs[k] = int(kwargs[k]) + setattr(topic_obj, k, kwargs[k]) + + prev_era = getattr(topic_obj, 'era', None) + if 'era' in kwargs and prev_era != kwargs['era']: + setattr(topic_obj, 'era', kwargs['era']) + create_era_link(topic, prev_era_to_delete=prev_era) + + return topic_obj + +def update_authors_place_data(topic_obj, **kwargs): + for en, he in [("birthPlace", "heBirthPlace"), ("deathPlace", "heDeathPlace")]: + new_val = kwargs.get(en, None) + old_val = getattr(topic_obj, en, None) + if new_val != old_val: + p = Place({'key': new_val}) + p.name_group.add_title(new_val, 'en', True, True) + he_new_val = kwargs.get(he) + if he_new_val: + p.name_group.add_title(he_new_val, 'he', True, True) + p.city_to_coordinates(new_val) + p.save() + properties = getattr(topic_obj, 'properties') + properties[en] = new_val + properties[he] = he_new_val + topic_obj.properties = properties + + return topic_obj def update_topic(topic_obj, **kwargs): """ @@ -1051,13 +1094,12 @@ def update_topic(topic_obj, **kwargs): old_category = "" orig_slug = topic_obj.slug - if 'title' in kwargs and kwargs['title'] != topic_obj.get_primary_title('en'): - topic_obj.add_title(kwargs['title'], 'en', True, True) - - if 'heTitle' in kwargs and kwargs['heTitle'] != topic_obj.get_primary_title('he'): - topic_obj.add_title(kwargs['heTitle'], 'he', True, True) + update_topic_titles(topic_obj, **kwargs) if 'category' in kwargs: + if kwargs['category'] == 'authors': + topic_obj = update_authors_place_data(topic_obj, **kwargs) + topic_obj = update_authors_era(topic_obj, **kwargs) orig_link = IntraTopicLink().load({"linkType": "displays-under", "fromTopic": topic_obj.slug, "toTopic": {"$ne": topic_obj.slug}}) old_category = orig_link.toTopic if orig_link else Topic.ROOT if old_category != kwargs['category']: diff --git a/sefaria/model/place.py b/sefaria/model/place.py index 5f88c8643b..51d009ec48 100644 --- a/sefaria/model/place.py +++ b/sefaria/model/place.py @@ -5,6 +5,7 @@ from sefaria.system.exceptions import InputError import structlog +from geopy.geocoders import Nominatim logger = structlog.get_logger(__name__) class Place(abst.AbstractMongoRecord): @@ -47,7 +48,10 @@ def primary_name(self, lang=None): def secondary_names(self, lang=None): return self.name_group.secondary_titles(lang) - ### + def city_to_coordinates(self, city): + geolocator = Nominatim(user_agent=self.key) + location = geolocator.geocode(city) + self.point_location(location) def point_location(self, lon=None, lat=None): if lat is None and lon is None: diff --git a/sefaria/model/topic.py b/sefaria/model/topic.py index 0cf3bc4a76..49199f4607 100644 --- a/sefaria/model/topic.py +++ b/sefaria/model/topic.py @@ -7,6 +7,7 @@ from sefaria.model.timeperiod import TimePeriod from sefaria.system.database import db import structlog, bleach +from sefaria.model.place import * import regex as re logger = structlog.get_logger(__name__) @@ -400,10 +401,23 @@ def get_person_by_key(key: str): """ return PersonTopic().load({"alt_ids.old-person-key": key}) + def annotate_place(self, d): + birthPlace = getattr(self, "birthPlace", None) + deathPlace = getattr(self, "deathPlace", None) + if birthPlace: + heBirthPlace = Place().load({"key": birthPlace}) + heDeathPlace = Place().load({"key": deathPlace}) + d['properties']['heBirthPlace'] = {'value': heBirthPlace.primary_name('he'), + 'dataSource': d['properties']['birthPlace']['dataSource']} + d['properties']['heDeathPlace'] = {'value': heDeathPlace.primary_name('he'), + 'dataSource': d['properties']['deathPlace']['dataSource']} + return d + def contents(self, **kwargs): annotate_time_period = kwargs.get('annotate_time_period', False) d = super(PersonTopic, self).contents(**kwargs) if annotate_time_period: + d = self.annotate_place(d) tp = self.most_accurate_time_period() if tp is not None: d['timePeriod'] = { diff --git a/static/js/AdminEditor.jsx b/static/js/AdminEditor.jsx index 964f5c65b7..cfc1e5f31a 100644 --- a/static/js/AdminEditor.jsx +++ b/static/js/AdminEditor.jsx @@ -38,9 +38,15 @@ const options_for_form = { "Birth Place": { label: "Place of Birth", field: "birthPlace", placeholder: "Place of birth", type: 'input' }, - "Death Place": { + "Hebrew Birth Place": { + label: "Hebrew Place of Birth", field: "heBirthPlace", placeholder: "Place of birth", type: 'input' + }, + "Place of Death": { label: "Place of Death", field: "deathPlace", placeholder: "Place of death", type: 'input' }, + "Hebrew Place of Death": { + label: "Hebrew Place of Death", field: "heDeathPlace", placeholder: "Place of death", type: 'input' + }, "Birth Year": { label: "Year of Birth", field: "birthYear", placeholder: "Year of birth", type: 'input' }, @@ -49,7 +55,7 @@ const options_for_form = { }, "Era": { label: "Era (GN/Gaonim, RI/Rishonim, AH/Achronim, CO/Contemporary)", field: "era", placeholder: "Choose an era", type: 'dropdown', - dropdown_data: ["GN", "RI", "AH", "CO"] + dropdown_data: Sefaria._eras } } diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 0cf27654e3..9d9dc7af5b 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -1196,6 +1196,8 @@ const EditorForExistingTopic = ({ toggle, data }) => { origEnAltTitles: data.titles.filter(x => !x.primary && x.lang === 'en').map(x => x.text), origHeAltTitles: data.titles.filter(x => !x.primary && x.lang === 'he').map(x => x.text), origBirthPlace: data?.properties?.birthPlace.value, + origHeBirthPlace: data?.properties?.heBirthPlace.value, + origHeDeathPlace: data?.properties?.heDeathPlace.value, origBirthYear: data?.properties?.birthYear.value, origDeathPlace: data?.properties?.deathPlace.value, origDeathYear: data?.properties?.deathYear.value, diff --git a/static/js/TopicEditor.jsx b/static/js/TopicEditor.jsx index c2e734b9a3..c316d38ae0 100644 --- a/static/js/TopicEditor.jsx +++ b/static/js/TopicEditor.jsx @@ -76,6 +76,10 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { alert(Sefaria._("Title must be provided.")); return false; } + if (data?.era && Sefaria.util.inArray(data.era, Sefaria._eras)) { + alert(`Era ${data.era} is invalid.`); + return false; + } if (sortedSubtopics.length > 0 && !isNew) { await saveReorderedSubtopics(); // make sure subtopics reordered before saving topic information below } @@ -86,16 +90,38 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { const postCategoryData = {topics: sortedSubtopics}; requestWithCallBack({url, data: postCategoryData, setSavingStatus, redirect: () => window.location.href = "/topics"}); } - const saveTopic = function () { - toggle(); - let url = ""; + const authorItems = (type) => { + switch(type) { + case 'labels': + return Sefaria._siteSettings.TORAH_SPECIFIC ? + ["Birth Place", "Hebrew Birth Place", "Birth Year", "Place of Death", "Hebrew Place of Death", "Death Year", "Era"] + : ["Birth Place", "Birth Year", "Place of Death", "Death Year"]; + case 'keys': + return Sefaria._siteSettings.TORAH_SPECIFIC ? + ["birthPlace", "heBirthPlace", "birthYear", "deathPlace", "heDeathPlace", "era"] + : ["birthPlace", "birthYear", "deathPlace", "deathYear"]; + } + } + const prepData = () => { let postData = {...data, "description": {"en": data.enDescription, "he": data.heDescription}, "title": data.enTitle, "heTitle": data.heTitle}; if (isCategory) { postData = {...postData, "catDescription": {"en": data.enCategoryDescription, "he": data.heCategoryDescription}}; } - postData.category = data.catSlug; + if (isAuthor) { + postData.altTitles = {}; + postData.altTitles.en = postData.enAltTitles.map(x => x.name); + postData.altTitles.he = postData.heAltTitles.map(x => x.name); + } + else { + authorItems('keys').forEach(x => { // we don't want to post these keys since they are only for authors + delete postData[x]; + }) + } + + postData.category = data.catSlug; + let url = ""; if (isNew) { url = "/api/topic/new"; } @@ -108,18 +134,34 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { } } + return [postData, url]; + } + + const saveTopic = function () { + toggle(); + const [postData, url] = prepData(); const postJSON = JSON.stringify(postData); - $.post(url, {"json": postJSON}, function(result) { - if (result.error) { - toggle(); - alert(result.error); - } else { - const newSlug = result.slug; - onCreateSuccess(newSlug); - } - }).fail( function(xhr, status, errorThrown) { - alert("Unfortunately, there may have been an error saving this topic information: "+errorThrown.toString()); - }); + if (onCreateSuccess) { // used by TopicSearch.jsx + $.post(url, {"json": postJSON}, function (result) { + if (result.error) { + toggle(); + alert(result.error); + } else { + const newSlug = result.slug; + onCreateSuccess(newSlug); + } + }).fail(function (xhr, status, errorThrown) { + alert("Unfortunately, there may have been an error saving this topic information: " + errorThrown.toString()); + }); + } + else { + requestWithCallBack({ + url, + data: postJSON, + setSavingStatus, + redirect: () => window.location.href = `/topics/${postData.slug}` + }); + } } const deleteObj = function() { @@ -133,10 +175,7 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { items.push("Hebrew Short Description"); } if (isAuthor) { - ["Birth Place", "Birth Year", "Death Place", "Death Year"].forEach(x => items.push(x)); - if (Sefaria._siteSettings.TORAH_SPECIFIC) { - items.push("Era"); - } + authorItems('labels').forEach(x => items.push(x)); } return <AdminEditor title="Topic Editor" close={close} catMenu={catMenu} data={data} savingStatus={savingStatus} validate={validate} deleteObj={deleteObj} updateData={updateData} isNew={isNew} diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index 9698478df8..6699d1c0f0 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -349,6 +349,7 @@ Sefaria = extend(Sefaria, { return (text.indexOf(title) > -1); }); }, + _eras: ["GN", "RI", "AH", "CO"], makeRefRe: function(titles) { // Construct and store a Regular Expression for matching citations // based on known books, or a list of titles explicitly passed From 8fc12368354bcd616789792bdeada858cd4265f9 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Wed, 23 Aug 2023 14:35:21 +0300 Subject: [PATCH 145/756] refactor(api texts): split api params in view. --- api/helper.py | 14 ------------ api/tests.py | 14 ------------ api/texts_api.py | 55 ++++++------------------------------------------ api/views.py | 13 ++++++++++-- 4 files changed, 18 insertions(+), 78 deletions(-) delete mode 100644 api/helper.py diff --git a/api/helper.py b/api/helper.py deleted file mode 100644 index 070e39e81a..0000000000 --- a/api/helper.py +++ /dev/null @@ -1,14 +0,0 @@ -from typing import List - -def split_query_param_and_add_defaults(query_string: str, list_length: int, defaults: List[str]) -> List[str]: - """ - split a string of query params into list of params by pipe. filling the list with defaults when there are not enough - :param query_string: - :param list_length: the required length of a parameters list - :param defaults: a list of default strings for potentially missing parameters - :return: list of parematers - """ - params = query_string.split('|', list_length - 1) - if len(params) < list_length: - params += defaults[len(params)-list_length:] - return params diff --git a/api/tests.py b/api/tests.py index 4eb6da02cc..05398172a0 100644 --- a/api/tests.py +++ b/api/tests.py @@ -3,23 +3,9 @@ django.setup() from reader.tests import SefariaTestCase import json -from api.helper import split_query_param_and_add_defaults from api.api_warnings import APIWarningCode -def test_split_at_pipe_with_default(): - for string, list_length, default, expected in [ - ('he|foo bar', 2, [], ['he', 'foo bar']), - ('he|foo bar', 2, ['baz'], ['he', 'foo bar']), - ('he', 2, ['baz'], ['he', 'baz']), - ('he|foo bar|baz', 3, [], ['he', 'foo bar', 'baz']), - ('he|foo bar|baz', 3, ['blue'], ['he', 'foo bar', 'baz']), - ('he|foo bar', 3, ['baz'], ['he', 'foo bar', 'baz']), - ('he', 3, ['foo', 'baz'], ['he', 'foo', 'baz']), - ]: - assert expected == split_query_param_and_add_defaults(string, list_length, default) - - c = Client() diff --git a/api/texts_api.py b/api/texts_api.py index 2ee0588b29..3f15d2f02f 100644 --- a/api/texts_api.py +++ b/api/texts_api.py @@ -2,48 +2,9 @@ django.setup() from sefaria.utils.hebrew import hebrew_term from .api_warnings import * -from .helper import split_query_param_and_add_defaults from typing import List -class VersionsParams(): - """ - an object for managing the versions params for TextsHandler - params can come from an API request or internal (sever side rendering) - lang is our code language. special values are: - source - for versions in the source language of the text - base - as source but with falling to the 'nearest' to source, or what we have defined as such - vtitle is the exact versionTitle. special values are: - no vtitle - the version with the max priority attr of the specified language - all - all versions of the specified language - representing_string is the original string that came from an API call - """ - - def __init__(self, lang: str, vtitle: str, representing_string=''): - self.lang = lang - self.vtitle = vtitle - self.representing_string = representing_string - - def __eq__(self, other): - return isinstance(other, VersionsParams) and self.lang == other.lang and self.vtitle == other.vtitle - - @staticmethod - def parse_api_params(version_params): - """ - an api call contains ref and list of version_params - a version params is string divided by pipe as 'lang|vtitle' - this function takes the list of version_params and returns list of VersionsParams - """ - version_params_list = [] - for params_string in version_params: - lang, vtitle = split_query_param_and_add_defaults(params_string, 2, ['']) - vtitle = vtitle.replace('_', ' ') - version_params = VersionsParams(lang, vtitle, params_string) - if version_params not in version_params_list: - version_params_list.append(version_params) - return version_params_list - - class TextsForClientHandler(): """ process api calls for text @@ -57,15 +18,14 @@ class TextsForClientHandler(): BASE = 'base' SOURCE = 'source' - def __init__(self, oref: Ref, versions_params: List[VersionsParams]): + def __init__(self, oref: Ref, versions_params: List[List[str]]): self.versions_params = versions_params self.oref = oref self.handled_version_params = [] self.all_versions = self.oref.version_list() self.return_obj = {'versions': [], 'warnings': []} - def _handle_warnings(self, version_params: VersionsParams) -> None: - lang, vtitle = version_params.lang, version_params.vtitle + def _handle_warnings(self, lang: str, vtitle: str, params_str: str) -> None: if lang == self.SOURCE: warning = APINoSourceText(self.oref) elif vtitle and vtitle != self.ALL: @@ -73,13 +33,12 @@ def _handle_warnings(self, version_params: VersionsParams) -> None: else: availabe_langs = {v['actualLanguage'] for v in self.all_versions} warning = APINoLanguageVersion(self.oref, sorted(availabe_langs)) - representing_string = version_params.representing_string or f'{version_params.lang}|{version_params.representing_string}' + representing_string = params_str or f'{lang}|{vtitle}' self.return_obj['warnings'].append({ representing_string: warning.get_message() }) - def _append_required_versions(self, version_params: VersionsParams) -> None: - lang, vtitle = version_params.lang, version_params.vtitle + def _append_required_versions(self, lang: str, vtitle: str, params_str: str) -> None: if lang == self.BASE: lang_condition = lambda v: v['isBaseText2'] #temporal name elif lang == self.SOURCE: @@ -98,7 +57,7 @@ def _append_required_versions(self, version_params: VersionsParams) -> None: if version not in self.return_obj['versions']: #do not return the same version even if included in two different version params self.return_obj['versions'].append(version) if not versions: - self._handle_warnings(version_params) + self._handle_warnings(lang, vtitle, params_str) def _add_text_to_versions(self) -> None: for version in self.return_obj['versions']: @@ -160,8 +119,8 @@ def _add_node_data_to_return_obj(self) -> None: }) def get_versions_for_query(self) -> dict: - for version_params in self.versions_params: - self._append_required_versions(version_params) + for lang, vtitle, params_str in self.versions_params: + self._append_required_versions(lang, vtitle, params_str) self._add_text_to_versions() self._add_ref_data_to_return_obj() self._add_index_data_to_return_obj() diff --git a/api/views.py b/api/views.py index fa55f9f615..9678ba210a 100644 --- a/api/views.py +++ b/api/views.py @@ -1,7 +1,8 @@ from sefaria.model import * -from .texts_api import TextsForClientHandler, VersionsParams +from .texts_api import TextsForClientHandler from sefaria.client.util import jsonResponse from django.views import View +from typing import List class Text(View): @@ -13,13 +14,21 @@ def dispatch(self, request, *args, **kwargs): return jsonResponse({'error': getattr(e, 'message', str(e))}, status=400) return super().dispatch(request, *args, **kwargs) + @staticmethod + def split_piped_params(params_string) -> List[str]: + params = params_string.split('|') + if len(params) < 2: + params.append('') + params[1] = params[1].replace('_', ' ') + return params + def get(self, request, *args, **kwargs): if self.oref.is_empty(): return jsonResponse({'error': f'We have no text for {self.oref}.'}, status=400) versions_params = request.GET.getlist('version', []) if not versions_params: versions_params = ['base'] - versions_params = VersionsParams.parse_api_params(versions_params) + versions_params = [self.split_piped_params(param_str) + [param_str] for param_str in versions_params] handler = TextsForClientHandler(self.oref, versions_params) data = handler.get_versions_for_query() return jsonResponse(data) From 5929826633926599a92d1f174a808ec0d13d560d Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Wed, 23 Aug 2023 14:57:45 +0300 Subject: [PATCH 146/756] refactor(api texts): restore TextChunk. --- sefaria/model/text.py | 443 ++++++++++++++++++------------------------ 1 file changed, 194 insertions(+), 249 deletions(-) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index b8cd392de7..8ca31587f5 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -1738,12 +1738,28 @@ def __call__(cls, *args, **kwargs): else: return super(TextFamilyDelegator, cls).__call__(*args, **kwargs) -class TextRange(AbstractTextRecord, metaclass=TextFamilyDelegator): + +class TextChunk(AbstractTextRecord, metaclass=TextFamilyDelegator): + """ + A chunk of text corresponding to the provided :class:`Ref`, language, and optional version name. + If it is possible to get a more complete text by merging multiple versions, a merged result will be returned. + + :param oref: :class:`Ref` + :param lang: "he" or "en". "he" means all rtl languages and "en" means all ltr languages + :param vtitle: optional. Title of the version desired. + :param actual_lang: optional. if vtitle isn't specified, prefer to find a version with ISO language `actual_lang`. As opposed to `lang` which can only be "he" or "en", `actual_lang` can be any valid 2 letter ISO language code. + """ text_attr = "text" - def __init__(self, oref, actual_lang='en', vtitle=None, **kwargs): - #kwargs are only for supporting old TextChunck. should be removed after merging + def __init__(self, oref, lang="en", vtitle=None, exclude_copyrighted=False, actual_lang=None, fallback_on_default_version=False): + """ + :param oref: + :type oref: Ref + :param lang: "he" or "en" + :param vtitle: + :return: + """ if isinstance(oref.index_node, JaggedArrayNode): self._oref = oref else: @@ -1752,41 +1768,57 @@ def __init__(self, oref, actual_lang='en', vtitle=None, **kwargs): raise InputError("Can not get TextChunk at this level, please provide a more precise reference") self._oref = child_ref self._ref_depth = len(self._oref.sections) - self.actual_lang = actual_lang self._versions = [] self._version_ids = None - self._saveable = False + self._saveable = False # Can this TextChunk be saved? + + self.lang = lang self.is_merged = False self.sources = [] self.text = self._original_text = self.empty_text() self.vtitle = vtitle + self.full_version = None self.versionSource = None # handling of source is hacky - self._choose_version(actual_lang=actual_lang, **kwargs) #kactual_lang and wargs are only for supporting old TextChunck. should be removed after merging - def _choose_version(self, **kwargs): #kwargs are only for supporting old TextChunck. should be removed after merging - if self.actual_lang and self.vtitle: + if lang and vtitle and not fallback_on_default_version: self._saveable = True - v = Version().load({"title": self._oref.index.title, "actualLanguage": self.actual_lang, "versionTitle": self.vtitle}, self._oref.part_projection()) + v = Version().load({"title": self._oref.index.title, "language": lang, "versionTitle": vtitle}, self._oref.part_projection()) + if exclude_copyrighted and v.is_copyrighted(): + raise InputError("Can not provision copyrighted text. {} ({}/{})".format(oref.normal(), vtitle, lang)) if v: self._versions += [v] - self.text = self._original_text = self.trim_text(v.content_node(self._oref.index_node)) - elif self.actual_lang: - self._choose_version_by_lang() + try: + self.text = self._original_text = self.trim_text(v.content_node(self._oref.index_node)) + except TypeError: + raise MissingKeyError(f'The version {vtitle} exists but has no key for the node {self._oref.index_node}') + elif lang: + if actual_lang is not None: + self._choose_version_by_lang(oref, lang, exclude_copyrighted, actual_lang, prioritized_vtitle=vtitle) + else: + self._choose_version_by_lang(oref, lang, exclude_copyrighted, prioritized_vtitle=vtitle) else: raise Exception("TextChunk requires a language.") - if not self._versions: - version_title_message = f" and version title '{self.vtile}'" if self.vtile else '' - raise NoVersionFoundError(f"No text record found for '{self._oref.index.title}' in {self.actual_lang}{version_title_message}") - def _choose_version_by_lang(self) -> None: - vset = VersionSet(self._oref.condition_query(actual_lang=self.actual_lang), proj=self._oref.part_projection()) + def _choose_version_by_lang(self, oref, lang: str, exclude_copyrighted: bool, actual_lang: str = None, prioritized_vtitle: str = None) -> None: + if prioritized_vtitle: + actual_lang = None + vset = VersionSet(self._oref.condition_query(lang, actual_lang), proj=self._oref.part_projection()) + if len(vset) == 0: + if VersionSet({"title": self._oref.index.title}).count() == 0: + raise NoVersionFoundError("No text record found for '{}'".format(self._oref.index.title)) + return if len(vset) == 1: v = vset[0] + if exclude_copyrighted and v.is_copyrighted(): + raise InputError("Can not provision copyrighted text. {} ({}/{})".format(oref.normal(), v.versionTitle, v.language)) self._versions += [v] self.text = self.trim_text(v.content_node(self._oref.index_node)) - elif len(vset) > 1: # multiple versions available, merge - merged_text, sources = vset.merge(self._oref.index_node) #todo: For commentaries, this merges the whole chapter. It may show up as merged, even if our part is not merged. + #todo: Should this instance, and the non-merge below, be made saveable? + else: # multiple versions available, merge + if exclude_copyrighted: + vset.remove(Version.is_copyrighted) + merged_text, sources = vset.merge(self._oref.index_node, prioritized_vtitle=prioritized_vtitle) #todo: For commentaries, this merges the whole chapter. It may show up as merged, even if our part is not merged. self.text = self.trim_text(merged_text) if len(set(sources)) == 1: for v in vset: @@ -1799,13 +1831,16 @@ def _choose_version_by_lang(self) -> None: self._versions = vset.array() def __str__(self): - args = f"{self._oref}, {self.actual_lang}" + args = "{}, {}".format(self._oref, self.lang) if self.vtitle: - args += f", {self.vtitle}" + args += ", {}".format(self.vtitle) return args def __repr__(self): # Wanted to use orig_tref, but repr can not include Unicode - return f"{self.__class__.__name__}({self.__str__()})" + args = "{}, {}".format(self._oref, self.lang) + if self.vtitle: + args += ", {}".format(self.vtitle) + return "{}({})".format(self.__class__.__name__, args) def version_ids(self): if self._version_ids is None: @@ -1826,6 +1861,55 @@ def ja(self, remove_html=False): else: return JaggedTextArray(self.text) + def save(self, force_save=False): + """ + For editing in place (i.e. self.text[3] = "Some text"), it is necessary to set force_save to True. This is + because by editing in place, both the self.text and the self._original_text fields will get changed, + causing the save to abort. + :param force_save: If set to True, will force a save even if no change was detected in the text. + :return: + """ + assert self._saveable, "Tried to save a read-only text: {}".format(self._oref.normal()) + assert not self._oref.is_range(), "Only non-range references can be saved: {}".format(self._oref.normal()) + #may support simple ranges in the future. + #self._oref.is_range() and self._oref.range_index() == len(self._oref.sections) - 1 + if not force_save: + if self.text == self._original_text: + logger.warning("Aborted save of {}. No change in text.".format(self._oref.normal())) + return False + + self._validate() + self._sanitize() + self._trim_ending_whitespace() + + if not self.version(): + self.full_version = Version( + { + "chapter": self._oref.index.nodes.create_skeleton(), + "versionTitle": self.vtitle, + "versionSource": self.versionSource, + "language": self.lang, + "title": self._oref.index.title + } + ) + else: + self.full_version = Version().load({"title": self._oref.index.title, "language": self.lang, "versionTitle": self.vtitle}) + assert self.full_version, "Failed to load Version record for {}, {}".format(self._oref.normal(), self.vtitle) + if self.versionSource: + self.full_version.versionSource = self.versionSource # hack + + content = self.full_version.sub_content(self._oref.index_node.version_address()) + self._pad(content) + self.full_version.sub_content(self._oref.index_node.version_address(), [i - 1 for i in self._oref.sections], self.text) + + self._check_available_text_pre_save() + + self.full_version.save() + self._oref.recalibrate_next_prev_refs(len(self.text)) + self._update_link_language_availability() + + return self + def _pad(self, content): """ Pads the passed content to the dimension of self._oref. @@ -1851,6 +1935,61 @@ def _pad(self, content): if pos < self._ref_depth - 2 and isinstance(parent_content[val - 1], str): parent_content[val - 1] = [parent_content[val - 1]] + def _check_available_text_pre_save(self): + """ + Stores the availability of this text in before a save is made, + so that we can know if segments have been added or deleted overall. + """ + self._available_text_pre_save = {} + langs_checked = [self.lang] # swtich to ["en", "he"] when global availability checks are needed + for lang in langs_checked: + try: + self._available_text_pre_save[lang] = self._oref.text(lang=lang).text + except NoVersionFoundError: + self._available_text_pre_save[lang] = [] + + def _check_available_segments_changed_post_save(self, lang=None): + """ + Returns a list of tuples containing a Ref and a boolean availability + for each Ref that was either made available or unavailble for `lang`. + If `lang` is None, returns changed availability across all langauges. + """ + if lang: + old_refs_available = self._text_to_ref_available(self._available_text_pre_save[self.lang]) + else: + # Looking for availability of in all langauges, merge results of Hebrew and English + old_en_refs_available = self._text_to_ref_available(self._available_text_pre_save["en"]) + old_he_refs_available = self._text_to_ref_available(self._available_text_pre_save["he"]) + zipped = list(itertools.zip_longest(old_en_refs_available, old_he_refs_available)) + old_refs_available = [] + for item in zipped: + en, he = item[0], item[1] + ref = en[0] if en else he[0] + old_refs_available.append((ref, (en and en[1] or he and he[1]))) + + new_refs_available = self._text_to_ref_available(self.text) + + changed = [] + zipped = list(itertools.zip_longest(old_refs_available, new_refs_available)) + for item in zipped: + old_text, new_text = item[0], item[1] + had_previously = old_text and old_text[1] + have_now = new_text and new_text[1] + + if not had_previously and have_now: + changed.append(new_text) + elif had_previously and not have_now: + # Current save is deleting a line of text, but it could still be + # available in a different version for this language. Check again. + if lang: + text_still_available = bool(old_text[0].text(lang=lang).text) + else: + text_still_available = bool(old_text[0].text("en").text) or bool(old_text[0].text("he").text) + if not text_still_available: + changed.append([old_text[0], False]) + + return changed + def _text_to_ref_available(self, text): """Converts a JaggedArray of text to flat list of (Ref, bool) if text is availble""" flat = JaggedArray(text).flatten_to_array_with_indices() @@ -1864,6 +2003,18 @@ def _text_to_ref_available(self, text): refs_available += [[ref, available]] return refs_available + def _update_link_language_availability(self): + """ + Check if current save has changed the overall availabilty of text for refs + in this language, pass refs to update revelant links if so. + """ + changed = self._check_available_segments_changed_post_save(lang=self.lang) + + if len(changed): + from . import link + for change in changed: + link.update_link_language_availabiliy(change[0], self.lang, change[1]) + def _validate(self): """ validate that depth/breadth of the TextChunk.text matches depth/breadth of the Ref @@ -1874,8 +2025,8 @@ def _validate(self): implied_depth = ref_depth + posted_depth if implied_depth != self._oref.index_node.depth: raise InputError( - f"Text Structure Mismatch. The stored depth of {self._oref.index_node.full_title()} is {self._oref.index_node.depth}, \ - but the text posted to {self._oref.normal()} implies a depth of {implied_depth}." + "Text Structure Mismatch. The stored depth of {} is {}, but the text posted to {} implies a depth of {}." + .format(self._oref.index_node.full_title(), self._oref.index_node.depth, self._oref.normal(), implied_depth) ) #validate that length of the array matches length of the ref @@ -1884,18 +2035,20 @@ def _validate(self): span_size = self._oref.span_size() if posted_depth == 0: #possible? raise InputError( - f"Text Structure Mismatch. {self._oref.normal()} implies a length of {span_size} sections, but the text posted is a string." + "Text Structure Mismatch. {} implies a length of {} sections, but the text posted is a string." + .format(self._oref.normal(), span_size) ) elif posted_depth == 1: #possible? raise InputError( - f"Text Structure Mismatch. {self._oref.normal()} implies a length of {span_size} sections, but the text posted is a simple list." + "Text Structure Mismatch. {} implies a length of {} sections, but the text posted is a simple list." + .format(self._oref.normal(), span_size) ) else: posted_length = len(self.text) if posted_length != span_size: raise InputError( - f"Text Structure Mismatch. {self._oref.normal()} implies a length of {span_size} sections, \ - but the text posted has {posted_length} elements." + "Text Structure Mismatch. {} implies a length of {} sections, but the text posted has {} elements." + .format(self._oref.normal(), span_size, posted_length) ) #todo: validate last section size if provided @@ -1903,21 +2056,23 @@ def _validate(self): range_length = self._oref.range_size() if posted_depth == 0: raise InputError( - f"Text Structure Mismatch. {self._oref.normal()} implies a length of {range_length}, but the text posted is a string." + "Text Structure Mismatch. {} implies a length of {}, but the text posted is a string." + .format(self._oref.normal(), range_length) ) elif posted_depth == 1: posted_length = len(self.text) if posted_length != range_length: raise InputError( - f"Text Structure Mismatch. {self._oref.normal()} implies a length of {range_length}, \ - but the text posted has {posted_length} elements." + "Text Structure Mismatch. {} implies a length of {}, but the text posted has {} elements." + .format(self._oref.normal(), range_length, posted_length) ) else: # this should never happen. The depth check should catch it. raise InputError( - f"Text Structure Mismatch. {self._oref.normal()} implies an simple array of length {range_length}, \ - but the text posted has depth {posted_depth}." + "Text Structure Mismatch. {} implies an simple array of length {}, but the text posted has depth {}." + .format(self._oref.normal(), range_length, posted_depth) ) + #maybe use JaggedArray.subarray()? def trim_text(self, txt): """ Trims a text loaded from Version record with self._oref.part_projection() to the specifications of self._oref @@ -1986,13 +2141,9 @@ def has_manually_wrapped_refs(self): # merged version return False - def get_language(self): - #method for getting a different lang attribue in old TextChunck and RangeText - #can be removed after merging and all uses can be replaced by self.acutal_lang - return self.actual_lang - def nonempty_segment_refs(self): """ + :return: list of segment refs with content in this TextChunk """ r = self._oref @@ -2004,7 +2155,7 @@ def nonempty_segment_refs(self): else: input_refs = [r] for temp_ref in input_refs: - temp_tc = temp_ref.text(lang=self.get_language(), vtitle=self.vtitle) + temp_tc = temp_ref.text(lang=self.lang, vtitle=self.vtitle) ja = temp_tc.ja() jarray = ja.mask().array() @@ -2033,9 +2184,9 @@ def find_string(self, regex_str, cleaner=lambda x: x, strict=True): text_list = [x for x in self.ja().flatten_to_array() if len(x) > 0] if len(text_list) != len(ref_list): if strict: - raise ValueError(f"The number of refs doesn't match the number of starting words. len(refs)={len(ref_list)} len(inds)={len(text_list)}") + raise ValueError("The number of refs doesn't match the number of starting words. len(refs)={} len(inds)={}".format(len(ref_list),len(ind_list))) else: - print(f"Warning: The number of refs doesn't match the number of starting words. len(refs)={len(ref_list)} len(inds)={len(text_list)}") + print("Warning: The number of refs doesn't match the number of starting words. len(refs)={} len(inds)={} {}".format(len(ref_list),len(ind_list),str(self._oref))) matches = [] for r, t in zip(ref_list, text_list): @@ -2068,9 +2219,9 @@ def text_index_map(self, tokenizer=lambda x: re.split(r'\s+', x), strict=True, r if len(ind_list) != len(ref_list): if strict: - raise ValueError(f"The number of refs doesn't match the number of starting words. len(refs)={len(ref_list)} len(inds)={len(ind_list)}") + raise ValueError("The number of refs doesn't match the number of starting words. len(refs)={} len(inds)={}".format(len(ref_list),len(ind_list))) else: - print(f"Warning: The number of refs doesn't match the number of starting words. len(refs)={len(ref_list)} len(inds)={len(ind_list)}") + print("Warning: The number of refs doesn't match the number of starting words. len(refs)={} len(inds)={} {}".format(len(ref_list),len(ind_list),str(self._oref))) if len(ind_list) > len(ref_list): ind_list = ind_list[:len(ref_list)] else: @@ -2082,212 +2233,6 @@ def text_index_map(self, tokenizer=lambda x: re.split(r'\s+', x), strict=True, r return ind_list, ref_list, total_len -class TextChunk(TextRange, metaclass=TextFamilyDelegator): - """ - A chunk of text corresponding to the provided :class:`Ref`, language, and optional version name. - If it is possible to get a more complete text by merging multiple versions, a merged result will be returned. - - :param oref: :class:`Ref` - :param lang: "he" or "en". "he" means all rtl languages and "en" means all ltr languages - :param vtitle: optional. Title of the version desired. - :param actual_lang: optional. if vtitle isn't specified, prefer to find a version with ISO language `actual_lang`. As opposed to `lang` which can only be "he" or "en", `actual_lang` can be any valid 2 letter ISO language code. - """ - - text_attr = "text" - - def __init__(self, oref, lang="en", vtitle=None, exclude_copyrighted=False, actual_lang=None, fallback_on_default_version=False): - """ - :param oref: - :type oref: Ref - :param lang: "he" or "en" - :param vtitle: - :return: - """ - - self.lang = lang - super(TextChunk, self).__init__(oref=oref, vtitle=vtitle, exclude_copyrighted=exclude_copyrighted, actual_lang=actual_lang, fallback_on_default_version=fallback_on_default_version) - - def _choose_version(self, exclude_copyrighted, actual_lang, fallback_on_default_version): - if self.lang and self.vtitle and not fallback_on_default_version: - self._saveable = True - v = Version().load({"title": self._oref.index.title, "language": self.lang, "versionTitle": self.vtitle}, self._oref.part_projection()) - if exclude_copyrighted and v.is_copyrighted(): - raise InputError(f"Can not provision copyrighted text. {self._oref.normal()} ({self.vtitle}/{self.lang})") - if v: - self._versions += [v] - self.text = self._original_text = self.trim_text(v.content_node(self._oref.index_node)) - elif self.lang: - if actual_lang is not None: - self._choose_version_by_lang(self.lang, exclude_copyrighted, actual_lang, prioritized_vtitle=self.vtitle) - else: - self._choose_version_by_lang(self.lang, exclude_copyrighted, prioritized_vtitle=self.vtitle) - else: - raise Exception("TextChunk requires a language.") - - def _choose_version_by_lang(self, lang: str, exclude_copyrighted: bool, actual_lang: str = None, prioritized_vtitle: str = None) -> None: - if prioritized_vtitle: - actual_lang = None - vset = VersionSet(self._oref.condition_query(lang, actual_lang), proj=self._oref.part_projection()) - if len(vset) == 0: - if VersionSet({"title": self._oref.index.title}).count() == 0: - raise NoVersionFoundError("No text record found for '{}'".format(self._oref.index.title)) - return - if len(vset) == 1: - v = vset[0] - if exclude_copyrighted and v.is_copyrighted(): - raise InputError("Can not provision copyrighted text. {} ({}/{})".format(oref.normal(), v.versionTitle, v.language)) - self._versions += [v] - self.text = self.trim_text(v.content_node(self._oref.index_node)) - #todo: Should this instance, and the non-merge below, be made saveable? - else: # multiple versions available, merge - if exclude_copyrighted: - vset.remove(Version.is_copyrighted) - merged_text, sources = vset.merge(self._oref.index_node, prioritized_vtitle=prioritized_vtitle) #todo: For commentaries, this merges the whole chapter. It may show up as merged, even if our part is not merged. - self.text = self.trim_text(merged_text) - if len(set(sources)) == 1: - for v in vset: - if v.versionTitle == sources[0]: - self._versions += [v] - break - else: - self.sources = sources - self.is_merged = True - self._versions = vset.array() - - def __str__(self): - args = "{}, {}".format(self._oref, self.lang) - if self.vtitle: - args += ", {}".format(self.vtitle) - return args - - def __repr__(self): # Wanted to use orig_tref, but repr can not include Unicode - args = "{}, {}".format(self._oref, self.lang) - if self.vtitle: - args += ", {}".format(self.vtitle) - return "{}({})".format(self.__class__.__name__, args) - - def save(self, force_save=False): - """ - For editing in place (i.e. self.text[3] = "Some text"), it is necessary to set force_save to True. This is - because by editing in place, both the self.text and the self._original_text fields will get changed, - causing the save to abort. - :param force_save: If set to True, will force a save even if no change was detected in the text. - :return: - """ - assert self._saveable, "Tried to save a read-only text: {}".format(self._oref.normal()) - assert not self._oref.is_range(), "Only non-range references can be saved: {}".format(self._oref.normal()) - #may support simple ranges in the future. - #self._oref.is_range() and self._oref.range_index() == len(self._oref.sections) - 1 - if not force_save: - if self.text == self._original_text: - logger.warning("Aborted save of {}. No change in text.".format(self._oref.normal())) - return False - - self._validate() - self._sanitize() - self._trim_ending_whitespace() - - if not self.version(): - self.full_version = Version( - { - "chapter": self._oref.index.nodes.create_skeleton(), - "versionTitle": self.vtitle, - "versionSource": self.versionSource, - "language": self.lang, - "title": self._oref.index.title - } - ) - else: - self.full_version = Version().load({"title": self._oref.index.title, "language": self.lang, "versionTitle": self.vtitle}) - assert self.full_version, "Failed to load Version record for {}, {}".format(self._oref.normal(), self.vtitle) - if self.versionSource: - self.full_version.versionSource = self.versionSource # hack - - content = self.full_version.sub_content(self._oref.index_node.version_address()) - self._pad(content) - self.full_version.sub_content(self._oref.index_node.version_address(), [i - 1 for i in self._oref.sections], self.text) - - self._check_available_text_pre_save() - - self.full_version.save() - self._oref.recalibrate_next_prev_refs(len(self.text)) - self._update_link_language_availability() - - return self - - def _check_available_text_pre_save(self): - """ - Stores the availability of this text in before a save is made, - so that we can know if segments have been added or deleted overall. - """ - self._available_text_pre_save = {} - langs_checked = [self.lang] # swtich to ["en", "he"] when global availability checks are needed - for lang in langs_checked: - try: - self._available_text_pre_save[lang] = self._oref.text(lang=lang).text - except NoVersionFoundError: - self._available_text_pre_save[lang] = [] - - def _check_available_segments_changed_post_save(self, lang=None): - """ - Returns a list of tuples containing a Ref and a boolean availability - for each Ref that was either made available or unavailble for `lang`. - If `lang` is None, returns changed availability across all langauges. - """ - if lang: - old_refs_available = self._text_to_ref_available(self._available_text_pre_save[self.lang]) - else: - # Looking for availability of in all langauges, merge results of Hebrew and English - old_en_refs_available = self._text_to_ref_available(self._available_text_pre_save["en"]) - old_he_refs_available = self._text_to_ref_available(self._available_text_pre_save["he"]) - zipped = list(itertools.zip_longest(old_en_refs_available, old_he_refs_available)) - old_refs_available = [] - for item in zipped: - en, he = item[0], item[1] - ref = en[0] if en else he[0] - old_refs_available.append((ref, (en and en[1] or he and he[1]))) - - new_refs_available = self._text_to_ref_available(self.text) - - changed = [] - zipped = list(itertools.zip_longest(old_refs_available, new_refs_available)) - for item in zipped: - old_text, new_text = item[0], item[1] - had_previously = old_text and old_text[1] - have_now = new_text and new_text[1] - - if not had_previously and have_now: - changed.append(new_text) - elif had_previously and not have_now: - # Current save is deleting a line of text, but it could still be - # available in a different version for this language. Check again. - if lang: - text_still_available = bool(old_text[0].text(lang=lang).text) - else: - text_still_available = bool(old_text[0].text("en").text) or bool(old_text[0].text("he").text) - if not text_still_available: - changed.append([old_text[0], False]) - - return changed - - def _update_link_language_availability(self): - """ - Check if current save has changed the overall availabilty of text for refs - in this language, pass refs to update revelant links if so. - """ - changed = self._check_available_segments_changed_post_save(lang=self.lang) - - if len(changed): - from . import link - for change in changed: - link.update_link_language_availabiliy(change[0], self.lang, change[1]) - - def get_language(self): - #method for getting a different lang attribue in old TextChunck and RangeText - #can be removed after merging and all uses can be replaced by self.acutal_lang - return self.lang - - class VirtualTextChunk(AbstractTextRecord): """ Delegated from TextChunk From e90b0e7e8ef9ecef8642412a349a7a530ce24051 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Wed, 23 Aug 2023 15:17:56 +0300 Subject: [PATCH 147/756] feat(api texts): new TextRange class for getting text. --- sefaria/model/text.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index 8ca31587f5..54254f0867 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -1739,6 +1739,29 @@ def __call__(cls, *args, **kwargs): return super(TextFamilyDelegator, cls).__call__(*args, **kwargs) +class TextRange(AbstractTextRecord, metaclass=TextFamilyDelegator): + + def __init__(self, oref, lang, vtitle): + if isinstance(oref.index_node, JaggedArrayNode): + self.oref = oref + else: + child_ref = oref.default_child_ref() + if child_ref == oref: + raise InputError("Can not get TextChunk at this level, please provide a more precise reference") + self.oref = child_ref + self.lang = lang + self.vtitle = vtitle + self._text = None + + def set_text(self): + self._text = Version().load({'actualLanguage': self.lang, 'versionTitle': self.vtitle}, self.oref.part_projection()) + + def get_text(self): + if not self._text: + self.set_text() + return self._text + + class TextChunk(AbstractTextRecord, metaclass=TextFamilyDelegator): """ A chunk of text corresponding to the provided :class:`Ref`, language, and optional version name. From 60fdb8a370313852e53b801643e49f51ddbe73ab Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Wed, 23 Aug 2023 16:00:50 +0300 Subject: [PATCH 148/756] chore: dependency function for places in Topic and Index --- reader/views.py | 20 ++++++++++---- sefaria/helper/topic.py | 27 ++++--------------- sefaria/model/dependencies.py | 5 +++- sefaria/model/place.py | 24 ++++++++++++++++- sefaria/model/text.py | 8 ++++++ sefaria/model/topic.py | 2 +- static/js/BookPage.jsx | 49 ++++++++++++++++++++++++++++++----- 7 files changed, 98 insertions(+), 37 deletions(-) diff --git a/reader/views.py b/reader/views.py index 00b42b4a22..cb80702769 100644 --- a/reader/views.py +++ b/reader/views.py @@ -59,10 +59,11 @@ from sefaria.helper.search import get_query_obj from sefaria.helper.crm.crm_mediator import CrmMediator from sefaria.search import get_search_categories +from sefaria.model.place import process_obj_with_places from sefaria.helper.topic import get_topic, get_all_topics, get_topics_for_ref, get_topics_for_book, \ get_bulk_topics, recommend_topics, get_top_topic, get_random_topic, \ get_random_topic_source, edit_topic_source, \ - update_order_of_topic_sources, delete_ref_topic_link + update_order_of_topic_sources, delete_ref_topic_link, update_authors_data from sefaria.helper.community_page import get_community_page_items from sefaria.helper.file import get_resized_file from sefaria.image_generator import make_img_http_response @@ -1663,6 +1664,7 @@ def index_api(request, title, raw=False): if request.method == "POST": # use the update function if update is in the params + func = tracker.update if request.GET.get("update", False) else tracker.add j = json.loads(request.POST.get("json")) if not j: @@ -1680,7 +1682,10 @@ def index_api(request, title, raw=False): apikey = db.apikeys.find_one({"key": key}) if not apikey: return jsonResponse({"error": "Unrecognized API key."}) - return jsonResponse(func(apikey["uid"], Index, j, method="API", raw=raw).contents(raw=raw)) + index = func(apikey["uid"], Index, j, method="API", raw=raw) + results = jsonResponse(index.contents(raw=raw)) + process_obj_with_places(index, j, [("compPlace", "heCompPlace"), ("pubPlace", "hePubPlace")]) + return results else: title = j.get("oldTitle", j.get("title")) try: @@ -1692,9 +1697,11 @@ def index_api(request, title, raw=False): pass # if this is a new text, allow any logged in user to submit @csrf_protect def protected_index_post(request): - return jsonResponse( - func(request.user.id, Index, j, raw=raw).contents(raw=raw) - ) + index = func(request.user.id, Index, j, raw=raw) + results = jsonResponse(index.contents(raw=raw)) + process_obj_with_places(index, j, [("compPlace", "heCompPlace"), ("pubPlace", "hePubPlace")]) + return results + return protected_index_post(request) if request.method == "DELETE": @@ -3108,6 +3115,9 @@ def add_new_topic_api(request): new_link = IntraTopicLink({"toTopic": data["category"], "fromTopic": t.slug, "linkType": "displays-under", "dataSource": "sefaria"}) new_link.save() + if data["category"] == 'authors': + t = update_authors_data(t, data) + t.description_published = True t.data_source = "sefaria" # any topic edited manually should display automatically in the TOC and this flag ensures this t.change_description(data["description"], data.get("catDescription", None)) diff --git a/sefaria/helper/topic.py b/sefaria/helper/topic.py index 3b0c9ac99c..1302b4fba0 100644 --- a/sefaria/helper/topic.py +++ b/sefaria/helper/topic.py @@ -5,6 +5,7 @@ from collections import defaultdict from functools import cmp_to_key from sefaria.model import * +from sefaria.model.place import process_obj_with_places from sefaria.system.exceptions import InputError from sefaria.model.topic import TopicLinkHelper from sefaria.system.database import db @@ -1050,7 +1051,9 @@ def update_topic_titles(topic_obj, **kwargs): return topic_obj -def update_authors_era(topic_obj, **kwargs): +def update_authors_data(topic_obj, kwargs): + # create any new places added to author, then update year and era info + process_obj_with_places(topic_obj.properties, kwargs, [("birthPlace", "heBirthPlace"), ("deathPlace", "heDeathPlace")]) for k in ["birthYear", "deathYear"]: assert kwargs[k].is_digit(), f"'{k}' must be a number but is {kwargs[k]}" kwargs[k] = int(kwargs[k]) @@ -1063,25 +1066,6 @@ def update_authors_era(topic_obj, **kwargs): return topic_obj -def update_authors_place_data(topic_obj, **kwargs): - for en, he in [("birthPlace", "heBirthPlace"), ("deathPlace", "heDeathPlace")]: - new_val = kwargs.get(en, None) - old_val = getattr(topic_obj, en, None) - if new_val != old_val: - p = Place({'key': new_val}) - p.name_group.add_title(new_val, 'en', True, True) - he_new_val = kwargs.get(he) - if he_new_val: - p.name_group.add_title(he_new_val, 'he', True, True) - p.city_to_coordinates(new_val) - p.save() - properties = getattr(topic_obj, 'properties') - properties[en] = new_val - properties[he] = he_new_val - topic_obj.properties = properties - - return topic_obj - def update_topic(topic_obj, **kwargs): """ Can update topic object's title, hebrew title, category, description, and categoryDescription fields @@ -1098,8 +1082,7 @@ def update_topic(topic_obj, **kwargs): if 'category' in kwargs: if kwargs['category'] == 'authors': - topic_obj = update_authors_place_data(topic_obj, **kwargs) - topic_obj = update_authors_era(topic_obj, **kwargs) + topic_obj = update_authors_data(topic_obj, kwargs) orig_link = IntraTopicLink().load({"linkType": "displays-under", "fromTopic": topic_obj.slug, "toTopic": {"$ne": topic_obj.slug}}) old_category = orig_link.toTopic if orig_link else Topic.ROOT if old_category != kwargs['category']: diff --git a/sefaria/model/dependencies.py b/sefaria/model/dependencies.py index 29e86fab2e..f4937cf7f7 100644 --- a/sefaria/model/dependencies.py +++ b/sefaria/model/dependencies.py @@ -2,7 +2,7 @@ dependencies.py -- list cross model dependencies and subscribe listeners to changes. """ -from . import abstract, link, note, history, schema, text, layer, version_state, timeperiod, garden, notification, collection, library, category, ref_data, user_profile, manuscript, topic +from . import abstract, link, note, history, schema, text, layer, version_state, timeperiod, garden, notification, collection, library, category, ref_data, user_profile, manuscript, topic, place from .abstract import subscribe, cascade, cascade_to_list, cascade_delete, cascade_delete_to_list import sefaria.system.cache as scache @@ -11,6 +11,7 @@ subscribe(text.process_index_change_in_core_cache, text.Index, "save") subscribe(version_state.create_version_state_on_index_creation, text.Index, "save") subscribe(text.process_index_change_in_toc, text.Index, "save") +subscribe(place.process_obj_with_places, text.Index, "save") # Index Name Change @@ -74,6 +75,8 @@ def process_version_title_change_in_search(ver, **kwargs): subscribe(topic.process_topic_delete, topic.Topic, "delete") subscribe(topic.process_topic_description_change, topic.Topic, "attributeChange", "description") subscribe(topic.process_topic_delete, topic.AuthorTopic, "delete") +subscribe(place.process_obj_with_places, topic.AuthorTopic, "save") + # Terms # TODO cascade change to Term.name. diff --git a/sefaria/model/place.py b/sefaria/model/place.py index 51d009ec48..ed7e8838f1 100644 --- a/sefaria/model/place.py +++ b/sefaria/model/place.py @@ -3,7 +3,6 @@ from . import abstract as abst from . import schema from sefaria.system.exceptions import InputError - import structlog from geopy.geocoders import Nominatim logger = structlog.get_logger(__name__) @@ -47,6 +46,15 @@ def primary_name(self, lang=None): def secondary_names(self, lang=None): return self.name_group.secondary_titles(lang) + + @classmethod + def create_new_place(cls, en, he=None): + p = Place({'key': en}) + p.name_group.add_title(en, 'en', True, True) + if he: + p.name_group.add_title(he, 'he', True, True) + p.city_to_coordinates(en) + p.save() def city_to_coordinates(self, city): geolocator = Nominatim(user_agent=self.key) @@ -84,3 +92,17 @@ def asGeoJson(self, with_polygons=False, as_string=False): return geojson.dumps(geojson.FeatureCollection(features)) else: return geojson.FeatureCollection(features) + + +def process_obj_with_places(obj_to_modify, new_obj_dict, keys): + # for Topics and Indexes, any modification of place attributes should create a new Place() + # 'obj_to_modify' is the original object instance and 'new_obj_dict' is a dictionary of the updated data + # 'keys' is a list of tuples where each tuple contains english and corresponding hebrew key such as ("birthPlace", "heBirthPlace") + for en, he in keys: + new_val = new_obj_dict.get(en, None) + he_new_val = new_obj_dict.get(he) + if Place().load({"key": new_val}) is None: + Place.create_new_place(en=new_val, he=he_new_val) + obj_to_modify[en] = new_val + obj_to_modify[he] = he_new_val + diff --git a/sefaria/model/text.py b/sefaria/model/text.py index 34312edfa3..5d1591553d 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -316,6 +316,7 @@ def expand_metadata_on_contents(self, contents): "he": composition_time_period.period_string("he"), } + composition_place = self.composition_place() if composition_place: contents["compPlaceString"] = { @@ -323,6 +324,13 @@ def expand_metadata_on_contents(self, contents): "he": composition_place.primary_name("he"), } + pub_place = self.publication_place() + if pub_place: + contents["pubPlaceString"] = { + "en": pub_place.primary_name("en"), + "he": pub_place.primary_name("he"), + } + return contents def _saveable_attrs(self): diff --git a/sefaria/model/topic.py b/sefaria/model/topic.py index 49199f4607..abf9a7a936 100644 --- a/sefaria/model/topic.py +++ b/sefaria/model/topic.py @@ -7,7 +7,7 @@ from sefaria.model.timeperiod import TimePeriod from sefaria.system.database import db import structlog, bleach -from sefaria.model.place import * +from sefaria.model.place import Place import regex as re logger = structlog.get_logger(__name__) diff --git a/static/js/BookPage.jsx b/static/js/BookPage.jsx index 4681bebd83..76d9af3a17 100644 --- a/static/js/BookPage.jsx +++ b/static/js/BookPage.jsx @@ -1065,6 +1065,11 @@ const EditTextInfo = function({initTitle, close}) { const [heDesc, setHeDesc] = useState(index.current?.heDesc || ""); const [heShortDesc, setHeShortDesc] = useState(index.current?.heShortDesc || ""); const [authors, setAuthors] = useState(index.current.authors?.map((item, i) =>({["name"]: item.en, ["slug"]: item.slug, ["id"]: i})) || []); + const [pubDate, setPubDate] = useState(index.current?.pubDate); + const [pubPlace, setPubPlace] = useState(index.current?.pubPlaceString?.en); + const [hePubPlace, setHePubPlace] = useState(index.current?.pubPlaceString?.he); + const [compPlace, setCompPlace] = useState(index.current?.compPlaceString?.en); + const [heCompPlace, setHeCompPlace] = useState(index.current?.compPlaceString?.he); const [errorMargin, setErrorMargin] = useState(Number(index.current?.errorMargin) || 0); const getYearAsStr = (initCompDate) => { if (typeof initCompDate === 'undefined' || initCompDate === '') { @@ -1090,7 +1095,7 @@ const EditTextInfo = function({initTitle, close}) { } } const [compDate, setCompDate] = useState(index.current?.compDate); - const [compDateStr, setCompDateStr] = useState(getYearAsStr(compDate)); + const initCompDate = useState(getYearAsStr(compDate)); //init comp date to display const toggleInProgress = function() { setSavingStatus(savingStatus => !savingStatus); @@ -1144,7 +1149,7 @@ const EditTextInfo = function({initTitle, close}) { let result = newValue.match(pattern); if (!result) { setErrorMargin(0); - setCompDate(0); + setCompDate(''); } else if (result[2] === "-") { const start = Number.parseInt(result[1]); @@ -1177,14 +1182,18 @@ const EditTextInfo = function({initTitle, close}) { const heTitleVariantNames = heTitleVariants.map(i => i.name); const authorSlugs = authors.map(i => i.slug); let postIndex = {title: enTitle, authors: authorSlugs, titleVariants: enTitleVariantNames, heTitleVariants: heTitleVariantNames, - heTitle, categories, enDesc, enShortDesc, heDesc, heShortDesc} + heTitle, categories, enDesc, enShortDesc, heDesc, heShortDesc, pubPlace, compPlace, hePubPlace, heCompPlace + } if (sections && sections.length > 0) { postIndex.sectionNames = sections; } if (enTitle !== oldTitle) { postIndex.oldTitle = oldTitle; } - if (compDateStr !== "") { + if (pubDate != index.current?.pubDate) { + postIndex.pubDate = pubDate; + } + if (compDate != initCompDate) { postIndex.compDate = compDate; postIndex.errorMargin = errorMargin; } @@ -1208,8 +1217,9 @@ const EditTextInfo = function({initTitle, close}) { window.location.href = "/admin/reset/"+index.current.title; // often this occurs when save occurs successfully but there is simply a timeout on cauldron so try resetting it }); }; - const validateThenSave = function () { - if (validate()) { + const validateThenSave = async function () { + const valid = await validate(); + if (valid) { save(); } } @@ -1296,8 +1306,33 @@ const EditTextInfo = function({initTitle, close}) { </div> : null} <div className="section"> <div><InterfaceText>Completion Year</InterfaceText></div><label><span className="optional"><InterfaceText>Optional. Provide a range if there is an error margin or the work was completed over the course of many years such as 1797-1800 or -900--200 (to denote 900 BCE to 200 BCE).</InterfaceText></span></label> - <br/><input id="compDate" onBlur={(e) => validateCompDate(e.target.value)} defaultValue={compDateStr}/> + <br/><input id="compDate" onBlur={(e) => validateCompDate(e.target.value)} defaultValue={initCompDate}/> + </div> + <div className="section"> + <div><InterfaceText>Place of Composition</InterfaceText></div> + <label><span className="optional"><InterfaceText>Optional</InterfaceText></span></label> + <input id="compPlace" onBlur={setCompPlace} defaultValue={compPlace}/> </div> + {Sefaria._siteSettings.TORAH_SPECIFIC && + <div className="section"> + <div><InterfaceText>Hebrew Place of Composition</InterfaceText></div><label> + <span className="optional"><InterfaceText>Optional</InterfaceText></span></label> + <input id="heCompPlace" onBlur={setHeCompPlace} defaultValue={heCompPlace}/> + </div>} + <div className="section"> + <div><InterfaceText>Publication Year</InterfaceText></div><label><span className="optional"><InterfaceText>Optional. Provide a range if there is an error margin or the work was completed over the course of many years such as 1797-1800 or -900--200 (to denote 900 BCE to 200 BCE).</InterfaceText></span></label> + <input id="pubDate" onBlur={setPubDate} defaultValue={pubDate}/> + </div> + <div className="section"> + <div><InterfaceText>Place of Publication</InterfaceText></div><label><span className="optional"><InterfaceText>Optional</InterfaceText></span></label> + <input id="pubPlace" onBlur={setPubPlace} defaultValue={pubPlace}/> + </div> + {Sefaria._siteSettings.TORAH_SPECIFIC && + <div className="section"> + <div><InterfaceText>Hebrew Place of Publication</InterfaceText></div> + <label><span className="optional"><InterfaceText>Optional</InterfaceText></span></label> + <input id="hePubPlace" onBlur={setHePubPlace} defaultValue={hePubPlace}/> + </div>} {index.current.hasOwnProperty("sectionNames") ? <div className="section"> <div><label><InterfaceText>Text Structure</InterfaceText></label></div> From 8750229884d170d539295712dacc5f9eebc8c486 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Wed, 23 Aug 2023 17:57:20 +0300 Subject: [PATCH 149/756] fix(api texts): fix getting text by TextRange. --- sefaria/model/text.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index 54254f0867..83cdb8858c 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -1751,10 +1751,34 @@ def __init__(self, oref, lang, vtitle): self.oref = child_ref self.lang = lang self.vtitle = vtitle + self._version = None #todo - what we want to do if the version doesnt exist. for now the getter will cause error self._text = None + def set_version(self): + self._version = Version().load({'actualLanguage': self.lang, 'versionTitle': self.vtitle}, self.oref.part_projection()) + + def get_version(self): + if not self._version: + self.set_version() + return self._version + + def _trim_text(self, text): + """ + part_projection trims only the upper level of the jagged array. this function trims its lower levels and get rid of 1 element arrays wrappings + """ + for s, section in enumerate(self.oref.toSections[1:], 1): #start cut form end, for cutting from the start will change the indexes + subtext = reduce(lambda x, _: x[-1], range(s), text) + del subtext[section:] + for s, section in enumerate(self.oref.sections[1:], 1): + subtext = reduce(lambda x, _: x[0], range(s), text) + del subtext[:section-1] + matching_sections = itertools.takewhile(lambda pair: pair[0] == pair[1], zip(self.oref.sections, self.oref.toSections)) + redundant_depth = len(list(matching_sections)) + return reduce(lambda x, _: x[0], range(redundant_depth), text) + def set_text(self): - self._text = Version().load({'actualLanguage': self.lang, 'versionTitle': self.vtitle}, self.oref.part_projection()) + self._text = self._trim_text(self.get_version().content_node(self.oref.index_node)) + return self._text def get_text(self): if not self._text: From 02f703a54f919512e5beb6e43bfeefca3ed65b6d Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Thu, 24 Aug 2023 16:06:34 +0300 Subject: [PATCH 150/756] chore: everything in topics works except for alt titles --- reader/views.py | 10 +++----- sefaria/helper/topic.py | 45 +++++++++++++++++++++------------- sefaria/model/dependencies.py | 2 -- sefaria/model/place.py | 18 ++++++++------ sefaria/model/topic.py | 18 +++++++------- static/js/AdminEditor.jsx | 5 ++-- static/js/Misc.jsx | 25 ++++++++++--------- static/js/TopicEditor.jsx | 46 ++++++++++++++++++----------------- 8 files changed, 92 insertions(+), 77 deletions(-) diff --git a/reader/views.py b/reader/views.py index cb80702769..51467ce98a 100644 --- a/reader/views.py +++ b/reader/views.py @@ -79,7 +79,7 @@ from sefaria.utils.user import delete_user_account from django.core.mail import EmailMultiAlternatives from babel import Locale -from sefaria.helper.topic import update_topic +from sefaria.helper.topic import update_topic, update_topic_titles from sefaria.helper.category import update_order_of_category_children, check_term if USE_VARNISH: @@ -1684,7 +1684,7 @@ def index_api(request, title, raw=False): return jsonResponse({"error": "Unrecognized API key."}) index = func(apikey["uid"], Index, j, method="API", raw=raw) results = jsonResponse(index.contents(raw=raw)) - process_obj_with_places(index, j, [("compPlace", "heCompPlace"), ("pubPlace", "hePubPlace")]) + process_obj_with_places(index, j, [("compPlace", "heCompPlace"), ("pubPlace", "hePubPlace")], dataSource='learning-team-editing-tool') # create place if new places added return results else: title = j.get("oldTitle", j.get("title")) @@ -1699,7 +1699,7 @@ def index_api(request, title, raw=False): def protected_index_post(request): index = func(request.user.id, Index, j, raw=raw) results = jsonResponse(index.contents(raw=raw)) - process_obj_with_places(index, j, [("compPlace", "heCompPlace"), ("pubPlace", "hePubPlace")]) + process_obj_with_places(index, j, [("compPlace", "heCompPlace"), ("pubPlace", "hePubPlace")]) # create place if new places added return results return protected_index_post(request) @@ -3107,9 +3107,7 @@ def add_new_topic_api(request): data = json.loads(request.POST["json"]) isTopLevelDisplay = data["category"] == Topic.ROOT t = Topic({'slug': "", "isTopLevelDisplay": isTopLevelDisplay, "data_source": "sefaria", "numSources": 0}) - t.add_title(data["title"], 'en', True, True) - if "heTitle" in data: - t.add_title(data["heTitle"], "he", True, True) + update_topic_titles(t, data) if not isTopLevelDisplay: # not Top Level so create an IntraTopicLink to category new_link = IntraTopicLink({"toTopic": data["category"], "fromTopic": t.slug, "linkType": "displays-under", "dataSource": "sefaria"}) diff --git a/sefaria/helper/topic.py b/sefaria/helper/topic.py index 1302b4fba0..d4453f56bc 100644 --- a/sefaria/helper/topic.py +++ b/sefaria/helper/topic.py @@ -1039,33 +1039,44 @@ def topic_change_category(topic_obj, new_category, old_category="", rebuild=Fals rebuild_topic_toc(topic_obj, category_changed=True) return topic_obj -def update_topic_titles(topic_obj, **kwargs): +def update_topic_titles(topic_obj, data): for lang in ['en', 'he']: for title in topic_obj.get_titles(lang): topic_obj.remove_title(title, lang) - for title in kwargs['altTitles'][lang]: + for title in data['altTitles'][lang]: topic_obj.add_title(title, lang) - topic_obj.add_title(kwargs['title'], 'en', True, True) - topic_obj.add_title(kwargs['heTitle'], 'he', True, True) + topic_obj.add_title(data['title'], 'en', True, True) + topic_obj.add_title(data['heTitle'], 'he', True, True) return topic_obj -def update_authors_data(topic_obj, kwargs): +def update_authors_data(topic_obj, data, dataSource='learning-team-editing-tool'): # create any new places added to author, then update year and era info - process_obj_with_places(topic_obj.properties, kwargs, [("birthPlace", "heBirthPlace"), ("deathPlace", "heDeathPlace")]) - for k in ["birthYear", "deathYear"]: - assert kwargs[k].is_digit(), f"'{k}' must be a number but is {kwargs[k]}" - kwargs[k] = int(kwargs[k]) - setattr(topic_obj, k, kwargs[k]) + keys = [("birthPlace", "heBirthPlace"), ("deathPlace", "heDeathPlace")] + if not hasattr(topic_obj, 'properties'): + topic_obj.properties = {} + topic_obj.properties = process_obj_with_places(topic_obj.properties, data, keys, dataSource=dataSource) - prev_era = getattr(topic_obj, 'era', None) - if 'era' in kwargs and prev_era != kwargs['era']: - setattr(topic_obj, 'era', kwargs['era']) - create_era_link(topic, prev_era_to_delete=prev_era) + topic_obj = update_author_era(topic_obj, data, dataSource='learning-team-editing-tool') return topic_obj +def update_author_era(topic_obj, data, dataSource='learning-team-editing-tool'): + for k in ["birthYear", "deathYear"]: + year = data.get(k) + if year: + assert isinstance(year, int) or year.isdigit(), f"'{k}' must be a number but is {year}" + year = int(year) + topic_obj.properties[k] = {'value': year, 'dataSource': dataSource} + + prev_era = topic_obj.properties.get('era', {}).get('value') + if data.get('era', '') != '' and prev_era != data['era']: + topic_obj.properties['era'] = {'value': data['era'], 'dataSource': dataSource} + create_era_link(topic_obj, prev_era_to_delete=prev_era) + return topic_obj + + def update_topic(topic_obj, **kwargs): """ Can update topic object's title, hebrew title, category, description, and categoryDescription fields @@ -1078,11 +1089,11 @@ def update_topic(topic_obj, **kwargs): old_category = "" orig_slug = topic_obj.slug - update_topic_titles(topic_obj, **kwargs) + update_topic_titles(topic_obj, kwargs) + if kwargs.get('catSlug') == 'authors': + topic_obj = update_authors_data(topic_obj, kwargs) if 'category' in kwargs: - if kwargs['category'] == 'authors': - topic_obj = update_authors_data(topic_obj, kwargs) orig_link = IntraTopicLink().load({"linkType": "displays-under", "fromTopic": topic_obj.slug, "toTopic": {"$ne": topic_obj.slug}}) old_category = orig_link.toTopic if orig_link else Topic.ROOT if old_category != kwargs['category']: diff --git a/sefaria/model/dependencies.py b/sefaria/model/dependencies.py index f4937cf7f7..1201de3370 100644 --- a/sefaria/model/dependencies.py +++ b/sefaria/model/dependencies.py @@ -11,7 +11,6 @@ subscribe(text.process_index_change_in_core_cache, text.Index, "save") subscribe(version_state.create_version_state_on_index_creation, text.Index, "save") subscribe(text.process_index_change_in_toc, text.Index, "save") -subscribe(place.process_obj_with_places, text.Index, "save") # Index Name Change @@ -75,7 +74,6 @@ def process_version_title_change_in_search(ver, **kwargs): subscribe(topic.process_topic_delete, topic.Topic, "delete") subscribe(topic.process_topic_description_change, topic.Topic, "attributeChange", "description") subscribe(topic.process_topic_delete, topic.AuthorTopic, "delete") -subscribe(place.process_obj_with_places, topic.AuthorTopic, "save") # Terms diff --git a/sefaria/model/place.py b/sefaria/model/place.py index ed7e8838f1..c83d804f08 100644 --- a/sefaria/model/place.py +++ b/sefaria/model/place.py @@ -55,11 +55,12 @@ def create_new_place(cls, en, he=None): p.name_group.add_title(he, 'he', True, True) p.city_to_coordinates(en) p.save() + return p def city_to_coordinates(self, city): - geolocator = Nominatim(user_agent=self.key) + geolocator = Nominatim(user_agent='hello@sefaria.org') location = geolocator.geocode(city) - self.point_location(location) + self.point_location(lon=location.longitude, lat=location.latitude) def point_location(self, lon=None, lat=None): if lat is None and lon is None: @@ -94,15 +95,18 @@ def asGeoJson(self, with_polygons=False, as_string=False): return geojson.FeatureCollection(features) -def process_obj_with_places(obj_to_modify, new_obj_dict, keys): +def process_obj_with_places(obj_to_modify, new_obj_dict, keys, dataSource='sefaria'): # for Topics and Indexes, any modification of place attributes should create a new Place() # 'obj_to_modify' is the original object instance and 'new_obj_dict' is a dictionary of the updated data # 'keys' is a list of tuples where each tuple contains english and corresponding hebrew key such as ("birthPlace", "heBirthPlace") for en, he in keys: new_val = new_obj_dict.get(en, None) he_new_val = new_obj_dict.get(he) - if Place().load({"key": new_val}) is None: - Place.create_new_place(en=new_val, he=he_new_val) - obj_to_modify[en] = new_val - obj_to_modify[he] = he_new_val + if new_val != '': + place = Place().load({"key": new_val}) + if place is None: + place = Place.create_new_place(en=new_val, he=he_new_val) + obj_to_modify[en] = {'value': place.primary_name('en'), 'dataSource': dataSource} + obj_to_modify[he] = {'value': place.primary_name('he'), 'dataSource': dataSource} + return obj_to_modify diff --git a/sefaria/model/topic.py b/sefaria/model/topic.py index abf9a7a936..0cf82d38d8 100644 --- a/sefaria/model/topic.py +++ b/sefaria/model/topic.py @@ -402,15 +402,15 @@ def get_person_by_key(key: str): return PersonTopic().load({"alt_ids.old-person-key": key}) def annotate_place(self, d): - birthPlace = getattr(self, "birthPlace", None) - deathPlace = getattr(self, "deathPlace", None) - if birthPlace: - heBirthPlace = Place().load({"key": birthPlace}) - heDeathPlace = Place().load({"key": deathPlace}) - d['properties']['heBirthPlace'] = {'value': heBirthPlace.primary_name('he'), - 'dataSource': d['properties']['birthPlace']['dataSource']} - d['properties']['heDeathPlace'] = {'value': heDeathPlace.primary_name('he'), - 'dataSource': d['properties']['deathPlace']['dataSource']} + properties = d.get('properties', {}) + for k in ['birthPlace', 'deathPlace']: + place = properties.get(k) + heKey = 'he' + k[0].upper() + k[1:] # birthPlace => heBirthPlace + if place and heKey not in properties: + value, dataSource = place['value'], place['dataSource'] + place_obj = Place().load({"key": value}) + name = place_obj.primary_name('he') + d['properties'][heKey] = {'value': name, 'dataSource': dataSource} return d def contents(self, **kwargs): diff --git a/static/js/AdminEditor.jsx b/static/js/AdminEditor.jsx index cfc1e5f31a..765023c302 100644 --- a/static/js/AdminEditor.jsx +++ b/static/js/AdminEditor.jsx @@ -126,8 +126,9 @@ const AdminEditor = ({title, data, close, catMenu, updateData, savingStatus, updateData({...data}); } const handleTitleVariants = (newTitles, field) => { - data[field] = newTitles; - updateData({...data}); + const newData = {...data}; + newData[field] = [...newTitles]; + updateData(newData); } const preprocess = async () => { setValidatingLinks(true); diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 9d9dc7af5b..3d5c5d0395 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -1185,6 +1185,9 @@ const ReorderEditorWrapper = ({toggle, type, data}) => { } const EditorForExistingTopic = ({ toggle, data }) => { + const prepAltTitles = (lang) => { + return data.titles.filter(x => !x.primary && x.lang === lang).map((item, i) => ({["name"]: item.text, ["id"]: i})) + } const initCatSlug = TopicToCategorySlug(data); const origData = { origSlug: data.slug, @@ -1193,15 +1196,15 @@ const EditorForExistingTopic = ({ toggle, data }) => { origHe: data.primaryTitle.he || "", origDesc: data.description || {"en": "", "he": ""}, origCategoryDesc: data.categoryDescription || {"en": "", "he": ""}, - origEnAltTitles: data.titles.filter(x => !x.primary && x.lang === 'en').map(x => x.text), - origHeAltTitles: data.titles.filter(x => !x.primary && x.lang === 'he').map(x => x.text), - origBirthPlace: data?.properties?.birthPlace.value, - origHeBirthPlace: data?.properties?.heBirthPlace.value, - origHeDeathPlace: data?.properties?.heDeathPlace.value, - origBirthYear: data?.properties?.birthYear.value, - origDeathPlace: data?.properties?.deathPlace.value, - origDeathYear: data?.properties?.deathYear.value, - origEra: data?.properties?.era.value + origEnAltTitles: prepAltTitles('en'), + origHeAltTitles: prepAltTitles('he'), + origBirthPlace: data?.properties?.birthPlace?.value, + origHeBirthPlace: data?.properties?.heBirthPlace?.value, + origHeDeathPlace: data?.properties?.heDeathPlace?.value, + origBirthYear: data?.properties?.birthYear?.value, + origDeathPlace: data?.properties?.deathPlace?.value, + origDeathYear: data?.properties?.deathYear?.value, + origEra: data?.properties?.era?.value }; const origWasCat = "displays-above" in data?.links; @@ -1210,7 +1213,6 @@ const EditorForExistingTopic = ({ toggle, data }) => { <TopicEditor origData={origData} origWasCat={origWasCat} - onCreateSuccess={(slug) => window.location.href = `"/topics/"${slug}`} close={toggle} /> ); @@ -1261,8 +1263,7 @@ const CategoryAdderWrapper = ({toggle, data, type}) => { return <CategoryEditor origData={origData} close={toggle} origPath={data}/>; case "topics": origData['origCategorySlug'] = data; - return <TopicEditor origData={origData} close={toggle} origWasCat={false} - onCreateSuccess={(slug) => window.location.href = "/topics/" + slug}/>; + return <TopicEditor origData={origData} close={toggle} origWasCat={false}/>; } } diff --git a/static/js/TopicEditor.jsx b/static/js/TopicEditor.jsx index c316d38ae0..33f93cbdb9 100644 --- a/static/js/TopicEditor.jsx +++ b/static/js/TopicEditor.jsx @@ -3,7 +3,7 @@ import {InterfaceText, requestWithCallBack, TitleVariants, ToggleSet} from "./Mi import $ from "./sefaria/sefariaJquery"; import {AdminEditor} from "./AdminEditor"; import {Reorder} from "./CategoryEditor"; -import React, {useState} from "react"; +import React, {useState, useEffect} from "react"; const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { @@ -12,9 +12,12 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { enDescription: origData?.origDesc?.en || "", enCategoryDescription: origData?.origCategoryDesc?.en, heCategoryDescription: origData?.origCategoryDesc?.he, - enAltTitles: origData.origEnAltTitles, heAltTitles: origData.origHeAltTitles, - birthPlace: origData.origBirthPlace, birthYear: origData.origBirthYear, - deathYear: origData.origDeathYear, era: origData.origEra, deathPlace: origData.origDeathPlace + enAltTitles: origData?.origEnAltTitles || [], + heAltTitles: origData?.origHeAltTitles || [], + birthPlace: origData.origBirthPlace || "", heBirthPlace: origData.origHeBirthPlace || "", + birthYear: origData.origBirthYear || "", heDeathPlace: origData.origHeDeathPlace || "", + deathYear: origData.origDeathYear || "", era: origData.origEra || "", + deathPlace: origData.origDeathPlace || "" }); const isNew = !('origSlug' in origData); const [savingStatus, setSavingStatus] = useState(false); @@ -26,6 +29,9 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { .filter(x => x.slug !== origData.origSlug) // dont include topics that are self-linked || []); const [isChanged, setIsChanged] = useState(false); + useEffect(() => { + console.log('Y has changed', data.enAltTitles); + }, [data.enAltTitles]); const toggle = function() { setSavingStatus(savingStatus => !savingStatus); @@ -38,7 +44,7 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { const newIsCategory = origWasCat || e.target.value === Sefaria._("Main Menu"); setIsCategory(newIsCategory); setIsAuthor(data.catSlug === 'authors'); - setData(data); + setData({...data}); } let slugsToTitles = Sefaria.slugsToTitles(); @@ -76,7 +82,7 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { alert(Sefaria._("Title must be provided.")); return false; } - if (data?.era && Sefaria.util.inArray(data.era, Sefaria._eras)) { + if (data?.era && Sefaria.util.inArray(data.era, Sefaria._eras) === -1) { alert(`Era ${data.era} is invalid.`); return false; } @@ -108,18 +114,9 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { if (isCategory) { postData = {...postData, "catDescription": {"en": data.enCategoryDescription, "he": data.heCategoryDescription}}; } - - if (isAuthor) { - postData.altTitles = {}; - postData.altTitles.en = postData.enAltTitles.map(x => x.name); - postData.altTitles.he = postData.heAltTitles.map(x => x.name); - } - else { - authorItems('keys').forEach(x => { // we don't want to post these keys since they are only for authors - delete postData[x]; - }) - } - + postData.altTitles = {}; + postData.altTitles.en = postData.enAltTitles.map(x => x.name); + postData.altTitles.he = postData.heAltTitles.map(x => x.name); postData.category = data.catSlug; let url = ""; if (isNew) { @@ -140,8 +137,8 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { const saveTopic = function () { toggle(); const [postData, url] = prepData(); - const postJSON = JSON.stringify(postData); if (onCreateSuccess) { // used by TopicSearch.jsx + const postJSON = JSON.stringify(postData); $.post(url, {"json": postJSON}, function (result) { if (result.error) { toggle(); @@ -157,7 +154,7 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { else { requestWithCallBack({ url, - data: postJSON, + data: postData, setSavingStatus, redirect: () => window.location.href = `/topics/${postData.slug}` }); @@ -168,7 +165,11 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { const url = `/api/topic/delete/${data.origSlug}`; requestWithCallBack({url, type: "DELETE", redirect: () => window.location.href = "/topics"}); } - + // const handleTitleVariants = (newTitles, field) => { + // const newData = {...data}; + // newData[field] = [...newTitles]; + // updateData(newData); + // } let items = ["Title", "Hebrew Title", "English Alternate Titles", "Hebrew Alternate Titles", "Category Menu", "English Description", "Hebrew Description"]; if (isCategory) { items.push("English Short Description"); @@ -183,7 +184,8 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { [isNew ? null : <Reorder subcategoriesAndBooks={sortedSubtopics} updateOrder={setSortedSubtopics} - displayType="topics"/> + displayType="topics"/>, + // <TitleVariants update={(newTitles) => handleTitleVariants(newTitles, field)} titles={titles} id={field}/> ] } />; } From a7139039a240d7e9966e64a751904e89c498d35a Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Thu, 24 Aug 2023 13:20:45 -0400 Subject: [PATCH 151/756] Switch to use localStorage instead of sessionStorage for banners and modals --- static/js/Misc.jsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 3ba4d3cad7..0d4a2c242f 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -2095,11 +2095,11 @@ const InterruptingMessage = ({ const showDelay = 5000; const markModalAsHasBeenInteractedWith = (modalName) => { - sessionStorage.setItem("modal_" + modalName, "true"); + localStorage.setItem("modal_" + modalName, "true"); }; const hasModalBeenInteractedWith = (modalName) => { - return JSON.parse(sessionStorage.getItem("modal_" + modalName)); + return JSON.parse(localStorage.getItem("modal_" + modalName)); }; const trackModalInteraction = (modalName, eventDescription) => { @@ -2265,11 +2265,11 @@ const Banner = ({ onClose }) => { const showDelay = 5000; const markBannerAsHasBeenInteractedWith = (bannerName) => { - sessionStorage.setItem("banner_" + bannerName, "true"); + localStorage.setItem("banner_" + bannerName, "true"); }; const hasBannerBeenInteractedWith = (bannerName) => { - return JSON.parse(sessionStorage.getItem("banner_" + bannerName)); + return JSON.parse(localStorage.getItem("banner_" + bannerName)); }; const trackBannerInteraction = (bannerName, eventDescription) => { From e8e60809604879db7af9c6cd0af086b3d05e2faf Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Fri, 25 Aug 2023 12:23:26 -0400 Subject: [PATCH 152/756] Make the banner compatible with the old InterruptingMessage code --- static/js/ReaderApp.jsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index 92fe1e1f46..017607553a 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -2188,8 +2188,7 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { <AdContext.Provider value={this.getUserContext()}> <div id="readerAppWrap"> <InterruptingMessage /> - {/* <Banner onClose={this.setContainerMode} /> */} - <Banner /> + <Banner onClose={this.setContainerMode} /> */} <div className={classes} onClick={this.handleInAppLinkClick}> {header} {panels} From e44624f29e1f1e997fd4a9ff4d5a5acb4229ebf2 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Fri, 25 Aug 2023 19:00:47 -0400 Subject: [PATCH 153/756] Solve the bug where a linebreak is not at the end of the text content --- static/js/context.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/context.js b/static/js/context.js index 3d53a9a520..c2694f2fcc 100644 --- a/static/js/context.js +++ b/static/js/context.js @@ -22,7 +22,7 @@ const transformValues = (obj, callback) => { export function replaceNewLinesWithLinebreaks(content) { return transformValues( content, - (s) => s.replace(/\n/gi, "  \n") + "  \n" + (s) => s.replace(/\n/gi, "  \n") + "  \n  \n" ); } From 5e5baab1b293cfb6d3781d1894eb28803c2ddd8c Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Fri, 25 Aug 2023 19:01:35 -0400 Subject: [PATCH 154/756] Preserve same behavior as before for when banners are closed to handle containers --- static/js/ReaderApp.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index 017607553a..b04c437228 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -2188,7 +2188,7 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { <AdContext.Provider value={this.getUserContext()}> <div id="readerAppWrap"> <InterruptingMessage /> - <Banner onClose={this.setContainerMode} /> */} + <Banner onClose={this.setContainerMode} /> <div className={classes} onClick={this.handleInAppLinkClick}> {header} {panels} From 8188b845c6bce8200927377edfc900dcae0d8c23 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Sun, 27 Aug 2023 15:04:01 +0300 Subject: [PATCH 155/756] feat(Admin Editor): topics and indices can be edited with place info --- reader/views.py | 3 -- sefaria/helper/topic.py | 17 +++----- sefaria/model/dependencies.py | 3 +- sefaria/model/place.py | 32 ++++++++------ sefaria/model/text.py | 2 +- sefaria/model/topic.py | 2 +- static/js/AdminEditor.jsx | 8 ++-- static/js/BookPage.jsx | 12 +++--- static/js/Misc.jsx | 2 +- static/js/TopicEditor.jsx | 80 ++++++++++++++--------------------- 10 files changed, 72 insertions(+), 89 deletions(-) diff --git a/reader/views.py b/reader/views.py index 51467ce98a..334969db26 100644 --- a/reader/views.py +++ b/reader/views.py @@ -59,7 +59,6 @@ from sefaria.helper.search import get_query_obj from sefaria.helper.crm.crm_mediator import CrmMediator from sefaria.search import get_search_categories -from sefaria.model.place import process_obj_with_places from sefaria.helper.topic import get_topic, get_all_topics, get_topics_for_ref, get_topics_for_book, \ get_bulk_topics, recommend_topics, get_top_topic, get_random_topic, \ get_random_topic_source, edit_topic_source, \ @@ -1684,7 +1683,6 @@ def index_api(request, title, raw=False): return jsonResponse({"error": "Unrecognized API key."}) index = func(apikey["uid"], Index, j, method="API", raw=raw) results = jsonResponse(index.contents(raw=raw)) - process_obj_with_places(index, j, [("compPlace", "heCompPlace"), ("pubPlace", "hePubPlace")], dataSource='learning-team-editing-tool') # create place if new places added return results else: title = j.get("oldTitle", j.get("title")) @@ -1699,7 +1697,6 @@ def index_api(request, title, raw=False): def protected_index_post(request): index = func(request.user.id, Index, j, raw=raw) results = jsonResponse(index.contents(raw=raw)) - process_obj_with_places(index, j, [("compPlace", "heCompPlace"), ("pubPlace", "hePubPlace")]) # create place if new places added return results return protected_index_post(request) diff --git a/sefaria/helper/topic.py b/sefaria/helper/topic.py index d4453f56bc..fac4614551 100644 --- a/sefaria/helper/topic.py +++ b/sefaria/helper/topic.py @@ -5,7 +5,7 @@ from collections import defaultdict from functools import cmp_to_key from sefaria.model import * -from sefaria.model.place import process_obj_with_places +from sefaria.model.place import process_topic_place_change from sefaria.system.exceptions import InputError from sefaria.model.topic import TopicLinkHelper from sefaria.system.database import db @@ -1052,23 +1052,20 @@ def update_topic_titles(topic_obj, data): def update_authors_data(topic_obj, data, dataSource='learning-team-editing-tool'): - # create any new places added to author, then update year and era info - keys = [("birthPlace", "heBirthPlace"), ("deathPlace", "heDeathPlace")] + # update place info added to author, then update year and era info if not hasattr(topic_obj, 'properties'): topic_obj.properties = {} - topic_obj.properties = process_obj_with_places(topic_obj.properties, data, keys, dataSource=dataSource) - + process_topic_place_change(topic_obj, data) topic_obj = update_author_era(topic_obj, data, dataSource='learning-team-editing-tool') return topic_obj def update_author_era(topic_obj, data, dataSource='learning-team-editing-tool'): for k in ["birthYear", "deathYear"]: - year = data.get(k) - if year: - assert isinstance(year, int) or year.isdigit(), f"'{k}' must be a number but is {year}" - year = int(year) - topic_obj.properties[k] = {'value': year, 'dataSource': dataSource} + year = data.get(k, '') # set to None if blank, otherwise validate number + if year != '': + assert int(year), f"'{k}' must be a number but is {year}" + topic_obj.properties[k] = {'value': year, 'dataSource': dataSource} prev_era = topic_obj.properties.get('era', {}).get('value') if data.get('era', '') != '' and prev_era != data['era']: diff --git a/sefaria/model/dependencies.py b/sefaria/model/dependencies.py index 1201de3370..eb842a16bb 100644 --- a/sefaria/model/dependencies.py +++ b/sefaria/model/dependencies.py @@ -11,7 +11,8 @@ subscribe(text.process_index_change_in_core_cache, text.Index, "save") subscribe(version_state.create_version_state_on_index_creation, text.Index, "save") subscribe(text.process_index_change_in_toc, text.Index, "save") - +subscribe(place.process_index_place_change, text.Index, 'attributeChange', 'compPlace') +subscribe(place.process_index_place_change, text.Index, 'attributeChange', 'pubPlace') # Index Name Change subscribe(text.process_index_title_change_in_core_cache, text.Index, "attributeChange", "title") diff --git a/sefaria/model/place.py b/sefaria/model/place.py index c83d804f08..d4091a1012 100644 --- a/sefaria/model/place.py +++ b/sefaria/model/place.py @@ -49,6 +49,9 @@ def secondary_names(self, lang=None): @classmethod def create_new_place(cls, en, he=None): + p = Place().load({'key': en}) + if p: + return p p = Place({'key': en}) p.name_group.add_title(en, 'en', True, True) if he: @@ -95,18 +98,21 @@ def asGeoJson(self, with_polygons=False, as_string=False): return geojson.FeatureCollection(features) -def process_obj_with_places(obj_to_modify, new_obj_dict, keys, dataSource='sefaria'): - # for Topics and Indexes, any modification of place attributes should create a new Place() - # 'obj_to_modify' is the original object instance and 'new_obj_dict' is a dictionary of the updated data - # 'keys' is a list of tuples where each tuple contains english and corresponding hebrew key such as ("birthPlace", "heBirthPlace") - for en, he in keys: - new_val = new_obj_dict.get(en, None) - he_new_val = new_obj_dict.get(he) +def process_index_place_change(indx, **kwargs): + key = kwargs['attr'] + he_key = 'he' + key[0].upper() + key[1:] # birthPlace => heBirthPlace + he_new_val = getattr(indx, he_key, '') + Place.create_new_place(en=kwargs['new'], he=he_new_val) + +def process_topic_place_change(topic_obj, data): + keys = ["birthPlace", "deathPlace"] + for key in keys: + he_key = 'he' + key[0].upper() + key[1:] # birthPlace => heBirthPlace + he_new_val = data.get(he_key, '') + new_val = data.get(key, '') if new_val != '': - place = Place().load({"key": new_val}) - if place is None: - place = Place.create_new_place(en=new_val, he=he_new_val) - obj_to_modify[en] = {'value': place.primary_name('en'), 'dataSource': dataSource} - obj_to_modify[he] = {'value': place.primary_name('he'), 'dataSource': dataSource} - return obj_to_modify + place = Place.create_new_place(en=new_val, he=he_new_val) + topic_obj.properties[key] = {"value": place.primary_name('en'), 'dataSource': 'learning-team-editing-tool'} + else: + topic_obj.properties.pop(key, None) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index 5d1591553d..27babfefa0 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -182,7 +182,7 @@ class Index(abst.AbstractMongoRecord, AbstractIndex): criteria_field = 'title' criteria_override_field = 'oldTitle' # used when primary attribute changes. field that holds old value. track_pkeys = True - pkeys = ["title"] + pkeys = ["title", "compPlace", "pubPlace"] required_attrs = [ "title", diff --git a/sefaria/model/topic.py b/sefaria/model/topic.py index 0cf82d38d8..01a14073d6 100644 --- a/sefaria/model/topic.py +++ b/sefaria/model/topic.py @@ -21,7 +21,7 @@ class Topic(abst.SluggedAbstractMongoRecord, AbstractTitledObject): 'person': 'PersonTopic', 'author': 'AuthorTopic', } - pkeys = ["description"] + pkeys = ["description", 'properties'] track_pkeys = True reverse_subclass_map = {v: k for k, v in subclass_map.items()} required_attrs = [ diff --git a/static/js/AdminEditor.jsx b/static/js/AdminEditor.jsx index 765023c302..7948215bc3 100644 --- a/static/js/AdminEditor.jsx +++ b/static/js/AdminEditor.jsx @@ -127,7 +127,7 @@ const AdminEditor = ({title, data, close, catMenu, updateData, savingStatus, } const handleTitleVariants = (newTitles, field) => { const newData = {...data}; - newData[field] = [...newTitles]; + newData[field] = newTitles.map(x => Object.assign({}, x)); updateData(newData); } const preprocess = async () => { @@ -146,7 +146,7 @@ const AdminEditor = ({title, data, close, catMenu, updateData, savingStatus, setValidatingLinks(false); } const getDropdown = (field, dropdown_data, placeholder) => { - const chooseCat = <option key="chooseCategory" value={data.origEra} + const chooseCat = <option key={`chooseCategory_${field}`} value={data.origEra} selected={!dropdown_data.includes(data[field])}>{placeholder}</option>; return <div id={`dropdown_${field}`} className="categoryChooserMenu"> <select key={field} id={field} onChange={setInputValue}> @@ -169,11 +169,11 @@ const AdminEditor = ({title, data, close, catMenu, updateData, savingStatus, obj = <TitleVariants update={(newTitles) => handleTitleVariants(newTitles, field)} titles={titles} id={field}/>; break; case 'textarea': - obj = <textarea className="default" id={field} onBlur={setInputValue} defaultValue={data[field]} + obj = <textarea className="default" id={field} onChange={setInputValue} defaultValue={data[field]} placeholder={Sefaria._(placeholder)}/>; break; default: - obj = <input type='text' id={field} onBlur={setInputValue} defaultValue={data[field]} + obj = <input type='text' id={field} onChange={setInputValue} defaultValue={data[field]} placeholder={Sefaria._(placeholder)}/>; } diff --git a/static/js/BookPage.jsx b/static/js/BookPage.jsx index 76d9af3a17..485d3a7a53 100644 --- a/static/js/BookPage.jsx +++ b/static/js/BookPage.jsx @@ -1095,7 +1095,7 @@ const EditTextInfo = function({initTitle, close}) { } } const [compDate, setCompDate] = useState(index.current?.compDate); - const initCompDate = useState(getYearAsStr(compDate)); //init comp date to display + const [initCompDate, setInitCompDate] = useState(getYearAsStr(compDate)); //init comp date to display const toggleInProgress = function() { setSavingStatus(savingStatus => !savingStatus); @@ -1311,27 +1311,27 @@ const EditTextInfo = function({initTitle, close}) { <div className="section"> <div><InterfaceText>Place of Composition</InterfaceText></div> <label><span className="optional"><InterfaceText>Optional</InterfaceText></span></label> - <input id="compPlace" onBlur={setCompPlace} defaultValue={compPlace}/> + <input id="compPlace" onChange={(e) => setCompPlace(e.target.value)} defaultValue={compPlace}/> </div> {Sefaria._siteSettings.TORAH_SPECIFIC && <div className="section"> <div><InterfaceText>Hebrew Place of Composition</InterfaceText></div><label> <span className="optional"><InterfaceText>Optional</InterfaceText></span></label> - <input id="heCompPlace" onBlur={setHeCompPlace} defaultValue={heCompPlace}/> + <input id="heCompPlace" onChange={(e) => setHeCompPlace(e.target.value)} defaultValue={heCompPlace}/> </div>} <div className="section"> <div><InterfaceText>Publication Year</InterfaceText></div><label><span className="optional"><InterfaceText>Optional. Provide a range if there is an error margin or the work was completed over the course of many years such as 1797-1800 or -900--200 (to denote 900 BCE to 200 BCE).</InterfaceText></span></label> - <input id="pubDate" onBlur={setPubDate} defaultValue={pubDate}/> + <input id="pubDate" onChange={(e) => setPubDate(e.target.value)} defaultValue={pubDate}/> </div> <div className="section"> <div><InterfaceText>Place of Publication</InterfaceText></div><label><span className="optional"><InterfaceText>Optional</InterfaceText></span></label> - <input id="pubPlace" onBlur={setPubPlace} defaultValue={pubPlace}/> + <input id="pubPlace" onChange={(e) => setPubPlace(e.target.value)} defaultValue={pubPlace}/> </div> {Sefaria._siteSettings.TORAH_SPECIFIC && <div className="section"> <div><InterfaceText>Hebrew Place of Publication</InterfaceText></div> <label><span className="optional"><InterfaceText>Optional</InterfaceText></span></label> - <input id="hePubPlace" onBlur={setHePubPlace} defaultValue={hePubPlace}/> + <input id="hePubPlace" onChange={(e) => setHePubPlace(e.target.value)} defaultValue={hePubPlace}/> </div>} {index.current.hasOwnProperty("sectionNames") ? <div className="section"> diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 3d5c5d0395..c438e4aacd 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -1018,7 +1018,7 @@ class ToggleOption extends Component { //style={this.props.style} -const requestWithCallBack = ({url, setSavingStatus, redirect, type="POST", data={}}) => { +const requestWithCallBack = ({url, setSavingStatus, redirect, type="POST", data={}, redirect_params}) => { let ajaxPayload = {url, type}; if (type === "POST") { ajaxPayload.data = {json: JSON.stringify(data)}; diff --git a/static/js/TopicEditor.jsx b/static/js/TopicEditor.jsx index 33f93cbdb9..a8b6df972a 100644 --- a/static/js/TopicEditor.jsx +++ b/static/js/TopicEditor.jsx @@ -29,9 +29,6 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { .filter(x => x.slug !== origData.origSlug) // dont include topics that are self-linked || []); const [isChanged, setIsChanged] = useState(false); - useEffect(() => { - console.log('Y has changed', data.enAltTitles); - }, [data.enAltTitles]); const toggle = function() { setSavingStatus(savingStatus => !savingStatus); @@ -53,7 +50,7 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { "Main Menu": {"en": "Main Menu", "he": Sefaria.translation('he', "Main Menu")} }; slugsToTitles = Object.assign(specialCases, slugsToTitles); - const [catMenu, setCatMenu] = useState(<div className="section"> + const catMenu = <div className="section"> <label><InterfaceText>Parent Topic</InterfaceText></label> <div className="categoryChooserMenu"> <select key="topicCats" id="topicCats" onChange={handleCatChange}> @@ -63,11 +60,12 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { })} </select> </div> - </div>); + </div>; const updateData = function(newData) { setIsChanged(true); setData(newData); + console.log(newData); } const validate = async function () { if (!isChanged) { @@ -82,10 +80,6 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { alert(Sefaria._("Title must be provided.")); return false; } - if (data?.era && Sefaria.util.inArray(data.era, Sefaria._eras) === -1) { - alert(`Era ${data.era} is invalid.`); - return false; - } if (sortedSubtopics.length > 0 && !isNew) { await saveReorderedSubtopics(); // make sure subtopics reordered before saving topic information below } @@ -100,12 +94,12 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { switch(type) { case 'labels': return Sefaria._siteSettings.TORAH_SPECIFIC ? - ["Birth Place", "Hebrew Birth Place", "Birth Year", "Place of Death", "Hebrew Place of Death", "Death Year", "Era"] - : ["Birth Place", "Birth Year", "Place of Death", "Death Year"]; + ["English Alternate Titles", "Hebrew Alternate Titles", "Birth Place", "Hebrew Birth Place", "Birth Year", "Place of Death", "Hebrew Place of Death", "Death Year", "Era"] + : ["English Alternate Titles", "Birth Place", "Birth Year", "Place of Death", "Death Year"]; case 'keys': return Sefaria._siteSettings.TORAH_SPECIFIC ? - ["birthPlace", "heBirthPlace", "birthYear", "deathPlace", "heDeathPlace", "era"] - : ["birthPlace", "birthYear", "deathPlace", "deathYear"]; + ["enAltTitles", "heAltTitles", "birthPlace", "heBirthPlace", "birthYear", "deathPlace", "heDeathPlace", "era"] + : ["enAltTitles", "birthPlace", "birthYear", "deathPlace", "deathYear"]; } } const prepData = () => { @@ -114,16 +108,14 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { if (isCategory) { postData = {...postData, "catDescription": {"en": data.enCategoryDescription, "he": data.heCategoryDescription}}; } + if (data?.era && Sefaria.util.inArray(data.era, Sefaria._eras) === -1) { + delete postData.era; + } postData.altTitles = {}; - postData.altTitles.en = postData.enAltTitles.map(x => x.name); + postData.altTitles.en = postData.enAltTitles.map(x => x.name); // alt titles implemented using TitleVariants which contains list of objects with 'name' property postData.altTitles.he = postData.heAltTitles.map(x => x.name); postData.category = data.catSlug; - let url = ""; - if (isNew) { - url = "/api/topic/new"; - } - else { - url = `/api/topics/${data.origSlug}`; + if (!isNew) { postData = {...postData, origCategory: data.origCategorySlug, origDescription: data.origDesc, origSlug: data.origSlug}; if (isCategory) { @@ -131,46 +123,37 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { } } - return [postData, url]; + return postData; } const saveTopic = function () { toggle(); - const [postData, url] = prepData(); - if (onCreateSuccess) { // used by TopicSearch.jsx - const postJSON = JSON.stringify(postData); - $.post(url, {"json": postJSON}, function (result) { - if (result.error) { - toggle(); - alert(result.error); - } else { - const newSlug = result.slug; + const postData = prepData(); + let postURL = isNew ? "/api/topic/new" : `/api/topics/${data.origSlug}`; + const postJSON = JSON.stringify(postData); + $.post(postURL, {"json": postJSON}, function (result) { + if (result.error) { + toggle(); + alert(result.error); + } else { + const newSlug = result.slug; + if (onCreateSuccess) { onCreateSuccess(newSlug); } - }).fail(function (xhr, status, errorThrown) { - alert("Unfortunately, there may have been an error saving this topic information: " + errorThrown.toString()); - }); - } - else { - requestWithCallBack({ - url, - data: postData, - setSavingStatus, - redirect: () => window.location.href = `/topics/${postData.slug}` - }); - } + else { + window.location.href = `/topics/${newSlug}`; + } + } + }).fail(function (xhr, status, errorThrown) { + alert("Unfortunately, there may have been an error saving this topic information: " + errorThrown.toString()); + }); } const deleteObj = function() { const url = `/api/topic/delete/${data.origSlug}`; requestWithCallBack({url, type: "DELETE", redirect: () => window.location.href = "/topics"}); } - // const handleTitleVariants = (newTitles, field) => { - // const newData = {...data}; - // newData[field] = [...newTitles]; - // updateData(newData); - // } - let items = ["Title", "Hebrew Title", "English Alternate Titles", "Hebrew Alternate Titles", "Category Menu", "English Description", "Hebrew Description"]; + let items = ["Title", "Hebrew Title", "English Description", "Hebrew Description", "Category Menu"]; if (isCategory) { items.push("English Short Description"); items.push("Hebrew Short Description"); @@ -185,7 +168,6 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { <Reorder subcategoriesAndBooks={sortedSubtopics} updateOrder={setSortedSubtopics} displayType="topics"/>, - // <TitleVariants update={(newTitles) => handleTitleVariants(newTitles, field)} titles={titles} id={field}/> ] } />; } From 547823834cee5f329f6d88e3723f768c9062a618 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Sun, 27 Aug 2023 15:17:33 +0300 Subject: [PATCH 156/756] chore: remove unnecessary changes in views.py --- reader/views.py | 10 ++++------ sefaria/model/topic.py | 2 +- static/js/BookPage.jsx | 20 ++++++++++---------- static/js/Misc.jsx | 2 +- 4 files changed, 16 insertions(+), 18 deletions(-) diff --git a/reader/views.py b/reader/views.py index 334969db26..29a6e178a4 100644 --- a/reader/views.py +++ b/reader/views.py @@ -1681,9 +1681,7 @@ def index_api(request, title, raw=False): apikey = db.apikeys.find_one({"key": key}) if not apikey: return jsonResponse({"error": "Unrecognized API key."}) - index = func(apikey["uid"], Index, j, method="API", raw=raw) - results = jsonResponse(index.contents(raw=raw)) - return results + return jsonResponse(func(apikey["uid"], Index, j, method="API", raw=raw).contents(raw=raw)) else: title = j.get("oldTitle", j.get("title")) try: @@ -1695,9 +1693,9 @@ def index_api(request, title, raw=False): pass # if this is a new text, allow any logged in user to submit @csrf_protect def protected_index_post(request): - index = func(request.user.id, Index, j, raw=raw) - results = jsonResponse(index.contents(raw=raw)) - return results + return jsonResponse( + func(request.user.id, Index, j, raw=raw).contents(raw=raw) + ) return protected_index_post(request) diff --git a/sefaria/model/topic.py b/sefaria/model/topic.py index 01a14073d6..0cf82d38d8 100644 --- a/sefaria/model/topic.py +++ b/sefaria/model/topic.py @@ -21,7 +21,7 @@ class Topic(abst.SluggedAbstractMongoRecord, AbstractTitledObject): 'person': 'PersonTopic', 'author': 'AuthorTopic', } - pkeys = ["description", 'properties'] + pkeys = ["description"] track_pkeys = True reverse_subclass_map = {v: k for k, v in subclass_map.items()} required_attrs = [ diff --git a/static/js/BookPage.jsx b/static/js/BookPage.jsx index 485d3a7a53..e2008c0ece 100644 --- a/static/js/BookPage.jsx +++ b/static/js/BookPage.jsx @@ -1034,13 +1034,13 @@ const SectionTypesBox = function({sections, canEdit, updateParent}) { return <div id="sBox" ref={box}> {sections.map(function(section, i) { if (i === 0) { - return <input onBlur={updateSelfAndParent} className={'sectionType'} defaultValue={section}/>; + return <input onChange={updateSelfAndParent} className={'sectionType'} defaultValue={section}/>; } else if (canEdit) { - return <span><input onBlur={updateSelfAndParent} className={'sectionType'} defaultValue={section}/><span className="remove" onClick={(i) => remove(i)}>X</span></span>; + return <span><input onChange={updateSelfAndParent} className={'sectionType'} defaultValue={section}/><span className="remove" onClick={(i) => remove(i)}>X</span></span>; } else { - return <input onBlur={updateSelfAndParent} className={'sectionType'} defaultValue={section}/>; + return <input onChange={updateSelfAndParent} className={'sectionType'} defaultValue={section}/>; } })} {canEdit ? <span className="add" onClick={add}>Add Section</span> : null} @@ -1257,31 +1257,31 @@ const EditTextInfo = function({initTitle, close}) { <AdminToolHeader title={"Index Editor"} close={close} validate={validateThenSave}/> <div className="section"> <label><InterfaceText>Text Title</InterfaceText></label> - <input type="text" id="textTitle" onBlur={(e) => setEnTitle(e.target.value)} defaultValue={enTitle}/> + <input type="text" id="textTitle" onChange={(e) => setEnTitle(e.target.value)} defaultValue={enTitle}/> </div> {Sefaria._siteSettings.TORAH_SPECIFIC ? <div className="section"> <label><InterfaceText>Hebrew Title</InterfaceText></label> - <input id="textTitle" type="text" onBlur={(e) => setHeTitle(e.target.value)} defaultValue={heTitle}/> + <input id="textTitle" type="text" onChange={(e) => setHeTitle(e.target.value)} defaultValue={heTitle}/> </div> : null} <div className="section"> <label><InterfaceText>English Description</InterfaceText></label> - <textarea className="default" onBlur={(e) => setEnDesc(e.target.value)} defaultValue={enDesc}/> + <textarea className="default" onChange={(e) => setEnDesc(e.target.value)} defaultValue={enDesc}/> </div> <div className="section"> <label><InterfaceText>Short English Description</InterfaceText></label> - <textarea className="default" onBlur={(e) => setEnShortDesc(e.target.value)} defaultValue={enShortDesc}/> + <textarea className="default" onChange={(e) => setEnShortDesc(e.target.value)} defaultValue={enShortDesc}/> </div> {Sefaria._siteSettings.TORAH_SPECIFIC ? <div className="section"> <label><InterfaceText>Hebrew Description</InterfaceText></label> - <textarea className="default" onBlur={(e) => setHeDesc(e.target.value)} defaultValue={heDesc}/> + <textarea className="default" onChange={(e) => setHeDesc(e.target.value)} defaultValue={heDesc}/> </div> : null} {Sefaria._siteSettings.TORAH_SPECIFIC ? <div className="section"> <label><InterfaceText>Short Hebrew Description</InterfaceText></label> - <textarea className="default" onBlur={(e) => setHeShortDesc(e.target.value)} defaultValue={heShortDesc}/> + <textarea className="default" onChange={(e) => setHeShortDesc(e.target.value)} defaultValue={heShortDesc}/> </div> : null} <div className="section"> @@ -1306,7 +1306,7 @@ const EditTextInfo = function({initTitle, close}) { </div> : null} <div className="section"> <div><InterfaceText>Completion Year</InterfaceText></div><label><span className="optional"><InterfaceText>Optional. Provide a range if there is an error margin or the work was completed over the course of many years such as 1797-1800 or -900--200 (to denote 900 BCE to 200 BCE).</InterfaceText></span></label> - <br/><input id="compDate" onBlur={(e) => validateCompDate(e.target.value)} defaultValue={initCompDate}/> + <br/><input id="compDate" onChange={(e) => validateCompDate(e.target.value)} defaultValue={initCompDate}/> </div> <div className="section"> <div><InterfaceText>Place of Composition</InterfaceText></div> diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index c438e4aacd..451451754a 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -1185,7 +1185,7 @@ const ReorderEditorWrapper = ({toggle, type, data}) => { } const EditorForExistingTopic = ({ toggle, data }) => { - const prepAltTitles = (lang) => { + const prepAltTitles = (lang) => { // necessary for use with TitleVariants component return data.titles.filter(x => !x.primary && x.lang === lang).map((item, i) => ({["name"]: item.text, ["id"]: i})) } const initCatSlug = TopicToCategorySlug(data); From 26d770ef4a581a37e5bec5178bb53ec32a09d79e Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Sun, 27 Aug 2023 16:18:12 +0300 Subject: [PATCH 157/756] chore: geopy to requirements --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 0c7399793b..f72e797543 100644 --- a/requirements.txt +++ b/requirements.txt @@ -25,6 +25,7 @@ PyJWT==1.7.1 # pinned b/c current version 2.0.0 breaks simplejwt. waiting for 2. elasticsearch==7.9.1 elasticsearch_dsl==7.3.0 geojson==2.5.0 +geopy==2.3.0 gevent==20.12.0 google-api-python-client==1.12.5 google-cloud-logging==1.15.1 From 0024c677655567f60604c6430a62606db715e415 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Mon, 28 Aug 2023 10:27:55 +0300 Subject: [PATCH 158/756] helm: allow for connecting to ES without a password --- .../templates/configmap/local-settings-file.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml b/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml index 2c1901aeb2..1b6357df82 100644 --- a/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml +++ b/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml @@ -134,7 +134,8 @@ data: } SERVER_EMAIL = os.getenv("SERVER_EMAIL") - SEARCH_ADMIN = f'http://{os.getenv("ELASTIC_USERNAME")}:{os.getenv("ELASTIC_PASSWORD")}@' + {{ .Values.nginx.SEARCH_HOST | quote }} + ":9200" + auth_str = f'{os.getenv("ELASTIC_USERNAME")}:{os.getenv("ELASTIC_PASSWORD")}@' if os.getenv("ELASTIC_USERNAME") else "" + SEARCH_ADMIN = f'http://{auth_str}' + {{ .Values.nginx.SEARCH_HOST | quote }} + ":9200" SEARCH_INDEX_ON_SAVE = True SEARCH_INDEX_NAME_TEXT = 'text' # name of the ElasticSearch index to use SEARCH_INDEX_NAME_SHEET = 'sheet' From 9e298d16dff9d4a2e6457c2e97bb380e3c37b71b Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Mon, 28 Aug 2023 12:01:20 +0300 Subject: [PATCH 159/756] helm: change to single quotes --- .../templates/configmap/local-settings-file.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml b/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml index 247d0c57b2..838848cdc4 100644 --- a/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml +++ b/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml @@ -135,7 +135,7 @@ data: } SERVER_EMAIL = os.getenv("SERVER_EMAIL") - auth_str = f'{os.getenv("ELASTIC_USERNAME")}:{os.getenv("ELASTIC_PASSWORD")}@' if os.getenv("ELASTIC_USERNAME") else "" + auth_str = f'{os.getenv("ELASTIC_USERNAME")}:{os.getenv("ELASTIC_PASSWORD")}@' if os.getenv("ELASTIC_USERNAME") else '' SEARCH_ADMIN = f'http://{auth_str}' + {{ .Values.nginx.SEARCH_HOST | quote }} + ":9200" SEARCH_INDEX_ON_SAVE = True SEARCH_INDEX_NAME_TEXT = 'text' # name of the ElasticSearch index to use From b5bfe4166c13395fed543e755bd703570611548c Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Mon, 28 Aug 2023 15:03:12 +0300 Subject: [PATCH 160/756] chore: remove authorItems function and add validation for years --- reader/views.py | 4 ++-- sefaria/helper/topic.py | 16 +++++++--------- sefaria/model/place.py | 4 ++-- static/js/AdminEditor.jsx | 3 ++- static/js/BookPage.jsx | 2 +- static/js/TopicEditor.jsx | 19 +++++-------------- 6 files changed, 19 insertions(+), 29 deletions(-) diff --git a/reader/views.py b/reader/views.py index 29a6e178a4..02596dba50 100644 --- a/reader/views.py +++ b/reader/views.py @@ -62,7 +62,7 @@ from sefaria.helper.topic import get_topic, get_all_topics, get_topics_for_ref, get_topics_for_book, \ get_bulk_topics, recommend_topics, get_top_topic, get_random_topic, \ get_random_topic_source, edit_topic_source, \ - update_order_of_topic_sources, delete_ref_topic_link, update_authors_data + update_order_of_topic_sources, delete_ref_topic_link, update_authors_place_and_time from sefaria.helper.community_page import get_community_page_items from sefaria.helper.file import get_resized_file from sefaria.image_generator import make_img_http_response @@ -3109,7 +3109,7 @@ def add_new_topic_api(request): new_link.save() if data["category"] == 'authors': - t = update_authors_data(t, data) + t = update_authors_place_and_time(t, data) t.description_published = True t.data_source = "sefaria" # any topic edited manually should display automatically in the TOC and this flag ensures this diff --git a/sefaria/helper/topic.py b/sefaria/helper/topic.py index fac4614551..e2ad46c361 100644 --- a/sefaria/helper/topic.py +++ b/sefaria/helper/topic.py @@ -713,7 +713,7 @@ def get_sheet_order(topic_slug, sheet_id): else: sheet = db.sheets.find_one({"id": sheet_id}, {"views": 1, "includedRefs": 1, "dateCreated": 1, "options": 1, "title": 1, "topics": 1}) includedRefs = [] - for tref in sheet['includedRefs']: + for tref in sheet['includedRefs']: try: oref = get_ref_safely(tref) if oref is None: @@ -913,7 +913,7 @@ def calculate_popular_writings_for_authors(top_n, min_pr): continue if getattr(oref.index, 'authors', None) is None: continue for author in oref.index.authors: - by_author[author] += [rd.contents()] + by_author[author] += [rd.contents()] for author, rd_list in by_author.items(): rd_list = list(filter(lambda x: x['pagesheetrank'] > min_pr, rd_list)) if len(rd_list) == 0: continue @@ -1051,20 +1051,18 @@ def update_topic_titles(topic_obj, data): return topic_obj -def update_authors_data(topic_obj, data, dataSource='learning-team-editing-tool'): +def update_authors_place_and_time(topic_obj, data, dataSource='learning-team-editing-tool'): # update place info added to author, then update year and era info if not hasattr(topic_obj, 'properties'): topic_obj.properties = {} process_topic_place_change(topic_obj, data) - topic_obj = update_author_era(topic_obj, data, dataSource='learning-team-editing-tool') + topic_obj = update_author_era(topic_obj, data, dataSource=dataSource) return topic_obj def update_author_era(topic_obj, data, dataSource='learning-team-editing-tool'): for k in ["birthYear", "deathYear"]: - year = data.get(k, '') # set to None if blank, otherwise validate number - if year != '': - assert int(year), f"'{k}' must be a number but is {year}" + year = data.get(k, '') topic_obj.properties[k] = {'value': year, 'dataSource': dataSource} prev_era = topic_obj.properties.get('era', {}).get('value') @@ -1088,7 +1086,7 @@ def update_topic(topic_obj, **kwargs): update_topic_titles(topic_obj, kwargs) if kwargs.get('catSlug') == 'authors': - topic_obj = update_authors_data(topic_obj, kwargs) + topic_obj = update_authors_place_and_time(topic_obj, kwargs) if 'category' in kwargs: orig_link = IntraTopicLink().load({"linkType": "displays-under", "fromTopic": topic_obj.slug, "toTopic": {"$ne": topic_obj.slug}}) @@ -1244,7 +1242,7 @@ def delete_ref_topic_link(tref, to_topic, link_type, lang): if link is None: return {"error": f"Link between {tref} and {to_topic} doesn't exist."} - if lang in link.order['availableLangs']: + if lang in link.order['availableLangs']: link.order['availableLangs'].remove(lang) if lang in link.order['curatedPrimacy']: link.order['curatedPrimacy'].pop(lang) diff --git a/sefaria/model/place.py b/sefaria/model/place.py index d4091a1012..b7251567a6 100644 --- a/sefaria/model/place.py +++ b/sefaria/model/place.py @@ -49,10 +49,10 @@ def secondary_names(self, lang=None): @classmethod def create_new_place(cls, en, he=None): - p = Place().load({'key': en}) + p = cls().load({'key': en}) if p: return p - p = Place({'key': en}) + p = cls({'key': en}) p.name_group.add_title(en, 'en', True, True) if he: p.name_group.add_title(he, 'he', True, True) diff --git a/static/js/AdminEditor.jsx b/static/js/AdminEditor.jsx index 7948215bc3..7061b4b7bf 100644 --- a/static/js/AdminEditor.jsx +++ b/static/js/AdminEditor.jsx @@ -173,7 +173,8 @@ const AdminEditor = ({title, data, close, catMenu, updateData, savingStatus, placeholder={Sefaria._(placeholder)}/>; break; default: - obj = <input type='text' id={field} onChange={setInputValue} defaultValue={data[field]} + const inputType = field.includes('Year') ? 'number' : 'text'; + obj = <input type={inputType} id={field} onChange={setInputValue} defaultValue={data[field]} placeholder={Sefaria._(placeholder)}/>; } diff --git a/static/js/BookPage.jsx b/static/js/BookPage.jsx index e2008c0ece..1513ca6315 100644 --- a/static/js/BookPage.jsx +++ b/static/js/BookPage.jsx @@ -1321,7 +1321,7 @@ const EditTextInfo = function({initTitle, close}) { </div>} <div className="section"> <div><InterfaceText>Publication Year</InterfaceText></div><label><span className="optional"><InterfaceText>Optional. Provide a range if there is an error margin or the work was completed over the course of many years such as 1797-1800 or -900--200 (to denote 900 BCE to 200 BCE).</InterfaceText></span></label> - <input id="pubDate" onChange={(e) => setPubDate(e.target.value)} defaultValue={pubDate}/> + <input type='number' id="pubDate" onChange={(e) => setPubDate(e.target.value)} defaultValue={pubDate}/> </div> <div className="section"> <div><InterfaceText>Place of Publication</InterfaceText></div><label><span className="optional"><InterfaceText>Optional</InterfaceText></span></label> diff --git a/static/js/TopicEditor.jsx b/static/js/TopicEditor.jsx index a8b6df972a..d962e88bd2 100644 --- a/static/js/TopicEditor.jsx +++ b/static/js/TopicEditor.jsx @@ -90,18 +90,7 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { const postCategoryData = {topics: sortedSubtopics}; requestWithCallBack({url, data: postCategoryData, setSavingStatus, redirect: () => window.location.href = "/topics"}); } - const authorItems = (type) => { - switch(type) { - case 'labels': - return Sefaria._siteSettings.TORAH_SPECIFIC ? - ["English Alternate Titles", "Hebrew Alternate Titles", "Birth Place", "Hebrew Birth Place", "Birth Year", "Place of Death", "Hebrew Place of Death", "Death Year", "Era"] - : ["English Alternate Titles", "Birth Place", "Birth Year", "Place of Death", "Death Year"]; - case 'keys': - return Sefaria._siteSettings.TORAH_SPECIFIC ? - ["enAltTitles", "heAltTitles", "birthPlace", "heBirthPlace", "birthYear", "deathPlace", "heDeathPlace", "era"] - : ["enAltTitles", "birthPlace", "birthYear", "deathPlace", "deathYear"]; - } - } + const prepData = () => { let postData = {...data, "description": {"en": data.enDescription, "he": data.heDescription}, "title": data.enTitle, "heTitle": data.heTitle}; @@ -112,7 +101,8 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { delete postData.era; } postData.altTitles = {}; - postData.altTitles.en = postData.enAltTitles.map(x => x.name); // alt titles implemented using TitleVariants which contains list of objects with 'name' property + // alt titles implemented using TitleVariants which contains list of objects with 'name' property. + postData.altTitles.en = postData.enAltTitles.map(x => x.name); postData.altTitles.he = postData.heAltTitles.map(x => x.name); postData.category = data.catSlug; if (!isNew) { @@ -159,7 +149,8 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { items.push("Hebrew Short Description"); } if (isAuthor) { - authorItems('labels').forEach(x => items.push(x)); + const authorItems = ["English Alternate Titles", "Hebrew Alternate Titles", "Birth Place", "Hebrew Birth Place", "Birth Year", "Place of Death", "Hebrew Place of Death", "Death Year", "Era"]; + authorItems.forEach(x => items.push(x)); } return <AdminEditor title="Topic Editor" close={close} catMenu={catMenu} data={data} savingStatus={savingStatus} validate={validate} deleteObj={deleteObj} updateData={updateData} isNew={isNew} From 71ffb9e70a1178cc3a54b76fde04f51d4ebb006d Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Mon, 28 Aug 2023 15:31:14 +0300 Subject: [PATCH 161/756] chore: get_he_key instead of generating string --- sefaria/model/place.py | 9 +++++---- sefaria/utils/hebrew.py | 3 +++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/sefaria/model/place.py b/sefaria/model/place.py index b7251567a6..ebeb8e6b2c 100644 --- a/sefaria/model/place.py +++ b/sefaria/model/place.py @@ -5,6 +5,7 @@ from sefaria.system.exceptions import InputError import structlog from geopy.geocoders import Nominatim +from sefaria.utils.hebrew import get_he_key logger = structlog.get_logger(__name__) class Place(abst.AbstractMongoRecord): @@ -97,17 +98,17 @@ def asGeoJson(self, with_polygons=False, as_string=False): else: return geojson.FeatureCollection(features) - def process_index_place_change(indx, **kwargs): key = kwargs['attr'] - he_key = 'he' + key[0].upper() + key[1:] # birthPlace => heBirthPlace + he_key = get_he_key(key) he_new_val = getattr(indx, he_key, '') - Place.create_new_place(en=kwargs['new'], he=he_new_val) + if kwargs['new'] != '': + Place.create_new_place(en=kwargs['new'], he=he_new_val) def process_topic_place_change(topic_obj, data): keys = ["birthPlace", "deathPlace"] for key in keys: - he_key = 'he' + key[0].upper() + key[1:] # birthPlace => heBirthPlace + he_key = get_he_key(key) he_new_val = data.get(he_key, '') new_val = data.get(key, '') if new_val != '': diff --git a/sefaria/utils/hebrew.py b/sefaria/utils/hebrew.py index 804178b0c9..c9389e7003 100644 --- a/sefaria/utils/hebrew.py +++ b/sefaria/utils/hebrew.py @@ -546,6 +546,9 @@ def hebrew_parasha_name(value): # Hebrew Abbrev Matching ######## +def get_he_key(key): + return 'he' + key[0].upper() + key[1:] # birthPlace => heBirthPlace + def get_abbr(abbr: str, unabbr: List[str], match=lambda x, y: x.startswith(y), lang='he'): abbr = re.sub('[^א-ת]', '', abbr) if lang == 'he' else re.sub('[^a-z]', '', abbr) indexes = [[index for index, letter in enumerate(abbr) if word[0] == letter] for w, word in enumerate(unabbr)] From 4b6aa2b7f66b9341d4c0635872367da83eba66f6 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Mon, 28 Aug 2023 12:13:34 -0400 Subject: [PATCH 162/756] Rename timesUp to be more descriptive --- static/css/s2.css | 42 +++++++++++++++++------------------------ static/js/Misc.jsx | 42 ++++++++++++++++++++--------------------- static/js/ReaderApp.jsx | 8 -------- 3 files changed, 38 insertions(+), 54 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index 0a5f9d7c17..9cb2ef9386 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -1214,63 +1214,55 @@ div.interfaceLinks-row a { margin: 0 0 30px; color: #333; } -.interface-hebrew #interruptingMessage h1{ +.interface-hebrew #interruptingMessage h1 { font-style: normal; } /* Styles used from previously existing modals */ -#highHolidayDonation { +.line-break { + white-space: pre-wrap; +} + +#defaultModal { width: 410px; max-height: 100%; max-width: 100%; } -.interface-english #highHolidayDonation { +.interface-english #defaultModal { text-align: left; } -.interface-hebrew #highHolidayDonation { +.interface-hebrew #defaultModal { text-align: right; direction: rtl; } -#highHolidayDonation p { +#defaultModalBody { color: #555; + margin-top: 0; } -.interface-hebrew p.int-en { - display: none; -} - -#highHolidayDonation p .int-en { - font-family: "adobe-garamond-pro", Georgia, serif; -} - -#highHolidayDonation p .int-he { - font-family: "adobe-garamond-pro", Georgia, serif; - /* font-family: "Heebo", sans-serif; */ +#defaultModalBody .reactMarkdown { + font-family: "adobe-garamond-pro", Georgia, serif; } -#highHolidayDonation p.sub { +#defaultModal #defaultModalBody .sub { color: #999; font-size: 12px; font-family: "Roboto", "Helvetica Neue", Helvetica, sans-serif; } -#highHolidayDonation p { - margin-top: 0; -} - -#highHolidayDonation .button { +#defaultModal .button { margin-bottom: 20px; } -#highHolidayDonation img { +#defaultModal img { max-width: 100%; } -#highHolidayDonation .buttons { +#defaultModal .buttons { text-align: right; } @@ -5797,7 +5789,7 @@ But not to use a display block directive that might break continuous mode for ot margin-bottom: 5px; color: #999; } -.textList.singlePanel .versionsTextList .topFiltersInner, .line-break { +.textList.singlePanel .versionsTextList .topFiltersInner { white-space: pre-wrap; } .showMoreFilters { diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 0d4a2c242f..522c3d4d0f 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -60,7 +60,7 @@ const __filterChildrenByLanguage = (children, language) => { return newChildren; }; -const InterfaceText = ({text, html, markdown, children, context, styleClasses}) => { +const InterfaceText = ({text, html, markdown, children, context}) => { /** * Renders a single span for interface string with either class `int-en`` or `int-he` depending on Sefaria.interfaceLang. * If passed explicit text or html objects as props with "en" and/or "he", will only use those to determine correct text or fallback text to display. @@ -68,12 +68,11 @@ const InterfaceText = ({text, html, markdown, children, context, styleClasses}) * `children` can be the English string, which will be translated with Sefaria._ if needed. * `children` can also take the form of <LangText> components above, so they can be used for longer paragrpahs or paragraphs containing html, if needed. * `context` is passed to Sefaria._ for additional translation context - * `styleClasses` are CSS classes that you want applied to all the interface languages */ const contentVariable = html ? html : markdown ? markdown : text; // assumption is `markdown` or `html` are preferred over `text` if they are present const isHebrew = Sefaria.interfaceLang === "hebrew"; - let elemclasses = classNames(styleClasses, {"int-en": !isHebrew, "int-he": isHebrew}); + let elemclasses = classNames({"int-en": !isHebrew, "int-he": isHebrew}); let textResponse = null; if (contentVariable) {// Prioritize explicit props passed in for text of the element, does not attempt to use Sefaria._() for this case. let {he, en} = contentVariable; @@ -2089,7 +2088,7 @@ function OnInView({ children, onVisible }) { const InterruptingMessage = ({ onClose, }) => { - const [timesUp, setTimesUp] = useState(false); + const [interruptingMessageShowDelayHasElapsed, setInterruptingMessageShowDelayHasElapsed] = useState(false); const [hasInteractedWithModal, setHasInteractedWithModal] = useState(false); const strapi = useContext(StrapiDataContext); const showDelay = 5000; @@ -2177,13 +2176,13 @@ const InterruptingMessage = ({ useEffect(() => { if (shouldShow()) { const timeoutId = setTimeout(() => { - setTimesUp(true); + setInterruptingMessageShowDelayHasElapsed(true); }, showDelay); return () => clearTimeout(timeoutId); // clearTimeout on component unmount } }, [strapi.modal]); // execute useEffect when the modal changes - if (!timesUp) return null; + if (!interruptingMessageShowDelayHasElapsed) return null; console.log("data for the component"); console.log(strapi.modal); @@ -2191,7 +2190,7 @@ const InterruptingMessage = ({ console.log("rendering component"); return ( <OnInView onVisible={trackModalImpression}> - <div id="interruptingMessageBox" className={timesUp ? "" : "hidden"}> + <div id="interruptingMessageBox" className={interruptingMessageShowDelayHasElapsed ? "" : "hidden"}> <div id="interruptingMessageOverlay"></div> <div id="interruptingMessage"> <div className="colorLine"></div> @@ -2205,19 +2204,20 @@ const InterruptingMessage = ({ × </div> <div id="interruptingMessageContent"> - <div id="highHolidayDonation"> + <div id="defaultModal"> {strapi.modal.modalHeader.en && ( - <h4 className="int-en">{strapi.modal.modalHeader.en}</h4> + <h1 className="int-en">{strapi.modal.modalHeader.en}</h1> )} {strapi.modal.modalHeader.he && ( - <h4 className="int-he">{strapi.modal.modalHeader.he}</h4> + <h1 className="int-he">{strapi.modal.modalHeader.he}</h1> )} - <InterfaceText - markdown={replaceNewLinesWithLinebreaks( - strapi.modal.modalText - )} - styleClasses={["line-break"]} - /> + <div id="defaultModalBody" className="line-break"> + <InterfaceText + markdown={replaceNewLinesWithLinebreaks( + strapi.modal.modalText + )} + /> + </div> <div className="buttons"> <a className="button int-en" @@ -2259,7 +2259,7 @@ const InterruptingMessage = ({ InterruptingMessage.displayName = "InterruptingMessage"; const Banner = ({ onClose }) => { - const [timesUp, setTimesUp] = useState(false); + const [bannerShowDelayHasElapsed, setBannerShowDelayHasElapsed] = useState(false); const [hasInteractedWithBanner, setHasInteractedWithBanner] = useState(false); const strapi = useContext(StrapiDataContext); const showDelay = 5000; @@ -2340,13 +2340,13 @@ const Banner = ({ onClose }) => { if (document.getElementById("s2").classList.contains("headerOnly")) { document.body.classList.add("hasBannerMessage"); } - setTimesUp(true); + setBannerShowDelayHasElapsed(true); }, showDelay); return () => clearTimeout(timeoutId); // clearTimeout on component unmount } }, [strapi.banner]); // execute useEffect when the modal changes - if (!timesUp) return null; + if (!bannerShowDelayHasElapsed) return null; console.log("data for the component"); console.log(strapi.banner); @@ -2355,10 +2355,10 @@ const Banner = ({ onClose }) => { console.log(strapi.banner.bannerText); return ( <OnInView onVisible={trackBannerImpression}> - <div id="bannerMessage" className={timesUp ? "" : "hidden"}> + <div id="bannerMessage" className={bannerShowDelayHasElapsed ? "" : "hidden"}> <div id="bannerMessageContent"> <div id="bannerTextBox"> - <InterfaceText markdown={replaceNewLinesWithLinebreaks(strapi.banner.bannerText)} styleClasses={['line-break']} /> + <InterfaceText markdown={replaceNewLinesWithLinebreaks(strapi.banner.bannerText)} /> </div> <div id="bannerButtonBox"> <a diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index b04c437228..23fe95e32d 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -2159,13 +2159,6 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { {panels} </div>) : null; - // var interruptingMessage = Sefaria.interruptingMessage ? - // (<InterruptingMessage - // messageName={Sefaria.interruptingMessage.name} - // messageHTML={Sefaria.interruptingMessage.html} - // style={Sefaria.interruptingMessage.style} - // repetition={Sefaria.interruptingMessage.repetition} - // onClose={this.rerender} />) : <Promotions rerender={this.rerender} adType="banner"/>; const sefariaModal = ( <SignUpModal onClose={this.toggleSignUpModal} @@ -2195,7 +2188,6 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { {sefariaModal} {communityPagePreviewControls} <CookiesNotification /> - {/* <ExampleComponent /> */} </div> </div> </AdContext.Provider> From 7bca11fcbe565e0cb63218a6371f6937768eeeed Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Mon, 28 Aug 2023 12:36:10 -0400 Subject: [PATCH 163/756] Rename to support more general button events --- static/js/Misc.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 522c3d4d0f..61f1427425 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -2224,7 +2224,7 @@ const InterruptingMessage = ({ target="_blank" href={strapi.modal.buttonURL.en} onClick={() => { - closeModal("donate_button_clicked"); + closeModal("modal_button_clicked"); }} > <span className="int-en"> @@ -2236,7 +2236,7 @@ const InterruptingMessage = ({ target="_blank" href={strapi.modal.buttonURL.he} onClick={() => { - closeModal("donate_button_clicked"); + closeModal("modal_button_clicked"); }} > <span className="int-he"> From f9b3009cacfc807dca7b85f0afda0262f2533f04 Mon Sep 17 00:00:00 2001 From: Russel Neiss <russel.neiss@gmail.com> Date: Mon, 28 Aug 2023 14:17:04 -0500 Subject: [PATCH 164/756] fix: Remove duplicate adContext import --- static/js/Misc.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 80a96b131b..d10f4e3b4f 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -23,7 +23,6 @@ import Cookies from "js-cookie"; import {EditTextInfo} from "./BookPage"; import ReactMarkdown from 'react-markdown'; import TrackG4 from "./sefaria/trackG4"; -import {AdContext} from "./context"; /** * Component meant to simply denote a language specific string to go inside an InterfaceText element * ``` From 615ea333d9c2d607dc1fa6cb1ed3b4ea1ddb7346 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Mon, 28 Aug 2023 18:37:55 -0400 Subject: [PATCH 165/756] feat(strapi-cms): Add showDelay as configurable parameter for banners and modals through Strapi CMS --- static/js/Misc.jsx | 6 +++--- static/js/context.js | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 61f1427425..714f5573ea 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -2055,7 +2055,7 @@ SignUpModal.propTypes = { modalContent: PropTypes.object.isRequired, }; -// Write comments explaining how this works + function OnInView({ children, onVisible }) { const elementRef = useRef(); @@ -2091,7 +2091,7 @@ const InterruptingMessage = ({ const [interruptingMessageShowDelayHasElapsed, setInterruptingMessageShowDelayHasElapsed] = useState(false); const [hasInteractedWithModal, setHasInteractedWithModal] = useState(false); const strapi = useContext(StrapiDataContext); - const showDelay = 5000; + const showDelay = strapi.modal.showDelay * 1000; const markModalAsHasBeenInteractedWith = (modalName) => { localStorage.setItem("modal_" + modalName, "true"); @@ -2262,7 +2262,7 @@ const Banner = ({ onClose }) => { const [bannerShowDelayHasElapsed, setBannerShowDelayHasElapsed] = useState(false); const [hasInteractedWithBanner, setHasInteractedWithBanner] = useState(false); const strapi = useContext(StrapiDataContext); - const showDelay = 5000; + const showDelay = strapi.banner.showDelay * 1000; const markBannerAsHasBeenInteractedWith = (bannerName) => { localStorage.setItem("banner_" + bannerName, "true"); diff --git a/static/js/context.js b/static/js/context.js index c2694f2fcc..d90071fdab 100644 --- a/static/js/context.js +++ b/static/js/context.js @@ -66,6 +66,7 @@ function StrapiDataProvider({ children }) { bannerText buttonText buttonURL + showDelay createdAt locale localizations { @@ -100,6 +101,7 @@ function StrapiDataProvider({ children }) { internalModalName buttonText buttonURL + showDelay createdAt locale localizations { From b5397c547beddca766f80731dab75ba4b24c4460 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Mon, 28 Aug 2023 19:52:11 -0400 Subject: [PATCH 166/756] fix(strapi-cms): Fix properly using the showDelay parameter from Strapi --- static/js/Misc.jsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 5b643424ec..96dd16423d 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -2092,7 +2092,6 @@ const InterruptingMessage = ({ const [interruptingMessageShowDelayHasElapsed, setInterruptingMessageShowDelayHasElapsed] = useState(false); const [hasInteractedWithModal, setHasInteractedWithModal] = useState(false); const strapi = useContext(StrapiDataContext); - const showDelay = strapi.modal.showDelay * 1000; const markModalAsHasBeenInteractedWith = (modalName) => { localStorage.setItem("modal_" + modalName, "true"); @@ -2178,7 +2177,7 @@ const InterruptingMessage = ({ if (shouldShow()) { const timeoutId = setTimeout(() => { setInterruptingMessageShowDelayHasElapsed(true); - }, showDelay); + }, strapi.modal.showDelay * 1000); return () => clearTimeout(timeoutId); // clearTimeout on component unmount } }, [strapi.modal]); // execute useEffect when the modal changes @@ -2263,7 +2262,6 @@ const Banner = ({ onClose }) => { const [bannerShowDelayHasElapsed, setBannerShowDelayHasElapsed] = useState(false); const [hasInteractedWithBanner, setHasInteractedWithBanner] = useState(false); const strapi = useContext(StrapiDataContext); - const showDelay = strapi.banner.showDelay * 1000; const markBannerAsHasBeenInteractedWith = (bannerName) => { localStorage.setItem("banner_" + bannerName, "true"); @@ -2342,7 +2340,7 @@ const Banner = ({ onClose }) => { document.body.classList.add("hasBannerMessage"); } setBannerShowDelayHasElapsed(true); - }, showDelay); + }, strapi.banner.showDelay * 1000); return () => clearTimeout(timeoutId); // clearTimeout on component unmount } }, [strapi.banner]); // execute useEffect when the modal changes From 25f022dfba708f2e173a7276505c6af634b84ed9 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Tue, 29 Aug 2023 11:42:10 +0300 Subject: [PATCH 167/756] test: add test for two normalization steps --- sefaria/helper/tests/normalization_tests.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/sefaria/helper/tests/normalization_tests.py b/sefaria/helper/tests/normalization_tests.py index 1b7d8903f0..441e24ce42 100644 --- a/sefaria/helper/tests/normalization_tests.py +++ b/sefaria/helper/tests/normalization_tests.py @@ -90,6 +90,21 @@ def test_nested_itag(): assert text[s:e] == """<i class="footnote">bull<sup>nested</sup><i class="footnote">The</i>.</i>""" +def test_two_steps_normalization(): + test_string = ' This is a {{test}}' + + bracket_normalizer = RegexNormalizer(r'\{\{|}}', r'') + strip_normalizer = RegexNormalizer(r'^\s*|\s*$', r'') + normalizer = NormalizerComposer(steps=[bracket_normalizer, strip_normalizer]) + + mapping = normalizer.get_mapping_after_normalization(test_string, reverse=True) + assert mapping == {0: 1, 11: 3, 17: 5} + orig_inds = [(13, 17)] + new_start, new_end = normalizer.convert_normalized_indices_to_unnormalized_indices(orig_inds, mapping, reverse=True)[0] + normalized_string = normalizer.normalize(test_string) + assert normalized_string[new_start:new_end] == "test" + + def test_word_to_char(): test_string = 'some words go here\n\nhello world' words = ['go', 'here', 'hello'] From 791a74f0c6ecc91bc258de40f6e9cb75c6897ea6 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Tue, 29 Aug 2023 11:43:10 +0300 Subject: [PATCH 168/756] fix: dont skip rest of list if new_inds is less than curr_inds because there may be a later new_ind that is overlaps --- sefaria/helper/normalization.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/sefaria/helper/normalization.py b/sefaria/helper/normalization.py index 1aea57e560..731c0c294e 100644 --- a/sefaria/helper/normalization.py +++ b/sefaria/helper/normalization.py @@ -86,7 +86,6 @@ def remove_subsets_reducer(curr_text_to_remove: list, next: tuple) -> list: def get_mapping_after_normalization(self, text, removal_list=None, reverse=False, **kwargs): """ text - unnormalized text - find_text_to_remove - function which takes text as param and return list of tuples. each tuple is of form ((start, end), replacement) where start and end are indices in unnormalized string and replacement is the string that will replace text at these indices removal_list - instead of passing `find_text_to_remove`, you can pass an already calculated list of tuples. should be in same format as return value of find_text_to_remove reverse - bool. If True, then will return mapping from unnormalized string to normalized string @@ -109,7 +108,10 @@ def get_mapping_after_normalization(self, text, removal_list=None, reverse=False # must be match object start, end = removal.start(), removal.end() normalized_text_index = start if reverse else (start + min(len(subst), end-start) - total_removed) - total_removed += (end - start - len(subst)) + temp_removed = end - start - len(subst) + if temp_removed == 0: + continue + total_removed += temp_removed removal_map[normalized_text_index] = total_removed return removal_map @@ -288,14 +290,16 @@ def merge_removal_inds(curr_removal_inds, new_removal_inds): inds_are_final = True for i, (curr_inds, curr_repl) in enumerate(curr_removal_inds[last_curr:]): if new_inds[1] <= curr_inds[0]: - # curr_inds are past new_inds indicating rest of curr_inds will also be past. break early. - break + # new_inds are before curr_inds and not overlapping. easy case. + inds_are_final = True elif curr_inds[0] >= new_inds[0] and curr_inds[1] <= new_inds[1]: # are curr_inds subset of new_inds? - # if earlier inds are a subset of later inds, later inds override + # if curr_inds is a full subset of new_inds, new_inds override merged_inds.remove((curr_inds, curr_repl)) + last_curr += 1 elif new_inds[0] < curr_inds[1] or new_inds[1] > curr_inds[0]: - # if later inds overlap and earlier inds are not a subset, merge + # if new_inds overlap and curr_inds are not a subset, merge if new_inds[0] >= curr_inds[0] and new_inds[1] <= curr_inds[1]: + # new_inds is a full subset of curr_inds merged_repl = curr_repl[:new_inds[0] - curr_inds[0]] + new_repl + curr_repl[new_inds[1] - curr_inds[1]:] merged_inds[i+last_curr] = (curr_inds, merged_repl) @@ -305,7 +309,6 @@ def merge_removal_inds(curr_removal_inds, new_removal_inds): else: # overlap that's not a subset. more complicated merge that I don't want to deal with now pass - last_curr += 1 if inds_are_final: merged_inds += [(new_inds, new_repl)] return merged_inds From 6c7c82682ce9279448acada3ac39b8f1d80e5f22 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Tue, 29 Aug 2023 12:44:50 +0300 Subject: [PATCH 169/756] refactor: greatly simplify logic for merging replacements --- sefaria/helper/normalization.py | 54 ++++++++++++--------------------- 1 file changed, 20 insertions(+), 34 deletions(-) diff --git a/sefaria/helper/normalization.py b/sefaria/helper/normalization.py index 731c0c294e..18341031fd 100644 --- a/sefaria/helper/normalization.py +++ b/sefaria/helper/normalization.py @@ -274,44 +274,30 @@ def find_text_to_remove(self, s, **kwargs): snorm = step.normalize(snorm, **kwargs) # merge any overlapping ranges # later edits should override earlier ones - final_text_to_remove = reduce(lambda a, b: self.merge_removal_inds(a, b), all_text_to_remove) + final_text_to_remove = reduce(self.merge_removal_inds_new, all_text_to_remove) final_text_to_remove.sort(key=lambda x: x[0]) return final_text_to_remove @staticmethod - def merge_removal_inds(curr_removal_inds, new_removal_inds): - if isinstance(new_removal_inds, tuple): - new_removal_inds = [new_removal_inds] - curr_removal_inds.sort(key=lambda x: x[0]) - new_removal_inds.sort(key=lambda x: x[0]) - merged_inds = curr_removal_inds[:] - last_curr = 0 - for new_inds, new_repl in new_removal_inds: - inds_are_final = True - for i, (curr_inds, curr_repl) in enumerate(curr_removal_inds[last_curr:]): - if new_inds[1] <= curr_inds[0]: - # new_inds are before curr_inds and not overlapping. easy case. - inds_are_final = True - elif curr_inds[0] >= new_inds[0] and curr_inds[1] <= new_inds[1]: # are curr_inds subset of new_inds? - # if curr_inds is a full subset of new_inds, new_inds override - merged_inds.remove((curr_inds, curr_repl)) - last_curr += 1 - elif new_inds[0] < curr_inds[1] or new_inds[1] > curr_inds[0]: - # if new_inds overlap and curr_inds are not a subset, merge - if new_inds[0] >= curr_inds[0] and new_inds[1] <= curr_inds[1]: - # new_inds is a full subset of curr_inds - merged_repl = curr_repl[:new_inds[0] - curr_inds[0]] + new_repl + curr_repl[new_inds[1] - - curr_inds[1]:] - merged_inds[i+last_curr] = (curr_inds, merged_repl) - inds_are_final = False - last_curr += 1 - break - else: - # overlap that's not a subset. more complicated merge that I don't want to deal with now - pass - if inds_are_final: - merged_inds += [(new_inds, new_repl)] - return merged_inds + def merge_removal_inds_new(*all_removal_inds): + combined_removal_inds = reduce(lambda a, b: a + b, all_removal_inds, []) + combined_removal_inds.sort(key=lambda x: x[0][0]) + merged_removal_inds = [] + for curr_inds, curr_repl in combined_removal_inds: + if len(merged_removal_inds) == 0: + merged_removal_inds += [(curr_inds, curr_repl)] + continue + last_inds, last_repl = merged_removal_inds[-1] + if curr_inds[0] >= last_inds[1]: + # If current interval doesn't overlap with the last interval in result, append it + merged_removal_inds += [(curr_inds, curr_repl)] + else: + # some sort of overlap + temp_merged_inds = (last_inds[0], max(last_inds[1], curr_inds[1])) + temp_merged_repl = last_repl[:curr_inds[0]-last_inds[0]] + curr_repl + last_repl[(curr_inds[1]+1)-last_inds[0]:] + merged_removal_inds[-1] = (temp_merged_inds, temp_merged_repl) + + return merged_removal_inds class TableReplaceNormalizer(AbstractNormalizer): From 323e6d5c7b69dc85458edf76dfdb8312393097cf Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Tue, 29 Aug 2023 13:08:30 +0300 Subject: [PATCH 170/756] chore: onChange to onBlur for compDate so that it allows user to type full range of years --- static/js/BookPage.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/BookPage.jsx b/static/js/BookPage.jsx index 1513ca6315..d2fde29d4e 100644 --- a/static/js/BookPage.jsx +++ b/static/js/BookPage.jsx @@ -1306,7 +1306,7 @@ const EditTextInfo = function({initTitle, close}) { </div> : null} <div className="section"> <div><InterfaceText>Completion Year</InterfaceText></div><label><span className="optional"><InterfaceText>Optional. Provide a range if there is an error margin or the work was completed over the course of many years such as 1797-1800 or -900--200 (to denote 900 BCE to 200 BCE).</InterfaceText></span></label> - <br/><input id="compDate" onChange={(e) => validateCompDate(e.target.value)} defaultValue={initCompDate}/> + <br/><input id="compDate" onBlur={(e) => validateCompDate(e.target.value)} defaultValue={initCompDate}/> </div> <div className="section"> <div><InterfaceText>Place of Composition</InterfaceText></div> From 1a51b6dd7e81d94f2568900299148438998fed46 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Tue, 29 Aug 2023 13:58:50 +0300 Subject: [PATCH 171/756] fix: off-by-one error in calculation of end index --- sefaria/helper/normalization.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sefaria/helper/normalization.py b/sefaria/helper/normalization.py index 18341031fd..0e72255047 100644 --- a/sefaria/helper/normalization.py +++ b/sefaria/helper/normalization.py @@ -127,7 +127,9 @@ def convert_normalized_indices_to_unnormalized_indices(normalized_indices, remov sign = -1 if reverse else 1 for start, end in normalized_indices: unnorm_start_index = bisect_right(removal_keys, start) - 1 - unnorm_end_index = bisect_right(removal_keys, (end - 1 if reverse else end)) - 1 # not sure if end-1 is specific to reverse case, but seems to be working + # special case if range is zero-length. treat end as literal and not off-by-one. + bisect_end_index = end if end == start else (end - 1) + unnorm_end_index = bisect_right(removal_keys, bisect_end_index) - 1 unnorm_start = start if unnorm_start_index < 0 else start + (sign * removal_map[removal_keys[unnorm_start_index]]) unnorm_end = end if unnorm_end_index < 0 else end + (sign * removal_map[removal_keys[unnorm_end_index]]) From 3be01562c940374553018cca4c69b61bc51cd37b Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Tue, 29 Aug 2023 13:59:32 +0300 Subject: [PATCH 172/756] test: fix tests that were incorrect due to off-by-one error --- sefaria/helper/tests/normalization_tests.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/sefaria/helper/tests/normalization_tests.py b/sefaria/helper/tests/normalization_tests.py index 441e24ce42..eaff8ff116 100644 --- a/sefaria/helper/tests/normalization_tests.py +++ b/sefaria/helper/tests/normalization_tests.py @@ -49,15 +49,27 @@ def test_br_tag_html_composer(): assert text[start4:end4] == '</b> ' -def test_normalizer_composer(): +def test_simpler_normalizer_composer(): + text = ' [sup' + normalized = " sup" + nsc = NormalizerComposer(['brackets', 'double-space']) + assert nsc.normalize(text) == normalized + text_to_remove = nsc.find_text_to_remove(text) + assert len(text_to_remove) == 2 + (start0, end0), repl0 = text_to_remove[0] + assert text[start0:end0] == " " + assert repl0 == ' ' + + +def test_complicated_normalizer_composer(): text = """(<i>hello</i> other stuff) [sup] <b>(this is) a test</b>""" normalized = """ sup a test """ nsc = NormalizerComposer(['html', "parens-plus-contents", 'brackets', 'double-space']) assert nsc.normalize(text) == normalized text_to_remove = nsc.find_text_to_remove(text) - assert len(text_to_remove) == 5 + assert len(text_to_remove) == 6 (start0, end0), repl0 = text_to_remove[0] - assert text[start0:end0] == "(<i>hello</i> other stuff) [" + assert text[start0:end0] == "(<i>hello</i> other stuff) " assert repl0 == ' ' @@ -111,7 +123,7 @@ def test_word_to_char(): word_indices = (2, 4) result = char_indices_from_word_indices(test_string, [word_indices])[0] start, end = result - assert test_string[start:end] == 'go here\n\nhello ' # TODO used to not have trailing space. not sure how critical this is. + assert test_string[start:end] == 'go here\n\nhello' assert test_string[start:end].split() == words From 304b88c3122c6a0f54fdaf7e71b5914c18d97d44 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Tue, 29 Aug 2023 14:02:38 +0300 Subject: [PATCH 173/756] refactor: incrementally update final_text_to_remove instead of merging later on. --- sefaria/helper/normalization.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sefaria/helper/normalization.py b/sefaria/helper/normalization.py index 0e72255047..29c1a2e5c1 100644 --- a/sefaria/helper/normalization.py +++ b/sefaria/helper/normalization.py @@ -259,7 +259,7 @@ def find_text_to_remove(self, s, **kwargs): apply normalization steps one-by-one and keep track of mapping from one step to the next iteratively apply mappings (in reverse) on each step's removal inds to get inds in original string """ - all_text_to_remove = [] + final_text_to_remove = [] mappings = [] snorm = s for step in self.steps: @@ -271,12 +271,12 @@ def find_text_to_remove(self, s, **kwargs): for mapping in reversed(mappings): text_to_remove_inds = step.convert_normalized_indices_to_unnormalized_indices(text_to_remove_inds, mapping) temp_text_to_remove = list(zip(text_to_remove_inds, text_to_remove_repls)) - all_text_to_remove += [temp_text_to_remove] + + # merge any overlapping ranges + # later edits should override earlier ones + final_text_to_remove = self.merge_removal_inds_new(final_text_to_remove, temp_text_to_remove) mappings += [step.get_mapping_after_normalization(snorm, **kwargs)] snorm = step.normalize(snorm, **kwargs) - # merge any overlapping ranges - # later edits should override earlier ones - final_text_to_remove = reduce(self.merge_removal_inds_new, all_text_to_remove) final_text_to_remove.sort(key=lambda x: x[0]) return final_text_to_remove From 58abd126f929b8ab0452d3816f997a258f5d4d73 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Tue, 29 Aug 2023 15:32:39 -0400 Subject: [PATCH 174/756] chore(strapi-cms): Add .node-version to allow usage of nodenv --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index dcbcedb487..4cac56b0bb 100644 --- a/.gitignore +++ b/.gitignore @@ -81,6 +81,7 @@ node_modules ##################### venv/ .python-version +.node-version # Partner files # ################# From 9c8d3c4833f29e61830d6e81bf03c83d458537c4 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Tue, 29 Aug 2023 15:33:39 -0400 Subject: [PATCH 175/756] fix(strapi-cms): Temporary fix to allow media asset URLs to work both in production and locally --- static/js/Promotions.jsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/static/js/Promotions.jsx b/static/js/Promotions.jsx index aec8d21c36..ba74d3e8e1 100644 --- a/static/js/Promotions.jsx +++ b/static/js/Promotions.jsx @@ -168,7 +168,10 @@ const SidebarAd = React.memo(({ context, matchingAd }) => { > {matchingAd.buttonIcon?.data ? ( <img - src={STRAPI_INSTANCE + matchingAd.buttonIcon.data.attributes.url} + // TODO: Create middleware to handle serving media assets to distinguish between different environments + // The absolute path is needed for debugging purposes to get the media asset from the local Strapi server + // The local Strapi instance provides a relative path through the API + src={(matchingAd.debug ? STRAPI_INSTANCE : '') + matchingAd.buttonIcon.data.attributes.url} alt={matchingAd.buttonIcon.data.attributes.alternativeText} aria-hidden="true" /> From d064faadda95af3947e11753160ef8103e0b78e2 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Tue, 29 Aug 2023 19:07:33 -0400 Subject: [PATCH 176/756] docs(strapi-cms): Add lots of documentation explaining nuanced parts of the code --- static/js/Misc.jsx | 44 +++++++++++++--------------------------- static/js/Promotions.jsx | 9 ++++---- static/js/ReaderApp.jsx | 6 ++++-- static/js/TopicPage.jsx | 2 +- static/js/context.js | 30 ++++++++++++++------------- 5 files changed, 40 insertions(+), 51 deletions(-) diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 96dd16423d..a9fd787f1f 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -2056,32 +2056,43 @@ SignUpModal.propTypes = { modalContent: PropTypes.object.isRequired, }; - +// Have a callback function run when its children (element or nested group of elements) become fully visible within the viewport function OnInView({ children, onVisible }) { - const elementRef = useRef(); + // Get a mutable reference object for the child and/or children to be rendered within this component wrapped in a div + // The reference is attached to the DOM element returned from this component + // This allows us to access properties of the DOM element directly + const elementRef = useRef(); useEffect(() => { const observer = new IntersectionObserver( + // Callback function will be invoked whenever the visibility of the observed element changes (entries) => { const [entry] = entries; + // Check if the observed element is intersecting with the viewport (it's visible) + // Invoke provided prop callback for analytics purposes if (entry.isIntersecting) { onVisible(); } }, + // The entire element must be entirely visible { threshold: 1 } ); + // Start observing the element, but wait until the element exists if (elementRef.current) { observer.observe(elementRef.current); } + // Cleanup when the component unmounts return () => { + // Stop observing the element when it's no longer on the screen and can't be visible if (elementRef.current) { observer.unobserve(elementRef.current); } }; }, [onVisible]); + // Attach elementRef to a div wrapper and pass the children to be rendered within it return <div ref={elementRef}>{children}</div>; } @@ -2116,11 +2127,7 @@ const InterruptingMessage = ({ }); }; - // Need to figure out caching for Strapi so multiple queries aren't made on different page loads - // Use user context to determine whether this is valid for a user? - // Maybe user context should be used to find if there's a compatible modal const shouldShow = () => { - console.log("checking whether to show modal or not"); if (!strapi.modal) return false; if (Sefaria.interfaceLang === 'hebrew' && !strapi.modal.locales.includes('he')) return false; if ( @@ -2129,7 +2136,6 @@ const InterruptingMessage = ({ ) ) return false; - console.log('lets check a modal'); let shouldShowModal = false; @@ -2162,7 +2168,6 @@ const InterruptingMessage = ({ const closeModal = (eventDescription) => { if (onClose) onClose(); - console.log(eventDescription); markModalAsHasBeenInteractedWith( strapi.modal.internalModalName ); @@ -2183,11 +2188,8 @@ const InterruptingMessage = ({ }, [strapi.modal]); // execute useEffect when the modal changes if (!interruptingMessageShowDelayHasElapsed) return null; - console.log("data for the component"); - console.log(strapi.modal); if (!hasInteractedWithModal) { - console.log("rendering component"); return ( <OnInView onVisible={trackModalImpression}> <div id="interruptingMessageBox" className={interruptingMessageShowDelayHasElapsed ? "" : "hidden"}> @@ -2279,7 +2281,6 @@ const Banner = ({ onClose }) => { }; const trackBannerImpression = () => { - console.log("We've got visibility!"); gtag("event", "banner_viewed", { campaignID: strapi.banner.internalBannerName, adType: "banner", @@ -2287,7 +2288,6 @@ const Banner = ({ onClose }) => { }; const shouldShow = () => { - console.log("checking whether to show banner or not"); if (!strapi.banner) return false; if (Sefaria.interfaceLang === 'hebrew' && !strapi.banner.locales.includes('he')) return false; if (hasBannerBeenInteractedWith(strapi.banner.internalBannerName)) @@ -2321,7 +2321,6 @@ const Banner = ({ onClose }) => { const closeBanner = (eventDescription) => { if (onClose) onClose(); - console.log(eventDescription); markBannerAsHasBeenInteractedWith(strapi.banner.internalBannerName); setHasInteractedWithBanner(true); trackBannerInteraction( @@ -2332,7 +2331,6 @@ const Banner = ({ onClose }) => { useEffect(() => { if (shouldShow()) { - console.log("reaching here..."); const timeoutId = setTimeout(() => { // s2 is the div that contains the React root and needs to be manipulated by traditional DOM methods @@ -2343,15 +2341,11 @@ const Banner = ({ onClose }) => { }, strapi.banner.showDelay * 1000); return () => clearTimeout(timeoutId); // clearTimeout on component unmount } - }, [strapi.banner]); // execute useEffect when the modal changes + }, [strapi.banner]); // execute useEffect when the banner changes if (!bannerShowDelayHasElapsed) return null; - console.log("data for the component"); - console.log(strapi.banner); if (!hasInteractedWithBanner) { - console.log("rendering banner"); - console.log(strapi.banner.bannerText); return ( <OnInView onVisible={trackBannerImpression}> <div id="bannerMessage" className={bannerShowDelayHasElapsed ? "" : "hidden"}> @@ -2388,16 +2382,6 @@ const Banner = ({ onClose }) => { Banner.displayName = "Banner"; - -// InterruptingMessage.propTypes = { -// messageName: PropTypes.string.isRequired, -// messageHTML: PropTypes.string.isRequired, -// style: PropTypes.string.isRequired, -// repetition: PropTypes.number.isRequired, -// onClose: PropTypes.func.isRequired -// }; - - const NBox = ({ content, n, stretch, gap=0 }) => { // Wrap a list of elements into an n-column flexbox // If `stretch`, extend the final row into any remaining empty columns diff --git a/static/js/Promotions.jsx b/static/js/Promotions.jsx index ba74d3e8e1..a8cb3ded22 100644 --- a/static/js/Promotions.jsx +++ b/static/js/Promotions.jsx @@ -12,8 +12,6 @@ const Promotions = () => { useEffect(() => { if (strapi.dataFromStrapiHasBeenReceived) { Sefaria._inAppAds = []; - console.log("we got some data"); - console.log(JSON.stringify(strapi.strapiData, null, 2)); const sidebarAds = strapi.strapiData?.sidebarAds?.data; @@ -47,6 +45,7 @@ const Promotions = () => { }, debug: sidebarAd.debug, }); + // Add a separate ad if there's a Hebrew translation. There can't be an ad with only Hebrew if (sidebarAd.localizations?.data?.length) { const hebrewAttributes = sidebarAd.localizations.data[0].attributes; const [buttonText, bodyText, buttonURL, title] = [ @@ -80,13 +79,13 @@ const Promotions = () => { } } }, [strapi.dataFromStrapiHasBeenReceived]); - // empty array happens when the page loads, equivalent of didcomponentmount // dataFromStrapiHasBeenReceived will originally be null until that part is scheduled and executed + useEffect(() => { if (inAppAds) { setMatchingAds(getCurrentMatchingAds()); } - }, [context, inAppAds]); // when state changes, the effect will run + }, [context, inAppAds]); function showToUser(ad) { @@ -153,6 +152,8 @@ function trackSidebarAdClick(ad) { }); } +// Don't continuously rerender a SidebarAd if the parent component decides to rerender +// This is done to prevent multiple views from registering from OnInView const SidebarAd = React.memo(({ context, matchingAd }) => { const classes = classNames({ sidebarPromo: 1, diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index 23fe95e32d..003017184f 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -2159,7 +2159,7 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { {panels} </div>) : null; - const sefariaModal = ( + const signUpModal = ( <SignUpModal onClose={this.toggleSignUpModal} show={this.state.showSignUpModal} @@ -2177,6 +2177,8 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { var classes = classNames(classDict); return ( + // The Strapi context is put at the highest level of scope so any component or children within ReaderApp can use the static content received + // InterruptingMessage modals and Banners will always render if available but stay hidden initially <StrapiDataProvider> <AdContext.Provider value={this.getUserContext()}> <div id="readerAppWrap"> @@ -2185,7 +2187,7 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { <div className={classes} onClick={this.handleInAppLinkClick}> {header} {panels} - {sefariaModal} + {signUpModal} {communityPagePreviewControls} <CookiesNotification /> </div> diff --git a/static/js/TopicPage.jsx b/static/js/TopicPage.jsx index 04f8d2dbe6..93654ff1f5 100644 --- a/static/js/TopicPage.jsx +++ b/static/js/TopicPage.jsx @@ -526,7 +526,7 @@ const TopicPage = ({ timePeriod={topicData.timePeriod} properties={topicData.properties} /> - {!topicData.isLoading && <Promotions/>} + {!topicData.isLoading && <Promotions/> /* Dont allow sidebar ads to be shown until the rest of the topics page has loaded */} </> ) : null} </div> diff --git a/static/js/context.js b/static/js/context.js index d90071fdab..81fe5b56e0 100644 --- a/static/js/context.js +++ b/static/js/context.js @@ -26,6 +26,7 @@ export function replaceNewLinesWithLinebreaks(content) { ); } +// Gets data from a Strapi CMS instance to be used for displaying static content function StrapiDataProvider({ children }) { const [dataFromStrapiHasBeenReceived, setDataFromStrapiHasBeenReceived] = useState(false); @@ -49,6 +50,9 @@ function StrapiDataProvider({ children }) { twoWeeksAgo.setDate(currentDate.getDate() - 14); let startDate = getJSONDateStringInLocalTimeZone(twoWeeksAgo); let endDate = getJSONDateStringInLocalTimeZone(twoWeeksFromNow); + // The GraphQL query has a filter for each content type to find instances that have start dates earlier than the present time + // Content type instances will be found that are one month within their start date. Their date range can't exceed one month + // There is no conflict resolution for overlapping timeframes const query = ` query { banners( @@ -199,30 +203,28 @@ function StrapiDataProvider({ children }) { .then((result) => { setStrapiData(result.data); setDataFromStrapiHasBeenReceived(true); - // maybe sort by start date to choose which one should have a greater priority if more than one compatible one exists - // e.g. there are modals with overlapping time frames + + // Decompose the modals and banners from the GraphQL query response for easier handling let modals = result.data?.modals?.data; - console.log(modals); let banners = result.data?.banners?.data; - console.log(banners); const currentDate = new Date(); if (modals?.length) { - // if they end up being sorted, the first one will be the compatible one + // Only one modal can be displayed currently. The first one that matches will be the one shown let modal = modals.find( (modal) => currentDate >= new Date(modal.attributes.modalStartDate) && currentDate <= new Date(modal.attributes.modalEndDate) ); - console.log("found acceptable modal:"); - console.log(modal); if (modal) { - console.log("setting the modal"); + // Check if there is a Hebrew translation for the modal if (modal.attributes.localizations?.data?.length) { let localization_attributes = modal.attributes.localizations.data[0].attributes; + // Ignore the locale because only Hebrew is supported currently let { locale, ...hebrew_attributes } = localization_attributes; + // Iterate over the localizable attributes in parallel to create an object compatible for use in an InterfaceText Object.keys(hebrew_attributes).forEach((attribute) => { modal.attributes[attribute] = { en: modal.attributes[attribute], @@ -249,21 +251,22 @@ function StrapiDataProvider({ children }) { } if (banners?.length) { + // Only one banner can be displayed currently. The first one that matches will be the one shown let banner = banners.find( (b) => currentDate >= new Date(b.attributes.bannerStartDate) && currentDate <= new Date(b.attributes.bannerEndDate) ); - console.log("found acceptable banner:"); - console.log(banner); if (banner) { - console.log("setting the banner"); + // Check if there is a Hebrew translation if (banner.attributes.localizations?.data?.length) { let localization_attributes = banner.attributes.localizations.data[0].attributes; + // Get the hebrew attributes let { locale, ...hebrew_attributes } = localization_attributes; + // Iterate over the localizable attributes in parallel to create an object compatible for use in an InterfaceText Object.keys(hebrew_attributes).forEach((attribute) => { banner.attributes[attribute] = { en: banner.attributes[attribute], @@ -272,7 +275,7 @@ function StrapiDataProvider({ children }) { }); banner.attributes.locales = ["en", "he"]; } else { - // Maybe have the GraphQL return null entries for each key so the same technique can be used from above? + // TODO: Make the GraphQL query return nilable attributes so the attributes (just their keys) can be iterated over within the localization object ["bannerText", "buttonText", "buttonURL"].forEach( (attribute) => { banner.attributes[attribute] = { @@ -284,7 +287,6 @@ function StrapiDataProvider({ children }) { banner.attributes.locales = ["en"]; } setBanner(banner.attributes); - console.log(banner.attributes); } } }) @@ -300,7 +302,7 @@ function StrapiDataProvider({ children }) { <StrapiDataContext.Provider value={{ dataFromStrapiHasBeenReceived, - strapiData, + strapiData, // All the data returned from the GraphQL query is here but only Promotions uses it in this current state modal, banner, }} From 82c52dca3b03260330f95549362c01fa6c7bb3e6 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Wed, 30 Aug 2023 16:16:33 +0300 Subject: [PATCH 177/756] feat(api texts): TextRangeSet and modifying TextRange for it. --- sefaria/model/text.py | 83 ++++++++++++++++++++++++++++++++----------- 1 file changed, 62 insertions(+), 21 deletions(-) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index 83cdb8858c..65a5af3278 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -1739,34 +1739,48 @@ def __call__(cls, *args, **kwargs): return super(TextFamilyDelegator, cls).__call__(*args, **kwargs) -class TextRange(AbstractTextRecord, metaclass=TextFamilyDelegator): +class TextRangeBase: - def __init__(self, oref, lang, vtitle): - if isinstance(oref.index_node, JaggedArrayNode): + def __init__(self, oref): + if isinstance(oref.index_node, JaggedArrayNode): #text cannot be SchemaNode self.oref = oref + elif oref.has_default_child(): #use default child: + self.oref = oref.default_child_ref() else: - child_ref = oref.default_child_ref() - if child_ref == oref: - raise InputError("Can not get TextChunk at this level, please provide a more precise reference") - self.oref = child_ref + raise InputError("Can not get TextRange at this level, please provide a more precise reference") + + +class TextRange(TextRangeBase): + + def __init__(self, oref, lang, vtitle): + super().__init__(oref) self.lang = lang self.vtitle = vtitle self._version = None #todo - what we want to do if the version doesnt exist. for now the getter will cause error self._text = None - def set_version(self): - self._version = Version().load({'actualLanguage': self.lang, 'versionTitle': self.vtitle}, self.oref.part_projection()) - - def get_version(self): - if not self._version: - self.set_version() + @property + def version(self): + if self._version is None: # todo if there is no version it will run any time + self._version = Version().load({'title': self.oref.index.title, 'actualLanguage': self.lang, 'versionTitle': self.vtitle}, + self.oref.part_projection()) return self._version + @version.setter + def version(self, version): #version can be used if version is already in memory + self._validate_version(version) + self._version = version + + def _validate_version(self, version): + if self.lang != version.actualLanguage or self.vtitle != version.versionTitle: + raise InputError("Given version is not matching to given language and versionTitle") + def _trim_text(self, text): """ part_projection trims only the upper level of the jagged array. this function trims its lower levels and get rid of 1 element arrays wrappings """ - for s, section in enumerate(self.oref.toSections[1:], 1): #start cut form end, for cutting from the start will change the indexes + #TODO can we get the specific text directly from mongo? + for s, section in enumerate(self.oref.toSections[1:], 1): #start cut from end, for cutting from the start will change the indexes subtext = reduce(lambda x, _: x[-1], range(s), text) del subtext[section:] for s, section in enumerate(self.oref.sections[1:], 1): @@ -1776,16 +1790,43 @@ def _trim_text(self, text): redundant_depth = len(list(matching_sections)) return reduce(lambda x, _: x[0], range(redundant_depth), text) - def set_text(self): - self._text = self._trim_text(self.get_version().content_node(self.oref.index_node)) - return self._text - - def get_text(self): - if not self._text: - self.set_text() + @property + def text(self): + if self._text is None: + self._text = self._trim_text(self.version.content_node(self.oref.index_node)) #todo if there is no version it will fail return self._text +class TextRangeSet(TextRangeBase, abst.AbstractMongoSet): + """ + this class if for getting by one query a list of TextRange like other classes inheriting from AbstractMongoSet. + but TextRange is not inheriting from AbstarctMongoRecord, so it uses Version as recordClass and + override _read_records for getting TextRange. + """ + recordClass = Version #records will be converted to TextRange by overridden _read_records method + + def __init__(self, oref, version_langs_and_vtitles): + TextRangeBase.__init__(self, oref) + query = {'$or': [ + {'$and': [ + {'title': self.oref.index.title}, + {'actualLanguage': lang}, + {'versionTitle': vtitle} + ]} + for lang, vtitle in version_langs_and_vtitles]} + abst.AbstractMongoSet.__init__(self, query=query, proj=self.oref.part_projection()) + + def _read_records(self): + if self.records is None: + super()._read_records() #this makes self.records list of Version. now we'll convert them to TextRange + for i, rec in enumerate(self.records): + text_range = TextRange(self.oref, rec.actualLanguage, rec.versionTitle) + text_range.version = rec + self.records[i] = text_range + + #TODO should override some unsupported methods of AbstractMongoSet - for now: update, save, delete, contents + + class TextChunk(AbstractTextRecord, metaclass=TextFamilyDelegator): """ A chunk of text corresponding to the provided :class:`Ref`, language, and optional version name. From b542605a37744709ea31f5ec864512a4bacacc5c Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Thu, 31 Aug 2023 14:18:27 +0300 Subject: [PATCH 178/756] helm: use single quotes --- .../templates/configmap/local-settings-file.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml b/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml index 838848cdc4..5640173d53 100644 --- a/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml +++ b/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml @@ -136,7 +136,7 @@ data: SERVER_EMAIL = os.getenv("SERVER_EMAIL") auth_str = f'{os.getenv("ELASTIC_USERNAME")}:{os.getenv("ELASTIC_PASSWORD")}@' if os.getenv("ELASTIC_USERNAME") else '' - SEARCH_ADMIN = f'http://{auth_str}' + {{ .Values.nginx.SEARCH_HOST | quote }} + ":9200" + SEARCH_ADMIN = f'http://{auth_str}' + {{ .Values.nginx.SEARCH_HOST | quote }} + ':9200' SEARCH_INDEX_ON_SAVE = True SEARCH_INDEX_NAME_TEXT = 'text' # name of the ElasticSearch index to use SEARCH_INDEX_NAME_SHEET = 'sheet' From af865685c12bc171194bfea46e618370e27be293 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Thu, 31 Aug 2023 14:25:36 +0300 Subject: [PATCH 179/756] helm: use double quotes --- .../templates/configmap/local-settings-file.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml b/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml index 5640173d53..838848cdc4 100644 --- a/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml +++ b/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml @@ -136,7 +136,7 @@ data: SERVER_EMAIL = os.getenv("SERVER_EMAIL") auth_str = f'{os.getenv("ELASTIC_USERNAME")}:{os.getenv("ELASTIC_PASSWORD")}@' if os.getenv("ELASTIC_USERNAME") else '' - SEARCH_ADMIN = f'http://{auth_str}' + {{ .Values.nginx.SEARCH_HOST | quote }} + ':9200' + SEARCH_ADMIN = f'http://{auth_str}' + {{ .Values.nginx.SEARCH_HOST | quote }} + ":9200" SEARCH_INDEX_ON_SAVE = True SEARCH_INDEX_NAME_TEXT = 'text' # name of the ElasticSearch index to use SEARCH_INDEX_NAME_SHEET = 'sheet' From 11a5649e670e95a6cb2798a793b86fa137a13a67 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Thu, 31 Aug 2023 14:27:24 +0300 Subject: [PATCH 180/756] helm: use double quotes --- .../templates/configmap/local-settings-file.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml b/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml index 838848cdc4..5640173d53 100644 --- a/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml +++ b/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml @@ -136,7 +136,7 @@ data: SERVER_EMAIL = os.getenv("SERVER_EMAIL") auth_str = f'{os.getenv("ELASTIC_USERNAME")}:{os.getenv("ELASTIC_PASSWORD")}@' if os.getenv("ELASTIC_USERNAME") else '' - SEARCH_ADMIN = f'http://{auth_str}' + {{ .Values.nginx.SEARCH_HOST | quote }} + ":9200" + SEARCH_ADMIN = f'http://{auth_str}' + {{ .Values.nginx.SEARCH_HOST | quote }} + ':9200' SEARCH_INDEX_ON_SAVE = True SEARCH_INDEX_NAME_TEXT = 'text' # name of the ElasticSearch index to use SEARCH_INDEX_NAME_SHEET = 'sheet' From ed5c31c98c9807e4362203f98f3df4fa6cba8f59 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Thu, 31 Aug 2023 14:50:34 +0300 Subject: [PATCH 181/756] helm: use double quotes --- .../templates/configmap/local-settings-file.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml b/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml index 5640173d53..838848cdc4 100644 --- a/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml +++ b/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml @@ -136,7 +136,7 @@ data: SERVER_EMAIL = os.getenv("SERVER_EMAIL") auth_str = f'{os.getenv("ELASTIC_USERNAME")}:{os.getenv("ELASTIC_PASSWORD")}@' if os.getenv("ELASTIC_USERNAME") else '' - SEARCH_ADMIN = f'http://{auth_str}' + {{ .Values.nginx.SEARCH_HOST | quote }} + ':9200' + SEARCH_ADMIN = f'http://{auth_str}' + {{ .Values.nginx.SEARCH_HOST | quote }} + ":9200" SEARCH_INDEX_ON_SAVE = True SEARCH_INDEX_NAME_TEXT = 'text' # name of the ElasticSearch index to use SEARCH_INDEX_NAME_SHEET = 'sheet' From 4297412cccd03be41df2bd96462a41593c451349 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Sun, 3 Sep 2023 12:58:42 +0300 Subject: [PATCH 182/756] refactor(api texts): replace TextHandler (and TextRangeSet) by TextManager. --- api/views.py | 25 +++++- sefaria/model/text.py | 40 +--------- .../model/text_manager.py | 76 +++++++++---------- 3 files changed, 57 insertions(+), 84 deletions(-) rename api/texts_api.py => sefaria/model/text_manager.py (65%) diff --git a/api/views.py b/api/views.py index 9678ba210a..5a3eb6a1dd 100644 --- a/api/views.py +++ b/api/views.py @@ -1,8 +1,9 @@ from sefaria.model import * -from .texts_api import TextsForClientHandler +from sefaria.model.text_manager import TextManager from sefaria.client.util import jsonResponse from django.views import View from typing import List +from .api_warnings import * class Text(View): @@ -22,13 +23,29 @@ def split_piped_params(params_string) -> List[str]: params[1] = params[1].replace('_', ' ') return params + def _handle_warnings(self, data): + data['warnings'] = [] + for lang, vtitle in data['missings']: + if lang == 'source': + warning = APINoSourceText(self.oref) + elif vtitle and vtitle != 'all': + warning = APINoVersion(self.oref, vtitle, lang) + else: + warning = APINoLanguageVersion(self.oref, data['availabe_langs']) + representing_string = f'{lang}|{vtitle}' if vtitle else lang + data['warnings'].append({representing_string: warning.get_message()}) + data.pop('missings') + data.pop('availabe_langs') + return data + def get(self, request, *args, **kwargs): if self.oref.is_empty(): return jsonResponse({'error': f'We have no text for {self.oref}.'}, status=400) versions_params = request.GET.getlist('version', []) if not versions_params: versions_params = ['base'] - versions_params = [self.split_piped_params(param_str) + [param_str] for param_str in versions_params] - handler = TextsForClientHandler(self.oref, versions_params) - data = handler.get_versions_for_query() + versions_params = [self.split_piped_params(param_str) for param_str in versions_params] + text_manager = TextManager(self.oref, versions_params) + data = text_manager.get_versions_for_query() + data = self._handle_warnings(data) return jsonResponse(data) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index 65a5af3278..1897d4c6d0 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -1739,21 +1739,15 @@ def __call__(cls, *args, **kwargs): return super(TextFamilyDelegator, cls).__call__(*args, **kwargs) -class TextRangeBase: +class TextRange: - def __init__(self, oref): + def __init__(self, oref, lang, vtitle): if isinstance(oref.index_node, JaggedArrayNode): #text cannot be SchemaNode self.oref = oref elif oref.has_default_child(): #use default child: self.oref = oref.default_child_ref() else: raise InputError("Can not get TextRange at this level, please provide a more precise reference") - - -class TextRange(TextRangeBase): - - def __init__(self, oref, lang, vtitle): - super().__init__(oref) self.lang = lang self.vtitle = vtitle self._version = None #todo - what we want to do if the version doesnt exist. for now the getter will cause error @@ -1797,36 +1791,6 @@ def text(self): return self._text -class TextRangeSet(TextRangeBase, abst.AbstractMongoSet): - """ - this class if for getting by one query a list of TextRange like other classes inheriting from AbstractMongoSet. - but TextRange is not inheriting from AbstarctMongoRecord, so it uses Version as recordClass and - override _read_records for getting TextRange. - """ - recordClass = Version #records will be converted to TextRange by overridden _read_records method - - def __init__(self, oref, version_langs_and_vtitles): - TextRangeBase.__init__(self, oref) - query = {'$or': [ - {'$and': [ - {'title': self.oref.index.title}, - {'actualLanguage': lang}, - {'versionTitle': vtitle} - ]} - for lang, vtitle in version_langs_and_vtitles]} - abst.AbstractMongoSet.__init__(self, query=query, proj=self.oref.part_projection()) - - def _read_records(self): - if self.records is None: - super()._read_records() #this makes self.records list of Version. now we'll convert them to TextRange - for i, rec in enumerate(self.records): - text_range = TextRange(self.oref, rec.actualLanguage, rec.versionTitle) - text_range.version = rec - self.records[i] = text_range - - #TODO should override some unsupported methods of AbstractMongoSet - for now: update, save, delete, contents - - class TextChunk(AbstractTextRecord, metaclass=TextFamilyDelegator): """ A chunk of text corresponding to the provided :class:`Ref`, language, and optional version name. diff --git a/api/texts_api.py b/sefaria/model/text_manager.py similarity index 65% rename from api/texts_api.py rename to sefaria/model/text_manager.py index 3f15d2f02f..c8508c3f5a 100644 --- a/api/texts_api.py +++ b/sefaria/model/text_manager.py @@ -1,69 +1,62 @@ import django django.setup() +from sefaria.model import * from sefaria.utils.hebrew import hebrew_term -from .api_warnings import * from typing import List - -class TextsForClientHandler(): - """ - process api calls for text - return_obj is dict that includes in its root: - ref and index data - 'versions' - list of versions details and text - 'warning' - for any version_params that has an warning - """ - +class TextManager: ALL = 'all' BASE = 'base' SOURCE = 'source' + TRANSLATION = 'translation' def __init__(self, oref: Ref, versions_params: List[List[str]]): self.versions_params = versions_params self.oref = oref self.handled_version_params = [] - self.all_versions = self.oref.version_list() - self.return_obj = {'versions': [], 'warnings': []} + self.all_versions = self.oref.versionset() + self.return_obj = { + 'versions': [], + 'missings': [], + 'availabe_langs': sorted({v.actualLanguage for v in self.all_versions}) + } - def _handle_warnings(self, lang: str, vtitle: str, params_str: str) -> None: - if lang == self.SOURCE: - warning = APINoSourceText(self.oref) - elif vtitle and vtitle != self.ALL: - warning = APINoVersion(self.oref, vtitle, lang) - else: - availabe_langs = {v['actualLanguage'] for v in self.all_versions} - warning = APINoLanguageVersion(self.oref, sorted(availabe_langs)) - representing_string = params_str or f'{lang}|{vtitle}' - self.return_obj['warnings'].append({ - representing_string: warning.get_message() - }) + def _append_version(self, version): + #TODO part of this function duplicate the functionality of Ref.versionlist(). maybe we should mvoe it to Version + fields = Version.optional_attrs + Version.required_attrs + for attr in ['chapter', 'title', 'language']: + fields.remove(attr) + version_details = {f: getattr(version, f, "") for f in fields} + text_range = TextRange(self.oref, version.actualLanguage, version.versionTitle) + text_range.version = version + version_details['text'] = text_range.text + if self.oref.is_book_level(): + first_section_ref = version.first_section_ref() or version.get_index().nodes.first_leaf().first_section_ref() + version_details['firstSectionRef'] = first_section_ref.normal() + self.return_obj['versions'].append(version_details) - def _append_required_versions(self, lang: str, vtitle: str, params_str: str) -> None: + def _append_required_versions(self, lang: str, vtitle: str) -> None: if lang == self.BASE: - lang_condition = lambda v: v['isBaseText2'] #temporal name + lang_condition = lambda v: getattr(v, 'isBaseText2', False) # temporal name elif lang == self.SOURCE: - lang_condition = lambda v: v['isSource'] + lang_condition = lambda v: getattr(v, 'isSource', False) + elif lang == self.TRANSLATION: + lang_condition = lambda v: not getattr(v, 'isSource', False) elif lang: - lang_condition = lambda v: v['actualLanguage'] == lang + lang_condition = lambda v: v.actualLanguage == lang else: lang_condition = lambda v: True if vtitle and vtitle != self.ALL: - versions = [v for v in self.all_versions if lang_condition(v) and v['versionTitle'] == vtitle] + versions = [v for v in self.all_versions if lang_condition(v) and v.versionTitle == vtitle] else: versions = [v for v in self.all_versions if lang_condition(v)] if vtitle != self.ALL and versions: - versions = [max(versions, key=lambda v: v['priority'] or 0)] + versions = [max(versions, key=lambda v: getattr(v, 'priority', 0))] for version in versions: if version not in self.return_obj['versions']: #do not return the same version even if included in two different version params - self.return_obj['versions'].append(version) + self._append_version(version) if not versions: - self._handle_warnings(lang, vtitle, params_str) - - def _add_text_to_versions(self) -> None: - for version in self.return_obj['versions']: - version.pop('title') - version.pop('language') #should be removed after language is removed from attrs - version['text'] = TextRange(self.oref, version['actualLanguage'], version['versionTitle']).text + self.return_obj['missings'].append((lang, vtitle)) def _add_ref_data_to_return_obj(self) -> None: oref = self.oref @@ -119,9 +112,8 @@ def _add_node_data_to_return_obj(self) -> None: }) def get_versions_for_query(self) -> dict: - for lang, vtitle, params_str in self.versions_params: - self._append_required_versions(lang, vtitle, params_str) - self._add_text_to_versions() + for lang, vtitle in self.versions_params: + self._append_required_versions(lang, vtitle) self._add_ref_data_to_return_obj() self._add_index_data_to_return_obj() self._add_node_data_to_return_obj() From b9fce2822a666e69dd99c24dac70c26e08010a15 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Sun, 3 Sep 2023 13:13:36 +0300 Subject: [PATCH 183/756] feat(api texts): warning for no translation. --- api/api_warnings.py | 7 +++++++ api/views.py | 3 ++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/api/api_warnings.py b/api/api_warnings.py index 40cb37b1cc..586802a664 100644 --- a/api/api_warnings.py +++ b/api/api_warnings.py @@ -52,3 +52,10 @@ class APINoSourceText(TextsAPIResponseMessage): def __init__(self, oref: Ref): self.warning_code = APIWarningCode.APINoSourceText.value self.message = f'We do not have the source text for {oref}' + + +class APINoTranslationText(TextsAPIResponseMessage): + + def __init__(self, oref: Ref): + self.warning_code = APIWarningCode.APINoSourceText.value + self.message = f'We do not have a translation for {oref}' diff --git a/api/views.py b/api/views.py index 5a3eb6a1dd..9577f5967d 100644 --- a/api/views.py +++ b/api/views.py @@ -2,7 +2,6 @@ from sefaria.model.text_manager import TextManager from sefaria.client.util import jsonResponse from django.views import View -from typing import List from .api_warnings import * @@ -28,6 +27,8 @@ def _handle_warnings(self, data): for lang, vtitle in data['missings']: if lang == 'source': warning = APINoSourceText(self.oref) + elif lang == 'translation': + warning = APINoTranslationText(self.oref) elif vtitle and vtitle != 'all': warning = APINoVersion(self.oref, vtitle, lang) else: From fbd334a0b26bc1a922b22ccfa882bb05f314fadf Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Sun, 3 Sep 2023 14:23:50 +0300 Subject: [PATCH 184/756] fix(api texts): error code for no translation. --- api/api_warnings.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api/api_warnings.py b/api/api_warnings.py index 586802a664..7c8b5ccb44 100644 --- a/api/api_warnings.py +++ b/api/api_warnings.py @@ -8,6 +8,7 @@ class APIWarningCode(Enum): APINoVersion = 101 APINoLanguageVersion = 102 APINoSourceText = 103 + APINoTranslationText = 104 """ classes for data warnings in API calls. @@ -57,5 +58,5 @@ def __init__(self, oref: Ref): class APINoTranslationText(TextsAPIResponseMessage): def __init__(self, oref: Ref): - self.warning_code = APIWarningCode.APINoSourceText.value + self.warning_code = APIWarningCode.APINoTranslationText.value self.message = f'We do not have a translation for {oref}' From d2c76920c415956077643cd2ce36f363d4194a0c Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Sun, 3 Sep 2023 14:23:58 +0300 Subject: [PATCH 185/756] test(api texts): tests for translation. --- api/tests.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/api/tests.py b/api/tests.py index 05398172a0..0bf2167dd9 100644 --- a/api/tests.py +++ b/api/tests.py @@ -33,6 +33,17 @@ def test_api_get_text_source_all(self): self.assertEqual(data["sections"], ["22a"]) self.assertEqual(data["toSections"], ["22a"]) + def test_api_get_text_translation_all(self): + response = c.get('/api/v3/texts/Shabbat.22a?version=translation|all') + self.assertEqual(200, response.status_code) + data = json.loads(response.content) + self.assertTrue(len(data["versions"]) > 1) + self.assertTrue(any(v['actualLanguage'] == 'en' for v in data["versions"])) + self.assertEqual(data["book"], "Shabbat") + self.assertEqual(data["categories"], ["Talmud", "Bavli", "Seder Moed"]) + self.assertEqual(data["sections"], ["22a"]) + self.assertEqual(data["toSections"], ["22a"]) + def test_api_get_text_lang_all(self): response = c.get('/api/v3/texts/Rashi_on_Genesis.2.3?version=en|all') self.assertEqual(200, response.status_code) @@ -114,6 +125,14 @@ def test_api_get_text_no_source(self): self.assertEqual(data['warnings'][0]['source']['warning_code'], APIWarningCode.APINoSourceText.value) self.assertEqual(data['warnings'][0]['source']['message'], 'We do not have the source text for The Book of Maccabees I 1') + def test_api_get_text_no_translation(self): + response = c.get("/api/v3/texts/Shuvi_Shuvi_HaShulamit?version=translation") + self.assertEqual(200, response.status_code) + data = json.loads(response.content) + self.assertEqual(len(data["versions"]), 0) + self.assertEqual(data['warnings'][0]['translation']['warning_code'], APIWarningCode.APINoTranslationText.value) + self.assertEqual(data['warnings'][0]['translation']['message'], 'We do not have a translation for Shuvi Shuvi HaShulamit') + def test_api_get_text_no_language(self): response = c.get("/api/v3/texts/The_Book_of_Maccabees_I.1?version=en|Brenton's_Septuagint&version=sgrg|all") self.assertEqual(200, response.status_code) From 113dea153fd0f57fd2f9794d95d09614f6d62d20 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Sun, 3 Sep 2023 16:41:00 +0300 Subject: [PATCH 186/756] chore: deprecate errorMargin --- sefaria/client/wrapper.py | 11 +--- sefaria/model/text.py | 128 +++++++++----------------------------- 2 files changed, 30 insertions(+), 109 deletions(-) diff --git a/sefaria/client/wrapper.py b/sefaria/client/wrapper.py index f5af482b56..26e70bb73f 100644 --- a/sefaria/client/wrapper.py +++ b/sefaria/client/wrapper.py @@ -54,16 +54,9 @@ def format_link_object_for_client(link, with_text, ref, pos=None): com["sourceVersion"] = {"title": link.versions[linkPos]["title"], "language": link.versions[linkPos].get("language", None)} com["displayedText"] = link.displayedText[linkPos] # we only want source displayedText - compDate = getattr(linkRef.index, "compDate", None) + compDate = getattr(linkRef.index, "compDate", None) # default comp date to in the future if compDate: - try: - com["compDate"] = int(compDate) - except ValueError: - com["compDate"] = 3000 # default comp date to in the future - try: - com["errorMargin"] = int(getattr(linkRef.index, "errorMargin", 0)) - except ValueError: - com["errorMargin"] = 0 + com["compDate"] = compDate # Pad out the sections list, so that comparison between comment numbers are apples-to-apples lsections = linkRef.sections[:] + [0] * (linkRef.index_node.depth - len(linkRef.sections)) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index 27babfefa0..9e7084d5a1 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -203,7 +203,6 @@ class Index(abst.AbstractMongoRecord, AbstractIndex): "compDate", "compPlace", "pubPlace", - "errorMargin", "era", "dependence", # (str) Values: "Commentary" or "Targum" - to denote commentaries and other potential not standalone texts "base_text_titles", # (list) the base book(s) this one is dependant on @@ -307,30 +306,20 @@ def expand_metadata_on_contents(self, contents): contents["base_text_titles"] = [{"en": btitle, "he": hebrew_term(btitle)} for btitle in self.base_text_titles] contents["heCategories"] = list(map(hebrew_term, self.categories)) + contents = self.time_period_and_place_contents(contents) + return contents - - composition_time_period = self.composition_time_period() - if composition_time_period: - contents["compDateString"] = { - "en": composition_time_period.period_string("en"), - "he": composition_time_period.period_string("he"), - } - - - composition_place = self.composition_place() - if composition_place: - contents["compPlaceString"] = { - "en": composition_place.primary_name("en"), - "he": composition_place.primary_name("he"), - } - - pub_place = self.publication_place() - if pub_place: - contents["pubPlaceString"] = { - "en": pub_place.primary_name("en"), - "he": pub_place.primary_name("he"), - } - + def time_period_and_place_contents(self, contents): + """ Used to expand contents for date and time info """ + for k, f in [("compDateString", self.composition_time_period), ("pubDateString", self.publication_time_period)]: + time_period = f() + if time_period: + contents[k] = {"en": time_period.period_string('en'), 'he': time_period.period_string('he')} + + for k, f in [("compPlaceString", self.composition_place), ("pubPlaceString", self.publication_place)]: + place = f() + if place: + contents[k] = {"en": place.primary_name('en'), 'he': place.primary_name('he')} return contents def _saveable_attrs(self): @@ -444,7 +433,7 @@ def publication_place(self): # This is similar to logic on GardenStop def composition_time_period(self): - return self._get_time_period("compDate", "errorMargin") + return self._get_time_period("compDate") def publication_time_period(self): return self._get_time_period("pubDate") @@ -454,77 +443,27 @@ def best_time_period(self): :return: TimePeriod: First tries to return `compDate`. Deals with ranges and negative values for compDate If no compDate, looks at author info """ - start, end, startIsApprox, endIsApprox = None, None, None, None - if getattr(self, "compDate", None): - errorMargin = int(getattr(self, "errorMargin", 0)) - self.startIsApprox = self.endIsApprox = errorMargin > 0 - - try: - year = int(getattr(self, "compDate")) - start = year - errorMargin - end = year + errorMargin - except ValueError as e: - years = getattr(self, "compDate").split("-") - if years[0] == "" and len(years) == 3: #Fix for first value being negative - years[0] = -int(years[1]) - years[1] = int(years[2]) - try: - start = int(years[0]) - errorMargin - end = int(years[1]) + errorMargin - except UnicodeEncodeError as e: - pass - + return self._get_time_period('compDate') else: author = self.author_objects()[0] if len(self.author_objects()) > 0 else None tp = author and author.most_accurate_time_period() - if tp is not None: - tpvars = vars(tp) - start = tp.start if "start" in tpvars else None - end = tp.end if "end" in tpvars else None - startIsApprox = tp.startIsApprox if "startIsApprox" in tpvars else None - endIsApprox = tp.endIsApprox if "endIsApprox" in tpvars else None - - if not start is None: - from sefaria.model.timeperiod import TimePeriod - if not startIsApprox is None: - return TimePeriod({ - "start": start, - "end": end, - "startIsApprox": startIsApprox, - "endIsApprox": endIsApprox - }) - else: - return TimePeriod({ - "start": start, - "end": end - }) + return tp - def _get_time_period(self, date_field, margin_field=None): + def _get_time_period(self, date_field): + """ + Assumes that value of `date_field` ('pubDate' or 'compDate') is a list of integers. + """ from . import timeperiod if not getattr(self, date_field, None): return None - - try: - error_margin = int(getattr(self, margin_field, 0)) if margin_field else 0 - except ValueError: - error_margin = 0 - startIsApprox = endIsApprox = error_margin > 0 - - try: - year = int(getattr(self, date_field)) - start = year - error_margin - end = year + error_margin - except ValueError as e: - try: - years = getattr(self, date_field).split("-") - if years[0] == "" and len(years) == 3: #Fix for first value being negative - years[0] = -int(years[1]) - years[1] = int(years[2]) - start = int(years[0]) - error_margin - end = int(years[1]) + error_margin - except ValueError as e: - return None + years = getattr(self, date_field) + if len(years) > 1: + start, end = years + startIsApprox = endIsApprox = True + else: + start = end = years[0] + startIsApprox = endIsApprox = False return timeperiod.TimePeriod({ "start": start, "startIsApprox": startIsApprox, @@ -688,16 +627,10 @@ def _normalize(self): "lengths", # optional for old style "transliteratedTitle",# optional for old style """ - deprecated_attrs = ["titleVariants","sectionNames","heTitle","heTitleVariants","maps","length","lengths", "transliteratedTitle"] + deprecated_attrs = ["titleVariants","sectionNames","heTitle","heTitleVariants","maps","length","lengths", "transliteratedTitle", "errorMargin"] for attr in deprecated_attrs: if getattr(self, attr, None): delattr(self, attr) - try: - error_margin_value = getattr(self, "errorMargin", 0) - int(error_margin_value) - except ValueError: - logger.warning("Index record '{}' has invalid 'errorMargin': {} field, removing".format(self.title, error_margin_value)) - delattr(self, "errorMargin") def _update_alt_structs_on_title_change(self): old_title = self.pkeys_orig_values["title"] @@ -758,11 +691,6 @@ def _validate(self): if getattr(self, "collective_title", None) and not hebrew_term(getattr(self, "collective_title", None)): raise InputError("You must add a hebrew translation Term for any new Collective Title: {}.".format(self.collective_title)) - try: - int(getattr(self, "errorMargin", 0)) - except (ValueError): - raise InputError("composition date error margin must be an integer") - #complex style records- all records should now conform to this if self.nodes: # Make sure that all primary titles match From 917bc1c3bde9081fa43f863a99d2c3eba0d82581 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Mon, 4 Sep 2023 09:20:58 +0300 Subject: [PATCH 187/756] fix(Search Test): modify text in the exact same way in test as in app --- sefaria/search.py | 28 ++++++++++++++++------------ sefaria/tests/search.py | 9 +++------ 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/sefaria/search.py b/sefaria/search.py index 3bf18fec0f..d42d39e614 100644 --- a/sefaria/search.py +++ b/sefaria/search.py @@ -613,6 +613,17 @@ def remove_footnotes(cls, content): content = AbstractTextRecord.strip_itags(content) return content + @classmethod + def modify_text_in_doc(cls, content): + content = AbstractTextRecord.strip_imgs(content) + content = cls.remove_footnotes(content) + content = strip_cantillation(content, strip_vowels=False).strip() + content = re.sub(r'<[^>]+>', ' ', content) # replace HTML tags with space so that words dont get smushed together + content = re.sub(r'\([^)]+\)', ' ', content) # remove all parens + while " " in content: # make sure there are not many spaces in a row + content = content.replace(" ", " ") + return content + @classmethod def make_text_index_document(cls, tref, heTref, version, lang, version_priority, content, categories, hebrew_version_title): """ @@ -621,15 +632,8 @@ def make_text_index_document(cls, tref, heTref, version, lang, version_priority, # Don't bother indexing if there's no content if not content: return False - content = AbstractTextRecord.strip_imgs(content) - content = cls.remove_footnotes(content) - content_wo_cant = strip_cantillation(content, strip_vowels=False).strip() - content_wo_cant = re.sub(r'<[^>]+>', ' ', content_wo_cant) # replace HTML tags with space so that words dont get smushed together - content_wo_cant = re.sub(r'\([^)]+\)', ' ', content_wo_cant) # remove all parens - while " " in content_wo_cant: # make sure there are not many spaces in a row - content_wo_cant = content_wo_cant.replace(" ", " ") - - if len(content_wo_cant) == 0: + content = cls.modify_text_in_doc(content) + if len(content) == 0: return False oref = Ref(tref) @@ -657,9 +661,9 @@ def make_text_index_document(cls, tref, heTref, version, lang, version_priority, "path": "/".join(indexed_categories + [cls.curr_index.title]), "pagesheetrank": pagesheetrank, "comp_date": comp_start_date, - #"hebmorph_semi_exact": content_wo_cant, - "exact": content_wo_cant, - "naive_lemmatizer": content_wo_cant, + #"hebmorph_semi_exact": content, + "exact": content, + "naive_lemmatizer": content, 'hebrew_version_title': hebrew_version_title, } diff --git a/sefaria/tests/search.py b/sefaria/tests/search.py index a04a368599..a47f146637 100644 --- a/sefaria/tests/search.py +++ b/sefaria/tests/search.py @@ -24,10 +24,7 @@ def test_make_text_index_document(): ref_data = RefData().load({"ref": tref}) pagesheetrank = ref_data.pagesheetrank if ref_data is not None else RefData.DEFAULT_PAGESHEETRANK - content_wo_cant = strip_cantillation(content, strip_vowels=False).strip() - content_wo_cant = re.sub(r'<[^>]+>', '', content_wo_cant) - content_wo_cant = re.sub(r'\([^)]+\)', '', content_wo_cant) - + content = TI.modify_text_in_doc(content) assert doc == { "ref": tref, "heRef": he_ref, @@ -40,8 +37,8 @@ def test_make_text_index_document(): "path": "/".join(categories + [index.title]), "pagesheetrank": pagesheetrank, "comp_date": comp_date, - "exact": content_wo_cant, - "naive_lemmatizer": content_wo_cant, + "exact": content, + "naive_lemmatizer": content, 'hebrew_version_title': heVtitle, } From 8495c5a847fc38c811aded384e20ffa6b3b67dfa Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Mon, 4 Sep 2023 10:31:57 +0300 Subject: [PATCH 188/756] chore: add test_best_time_period --- sefaria/model/tests/text_test.py | 5 +++++ sefaria/model/text.py | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/sefaria/model/tests/text_test.py b/sefaria/model/tests/text_test.py index 4d372fd55b..00192188e2 100644 --- a/sefaria/model/tests/text_test.py +++ b/sefaria/model/tests/text_test.py @@ -167,6 +167,11 @@ def test_invalid_index_save_no_category(): assert "You must create category Mishnah/Commentary/Bartenura/Gargamel before adding texts to it." in str(e_info.value) assert model.IndexSet({"title": title}).count() == 0 +def test_best_time_period(): + i = model.library.get_index("Rashi on Genesis") + assert i.best_time_period().period_string('en') == ' (c.1075 - c.1105 CE)' + i.compDate = None + assert i.best_time_period().period_string('en') == ' (1040 - 1105 CE)' # now that compDate is None, period_string should return Rashi's birth to death years def test_invalid_index_save_no_hebrew_collective_title(): title = 'Bartenura (The Next Generation)' diff --git a/sefaria/model/text.py b/sefaria/model/text.py index 9e7084d5a1..a37227d855 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -200,6 +200,7 @@ class Index(abst.AbstractMongoRecord, AbstractIndex): "enShortDesc", "heShortDesc", "pubDate", + "hasErrorMargin" "compDate", "compPlace", "pubPlace", @@ -627,7 +628,7 @@ def _normalize(self): "lengths", # optional for old style "transliteratedTitle",# optional for old style """ - deprecated_attrs = ["titleVariants","sectionNames","heTitle","heTitleVariants","maps","length","lengths", "transliteratedTitle", "errorMargin"] + deprecated_attrs = ["titleVariants","sectionNames","heTitle","heTitleVariants","maps","length","lengths", "transliteratedTitle"] for attr in deprecated_attrs: if getattr(self, attr, None): delattr(self, attr) From cd34e50f48052228371ecef94b47aebb6206ebee Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Mon, 4 Sep 2023 15:25:11 +0300 Subject: [PATCH 189/756] chore: remove unnecessary lines in AboutBox regarding compDate and compDateString After refactoring _get_time_period, compDateString will be returned when and only when compDate is present, so the case in AboutBox should not occur --- sefaria/model/text.py | 20 ++++++++--------- static/js/AboutBox.jsx | 18 +++------------ static/js/BookPage.jsx | 51 ++++++++++++++---------------------------- 3 files changed, 29 insertions(+), 60 deletions(-) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index a37227d855..5044b6eabf 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -200,7 +200,7 @@ class Index(abst.AbstractMongoRecord, AbstractIndex): "enShortDesc", "heShortDesc", "pubDate", - "hasErrorMargin" + "hasErrorMargin", # (bool) whether or not compDate is exact. used to be 'errorMargin' which was an integer amount that compDate was off by "compDate", "compPlace", "pubPlace", @@ -456,16 +456,14 @@ def _get_time_period(self, date_field): Assumes that value of `date_field` ('pubDate' or 'compDate') is a list of integers. """ from . import timeperiod - if not getattr(self, date_field, None): - return None - years = getattr(self, date_field) - if len(years) > 1: - start, end = years - startIsApprox = endIsApprox = True - else: - start = end = years[0] - startIsApprox = endIsApprox = False - return timeperiod.TimePeriod({ + years = getattr(self, date_field, None) + if years is not None: + startIsApprox = endIsApprox = getattr(self, 'hasErrorMargin', False) + if len(years) > 1: + start, end = years + else: + start = end = years[0] + return timeperiod.TimePeriod({ "start": start, "startIsApprox": startIsApprox, "end": end, diff --git a/static/js/AboutBox.jsx b/static/js/AboutBox.jsx index f78cb31066..938b389e3e 100644 --- a/static/js/AboutBox.jsx +++ b/static/js/AboutBox.jsx @@ -114,7 +114,6 @@ class AboutBox extends Component { authorsElems[lang] = authorArray.map((author, iauthor) => <span>{iauthor > 0 ? ", " : ""}<a key={author.slug} href={`/topics/${author.slug}`}>{author[lang]}</a></span> ); } } - // use compPlaceString and compDateString if available. then use compPlace o/w use pubPlace o/w nothing let placeTextEn, placeTextHe; if (d.compPlaceString) { placeTextEn = d.compPlaceString.en; @@ -131,20 +130,9 @@ class AboutBox extends Component { if (d.compDateString) { dateTextEn = d.compDateString.en; dateTextHe = d.compDateString.he - } else if (d.compDate) { - if (d.errorMargin !== 0) { - //I don't think there are any texts which are mixed BCE/CE - const lowerDate = Math.abs(d.compDate - d.errorMargin); - const upperDate = Math.abs(d.compDate - d.errorMargin); - dateTextEn = `(c.${lowerDate} - c.${upperDate} ${d.compDate < 0 ? "BCE" : "CE"})`; - dateTextHe = `(${lowerDate} - ${upperDate} ${d.compDate < 0 ? 'לפנה"ס בקירוב' : 'לספירה בקירוב'})`; - } else { - dateTextEn = `(${Math.abs(d.compDate)} ${d.compDate < 0 ? "BCE" : "CE"})`; - dateTextHe = `(${Math.abs(d.compDate)} ${d.compDate < 0 ? 'לפנה"ס בקירוב' : 'לספירה בקירוב'})`; - } - } else if (d.pubDate) { - dateTextEn = `(${Math.abs(d.pubDate)} ${d.pubDate < 0 ? "BCE" : "CE"})`; - dateTextHe = `(${Math.abs(d.pubDate)} ${d.pubDate < 0 ? 'לפנה"ס בקירוב' : 'לספירה בקירוב'})`; + } else if (d.pubDateString) { + dateTextEn = d.pubDateString.en; + dateTextHe = d.pubDateString.he; } const bookPageUrl = "/" + Sefaria.normRef(d.title); detailSection = ( diff --git a/static/js/BookPage.jsx b/static/js/BookPage.jsx index d2fde29d4e..6f21dc9187 100644 --- a/static/js/BookPage.jsx +++ b/static/js/BookPage.jsx @@ -1070,32 +1070,20 @@ const EditTextInfo = function({initTitle, close}) { const [hePubPlace, setHePubPlace] = useState(index.current?.pubPlaceString?.he); const [compPlace, setCompPlace] = useState(index.current?.compPlaceString?.en); const [heCompPlace, setHeCompPlace] = useState(index.current?.compPlaceString?.he); - const [errorMargin, setErrorMargin] = useState(Number(index.current?.errorMargin) || 0); - const getYearAsStr = (initCompDate) => { - if (typeof initCompDate === 'undefined' || initCompDate === '') { + const getYearAsStr = (init) => { + if (typeof init === 'undefined' || init.length === 0) { return ""; + } else if (init.length === 2) { + return `${init[0]}-${init[1]}`; } - else { - initCompDate = String(initCompDate); - let pattern = /(-?\d+)(-?)(-?\d*)/; // this may occur if it is a range. Some books, such as Genesis store compDate as a range - let result = initCompDate.match(pattern); - if (result[2] === "-") { - return initCompDate; - } - else { - initCompDate = Number(initCompDate); - if (errorMargin === 0) { - return `${initCompDate}`; - } else { - const start = initCompDate - errorMargin; - const end = initCompDate + errorMargin; - return `${start}-${end}`; - } - } + else if (init.length === 1) { + return `${init[0]}`; } } const [compDate, setCompDate] = useState(index.current?.compDate); - const [initCompDate, setInitCompDate] = useState(getYearAsStr(compDate)); //init comp date to display + const initCompDate = getYearAsStr(index.current?.compDate); //init comp date to display + const initPubDate = getYearAsStr(index.current?.pubDate); + const toggleInProgress = function() { setSavingStatus(savingStatus => !savingStatus); @@ -1144,12 +1132,11 @@ const EditTextInfo = function({initTitle, close}) { } return true; } - const validateCompDate = (newValue) => { + const validateCompDate = (newValue, setter) => { let pattern = /(-?\d+)(-?)(-?\d*)/; let result = newValue.match(pattern); if (!result) { - setErrorMargin(0); - setCompDate(''); + setter([]); } else if (result[2] === "-") { const start = Number.parseInt(result[1]); @@ -1161,19 +1148,16 @@ const EditTextInfo = function({initTitle, close}) { alert(`Invalid date format ${start} to ${end}`); } else { - const midpoint = (start + end) / 2; - setCompDate(midpoint); - setErrorMargin(midpoint - start); + setter([start, end]); } } else { - setErrorMargin(0); const year = Number.parseInt(newValue); if (Number.isNaN(year)) { alert("Year must be an integer or range of integers."); } else { - setCompDate(year); + setter([year]); } } } @@ -1190,12 +1174,11 @@ const EditTextInfo = function({initTitle, close}) { if (enTitle !== oldTitle) { postIndex.oldTitle = oldTitle; } - if (pubDate != index.current?.pubDate) { + if (getYearAsStr(pubDate) !== initPubDate) { postIndex.pubDate = pubDate; } - if (compDate != initCompDate) { + if (getYearAsStr(compDate) !== initCompDate) { postIndex.compDate = compDate; - postIndex.errorMargin = errorMargin; } let postJSON = JSON.stringify(postIndex); let title = enTitle.replace(/ /g, "_"); @@ -1306,7 +1289,7 @@ const EditTextInfo = function({initTitle, close}) { </div> : null} <div className="section"> <div><InterfaceText>Completion Year</InterfaceText></div><label><span className="optional"><InterfaceText>Optional. Provide a range if there is an error margin or the work was completed over the course of many years such as 1797-1800 or -900--200 (to denote 900 BCE to 200 BCE).</InterfaceText></span></label> - <br/><input id="compDate" onBlur={(e) => validateCompDate(e.target.value)} defaultValue={initCompDate}/> + <br/><input id="compDate" onBlur={(e) => validateCompDate(e.target.value, setCompDate)} defaultValue={initCompDate}/> </div> <div className="section"> <div><InterfaceText>Place of Composition</InterfaceText></div> @@ -1321,7 +1304,7 @@ const EditTextInfo = function({initTitle, close}) { </div>} <div className="section"> <div><InterfaceText>Publication Year</InterfaceText></div><label><span className="optional"><InterfaceText>Optional. Provide a range if there is an error margin or the work was completed over the course of many years such as 1797-1800 or -900--200 (to denote 900 BCE to 200 BCE).</InterfaceText></span></label> - <input type='number' id="pubDate" onChange={(e) => setPubDate(e.target.value)} defaultValue={pubDate}/> + <input id="pubDate" onBlur={(e) => validateCompDate(e.target.value, setPubDate)} defaultValue={initPubDate}/> </div> <div className="section"> <div><InterfaceText>Place of Publication</InterfaceText></div><label><span className="optional"><InterfaceText>Optional</InterfaceText></span></label> From af13c63051b7f2615c9f851ffbd97a8406ed0f56 Mon Sep 17 00:00:00 2001 From: Russel Neiss <russel.neiss@gmail.com> Date: Mon, 4 Sep 2023 22:19:59 -0500 Subject: [PATCH 190/756] static: updates team page --- templates/static/en/team.html | 14 -------------- templates/static/he/team.html | 13 ------------- 2 files changed, 27 deletions(-) diff --git a/templates/static/en/team.html b/templates/static/en/team.html index 5f12348737..242c871b80 100644 --- a/templates/static/en/team.html +++ b/templates/static/en/team.html @@ -329,20 +329,6 @@ <h1> <span class="int-en">Product & Engineering Director</span> </div> </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/roy.png' %}" alt="Headshot of Roy Pereg"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Roy Pereg</span> - </div> - <div class="teamTitle"> - <span class="int-en">Contractor - </span> - <span class="int-en">Hebrew Editorial Associate</span> - </div> - </div> </div> <div class="teamMember"> <div class="teamMemberImage"> diff --git a/templates/static/he/team.html b/templates/static/he/team.html index bf3fafd791..3b25c5291e 100644 --- a/templates/static/he/team.html +++ b/templates/static/he/team.html @@ -356,19 +356,6 @@ <h1> </div> </div> </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/roy.png' %}" alt="Headshot of Roy Pereg"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-he">רועי דב פרג</span> - </div> - <div class="teamTitle"> - <span class="int-he">רכז עריכת תוכן עברי</span> - </div> - </div> - </div> <div class="teamMember"> <div class="teamMemberImage"> <img src="{% static 'img/headshots/israel.png' %}" alt="Headshot of Israel Tsadok"> From 322597a5ca493dd4fcea79d4ca1e8a0a0bfaeb06 Mon Sep 17 00:00:00 2001 From: Russel Neiss <russel.neiss@gmail.com> Date: Mon, 4 Sep 2023 22:25:14 -0500 Subject: [PATCH 191/756] static: fix Hebrew typo --- templates/static/he/team.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/static/he/team.html b/templates/static/he/team.html index 3b25c5291e..f37c97453b 100644 --- a/templates/static/he/team.html +++ b/templates/static/he/team.html @@ -313,7 +313,7 @@ <h1> <span class="int-he">רזיאל ניס</span> </div> <div class="teamTitle"> - <span class="int-he">מנהלת מוצר והנדסה</span> + <span class="int-he">מנהל מוצר והנדסה</span> </div> </div> </div> From 265b76176dd459232467c61169caaabc5fd004c1 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Tue, 5 Sep 2023 17:11:31 -0400 Subject: [PATCH 192/756] Change colors to use defined color variables --- static/css/s2.css | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index ea1e0a341e..ce090cb28a 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -1240,7 +1240,7 @@ div.interfaceLinks-row a { } #defaultModalBody { - color: #555; + color: #555555; margin-top: 0; } @@ -1249,7 +1249,7 @@ div.interfaceLinks-row a { } #defaultModal #defaultModalBody .sub { - color: #999; + color: var(--medium-grey); font-size: 12px; font-family: "Roboto", "Helvetica Neue", Helvetica, sans-serif; } @@ -1285,8 +1285,8 @@ div.interfaceLinks-row a { padding: 10px; margin-bottom: 20px; border-radius: 7px; - border: 1px solid #EEE; - color: #333; + border: 1px solid #EEEEEE; + color: var(--beit-midrash-grey); } .header .my-profile img { From 7a18e06405b4c80a142b9ca1219ff11aac3c88c9 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Wed, 6 Sep 2023 00:19:33 -0400 Subject: [PATCH 193/756] Remove previous banners and modals (if they exist) so ones during new campaigns don't accumulate indefinitely --- static/js/TopicPage.jsx | 2 +- static/js/context.js | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/static/js/TopicPage.jsx b/static/js/TopicPage.jsx index 93654ff1f5..04f8d2dbe6 100644 --- a/static/js/TopicPage.jsx +++ b/static/js/TopicPage.jsx @@ -526,7 +526,7 @@ const TopicPage = ({ timePeriod={topicData.timePeriod} properties={topicData.properties} /> - {!topicData.isLoading && <Promotions/> /* Dont allow sidebar ads to be shown until the rest of the topics page has loaded */} + {!topicData.isLoading && <Promotions/>} </> ) : null} </div> diff --git a/static/js/context.js b/static/js/context.js index 81fe5b56e0..b75c641646 100644 --- a/static/js/context.js +++ b/static/js/context.js @@ -208,6 +208,20 @@ function StrapiDataProvider({ children }) { let modals = result.data?.modals?.data; let banners = result.data?.banners?.data; + let removeKeysFromLocalStorageWithPrefix = (prefix) => { + let keysToRemove = []; + // Removing keys while iterating affects the length of localStorage + for (let i = 0; i < localStorage.length; i++) { + let key = localStorage.key(i); + if (key.startsWith(prefix)) { + keysToRemove.push(key); + } + } + keysToRemove.forEach((key) => { + localStorage.removeItem(key); + }); + }; + const currentDate = new Date(); if (modals?.length) { // Only one modal can be displayed currently. The first one that matches will be the one shown @@ -217,6 +231,9 @@ function StrapiDataProvider({ children }) { currentDate <= new Date(modal.attributes.modalEndDate) ); if (modal) { + // Remove any other previous modals since there is a new modal to replace it + removeKeysFromLocalStorageWithPrefix("modal_"); + // Check if there is a Hebrew translation for the modal if (modal.attributes.localizations?.data?.length) { let localization_attributes = @@ -259,6 +276,9 @@ function StrapiDataProvider({ children }) { ); if (banner) { + // Remove any other previous banners since there is a new banner to replace it + removeKeysFromLocalStorageWithPrefix("banner_"); + // Check if there is a Hebrew translation if (banner.attributes.localizations?.data?.length) { let localization_attributes = From 724d900cf20d7d694072884ebfd6283cb87d41a5 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Wed, 6 Sep 2023 00:35:12 -0400 Subject: [PATCH 194/756] Move functions to the module where they're used --- static/js/Misc.jsx | 19 +++++++++++++++---- static/js/context.js | 15 --------------- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index a9fd787f1f..5413064307 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -8,7 +8,7 @@ import classNames from 'classnames'; import PropTypes from 'prop-types'; import Component from 'react-class'; import { usePaginatedDisplay } from './Hooks'; -import {ContentLanguageContext, AdContext, StrapiDataContext, replaceNewLinesWithLinebreaks} from './context'; +import {ContentLanguageContext, AdContext, StrapiDataContext} from './context'; import ReactCrop from 'react-image-crop'; import 'react-image-crop/dist/ReactCrop.css'; import {ContentText} from "./ContentText"; @@ -2058,9 +2058,6 @@ SignUpModal.propTypes = { // Have a callback function run when its children (element or nested group of elements) become fully visible within the viewport function OnInView({ children, onVisible }) { - // Get a mutable reference object for the child and/or children to be rendered within this component wrapped in a div - // The reference is attached to the DOM element returned from this component - // This allows us to access properties of the DOM element directly const elementRef = useRef(); useEffect(() => { @@ -2096,6 +2093,20 @@ function OnInView({ children, onVisible }) { return <div ref={elementRef}>{children}</div>; } +const transformValues = (obj, callback) => { + const newObj = {}; + for (let key in obj) { + newObj[key] = obj[key] !== null ? callback(obj[key]) : null; + } + return newObj; +}; + +const replaceNewLinesWithLinebreaks = (content) => { + return transformValues( + content, + (s) => s.replace(/\n/gi, "  \n") + "  \n  \n" + ); +} const InterruptingMessage = ({ onClose, diff --git a/static/js/context.js b/static/js/context.js index b75c641646..1784f811b3 100644 --- a/static/js/context.js +++ b/static/js/context.js @@ -11,21 +11,6 @@ AdContext.displayName = "AdContext"; const StrapiDataContext = React.createContext({}); StrapiDataContext.displayName = "StrapiDataContext"; -const transformValues = (obj, callback) => { - const newObj = {}; - for (let key in obj) { - newObj[key] = obj[key] !== null ? callback(obj[key]) : null; - } - return newObj; -}; - -export function replaceNewLinesWithLinebreaks(content) { - return transformValues( - content, - (s) => s.replace(/\n/gi, "  \n") + "  \n  \n" - ); -} - // Gets data from a Strapi CMS instance to be used for displaying static content function StrapiDataProvider({ children }) { const [dataFromStrapiHasBeenReceived, setDataFromStrapiHasBeenReceived] = From 7602cfa4d181544167a36ea5746609127697b752 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 6 Sep 2023 12:22:09 +0300 Subject: [PATCH 195/756] feat(Topics): add sort URL param to topic pages. This will track the sort option on topic pages which can be either "Relevance" or "Chronological". --- reader/views.py | 1 + static/js/Misc.jsx | 11 ++++++----- static/js/ReaderApp.jsx | 4 ++++ static/js/ReaderPanel.jsx | 5 +++++ static/js/TopicPage.jsx | 9 +++++++-- 5 files changed, 23 insertions(+), 7 deletions(-) diff --git a/reader/views.py b/reader/views.py index 00b42b4a22..16b2800ee1 100644 --- a/reader/views.py +++ b/reader/views.py @@ -3060,6 +3060,7 @@ def topic_page(request, topic, test_version=None): "initialMenu": "topics", "initialTopic": topic, "initialTab": urllib.parse.unquote(request.GET.get('tab', 'sources')), + "initialTopicSort": urllib.parse.unquote(request.GET.get('sort', 'Relevance')), "initialTopicTitle": { "en": topic_obj.get_primary_title('en'), "he": topic_obj.get_primary_title('he') diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 5d083ad7d6..9dade35b2e 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -364,10 +364,10 @@ const FilterableList = ({ filterFunc, sortFunc, renderItem, sortOptions, getData, data, renderEmptyList, renderHeader, renderFooter, showFilterHeader, refreshData, initialFilter, scrollableElement, pageSize, onDisplayedDataChange, initialRenderSize, - bottomMargin, containerClass + bottomMargin, containerClass, onSetSort, initialSort, }) => { const [filter, setFilter] = useState(initialFilter || ''); - const [sortOption, setSortOption] = useState(sortOptions[0]); + const [sortOption, setSortOption] = useState(initialSort || sortOptions[0]); const [displaySort, setDisplaySort] = useState(false); // Apply filter and sort to the raw data @@ -420,10 +420,11 @@ const FilterableList = ({ }, [dataUpToPage]); } - const onSortChange = newSortOption => { + const setSort = newSortOption => { if (newSortOption === sortOption) { return; } setSortOption(newSortOption); setDisplaySort(false); + onSetSort?.(newSortOption); }; const oldDesign = typeof showFilterHeader == 'undefined'; @@ -453,7 +454,7 @@ const FilterableList = ({ isOpen={displaySort} options={sortOptions.map(option => ({type: option, name: option, heName: Sefaria._(option, "FilterableList")}))} currOptionSelected={sortOption} - handleClick={onSortChange} + handleClick={setSort} /> </DropdownModal> : null @@ -480,7 +481,7 @@ const FilterableList = ({ <span key={option} className={classNames({'sans-serif': 1, 'sort-option': 1, noselect: 1, active: sortOption === option})} - onClick={() => onSortChange(option)} + onClick={() => setSort(option)} > <InterfaceText context="FilterableList">{option}</InterfaceText> </span> diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index 02ec1aa695..d5a777a751 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -50,6 +50,7 @@ class ReaderApp extends Component { searchQuery: props.initialQuery, searchTab: props.initialSearchTab, tab: props.initialTab, + topicSort: props.initialTopicSort, textSearchState: new SearchState({ type: 'text', appliedFilters: props.initialTextSearchFilters, @@ -166,6 +167,7 @@ class ReaderApp extends Component { textHighlights: state.textHighlights || null, profile: state.profile || null, tab: state.tab || null, + topicSort: state.topicSort || null, webPagesFilter: state.webPagesFilter || null, sideScrollPosition: state.sideScrollPosition || null, topicTestVersion: state.topicTestVersion || null @@ -378,6 +380,7 @@ class ReaderApp extends Component { (prev.searchQuery != next.searchQuery) || (prev.searchTab != next.searchTab) || (prev.tab !== next.tab) || + (prev.topicSort !== next.topicSort) || (prev.collectionName !== next.collectionName) || (prev.collectionTag !== next.collectionTag) || (!prevTextSearchState.isEqual({ other: nextTextSearchState, fields: ["appliedFilters", "field", "sortType"]})) || @@ -471,6 +474,7 @@ class ReaderApp extends Component { case "topics": if (state.navigationTopic) { hist.url = state.topicTestVersion ? `topics/${state.topicTestVersion}/${state.navigationTopic}` : `topics/${state.navigationTopic}`; + hist.url = hist.url + (state.topicSort ? `&sort=${state.topicSort}` : ''); hist.title = `${state.topicTitle[shortLang]} | ${ Sefaria._("Texts & Source Sheets from Torah, Talmud and Sefaria's library of Jewish sources.")}`; hist.mode = "topic"; } else if (state.navigationTopicCategory) { diff --git a/static/js/ReaderPanel.jsx b/static/js/ReaderPanel.jsx index 308099388f..93140ecef1 100644 --- a/static/js/ReaderPanel.jsx +++ b/static/js/ReaderPanel.jsx @@ -527,6 +527,9 @@ class ReaderPanel extends Component { : false this.conditionalSetState({tab: tab}) } + onSetTopicSort(topicSort) { + this.conditionalSetState({topicSort}); + } currentMode() { return this.state.mode; } @@ -918,6 +921,8 @@ class ReaderPanel extends Component { <TopicPage tab={this.state.tab} setTab={this.setTab} + onSetTopicSort={this.onSetTopicSort} + topicSort={this.state.topicSort} topic={this.state.navigationTopic} topicTitle={this.state.topicTitle} interfaceLang={this.props.interfaceLang} diff --git a/static/js/TopicPage.jsx b/static/js/TopicPage.jsx index 4e4f2c3e33..8d8057288c 100644 --- a/static/js/TopicPage.jsx +++ b/static/js/TopicPage.jsx @@ -390,7 +390,8 @@ const useTabDisplayData = (translationLanguagePreference) => { const TopicPage = ({ tab, topic, topicTitle, setTopic, setNavTopic, openTopics, multiPanel, showBaseText, navHome, - toggleSignUpModal, openDisplaySettings, setTab, openSearch, translationLanguagePreference, versionPref, topicTestVersion + toggleSignUpModal, openDisplaySettings, setTab, openSearch, translationLanguagePreference, versionPref, + topicTestVersion, onSetTopicSort, topicSort }) => { const defaultTopicData = {primaryTitle: topicTitle, tabs: {}, isLoading: true}; const [topicData, setTopicData] = useState(Sefaria.getTopicFromCache(topic, {with_html: true}) || defaultTopicData); @@ -505,6 +506,8 @@ const TopicPage = ({ }} initialRenderSize={(topicData._refsDisplayedByTab && topicData._refsDisplayedByTab[key]) || 0} renderItem={renderWrapper(toggleSignUpModal, topicData, topicTestVersion)} + onSetTopicSort={onSetTopicSort} + topicSort={topicSort} /> ); }) @@ -550,7 +553,7 @@ TopicPage.propTypes = { const TopicPageTab = ({ data, renderItem, classes, sortOptions, sortFunc, filterFunc, showFilterHeader, - scrollableElement, onDisplayedDataChange, initialRenderSize + scrollableElement, onDisplayedDataChange, initialRenderSize, onSetTopicSort, topicSort }) => { return ( <div className="topicTabContents"> @@ -568,6 +571,8 @@ const TopicPageTab = ({ sortOptions={sortOptions} onDisplayedDataChange={onDisplayedDataChange} initialRenderSize={initialRenderSize} + onSetSort={onSetTopicSort} + initialSort={topicSort} data={data} /> </div> : <LoadingMessage /> From b4a564532e3b23fab56aaeb348c00b24b61332fd Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 6 Sep 2023 12:28:08 +0300 Subject: [PATCH 196/756] feat(Topics): add sort URL param to topic pages. This will track the sort option on topic pages which can be either "Relevance" or "Chronological". --- static/js/Misc.jsx | 5 +++-- static/js/TopicPage.jsx | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 9dade35b2e..09cc511ebe 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -364,11 +364,12 @@ const FilterableList = ({ filterFunc, sortFunc, renderItem, sortOptions, getData, data, renderEmptyList, renderHeader, renderFooter, showFilterHeader, refreshData, initialFilter, scrollableElement, pageSize, onDisplayedDataChange, initialRenderSize, - bottomMargin, containerClass, onSetSort, initialSort, + bottomMargin, containerClass, onSetSort, externalSortOption, }) => { const [filter, setFilter] = useState(initialFilter || ''); - const [sortOption, setSortOption] = useState(initialSort || sortOptions[0]); + const [internalSortOption, setSortOption] = useState(externalSortOption || sortOptions[0]); const [displaySort, setDisplaySort] = useState(false); + const sortOption = externalSortOption || internalSortOption; // Apply filter and sort to the raw data const processData = rawData => rawData ? rawData diff --git a/static/js/TopicPage.jsx b/static/js/TopicPage.jsx index 8d8057288c..5b9dd5a1a2 100644 --- a/static/js/TopicPage.jsx +++ b/static/js/TopicPage.jsx @@ -572,7 +572,7 @@ const TopicPageTab = ({ onDisplayedDataChange={onDisplayedDataChange} initialRenderSize={initialRenderSize} onSetSort={onSetTopicSort} - initialSort={topicSort} + externalSortOption={topicSort} data={data} /> </div> : <LoadingMessage /> From 90d53605efe26b045093255a711889766e5f2f19 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 6 Sep 2023 12:52:26 +0300 Subject: [PATCH 197/756] fix(Topics): use prop to override sort of filterable list --- static/js/Misc.jsx | 30 ++++++++++++++++++++++++++++-- static/js/TopicPage.jsx | 2 +- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 9dade35b2e..7895a2a3c9 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -360,15 +360,41 @@ ProfilePic.propTypes = { }; +/** + * Renders a list of data that can be filtered and sorted + * @param filterFunc + * @param sortFunc + * @param renderItem + * @param sortOptions + * @param getData + * @param data + * @param renderEmptyList + * @param renderHeader + * @param renderFooter + * @param showFilterHeader + * @param refreshData + * @param initialFilter + * @param scrollableElement + * @param pageSize + * @param onDisplayedDataChange + * @param initialRenderSize + * @param bottomMargin + * @param containerClass + * @param onSetSort: optional. function that is passed the current sort option when the user changes it. Use this to control sort from outside the component. See `externalSortOption`. + * @param externalSortOption: optional. string that is one of the options in `sortOptions`. Use this to control sort from outside the component. See `onSetSort`. + * @returns {JSX.Element} + * @constructor + */ const FilterableList = ({ filterFunc, sortFunc, renderItem, sortOptions, getData, data, renderEmptyList, renderHeader, renderFooter, showFilterHeader, refreshData, initialFilter, scrollableElement, pageSize, onDisplayedDataChange, initialRenderSize, - bottomMargin, containerClass, onSetSort, initialSort, + bottomMargin, containerClass, onSetSort, externalSortOption, }) => { const [filter, setFilter] = useState(initialFilter || ''); - const [sortOption, setSortOption] = useState(initialSort || sortOptions[0]); + const [internalSortOption, setSortOption] = useState(sortOptions[0]); const [displaySort, setDisplaySort] = useState(false); + const sortOption = externalSortOption || internalSortOption; // Apply filter and sort to the raw data const processData = rawData => rawData ? rawData diff --git a/static/js/TopicPage.jsx b/static/js/TopicPage.jsx index 8d8057288c..5b9dd5a1a2 100644 --- a/static/js/TopicPage.jsx +++ b/static/js/TopicPage.jsx @@ -572,7 +572,7 @@ const TopicPageTab = ({ onDisplayedDataChange={onDisplayedDataChange} initialRenderSize={initialRenderSize} onSetSort={onSetTopicSort} - initialSort={topicSort} + externalSortOption={topicSort} data={data} /> </div> : <LoadingMessage /> From 7c8ff3ad40e3bdac412b87a4cb4d0d656fcd3cac Mon Sep 17 00:00:00 2001 From: yonadavGit <yonadav.le@gmail.com> Date: Wed, 6 Sep 2023 14:02:15 +0300 Subject: [PATCH 198/756] script to count mishnayot and chapters of beeri edition --- package-lock.json | 23404 +++++++++++++------------------------------- 1 file changed, 6617 insertions(+), 16787 deletions(-) diff --git a/package-lock.json b/package-lock.json index b667c646a9..26f5d4edc1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,17 +10,11 @@ "hasInstallScript": true, "license": "GPL-3.0", "dependencies": { - "@semantic-release/commit-analyzer": "^9.0.2", - "@semantic-release/git": "^10.0.1", - "@semantic-release/github": "^8.0.7", - "@semantic-release/npm": "^9.0.2", - "@semantic-release/release-notes-generator": "^10.0.3", "@sentry/react": "^7.57.0", "body-parser": "^1.15.1", "buffer": "^6.0.3", "cheerio": "^0.22.0", "classnames": "^2.2.5", - "conventional-changelog-conventionalcommits": "^6.1.0", "cookie-parser": "^1.4.2", "core-js": "^3.15.2", "css-modules-require-hook": "^4.2.3", @@ -135,6 +129,7 @@ "version": "7.21.4", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz", "integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==", + "dev": true, "dependencies": { "@babel/highlight": "^7.18.6" }, @@ -489,6 +484,7 @@ "version": "7.19.1", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true, "engines": { "node": ">=6.9.0" } @@ -535,6 +531,7 @@ "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.18.6", "chalk": "^2.0.0", @@ -2184,9685 +2181,1360 @@ "dev": true, "optional": true }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "node_modules/@sentry-internal/tracing": { + "version": "7.57.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.57.0.tgz", + "integrity": "sha512-tpViyDd8AhQGYYhI94xi2aaDopXOPfL2Apwrtb3qirWkomIQ2K86W1mPmkce+B0cFOnW2Dxv/ZTFKz6ghjK75A==", "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "@sentry/core": "7.57.0", + "@sentry/types": "7.57.0", + "@sentry/utils": "7.57.0", + "tslib": "^2.4.1 || ^1.9.3" }, "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "engines": { - "node": ">= 8" + "node": ">=8" } }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "node_modules/@sentry/browser": { + "version": "7.57.0", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.57.0.tgz", + "integrity": "sha512-E0HaYYlaqHFiIRZXxcvOO8Odvlt+TR1vFFXzqUWXPOvDRxURglTOCQ3EN/u6bxtAGJ6y/Zc2obgihTtypuel/w==", "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "@sentry-internal/tracing": "7.57.0", + "@sentry/core": "7.57.0", + "@sentry/replay": "7.57.0", + "@sentry/types": "7.57.0", + "@sentry/utils": "7.57.0", + "tslib": "^2.4.1 || ^1.9.3" }, "engines": { - "node": ">= 8" + "node": ">=8" } }, - "node_modules/@octokit/auth-token": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.4.tgz", - "integrity": "sha512-TWFX7cZF2LXoCvdmJWY7XVPi74aSY0+FfBZNSXEXFkMpjcqsQwDSYVv5FhRFaI0V1ECnwbz4j59T/G+rXNWaIQ==", + "node_modules/@sentry/core": { + "version": "7.57.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.57.0.tgz", + "integrity": "sha512-l014NudPH0vQlzybtXajPxYFfs9w762NoarjObC3gu76D1jzBBFzhdRelkGpDbSLNTIsKhEDDRpgAjBWJ9icfw==", + "dependencies": { + "@sentry/types": "7.57.0", + "@sentry/utils": "7.57.0", + "tslib": "^2.4.1 || ^1.9.3" + }, "engines": { - "node": ">= 14" + "node": ">=8" } }, - "node_modules/@octokit/core": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.2.4.tgz", - "integrity": "sha512-rYKilwgzQ7/imScn3M9/pFfUf4I1AZEH3KhyJmtPdE2zfaXAn2mFfUy4FbKewzc2We5y/LlKLj36fWJLKC2SIQ==", + "node_modules/@sentry/react": { + "version": "7.57.0", + "resolved": "https://registry.npmjs.org/@sentry/react/-/react-7.57.0.tgz", + "integrity": "sha512-XGNTjIoCG3naSmCU8qObd+y+CqAB6NQkGWOp2yyBwp2inyKF2ehJvDh6bIQloBYq2TmOJDa4NfXdMrkilxaLFQ==", "dependencies": { - "@octokit/auth-token": "^3.0.0", - "@octokit/graphql": "^5.0.0", - "@octokit/request": "^6.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^9.0.0", - "before-after-hook": "^2.2.0", - "universal-user-agent": "^6.0.0" + "@sentry/browser": "7.57.0", + "@sentry/types": "7.57.0", + "@sentry/utils": "7.57.0", + "hoist-non-react-statics": "^3.3.2", + "tslib": "^2.4.1 || ^1.9.3" }, "engines": { - "node": ">= 14" + "node": ">=8" + }, + "peerDependencies": { + "react": "15.x || 16.x || 17.x || 18.x" } }, - "node_modules/@octokit/endpoint": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.6.tgz", - "integrity": "sha512-5L4fseVRUsDFGR00tMWD/Trdeeihn999rTMGRMC1G/Ldi1uWlWJzI98H4Iak5DB/RVvQuyMYKqSK/R6mbSOQyg==", + "node_modules/@sentry/replay": { + "version": "7.57.0", + "resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.57.0.tgz", + "integrity": "sha512-pN4ryNS3J5EYbkXvR+O/+hseAJha7XDl8mPFtK0OGTHG10JzCi4tQJazblHQdpb5QBaMMPCeZ+isyfoQLDNXnw==", "dependencies": { - "@octokit/types": "^9.0.0", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" + "@sentry/core": "7.57.0", + "@sentry/types": "7.57.0", + "@sentry/utils": "7.57.0" }, "engines": { - "node": ">= 14" + "node": ">=12" } }, - "node_modules/@octokit/endpoint/node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "node_modules/@sentry/types": { + "version": "7.57.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.57.0.tgz", + "integrity": "sha512-D7ifoUfxuVCUyktIr5Gc+jXUbtcUMmfHdTtTbf1XCZHua5mJceK9wtl3YCg3eq/HK2Ppd52BKnTzEcS5ZKQM+w==", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/@octokit/graphql": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.6.tgz", - "integrity": "sha512-Fxyxdy/JH0MnIB5h+UQ3yCoh1FG4kWXfFKkpWqjZHw/p+Kc8Y44Hu/kCgNBT6nU1shNumEchmW/sUO1JuQnPcw==", + "node_modules/@sentry/utils": { + "version": "7.57.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.57.0.tgz", + "integrity": "sha512-YXrkMCiNklqkXctn4mKYkrzNCf/dfVcRUQrkXjeBC+PHXbcpPyaJgInNvztR7Skl8lE3JPGPN4v5XhLxK1bUUg==", "dependencies": { - "@octokit/request": "^6.0.0", - "@octokit/types": "^9.0.0", - "universal-user-agent": "^6.0.0" + "@sentry/types": "7.57.0", + "tslib": "^2.4.1 || ^1.9.3" }, "engines": { - "node": ">= 14" + "node": ">=8" } }, - "node_modules/@octokit/openapi-types": { - "version": "18.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-18.0.0.tgz", - "integrity": "sha512-V8GImKs3TeQRxRtXFpG2wl19V7444NIOTDF24AWuIbmNaNYOQMWRbjcGDXV5B+0n887fgDcuMNOmlul+k+oJtw==" + "node_modules/@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" }, - "node_modules/@octokit/plugin-paginate-rest": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-6.1.2.tgz", - "integrity": "sha512-qhrmtQeHU/IivxucOV1bbI/xZyC/iOBhclokv7Sut5vnejAIAEXVcGQeRpQlU39E0WwK9lNvJHphHri/DB6lbQ==", + "node_modules/@types/babel__core": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz", + "integrity": "sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==", + "dev": true, "dependencies": { - "@octokit/tsconfig": "^1.0.2", - "@octokit/types": "^9.2.3" - }, - "engines": { - "node": ">= 14" - }, - "peerDependencies": { - "@octokit/core": ">=4" + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" } }, - "node_modules/@octokit/plugin-request-log": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", - "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", - "peerDependencies": { - "@octokit/core": ">=3" + "node_modules/@types/babel__generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" } }, - "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-7.2.3.tgz", - "integrity": "sha512-I5Gml6kTAkzVlN7KCtjOM+Ruwe/rQppp0QU372K1GP7kNOYEKe8Xn5BW4sE62JAHdwpq95OQK/qGNyKQMUzVgA==", + "node_modules/@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dev": true, "dependencies": { - "@octokit/types": "^10.0.0" - }, - "engines": { - "node": ">= 14" - }, - "peerDependencies": { - "@octokit/core": ">=3" + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" } }, - "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-10.0.0.tgz", - "integrity": "sha512-Vm8IddVmhCgU1fxC1eyinpwqzXPEYu0NrYzD3YZjlGjyftdLBTeqNblRC0jmJmgxbJIsQlyogVeGnrNaaMVzIg==", + "node_modules/@types/babel__traverse": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", + "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", + "dev": true, "dependencies": { - "@octokit/openapi-types": "^18.0.0" + "@babel/types": "^7.3.0" } }, - "node_modules/@octokit/request": { - "version": "6.2.8", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.8.tgz", - "integrity": "sha512-ow4+pkVQ+6XVVsekSYBzJC0VTVvh/FCTUUgTsboGq+DTeWdyIFV8WSCdo0RIxk6wSkBTHqIK1mYuY7nOBXOchw==", + "node_modules/@types/eslint": { + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.37.0.tgz", + "integrity": "sha512-Piet7dG2JBuDIfohBngQ3rCt7MgO9xCO4xIMKxBThCq5PNRB91IjlJ10eJVwfoNtvTErmxLzwBZ7rHZtbOMmFQ==", + "dev": true, "dependencies": { - "@octokit/endpoint": "^7.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^9.0.0", - "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.7", - "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 14" + "@types/estree": "*", + "@types/json-schema": "*" } }, - "node_modules/@octokit/request-error": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.3.tgz", - "integrity": "sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ==", + "node_modules/@types/eslint-scope": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "dev": true, "dependencies": { - "@octokit/types": "^9.0.0", - "deprecation": "^2.0.0", - "once": "^1.4.0" - }, - "engines": { - "node": ">= 14" + "@types/eslint": "*", + "@types/estree": "*" } }, - "node_modules/@octokit/request/node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "engines": { - "node": ">=0.10.0" - } + "node_modules/@types/estree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz", + "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==", + "dev": true }, - "node_modules/@octokit/rest": { - "version": "19.0.13", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.13.tgz", - "integrity": "sha512-/EzVox5V9gYGdbAI+ovYj3nXQT1TtTHRT+0eZPcuC05UFSWO3mdO9UY1C0i2eLF9Un1ONJkAk+IEtYGAC+TahA==", + "node_modules/@types/hast": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.4.tgz", + "integrity": "sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==", "dependencies": { - "@octokit/core": "^4.2.1", - "@octokit/plugin-paginate-rest": "^6.1.2", - "@octokit/plugin-request-log": "^1.0.4", - "@octokit/plugin-rest-endpoint-methods": "^7.1.2" - }, - "engines": { - "node": ">= 14" + "@types/unist": "*" } }, - "node_modules/@octokit/tsconfig": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@octokit/tsconfig/-/tsconfig-1.0.2.tgz", - "integrity": "sha512-I0vDR0rdtP8p2lGMzvsJzbhdOWy405HcGovrspJ8RRibHnyRgggUSNO5AIox5LmqiwmatHKYsvj6VGFHkqS7lA==" + "node_modules/@types/is-hotkey": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/@types/is-hotkey/-/is-hotkey-0.1.7.tgz", + "integrity": "sha512-yB5C7zcOM7idwYZZ1wKQ3pTfjA9BbvFqRWvKB46GFddxnJtHwi/b9y84ykQtxQPg5qhdpg4Q/kWU3EGoCTmLzQ==" }, - "node_modules/@octokit/types": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.3.2.tgz", - "integrity": "sha512-D4iHGTdAnEEVsB8fl95m1hiz7D5YiRdQ9b/OEb3BYRVwbLsGHcRVPz+u+BgRLNk0Q0/4iZCBqDN96j2XNxfXrA==", - "dependencies": { - "@octokit/openapi-types": "^18.0.0" - } + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true }, - "node_modules/@pnpm/config.env-replace": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", - "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==", - "engines": { - "node": ">=12.22.0" + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" } }, - "node_modules/@pnpm/network.ca-file": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz", - "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==", + "node_modules/@types/istanbul-reports": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", + "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", + "dev": true, "dependencies": { - "graceful-fs": "4.2.10" - }, - "engines": { - "node": ">=12.22.0" + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" } }, - "node_modules/@pnpm/network.ca-file/node_modules/graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" + "node_modules/@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true }, - "node_modules/@pnpm/npm-conf": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.2.2.tgz", - "integrity": "sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==", - "dependencies": { - "@pnpm/config.env-replace": "^1.1.0", - "@pnpm/network.ca-file": "^1.0.1", - "config-chain": "^1.1.11" - }, - "engines": { - "node": ">=12" - } + "node_modules/@types/lodash": { + "version": "4.14.194", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.194.tgz", + "integrity": "sha512-r22s9tAS7imvBt2lyHC9B8AGwWnXaYb1tY09oyLkXDs4vArpYJzw09nj8MLx5VfciBPGIb+ZwG0ssYnEPJxn/g==" }, - "node_modules/@semantic-release/commit-analyzer": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/@semantic-release/commit-analyzer/-/commit-analyzer-9.0.2.tgz", - "integrity": "sha512-E+dr6L+xIHZkX4zNMe6Rnwg4YQrWNXK+rNsvwOPpdFppvZO1olE2fIgWhv89TkQErygevbjsZFSIxp+u6w2e5g==", + "node_modules/@types/mdast": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.11.tgz", + "integrity": "sha512-Y/uImid8aAwrEA24/1tcRZwpxX3pIFTSilcNDKSPn+Y2iDywSEachzRuvgAYYLR3wpGXAsMbv5lvKLDZLeYPAw==", "dependencies": { - "conventional-changelog-angular": "^5.0.0", - "conventional-commits-filter": "^2.0.0", - "conventional-commits-parser": "^3.2.3", - "debug": "^4.0.0", - "import-from": "^4.0.0", - "lodash": "^4.17.4", - "micromatch": "^4.0.2" - }, - "engines": { - "node": ">=14.17" - }, - "peerDependencies": { - "semantic-release": ">=18.0.0-beta.1" + "@types/unist": "*" } }, - "node_modules/@semantic-release/commit-analyzer/node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } + "node_modules/@types/node": { + "version": "18.15.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.12.tgz", + "integrity": "sha512-Wha1UwsB3CYdqUm2PPzh/1gujGCNtWVUYF0mB00fJFoR4gTyWTDPjSm+zBF787Ahw8vSGgBja90MkgFwvB86Dg==", + "dev": true }, - "node_modules/@semantic-release/error": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz", - "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==", + "node_modules/@types/prop-types": { + "version": "15.7.5", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", + "peer": true + }, + "node_modules/@types/react": { + "version": "18.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.14.tgz", + "integrity": "sha512-A0zjq+QN/O0Kpe30hA1GidzyFjatVvrpIvWLxD+xv67Vt91TWWgco9IvrJBkeyHm1trGaFS/FSGqPlhyeZRm0g==", "peer": true, - "engines": { - "node": ">=18" + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" } }, - "node_modules/@semantic-release/git": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@semantic-release/git/-/git-10.0.1.tgz", - "integrity": "sha512-eWrx5KguUcU2wUPaO6sfvZI0wPafUKAMNC18aXY4EnNcrZL86dEmpNVnC9uMpGZkmZJ9EfCVJBQx4pV4EMGT1w==", - "dependencies": { - "@semantic-release/error": "^3.0.0", - "aggregate-error": "^3.0.0", - "debug": "^4.0.0", - "dir-glob": "^3.0.0", - "execa": "^5.0.0", - "lodash": "^4.17.4", - "micromatch": "^4.0.0", - "p-reduce": "^2.0.0" - }, - "engines": { - "node": ">=14.17" - }, - "peerDependencies": { - "semantic-release": ">=18.0.0" - } - }, - "node_modules/@semantic-release/git/node_modules/@semantic-release/error": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-3.0.0.tgz", - "integrity": "sha512-5hiM4Un+tpl4cKw3lV4UgzJj+SmfNIDCLLw0TepzQxz9ZGV5ixnqkzIVF+3tp0ZHgcMKE+VNGHJjEeyFG2dcSw==", - "engines": { - "node": ">=14.17" - } - }, - "node_modules/@semantic-release/git/node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@semantic-release/git/node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "engines": { - "node": ">=6" - } - }, - "node_modules/@semantic-release/git/node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@semantic-release/git/node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/@semantic-release/git/node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@semantic-release/git/node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/@semantic-release/git/node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@semantic-release/git/node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/@semantic-release/git/node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/@semantic-release/git/node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@semantic-release/git/node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@semantic-release/git/node_modules/p-reduce": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-2.1.0.tgz", - "integrity": "sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@semantic-release/git/node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@semantic-release/git/node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@semantic-release/git/node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@semantic-release/git/node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/@semantic-release/git/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@semantic-release/github": { - "version": "8.0.7", - "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-8.0.7.tgz", - "integrity": "sha512-VtgicRIKGvmTHwm//iqTh/5NGQwsncOMR5vQK9pMT92Aem7dv37JFKKRuulUsAnUOIlO4G8wH3gPiBAA0iW0ww==", - "dependencies": { - "@octokit/rest": "^19.0.0", - "@semantic-release/error": "^3.0.0", - "aggregate-error": "^3.0.0", - "bottleneck": "^2.18.1", - "debug": "^4.0.0", - "dir-glob": "^3.0.0", - "fs-extra": "^11.0.0", - "globby": "^11.0.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "issue-parser": "^6.0.0", - "lodash": "^4.17.4", - "mime": "^3.0.0", - "p-filter": "^2.0.0", - "p-retry": "^4.0.0", - "url-join": "^4.0.0" - }, - "engines": { - "node": ">=14.17" - }, - "peerDependencies": { - "semantic-release": ">=18.0.0-beta.1" - } - }, - "node_modules/@semantic-release/github/node_modules/@semantic-release/error": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-3.0.0.tgz", - "integrity": "sha512-5hiM4Un+tpl4cKw3lV4UgzJj+SmfNIDCLLw0TepzQxz9ZGV5ixnqkzIVF+3tp0ZHgcMKE+VNGHJjEeyFG2dcSw==", - "engines": { - "node": ">=14.17" - } - }, - "node_modules/@semantic-release/github/node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@semantic-release/github/node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "engines": { - "node": ">=6" - } - }, - "node_modules/@semantic-release/github/node_modules/fs-extra": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", - "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, - "node_modules/@semantic-release/github/node_modules/mime": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", - "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/@semantic-release/npm": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/@semantic-release/npm/-/npm-9.0.2.tgz", - "integrity": "sha512-zgsynF6McdzxPnFet+a4iO9HpAlARXOM5adz7VGVCvj0ne8wtL2ZOQoDV2wZPDmdEotDIbVeJjafhelZjs9j6g==", - "dependencies": { - "@semantic-release/error": "^3.0.0", - "aggregate-error": "^3.0.0", - "execa": "^5.0.0", - "fs-extra": "^11.0.0", - "lodash": "^4.17.15", - "nerf-dart": "^1.0.0", - "normalize-url": "^6.0.0", - "npm": "^8.3.0", - "rc": "^1.2.8", - "read-pkg": "^5.0.0", - "registry-auth-token": "^5.0.0", - "semver": "^7.1.2", - "tempy": "^1.0.0" - }, - "engines": { - "node": ">=16 || ^14.17" - }, - "peerDependencies": { - "semantic-release": ">=19.0.0" - } - }, - "node_modules/@semantic-release/npm/node_modules/@semantic-release/error": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-3.0.0.tgz", - "integrity": "sha512-5hiM4Un+tpl4cKw3lV4UgzJj+SmfNIDCLLw0TepzQxz9ZGV5ixnqkzIVF+3tp0ZHgcMKE+VNGHJjEeyFG2dcSw==", - "engines": { - "node": ">=14.17" - } - }, - "node_modules/@semantic-release/npm/node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@semantic-release/npm/node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "engines": { - "node": ">=6" - } - }, - "node_modules/@semantic-release/npm/node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@semantic-release/npm/node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/@semantic-release/npm/node_modules/fs-extra": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", - "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, - "node_modules/@semantic-release/npm/node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@semantic-release/npm/node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/@semantic-release/npm/node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@semantic-release/npm/node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" - }, - "node_modules/@semantic-release/npm/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@semantic-release/npm/node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/@semantic-release/npm/node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@semantic-release/npm/node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@semantic-release/npm/node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@semantic-release/npm/node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@semantic-release/npm/node_modules/read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@semantic-release/npm/node_modules/registry-auth-token": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.0.2.tgz", - "integrity": "sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==", - "dependencies": { - "@pnpm/npm-conf": "^2.1.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@semantic-release/npm/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@semantic-release/npm/node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@semantic-release/npm/node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@semantic-release/npm/node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/@semantic-release/npm/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@semantic-release/npm/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@semantic-release/npm/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/@semantic-release/release-notes-generator": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/@semantic-release/release-notes-generator/-/release-notes-generator-10.0.3.tgz", - "integrity": "sha512-k4x4VhIKneOWoBGHkx0qZogNjCldLPRiAjnIpMnlUh6PtaWXp/T+C9U7/TaNDDtgDa5HMbHl4WlREdxHio6/3w==", - "dependencies": { - "conventional-changelog-angular": "^5.0.0", - "conventional-changelog-writer": "^5.0.0", - "conventional-commits-filter": "^2.0.0", - "conventional-commits-parser": "^3.2.3", - "debug": "^4.0.0", - "get-stream": "^6.0.0", - "import-from": "^4.0.0", - "into-stream": "^6.0.0", - "lodash": "^4.17.4", - "read-pkg-up": "^7.0.0" - }, - "engines": { - "node": ">=14.17" - }, - "peerDependencies": { - "semantic-release": ">=18.0.0-beta.1" - } - }, - "node_modules/@semantic-release/release-notes-generator/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@semantic-release/release-notes-generator/node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@semantic-release/release-notes-generator/node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" - }, - "node_modules/@semantic-release/release-notes-generator/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@semantic-release/release-notes-generator/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@semantic-release/release-notes-generator/node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@semantic-release/release-notes-generator/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@semantic-release/release-notes-generator/node_modules/read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@semantic-release/release-notes-generator/node_modules/read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dependencies": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@semantic-release/release-notes-generator/node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@semantic-release/release-notes-generator/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@sentry-internal/tracing": { - "version": "7.57.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.57.0.tgz", - "integrity": "sha512-tpViyDd8AhQGYYhI94xi2aaDopXOPfL2Apwrtb3qirWkomIQ2K86W1mPmkce+B0cFOnW2Dxv/ZTFKz6ghjK75A==", - "dependencies": { - "@sentry/core": "7.57.0", - "@sentry/types": "7.57.0", - "@sentry/utils": "7.57.0", - "tslib": "^2.4.1 || ^1.9.3" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@sentry/browser": { - "version": "7.57.0", - "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.57.0.tgz", - "integrity": "sha512-E0HaYYlaqHFiIRZXxcvOO8Odvlt+TR1vFFXzqUWXPOvDRxURglTOCQ3EN/u6bxtAGJ6y/Zc2obgihTtypuel/w==", - "dependencies": { - "@sentry-internal/tracing": "7.57.0", - "@sentry/core": "7.57.0", - "@sentry/replay": "7.57.0", - "@sentry/types": "7.57.0", - "@sentry/utils": "7.57.0", - "tslib": "^2.4.1 || ^1.9.3" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@sentry/core": { - "version": "7.57.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.57.0.tgz", - "integrity": "sha512-l014NudPH0vQlzybtXajPxYFfs9w762NoarjObC3gu76D1jzBBFzhdRelkGpDbSLNTIsKhEDDRpgAjBWJ9icfw==", - "dependencies": { - "@sentry/types": "7.57.0", - "@sentry/utils": "7.57.0", - "tslib": "^2.4.1 || ^1.9.3" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@sentry/react": { - "version": "7.57.0", - "resolved": "https://registry.npmjs.org/@sentry/react/-/react-7.57.0.tgz", - "integrity": "sha512-XGNTjIoCG3naSmCU8qObd+y+CqAB6NQkGWOp2yyBwp2inyKF2ehJvDh6bIQloBYq2TmOJDa4NfXdMrkilxaLFQ==", - "dependencies": { - "@sentry/browser": "7.57.0", - "@sentry/types": "7.57.0", - "@sentry/utils": "7.57.0", - "hoist-non-react-statics": "^3.3.2", - "tslib": "^2.4.1 || ^1.9.3" - }, - "engines": { - "node": ">=8" - }, - "peerDependencies": { - "react": "15.x || 16.x || 17.x || 18.x" - } - }, - "node_modules/@sentry/replay": { - "version": "7.57.0", - "resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.57.0.tgz", - "integrity": "sha512-pN4ryNS3J5EYbkXvR+O/+hseAJha7XDl8mPFtK0OGTHG10JzCi4tQJazblHQdpb5QBaMMPCeZ+isyfoQLDNXnw==", - "dependencies": { - "@sentry/core": "7.57.0", - "@sentry/types": "7.57.0", - "@sentry/utils": "7.57.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@sentry/types": { - "version": "7.57.0", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.57.0.tgz", - "integrity": "sha512-D7ifoUfxuVCUyktIr5Gc+jXUbtcUMmfHdTtTbf1XCZHua5mJceK9wtl3YCg3eq/HK2Ppd52BKnTzEcS5ZKQM+w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@sentry/utils": { - "version": "7.57.0", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.57.0.tgz", - "integrity": "sha512-YXrkMCiNklqkXctn4mKYkrzNCf/dfVcRUQrkXjeBC+PHXbcpPyaJgInNvztR7Skl8lE3JPGPN4v5XhLxK1bUUg==", - "dependencies": { - "@sentry/types": "7.57.0", - "tslib": "^2.4.1 || ^1.9.3" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@socket.io/component-emitter": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", - "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" - }, - "node_modules/@tootallnate/once": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", - "engines": { - "node": ">= 10" - } - }, - "node_modules/@types/babel__core": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz", - "integrity": "sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", - "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", - "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", - "dev": true, - "dependencies": { - "@babel/types": "^7.3.0" - } - }, - "node_modules/@types/eslint": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.37.0.tgz", - "integrity": "sha512-Piet7dG2JBuDIfohBngQ3rCt7MgO9xCO4xIMKxBThCq5PNRB91IjlJ10eJVwfoNtvTErmxLzwBZ7rHZtbOMmFQ==", - "dev": true, - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.4", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", - "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", - "dev": true, - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "node_modules/@types/estree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz", - "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==", - "dev": true - }, - "node_modules/@types/hast": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.4.tgz", - "integrity": "sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/is-hotkey": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/@types/is-hotkey/-/is-hotkey-0.1.7.tgz", - "integrity": "sha512-yB5C7zcOM7idwYZZ1wKQ3pTfjA9BbvFqRWvKB46GFddxnJtHwi/b9y84ykQtxQPg5qhdpg4Q/kWU3EGoCTmLzQ==" - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", - "dev": true - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", - "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "*", - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, - "node_modules/@types/lodash": { - "version": "4.14.194", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.194.tgz", - "integrity": "sha512-r22s9tAS7imvBt2lyHC9B8AGwWnXaYb1tY09oyLkXDs4vArpYJzw09nj8MLx5VfciBPGIb+ZwG0ssYnEPJxn/g==" - }, - "node_modules/@types/mdast": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.11.tgz", - "integrity": "sha512-Y/uImid8aAwrEA24/1tcRZwpxX3pIFTSilcNDKSPn+Y2iDywSEachzRuvgAYYLR3wpGXAsMbv5lvKLDZLeYPAw==", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/minimist": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", - "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==" - }, - "node_modules/@types/node": { - "version": "18.15.12", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.12.tgz", - "integrity": "sha512-Wha1UwsB3CYdqUm2PPzh/1gujGCNtWVUYF0mB00fJFoR4gTyWTDPjSm+zBF787Ahw8vSGgBja90MkgFwvB86Dg==", - "dev": true - }, - "node_modules/@types/normalize-package-data": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", - "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==" - }, - "node_modules/@types/prop-types": { - "version": "15.7.5", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", - "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", - "peer": true - }, - "node_modules/@types/react": { - "version": "18.2.14", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.14.tgz", - "integrity": "sha512-A0zjq+QN/O0Kpe30hA1GidzyFjatVvrpIvWLxD+xv67Vt91TWWgco9IvrJBkeyHm1trGaFS/FSGqPlhyeZRm0g==", - "peer": true, - "dependencies": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" - } - }, - "node_modules/@types/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" - }, - "node_modules/@types/scheduler": { - "version": "0.16.3", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", - "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==", - "peer": true - }, - "node_modules/@types/stack-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", - "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==", - "dev": true - }, - "node_modules/@types/triple-beam": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.2.tgz", - "integrity": "sha512-txGIh+0eDFzKGC25zORnswy+br1Ha7hj5cMVwKIU7+s0U2AxxJru/jZSMU6OC9MJWP6+pc/hc6ZjyZShpsyY2g==" - }, - "node_modules/@types/unist": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", - "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==" - }, - "node_modules/@types/yargs": { - "version": "13.0.12", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.12.tgz", - "integrity": "sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", - "dev": true - }, - "node_modules/@webassemblyjs/ast": { - "version": "1.11.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.5.tgz", - "integrity": "sha512-LHY/GSAZZRpsNQH+/oHqhRQ5FT7eoULcBqgfyTB5nQHogFnK3/7QoN7dLnwSE/JkUAF0SrRuclT7ODqMFtWxxQ==", - "dev": true, - "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.5", - "@webassemblyjs/helper-wasm-bytecode": "1.11.5" - } - }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.5.tgz", - "integrity": "sha512-1j1zTIC5EZOtCplMBG/IEwLtUojtwFVwdyVMbL/hwWqbzlQoJsWCOavrdnLkemwNoC/EOwtUFch3fuo+cbcXYQ==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.5.tgz", - "integrity": "sha512-L65bDPmfpY0+yFrsgz8b6LhXmbbs38OnwDCf6NpnMUYqa+ENfE5Dq9E42ny0qz/PdR0LJyq/T5YijPnU8AXEpA==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.11.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.5.tgz", - "integrity": "sha512-fDKo1gstwFFSfacIeH5KfwzjykIE6ldh1iH9Y/8YkAZrhmu4TctqYjSh7t0K2VyDSXOZJ1MLhht/k9IvYGcIxg==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.5.tgz", - "integrity": "sha512-DhykHXM0ZABqfIGYNv93A5KKDw/+ywBFnuWybZZWcuzWHfbp21wUfRkbtz7dMGwGgT4iXjWuhRMA2Mzod6W4WA==", - "dev": true, - "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.5", - "@webassemblyjs/helper-api-error": "1.11.5", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.5.tgz", - "integrity": "sha512-oC4Qa0bNcqnjAowFn7MPCETQgDYytpsfvz4ujZz63Zu/a/v71HeCAAmZsgZ3YVKec3zSPYytG3/PrRCqbtcAvA==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.11.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.5.tgz", - "integrity": "sha512-uEoThA1LN2NA+K3B9wDo3yKlBfVtC6rh0i4/6hvbz071E8gTNZD/pT0MsBf7MeD6KbApMSkaAK0XeKyOZC7CIA==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.5", - "@webassemblyjs/helper-buffer": "1.11.5", - "@webassemblyjs/helper-wasm-bytecode": "1.11.5", - "@webassemblyjs/wasm-gen": "1.11.5" - } - }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.5.tgz", - "integrity": "sha512-37aGq6qVL8A8oPbPrSGMBcp38YZFXcHfiROflJn9jxSdSMMM5dS5P/9e2/TpaJuhE+wFrbukN2WI6Hw9MH5acg==", - "dev": true, - "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.11.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.5.tgz", - "integrity": "sha512-ajqrRSXaTJoPW+xmkfYN6l8VIeNnR4vBOTQO9HzR7IygoCcKWkICbKFbVTNMjMgMREqXEr0+2M6zukzM47ZUfQ==", - "dev": true, - "dependencies": { - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.11.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.5.tgz", - "integrity": "sha512-WiOhulHKTZU5UPlRl53gHR8OxdGsSOxqfpqWeA2FmcwBMaoEdz6b2x2si3IwC9/fSPLfe8pBMRTHVMk5nlwnFQ==", - "dev": true - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.11.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.5.tgz", - "integrity": "sha512-C0p9D2fAu3Twwqvygvf42iGCQ4av8MFBLiTb+08SZ4cEdwzWx9QeAHDo1E2k+9s/0w1DM40oflJOpkZ8jW4HCQ==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.5", - "@webassemblyjs/helper-buffer": "1.11.5", - "@webassemblyjs/helper-wasm-bytecode": "1.11.5", - "@webassemblyjs/helper-wasm-section": "1.11.5", - "@webassemblyjs/wasm-gen": "1.11.5", - "@webassemblyjs/wasm-opt": "1.11.5", - "@webassemblyjs/wasm-parser": "1.11.5", - "@webassemblyjs/wast-printer": "1.11.5" - } - }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.11.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.5.tgz", - "integrity": "sha512-14vteRlRjxLK9eSyYFvw1K8Vv+iPdZU0Aebk3j6oB8TQiQYuO6hj9s4d7qf6f2HJr2khzvNldAFG13CgdkAIfA==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.5", - "@webassemblyjs/helper-wasm-bytecode": "1.11.5", - "@webassemblyjs/ieee754": "1.11.5", - "@webassemblyjs/leb128": "1.11.5", - "@webassemblyjs/utf8": "1.11.5" - } - }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.11.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.5.tgz", - "integrity": "sha512-tcKwlIXstBQgbKy1MlbDMlXaxpucn42eb17H29rawYLxm5+MsEmgPzeCP8B1Cl69hCice8LeKgZpRUAPtqYPgw==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.5", - "@webassemblyjs/helper-buffer": "1.11.5", - "@webassemblyjs/wasm-gen": "1.11.5", - "@webassemblyjs/wasm-parser": "1.11.5" - } - }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.11.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.5.tgz", - "integrity": "sha512-SVXUIwsLQlc8srSD7jejsfTU83g7pIGr2YYNb9oHdtldSxaOhvA5xwvIiWIfcX8PlSakgqMXsLpLfbbJ4cBYew==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.5", - "@webassemblyjs/helper-api-error": "1.11.5", - "@webassemblyjs/helper-wasm-bytecode": "1.11.5", - "@webassemblyjs/ieee754": "1.11.5", - "@webassemblyjs/leb128": "1.11.5", - "@webassemblyjs/utf8": "1.11.5" - } - }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.11.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.5.tgz", - "integrity": "sha512-f7Pq3wvg3GSPUPzR0F6bmI89Hdb+u9WXrSKc4v+N0aV0q6r42WoF92Jp2jEorBEBRoRNXgjp53nBniDXcqZYPA==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.5", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webpack-cli/configtest": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz", - "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==", - "dev": true, - "peerDependencies": { - "webpack": "4.x.x || 5.x.x", - "webpack-cli": "4.x.x" - } - }, - "node_modules/@webpack-cli/info": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz", - "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==", - "dev": true, - "dependencies": { - "envinfo": "^7.7.3" - }, - "peerDependencies": { - "webpack-cli": "4.x.x" - } - }, - "node_modules/@webpack-cli/serve": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz", - "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==", - "dev": true, - "peerDependencies": { - "webpack-cli": "4.x.x" - }, - "peerDependenciesMeta": { - "webpack-dev-server": { - "optional": true - } - } - }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true - }, - "node_modules/@yarnpkg/lockfile": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", - "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==" - }, - "node_modules/abab": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", - "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", - "dev": true - }, - "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "5.7.4", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", - "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-globals": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.4.tgz", - "integrity": "sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==", - "dev": true, - "dependencies": { - "acorn": "^6.0.1", - "acorn-walk": "^6.0.1" - } - }, - "node_modules/acorn-globals/node_modules/acorn": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", - "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-walk": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz", - "integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/aggregate-error": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-4.0.1.tgz", - "integrity": "sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w==", - "peer": true, - "dependencies": { - "clean-stack": "^4.0.0", - "indent-string": "^5.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/aggregate-error/node_modules/indent-string": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", - "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/ansi-align": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", - "integrity": "sha512-TdlOggdA/zURfMYa7ABC66j+oqfMew58KpJMbUlH3bcZP1b+cBHIHDDn5uH9INsxrHBPjsqM0tDB4jPTF/vgJA==", - "dev": true, - "dependencies": { - "string-width": "^2.0.0" - } - }, - "node_modules/ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/ansicolors": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz", - "integrity": "sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==", - "peer": true - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "optional": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "peer": true - }, - "node_modules/argv-formatter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/argv-formatter/-/argv-formatter-1.0.0.tgz", - "integrity": "sha512-F2+Hkm9xFaRg+GkaNnbwXNDV5O6pnCFEmqyhvfC/Ic5LbgOWjJh3L+mN/s91rxVL3znE7DYVpW0GJFT+4YBgWw==", - "peer": true - }, - "node_modules/arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", - "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "is-array-buffer": "^3.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", - "integrity": "sha512-H3LU5RLiSsGXPhN+Nipar0iR0IofH+8r89G2y1tBKxQ/agagKyAjhkAFDRBfodP2caPrNKHpAWNIM/c9yeL7uA==", - "dev": true - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" - }, - "node_modules/array-ify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", - "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==" - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/array.prototype.reduce": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.5.tgz", - "integrity": "sha512-kDdugMl7id9COE8R7MHF5jWk7Dqt/fs4Pv+JXoICnYwqpjjjbUurz6w5fT5IG6brLdJhv6/VoHB0H7oyIBXd+Q==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-array-method-boxes-properly": "^1.0.0", - "is-string": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/async": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" - }, - "node_modules/async-each": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.6.tgz", - "integrity": "sha512-c646jH1avxr+aVpndVMeAfYw7wAa6idufrlN3LPA4PmKS0QEGp6PIC9nwz0WQkkvBGAMEki3pFdtxaF39J9vvg==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ] - }, - "node_modules/async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", - "dev": true - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "node_modules/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true, - "bin": { - "atob": "bin/atob.js" - }, - "engines": { - "node": ">= 4.5.0" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", - "engines": { - "node": "*" - } - }, - "node_modules/aws4": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", - "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==" - }, - "node_modules/babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha512-XqYMR2dfdGMW+hd0IUZ2PwK+fGeFkOxZJ0wY+JaQAHzt1Zx8LcvpiZD2NiGkEG8qx0CfkAOr5xt76d1e8vG90g==", - "dev": true, - "peer": true, - "dependencies": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" - } - }, - "node_modules/babel-code-frame/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-code-frame/node_modules/ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-code-frame/node_modules/chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", - "dev": true, - "peer": true, - "dependencies": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-code-frame/node_modules/js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==", - "dev": true, - "peer": true - }, - "node_modules/babel-code-frame/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", - "dev": true, - "peer": true, - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-code-frame/node_modules/supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/babel-core": { - "version": "6.26.3", - "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", - "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", - "dev": true, - "peer": true, - "dependencies": { - "babel-code-frame": "^6.26.0", - "babel-generator": "^6.26.0", - "babel-helpers": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-register": "^6.26.0", - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "convert-source-map": "^1.5.1", - "debug": "^2.6.9", - "json5": "^0.5.1", - "lodash": "^4.17.4", - "minimatch": "^3.0.4", - "path-is-absolute": "^1.0.1", - "private": "^0.1.8", - "slash": "^1.0.0", - "source-map": "^0.5.7" - } - }, - "node_modules/babel-core/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "peer": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/babel-core/node_modules/json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha512-4xrs1aW+6N5DalkqSVA8fxh458CXvR99WU8WLKmq4v8eWAL86Xo3BVqyd3SkA9wEVjCMqyvvRRkshAdOnBp5rw==", - "dev": true, - "peer": true, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/babel-core/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true, - "peer": true - }, - "node_modules/babel-core/node_modules/slash": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "integrity": "sha512-3TYDR7xWt4dIqV2JauJr+EJeW356RXijHeUlO+8djJ+uBXPn8/2dpzBc8yQhh583sVvc9CvFAeQVgijsH+PNNg==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-generator": { - "version": "6.26.1", - "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", - "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", - "dev": true, - "peer": true, - "dependencies": { - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "detect-indent": "^4.0.0", - "jsesc": "^1.3.0", - "lodash": "^4.17.4", - "source-map": "^0.5.7", - "trim-right": "^1.0.1" - } - }, - "node_modules/babel-generator/node_modules/jsesc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", - "integrity": "sha512-Mke0DA0QjUWuJlhsE0ZPPhYiJkRap642SmI/4ztCFaUs6V2AiH1sfecc+57NgaryfAA2VR3v6O+CSjC1jZJKOA==", - "dev": true, - "peer": true, - "bin": { - "jsesc": "bin/jsesc" - } - }, - "node_modules/babel-helpers": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", - "integrity": "sha512-n7pFrqQm44TCYvrCDb0MqabAF+JUBq+ijBvNMUxpkLjJaAu32faIexewMumrH5KLLJ1HDyT0PTEqRyAe/GwwuQ==", - "dev": true, - "peer": true, - "dependencies": { - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" - } - }, - "node_modules/babel-jest": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.9.0.tgz", - "integrity": "sha512-ntuddfyiN+EhMw58PTNL1ph4C9rECiQXjI4nMMBKBaNjXvqLdkXpPRcMSr4iyBrJg/+wz9brFUD6RhOAT6r4Iw==", - "dev": true, - "dependencies": { - "@jest/transform": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/babel__core": "^7.1.0", - "babel-plugin-istanbul": "^5.1.0", - "babel-preset-jest": "^24.9.0", - "chalk": "^2.4.2", - "slash": "^2.0.0" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-loader": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.3.0.tgz", - "integrity": "sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q==", - "dev": true, - "dependencies": { - "find-cache-dir": "^3.3.1", - "loader-utils": "^2.0.0", - "make-dir": "^3.1.0", - "schema-utils": "^2.6.5" - }, - "engines": { - "node": ">= 8.9" - }, - "peerDependencies": { - "@babel/core": "^7.0.0", - "webpack": ">=2" - } - }, - "node_modules/babel-loader/node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/babel-messages": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", - "integrity": "sha512-Bl3ZiA+LjqaMtNYopA9TYE9HP1tQ+E5dLxE0XrAzcIJeK2UqF0/EaqXwBn9esd4UmTfEab+P+UYQ1GnioFIb/w==", - "dev": true, - "peer": true, - "dependencies": { - "babel-runtime": "^6.22.0" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz", - "integrity": "sha512-5LphC0USA8t4i1zCtjbbNb6jJj/9+X6P37Qfirc/70EQ34xKlMW+a1RHGwxGI+SwWpNwZ27HqvzAobeqaXwiZw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "find-up": "^3.0.0", - "istanbul-lib-instrument": "^3.3.0", - "test-exclude": "^5.2.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.9.0.tgz", - "integrity": "sha512-2EMA2P8Vp7lG0RAzr4HXqtYwacfMErOuv1U3wrvxHX6rD1sV6xS3WXG3r8TRQ2r6w8OhvSdWt+z41hQNwNm3Xw==", - "dev": true, - "dependencies": { - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", - "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.17.7", - "@babel/helper-define-polyfill-provider": "^0.3.3", - "semver": "^6.1.1" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz", - "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==", - "dev": true, - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.3", - "core-js-compat": "^3.25.1" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", - "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", - "dev": true, - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/babel-plugin-transform-remove-console": { - "version": "6.9.4", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-remove-console/-/babel-plugin-transform-remove-console-6.9.4.tgz", - "integrity": "sha512-88blrUrMX3SPiGkT1GnvVY8E/7A+k6oj3MNvUtTIxJflFzXTw1bHkuJ/y039ouhFMp2prRn5cQGzokViYi1dsg==", - "dev": true - }, - "node_modules/babel-preset-jest": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-24.9.0.tgz", - "integrity": "sha512-izTUuhE4TMfTRPF92fFwD2QfdXaZW08qvWTFCI51V8rW5x00UuPgc3ajRoWofXOuxjfcOM5zzSYsQS3H8KGCAg==", - "dev": true, - "dependencies": { - "@babel/plugin-syntax-object-rest-spread": "^7.0.0", - "babel-plugin-jest-hoist": "^24.9.0" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-register": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", - "integrity": "sha512-veliHlHX06wjaeY8xNITbveXSiI+ASFnOqvne/LaIJIqOWi2Ogmj91KOugEz/hoh/fwMhXNBJPCv8Xaz5CyM4A==", - "dev": true, - "peer": true, - "dependencies": { - "babel-core": "^6.26.0", - "babel-runtime": "^6.26.0", - "core-js": "^2.5.0", - "home-or-tmp": "^2.0.0", - "lodash": "^4.17.4", - "mkdirp": "^0.5.1", - "source-map-support": "^0.4.15" - } - }, - "node_modules/babel-register/node_modules/core-js": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", - "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", - "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", - "dev": true, - "hasInstallScript": true, - "peer": true - }, - "node_modules/babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==", - "dev": true, - "peer": true, - "dependencies": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" - } - }, - "node_modules/babel-runtime/node_modules/core-js": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", - "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", - "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", - "dev": true, - "hasInstallScript": true, - "peer": true - }, - "node_modules/babel-runtime/node_modules/regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", - "dev": true, - "peer": true - }, - "node_modules/babel-template": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", - "integrity": "sha512-PCOcLFW7/eazGUKIoqH97sO9A2UYMahsn/yRQ7uOk37iutwjq7ODtcTNF+iFDSHNfkctqsLRjLP7URnOx0T1fg==", - "dev": true, - "peer": true, - "dependencies": { - "babel-runtime": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "lodash": "^4.17.4" - } - }, - "node_modules/babel-traverse": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", - "integrity": "sha512-iSxeXx7apsjCHe9c7n8VtRXGzI2Bk1rBSOJgCCjfyXb6v1aCqE1KSEpq/8SXuVN8Ka/Rh1WDTF0MDzkvTA4MIA==", - "dev": true, - "peer": true, - "dependencies": { - "babel-code-frame": "^6.26.0", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "debug": "^2.6.8", - "globals": "^9.18.0", - "invariant": "^2.2.2", - "lodash": "^4.17.4" - } - }, - "node_modules/babel-traverse/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "peer": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/babel-traverse/node_modules/globals": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-traverse/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true, - "peer": true - }, - "node_modules/babel-types": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", - "integrity": "sha512-zhe3V/26rCWsEZK8kZN+HaQj5yQ1CilTObixFzKW1UWjqG7618Twz6YEsCnjfg5gBcJh02DrpCkS9h98ZqDY+g==", - "dev": true, - "peer": true, - "dependencies": { - "babel-runtime": "^6.26.0", - "esutils": "^2.0.2", - "lodash": "^4.17.4", - "to-fast-properties": "^1.0.3" - } - }, - "node_modules/babel-types/node_modules/to-fast-properties": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", - "integrity": "sha512-lxrWP8ejsq+7E3nNjwYmUBMAgjMTZoTI+sdBOpvNyijeDLa29LUn9QaoXAHv4+Z578hbmHHJKZknzxVtvo77og==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-watch": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/babel-watch/-/babel-watch-2.0.8.tgz", - "integrity": "sha512-dmuUumB5Mv0SOfDuQvJ2Dmx/4EYi76spzf/8wpH7D1DnL+3GEm4uIqY+/N+4CetAh0HLSZVl894bDD9pDdfzKw==", - "dev": true, - "dependencies": { - "chokidar": "^1.4.3", - "commander": "^2.9.0", - "lodash.debounce": "^4.0.8", - "source-map-support": "^0.4.0" - }, - "bin": { - "babel-watch": "babel-watch.js" - }, - "peerDependencies": { - "babel-core": "6.5.1 - 6.6.0 || ^6.6.x" - } - }, - "node_modules/babel-watch/node_modules/anymatch": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", - "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", - "dev": true, - "dependencies": { - "micromatch": "^2.1.5", - "normalize-path": "^2.0.0" - } - }, - "node_modules/babel-watch/node_modules/anymatch/node_modules/micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha512-LnU2XFEk9xxSJ6rfgAry/ty5qwUTyHYOBU0g4R6tIw5ljwgGIBmiKhRWLw5NpMOnrgUNcDJ4WMp8rl3sYVHLNA==", - "dev": true, - "dependencies": { - "arr-diff": "^2.0.0", - "array-unique": "^0.2.1", - "braces": "^1.8.2", - "expand-brackets": "^0.1.4", - "extglob": "^0.3.1", - "filename-regex": "^2.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.1", - "kind-of": "^3.0.2", - "normalize-path": "^2.0.1", - "object.omit": "^2.0.0", - "parse-glob": "^3.0.4", - "regex-cache": "^0.4.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-watch/node_modules/arr-diff": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "integrity": "sha512-dtXTVMkh6VkEEA7OhXnN1Ecb8aAGFdZ1LFxtOCoqj4qkyOJMt7+qs6Ahdy6p/NQCPYsRSXXivhSB/J5E9jmYKA==", - "dev": true, - "dependencies": { - "arr-flatten": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-watch/node_modules/array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha512-G2n5bG5fSUCpnsXz4+8FUkYsGPkNfLn9YvS66U5qbTIXI2Ynnlo4Bi42bWv+omKUCqz+ejzfClwne0alJWJPhg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-watch/node_modules/binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-watch/node_modules/braces": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "integrity": "sha512-xU7bpz2ytJl1bH9cgIurjpg/n8Gohy9GTw81heDYLJQ4RU60dlyJsa+atVF2pI0yMMvKxI9HkKwjePCj5XI1hw==", - "dev": true, - "dependencies": { - "expand-range": "^1.8.1", - "preserve": "^0.2.0", - "repeat-element": "^1.1.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-watch/node_modules/chokidar": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", - "integrity": "sha512-mk8fAWcRUOxY7btlLtitj3A45jOwSAxH4tOFOoEGbVsl6cL6pPMWUy7dwZ/canfj3QEdP6FHSnf/l1c6/WkzVg==", - "deprecated": "Chokidar 2 will break on node v14+. Upgrade to chokidar 3 with 15x less dependencies.", - "dev": true, - "dependencies": { - "anymatch": "^1.3.0", - "async-each": "^1.0.0", - "glob-parent": "^2.0.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^2.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0" - }, - "optionalDependencies": { - "fsevents": "^1.0.0" - } - }, - "node_modules/babel-watch/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "node_modules/babel-watch/node_modules/expand-brackets": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "integrity": "sha512-hxx03P2dJxss6ceIeri9cmYOT4SRs3Zk3afZwWpOsRqLqprhTR8u++SlC+sFGsQr7WGFPdMF7Gjc1njDLDK6UA==", - "dev": true, - "dependencies": { - "is-posix-bracket": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-watch/node_modules/extglob": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "integrity": "sha512-1FOj1LOwn42TMrruOHGt18HemVnbwAmAak7krWk+wa93KXxGbK+2jpezm+ytJYDaBX0/SPLZFHKM7m+tKobWGg==", - "dev": true, - "dependencies": { - "is-extglob": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-watch/node_modules/fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "deprecated": "fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "dependencies": { - "bindings": "^1.5.0", - "nan": "^2.12.1" - }, - "engines": { - "node": ">= 4.0" - } - }, - "node_modules/babel-watch/node_modules/glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha512-JDYOvfxio/t42HKdxkAYaCiBN7oYiuxykOxKxdaUW5Qn0zaYN3gRQWolrwdnf0shM9/EP0ebuuTmyoXNr1cC5w==", - "dev": true, - "dependencies": { - "is-glob": "^2.0.0" - } - }, - "node_modules/babel-watch/node_modules/is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==", - "dev": true, - "dependencies": { - "binary-extensions": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-watch/node_modules/is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-watch/node_modules/is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg==", - "dev": true, - "dependencies": { - "is-extglob": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-watch/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-watch/node_modules/normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", - "dev": true, - "dependencies": { - "remove-trailing-separator": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-watch/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/babel-watch/node_modules/readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/babel-watch/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/babel-watch/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/babylon": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", - "dev": true, - "peer": true, - "bin": { - "babylon": "bin/babylon.js" - } - }, - "node_modules/bail": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz", - "integrity": "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "node_modules/base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, - "dependencies": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/base/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", - "dev": true, - "dependencies": { - "is-descriptor": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "dependencies": { - "tweetnacl": "^0.14.3" - } - }, - "node_modules/before-after-hook": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", - "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==" - }, - "node_modules/big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, - "optional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "dev": true, - "optional": true, - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, - "node_modules/body-parser": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", - "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" - }, - "node_modules/bottleneck": { - "version": "2.19.5", - "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", - "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==" - }, - "node_modules/boxen": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", - "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", - "dev": true, - "dependencies": { - "ansi-align": "^2.0.0", - "camelcase": "^4.0.0", - "chalk": "^2.0.1", - "cli-boxes": "^1.0.0", - "string-width": "^2.0.0", - "term-size": "^1.2.0", - "widest-line": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/boxen/node_modules/camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browser-process-hrtime": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", - "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", - "dev": true - }, - "node_modules/browser-resolve": { - "version": "1.11.3", - "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", - "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", - "dev": true, - "dependencies": { - "resolve": "1.1.7" - } - }, - "node_modules/browser-resolve/node_modules/resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg==", - "dev": true - }, - "node_modules/browserslist": { - "version": "4.21.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", - "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001449", - "electron-to-chromium": "^1.4.284", - "node-releases": "^2.0.8", - "update-browserslist-db": "^1.0.10" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dev": true, - "dependencies": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase-keys": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", - "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", - "dependencies": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001480", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001480.tgz", - "integrity": "sha512-q7cpoPPvZYgtyC4VaBSN0Bt+PJ4c4EYRf0DrduInOz2SkFpHD5p3LnvEpqBp7UnJn+8x1Ogl1s38saUxe+ihQQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ] - }, - "node_modules/capture-exit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", - "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", - "dev": true, - "dependencies": { - "rsvp": "^4.8.4" - }, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/capture-stack-trace": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.2.tgz", - "integrity": "sha512-X/WM2UQs6VMHUtjUDnZTRI+i1crWteJySFzr9UpGoQa4WQffXVTTXuekjl7TjZRlcF2XfjgITT0HxZ9RnxeT0w==", - "dev": true, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cardinal": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-2.1.1.tgz", - "integrity": "sha512-JSr5eOgoEymtYHBjNWyjrMqet9Am2miJhlfKNdqLp6zoeAh0KN5dRAcxlecj5mAJrmQomgiOBj35xHLrFjqBpw==", - "peer": true, - "dependencies": { - "ansicolors": "~0.3.2", - "redeyed": "~2.1.0" - }, - "bin": { - "cdl": "bin/cdl.js" - } - }, - "node_modules/caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" - }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/character-entities": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", - "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-legacy": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", - "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-reference-invalid": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", - "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/cheerio": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz", - "integrity": "sha512-8/MzidM6G/TgRelkzDG13y3Y9LxBjCb+8yOEZ9+wwq5gVF2w2pV0wmHvjfT0RvuxGyR7UEuK36r+yYMbT4uKgA==", - "dependencies": { - "css-select": "~1.2.0", - "dom-serializer": "~0.1.0", - "entities": "~1.1.1", - "htmlparser2": "^3.9.1", - "lodash.assignin": "^4.0.9", - "lodash.bind": "^4.1.4", - "lodash.defaults": "^4.0.1", - "lodash.filter": "^4.4.0", - "lodash.flatten": "^4.2.0", - "lodash.foreach": "^4.3.0", - "lodash.map": "^4.4.0", - "lodash.merge": "^4.4.0", - "lodash.pick": "^4.2.1", - "lodash.reduce": "^4.4.0", - "lodash.reject": "^4.4.0", - "lodash.some": "^4.4.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "optional": true, - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", - "dev": true, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" - }, - "node_modules/class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dev": true, - "dependencies": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", - "dev": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/classnames": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", - "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" - }, - "node_modules/clean-stack": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-4.2.0.tgz", - "integrity": "sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg==", - "peer": true, - "dependencies": { - "escape-string-regexp": "5.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/clean-stack/node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-boxes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", - "integrity": "sha512-3Fo5wu8Ytle8q9iCzS4D2MWVL2X7JVWRiS1BnXbTFDhS9c/REkM9vd1AmabsoZoY5/dGi5TT9iKL8Kb6DeBRQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cli-table3": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", - "integrity": "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==", - "dependencies": { - "string-width": "^4.2.0" - }, - "engines": { - "node": "10.* || >= 12.*" - }, - "optionalDependencies": { - "@colors/colors": "1.5.0" - } - }, - "node_modules/cli-table3/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-table3/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "node_modules/cli-table3/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-table3/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-table3/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "dependencies": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, - "node_modules/cliui/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "dev": true, - "dependencies": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/clsx": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", - "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==", - "dev": true, - "dependencies": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/color": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", - "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", - "dependencies": { - "color-convert": "^1.9.3", - "color-string": "^1.6.0" - } - }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true - }, - "node_modules/colorspace": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", - "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", - "dependencies": { - "color": "^3.1.3", - "text-hex": "1.0.x" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/comma-separated-tokens": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", - "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", - "dev": true - }, - "node_modules/compare-func": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", - "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", - "dependencies": { - "array-ify": "^1.0.0", - "dot-prop": "^5.1.0" - } - }, - "node_modules/compare-func/node_modules/dot-prop": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", - "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", - "dependencies": { - "is-obj": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/compare-func/node_modules/is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true - }, - "node_modules/compute-scroll-into-view": { - "version": "1.0.20", - "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.20.tgz", - "integrity": "sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "node_modules/config-chain": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", - "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", - "dependencies": { - "ini": "^1.3.4", - "proto-list": "~1.2.1" - } - }, - "node_modules/configstore": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.5.tgz", - "integrity": "sha512-nlOhI4+fdzoK5xmJ+NY+1gZK56bwEaWZr8fYuXohZ9Vkc1o3a4T/R3M+yE/w7x/ZVJ1zF8c+oaOvF0dztdUgmA==", - "dev": true, - "dependencies": { - "dot-prop": "^4.2.1", - "graceful-fs": "^4.1.2", - "make-dir": "^1.0.0", - "unique-string": "^1.0.0", - "write-file-atomic": "^2.0.0", - "xdg-basedir": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/configstore/node_modules/make-dir": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", - "dev": true, - "dependencies": { - "pify": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/configstore/node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/conventional-changelog-angular": { - "version": "5.0.13", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.13.tgz", - "integrity": "sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA==", - "dependencies": { - "compare-func": "^2.0.0", - "q": "^1.5.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-changelog-conventionalcommits": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-6.1.0.tgz", - "integrity": "sha512-3cS3GEtR78zTfMzk0AizXKKIdN4OvSh7ibNz6/DPbhWWQu7LqE/8+/GqSodV+sywUR2gpJAdP/1JFf4XtN7Zpw==", - "dependencies": { - "compare-func": "^2.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/conventional-changelog-writer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-5.0.1.tgz", - "integrity": "sha512-5WsuKUfxW7suLblAbFnxAcrvf6r+0b7GvNaWUwUIk0bXMnENP/PEieGKVUQrjPqwPT4o3EPAASBXiY6iHooLOQ==", - "dependencies": { - "conventional-commits-filter": "^2.0.7", - "dateformat": "^3.0.0", - "handlebars": "^4.7.7", - "json-stringify-safe": "^5.0.1", - "lodash": "^4.17.15", - "meow": "^8.0.0", - "semver": "^6.0.0", - "split": "^1.0.0", - "through2": "^4.0.0" - }, - "bin": { - "conventional-changelog-writer": "cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-commits-filter": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.7.tgz", - "integrity": "sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA==", - "dependencies": { - "lodash.ismatch": "^4.4.0", - "modify-values": "^1.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-commits-parser": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.4.tgz", - "integrity": "sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q==", - "dependencies": { - "is-text-path": "^1.0.1", - "JSONStream": "^1.0.4", - "lodash": "^4.17.15", - "meow": "^8.0.0", - "split2": "^3.0.0", - "through2": "^4.0.0" - }, - "bin": { - "conventional-commits-parser": "cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, - "node_modules/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-parser": { - "version": "1.4.6", - "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz", - "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==", - "dependencies": { - "cookie": "0.4.1", - "cookie-signature": "1.0.6" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" - }, - "node_modules/copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/core-js": { - "version": "3.30.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.30.1.tgz", - "integrity": "sha512-ZNS5nbiSwDTq4hFosEDqm65izl2CWmLz0hARJMyNQBgkUZMIF51cQiMvIQKA6hvuaeWxQDP3hEedM1JZIgTldQ==", - "hasInstallScript": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/core-js-compat": { - "version": "3.30.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.30.1.tgz", - "integrity": "sha512-d690npR7MC6P0gq4npTl5n2VQeNAmUrJ90n+MHiKS7W2+xno4o3F5GDEuylSdi6EJ3VssibSGXOa1r3YXD3Mhw==", - "dev": true, - "dependencies": { - "browserslist": "^4.21.5" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" - }, - "node_modules/cosmiconfig": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.2.0.tgz", - "integrity": "sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==", - "peer": true, - "dependencies": { - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - } - }, - "node_modules/cosmiconfig/node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "peer": true - }, - "node_modules/cosmiconfig/node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "peer": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cosmiconfig/node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/create-error-class": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", - "integrity": "sha512-gYTKKexFO3kh200H1Nit76sRwRtOY32vQd3jpAQKpLtZqyNsSQNfI4N7o3eP2wUjV35pTWKRYqFUDBvUha/Pkw==", - "dev": true, - "dependencies": { - "capture-stack-trace": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "engines": { - "node": ">=4.8" - } - }, - "node_modules/cross-spawn/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/crypto-random-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", - "integrity": "sha512-GsVpkFPlycH7/fRR7Dhcmnoii54gV1nz7y4CWyeFS14N+JVBBhY+r8amRHE4BwSYal7BPTDp8isvAlCxyFt3Hg==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/css-loader": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.6.0.tgz", - "integrity": "sha512-M5lSukoWi1If8dhQAUCvj4H8vUt3vOnwbQBH9DdTm/s4Ym2B/3dPMtYZeJmq7Q3S3Pa+I94DcZ7pc9bP14cWIQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.3.1", - "cssesc": "^3.0.0", - "icss-utils": "^4.1.1", - "loader-utils": "^1.2.3", - "normalize-path": "^3.0.0", - "postcss": "^7.0.32", - "postcss-modules-extract-imports": "^2.0.0", - "postcss-modules-local-by-default": "^3.0.2", - "postcss-modules-scope": "^2.2.0", - "postcss-modules-values": "^3.0.0", - "postcss-value-parser": "^4.1.0", - "schema-utils": "^2.7.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">= 8.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" - } - }, - "node_modules/css-loader/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/css-loader/node_modules/loader-utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", - "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", - "dev": true, - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/css-modules-require-hook": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/css-modules-require-hook/-/css-modules-require-hook-4.2.3.tgz", - "integrity": "sha512-y3eGHAqmDl8JQdr1LYTwBkMxy7CSHCMy9YwpSjDqtGqCCEy9lKq/x/nmHVs+G7C1+xXmyIXjRu0q/MMn8w01mg==", - "dependencies": { - "debug": "^2.2.0", - "generic-names": "^1.0.1", - "glob-to-regexp": "^0.3.0", - "icss-replace-symbols": "^1.0.2", - "lodash": "^4.3.0", - "postcss": "^6.0.1", - "postcss-modules-extract-imports": "^1.0.0", - "postcss-modules-local-by-default": "^1.0.1", - "postcss-modules-resolve-imports": "^1.3.0", - "postcss-modules-scope": "^1.0.0", - "postcss-modules-values": "^1.1.1", - "seekout": "^1.0.1" - }, - "engines": { - "node": ">= 4" - } - }, - "node_modules/css-modules-require-hook/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/css-modules-require-hook/node_modules/icss-utils": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-3.0.1.tgz", - "integrity": "sha512-ANhVLoEfe0KoC9+z4yiTaXOneB49K6JIXdS+yAgH0NERELpdIT7kkj2XxUPuHafeHnn8umXnECSpsfk1RTaUew==", - "dependencies": { - "postcss": "^6.0.2" - } - }, - "node_modules/css-modules-require-hook/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/css-modules-require-hook/node_modules/postcss": { - "version": "6.0.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", - "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", - "dependencies": { - "chalk": "^2.4.1", - "source-map": "^0.6.1", - "supports-color": "^5.4.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/css-modules-require-hook/node_modules/postcss-modules-extract-imports": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.1.tgz", - "integrity": "sha512-6jt9XZwUhwmRUhb/CkyJY020PYaPJsCyt3UjbaWo6XEbH/94Hmv6MP7fG2C5NDU/BcHzyGYxNtHvM+LTf9HrYw==", - "dependencies": { - "postcss": "^6.0.1" - } - }, - "node_modules/css-modules-require-hook/node_modules/postcss-modules-local-by-default": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz", - "integrity": "sha512-X4cquUPIaAd86raVrBwO8fwRfkIdbwFu7CTfEOjiZQHVQwlHRSkTgH5NLDmMm5+1hQO8u6dZ+TOOJDbay1hYpA==", - "dependencies": { - "css-selector-tokenizer": "^0.7.0", - "postcss": "^6.0.1" - } - }, - "node_modules/css-modules-require-hook/node_modules/postcss-modules-resolve-imports": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/postcss-modules-resolve-imports/-/postcss-modules-resolve-imports-1.3.0.tgz", - "integrity": "sha512-9xECsLcuR1DRu7mVhK6GIkdEeg4sdZXbLEcuEkDh9mKiz+uxDBfDREYiVehINdW0UPF9gbHnb64ZQMuRsqqkDA==", - "dependencies": { - "css-selector-tokenizer": "^0.7.0", - "icss-utils": "^3.0.1", - "minimist": "^1.2.0" - }, - "engines": { - "node": ">= 4" - }, - "peerDependencies": { - "postcss": "^6.0.0" - } - }, - "node_modules/css-modules-require-hook/node_modules/postcss-modules-scope": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz", - "integrity": "sha512-LTYwnA4C1He1BKZXIx1CYiHixdSe9LWYVKadq9lK5aCCMkoOkFyZ7aigt+srfjlRplJY3gIol6KUNefdMQJdlw==", - "dependencies": { - "css-selector-tokenizer": "^0.7.0", - "postcss": "^6.0.1" - } - }, - "node_modules/css-modules-require-hook/node_modules/postcss-modules-values": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz", - "integrity": "sha512-i7IFaR9hlQ6/0UgFuqM6YWaCfA1Ej8WMg8A5DggnH1UGKJvTV/ugqq/KaULixzzOi3T/tF6ClBXcHGCzdd5unA==", - "dependencies": { - "icss-replace-symbols": "^1.1.0", - "postcss": "^6.0.1" - } - }, - "node_modules/css-modules-require-hook/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/css-select": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", - "integrity": "sha512-dUQOBoqdR7QwV90WysXPLXG5LO7nhYBgiWVfxF80DKPF8zx1t/pUd2FYy73emg3zrjtM6dzmYgbHKfV2rxiHQA==", - "dependencies": { - "boolbase": "~1.0.0", - "css-what": "2.1", - "domutils": "1.5.1", - "nth-check": "~1.0.1" - } - }, - "node_modules/css-selector-tokenizer": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.3.tgz", - "integrity": "sha512-jWQv3oCEL5kMErj4wRnK/OPoBi0D+P1FR2cDCKYPaMeD2eW3/mttav8HT4hT1CKopiJI/psEULjkClhvJo4Lvg==", - "dependencies": { - "cssesc": "^3.0.0", - "fastparse": "^1.1.2" - } - }, - "node_modules/css-what": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", - "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", - "engines": { - "node": "*" - } - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", - "dev": true - }, - "node_modules/cssstyle": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.4.0.tgz", - "integrity": "sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==", - "dev": true, - "dependencies": { - "cssom": "0.3.x" - } - }, - "node_modules/csstype": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", - "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==", - "peer": true - }, - "node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", - "dependencies": { - "assert-plus": "^1.0.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/data-urls": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz", - "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==", - "dev": true, - "dependencies": { - "abab": "^2.0.0", - "whatwg-mimetype": "^2.2.0", - "whatwg-url": "^7.0.0" - } - }, - "node_modules/data-urls/node_modules/whatwg-url": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", - "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", - "dev": true, - "dependencies": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" - } - }, - "node_modules/dateformat": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", - "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", - "engines": { - "node": "*" - } - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decamelize-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", - "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", - "dependencies": { - "decamelize": "^1.1.0", - "map-obj": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/decamelize-keys/node_modules/map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decode-uri-component": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", - "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", - "dev": true, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/deep-merge": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/deep-merge/-/deep-merge-1.0.0.tgz", - "integrity": "sha512-cVG7u0dA5OtrgliqM30OLE3OPrB59PjWEjtCG4Go78OI/f/kP6aK4J/cF4E2f0PpN7A1QmBVxh1dKJkrIU6jCQ==", - "dependencies": { - "xtend": "~1.0.3" - } - }, - "node_modules/define-properties": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", - "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", - "dev": true, - "dependencies": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dev": true, - "dependencies": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/del": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz", - "integrity": "sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==", - "dependencies": { - "globby": "^11.0.1", - "graceful-fs": "^4.2.4", - "is-glob": "^4.0.1", - "is-path-cwd": "^2.2.0", - "is-path-inside": "^3.0.2", - "p-map": "^4.0.0", - "rimraf": "^3.0.2", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/del/node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/del/node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "engines": { - "node": ">=6" - } - }, - "node_modules/del/node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/del/node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/del/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/del/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/deprecation": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", - "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/detect-indent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", - "integrity": "sha512-BDKtmHlOzwI7iRuEkhzsnPoi5ypEhWAJB5RvHWe1kMr06js3uK5B3734i3ui5Yd+wOJV1cpE4JnivPD283GU/A==", - "dev": true, - "peer": true, - "dependencies": { - "repeating": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/detect-newline": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", - "integrity": "sha512-CwffZFvlJffUg9zZA0uqrjQayUTC8ob94pnr5sFwaVv3IOmkfUHcWH+jXaQK3askE51Cqe8/9Ql/0uXNwqZ8Zg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/diff-match-patch": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz", - "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==" - }, - "node_modules/diff-sequences": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.9.0.tgz", - "integrity": "sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/dir-glob/node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/direction": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/direction/-/direction-1.0.4.tgz", - "integrity": "sha512-GYqKi1aH7PJXxdhTeZBFrg8vUBeKXi+cNprXsC1kpJcbcVnV9wBsrOu1cQEdG0WeQwlfHiy3XvnKfIrJ2R0NzQ==", - "bin": { - "direction": "cli.js" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/dom-serializer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", - "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", - "dependencies": { - "domelementtype": "^1.3.0", - "entities": "^1.1.1" - } - }, - "node_modules/domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" - }, - "node_modules/domexception": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", - "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", - "dev": true, - "dependencies": { - "webidl-conversions": "^4.0.2" - } - }, - "node_modules/domhandler": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", - "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", - "dependencies": { - "domelementtype": "1" - } - }, - "node_modules/dompurify": { - "version": "2.4.5", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.5.tgz", - "integrity": "sha512-jggCCd+8Iqp4Tsz0nIvpcb22InKEBrGz5dw3EQJMs8HPJDsKbFIO3STYtAvCfDx26Muevn1MHVI0XxjgFfmiSA==" - }, - "node_modules/domutils": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", - "integrity": "sha512-gSu5Oi/I+3wDENBsOWBiRK1eoGxcywYSqg3rR960/+EfY0CF4EX1VPkgHOZ3WiS/Jg2DtliF6BhWcHlfpYUcGw==", - "dependencies": { - "dom-serializer": "0", - "domelementtype": "1" - } - }, - "node_modules/dot-prop": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.1.tgz", - "integrity": "sha512-l0p4+mIuJIua0mhxGoh4a+iNL9bmeK5DvnSVQa6T0OhrVmaEa1XScX5Etc673FePCJOArq/4Pa2cLGODUWTPOQ==", - "dev": true, - "dependencies": { - "is-obj": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/double-ended-queue": { - "version": "2.1.0-0", - "resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz", - "integrity": "sha512-+BNfZ+deCo8hMNpDqDnvT+c0XpJ5cUa6mqYq89bho2Ifze4URTqRkcwR399hWoTrTkbZ/XJYDgP6rc7pRgffEQ==" - }, - "node_modules/draggabilly": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/draggabilly/-/draggabilly-3.0.0.tgz", - "integrity": "sha512-aEs+B6prbMZQMxc9lgTpCBfyCUhRur/VFucHhIOvlvvdARTj7TcDmX/cdOUtqbjJJUh7+agyJXR5Z6IFe1MxwQ==", - "dependencies": { - "get-size": "^3.0.0", - "unidragger": "^3.0.0" - } - }, - "node_modules/duplexer2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", - "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", - "peer": true, - "dependencies": { - "readable-stream": "^2.0.2" - } - }, - "node_modules/duplexer2/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "peer": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/duplexer2/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "peer": true - }, - "node_modules/duplexer2/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "peer": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/duplexer3": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz", - "integrity": "sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==", - "dev": true - }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", - "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" - }, - "node_modules/electron-to-chromium": { - "version": "1.4.368", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.368.tgz", - "integrity": "sha512-e2aeCAixCj9M7nJxdB/wDjO6mbYX+lJJxSJCXDzlr5YPGYVofuJwGN9nKg2o6wWInjX6XmxRinn3AeJMK81ltw==", - "dev": true - }, - "node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "node_modules/emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/enabled": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", - "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" - }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/engine.io-client": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.4.0.tgz", - "integrity": "sha512-GyKPDyoEha+XZ7iEqam49vz6auPnNJ9ZBfy89f+rMMas8AuiMWOZ9PVzu8xb9ZC6rafUqiGHSCfu22ih66E+1g==", - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1", - "engine.io-parser": "~5.0.3", - "ws": "~8.11.0", - "xmlhttprequest-ssl": "~2.0.0" - } - }, - "node_modules/engine.io-client/node_modules/ws": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", - "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/engine.io-parser": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz", - "integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/enhanced-resolve": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.13.0.tgz", - "integrity": "sha512-eyV8f0y1+bzyfh8xAwW/WTSZpLbjhqc4ne9eGSH4Zo2ejdyiNG9pU6mf9DG8a7+Auk6MFTlNOT4Y2y/9k8GKVg==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" - }, - "node_modules/env-ci": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/env-ci/-/env-ci-9.1.1.tgz", - "integrity": "sha512-Im2yEWeF4b2RAMAaWvGioXk6m0UNaIjD8hj28j2ij5ldnIFrDQT0+pzDvpbRkcjurhXhf/AsBKv8P2rtmGi9Aw==", - "peer": true, - "dependencies": { - "execa": "^7.0.0", - "java-properties": "^1.0.2" - }, - "engines": { - "node": "^16.14 || >=18" - } - }, - "node_modules/env-ci/node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "peer": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/env-ci/node_modules/execa": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", - "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", - "peer": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.1", - "human-signals": "^4.3.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^3.0.7", - "strip-final-newline": "^3.0.0" - }, - "engines": { - "node": "^14.18.0 || ^16.14.0 || >=18.0.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/env-ci/node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "peer": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/env-ci/node_modules/human-signals": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", - "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", - "peer": true, - "engines": { - "node": ">=14.18.0" - } - }, - "node_modules/env-ci/node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "peer": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/env-ci/node_modules/npm-run-path": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", - "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", - "peer": true, - "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/env-ci/node_modules/npm-run-path/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/env-ci/node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/env-ci/node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "peer": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/env-ci/node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/env-ci/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "peer": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/envinfo": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", - "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", - "dev": true, - "bin": { - "envinfo": "dist/cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-abstract": { - "version": "1.21.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz", - "integrity": "sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==", - "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-set-tostringtag": "^2.0.1", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.2.0", - "get-symbol-description": "^1.0.0", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.10", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.3", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", - "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.7", - "string.prototype.trimend": "^1.0.6", - "string.prototype.trimstart": "^1.0.6", - "typed-array-length": "^1.0.4", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.9" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-array-method-boxes-properly": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", - "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", - "dev": true - }, - "node_modules/es-module-lexer": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.2.1.tgz", - "integrity": "sha512-9978wrXM50Y4rTMmW5kXIC09ZdXQZqkE4mxhwkd8VbzsGkXGPgV4zWuqQJgCEzYngdo2dYDa0l8xhX4fkSwJSg==", - "dev": true - }, - "node_modules/es-set-tostringtag": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", - "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/escodegen": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", - "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", - "dev": true, - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=4.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, - "node_modules/escodegen/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/ev-emitter": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ev-emitter/-/ev-emitter-2.1.2.tgz", - "integrity": "sha512-jQ5Ql18hdCQ4qS+RCrbLfz1n+Pags27q5TwMKvZyhp5hh2UULUYZUy1keqj6k6SYsdqIYjnmz7xyyEY0V67B8Q==" - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/exec-sh": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.6.tgz", - "integrity": "sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w==", - "dev": true - }, - "node_modules/execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "dependencies": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==", - "dev": true, - "dependencies": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/expand-brackets/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", - "dev": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/expand-range": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", - "integrity": "sha512-AFASGfIlnIbkKPQwX1yHaDjFvh/1gyKJODme52V6IORh69uEYgZp0o9C+qsIGNVEiuuhQU0CSSl++Rlegg1qvA==", - "dev": true, - "dependencies": { - "fill-range": "^2.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-range/node_modules/fill-range": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", - "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", - "dev": true, - "dependencies": { - "is-number": "^2.1.0", - "isobject": "^2.0.0", - "randomatic": "^3.0.0", - "repeat-element": "^1.1.2", - "repeat-string": "^1.5.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-range/node_modules/is-number": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", - "integrity": "sha512-QUzH43Gfb9+5yckcrSA0VBDwEtDUchrk4F6tfJZQuNzDJbEDB9cZNzSfXGQ1jqmdDY/kl41lUOWM9syA8z8jlg==", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-range/node_modules/isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==", - "dev": true, - "dependencies": { - "isarray": "1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-range/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expect": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-24.9.0.tgz", - "integrity": "sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q==", - "dev": true, - "dependencies": { - "@jest/types": "^24.9.0", - "ansi-styles": "^3.2.0", - "jest-get-type": "^24.9.0", - "jest-matcher-utils": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-regex-util": "^24.9.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.1", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.5.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/express-winston": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/express-winston/-/express-winston-4.2.0.tgz", - "integrity": "sha512-EMD74g63nVHi7pFleQw7KHCxiA1pjF5uCwbCfzGqmFxs9KvlDPIVS3cMGpULm6MshExMT9TjC3SqmRGB9kb7yw==", - "dependencies": { - "chalk": "^2.4.2", - "lodash": "^4.17.21" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "winston": ">=3.x <4" - } - }, - "node_modules/express/node_modules/body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.1", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/express/node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/express/node_modules/raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "node_modules/extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", - "dev": true, - "dependencies": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "dependencies": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", - "dev": true, - "dependencies": { - "is-descriptor": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", - "engines": [ - "node >=0.6.0" - ] - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "node_modules/fast-glob": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", - "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "node_modules/fastest-levenshtein": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", - "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", - "engines": { - "node": ">= 4.9.1" - } - }, - "node_modules/fastparse": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", - "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==" - }, - "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/fecha": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", - "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" - }, - "node_modules/figures": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-5.0.0.tgz", - "integrity": "sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg==", - "peer": true, - "dependencies": { - "escape-string-regexp": "^5.0.0", - "is-unicode-supported": "^1.2.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/figures/node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/file-saver": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz", - "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==" - }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "dev": true, - "optional": true - }, - "node_modules/filename-regex": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", - "integrity": "sha512-BTCqyBaWBTsauvnHiE8i562+EdJj+oUpkqWp2R1iCoR8f6oo8STRu3of7WJJ0TqWtxN50a5YFpzYK4Jj9esYfQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/find-cache-dir": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", - "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", - "dev": true, - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/avajs/find-cache-dir?sponsor=1" - } - }, - "node_modules/find-cache-dir/node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/find-versions": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-5.1.0.tgz", - "integrity": "sha512-+iwzCJ7C5v5KgcBuueqVoNiHVoQpwiUK5XFLjf0affFTep+Wcw93tPvmb8tqujDNmzhBDPddnWV/qgWSXgq+Hg==", - "peer": true, - "dependencies": { - "semver-regex": "^4.0.5" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/find-yarn-workspace-root": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz", - "integrity": "sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ==", - "dependencies": { - "micromatch": "^4.0.2" - } - }, - "node_modules/find-yarn-workspace-root/node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/findandreplacedomtext": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/findandreplacedomtext/-/findandreplacedomtext-0.4.6.tgz", - "integrity": "sha512-CVRIKbEwfWoKTSnLrmyX26WjMY7o0aUUTm0RXN47fF/eHINJa8C+YHZxGagC7gMWVaAEBFwH7uVXuhwZcylWOQ==" - }, - "node_modules/fn.name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", - "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" - }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.3" - } - }, - "node_modules/for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/for-own": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", - "integrity": "sha512-SKmowqGTJoPzLO1T0BBJpkfp3EMacCMOuH40hOUbrbzElVktk4DioXVM99QkLCyKoiuOmyjgcWMpVz2xjE7LZw==", - "dev": true, - "dependencies": { - "for-in": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", - "engines": { - "node": "*" - } - }, - "node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==", - "dev": true, - "dependencies": { - "map-cache": "^0.2.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/from2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", - "dependencies": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" - } - }, - "node_modules/from2/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/from2/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/from2/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/fs-readdir-recursive": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", - "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", - "dev": true - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "node_modules/function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/generic-names": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/generic-names/-/generic-names-1.0.3.tgz", - "integrity": "sha512-b6OHfQuKasIKM9b6YPkX+KUj/TLBTx3B/1aT1T5F12FEuEqyFMdr59OMS53aoaSw8eVtapdqieX6lbg5opaOhA==", - "dependencies": { - "loader-utils": "^0.2.16" - } - }, - "node_modules/generic-names/node_modules/big.js": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", - "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", - "engines": { - "node": "*" - } - }, - "node_modules/generic-names/node_modules/emojis-list": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", - "integrity": "sha512-knHEZMgs8BB+MInokmNTg/OyPlAddghe1YBgNwJBc5zsJi/uyIcXoSDsL/W9ymOsBoBGdPIHXYJ9+qKFwRwDng==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/generic-names/node_modules/json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha512-4xrs1aW+6N5DalkqSVA8fxh458CXvR99WU8WLKmq4v8eWAL86Xo3BVqyd3SkA9wEVjCMqyvvRRkshAdOnBp5rw==", - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/generic-names/node_modules/loader-utils": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", - "integrity": "sha512-tiv66G0SmiOx+pLWMtGEkfSEejxvb6N6uRrQjfWJIT79W9GMpgKeCAmm9aVBKtd4WEgntciI8CsGqjpDoCWJug==", - "dependencies": { - "big.js": "^3.1.3", - "emojis-list": "^2.0.0", - "json5": "^0.5.0", - "object-assign": "^4.0.1" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", - "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-size": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-size/-/get-size-3.0.0.tgz", - "integrity": "sha512-Y8aiXLq4leR7807UY0yuKEwif5s3kbVp1nTv+i4jBeoUzByTLKkLWu/HorS6/pB+7gsB0o7OTogC8AoOOeT0Hw==" - }, - "node_modules/get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", - "dependencies": { - "assert-plus": "^1.0.0" - } - }, - "node_modules/git-log-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/git-log-parser/-/git-log-parser-1.2.0.tgz", - "integrity": "sha512-rnCVNfkTL8tdNryFuaY0fYiBWEBcgF748O6ZI61rslBvr2o7U65c2/6npCRqH40vuAhtgtDiqLTJjBVdrejCzA==", - "peer": true, - "dependencies": { - "argv-formatter": "~1.0.0", - "spawn-error-forwarder": "~1.0.0", - "split2": "~1.0.0", - "stream-combiner2": "~1.1.1", - "through2": "~2.0.0", - "traverse": "~0.6.6" - } - }, - "node_modules/git-log-parser/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "peer": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/git-log-parser/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "peer": true - }, - "node_modules/git-log-parser/node_modules/split2": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-1.0.0.tgz", - "integrity": "sha512-NKywug4u4pX/AZBB1FCPzZ6/7O+Xhz1qMVbzTvvKvikjO99oPN87SkK08mEY9P63/5lWjK+wgOOgApnTg5r6qg==", - "peer": true, - "dependencies": { - "through2": "~2.0.0" - } - }, - "node_modules/git-log-parser/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "peer": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/git-log-parser/node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "peer": true, - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "node_modules/git-log-parser/node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "peer": true, - "engines": { - "node": ">=0.4" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-base": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", - "integrity": "sha512-ab1S1g1EbO7YzauaJLkgLp7DZVAqj9M/dvKlTt8DkXA2tiOIcSMrlVI2J1RZyB5iJVccEscjGn+kpOG9788MHA==", - "dev": true, - "dependencies": { - "glob-parent": "^2.0.0", - "is-glob": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/glob-base/node_modules/glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha512-JDYOvfxio/t42HKdxkAYaCiBN7oYiuxykOxKxdaUW5Qn0zaYN3gRQWolrwdnf0shM9/EP0ebuuTmyoXNr1cC5w==", - "dev": true, - "dependencies": { - "is-glob": "^2.0.0" - } - }, - "node_modules/glob-base/node_modules/is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/glob-base/node_modules/is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg==", - "dev": true, - "dependencies": { - "is-extglob": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/glob-to-regexp": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz", - "integrity": "sha512-Iozmtbqv0noj0uDDqoL0zNq0VBEfK2YFoMAZoxJe4cwphvLR+JskfF30QhXHOR4m3KrE6NLRYw+U9MRXvifyig==" - }, - "node_modules/global-dirs": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", - "integrity": "sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==", - "dev": true, - "dependencies": { - "ini": "^1.3.4" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", - "dev": true, - "dependencies": { - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globby/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/got": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", - "integrity": "sha512-Y/K3EDuiQN9rTZhBvPRWMLXIKdeD1Rj0nzunfoi0Yyn5WBEbzxXKU9Ub2X41oZBagVWOBU3MuDonFMgPWQFnwg==", - "dev": true, - "dependencies": { - "create-error-class": "^3.0.0", - "duplexer3": "^0.1.4", - "get-stream": "^3.0.0", - "is-redirect": "^1.0.0", - "is-retry-allowed": "^1.0.0", - "is-stream": "^1.0.0", - "lowercase-keys": "^1.0.0", - "safe-buffer": "^5.0.1", - "timed-out": "^4.0.0", - "unzip-response": "^2.0.1", - "url-parse-lax": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/got/node_modules/get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" - }, - "node_modules/growly": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", - "integrity": "sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw==", - "dev": true - }, - "node_modules/handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, - "node_modules/handlebars/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", - "engines": { - "node": ">=4" - } - }, - "node_modules/har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "deprecated": "this library is no longer supported", - "dependencies": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/hard-rejection": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", - "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", - "dev": true, - "peer": true, - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-ansi/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "engines": { - "node": ">=4" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==", - "dev": true, - "dependencies": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==", - "dev": true, - "dependencies": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-values/node_modules/kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/hoist-non-react-statics": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", - "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "dependencies": { - "react-is": "^16.7.0" - } - }, - "node_modules/home-or-tmp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", - "integrity": "sha512-ycURW7oUxE2sNiPVw1HVEFsW+ecOpJ5zaj7eC0RlwhibhRBod20muUN8qu/gzx956YrLolVvs1MTXwKgC2rVEg==", - "dev": true, - "peer": true, - "dependencies": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/hook-std": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hook-std/-/hook-std-3.0.0.tgz", - "integrity": "sha512-jHRQzjSDzMtFy34AGj1DN+vq54WVuhSvKgrHf0OMiFQTwDD4L/qqofVEWjLOBMTn5+lCD3fPg32W9yOfnEJTTw==", - "peer": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" - }, - "node_modules/html-encoding-sniffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", - "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", - "dev": true, - "dependencies": { - "whatwg-encoding": "^1.0.1" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "node_modules/htmlparser2": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", - "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", - "dependencies": { - "domelementtype": "^1.3.1", - "domhandler": "^2.3.0", - "domutils": "^1.5.1", - "entities": "^1.1.1", - "inherits": "^2.0.1", - "readable-stream": "^3.1.1" - } - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/http-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", - "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", - "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - }, - "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" - } - }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/human-signals": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", - "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", - "peer": true, - "engines": { - "node": ">=16.17.0" - } - }, - "node_modules/humanize-duration": { - "version": "3.28.0", - "resolved": "https://registry.npmjs.org/humanize-duration/-/humanize-duration-3.28.0.tgz", - "integrity": "sha512-jMAxraOOmHuPbffLVDKkEKi/NeG8dMqP8lGRd6Tbf7JgAeG33jjgPWDbXXU7ypCI0o+oNKJFgbSB9FKVdWNI2A==" - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/icss-replace-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", - "integrity": "sha512-chIaY3Vh2mh2Q3RGXttaDIzeiPvaVXJ+C4DAh/w3c37SKZ/U6PGMmuicR2EQQp9bKG8zLMCl7I+PtIoOOPp8Gg==" - }, - "node_modules/icss-utils": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.1.tgz", - "integrity": "sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==", - "dev": true, - "dependencies": { - "postcss": "^7.0.14" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "engines": { - "node": ">= 4" - } - }, - "node_modules/ignore-by-default": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", - "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", - "dev": true - }, - "node_modules/immer": { - "version": "9.0.21", - "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", - "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/immer" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "peer": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-fresh/node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "peer": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/import-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/import-from/-/import-from-4.0.0.tgz", - "integrity": "sha512-P9J71vT5nLlDeV8FHs5nNxaLbrpfAV5cF5srvbZfpwpcJoM/xZR3hiv+q+SAnuSmuGbXMWud063iIMx/V/EWZQ==", - "engines": { - "node": ">=12.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-lazy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", - "integrity": "sha512-m7ZEHgtw69qOGw+jwxXkHlrlIPdTGkyh66zXZ1ajZbxkDBNjSY/LGbmjc7h0s2ELsUDTAhFr55TrPSSqJGPG0A==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/import-local": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", - "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", - "dev": true, - "dependencies": { - "pkg-dir": "^3.0.0", - "resolve-cwd": "^2.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/import-local/node_modules/pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dev": true, - "dependencies": { - "find-up": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" - }, - "node_modules/inline-style-parser": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", - "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==" - }, - "node_modules/internal-slot": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", - "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/interpret": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", - "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/into-stream": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-6.0.0.tgz", - "integrity": "sha512-XHbaOAvP+uFKUFsOgoNPRjLkwB+I22JFPFe5OjTkQ0nwgj6+pSjb4NmB6VMxaPshLiOf+zcpOCBQuLwC1KHhZA==", - "dependencies": { - "from2": "^2.3.0", - "p-is-promise": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dev": true, - "dependencies": { - "loose-envify": "^1.0.0" - } - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-alphabetical": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", - "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-alphanumerical": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", - "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", - "dependencies": { - "is-alphabetical": "^1.0.0", - "is-decimal": "^1.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-array-buffer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", - "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" - }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "dependencies": { - "has-bigints": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "optional": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dependencies": { - "ci-info": "^2.0.0" - }, - "bin": { - "is-ci": "bin.js" - } - }, - "node_modules/is-core-module": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.0.tgz", - "integrity": "sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==", - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-decimal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", - "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "dependencies": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-dotfile": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", - "integrity": "sha512-9YclgOGtN/f8zx0Pr4FQYMdibBiTaH3sn52vjYip4ZSf6C4/6RfTEZ+MR4GvKhCxdPh21Bg42/WL55f6KSnKpg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-equal-shallow": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", - "integrity": "sha512-0EygVC5qPvIyb+gSz7zdD5/AAoS6Qrx1e//6N4yv4oNm30kqvdmG66oZFWVlQHUWe5OjP08FuTw2IdT0EOTcYA==", - "dev": true, - "dependencies": { - "is-primitive": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "dependencies": { - "is-plain-object": "^2.0.4" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-finite": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", - "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-hexadecimal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", - "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-hotkey": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/is-hotkey/-/is-hotkey-0.1.8.tgz", - "integrity": "sha512-qs3NZ1INIS+H+yeo7cD9pDfwYV/jqRh1JG9S9zYrNudkoUQg7OL7ziXqRKu+InFjUIDoP2o6HIkLYMh1pcWgyQ==" - }, - "node_modules/is-installed-globally": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", - "integrity": "sha512-ERNhMg+i/XgDwPIPF3u24qpajVreaiSuvpb1Uu0jugw7KKcxGyCX8cgp8P5fwTmAuXku6beDHHECdKArjlg7tw==", - "dev": true, - "dependencies": { - "global-dirs": "^0.1.0", - "is-path-inside": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-npm": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", - "integrity": "sha512-9r39FIr3d+KD9SbX0sfMsHzb5PP3uimOiwr3YupUaUFG4W0l1U57Rx3utpttV7qz5U3jmrO5auUa04LU9pyHsg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-path-cwd": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", - "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/is-path-inside": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha512-qhsCR/Esx4U4hg/9I19OVUAJkGWtjRYHMRgUMZE2TDdj+Ag+kttZanLupfddNyglzz50cUlmWzUaI37GDfNx/g==", - "dev": true, - "dependencies": { - "path-is-inside": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-posix-bracket": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", - "integrity": "sha512-Yu68oeXJ7LeWNmZ3Zov/xg/oDBnBK2RNxwYY1ilNJX+tKKZqgPK+qOn/Gs9jEu66KDY9Netf5XLKNGzas/vPfQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-primitive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", - "integrity": "sha512-N3w1tFaRfk3UrPfqeRyD+GYDASU3W5VinKhlORy8EWVf/sIdDL9GAcew85XmktCfH+ngG7SRXEVDoO18WMdB/Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-redirect": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", - "integrity": "sha512-cr/SlUEe5zOGmzvj9bUyC4LVvkNVAXu4GytXLNMr1pny+a65MpQ9IJzFHD5vi7FyJgb4qt27+eS3TuQnqB+RQw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-retry-allowed": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", - "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-text-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", - "integrity": "sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==", - "dependencies": { - "text-extensions": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", - "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" - }, - "node_modules/is-unicode-supported": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", - "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" - }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" - }, - "node_modules/issue-parser": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-6.0.0.tgz", - "integrity": "sha512-zKa/Dxq2lGsBIXQ7CUZWTHfvxPC2ej0KfO7fIPqLlHB9J2hJ7rGhZ5rilhuufylr4RXYPzJUeFjKxz305OsNlA==", - "dependencies": { - "lodash.capitalize": "^4.2.1", - "lodash.escaperegexp": "^4.1.2", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.uniqby": "^4.7.0" - }, - "engines": { - "node": ">=10.13" - } - }, - "node_modules/istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", - "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", - "dev": true, - "dependencies": { - "@babel/generator": "^7.4.0", - "@babel/parser": "^7.4.3", - "@babel/template": "^7.4.0", - "@babel/traverse": "^7.4.3", - "@babel/types": "^7.4.0", - "istanbul-lib-coverage": "^2.0.5", - "semver": "^6.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/istanbul-lib-report": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", - "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", - "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/istanbul-lib-report/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", - "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "rimraf": "^2.6.3", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/istanbul-lib-source-maps/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/istanbul-reports": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.7.tgz", - "integrity": "sha512-uu1F/L1o5Y6LzPVSVZXNOoD/KXpJue9aeLRd0sM9uMXfZvzomB0WxVamWb5ue8kA2vVWEmW7EG+A5n3f1kqHKg==", - "dev": true, - "dependencies": { - "html-escaper": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/java-properties": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/java-properties/-/java-properties-1.0.2.tgz", - "integrity": "sha512-qjdpeo2yKlYTH7nFdK0vbZWuTCesk4o63v5iVOlhMQPfuIZQfW/HI35SjfhA+4qpg36rnFSvUK5b1m+ckIblQQ==", - "peer": true, - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/jest": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-24.9.0.tgz", - "integrity": "sha512-YvkBL1Zm7d2B1+h5fHEOdyjCG+sGMz4f8D86/0HiqJ6MB4MnDc8FgP5vdWsGnemOQro7lnYo8UakZ3+5A0jxGw==", - "dev": true, - "dependencies": { - "import-local": "^2.0.0", - "jest-cli": "^24.9.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-changed-files": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-24.9.0.tgz", - "integrity": "sha512-6aTWpe2mHF0DhL28WjdkO8LyGjs3zItPET4bMSeXU6T3ub4FPMw+mcOcbdGXQOAfmLcxofD23/5Bl9Z4AkFwqg==", - "dev": true, - "dependencies": { - "@jest/types": "^24.9.0", - "execa": "^1.0.0", - "throat": "^4.0.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-cli": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-24.9.0.tgz", - "integrity": "sha512-+VLRKyitT3BWoMeSUIHRxV/2g8y9gw91Jh5z2UmXZzkZKpbC08CSehVxgHUwTpy+HwGcns/tqafQDJW7imYvGg==", - "dev": true, - "dependencies": { - "@jest/core": "^24.9.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "chalk": "^2.0.1", - "exit": "^0.1.2", - "import-local": "^2.0.0", - "is-ci": "^2.0.0", - "jest-config": "^24.9.0", - "jest-util": "^24.9.0", - "jest-validate": "^24.9.0", - "prompts": "^2.0.1", - "realpath-native": "^1.1.0", - "yargs": "^13.3.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-config": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.9.0.tgz", - "integrity": "sha512-RATtQJtVYQrp7fvWg6f5y3pEFj9I+H8sWw4aKxnDZ96mob5i5SD6ZEGWgMLXQ4LE8UurrjbdlLWdUeo+28QpfQ==", - "dev": true, - "dependencies": { - "@babel/core": "^7.1.0", - "@jest/test-sequencer": "^24.9.0", - "@jest/types": "^24.9.0", - "babel-jest": "^24.9.0", - "chalk": "^2.0.1", - "glob": "^7.1.1", - "jest-environment-jsdom": "^24.9.0", - "jest-environment-node": "^24.9.0", - "jest-get-type": "^24.9.0", - "jest-jasmine2": "^24.9.0", - "jest-regex-util": "^24.3.0", - "jest-resolve": "^24.9.0", - "jest-util": "^24.9.0", - "jest-validate": "^24.9.0", - "micromatch": "^3.1.10", - "pretty-format": "^24.9.0", - "realpath-native": "^1.1.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-diff": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.9.0.tgz", - "integrity": "sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ==", - "dev": true, - "dependencies": { - "chalk": "^2.0.1", - "diff-sequences": "^24.9.0", - "jest-get-type": "^24.9.0", - "pretty-format": "^24.9.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-docblock": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-24.9.0.tgz", - "integrity": "sha512-F1DjdpDMJMA1cN6He0FNYNZlo3yYmOtRUnktrT9Q37njYzC5WEaDdmbynIgy0L/IvXvvgsG8OsqhLPXTpfmZAA==", - "dev": true, - "dependencies": { - "detect-newline": "^2.1.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-each": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.9.0.tgz", - "integrity": "sha512-ONi0R4BvW45cw8s2Lrx8YgbeXL1oCQ/wIDwmsM3CqM/nlblNCPmnC3IPQlMbRFZu3wKdQ2U8BqM6lh3LJ5Bsog==", - "dev": true, - "dependencies": { - "@jest/types": "^24.9.0", - "chalk": "^2.0.1", - "jest-get-type": "^24.9.0", - "jest-util": "^24.9.0", - "pretty-format": "^24.9.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-environment-jsdom": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.9.0.tgz", - "integrity": "sha512-Zv9FV9NBRzLuALXjvRijO2351DRQeLYXtpD4xNvfoVFw21IOKNhZAEUKcbiEtjTkm2GsJ3boMVgkaR7rN8qetA==", - "dev": true, - "dependencies": { - "@jest/environment": "^24.9.0", - "@jest/fake-timers": "^24.9.0", - "@jest/types": "^24.9.0", - "jest-mock": "^24.9.0", - "jest-util": "^24.9.0", - "jsdom": "^11.5.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-environment-node": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-24.9.0.tgz", - "integrity": "sha512-6d4V2f4nxzIzwendo27Tr0aFm+IXWa0XEUnaH6nU0FMaozxovt+sfRvh4J47wL1OvF83I3SSTu0XK+i4Bqe7uA==", - "dev": true, - "dependencies": { - "@jest/environment": "^24.9.0", - "@jest/fake-timers": "^24.9.0", - "@jest/types": "^24.9.0", - "jest-mock": "^24.9.0", - "jest-util": "^24.9.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-get-type": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", - "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-haste-map": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.9.0.tgz", - "integrity": "sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ==", - "dev": true, - "dependencies": { - "@jest/types": "^24.9.0", - "anymatch": "^2.0.0", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.1.15", - "invariant": "^2.2.4", - "jest-serializer": "^24.9.0", - "jest-util": "^24.9.0", - "jest-worker": "^24.9.0", - "micromatch": "^3.1.10", - "sane": "^4.0.3", - "walker": "^1.0.7" - }, - "engines": { - "node": ">= 6" - }, - "optionalDependencies": { - "fsevents": "^1.2.7" - } - }, - "node_modules/jest-haste-map/node_modules/anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "dependencies": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - } - }, - "node_modules/jest-haste-map/node_modules/fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "deprecated": "fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "dependencies": { - "bindings": "^1.5.0", - "nan": "^2.12.1" - }, - "engines": { - "node": ">= 4.0" - } - }, - "node_modules/jest-haste-map/node_modules/normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", - "dev": true, - "dependencies": { - "remove-trailing-separator": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/jest-jasmine2": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-24.9.0.tgz", - "integrity": "sha512-Cq7vkAgaYKp+PsX+2/JbTarrk0DmNhsEtqBXNwUHkdlbrTBLtMJINADf2mf5FkowNsq8evbPc07/qFO0AdKTzw==", - "dev": true, - "dependencies": { - "@babel/traverse": "^7.1.0", - "@jest/environment": "^24.9.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "chalk": "^2.0.1", - "co": "^4.6.0", - "expect": "^24.9.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^24.9.0", - "jest-matcher-utils": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-runtime": "^24.9.0", - "jest-snapshot": "^24.9.0", - "jest-util": "^24.9.0", - "pretty-format": "^24.9.0", - "throat": "^4.0.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-leak-detector": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-24.9.0.tgz", - "integrity": "sha512-tYkFIDsiKTGwb2FG1w8hX9V0aUb2ot8zY/2nFg087dUageonw1zrLMP4W6zsRO59dPkTSKie+D4rhMuP9nRmrA==", - "dev": true, - "dependencies": { - "jest-get-type": "^24.9.0", - "pretty-format": "^24.9.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-matcher-utils": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz", - "integrity": "sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA==", - "dev": true, - "dependencies": { - "chalk": "^2.0.1", - "jest-diff": "^24.9.0", - "jest-get-type": "^24.9.0", - "pretty-format": "^24.9.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-message-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", - "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/stack-utils": "^1.0.1", - "chalk": "^2.0.1", - "micromatch": "^3.1.10", - "slash": "^2.0.0", - "stack-utils": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-mock": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", - "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", - "dev": true, - "dependencies": { - "@jest/types": "^24.9.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", - "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-resolve": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.9.0.tgz", - "integrity": "sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ==", - "dev": true, - "dependencies": { - "@jest/types": "^24.9.0", - "browser-resolve": "^1.11.3", - "chalk": "^2.0.1", - "jest-pnp-resolver": "^1.2.1", - "realpath-native": "^1.1.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-24.9.0.tgz", - "integrity": "sha512-Fm7b6AlWnYhT0BXy4hXpactHIqER7erNgIsIozDXWl5dVm+k8XdGVe1oTg1JyaFnOxarMEbax3wyRJqGP2Pq+g==", - "dev": true, - "dependencies": { - "@jest/types": "^24.9.0", - "jest-regex-util": "^24.3.0", - "jest-snapshot": "^24.9.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-runner": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-24.9.0.tgz", - "integrity": "sha512-KksJQyI3/0mhcfspnxxEOBueGrd5E4vV7ADQLT9ESaCzz02WnbdbKWIf5Mkaucoaj7obQckYPVX6JJhgUcoWWg==", - "dev": true, - "dependencies": { - "@jest/console": "^24.7.1", - "@jest/environment": "^24.9.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "chalk": "^2.4.2", - "exit": "^0.1.2", - "graceful-fs": "^4.1.15", - "jest-config": "^24.9.0", - "jest-docblock": "^24.3.0", - "jest-haste-map": "^24.9.0", - "jest-jasmine2": "^24.9.0", - "jest-leak-detector": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-resolve": "^24.9.0", - "jest-runtime": "^24.9.0", - "jest-util": "^24.9.0", - "jest-worker": "^24.6.0", - "source-map-support": "^0.5.6", - "throat": "^4.0.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-runner/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/jest-runner/node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/jest-runtime": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-24.9.0.tgz", - "integrity": "sha512-8oNqgnmF3v2J6PVRM2Jfuj8oX3syKmaynlDMMKQ4iyzbQzIG6th5ub/lM2bCMTmoTKM3ykcUYI2Pw9xwNtjMnw==", - "dev": true, - "dependencies": { - "@jest/console": "^24.7.1", - "@jest/environment": "^24.9.0", - "@jest/source-map": "^24.3.0", - "@jest/transform": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/yargs": "^13.0.0", - "chalk": "^2.0.1", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.1.15", - "jest-config": "^24.9.0", - "jest-haste-map": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-mock": "^24.9.0", - "jest-regex-util": "^24.3.0", - "jest-resolve": "^24.9.0", - "jest-snapshot": "^24.9.0", - "jest-util": "^24.9.0", - "jest-validate": "^24.9.0", - "realpath-native": "^1.1.0", - "slash": "^2.0.0", - "strip-bom": "^3.0.0", - "yargs": "^13.3.0" - }, - "bin": { - "jest-runtime": "bin/jest-runtime.js" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-serializer": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.9.0.tgz", - "integrity": "sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-snapshot": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.9.0.tgz", - "integrity": "sha512-uI/rszGSs73xCM0l+up7O7a40o90cnrk429LOiK3aeTvfC0HHmldbd81/B7Ix81KSFe1lwkbl7GnBGG4UfuDew==", - "dev": true, - "dependencies": { - "@babel/types": "^7.0.0", - "@jest/types": "^24.9.0", - "chalk": "^2.0.1", - "expect": "^24.9.0", - "jest-diff": "^24.9.0", - "jest-get-type": "^24.9.0", - "jest-matcher-utils": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-resolve": "^24.9.0", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "pretty-format": "^24.9.0", - "semver": "^6.2.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", - "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", - "dev": true, - "dependencies": { - "@jest/console": "^24.9.0", - "@jest/fake-timers": "^24.9.0", - "@jest/source-map": "^24.9.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "callsites": "^3.0.0", - "chalk": "^2.0.1", - "graceful-fs": "^4.1.15", - "is-ci": "^2.0.0", - "mkdirp": "^0.5.1", - "slash": "^2.0.0", - "source-map": "^0.6.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-util/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/jest-validate": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.9.0.tgz", - "integrity": "sha512-HPIt6C5ACwiqSiwi+OfSSHbK8sG7akG8eATl+IPKaeIjtPOeBUd/g3J7DghugzxrGjI93qS/+RPKe1H6PqvhRQ==", - "dev": true, - "dependencies": { - "@jest/types": "^24.9.0", - "camelcase": "^5.3.1", - "chalk": "^2.0.1", - "jest-get-type": "^24.9.0", - "leven": "^3.1.0", - "pretty-format": "^24.9.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-watcher": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-24.9.0.tgz", - "integrity": "sha512-+/fLOfKPXXYJDYlks62/4R4GoT+GU1tYZed99JSCOsmzkkF7727RqKrjNAxtfO4YpGv11wybgRvCjR73lK2GZw==", - "dev": true, - "dependencies": { - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/yargs": "^13.0.0", - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.1", - "jest-util": "^24.9.0", - "string-length": "^2.0.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-worker": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", - "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", - "dev": true, - "dependencies": { - "merge-stream": "^2.0.0", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jquery": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-2.2.4.tgz", - "integrity": "sha512-lBHj60ezci2u1v2FqnZIraShGgEXq35qCzMv4lITyHGppTnA13rwR0MgwyNJh9TnDs3aXUvd1xjAotfraMHX/Q==" - }, - "node_modules/jquery-ui": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/jquery-ui/-/jquery-ui-1.12.1.tgz", - "integrity": "sha512-K/kDBMXkTky5LH+gqbMvttU1ipqCTaecKyAFjwHjUnPTVfm5I5PZC7We31iNR3yWtAHNqoxkLoit06lR/gKVlA==" - }, - "node_modules/jquery.cookie": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jquery.cookie/-/jquery.cookie-1.4.1.tgz", - "integrity": "sha512-c/hZOOL+8VSw/FkTVH637gS1/6YzMSCROpTZ2qBYwJ7s7sHajU7uBkSSiE5+GXWwrfCCyO+jsYjUQ7Hs2rIxAA==" - }, - "node_modules/jquery.scrollto": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/jquery.scrollto/-/jquery.scrollto-2.1.3.tgz", - "integrity": "sha512-4M+t5R4QRnq1CSRU9gswZ7Z4zRfcoWmgD31HRrLYCCI4J7tLWeE0ZoT+0JpI7GcT0YKTqcfw+p8t2yYEKFfrHA==", - "dependencies": { - "jquery": ">=1.8" - } - }, - "node_modules/js-cookie": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz", - "integrity": "sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==" - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "peer": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" - }, - "node_modules/jsdom": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz", - "integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==", - "dev": true, - "dependencies": { - "abab": "^2.0.0", - "acorn": "^5.5.3", - "acorn-globals": "^4.1.0", - "array-equal": "^1.0.0", - "cssom": ">= 0.3.2 < 0.4.0", - "cssstyle": "^1.0.0", - "data-urls": "^1.0.0", - "domexception": "^1.0.1", - "escodegen": "^1.9.1", - "html-encoding-sniffer": "^1.0.2", - "left-pad": "^1.3.0", - "nwsapi": "^2.0.7", - "parse5": "4.0.0", - "pn": "^1.1.0", - "request": "^2.87.0", - "request-promise-native": "^1.0.5", - "sax": "^1.2.4", - "symbol-tree": "^3.2.2", - "tough-cookie": "^2.3.4", - "w3c-hr-time": "^1.0.1", - "webidl-conversions": "^4.0.2", - "whatwg-encoding": "^1.0.3", - "whatwg-mimetype": "^2.1.0", - "whatwg-url": "^6.4.1", - "ws": "^5.2.0", - "xml-name-validator": "^3.0.0" - } - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" - }, - "node_modules/json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", - "engines": [ - "node >= 0.2.0" - ] - }, - "node_modules/JSONStream": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", - "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", - "dependencies": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" - }, - "bin": { - "JSONStream": "bin.js" - }, - "engines": { - "node": "*" - } - }, - "node_modules/jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/klaw-sync": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz", - "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==", - "dependencies": { - "graceful-fs": "^4.1.11" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/kuler": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", - "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" - }, - "node_modules/latest-version": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", - "integrity": "sha512-Be1YRHWWlZaSsrz2U+VInk+tO0EwLIyV+23RhWLINJYwg/UIikxjlj3MhH37/6/EDCAusjajvMkMMUXRaMWl/w==", - "dev": true, - "dependencies": { - "package-json": "^4.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/left-pad": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", - "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==", - "deprecated": "use String.prototype.padStart()", - "dev": true - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", - "dev": true, - "dependencies": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lines-and-columns": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.3.tgz", - "integrity": "sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w==", - "peer": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/load-json-file/node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", - "engines": { - "node": ">=4" - } - }, - "node_modules/loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", - "dev": true, - "engines": { - "node": ">=6.11.5" - } - }, - "node_modules/loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", - "dev": true, - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - }, - "engines": { - "node": ">=8.9.0" - } - }, - "node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", - "peer": true - }, - "node_modules/lodash.assign": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", - "integrity": "sha512-hFuH8TY+Yji7Eja3mGiuAxBqLagejScbG8GbG0j6o9vzn0YL14My+ktnqtZgFTosKymC9/44wP6s7xyuLfnClw==", - "dev": true - }, - "node_modules/lodash.assignin": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz", - "integrity": "sha512-yX/rx6d/UTVh7sSVWVSIMjfnz95evAgDFdb1ZozC35I9mSFCkmzptOzevxjgbQUsc78NR44LVHWjsoMQXy9FDg==" - }, - "node_modules/lodash.bind": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz", - "integrity": "sha512-lxdsn7xxlCymgLYo1gGvVrfHmkjDiyqVv62FAeF2i5ta72BipE1SLxw8hPEPLhD4/247Ijw07UQH7Hq/chT5LA==" - }, - "node_modules/lodash.capitalize": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz", - "integrity": "sha512-kZzYOKspf8XVX5AvmQF94gQW0lejFVgb80G85bU4ZWzoJ6C03PQg3coYAUpSTpQWelrZELd3XWgHzw4Ck5kaIw==" + "node_modules/@types/scheduler": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", + "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==", + "peer": true }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "node_modules/@types/stack-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", + "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==", "dev": true }, - "node_modules/lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" + "node_modules/@types/triple-beam": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.2.tgz", + "integrity": "sha512-txGIh+0eDFzKGC25zORnswy+br1Ha7hj5cMVwKIU7+s0U2AxxJru/jZSMU6OC9MJWP6+pc/hc6ZjyZShpsyY2g==" }, - "node_modules/lodash.escaperegexp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", - "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==" + "node_modules/@types/unist": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", + "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==" }, - "node_modules/lodash.filter": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz", - "integrity": "sha512-pXYUy7PR8BCLwX5mgJ/aNtyOvuJTdZAo9EQFUvMIYugqmJxnrYaANvTbgndOzHSCSR0wnlBBfRXJL5SbWxo3FQ==" + "node_modules/@types/yargs": { + "version": "13.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.12.tgz", + "integrity": "sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } }, - "node_modules/lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==" + "node_modules/@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true }, - "node_modules/lodash.foreach": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", - "integrity": "sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ==" + "node_modules/@webassemblyjs/ast": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.5.tgz", + "integrity": "sha512-LHY/GSAZZRpsNQH+/oHqhRQ5FT7eoULcBqgfyTB5nQHogFnK3/7QoN7dLnwSE/JkUAF0SrRuclT7ODqMFtWxxQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.5", + "@webassemblyjs/helper-wasm-bytecode": "1.11.5" + } }, - "node_modules/lodash.frompairs": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.frompairs/-/lodash.frompairs-4.0.1.tgz", - "integrity": "sha512-dvqe2I+cO5MzXCMhUnfYFa9MD+/760yx2aTAN1lqEcEkf896TxgrX373igVdqSJj6tQd0jnSLE1UMuKufqqxFw==", + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.5.tgz", + "integrity": "sha512-1j1zTIC5EZOtCplMBG/IEwLtUojtwFVwdyVMbL/hwWqbzlQoJsWCOavrdnLkemwNoC/EOwtUFch3fuo+cbcXYQ==", "dev": true }, - "node_modules/lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.5.tgz", + "integrity": "sha512-L65bDPmfpY0+yFrsgz8b6LhXmbbs38OnwDCf6NpnMUYqa+ENfE5Dq9E42ny0qz/PdR0LJyq/T5YijPnU8AXEpA==", "dev": true }, - "node_modules/lodash.ismatch": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", - "integrity": "sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==" - }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" - }, - "node_modules/lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" - }, - "node_modules/lodash.map": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", - "integrity": "sha512-worNHGKLDetmcEYDvh2stPCrrQRkP20E4l0iIS7F8EvzMqBBi7ltvFN5m1HvTf1P7Jk1txKhvFcmYsCr8O2F1Q==" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" - }, - "node_modules/lodash.pick": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", - "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==" - }, - "node_modules/lodash.reduce": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz", - "integrity": "sha512-6raRe2vxCYBhpBu+B+TtNGUzah+hQjVdu3E17wfusjyrXBka2nBS8OH/gjVZ5PvHOhWmIZTYri09Z6n/QfnNMw==" - }, - "node_modules/lodash.reject": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz", - "integrity": "sha512-qkTuvgEzYdyhiJBx42YPzPo71R1aEr0z79kAv7Ixg8wPFEjgRgJdUsGMG3Hf3OYSF/kHI79XhNlt+5Ar6OzwxQ==" - }, - "node_modules/lodash.some": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz", - "integrity": "sha512-j7MJE+TuT51q9ggt4fSgVqro163BEFjAt3u97IqU+JA2DkWl80nFTrowzLpZ/BnpN7rrl0JA/593NAdd8p/scQ==" - }, - "node_modules/lodash.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.5.tgz", + "integrity": "sha512-fDKo1gstwFFSfacIeH5KfwzjykIE6ldh1iH9Y/8YkAZrhmu4TctqYjSh7t0K2VyDSXOZJ1MLhht/k9IvYGcIxg==", "dev": true }, - "node_modules/lodash.topairs": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.topairs/-/lodash.topairs-4.3.0.tgz", - "integrity": "sha512-qrRMbykBSEGdOgQLJJqVSdPWMD7Q+GJJ5jMRfQYb+LTLsw3tYVIabnCzRqTJb2WTo17PG5gNzXuFaZgYH/9SAQ==", + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.5.tgz", + "integrity": "sha512-DhykHXM0ZABqfIGYNv93A5KKDw/+ywBFnuWybZZWcuzWHfbp21wUfRkbtz7dMGwGgT4iXjWuhRMA2Mzod6W4WA==", + "dev": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.5", + "@webassemblyjs/helper-api-error": "1.11.5", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.5.tgz", + "integrity": "sha512-oC4Qa0bNcqnjAowFn7MPCETQgDYytpsfvz4ujZz63Zu/a/v71HeCAAmZsgZ3YVKec3zSPYytG3/PrRCqbtcAvA==", "dev": true }, - "node_modules/lodash.uniqby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", - "integrity": "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==" + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.5.tgz", + "integrity": "sha512-uEoThA1LN2NA+K3B9wDo3yKlBfVtC6rh0i4/6hvbz071E8gTNZD/pT0MsBf7MeD6KbApMSkaAK0XeKyOZC7CIA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.5", + "@webassemblyjs/helper-buffer": "1.11.5", + "@webassemblyjs/helper-wasm-bytecode": "1.11.5", + "@webassemblyjs/wasm-gen": "1.11.5" + } }, - "node_modules/logform": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.5.1.tgz", - "integrity": "sha512-9FyqAm9o9NKKfiAKfZoYo9bGXXuwMkxQiQttkT4YjjVtQVIQtK6LmVtlxmCaFswo6N4AfEkHqZTV0taDtPotNg==", + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.5.tgz", + "integrity": "sha512-37aGq6qVL8A8oPbPrSGMBcp38YZFXcHfiROflJn9jxSdSMMM5dS5P/9e2/TpaJuhE+wFrbukN2WI6Hw9MH5acg==", + "dev": true, "dependencies": { - "@colors/colors": "1.5.0", - "@types/triple-beam": "^1.3.2", - "fecha": "^4.2.0", - "ms": "^2.1.1", - "safe-stable-stringify": "^2.3.1", - "triple-beam": "^1.3.0" + "@xtuc/ieee754": "^1.2.0" } }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.5.tgz", + "integrity": "sha512-ajqrRSXaTJoPW+xmkfYN6l8VIeNnR4vBOTQO9HzR7IygoCcKWkICbKFbVTNMjMgMREqXEr0+2M6zukzM47ZUfQ==", + "dev": true, "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" + "@xtuc/long": "4.2.2" } }, - "node_modules/lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.5.tgz", + "integrity": "sha512-WiOhulHKTZU5UPlRl53gHR8OxdGsSOxqfpqWeA2FmcwBMaoEdz6b2x2si3IwC9/fSPLfe8pBMRTHVMk5nlwnFQ==", + "dev": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.5.tgz", + "integrity": "sha512-C0p9D2fAu3Twwqvygvf42iGCQ4av8MFBLiTb+08SZ4cEdwzWx9QeAHDo1E2k+9s/0w1DM40oflJOpkZ8jW4HCQ==", "dev": true, - "engines": { - "node": ">=0.10.0" + "dependencies": { + "@webassemblyjs/ast": "1.11.5", + "@webassemblyjs/helper-buffer": "1.11.5", + "@webassemblyjs/helper-wasm-bytecode": "1.11.5", + "@webassemblyjs/helper-wasm-section": "1.11.5", + "@webassemblyjs/wasm-gen": "1.11.5", + "@webassemblyjs/wasm-opt": "1.11.5", + "@webassemblyjs/wasm-parser": "1.11.5", + "@webassemblyjs/wast-printer": "1.11.5" } }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.5.tgz", + "integrity": "sha512-14vteRlRjxLK9eSyYFvw1K8Vv+iPdZU0Aebk3j6oB8TQiQYuO6hj9s4d7qf6f2HJr2khzvNldAFG13CgdkAIfA==", "dev": true, "dependencies": { - "yallist": "^3.0.2" + "@webassemblyjs/ast": "1.11.5", + "@webassemblyjs/helper-wasm-bytecode": "1.11.5", + "@webassemblyjs/ieee754": "1.11.5", + "@webassemblyjs/leb128": "1.11.5", + "@webassemblyjs/utf8": "1.11.5" } }, - "node_modules/make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.5.tgz", + "integrity": "sha512-tcKwlIXstBQgbKy1MlbDMlXaxpucn42eb17H29rawYLxm5+MsEmgPzeCP8B1Cl69hCice8LeKgZpRUAPtqYPgw==", "dev": true, "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "engines": { - "node": ">=6" + "@webassemblyjs/ast": "1.11.5", + "@webassemblyjs/helper-buffer": "1.11.5", + "@webassemblyjs/wasm-gen": "1.11.5", + "@webassemblyjs/wasm-parser": "1.11.5" } }, - "node_modules/make-dir/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.5.tgz", + "integrity": "sha512-SVXUIwsLQlc8srSD7jejsfTU83g7pIGr2YYNb9oHdtldSxaOhvA5xwvIiWIfcX8PlSakgqMXsLpLfbbJ4cBYew==", "dev": true, - "bin": { - "semver": "bin/semver" + "dependencies": { + "@webassemblyjs/ast": "1.11.5", + "@webassemblyjs/helper-api-error": "1.11.5", + "@webassemblyjs/helper-wasm-bytecode": "1.11.5", + "@webassemblyjs/ieee754": "1.11.5", + "@webassemblyjs/leb128": "1.11.5", + "@webassemblyjs/utf8": "1.11.5" } }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.5.tgz", + "integrity": "sha512-f7Pq3wvg3GSPUPzR0F6bmI89Hdb+u9WXrSKc4v+N0aV0q6r42WoF92Jp2jEorBEBRoRNXgjp53nBniDXcqZYPA==", "dev": true, "dependencies": { - "tmpl": "1.0.5" + "@webassemblyjs/ast": "1.11.5", + "@xtuc/long": "4.2.2" } }, - "node_modules/map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", + "node_modules/@webpack-cli/configtest": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz", + "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==", "dev": true, - "engines": { - "node": ">=0.10.0" + "peerDependencies": { + "webpack": "4.x.x || 5.x.x", + "webpack-cli": "4.x.x" } }, - "node_modules/map-obj": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", - "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", - "engines": { - "node": ">=8" + "node_modules/@webpack-cli/info": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz", + "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==", + "dev": true, + "dependencies": { + "envinfo": "^7.7.3" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "webpack-cli": "4.x.x" } }, - "node_modules/map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==", + "node_modules/@webpack-cli/serve": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz", + "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==", "dev": true, - "dependencies": { - "object-visit": "^1.0.0" + "peerDependencies": { + "webpack-cli": "4.x.x" }, - "engines": { - "node": ">=0.10.0" + "peerDependenciesMeta": { + "webpack-dev-server": { + "optional": true + } } }, - "node_modules/marked": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/marked/-/marked-5.1.2.tgz", - "integrity": "sha512-ahRPGXJpjMjwSOlBoTMZAK7ATXkli5qCPxZ21TG44rx1KEo44bii4ekgTDQPNRQ4Kh7JMb9Ub1PVk1NxRSsorg==", - "peer": true, - "bin": { - "marked": "bin/marked.js" + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "node_modules/@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==" + }, + "node_modules/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "dev": true + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" }, "engines": { - "node": ">= 16" + "node": ">= 0.6" } }, - "node_modules/marked-terminal": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-5.2.0.tgz", - "integrity": "sha512-Piv6yNwAQXGFjZSaiNljyNFw7jKDdGrw70FSbtxEyldLsyeuV5ZHm/1wW++kWbrOF1VPnUgYOhB2oLL0ZpnekA==", - "peer": true, - "dependencies": { - "ansi-escapes": "^6.2.0", - "cardinal": "^2.1.1", - "chalk": "^5.2.0", - "cli-table3": "^0.6.3", - "node-emoji": "^1.11.0", - "supports-hyperlinks": "^2.3.0" + "node_modules/acorn": { + "version": "5.7.4", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", + "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" }, "engines": { - "node": ">=14.13.1 || >=16.0.0" - }, - "peerDependencies": { - "marked": "^1.0.0 || ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0" + "node": ">=0.4.0" } }, - "node_modules/marked-terminal/node_modules/ansi-escapes": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.0.tgz", - "integrity": "sha512-kzRaCqXnpzWs+3z5ABPQiVke+iq0KXkHo8xiWV4RPTi5Yli0l97BEQuhXV1s7+aSU/fu1kUuxgS4MsQ0fRuygw==", - "peer": true, + "node_modules/acorn-globals": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.4.tgz", + "integrity": "sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==", + "dev": true, "dependencies": { - "type-fest": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "acorn": "^6.0.1", + "acorn-walk": "^6.0.1" } }, - "node_modules/marked-terminal/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "peer": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" + "node_modules/acorn-globals/node_modules/acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "dev": true, + "bin": { + "acorn": "bin/acorn" }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "engines": { + "node": ">=0.4.0" } }, - "node_modules/marked-terminal/node_modules/type-fest": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", - "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", - "peer": true, + "node_modules/acorn-walk": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz", + "integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==", + "dev": true, "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.4.0" } }, - "node_modules/math-random": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz", - "integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==", - "dev": true - }, - "node_modules/mdast-util-definitions": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-4.0.0.tgz", - "integrity": "sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ==", + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dependencies": { - "unist-util-visit": "^2.0.0" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/mdast-util-from-markdown": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-0.8.5.tgz", - "integrity": "sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==", - "dependencies": { - "@types/mdast": "^3.0.0", - "mdast-util-to-string": "^2.0.0", - "micromark": "~2.11.0", - "parse-entities": "^2.0.0", - "unist-util-stringify-position": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" } }, - "node_modules/mdast-util-to-hast": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-10.2.0.tgz", - "integrity": "sha512-JoPBfJ3gBnHZ18icCwHR50orC9kNH81tiR1gs01D8Q5YpV6adHNO9nKNuFBCJQ941/32PT1a63UF/DitmS3amQ==", + "node_modules/ansi-align": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", + "integrity": "sha512-TdlOggdA/zURfMYa7ABC66j+oqfMew58KpJMbUlH3bcZP1b+cBHIHDDn5uH9INsxrHBPjsqM0tDB4jPTF/vgJA==", + "dev": true, "dependencies": { - "@types/mdast": "^3.0.0", - "@types/unist": "^2.0.0", - "mdast-util-definitions": "^4.0.0", - "mdurl": "^1.0.0", - "unist-builder": "^2.0.0", - "unist-util-generated": "^1.0.0", - "unist-util-position": "^3.0.0", - "unist-util-visit": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "string-width": "^2.0.0" } }, - "node_modules/mdast-util-to-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz", - "integrity": "sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "node_modules/ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true, + "engines": { + "node": ">=4" } }, - "node_modules/mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==" - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, "engines": { - "node": ">= 0.6" + "node": ">=6" } }, - "node_modules/meow": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", - "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dependencies": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.18.0", - "yargs-parser": "^20.2.3" + "color-convert": "^1.9.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4" } }, - "node_modules/meow/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "optional": true, "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" }, "engines": { - "node": ">=8" + "node": ">= 8" } }, - "node_modules/meow/node_modules/hosted-git-info": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", - "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", - "dependencies": { - "lru-cache": "^6.0.0" - }, + "node_modules/arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", + "dev": true, "engines": { - "node": ">=10" + "node": ">=0.10.0" } }, - "node_modules/meow/node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" - }, - "node_modules/meow/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dependencies": { - "p-locate": "^4.1.0" - }, + "node_modules/arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/meow/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, + "node_modules/arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", + "dev": true, "engines": { - "node": ">=10" + "node": ">=0.10.0" } }, - "node_modules/meow/node_modules/normalize-package-data": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", - "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "node_modules/array-buffer-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", + "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "dev": true, "dependencies": { - "hosted-git-info": "^4.0.1", - "is-core-module": "^2.5.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" }, - "engines": { - "node": ">=10" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/meow/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dependencies": { - "p-limit": "^2.2.0" - }, + "node_modules/array-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", + "integrity": "sha512-H3LU5RLiSsGXPhN+Nipar0iR0IofH+8r89G2y1tBKxQ/agagKyAjhkAFDRBfodP2caPrNKHpAWNIM/c9yeL7uA==", + "dev": true + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==", + "dev": true, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/meow/node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "node_modules/array.prototype.reduce": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.5.tgz", + "integrity": "sha512-kDdugMl7id9COE8R7MHF5jWk7Dqt/fs4Pv+JXoICnYwqpjjjbUurz6w5fT5IG6brLdJhv6/VoHB0H7oyIBXd+Q==", + "dev": true, "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-array-method-boxes-properly": "^1.0.0", + "is-string": "^1.0.7" }, "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/meow/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "engines": { - "node": ">=8" + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dependencies": { + "safer-buffer": "~2.1.0" } }, - "node_modules/meow/node_modules/read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", "engines": { - "node": ">=8" + "node": ">=0.8" } }, - "node_modules/meow/node_modules/read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dependencies": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, + "node_modules/assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==", + "dev": true, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/meow/node_modules/read-pkg-up/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "node_modules/astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/meow/node_modules/read-pkg/node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" + "node_modules/async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" }, - "node_modules/meow/node_modules/read-pkg/node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } + "node_modules/async-each": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.6.tgz", + "integrity": "sha512-c646jH1avxr+aVpndVMeAfYw7wAa6idufrlN3LPA4PmKS0QEGp6PIC9nwz0WQkkvBGAMEki3pFdtxaF39J9vvg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] }, - "node_modules/meow/node_modules/read-pkg/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "bin": { - "semver": "bin/semver" - } + "node_modules/async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", + "dev": true }, - "node_modules/meow/node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", "engines": { - "node": ">=8" + "node": ">= 4.0.0" } }, - "node_modules/meow/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dependencies": { - "lru-cache": "^6.0.0" - }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true, "bin": { - "semver": "bin/semver.js" + "atob": "bin/atob.js" }, "engines": { - "node": ">=10" + "node": ">= 4.5.0" } }, - "node_modules/meow/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/meow/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true, "engines": { - "node": ">=10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", "engines": { - "node": ">= 8" + "node": "*" } }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "engines": { - "node": ">= 0.6" - } + "node_modules/aws4": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", + "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==" }, - "node_modules/micromark": { - "version": "2.11.4", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-2.11.4.tgz", - "integrity": "sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], + "node_modules/babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha512-XqYMR2dfdGMW+hd0IUZ2PwK+fGeFkOxZJ0wY+JaQAHzt1Zx8LcvpiZD2NiGkEG8qx0CfkAOr5xt76d1e8vG90g==", + "dev": true, + "peer": true, "dependencies": { - "debug": "^4.0.0", - "parse-entities": "^2.0.0" + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" } }, - "node_modules/micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "node_modules/babel-code-frame/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", "dev": true, - "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - }, + "peer": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/micromatch/node_modules/braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "node_modules/babel-code-frame/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", "dev": true, - "dependencies": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, + "peer": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/micromatch/node_modules/braces/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "node_modules/babel-code-frame/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", "dev": true, + "peer": true, "dependencies": { - "is-extendable": "^0.1.0" + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/micromatch/node_modules/fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "node_modules/babel-code-frame/node_modules/js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==", + "dev": true, + "peer": true + }, + "node_modules/babel-code-frame/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", "dev": true, + "peer": true, "dependencies": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" + "ansi-regex": "^2.0.0" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/micromatch/node_modules/fill-range/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "node_modules/babel-code-frame/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, + "peer": true, "engines": { - "node": ">=0.10.0" + "node": ">=0.8.0" } }, - "node_modules/micromatch/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "node_modules/babel-core": { + "version": "6.26.3", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", + "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", "dev": true, - "engines": { - "node": ">=0.10.0" + "peer": true, + "dependencies": { + "babel-code-frame": "^6.26.0", + "babel-generator": "^6.26.0", + "babel-helpers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-register": "^6.26.0", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "convert-source-map": "^1.5.1", + "debug": "^2.6.9", + "json5": "^0.5.1", + "lodash": "^4.17.4", + "minimatch": "^3.0.4", + "path-is-absolute": "^1.0.1", + "private": "^0.1.8", + "slash": "^1.0.0", + "source-map": "^0.5.7" } }, - "node_modules/micromatch/node_modules/to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "node_modules/babel-core/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, + "peer": true, "dependencies": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - }, + "ms": "2.0.0" + } + }, + "node_modules/babel-core/node_modules/json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha512-4xrs1aW+6N5DalkqSVA8fxh458CXvR99WU8WLKmq4v8eWAL86Xo3BVqyd3SkA9wEVjCMqyvvRRkshAdOnBp5rw==", + "dev": true, + "peer": true, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/babel-core/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "peer": true + }, + "node_modules/babel-core/node_modules/slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha512-3TYDR7xWt4dIqV2JauJr+EJeW356RXijHeUlO+8djJ+uBXPn8/2dpzBc8yQhh583sVvc9CvFAeQVgijsH+PNNg==", + "dev": true, + "peer": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "node_modules/babel-generator": { + "version": "6.26.1", + "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", + "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", + "dev": true, + "peer": true, + "dependencies": { + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "detect-indent": "^4.0.0", + "jsesc": "^1.3.0", + "lodash": "^4.17.4", + "source-map": "^0.5.7", + "trim-right": "^1.0.1" + } + }, + "node_modules/babel-generator/node_modules/jsesc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", + "integrity": "sha512-Mke0DA0QjUWuJlhsE0ZPPhYiJkRap642SmI/4ztCFaUs6V2AiH1sfecc+57NgaryfAA2VR3v6O+CSjC1jZJKOA==", + "dev": true, + "peer": true, "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" + "jsesc": "bin/jsesc" } }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "node_modules/babel-helpers": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", + "integrity": "sha512-n7pFrqQm44TCYvrCDb0MqabAF+JUBq+ijBvNMUxpkLjJaAu32faIexewMumrH5KLLJ1HDyT0PTEqRyAe/GwwuQ==", + "dev": true, + "peer": true, + "dependencies": { + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "node_modules/babel-jest": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.9.0.tgz", + "integrity": "sha512-ntuddfyiN+EhMw58PTNL1ph4C9rECiQXjI4nMMBKBaNjXvqLdkXpPRcMSr4iyBrJg/+wz9brFUD6RhOAT6r4Iw==", + "dev": true, + "dependencies": { + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/babel__core": "^7.1.0", + "babel-plugin-istanbul": "^5.1.0", + "babel-preset-jest": "^24.9.0", + "chalk": "^2.4.2", + "slash": "^2.0.0" + }, "engines": { - "node": ">= 0.6" + "node": ">= 6" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "node_modules/babel-loader": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.3.0.tgz", + "integrity": "sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q==", + "dev": true, "dependencies": { - "mime-db": "1.52.0" + "find-cache-dir": "^3.3.1", + "loader-utils": "^2.0.0", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" }, "engines": { - "node": ">= 0.6" + "node": ">= 8.9" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "webpack": ">=2" } }, - "node_modules/mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "peer": true, + "node_modules/babel-loader/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, "engines": { - "node": ">=12" + "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "engines": { - "node": ">=4" + "node_modules/babel-messages": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "integrity": "sha512-Bl3ZiA+LjqaMtNYopA9TYE9HP1tQ+E5dLxE0XrAzcIJeK2UqF0/EaqXwBn9esd4UmTfEab+P+UYQ1GnioFIb/w==", + "dev": true, + "peer": true, + "dependencies": { + "babel-runtime": "^6.22.0" } }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/babel-plugin-istanbul": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz", + "integrity": "sha512-5LphC0USA8t4i1zCtjbbNb6jJj/9+X6P37Qfirc/70EQ34xKlMW+a1RHGwxGI+SwWpNwZ27HqvzAobeqaXwiZw==", + "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "@babel/helper-plugin-utils": "^7.0.0", + "find-up": "^3.0.0", + "istanbul-lib-instrument": "^3.3.0", + "test-exclude": "^5.2.3" }, "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=6" } }, - "node_modules/minimist-options": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", - "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "node_modules/babel-plugin-jest-hoist": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.9.0.tgz", + "integrity": "sha512-2EMA2P8Vp7lG0RAzr4HXqtYwacfMErOuv1U3wrvxHX6rD1sV6xS3WXG3r8TRQ2r6w8OhvSdWt+z41hQNwNm3Xw==", + "dev": true, "dependencies": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0", - "kind-of": "^6.0.3" + "@types/babel__traverse": "^7.0.6" }, "engines": { "node": ">= 6" } }, - "node_modules/minimist-options/node_modules/is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", + "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", "dev": true, "dependencies": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" + "@babel/compat-data": "^7.17.7", + "@babel/helper-define-polyfill-provider": "^0.3.3", + "semver": "^6.1.1" }, - "engines": { - "node": ">=0.10.0" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz", + "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==", + "dev": true, "dependencies": { - "minimist": "^1.2.6" + "@babel/helper-define-polyfill-provider": "^0.3.3", + "core-js-compat": "^3.25.1" }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/modify-values": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", - "integrity": "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==", - "engines": { - "node": ">=0.10.0" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/nan": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", - "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==", + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", + "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", "dev": true, - "optional": true + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.3.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "node_modules/nanoid": { - "version": "2.1.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-2.1.11.tgz", - "integrity": "sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA==" + "node_modules/babel-plugin-transform-remove-console": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-remove-console/-/babel-plugin-transform-remove-console-6.9.4.tgz", + "integrity": "sha512-88blrUrMX3SPiGkT1GnvVY8E/7A+k6oj3MNvUtTIxJflFzXTw1bHkuJ/y039ouhFMp2prRn5cQGzokViYi1dsg==", + "dev": true }, - "node_modules/nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "node_modules/babel-preset-jest": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-24.9.0.tgz", + "integrity": "sha512-izTUuhE4TMfTRPF92fFwD2QfdXaZW08qvWTFCI51V8rW5x00UuPgc3ajRoWofXOuxjfcOM5zzSYsQS3H8KGCAg==", "dev": true, "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "@babel/plugin-syntax-object-rest-spread": "^7.0.0", + "babel-plugin-jest-hoist": "^24.9.0" }, "engines": { - "node": ">=0.10.0" + "node": ">= 6" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "engines": { - "node": ">= 0.6" + "node_modules/babel-register": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", + "integrity": "sha512-veliHlHX06wjaeY8xNITbveXSiI+ASFnOqvne/LaIJIqOWi2Ogmj91KOugEz/hoh/fwMhXNBJPCv8Xaz5CyM4A==", + "dev": true, + "peer": true, + "dependencies": { + "babel-core": "^6.26.0", + "babel-runtime": "^6.26.0", + "core-js": "^2.5.0", + "home-or-tmp": "^2.0.0", + "lodash": "^4.17.4", + "mkdirp": "^0.5.1", + "source-map-support": "^0.4.15" } }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + "node_modules/babel-register/node_modules/core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", + "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", + "dev": true, + "hasInstallScript": true, + "peer": true }, - "node_modules/nerf-dart": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/nerf-dart/-/nerf-dart-1.0.0.tgz", - "integrity": "sha512-EZSPZB70jiVsivaBLYDCyntd5eH8NTSMOn3rB+HxwdmKThGELLdYv8qVIMWvZEFy9w8ZZpW9h9OB32l1rGtj7g==" + "node_modules/babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==", + "dev": true, + "peer": true, + "dependencies": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + } }, - "node_modules/nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + "node_modules/babel-runtime/node_modules/core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", + "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", + "dev": true, + "hasInstallScript": true, + "peer": true + }, + "node_modules/babel-runtime/node_modules/regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", + "dev": true, + "peer": true }, - "node_modules/node-emoji": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", - "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", + "node_modules/babel-template": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", + "integrity": "sha512-PCOcLFW7/eazGUKIoqH97sO9A2UYMahsn/yRQ7uOk37iutwjq7ODtcTNF+iFDSHNfkctqsLRjLP7URnOx0T1fg==", + "dev": true, "peer": true, "dependencies": { - "lodash": "^4.17.21" + "babel-runtime": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "lodash": "^4.17.4" } }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "node_modules/babel-traverse": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "integrity": "sha512-iSxeXx7apsjCHe9c7n8VtRXGzI2Bk1rBSOJgCCjfyXb6v1aCqE1KSEpq/8SXuVN8Ka/Rh1WDTF0MDzkvTA4MIA==", + "dev": true, + "peer": true, "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } + "babel-code-frame": "^6.26.0", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "debug": "^2.6.8", + "globals": "^9.18.0", + "invariant": "^2.2.2", + "lodash": "^4.17.4" + } + }, + "node_modules/babel-traverse/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "peer": true, + "dependencies": { + "ms": "2.0.0" } }, - "node_modules/node-fetch/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + "node_modules/babel-traverse/node_modules/globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/node-fetch/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + "node_modules/babel-traverse/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "peer": true }, - "node_modules/node-fetch/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "node_modules/babel-types": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha512-zhe3V/26rCWsEZK8kZN+HaQj5yQ1CilTObixFzKW1UWjqG7618Twz6YEsCnjfg5gBcJh02DrpCkS9h98ZqDY+g==", + "dev": true, + "peer": true, "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" } }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true - }, - "node_modules/node-notifier": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.5.tgz", - "integrity": "sha512-tVbHs7DyTLtzOiN78izLA85zRqB9NvEXkAf014Vx3jtSvn/xBl6bR8ZYifj+dFcFrKI21huSQgJZ6ZtL3B4HfQ==", + "node_modules/babel-types/node_modules/to-fast-properties": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha512-lxrWP8ejsq+7E3nNjwYmUBMAgjMTZoTI+sdBOpvNyijeDLa29LUn9QaoXAHv4+Z578hbmHHJKZknzxVtvo77og==", "dev": true, - "dependencies": { - "growly": "^1.3.0", - "is-wsl": "^1.1.0", - "semver": "^5.5.0", - "shellwords": "^0.1.1", - "which": "^1.3.0" + "peer": true, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/node-notifier/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "node_modules/babel-watch": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/babel-watch/-/babel-watch-2.0.8.tgz", + "integrity": "sha512-dmuUumB5Mv0SOfDuQvJ2Dmx/4EYi76spzf/8wpH7D1DnL+3GEm4uIqY+/N+4CetAh0HLSZVl894bDD9pDdfzKw==", "dev": true, + "dependencies": { + "chokidar": "^1.4.3", + "commander": "^2.9.0", + "lodash.debounce": "^4.0.8", + "source-map-support": "^0.4.0" + }, "bin": { - "semver": "bin/semver" + "babel-watch": "babel-watch.js" + }, + "peerDependencies": { + "babel-core": "6.5.1 - 6.6.0 || ^6.6.x" } }, - "node_modules/node-releases": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", - "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", - "dev": true + "node_modules/babel-watch/node_modules/anymatch": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", + "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", + "dev": true, + "dependencies": { + "micromatch": "^2.1.5", + "normalize-path": "^2.0.0" + } }, - "node_modules/nodemon": { - "version": "1.19.4", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-1.19.4.tgz", - "integrity": "sha512-VGPaqQBNk193lrJFotBU8nvWZPqEZY2eIzymy2jjY0fJ9qIsxA0sxQ8ATPl0gZC645gijYEc1jtZvpS8QWzJGQ==", + "node_modules/babel-watch/node_modules/anymatch/node_modules/micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha512-LnU2XFEk9xxSJ6rfgAry/ty5qwUTyHYOBU0g4R6tIw5ljwgGIBmiKhRWLw5NpMOnrgUNcDJ4WMp8rl3sYVHLNA==", "dev": true, - "hasInstallScript": true, "dependencies": { - "chokidar": "^2.1.8", - "debug": "^3.2.6", - "ignore-by-default": "^1.0.1", - "minimatch": "^3.0.4", - "pstree.remy": "^1.1.7", - "semver": "^5.7.1", - "supports-color": "^5.5.0", - "touch": "^3.1.0", - "undefsafe": "^2.0.2", - "update-notifier": "^2.5.0" - }, - "bin": { - "nodemon": "bin/nodemon.js" + "arr-diff": "^2.0.0", + "array-unique": "^0.2.1", + "braces": "^1.8.2", + "expand-brackets": "^0.1.4", + "extglob": "^0.3.1", + "filename-regex": "^2.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.1", + "kind-of": "^3.0.2", + "normalize-path": "^2.0.1", + "object.omit": "^2.0.0", + "parse-glob": "^3.0.4", + "regex-cache": "^0.4.2" }, "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/nodemon/node_modules/anymatch": { + "node_modules/babel-watch/node_modules/arr-diff": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha512-dtXTVMkh6VkEEA7OhXnN1Ecb8aAGFdZ1LFxtOCoqj4qkyOJMt7+qs6Ahdy6p/NQCPYsRSXXivhSB/J5E9jmYKA==", "dev": true, "dependencies": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" + "arr-flatten": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/nodemon/node_modules/anymatch/node_modules/normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "node_modules/babel-watch/node_modules/array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha512-G2n5bG5fSUCpnsXz4+8FUkYsGPkNfLn9YvS66U5qbTIXI2Ynnlo4Bi42bWv+omKUCqz+ejzfClwne0alJWJPhg==", "dev": true, - "dependencies": { - "remove-trailing-separator": "^1.0.1" - }, "engines": { "node": ">=0.10.0" } }, - "node_modules/nodemon/node_modules/binary-extensions": { + "node_modules/babel-watch/node_modules/binary-extensions": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", @@ -11871,149 +3543,156 @@ "node": ">=0.10.0" } }, - "node_modules/nodemon/node_modules/braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "node_modules/babel-watch/node_modules/braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha512-xU7bpz2ytJl1bH9cgIurjpg/n8Gohy9GTw81heDYLJQ4RU60dlyJsa+atVF2pI0yMMvKxI9HkKwjePCj5XI1hw==", "dev": true, "dependencies": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" + "expand-range": "^1.8.1", + "preserve": "^0.2.0", + "repeat-element": "^1.1.2" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/nodemon/node_modules/chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", - "deprecated": "Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies", + "node_modules/babel-watch/node_modules/chokidar": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", + "integrity": "sha512-mk8fAWcRUOxY7btlLtitj3A45jOwSAxH4tOFOoEGbVsl6cL6pPMWUy7dwZ/canfj3QEdP6FHSnf/l1c6/WkzVg==", + "deprecated": "Chokidar 2 will break on node v14+. Upgrade to chokidar 3 with 15x less dependencies.", "dev": true, "dependencies": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", + "anymatch": "^1.3.0", + "async-each": "^1.0.0", + "glob-parent": "^2.0.0", + "inherits": "^2.0.1", "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", + "is-glob": "^2.0.0", "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" + "readdirp": "^2.0.0" }, "optionalDependencies": { - "fsevents": "^1.2.7" + "fsevents": "^1.0.0" } }, - "node_modules/nodemon/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } + "node_modules/babel-watch/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true }, - "node_modules/nodemon/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "node_modules/babel-watch/node_modules/expand-brackets": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha512-hxx03P2dJxss6ceIeri9cmYOT4SRs3Zk3afZwWpOsRqLqprhTR8u++SlC+sFGsQr7WGFPdMF7Gjc1njDLDK6UA==", "dev": true, "dependencies": { - "is-extendable": "^0.1.0" + "is-posix-bracket": "^0.1.0" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/nodemon/node_modules/fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "node_modules/babel-watch/node_modules/extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha512-1FOj1LOwn42TMrruOHGt18HemVnbwAmAak7krWk+wa93KXxGbK+2jpezm+ytJYDaBX0/SPLZFHKM7m+tKobWGg==", "dev": true, "dependencies": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" + "is-extglob": "^1.0.0" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/nodemon/node_modules/fsevents": { + "node_modules/babel-watch/node_modules/fsevents": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", "deprecated": "fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.", "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/babel-watch/node_modules/glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha512-JDYOvfxio/t42HKdxkAYaCiBN7oYiuxykOxKxdaUW5Qn0zaYN3gRQWolrwdnf0shM9/EP0ebuuTmyoXNr1cC5w==", + "dev": true, + "dependencies": { + "is-glob": "^2.0.0" + } + }, + "node_modules/babel-watch/node_modules/is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==", + "dev": true, "dependencies": { - "bindings": "^1.5.0", - "nan": "^2.12.1" + "binary-extensions": "^1.0.0" }, "engines": { - "node": ">= 4.0" + "node": ">=0.10.0" } }, - "node_modules/nodemon/node_modules/glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", + "node_modules/babel-watch/node_modules/is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww==", "dev": true, - "dependencies": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/nodemon/node_modules/glob-parent/node_modules/is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", + "node_modules/babel-watch/node_modules/is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg==", "dev": true, "dependencies": { - "is-extglob": "^2.1.0" + "is-extglob": "^1.0.0" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/nodemon/node_modules/is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==", + "node_modules/babel-watch/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", "dev": true, "dependencies": { - "binary-extensions": "^1.0.0" + "is-buffer": "^1.1.5" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/nodemon/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "node_modules/babel-watch/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", "dev": true, + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, "engines": { "node": ">=0.10.0" } }, - "node_modules/nodemon/node_modules/readable-stream": { + "node_modules/babel-watch/node_modules/readable-stream": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", @@ -12028,7 +3707,7 @@ "util-deprecate": "~1.0.1" } }, - "node_modules/nodemon/node_modules/readdirp": { + "node_modules/babel-watch/node_modules/readdirp": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", @@ -12042,22 +3721,13 @@ "node": ">=0.10" } }, - "node_modules/nodemon/node_modules/safe-buffer": { + "node_modules/babel-watch/node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true }, - "node_modules/nodemon/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/nodemon/node_modules/string_decoder": { + "node_modules/babel-watch/node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", @@ -12066,8025 +3736,6613 @@ "safe-buffer": "~5.1.0" } }, - "node_modules/nodemon/node_modules/to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "node_modules/babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", "dev": true, - "dependencies": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/nopt": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", - "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "*" - } - }, - "node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/normalize-package-data/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "peer": true, "bin": { - "semver": "bin/semver" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" + "babylon": "bin/babylon.js" } }, - "node_modules/normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", - "engines": { - "node": ">=10" - }, + "node_modules/bail": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz", + "integrity": "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==", "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm": { - "version": "8.19.4", - "resolved": "https://registry.npmjs.org/npm/-/npm-8.19.4.tgz", - "integrity": "sha512-3HANl8i9DKnUA89P4KEgVNN28EjSeDCmvEqbzOAuxCFDzdBZzjUl99zgnGpOUumvW5lvJo2HKcjrsc+tfyv1Hw==", - "bundleDependencies": [ - "@isaacs/string-locale-compare", - "@npmcli/arborist", - "@npmcli/ci-detect", - "@npmcli/config", - "@npmcli/fs", - "@npmcli/map-workspaces", - "@npmcli/package-json", - "@npmcli/run-script", - "abbrev", - "archy", - "cacache", - "chalk", - "chownr", - "cli-columns", - "cli-table3", - "columnify", - "fastest-levenshtein", - "fs-minipass", - "glob", - "graceful-fs", - "hosted-git-info", - "ini", - "init-package-json", - "is-cidr", - "json-parse-even-better-errors", - "libnpmaccess", - "libnpmdiff", - "libnpmexec", - "libnpmfund", - "libnpmhook", - "libnpmorg", - "libnpmpack", - "libnpmpublish", - "libnpmsearch", - "libnpmteam", - "libnpmversion", - "make-fetch-happen", - "minimatch", - "minipass", - "minipass-pipeline", - "mkdirp", - "mkdirp-infer-owner", - "ms", - "node-gyp", - "nopt", - "npm-audit-report", - "npm-install-checks", - "npm-package-arg", - "npm-pick-manifest", - "npm-profile", - "npm-registry-fetch", - "npm-user-validate", - "npmlog", - "opener", - "p-map", - "pacote", - "parse-conflict-json", - "proc-log", - "qrcode-terminal", - "read", - "read-package-json", - "read-package-json-fast", - "readdir-scoped-modules", - "rimraf", - "semver", - "ssri", - "tar", - "text-table", - "tiny-relative-date", - "treeverse", - "validate-npm-package-name", - "which", - "write-file-atomic" - ], - "dependencies": { - "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/arborist": "^5.6.3", - "@npmcli/ci-detect": "^2.0.0", - "@npmcli/config": "^4.2.1", - "@npmcli/fs": "^2.1.0", - "@npmcli/map-workspaces": "^2.0.3", - "@npmcli/package-json": "^2.0.0", - "@npmcli/run-script": "^4.2.1", - "abbrev": "~1.1.1", - "archy": "~1.0.0", - "cacache": "^16.1.3", - "chalk": "^4.1.2", - "chownr": "^2.0.0", - "cli-columns": "^4.0.0", - "cli-table3": "^0.6.2", - "columnify": "^1.6.0", - "fastest-levenshtein": "^1.0.12", - "fs-minipass": "^2.1.0", - "glob": "^8.0.1", - "graceful-fs": "^4.2.10", - "hosted-git-info": "^5.2.1", - "ini": "^3.0.1", - "init-package-json": "^3.0.2", - "is-cidr": "^4.0.2", - "json-parse-even-better-errors": "^2.3.1", - "libnpmaccess": "^6.0.4", - "libnpmdiff": "^4.0.5", - "libnpmexec": "^4.0.14", - "libnpmfund": "^3.0.5", - "libnpmhook": "^8.0.4", - "libnpmorg": "^4.0.4", - "libnpmpack": "^4.1.3", - "libnpmpublish": "^6.0.5", - "libnpmsearch": "^5.0.4", - "libnpmteam": "^4.0.4", - "libnpmversion": "^3.0.7", - "make-fetch-happen": "^10.2.0", - "minimatch": "^5.1.0", - "minipass": "^3.1.6", - "minipass-pipeline": "^1.2.4", - "mkdirp": "^1.0.4", - "mkdirp-infer-owner": "^2.0.0", - "ms": "^2.1.2", - "node-gyp": "^9.1.0", - "nopt": "^6.0.0", - "npm-audit-report": "^3.0.0", - "npm-install-checks": "^5.0.0", - "npm-package-arg": "^9.1.0", - "npm-pick-manifest": "^7.0.2", - "npm-profile": "^6.2.0", - "npm-registry-fetch": "^13.3.1", - "npm-user-validate": "^1.0.1", - "npmlog": "^6.0.2", - "opener": "^1.5.2", - "p-map": "^4.0.0", - "pacote": "^13.6.2", - "parse-conflict-json": "^2.0.2", - "proc-log": "^2.0.1", - "qrcode-terminal": "^0.12.0", - "read": "~1.0.7", - "read-package-json": "^5.0.2", - "read-package-json-fast": "^2.0.3", - "readdir-scoped-modules": "^1.1.0", - "rimraf": "^3.0.2", - "semver": "^7.3.7", - "ssri": "^9.0.1", - "tar": "^6.1.11", - "text-table": "~0.2.0", - "tiny-relative-date": "^1.3.0", - "treeverse": "^2.0.0", - "validate-npm-package-name": "^4.0.0", - "which": "^2.0.2", - "write-file-atomic": "^4.0.1" - }, - "bin": { - "npm": "bin/npm-cli.js", - "npx": "bin/npx-cli.js" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", - "dev": true, - "dependencies": { - "path-key": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/npm/node_modules/@colors/colors": { - "version": "1.5.0", - "inBundle": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/npm/node_modules/@gar/promisify": { - "version": "1.1.3", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/@isaacs/string-locale-compare": { - "version": "1.1.0", - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/@npmcli/arborist": { - "version": "5.6.3", - "inBundle": true, - "license": "ISC", - "dependencies": { - "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/installed-package-contents": "^1.0.7", - "@npmcli/map-workspaces": "^2.0.3", - "@npmcli/metavuln-calculator": "^3.0.1", - "@npmcli/move-file": "^2.0.0", - "@npmcli/name-from-folder": "^1.0.1", - "@npmcli/node-gyp": "^2.0.0", - "@npmcli/package-json": "^2.0.0", - "@npmcli/query": "^1.2.0", - "@npmcli/run-script": "^4.1.3", - "bin-links": "^3.0.3", - "cacache": "^16.1.3", - "common-ancestor-path": "^1.0.1", - "hosted-git-info": "^5.2.1", - "json-parse-even-better-errors": "^2.3.1", - "json-stringify-nice": "^1.1.4", - "minimatch": "^5.1.0", - "mkdirp": "^1.0.4", - "mkdirp-infer-owner": "^2.0.0", - "nopt": "^6.0.0", - "npm-install-checks": "^5.0.0", - "npm-package-arg": "^9.0.0", - "npm-pick-manifest": "^7.0.2", - "npm-registry-fetch": "^13.0.0", - "npmlog": "^6.0.2", - "pacote": "^13.6.1", - "parse-conflict-json": "^2.0.1", - "proc-log": "^2.0.0", - "promise-all-reject-late": "^1.0.0", - "promise-call-limit": "^1.0.1", - "read-package-json-fast": "^2.0.2", - "readdir-scoped-modules": "^1.1.0", - "rimraf": "^3.0.2", - "semver": "^7.3.7", - "ssri": "^9.0.0", - "treeverse": "^2.0.0", - "walk-up-path": "^1.0.0" - }, - "bin": { - "arborist": "bin/index.js" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/@npmcli/ci-detect": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16" - } - }, - "node_modules/npm/node_modules/@npmcli/config": { - "version": "4.2.2", - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/map-workspaces": "^2.0.2", - "ini": "^3.0.0", - "mkdirp-infer-owner": "^2.0.0", - "nopt": "^6.0.0", - "proc-log": "^2.0.0", - "read-package-json-fast": "^2.0.3", - "semver": "^7.3.5", - "walk-up-path": "^1.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/@npmcli/disparity-colors": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "ansi-styles": "^4.3.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/@npmcli/fs": { - "version": "2.1.2", - "inBundle": true, - "license": "ISC", - "dependencies": { - "@gar/promisify": "^1.1.3", - "semver": "^7.3.5" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/@npmcli/git": { - "version": "3.0.2", - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/promise-spawn": "^3.0.0", - "lru-cache": "^7.4.4", - "mkdirp": "^1.0.4", - "npm-pick-manifest": "^7.0.0", - "proc-log": "^2.0.0", - "promise-inflight": "^1.0.1", - "promise-retry": "^2.0.1", - "semver": "^7.3.5", - "which": "^2.0.2" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/@npmcli/installed-package-contents": { - "version": "1.0.7", - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-bundled": "^1.1.1", - "npm-normalize-package-bin": "^1.0.1" - }, - "bin": { - "installed-package-contents": "index.js" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/npm/node_modules/@npmcli/installed-package-contents/node_modules/npm-bundled": { - "version": "1.1.2", - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-normalize-package-bin": "^1.0.1" - } - }, - "node_modules/npm/node_modules/@npmcli/map-workspaces": { - "version": "2.0.4", - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/name-from-folder": "^1.0.1", - "glob": "^8.0.1", - "minimatch": "^5.0.1", - "read-package-json-fast": "^2.0.3" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { - "version": "3.1.1", - "inBundle": true, - "license": "ISC", - "dependencies": { - "cacache": "^16.0.0", - "json-parse-even-better-errors": "^2.3.1", - "pacote": "^13.0.3", - "semver": "^7.3.5" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/@npmcli/move-file": { - "version": "2.0.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "mkdirp": "^1.0.4", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/npm/node_modules/@npmcli/name-from-folder": { - "version": "1.0.1", - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/@npmcli/node-gyp": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, - "node_modules/npm/node_modules/@npmcli/package-json": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC", + "node_modules/base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, "dependencies": { - "json-parse-even-better-errors": "^2.3.1" + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/@npmcli/promise-spawn": { - "version": "3.0.0", - "inBundle": true, - "license": "ISC", + "node_modules/base/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "dev": true, "dependencies": { - "infer-owner": "^1.0.4" + "is-descriptor": "^1.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/@npmcli/query": { - "version": "1.2.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-package-arg": "^9.1.0", - "postcss-selector-parser": "^6.0.10", - "semver": "^7.3.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, - "node_modules/npm/node_modules/@npmcli/run-script": { - "version": "4.2.1", - "inBundle": true, - "license": "ISC", + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", "dependencies": { - "@npmcli/node-gyp": "^2.0.0", - "@npmcli/promise-spawn": "^3.0.0", - "node-gyp": "^9.0.0", - "read-package-json-fast": "^2.0.3", - "which": "^2.0.2" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "tweetnacl": "^0.14.3" } }, - "node_modules/npm/node_modules/@tootallnate/once": { - "version": "2.0.0", - "inBundle": true, - "license": "MIT", + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true, "engines": { - "node": ">= 10" + "node": "*" } }, - "node_modules/npm/node_modules/abbrev": { - "version": "1.1.1", - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/agent-base": { - "version": "6.0.2", - "inBundle": true, - "license": "MIT", - "dependencies": { - "debug": "4" - }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "optional": true, "engines": { - "node": ">= 6.0.0" + "node": ">=8" } }, - "node_modules/npm/node_modules/agentkeepalive": { - "version": "4.2.1", - "inBundle": true, - "license": "MIT", + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "optional": true, "dependencies": { - "debug": "^4.1.0", - "depd": "^1.1.2", - "humanize-ms": "^1.2.1" - }, - "engines": { - "node": ">= 8.0.0" + "file-uri-to-path": "1.0.0" } }, - "node_modules/npm/node_modules/aggregate-error": { - "version": "3.1.0", - "inBundle": true, - "license": "MIT", + "node_modules/body-parser": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" }, "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/ansi-regex": { - "version": "5.0.1", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/npm/node_modules/ansi-styles": { - "version": "4.3.0", - "inBundle": true, - "license": "MIT", + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "ms": "2.0.0" } }, - "node_modules/npm/node_modules/aproba": { + "node_modules/body-parser/node_modules/ms": { "version": "2.0.0", - "inBundle": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/npm/node_modules/archy": { + "node_modules/boolbase": { "version": "1.0.0", - "inBundle": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" }, - "node_modules/npm/node_modules/are-we-there-yet": { - "version": "3.0.1", - "inBundle": true, - "license": "ISC", + "node_modules/boxen": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", + "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", + "dev": true, "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" + "ansi-align": "^2.0.0", + "camelcase": "^4.0.0", + "chalk": "^2.0.1", + "cli-boxes": "^1.0.0", + "string-width": "^2.0.0", + "term-size": "^1.2.0", + "widest-line": "^2.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=4" } }, - "node_modules/npm/node_modules/asap": { - "version": "2.0.6", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/balanced-match": { - "version": "1.0.2", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/bin-links": { - "version": "3.0.3", - "inBundle": true, - "license": "ISC", - "dependencies": { - "cmd-shim": "^5.0.0", - "mkdirp-infer-owner": "^2.0.0", - "npm-normalize-package-bin": "^2.0.0", - "read-cmd-shim": "^3.0.0", - "rimraf": "^3.0.0", - "write-file-atomic": "^4.0.0" - }, + "node_modules/boxen/node_modules/camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw==", + "dev": true, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=4" } }, - "node_modules/npm/node_modules/bin-links/node_modules/npm-normalize-package-bin": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/npm/node_modules/binary-extensions": { - "version": "2.2.0", - "inBundle": true, - "license": "MIT", + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dependencies": { + "fill-range": "^7.0.1" + }, "engines": { "node": ">=8" } }, - "node_modules/npm/node_modules/brace-expansion": { - "version": "2.0.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } + "node_modules/browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", + "dev": true }, - "node_modules/npm/node_modules/builtins": { - "version": "5.0.1", - "inBundle": true, - "license": "MIT", + "node_modules/browser-resolve": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", + "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", + "dev": true, "dependencies": { - "semver": "^7.0.0" + "resolve": "1.1.7" } }, - "node_modules/npm/node_modules/cacache": { - "version": "16.1.3", - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/fs": "^2.1.0", - "@npmcli/move-file": "^2.0.0", - "chownr": "^2.0.0", - "fs-minipass": "^2.1.0", - "glob": "^8.0.1", - "infer-owner": "^1.0.4", - "lru-cache": "^7.7.1", - "minipass": "^3.1.6", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "mkdirp": "^1.0.4", - "p-map": "^4.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^3.0.2", - "ssri": "^9.0.0", - "tar": "^6.1.11", - "unique-filename": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } + "node_modules/browser-resolve/node_modules/resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg==", + "dev": true }, - "node_modules/npm/node_modules/chalk": { - "version": "4.1.2", - "inBundle": true, - "license": "MIT", + "node_modules/browserslist": { + "version": "4.21.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", + "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "caniuse-lite": "^1.0.30001449", + "electron-to-chromium": "^1.4.284", + "node-releases": "^2.0.8", + "update-browserslist-db": "^1.0.10" }, - "engines": { - "node": ">=10" + "bin": { + "browserslist": "cli.js" }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/npm/node_modules/chownr": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC", "engines": { - "node": ">=10" + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/npm/node_modules/cidr-regex": { - "version": "3.1.1", - "inBundle": true, - "license": "BSD-2-Clause", + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, "dependencies": { - "ip-regex": "^4.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/clean-stack": { - "version": "2.2.0", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=6" + "node-int64": "^0.4.0" } }, - "node_modules/npm/node_modules/cli-columns": { - "version": "4.0.0", - "inBundle": true, - "license": "MIT", + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "dependencies": { - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">= 10" + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" } }, - "node_modules/npm/node_modules/cli-table3": { - "version": "0.6.2", - "inBundle": true, - "license": "MIT", - "dependencies": { - "string-width": "^4.2.0" - }, - "engines": { - "node": "10.* || >= 12.*" - }, - "optionalDependencies": { - "@colors/colors": "1.5.0" - } + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true }, - "node_modules/npm/node_modules/clone": { - "version": "1.0.4", - "inBundle": true, - "license": "MIT", + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "engines": { - "node": ">=0.8" + "node": ">= 0.8" } }, - "node_modules/npm/node_modules/cmd-shim": { - "version": "5.0.0", - "inBundle": true, - "license": "ISC", + "node_modules/cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, "dependencies": { - "mkdirp-infer-owner": "^2.0.0" + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/color-convert": { - "version": "2.0.1", - "inBundle": true, - "license": "MIT", + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", "dependencies": { - "color-name": "~1.1.4" + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" }, - "engines": { - "node": ">=7.0.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/npm/node_modules/color-name": { - "version": "1.1.4", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/color-support": { - "version": "1.1.3", - "inBundle": true, - "license": "ISC", - "bin": { - "color-support": "bin.js" + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" } }, - "node_modules/npm/node_modules/columnify": { - "version": "1.6.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "strip-ansi": "^6.0.1", - "wcwidth": "^1.0.0" - }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, "engines": { - "node": ">=8.0.0" + "node": ">=6" } }, - "node_modules/npm/node_modules/common-ancestor-path": { - "version": "1.0.1", - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/concat-map": { - "version": "0.0.1", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/console-control-strings": { - "version": "1.1.0", - "inBundle": true, - "license": "ISC" + "node_modules/caniuse-lite": { + "version": "1.0.30001480", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001480.tgz", + "integrity": "sha512-q7cpoPPvZYgtyC4VaBSN0Bt+PJ4c4EYRf0DrduInOz2SkFpHD5p3LnvEpqBp7UnJn+8x1Ogl1s38saUxe+ihQQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] }, - "node_modules/npm/node_modules/cssesc": { - "version": "3.0.0", - "inBundle": true, - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" + "node_modules/capture-exit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", + "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", + "dev": true, + "dependencies": { + "rsvp": "^4.8.4" }, "engines": { - "node": ">=4" + "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/npm/node_modules/debug": { - "version": "4.3.4", - "inBundle": true, - "license": "MIT", - "dependencies": { - "ms": "2.1.2" - }, + "node_modules/capture-stack-trace": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.2.tgz", + "integrity": "sha512-X/WM2UQs6VMHUtjUDnZTRI+i1crWteJySFzr9UpGoQa4WQffXVTTXuekjl7TjZRlcF2XfjgITT0HxZ9RnxeT0w==", + "dev": true, "engines": { - "node": ">=6.0" + "node": ">=0.10.0" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/npm/node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/debuglog": { - "version": "1.0.1", - "inBundle": true, - "license": "MIT", - "engines": { - "node": "*" - } + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" }, - "node_modules/npm/node_modules/defaults": { - "version": "1.0.3", - "inBundle": true, - "license": "MIT", + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dependencies": { - "clone": "^1.0.2" - } - }, - "node_modules/npm/node_modules/delegates": { - "version": "1.0.0", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/depd": { - "version": "1.1.2", - "inBundle": true, - "license": "MIT", + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, "engines": { - "node": ">= 0.6" - } - }, - "node_modules/npm/node_modules/dezalgo": { - "version": "1.0.4", - "inBundle": true, - "license": "ISC", - "dependencies": { - "asap": "^2.0.0", - "wrappy": "1" + "node": ">=4" } }, - "node_modules/npm/node_modules/diff": { - "version": "5.1.0", - "inBundle": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" + "node_modules/character-entities": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", + "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/npm/node_modules/emoji-regex": { - "version": "8.0.0", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/encoding": { - "version": "0.1.13", - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "iconv-lite": "^0.6.2" + "node_modules/character-entities-legacy": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", + "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/npm/node_modules/env-paths": { - "version": "2.2.1", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=6" + "node_modules/character-reference-invalid": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", + "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/npm/node_modules/err-code": { - "version": "2.0.3", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/fastest-levenshtein": { - "version": "1.0.12", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/fs-minipass": { - "version": "2.1.0", - "inBundle": true, - "license": "ISC", + "node_modules/cheerio": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz", + "integrity": "sha512-8/MzidM6G/TgRelkzDG13y3Y9LxBjCb+8yOEZ9+wwq5gVF2w2pV0wmHvjfT0RvuxGyR7UEuK36r+yYMbT4uKgA==", "dependencies": { - "minipass": "^3.0.0" + "css-select": "~1.2.0", + "dom-serializer": "~0.1.0", + "entities": "~1.1.1", + "htmlparser2": "^3.9.1", + "lodash.assignin": "^4.0.9", + "lodash.bind": "^4.1.4", + "lodash.defaults": "^4.0.1", + "lodash.filter": "^4.4.0", + "lodash.flatten": "^4.2.0", + "lodash.foreach": "^4.3.0", + "lodash.map": "^4.4.0", + "lodash.merge": "^4.4.0", + "lodash.pick": "^4.2.1", + "lodash.reduce": "^4.4.0", + "lodash.reject": "^4.4.0", + "lodash.some": "^4.4.0" }, "engines": { - "node": ">= 8" + "node": ">= 0.6" } }, - "node_modules/npm/node_modules/fs.realpath": { - "version": "1.0.0", - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/function-bind": { - "version": "1.1.1", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/gauge": { - "version": "4.0.4", - "inBundle": true, - "license": "ISC", + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "optional": true, "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.3", - "console-control-strings": "^1.1.0", - "has-unicode": "^2.0.1", - "signal-exit": "^3.0.7", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.5" + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" } }, - "node_modules/npm/node_modules/glob": { - "version": "8.0.3", - "inBundle": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true, + "engines": { + "node": ">=6.0" } }, - "node_modules/npm/node_modules/graceful-fs": { - "version": "4.2.10", - "inBundle": true, - "license": "ISC" + "node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" }, - "node_modules/npm/node_modules/has": { - "version": "1.0.3", - "inBundle": true, - "license": "MIT", + "node_modules/class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, "dependencies": { - "function-bind": "^1.1.1" + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" }, "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/npm/node_modules/has-flag": { - "version": "4.0.0", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/has-unicode": { - "version": "2.0.1", - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/hosted-git-info": { - "version": "5.2.1", - "inBundle": true, - "license": "ISC", + "node_modules/class-utils/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, "dependencies": { - "lru-cache": "^7.5.1" + "is-descriptor": "^0.1.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/http-cache-semantics": { - "version": "4.1.1", - "inBundle": true, - "license": "BSD-2-Clause" - }, - "node_modules/npm/node_modules/http-proxy-agent": { - "version": "5.0.0", - "inBundle": true, - "license": "MIT", + "node_modules/class-utils/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", + "dev": true, "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" + "kind-of": "^3.0.2" }, "engines": { - "node": ">= 6" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/https-proxy-agent": { - "version": "5.0.1", - "inBundle": true, - "license": "MIT", + "node_modules/class-utils/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, "dependencies": { - "agent-base": "6", - "debug": "4" + "is-buffer": "^1.1.5" }, "engines": { - "node": ">= 6" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/humanize-ms": { - "version": "1.2.1", - "inBundle": true, - "license": "MIT", + "node_modules/class-utils/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", + "dev": true, "dependencies": { - "ms": "^2.0.0" + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/iconv-lite": { - "version": "0.6.3", - "inBundle": true, - "license": "MIT", - "optional": true, + "node_modules/class-utils/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" + "is-buffer": "^1.1.5" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/ignore-walk": { - "version": "5.0.1", - "inBundle": true, - "license": "ISC", + "node_modules/class-utils/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, "dependencies": { - "minimatch": "^5.0.1" + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/imurmurhash": { - "version": "0.1.4", - "inBundle": true, - "license": "MIT", + "node_modules/class-utils/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, "engines": { - "node": ">=0.8.19" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/indent-string": { - "version": "4.0.0", - "inBundle": true, - "license": "MIT", + "node_modules/classnames": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", + "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" + }, + "node_modules/cli-boxes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", + "integrity": "sha512-3Fo5wu8Ytle8q9iCzS4D2MWVL2X7JVWRiS1BnXbTFDhS9c/REkM9vd1AmabsoZoY5/dGi5TT9iKL8Kb6DeBRQg==", + "dev": true, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/infer-owner": { - "version": "1.0.4", - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/inflight": { - "version": "1.0.6", - "inBundle": true, - "license": "ISC", + "node_modules/cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, "dependencies": { - "once": "^1.3.0", - "wrappy": "1" + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" } }, - "node_modules/npm/node_modules/inherits": { - "version": "2.0.4", - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/ini": { - "version": "3.0.1", - "inBundle": true, - "license": "ISC", + "node_modules/cliui/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=6" } }, - "node_modules/npm/node_modules/init-package-json": { - "version": "3.0.2", - "inBundle": true, - "license": "ISC", + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, "dependencies": { - "npm-package-arg": "^9.0.1", - "promzard": "^0.3.0", - "read": "^1.0.7", - "read-package-json": "^5.0.0", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4", - "validate-npm-package-name": "^4.0.0" + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=6" } }, - "node_modules/npm/node_modules/ip": { - "version": "2.0.0", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/ip-regex": { - "version": "4.3.0", - "inBundle": true, - "license": "MIT", + "node_modules/clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", "engines": { - "node": ">=8" + "node": ">=6" } }, - "node_modules/npm/node_modules/is-cidr": { - "version": "4.0.2", - "inBundle": true, - "license": "BSD-2-Clause", - "dependencies": { - "cidr-regex": "^3.1.1" - }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, "engines": { - "node": ">=10" + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" } }, - "node_modules/npm/node_modules/is-core-module": { - "version": "2.10.0", - "inBundle": true, - "license": "MIT", + "node_modules/collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==", + "dev": true, "dependencies": { - "has": "^1.0.3" + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/npm/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "inBundle": true, - "license": "MIT", "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/is-lambda": { - "version": "1.0.1", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/isexe": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/json-stringify-nice": { - "version": "1.1.4", - "inBundle": true, - "license": "ISC", - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" } }, - "node_modules/npm/node_modules/jsonparse": { - "version": "1.3.1", - "engines": [ - "node >= 0.2.0" - ], - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/just-diff": { - "version": "5.1.1", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/just-diff-apply": { - "version": "5.4.1", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/libnpmaccess": { - "version": "6.0.4", - "inBundle": true, - "license": "ISC", + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dependencies": { - "aproba": "^2.0.0", - "minipass": "^3.1.1", - "npm-package-arg": "^9.0.1", - "npm-registry-fetch": "^13.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "color-name": "1.1.3" } }, - "node_modules/npm/node_modules/libnpmdiff": { - "version": "4.0.5", - "inBundle": true, - "license": "ISC", + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", "dependencies": { - "@npmcli/disparity-colors": "^2.0.0", - "@npmcli/installed-package-contents": "^1.0.7", - "binary-extensions": "^2.2.0", - "diff": "^5.1.0", - "minimatch": "^5.0.1", - "npm-package-arg": "^9.0.1", - "pacote": "^13.6.1", - "tar": "^6.1.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" } }, - "node_modules/npm/node_modules/libnpmexec": { - "version": "4.0.14", - "inBundle": true, - "license": "ISC", + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true + }, + "node_modules/colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", "dependencies": { - "@npmcli/arborist": "^5.6.3", - "@npmcli/ci-detect": "^2.0.0", - "@npmcli/fs": "^2.1.1", - "@npmcli/run-script": "^4.2.0", - "chalk": "^4.1.0", - "mkdirp-infer-owner": "^2.0.0", - "npm-package-arg": "^9.0.1", - "npmlog": "^6.0.2", - "pacote": "^13.6.1", - "proc-log": "^2.0.0", - "read": "^1.0.7", - "read-package-json-fast": "^2.0.2", - "semver": "^7.3.7", - "walk-up-path": "^1.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "color": "^3.1.3", + "text-hex": "1.0.x" } }, - "node_modules/npm/node_modules/libnpmfund": { - "version": "3.0.5", - "inBundle": true, - "license": "ISC", + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dependencies": { - "@npmcli/arborist": "^5.6.3" + "delayed-stream": "~1.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">= 0.8" } }, - "node_modules/npm/node_modules/libnpmhook": { - "version": "8.0.4", - "inBundle": true, - "license": "ISC", - "dependencies": { - "aproba": "^2.0.0", - "npm-registry-fetch": "^13.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node_modules/comma-separated-tokens": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", + "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/npm/node_modules/libnpmorg": { - "version": "4.0.4", - "inBundle": true, - "license": "ISC", - "dependencies": { - "aproba": "^2.0.0", - "npm-registry-fetch": "^13.0.0" - }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">= 6" } }, - "node_modules/npm/node_modules/libnpmpack": { - "version": "4.1.3", - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/run-script": "^4.1.3", - "npm-package-arg": "^9.0.1", - "pacote": "^13.6.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true }, - "node_modules/npm/node_modules/libnpmpublish": { - "version": "6.0.5", - "inBundle": true, - "license": "ISC", - "dependencies": { - "normalize-package-data": "^4.0.0", - "npm-package-arg": "^9.0.1", - "npm-registry-fetch": "^13.0.0", - "semver": "^7.3.7", - "ssri": "^9.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } + "node_modules/component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true }, - "node_modules/npm/node_modules/libnpmsearch": { - "version": "5.0.4", - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-registry-fetch": "^13.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } + "node_modules/compute-scroll-into-view": { + "version": "1.0.20", + "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.20.tgz", + "integrity": "sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, - "node_modules/npm/node_modules/libnpmteam": { - "version": "4.0.4", - "inBundle": true, - "license": "ISC", + "node_modules/configstore": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.5.tgz", + "integrity": "sha512-nlOhI4+fdzoK5xmJ+NY+1gZK56bwEaWZr8fYuXohZ9Vkc1o3a4T/R3M+yE/w7x/ZVJ1zF8c+oaOvF0dztdUgmA==", + "dev": true, "dependencies": { - "aproba": "^2.0.0", - "npm-registry-fetch": "^13.0.0" + "dot-prop": "^4.2.1", + "graceful-fs": "^4.1.2", + "make-dir": "^1.0.0", + "unique-string": "^1.0.0", + "write-file-atomic": "^2.0.0", + "xdg-basedir": "^3.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=4" } }, - "node_modules/npm/node_modules/libnpmversion": { - "version": "3.0.7", - "inBundle": true, - "license": "ISC", + "node_modules/configstore/node_modules/make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "dev": true, "dependencies": { - "@npmcli/git": "^3.0.0", - "@npmcli/run-script": "^4.1.3", - "json-parse-even-better-errors": "^2.3.1", - "proc-log": "^2.0.0", - "semver": "^7.3.7" + "pify": "^3.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=4" } }, - "node_modules/npm/node_modules/lru-cache": { - "version": "7.13.2", - "inBundle": true, - "license": "ISC", + "node_modules/configstore/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, "engines": { - "node": ">=12" + "node": ">=4" } }, - "node_modules/npm/node_modules/make-fetch-happen": { - "version": "10.2.1", - "inBundle": true, - "license": "ISC", + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "dependencies": { - "agentkeepalive": "^4.2.1", - "cacache": "^16.1.0", - "http-cache-semantics": "^4.1.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^7.7.1", - "minipass": "^3.1.6", - "minipass-collect": "^1.0.2", - "minipass-fetch": "^2.0.3", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.3", - "promise-retry": "^2.0.1", - "socks-proxy-agent": "^7.0.0", - "ssri": "^9.0.0" + "safe-buffer": "5.2.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">= 0.6" } }, - "node_modules/npm/node_modules/minimatch": { - "version": "5.1.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "engines": { - "node": ">=10" + "node": ">= 0.6" } }, - "node_modules/npm/node_modules/minipass": { - "version": "3.3.4", - "inBundle": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true }, - "node_modules/npm/node_modules/minipass-collect": { - "version": "1.0.2", - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, + "node_modules/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", "engines": { - "node": ">= 8" + "node": ">= 0.6" } }, - "node_modules/npm/node_modules/minipass-fetch": { - "version": "2.1.1", - "inBundle": true, - "license": "MIT", + "node_modules/cookie-parser": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz", + "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==", "dependencies": { - "minipass": "^3.1.6", - "minipass-sized": "^1.0.3", - "minizlib": "^2.1.2" + "cookie": "0.4.1", + "cookie-signature": "1.0.6" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - }, - "optionalDependencies": { - "encoding": "^0.1.13" + "node": ">= 0.8.0" } }, - "node_modules/npm/node_modules/minipass-flush": { - "version": "1.0.5", - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==", + "dev": true, "engines": { - "node": ">= 8" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/minipass-json-stream": { - "version": "1.0.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "jsonparse": "^1.3.1", - "minipass": "^3.0.0" + "node_modules/core-js": { + "version": "3.30.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.30.1.tgz", + "integrity": "sha512-ZNS5nbiSwDTq4hFosEDqm65izl2CWmLz0hARJMyNQBgkUZMIF51cQiMvIQKA6hvuaeWxQDP3hEedM1JZIgTldQ==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" } }, - "node_modules/npm/node_modules/minipass-pipeline": { - "version": "1.2.4", - "inBundle": true, - "license": "ISC", + "node_modules/core-js-compat": { + "version": "3.30.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.30.1.tgz", + "integrity": "sha512-d690npR7MC6P0gq4npTl5n2VQeNAmUrJ90n+MHiKS7W2+xno4o3F5GDEuylSdi6EJ3VssibSGXOa1r3YXD3Mhw==", + "dev": true, "dependencies": { - "minipass": "^3.0.0" + "browserslist": "^4.21.5" }, - "engines": { - "node": ">=8" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" } }, - "node_modules/npm/node_modules/minipass-sized": { - "version": "1.0.3", - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" }, - "node_modules/npm/node_modules/minizlib": { - "version": "2.1.2", - "inBundle": true, - "license": "MIT", + "node_modules/create-error-class": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", + "integrity": "sha512-gYTKKexFO3kh200H1Nit76sRwRtOY32vQd3jpAQKpLtZqyNsSQNfI4N7o3eP2wUjV35pTWKRYqFUDBvUha/Pkw==", + "dev": true, "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/npm/node_modules/mkdirp": { - "version": "1.0.4", - "inBundle": true, - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" + "capture-stack-trace": "^1.0.0" }, "engines": { - "node": ">=10" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/mkdirp-infer-owner": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC", + "node_modules/cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", "dependencies": { - "chownr": "^2.0.0", - "infer-owner": "^1.0.4", - "mkdirp": "^1.0.3" + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" }, "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/ms": { - "version": "2.1.3", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/mute-stream": { - "version": "0.0.8", - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/negotiator": { - "version": "0.6.3", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" + "node": ">=4.8" } }, - "node_modules/npm/node_modules/node-gyp": { - "version": "9.1.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "env-paths": "^2.2.0", - "glob": "^7.1.4", - "graceful-fs": "^4.2.6", - "make-fetch-happen": "^10.0.3", - "nopt": "^5.0.0", - "npmlog": "^6.0.0", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.2", - "which": "^2.0.2" - }, + "node_modules/cross-spawn/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "bin": { - "node-gyp": "bin/node-gyp.js" - }, - "engines": { - "node": "^12.22 || ^14.13 || >=16" + "semver": "bin/semver" } }, - "node_modules/npm/node_modules/node-gyp/node_modules/brace-expansion": { - "version": "1.1.11", - "inBundle": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "node_modules/crypto-random-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", + "integrity": "sha512-GsVpkFPlycH7/fRR7Dhcmnoii54gV1nz7y4CWyeFS14N+JVBBhY+r8amRHE4BwSYal7BPTDp8isvAlCxyFt3Hg==", + "dev": true, + "engines": { + "node": ">=4" } }, - "node_modules/npm/node_modules/node-gyp/node_modules/glob": { - "version": "7.2.3", - "inBundle": true, - "license": "ISC", + "node_modules/css-loader": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.6.0.tgz", + "integrity": "sha512-M5lSukoWi1If8dhQAUCvj4H8vUt3vOnwbQBH9DdTm/s4Ym2B/3dPMtYZeJmq7Q3S3Pa+I94DcZ7pc9bP14cWIQ==", + "dev": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "camelcase": "^5.3.1", + "cssesc": "^3.0.0", + "icss-utils": "^4.1.1", + "loader-utils": "^1.2.3", + "normalize-path": "^3.0.0", + "postcss": "^7.0.32", + "postcss-modules-extract-imports": "^2.0.0", + "postcss-modules-local-by-default": "^3.0.2", + "postcss-modules-scope": "^2.2.0", + "postcss-modules-values": "^3.0.0", + "postcss-value-parser": "^4.1.0", + "schema-utils": "^2.7.0", + "semver": "^6.3.0" }, "engines": { - "node": "*" + "node": ">= 8.9.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/node-gyp/node_modules/minimatch": { - "version": "3.1.2", - "inBundle": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" + "type": "opencollective", + "url": "https://opencollective.com/webpack" }, - "engines": { - "node": "*" + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" } }, - "node_modules/npm/node_modules/node-gyp/node_modules/nopt": { - "version": "5.0.0", - "inBundle": true, - "license": "ISC", + "node_modules/css-loader/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, "dependencies": { - "abbrev": "1" + "minimist": "^1.2.0" }, "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": ">=6" + "json5": "lib/cli.js" } }, - "node_modules/npm/node_modules/nopt": { - "version": "6.0.0", - "inBundle": true, - "license": "ISC", + "node_modules/css-loader/node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, "dependencies": { - "abbrev": "^1.0.0" - }, - "bin": { - "nopt": "bin/nopt.js" + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=4.0.0" } }, - "node_modules/npm/node_modules/normalize-package-data": { - "version": "4.0.1", - "inBundle": true, - "license": "BSD-2-Clause", + "node_modules/css-modules-require-hook": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/css-modules-require-hook/-/css-modules-require-hook-4.2.3.tgz", + "integrity": "sha512-y3eGHAqmDl8JQdr1LYTwBkMxy7CSHCMy9YwpSjDqtGqCCEy9lKq/x/nmHVs+G7C1+xXmyIXjRu0q/MMn8w01mg==", "dependencies": { - "hosted-git-info": "^5.0.0", - "is-core-module": "^2.8.1", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4" + "debug": "^2.2.0", + "generic-names": "^1.0.1", + "glob-to-regexp": "^0.3.0", + "icss-replace-symbols": "^1.0.2", + "lodash": "^4.3.0", + "postcss": "^6.0.1", + "postcss-modules-extract-imports": "^1.0.0", + "postcss-modules-local-by-default": "^1.0.1", + "postcss-modules-resolve-imports": "^1.3.0", + "postcss-modules-scope": "^1.0.0", + "postcss-modules-values": "^1.1.1", + "seekout": "^1.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">= 4" } }, - "node_modules/npm/node_modules/npm-audit-report": { - "version": "3.0.0", - "inBundle": true, - "license": "ISC", + "node_modules/css-modules-require-hook/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dependencies": { - "chalk": "^4.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "ms": "2.0.0" } }, - "node_modules/npm/node_modules/npm-bundled": { - "version": "2.0.1", - "inBundle": true, - "license": "ISC", + "node_modules/css-modules-require-hook/node_modules/icss-utils": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-3.0.1.tgz", + "integrity": "sha512-ANhVLoEfe0KoC9+z4yiTaXOneB49K6JIXdS+yAgH0NERELpdIT7kkj2XxUPuHafeHnn8umXnECSpsfk1RTaUew==", "dependencies": { - "npm-normalize-package-bin": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "postcss": "^6.0.2" } }, - "node_modules/npm/node_modules/npm-bundled/node_modules/npm-normalize-package-bin": { + "node_modules/css-modules-require-hook/node_modules/ms": { "version": "2.0.0", - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/npm/node_modules/npm-install-checks": { - "version": "5.0.0", - "inBundle": true, - "license": "BSD-2-Clause", + "node_modules/css-modules-require-hook/node_modules/postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", "dependencies": { - "semver": "^7.1.1" + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=4.0.0" } }, - "node_modules/npm/node_modules/npm-normalize-package-bin": { - "version": "1.0.1", - "inBundle": true, - "license": "ISC" + "node_modules/css-modules-require-hook/node_modules/postcss-modules-extract-imports": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.1.tgz", + "integrity": "sha512-6jt9XZwUhwmRUhb/CkyJY020PYaPJsCyt3UjbaWo6XEbH/94Hmv6MP7fG2C5NDU/BcHzyGYxNtHvM+LTf9HrYw==", + "dependencies": { + "postcss": "^6.0.1" + } }, - "node_modules/npm/node_modules/npm-package-arg": { - "version": "9.1.0", - "inBundle": true, - "license": "ISC", + "node_modules/css-modules-require-hook/node_modules/postcss-modules-local-by-default": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz", + "integrity": "sha512-X4cquUPIaAd86raVrBwO8fwRfkIdbwFu7CTfEOjiZQHVQwlHRSkTgH5NLDmMm5+1hQO8u6dZ+TOOJDbay1hYpA==", "dependencies": { - "hosted-git-info": "^5.0.0", - "proc-log": "^2.0.1", - "semver": "^7.3.5", - "validate-npm-package-name": "^4.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "css-selector-tokenizer": "^0.7.0", + "postcss": "^6.0.1" } }, - "node_modules/npm/node_modules/npm-packlist": { - "version": "5.1.3", - "inBundle": true, - "license": "ISC", + "node_modules/css-modules-require-hook/node_modules/postcss-modules-resolve-imports": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/postcss-modules-resolve-imports/-/postcss-modules-resolve-imports-1.3.0.tgz", + "integrity": "sha512-9xECsLcuR1DRu7mVhK6GIkdEeg4sdZXbLEcuEkDh9mKiz+uxDBfDREYiVehINdW0UPF9gbHnb64ZQMuRsqqkDA==", "dependencies": { - "glob": "^8.0.1", - "ignore-walk": "^5.0.1", - "npm-bundled": "^2.0.0", - "npm-normalize-package-bin": "^2.0.0" - }, - "bin": { - "npm-packlist": "bin/index.js" + "css-selector-tokenizer": "^0.7.0", + "icss-utils": "^3.0.1", + "minimist": "^1.2.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">= 4" + }, + "peerDependencies": { + "postcss": "^6.0.0" } }, - "node_modules/npm/node_modules/npm-packlist/node_modules/npm-normalize-package-bin": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node_modules/css-modules-require-hook/node_modules/postcss-modules-scope": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz", + "integrity": "sha512-LTYwnA4C1He1BKZXIx1CYiHixdSe9LWYVKadq9lK5aCCMkoOkFyZ7aigt+srfjlRplJY3gIol6KUNefdMQJdlw==", + "dependencies": { + "css-selector-tokenizer": "^0.7.0", + "postcss": "^6.0.1" } }, - "node_modules/npm/node_modules/npm-pick-manifest": { - "version": "7.0.2", - "inBundle": true, - "license": "ISC", + "node_modules/css-modules-require-hook/node_modules/postcss-modules-values": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz", + "integrity": "sha512-i7IFaR9hlQ6/0UgFuqM6YWaCfA1Ej8WMg8A5DggnH1UGKJvTV/ugqq/KaULixzzOi3T/tF6ClBXcHGCzdd5unA==", "dependencies": { - "npm-install-checks": "^5.0.0", - "npm-normalize-package-bin": "^2.0.0", - "npm-package-arg": "^9.0.0", - "semver": "^7.3.5" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "icss-replace-symbols": "^1.1.0", + "postcss": "^6.0.1" } }, - "node_modules/npm/node_modules/npm-pick-manifest/node_modules/npm-normalize-package-bin": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC", + "node_modules/css-modules-require-hook/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/npm-profile": { - "version": "6.2.1", - "inBundle": true, - "license": "ISC", + "node_modules/css-select": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "integrity": "sha512-dUQOBoqdR7QwV90WysXPLXG5LO7nhYBgiWVfxF80DKPF8zx1t/pUd2FYy73emg3zrjtM6dzmYgbHKfV2rxiHQA==", "dependencies": { - "npm-registry-fetch": "^13.0.1", - "proc-log": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "boolbase": "~1.0.0", + "css-what": "2.1", + "domutils": "1.5.1", + "nth-check": "~1.0.1" } }, - "node_modules/npm/node_modules/npm-registry-fetch": { - "version": "13.3.1", - "inBundle": true, - "license": "ISC", + "node_modules/css-selector-tokenizer": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.3.tgz", + "integrity": "sha512-jWQv3oCEL5kMErj4wRnK/OPoBi0D+P1FR2cDCKYPaMeD2eW3/mttav8HT4hT1CKopiJI/psEULjkClhvJo4Lvg==", "dependencies": { - "make-fetch-happen": "^10.0.6", - "minipass": "^3.1.6", - "minipass-fetch": "^2.0.3", - "minipass-json-stream": "^1.0.1", - "minizlib": "^2.1.2", - "npm-package-arg": "^9.0.1", - "proc-log": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "cssesc": "^3.0.0", + "fastparse": "^1.1.2" } }, - "node_modules/npm/node_modules/npm-user-validate": { - "version": "1.0.1", - "inBundle": true, - "license": "BSD-2-Clause" + "node_modules/css-what": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", + "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", + "engines": { + "node": "*" + } }, - "node_modules/npm/node_modules/npmlog": { - "version": "6.0.2", - "inBundle": true, - "license": "ISC", - "dependencies": { - "are-we-there-yet": "^3.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^4.0.3", - "set-blocking": "^2.0.0" + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "bin": { + "cssesc": "bin/cssesc" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=4" } }, - "node_modules/npm/node_modules/once": { + "node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + }, + "node_modules/cssstyle": { "version": "1.4.0", - "inBundle": true, - "license": "ISC", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.4.0.tgz", + "integrity": "sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==", + "dev": true, "dependencies": { - "wrappy": "1" + "cssom": "0.3.x" } }, - "node_modules/npm/node_modules/opener": { - "version": "1.5.2", - "inBundle": true, - "license": "(WTFPL OR MIT)", - "bin": { - "opener": "bin/opener-bin.js" - } + "node_modules/csstype": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==", + "peer": true }, - "node_modules/npm/node_modules/p-map": { - "version": "4.0.0", - "inBundle": true, - "license": "MIT", + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm/node_modules/pacote": { - "version": "13.6.2", - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/git": "^3.0.0", - "@npmcli/installed-package-contents": "^1.0.7", - "@npmcli/promise-spawn": "^3.0.0", - "@npmcli/run-script": "^4.1.0", - "cacache": "^16.0.0", - "chownr": "^2.0.0", - "fs-minipass": "^2.1.0", - "infer-owner": "^1.0.4", - "minipass": "^3.1.6", - "mkdirp": "^1.0.4", - "npm-package-arg": "^9.0.0", - "npm-packlist": "^5.1.0", - "npm-pick-manifest": "^7.0.0", - "npm-registry-fetch": "^13.0.1", - "proc-log": "^2.0.0", - "promise-retry": "^2.0.1", - "read-package-json": "^5.0.0", - "read-package-json-fast": "^2.0.3", - "rimraf": "^3.0.2", - "ssri": "^9.0.0", - "tar": "^6.1.11" - }, - "bin": { - "pacote": "lib/bin.js" + "assert-plus": "^1.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=0.10" } }, - "node_modules/npm/node_modules/parse-conflict-json": { - "version": "2.0.2", - "inBundle": true, - "license": "ISC", + "node_modules/data-urls": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz", + "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==", + "dev": true, "dependencies": { - "json-parse-even-better-errors": "^2.3.1", - "just-diff": "^5.0.1", - "just-diff-apply": "^5.2.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "abab": "^2.0.0", + "whatwg-mimetype": "^2.2.0", + "whatwg-url": "^7.0.0" } }, - "node_modules/npm/node_modules/path-is-absolute": { - "version": "1.0.1", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" + "node_modules/data-urls/node_modules/whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "dev": true, + "dependencies": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" } }, - "node_modules/npm/node_modules/postcss-selector-parser": { - "version": "6.0.10", - "inBundle": true, - "license": "MIT", + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" + "ms": "2.1.2" }, "engines": { - "node": ">=4" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/npm/node_modules/proc-log": { - "version": "2.0.1", - "inBundle": true, - "license": "ISC", + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/promise-all-reject-late": { - "version": "1.0.1", - "inBundle": true, - "license": "ISC", - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/promise-call-limit": { - "version": "1.0.1", - "inBundle": true, - "license": "ISC", - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/promise-inflight": { - "version": "1.0.1", - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/promise-retry": { - "version": "2.0.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "err-code": "^2.0.2", - "retry": "^0.12.0" - }, + "node_modules/decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "dev": true, "engines": { - "node": ">=10" + "node": ">=0.10" } }, - "node_modules/npm/node_modules/promzard": { - "version": "0.3.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "read": "1" + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "engines": { + "node": ">=4.0.0" } }, - "node_modules/npm/node_modules/qrcode-terminal": { - "version": "0.12.0", - "inBundle": true, - "bin": { - "qrcode-terminal": "bin/qrcode-terminal.js" - } + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true }, - "node_modules/npm/node_modules/read": { - "version": "1.0.7", - "inBundle": true, - "license": "ISC", + "node_modules/deep-merge": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/deep-merge/-/deep-merge-1.0.0.tgz", + "integrity": "sha512-cVG7u0dA5OtrgliqM30OLE3OPrB59PjWEjtCG4Go78OI/f/kP6aK4J/cF4E2f0PpN7A1QmBVxh1dKJkrIU6jCQ==", "dependencies": { - "mute-stream": "~0.0.4" - }, - "engines": { - "node": ">=0.8" + "xtend": "~1.0.3" } }, - "node_modules/npm/node_modules/read-cmd-shim": { - "version": "3.0.0", - "inBundle": true, - "license": "ISC", + "node_modules/define-properties": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", + "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", + "dev": true, + "dependencies": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/npm/node_modules/read-package-json": { - "version": "5.0.2", - "inBundle": true, - "license": "ISC", + "node_modules/define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, "dependencies": { - "glob": "^8.0.1", - "json-parse-even-better-errors": "^2.3.1", - "normalize-package-data": "^4.0.0", - "npm-normalize-package-bin": "^2.0.0" + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/read-package-json-fast": { - "version": "2.0.3", - "inBundle": true, - "license": "ISC", - "dependencies": { - "json-parse-even-better-errors": "^2.3.0", - "npm-normalize-package-bin": "^1.0.1" - }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "engines": { - "node": ">=10" + "node": ">=0.4.0" } }, - "node_modules/npm/node_modules/read-package-json/node_modules/npm-normalize-package-bin": { + "node_modules/depd": { "version": "2.0.0", - "inBundle": true, - "license": "ISC", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">= 0.8" } }, - "node_modules/npm/node_modules/readable-stream": { - "version": "3.6.0", - "inBundle": true, - "license": "MIT", + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-indent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", + "integrity": "sha512-BDKtmHlOzwI7iRuEkhzsnPoi5ypEhWAJB5RvHWe1kMr06js3uK5B3734i3ui5Yd+wOJV1cpE4JnivPD283GU/A==", + "dev": true, + "peer": true, "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "repeating": "^2.0.0" }, "engines": { - "node": ">= 6" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/readdir-scoped-modules": { - "version": "1.1.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "debuglog": "^1.0.1", - "dezalgo": "^1.0.0", - "graceful-fs": "^4.1.2", - "once": "^1.3.0" + "node_modules/detect-newline": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", + "integrity": "sha512-CwffZFvlJffUg9zZA0uqrjQayUTC8ob94pnr5sFwaVv3IOmkfUHcWH+jXaQK3askE51Cqe8/9Ql/0uXNwqZ8Zg==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/retry": { - "version": "0.12.0", - "inBundle": true, - "license": "MIT", + "node_modules/diff-match-patch": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz", + "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==" + }, + "node_modules/diff-sequences": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.9.0.tgz", + "integrity": "sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew==", + "dev": true, "engines": { - "node": ">= 4" + "node": ">= 6" } }, - "node_modules/npm/node_modules/rimraf": { - "version": "3.0.2", - "inBundle": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, + "node_modules/direction": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/direction/-/direction-1.0.4.tgz", + "integrity": "sha512-GYqKi1aH7PJXxdhTeZBFrg8vUBeKXi+cNprXsC1kpJcbcVnV9wBsrOu1cQEdG0WeQwlfHiy3XvnKfIrJ2R0NzQ==", "bin": { - "rimraf": "bin.js" + "direction": "cli.js" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/npm/node_modules/rimraf/node_modules/brace-expansion": { - "version": "1.1.11", - "inBundle": true, - "license": "MIT", + "node_modules/dom-serializer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", + "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "domelementtype": "^1.3.0", + "entities": "^1.1.1" } }, - "node_modules/npm/node_modules/rimraf/node_modules/glob": { - "version": "7.2.3", - "inBundle": true, - "license": "ISC", + "node_modules/domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + }, + "node_modules/domexception": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", + "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", + "dev": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "webidl-conversions": "^4.0.2" } }, - "node_modules/npm/node_modules/rimraf/node_modules/minimatch": { - "version": "3.1.2", - "inBundle": true, - "license": "ISC", + "node_modules/domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" + "domelementtype": "1" } }, - "node_modules/npm/node_modules/safe-buffer": { - "version": "5.2.1", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/safer-buffer": { - "version": "2.1.2", - "inBundle": true, - "license": "MIT", - "optional": true + "node_modules/dompurify": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.5.tgz", + "integrity": "sha512-jggCCd+8Iqp4Tsz0nIvpcb22InKEBrGz5dw3EQJMs8HPJDsKbFIO3STYtAvCfDx26Muevn1MHVI0XxjgFfmiSA==" }, - "node_modules/npm/node_modules/semver": { - "version": "7.3.7", - "inBundle": true, - "license": "ISC", + "node_modules/domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha512-gSu5Oi/I+3wDENBsOWBiRK1eoGxcywYSqg3rR960/+EfY0CF4EX1VPkgHOZ3WiS/Jg2DtliF6BhWcHlfpYUcGw==", "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" + "dom-serializer": "0", + "domelementtype": "1" } }, - "node_modules/npm/node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "inBundle": true, - "license": "ISC", + "node_modules/dot-prop": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.1.tgz", + "integrity": "sha512-l0p4+mIuJIua0mhxGoh4a+iNL9bmeK5DvnSVQa6T0OhrVmaEa1XScX5Etc673FePCJOArq/4Pa2cLGODUWTPOQ==", + "dev": true, "dependencies": { - "yallist": "^4.0.0" + "is-obj": "^1.0.0" }, "engines": { - "node": ">=10" + "node": ">=4" } }, - "node_modules/npm/node_modules/set-blocking": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/signal-exit": { - "version": "3.0.7", - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/smart-buffer": { - "version": "4.2.0", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } + "node_modules/double-ended-queue": { + "version": "2.1.0-0", + "resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz", + "integrity": "sha512-+BNfZ+deCo8hMNpDqDnvT+c0XpJ5cUa6mqYq89bho2Ifze4URTqRkcwR399hWoTrTkbZ/XJYDgP6rc7pRgffEQ==" }, - "node_modules/npm/node_modules/socks": { - "version": "2.7.0", - "inBundle": true, - "license": "MIT", + "node_modules/draggabilly": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/draggabilly/-/draggabilly-3.0.0.tgz", + "integrity": "sha512-aEs+B6prbMZQMxc9lgTpCBfyCUhRur/VFucHhIOvlvvdARTj7TcDmX/cdOUtqbjJJUh7+agyJXR5Z6IFe1MxwQ==", "dependencies": { - "ip": "^2.0.0", - "smart-buffer": "^4.2.0" - }, - "engines": { - "node": ">= 10.13.0", - "npm": ">= 3.0.0" + "get-size": "^3.0.0", + "unidragger": "^3.0.0" } }, - "node_modules/npm/node_modules/socks-proxy-agent": { - "version": "7.0.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "agent-base": "^6.0.2", - "debug": "^4.3.3", - "socks": "^2.6.2" - }, - "engines": { - "node": ">= 10" - } + "node_modules/duplexer3": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz", + "integrity": "sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==", + "dev": true }, - "node_modules/npm/node_modules/spdx-correct": { - "version": "3.1.1", - "inBundle": true, - "license": "Apache-2.0", + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" } }, - "node_modules/npm/node_modules/spdx-exceptions": { - "version": "2.3.0", - "inBundle": true, - "license": "CC-BY-3.0" + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/electron-to-chromium": { + "version": "1.4.368", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.368.tgz", + "integrity": "sha512-e2aeCAixCj9M7nJxdB/wDjO6mbYX+lJJxSJCXDzlr5YPGYVofuJwGN9nKg2o6wWInjX6XmxRinn3AeJMK81ltw==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true }, - "node_modules/npm/node_modules/spdx-expression-parse": { - "version": "3.0.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true, + "engines": { + "node": ">= 4" } }, - "node_modules/npm/node_modules/spdx-license-ids": { - "version": "3.0.11", - "inBundle": true, - "license": "CC0-1.0" + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" }, - "node_modules/npm/node_modules/ssri": { - "version": "9.0.1", - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.1.1" - }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">= 0.8" } }, - "node_modules/npm/node_modules/string_decoder": { - "version": "1.3.0", - "inBundle": true, - "license": "MIT", + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, "dependencies": { - "safe-buffer": "~5.2.0" + "once": "^1.4.0" } }, - "node_modules/npm/node_modules/string-width": { - "version": "4.2.3", - "inBundle": true, - "license": "MIT", + "node_modules/engine.io-client": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.4.0.tgz", + "integrity": "sha512-GyKPDyoEha+XZ7iEqam49vz6auPnNJ9ZBfy89f+rMMas8AuiMWOZ9PVzu8xb9ZC6rafUqiGHSCfu22ih66E+1g==", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.3", + "ws": "~8.11.0", + "xmlhttprequest-ssl": "~2.0.0" } }, - "node_modules/npm/node_modules/strip-ansi": { - "version": "6.0.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, + "node_modules/engine.io-client/node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/supports-color": { - "version": "7.2.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" + "node": ">=10.0.0" }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/tar": { - "version": "6.1.11", - "inBundle": true, - "license": "ISC", - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" }, - "engines": { - "node": ">= 10" + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } } }, - "node_modules/npm/node_modules/text-table": { - "version": "0.2.0", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/tiny-relative-date": { - "version": "1.3.0", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/treeverse": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC", + "node_modules/engine.io-parser": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz", + "integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==", "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=10.0.0" } }, - "node_modules/npm/node_modules/unique-filename": { - "version": "2.0.1", - "inBundle": true, - "license": "ISC", + "node_modules/enhanced-resolve": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.13.0.tgz", + "integrity": "sha512-eyV8f0y1+bzyfh8xAwW/WTSZpLbjhqc4ne9eGSH4Zo2ejdyiNG9pU6mf9DG8a7+Auk6MFTlNOT4Y2y/9k8GKVg==", + "dev": true, "dependencies": { - "unique-slug": "^3.0.0" + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=10.13.0" } }, - "node_modules/npm/node_modules/unique-slug": { - "version": "3.0.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4" + "node_modules/entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" + }, + "node_modules/envinfo": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", + "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", + "dev": true, + "bin": { + "envinfo": "dist/cli.js" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=4" } }, - "node_modules/npm/node_modules/util-deprecate": { - "version": "1.0.2", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/validate-npm-package-license": { - "version": "3.0.4", - "inBundle": true, - "license": "Apache-2.0", + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" + "is-arrayish": "^0.2.1" } }, - "node_modules/npm/node_modules/validate-npm-package-name": { - "version": "4.0.0", - "inBundle": true, - "license": "ISC", + "node_modules/es-abstract": { + "version": "1.21.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz", + "integrity": "sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==", + "dev": true, "dependencies": { - "builtins": "^5.0.0" + "array-buffer-byte-length": "^1.0.0", + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-set-tostringtag": "^2.0.1", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.2.0", + "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.5", + "is-array-buffer": "^3.0.2", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.10", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "safe-regex-test": "^1.0.0", + "string.prototype.trim": "^1.2.7", + "string.prototype.trimend": "^1.0.6", + "string.prototype.trimstart": "^1.0.6", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.9" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/npm/node_modules/walk-up-path": { + "node_modules/es-array-method-boxes-properly": { "version": "1.0.0", - "inBundle": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", + "dev": true }, - "node_modules/npm/node_modules/wcwidth": { - "version": "1.0.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "defaults": "^1.0.3" - } + "node_modules/es-module-lexer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.2.1.tgz", + "integrity": "sha512-9978wrXM50Y4rTMmW5kXIC09ZdXQZqkE4mxhwkd8VbzsGkXGPgV4zWuqQJgCEzYngdo2dYDa0l8xhX4fkSwJSg==", + "dev": true }, - "node_modules/npm/node_modules/which": { - "version": "2.0.2", - "inBundle": true, - "license": "ISC", + "node_modules/es-set-tostringtag": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", + "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "dev": true, "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" + "get-intrinsic": "^1.1.3", + "has": "^1.0.3", + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">= 8" - } - }, - "node_modules/npm/node_modules/wide-align": { - "version": "1.1.5", - "inBundle": true, - "license": "ISC", - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" + "node": ">= 0.4" } }, - "node_modules/npm/node_modules/wrappy": { - "version": "1.0.2", - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/write-file-atomic": { - "version": "4.0.2", - "inBundle": true, - "license": "ISC", + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/yallist": { - "version": "4.0.0", - "inBundle": true, - "license": "ISC" - }, - "node_modules/nth-check": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", - "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", - "dependencies": { - "boolbase": "~1.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/nwsapi": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.4.tgz", - "integrity": "sha512-NHj4rzRo0tQdijE9ZqAx6kYDcoRwYwSYzCA8MY3JzfxlrvEU0jhnhJT9BhqhJs7I/dKcrDm6TyulaRqZPIhN5g==", - "dev": true - }, - "node_modules/oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, "engines": { - "node": "*" + "node": ">=6" } }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "engines": { - "node": ">=0.10.0" + "node": ">=0.8.0" } }, - "node_modules/object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==", + "node_modules/escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", "dev": true, "dependencies": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" }, "engines": { - "node": ">=0.10.0" + "node": ">=4.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" } }, - "node_modules/object-copy/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "node_modules/escodegen/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, + "optional": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/object-copy/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, "dependencies": { - "kind-of": "^3.0.2" + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=8.0.0" } }, - "node_modules/object-copy/node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true, - "dependencies": { - "kind-of": "^3.0.2" + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" }, "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "node_modules/object-copy/node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" + "estraverse": "^5.2.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=4.0" } }, - "node_modules/object-copy/node_modules/is-descriptor/node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=4.0" } }, - "node_modules/object-copy/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, "engines": { - "node": ">=0.10.0" + "node": ">=4.0" } }, - "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "engines": { - "node": ">= 0.4" + "node": ">= 0.6" } }, - "node_modules/object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==", + "node_modules/ev-emitter": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ev-emitter/-/ev-emitter-2.1.2.tgz", + "integrity": "sha512-jQ5Ql18hdCQ4qS+RCrbLfz1n+Pags27q5TwMKvZyhp5hh2UULUYZUy1keqj6k6SYsdqIYjnmz7xyyEY0V67B8Q==" + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true, - "dependencies": { - "isobject": "^3.0.0" - }, "engines": { - "node": ">=0.10.0" + "node": ">=0.8.x" } }, - "node_modules/object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "node_modules/exec-sh": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.6.tgz", + "integrity": "sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w==", + "dev": true + }, + "node_modules/execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=6" } }, - "node_modules/object.getownpropertydescriptors": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.5.tgz", - "integrity": "sha512-yDNzckpM6ntyQiGTik1fKV1DcVDRS+w8bvpWNCBanvH5LfRX9O8WTHqQzG4RZwRAM4I0oU7TV11Lj5v0g20ibw==", + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", "dev": true, - "dependencies": { - "array.prototype.reduce": "^1.0.5", - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, "engines": { - "node": ">= 0.8" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 0.8.0" } }, - "node_modules/object.omit": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", - "integrity": "sha512-UiAM5mhmIuKLsOvrL+B0U2d1hXHF3bFYWIuH1LMpuV2EJEHG1Ntz06PgLEHjm6VFd87NpH8rastvPoyv6UW2fA==", + "node_modules/expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==", "dev": true, "dependencies": { - "for-own": "^0.1.4", - "is-extendable": "^0.1.1" + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/object.omit/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "node_modules/expand-brackets/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, - "engines": { - "node": ">=0.10.0" + "dependencies": { + "ms": "2.0.0" } }, - "node_modules/object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", + "node_modules/expand-brackets/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", "dev": true, "dependencies": { - "isobject": "^3.0.1" + "is-descriptor": "^0.1.0" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "node_modules/expand-brackets/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, "dependencies": { - "ee-first": "1.1.1" + "is-extendable": "^0.1.0" }, "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": { - "wrappy": "1" + "node": ">=0.10.0" } }, - "node_modules/one-time": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", - "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "node_modules/expand-brackets/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", + "dev": true, "dependencies": { - "fn.name": "1.x.x" + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "peer": true, + "node_modules/expand-brackets/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, "dependencies": { - "mimic-fn": "^4.0.0" + "is-buffer": "^1.1.5" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/open": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", - "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "node_modules/expand-brackets/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", + "dev": true, "dependencies": { - "is-docker": "^2.0.0", - "is-wsl": "^2.1.1" + "kind-of": "^3.0.2" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/open/node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "node_modules/expand-brackets/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, "dependencies": { - "is-docker": "^2.0.0" + "is-buffer": "^1.1.5" }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "node_modules/expand-brackets/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, "dependencies": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" }, "engines": { - "node": ">= 0.8.0" + "node": ">=0.10.0" } }, - "node_modules/os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==", + "node_modules/expand-brackets/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", "dev": true, - "peer": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "node_modules/expand-brackets/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/p-each-series": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-1.0.0.tgz", - "integrity": "sha512-J/e9xiZZQNrt+958FFzJ+auItsBGq+UrQ7nE89AUP7UOTtjHnkISANXLdayhVzh538UnLMCSlf13lFfRIAKQOA==", + "node_modules/expand-brackets/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/expand-range": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "integrity": "sha512-AFASGfIlnIbkKPQwX1yHaDjFvh/1gyKJODme52V6IORh69uEYgZp0o9C+qsIGNVEiuuhQU0CSSl++Rlegg1qvA==", "dev": true, "dependencies": { - "p-reduce": "^1.0.0" + "fill-range": "^2.1.0" }, "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/p-filter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-2.1.0.tgz", - "integrity": "sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==", + "node_modules/expand-range/node_modules/fill-range": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", + "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", + "dev": true, "dependencies": { - "p-map": "^2.0.0" + "is-number": "^2.1.0", + "isobject": "^2.0.0", + "randomatic": "^3.0.0", + "repeat-element": "^1.1.2", + "repeat-string": "^1.5.2" }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "node_modules/expand-range/node_modules/is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha512-QUzH43Gfb9+5yckcrSA0VBDwEtDUchrk4F6tfJZQuNzDJbEDB9cZNzSfXGQ1jqmdDY/kl41lUOWM9syA8z8jlg==", "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/p-is-promise": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-3.0.0.tgz", - "integrity": "sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ==", + "node_modules/expand-range/node_modules/isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==", + "dev": true, + "dependencies": { + "isarray": "1.0.0" + }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "node_modules/expand-range/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, "dependencies": { - "p-try": "^2.0.0" + "is-buffer": "^1.1.5" }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "node_modules/expect": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-24.9.0.tgz", + "integrity": "sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q==", "dev": true, "dependencies": { - "p-limit": "^2.0.0" + "@jest/types": "^24.9.0", + "ansi-styles": "^3.2.0", + "jest-get-type": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-regex-util": "^24.9.0" }, "engines": { - "node": ">=6" + "node": ">= 6" } }, - "node_modules/p-map": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", - "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "node_modules/express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, "engines": { - "node": ">=6" + "node": ">= 0.10.0" } }, - "node_modules/p-reduce": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz", - "integrity": "sha512-3Tx1T3oM1xO/Y8Gj0sWyE78EIJZ+t+aEmXUdvQgvGmSMri7aPTHoovbXEreWKkL5j21Er60XAWLTzKbAKYOujQ==", - "dev": true, + "node_modules/express-winston": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/express-winston/-/express-winston-4.2.0.tgz", + "integrity": "sha512-EMD74g63nVHi7pFleQw7KHCxiA1pjF5uCwbCfzGqmFxs9KvlDPIVS3cMGpULm6MshExMT9TjC3SqmRGB9kb7yw==", + "dependencies": { + "chalk": "^2.4.2", + "lodash": "^4.17.21" + }, "engines": { - "node": ">=4" + "node": ">= 6" + }, + "peerDependencies": { + "winston": ">=3.x <4" } }, - "node_modules/p-retry": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", - "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "node_modules/express/node_modules/body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", "dependencies": { - "@types/retry": "0.12.0", - "retry": "^0.13.1" + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" }, "engines": { - "node": ">=8" + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "node_modules/express/node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", "engines": { - "node": ">=6" + "node": ">= 0.6" } }, - "node_modules/package-json": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", - "integrity": "sha512-q/R5GrMek0vzgoomq6rm9OX+3PQve8sLwTirmK30YB3Cu0Bbt9OX9M/SIUnroN5BGJkzwGsFwDaRGD9EwBOlCA==", - "dev": true, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dependencies": { - "got": "^6.7.1", - "registry-auth-token": "^3.0.1", - "registry-url": "^3.0.3", - "semver": "^5.1.0" - }, - "engines": { - "node": ">=4" + "ms": "2.0.0" } }, - "node_modules/package-json/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "peer": true, + "node_modules/express/node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", "dependencies": { - "callsites": "^3.0.0" + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" }, "engines": { - "node": ">=6" + "node": ">= 0.8" } }, - "node_modules/parse-entities": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", - "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, "dependencies": { - "character-entities": "^1.0.0", - "character-entities-legacy": "^1.0.0", - "character-reference-invalid": "^1.0.0", - "is-alphanumerical": "^1.0.0", - "is-decimal": "^1.0.0", - "is-hexadecimal": "^1.0.0" + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/parse-glob": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", - "integrity": "sha512-FC5TeK0AwXzq3tUBFtH74naWkPQCEWs4K+xMxWZBlKDWu0bVHXGZa+KKqxKidd7xwhdZ19ZNuF2uO1M/r196HA==", + "node_modules/extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", "dev": true, "dependencies": { - "glob-base": "^0.3.0", - "is-dotfile": "^1.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.0" + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/parse-glob/node_modules/is-extglob": { + "node_modules/extglob/node_modules/define-property": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww==", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, "engines": { "node": ">=0.10.0" } }, - "node_modules/parse-glob/node_modules/is-glob": { + "node_modules/extglob/node_modules/extend-shallow": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg==", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", "dev": true, "dependencies": { - "is-extglob": "^1.0.0" + "is-extendable": "^0.1.0" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", - "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - }, + "node_modules/extglob/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/parse-srcset": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz", - "integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==" + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "engines": [ + "node >=0.6.0" + ] }, - "node_modules/parse5": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", - "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", - "dev": true + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "engines": { - "node": ">= 0.8" - } + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, - "node_modules/pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true }, - "node_modules/patch-package": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-6.5.1.tgz", - "integrity": "sha512-I/4Zsalfhc6bphmJTlrLoOcAF87jcxko4q0qsv4bGcurbr8IskEOtdnt9iCmsQVGL1B+iUhSQqweyTLJfCF9rA==", - "dependencies": { - "@yarnpkg/lockfile": "^1.1.0", - "chalk": "^4.1.2", - "cross-spawn": "^6.0.5", - "find-yarn-workspace-root": "^2.0.0", - "fs-extra": "^9.0.0", - "is-ci": "^2.0.0", - "klaw-sync": "^6.0.0", - "minimist": "^1.2.6", - "open": "^7.4.2", - "rimraf": "^2.6.3", - "semver": "^5.6.0", - "slash": "^2.0.0", - "tmp": "^0.0.33", - "yaml": "^1.10.2" - }, - "bin": { - "patch-package": "index.js" - }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true, "engines": { - "node": ">=10", - "npm": ">5" + "node": ">= 4.9.1" } }, - "node_modules/patch-package/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } + "node_modules/fastparse": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", + "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==" }, - "node_modules/patch-package/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "bser": "2.1.1" } }, - "node_modules/patch-package/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" }, - "node_modules/patch-package/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "node_modules/file-saver": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz", + "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==" }, - "node_modules/patch-package/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "optional": true }, - "node_modules/patch-package/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "bin": { - "semver": "bin/semver" + "node_modules/filename-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "integrity": "sha512-BTCqyBaWBTsauvnHiE8i562+EdJj+oUpkqWp2R1iCoR8f6oo8STRu3of7WJJ0TqWtxN50a5YFpzYK4Jj9esYfQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/patch-package/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dependencies": { - "has-flag": "^4.0.0" + "to-regex-range": "^5.0.1" }, "engines": { "node": ">=8" } }, - "node_modules/path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==", - "dev": true - }, - "node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, "engines": { - "node": ">=4" + "node": ">= 0.8" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "engines": { - "node": ">=0.10.0" + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" } }, - "node_modules/path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", - "dev": true + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, "engines": { - "node": ">=4" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" } }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" - }, - "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" - }, - "node_modules/path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "node_modules/find-cache-dir/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", "dev": true, "dependencies": { - "pify": "^3.0.0" + "semver": "^6.0.0" }, "engines": { - "node": ">=4" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/path-type/node_modules/pify": { + "node_modules/find-up": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, + "dependencies": { + "locate-path": "^3.0.0" + }, "engines": { - "node": ">=4" + "node": ">=6" } }, - "node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" - }, - "node_modules/picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" + "node_modules/find-yarn-workspace-root": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz", + "integrity": "sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ==", + "dependencies": { + "micromatch": "^4.0.2" + } }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "node_modules/find-yarn-workspace-root/node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, "engines": { "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "node_modules/findandreplacedomtext": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/findandreplacedomtext/-/findandreplacedomtext-0.4.6.tgz", + "integrity": "sha512-CVRIKbEwfWoKTSnLrmyX26WjMY7o0aUUTm0RXN47fF/eHINJa8C+YHZxGagC7gMWVaAEBFwH7uVXuhwZcylWOQ==" + }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", "dev": true, - "engines": { - "node": ">=6" + "dependencies": { + "is-callable": "^1.1.3" } }, - "node_modules/pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "node_modules/for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==", "dev": true, "engines": { - "node": ">= 6" + "node": ">=0.10.0" } }, - "node_modules/pkg-conf": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", - "integrity": "sha512-C+VUP+8jis7EsQZIhDYmS5qlNtjv2yP4SNtjXK9AP1ZcTRlnSfuumaTnRfYZnYgUUYVIKqL0fRvmUGDV2fmp6g==", - "peer": true, + "node_modules/for-own": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha512-SKmowqGTJoPzLO1T0BBJpkfp3EMacCMOuH40hOUbrbzElVktk4DioXVM99QkLCyKoiuOmyjgcWMpVz2xjE7LZw==", + "dev": true, "dependencies": { - "find-up": "^2.0.0", - "load-json-file": "^4.0.0" + "for-in": "^1.0.1" }, "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/pkg-conf/node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", - "peer": true, - "dependencies": { - "locate-path": "^2.0.0" - }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", "engines": { - "node": ">=4" + "node": "*" } }, - "node_modules/pkg-conf/node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", - "peer": true, + "node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" }, "engines": { - "node": ">=4" + "node": ">= 0.12" } }, - "node_modules/pkg-conf/node_modules/p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "peer": true, - "dependencies": { - "p-try": "^1.0.0" - }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "engines": { - "node": ">=4" + "node": ">= 0.6" } }, - "node_modules/pkg-conf/node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", - "peer": true, + "node_modules/fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==", + "dev": true, "dependencies": { - "p-limit": "^1.1.0" + "map-cache": "^0.2.2" }, "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/pkg-conf/node_modules/p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", - "peer": true, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "engines": { - "node": ">=4" + "node": ">= 0.6" } }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, + "node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dependencies": { - "find-up": "^4.0.0" + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" } }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "node_modules/fs-readdir-recursive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", + "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=8" + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", "dev": true, "dependencies": { - "p-locate": "^4.1.0" + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/generic-names": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/generic-names/-/generic-names-1.0.3.tgz", + "integrity": "sha512-b6OHfQuKasIKM9b6YPkX+KUj/TLBTx3B/1aT1T5F12FEuEqyFMdr59OMS53aoaSw8eVtapdqieX6lbg5opaOhA==", "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" + "loader-utils": "^0.2.16" } }, - "node_modules/pkg-dir/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, + "node_modules/generic-names/node_modules/big.js": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", + "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", "engines": { - "node": ">=8" + "node": "*" } }, - "node_modules/pn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", - "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", - "dev": true - }, - "node_modules/posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==", - "dev": true, + "node_modules/generic-names/node_modules/emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha512-knHEZMgs8BB+MInokmNTg/OyPlAddghe1YBgNwJBc5zsJi/uyIcXoSDsL/W9ymOsBoBGdPIHXYJ9+qKFwRwDng==", "engines": { - "node": ">=0.10.0" + "node": ">= 0.10" } }, - "node_modules/postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "node_modules/generic-names/node_modules/json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha512-4xrs1aW+6N5DalkqSVA8fxh458CXvR99WU8WLKmq4v8eWAL86Xo3BVqyd3SkA9wEVjCMqyvvRRkshAdOnBp5rw==", + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/generic-names/node_modules/loader-utils": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", + "integrity": "sha512-tiv66G0SmiOx+pLWMtGEkfSEejxvb6N6uRrQjfWJIT79W9GMpgKeCAmm9aVBKtd4WEgntciI8CsGqjpDoCWJug==", "dependencies": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0", + "object-assign": "^4.0.1" } }, - "node_modules/postcss-modules-extract-imports": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz", - "integrity": "sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==", + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, - "dependencies": { - "postcss": "^7.0.5" - }, "engines": { - "node": ">= 6" + "node": ">=6.9.0" } }, - "node_modules/postcss-modules-local-by-default": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.3.tgz", - "integrity": "sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw==", + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, - "dependencies": { - "icss-utils": "^4.1.1", - "postcss": "^7.0.32", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.1.0" - }, "engines": { - "node": ">= 6" + "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/postcss-modules-scope": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz", - "integrity": "sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ==", - "dev": true, + "node_modules/get-intrinsic": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", "dependencies": { - "postcss": "^7.0.6", - "postcss-selector-parser": "^6.0.0" + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" }, - "engines": { - "node": ">= 6" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/postcss-modules-values": { + "node_modules/get-size": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz", - "integrity": "sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg==", + "resolved": "https://registry.npmjs.org/get-size/-/get-size-3.0.0.tgz", + "integrity": "sha512-Y8aiXLq4leR7807UY0yuKEwif5s3kbVp1nTv+i4jBeoUzByTLKkLWu/HorS6/pB+7gsB0o7OTogC8AoOOeT0Hw==" + }, + "node_modules/get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", "dev": true, "dependencies": { - "icss-utils": "^4.0.0", - "postcss": "^7.0.6" + "pump": "^3.0.0" + }, + "engines": { + "node": ">=6" } }, - "node_modules/postcss-selector-parser": { - "version": "6.0.11", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz", - "integrity": "sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==", + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", "dev": true, "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" }, "engines": { - "node": ">=4" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true - }, - "node_modules/postcss/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==", + "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", - "dev": true, - "engines": { - "node": ">= 0.8.0" + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "dependencies": { + "assert-plus": "^1.0.0" } }, - "node_modules/prepend-http": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha512-PhmXi5XmoyKw1Un4E+opM2KcsJInDvKyuOumcjjw3waw86ZNjHwVUOOWLc4bCzLdcKNaWBH9e99sbWzDQsVaYg==", - "dev": true, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/preserve": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", - "integrity": "sha512-s/46sYeylUfHNjI+sA/78FAHlmIuKqI9wNnzEOGehAlUUYeObv5C2mOinXBjyUyWmJ2SfcS2/ydApH4hTF4WXQ==", + "node_modules/glob-base": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "integrity": "sha512-ab1S1g1EbO7YzauaJLkgLp7DZVAqj9M/dvKlTt8DkXA2tiOIcSMrlVI2J1RZyB5iJVccEscjGn+kpOG9788MHA==", "dev": true, + "dependencies": { + "glob-parent": "^2.0.0", + "is-glob": "^2.0.0" + }, "engines": { "node": ">=0.10.0" } }, - "node_modules/pretty-format": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", - "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "node_modules/glob-base/node_modules/glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha512-JDYOvfxio/t42HKdxkAYaCiBN7oYiuxykOxKxdaUW5Qn0zaYN3gRQWolrwdnf0shM9/EP0ebuuTmyoXNr1cC5w==", "dev": true, "dependencies": { - "@jest/types": "^24.9.0", - "ansi-regex": "^4.0.0", - "ansi-styles": "^3.2.0", - "react-is": "^16.8.4" - }, - "engines": { - "node": ">= 6" + "is-glob": "^2.0.0" } }, - "node_modules/private": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", - "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", + "node_modules/glob-base/node_modules/is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww==", "dev": true, - "peer": true, "engines": { - "node": ">= 0.6" + "node": ">=0.10.0" } }, - "node_modules/process-nextick-args": { + "node_modules/glob-base/node_modules/is-glob": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg==", "dev": true, "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" + "is-extglob": "^1.0.0" }, "engines": { - "node": ">= 6" - } - }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" + "node": ">=0.10.0" } }, - "node_modules/property-information": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz", - "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==", + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "optional": true, "dependencies": { - "xtend": "^4.0.0" + "is-glob": "^4.0.1" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/property-information/node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", "engines": { - "node": ">=0.4" + "node": ">= 6" } }, - "node_modules/proto-list": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", - "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==" + "node_modules/glob-to-regexp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz", + "integrity": "sha512-Iozmtbqv0noj0uDDqoL0zNq0VBEfK2YFoMAZoxJe4cwphvLR+JskfF30QhXHOR4m3KrE6NLRYw+U9MRXvifyig==" }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "node_modules/global-dirs": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", + "integrity": "sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==", + "dev": true, "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" + "ini": "^1.3.4" }, "engines": { - "node": ">= 0.10" + "node": ">=4" } }, - "node_modules/pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", - "dev": true - }, - "node_modules/psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" - }, - "node_modules/pstree.remy": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", - "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", - "dev": true - }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true, - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", "engines": { - "node": ">=0.6.0", - "teleport": ">=0.2.0" + "node": ">=4" } }, - "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dev": true, "dependencies": { - "side-channel": "^1.0.4" + "define-properties": "^1.1.3" }, "engines": { - "node": ">=0.6" + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/querystring": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.1.tgz", - "integrity": "sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==", - "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", - "engines": { - "node": ">=0.4.x" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/quick-lru": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", - "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", - "engines": { - "node": ">=8" + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/randomatic": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", - "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", + "node_modules/got": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", + "integrity": "sha512-Y/K3EDuiQN9rTZhBvPRWMLXIKdeD1Rj0nzunfoi0Yyn5WBEbzxXKU9Ub2X41oZBagVWOBU3MuDonFMgPWQFnwg==", "dev": true, "dependencies": { - "is-number": "^4.0.0", - "kind-of": "^6.0.0", - "math-random": "^1.0.1" + "create-error-class": "^3.0.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "is-redirect": "^1.0.0", + "is-retry-allowed": "^1.0.0", + "is-stream": "^1.0.0", + "lowercase-keys": "^1.0.0", + "safe-buffer": "^5.0.1", + "timed-out": "^4.0.0", + "unzip-response": "^2.0.1", + "url-parse-lax": "^1.0.0" }, "engines": { - "node": ">= 0.10.0" + "node": ">=4" } }, - "node_modules/randomatic/node_modules/is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "node_modules/got/node_modules/get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "dependencies": { - "safe-buffer": "^5.1.0" - } + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "node_modules/growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw==", + "dev": true + }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", "engines": { - "node": ">= 0.6" + "node": ">=4" } }, - "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" + "ajv": "^6.12.3", + "har-schema": "^2.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">=6" } }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" + "function-bind": "^1.1.1" }, - "bin": { - "rc": "cli.js" + "engines": { + "node": ">= 0.4.0" } }, - "node_modules/react": { - "version": "16.14.0", - "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", - "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", + "node_modules/has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", + "dev": true, + "peer": true, "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2" + "ansi-regex": "^2.0.0" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/react-class": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/react-class/-/react-class-3.2.2.tgz", - "integrity": "sha512-6mGkYJjqsCyazXfIIjAe3vf+KMsSTgJnyj7UK7tgFYIfv2lBQT9MB7rs8DQxESPNhBDu6UZ1jnTLpLzA2wKn6w==", - "dependencies": { - "object-assign": "^4.1.0" - }, - "peerDependencies": { - "react": ">=0.14.0 || >=15.0.0-0", - "react-dom": ">=0.14.0 || >=15.0.0-0" + "node_modules/has-ansi/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/react-dom": { - "version": "16.14.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz", - "integrity": "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==", - "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2", - "scheduler": "^0.19.1" - }, - "peerDependencies": { - "react": "^16.14.0" + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/react-image-crop": { - "version": "8.6.12", - "resolved": "https://registry.npmjs.org/react-image-crop/-/react-image-crop-8.6.12.tgz", - "integrity": "sha512-3CNz1xfsRRSH/iH023IDMXxzsb1M6RTHHUVsVcb8uFPcjGiA9WisvQ24G1eRDf2j4NlybupOEEdfK2vT0etN6A==", - "dependencies": { - "clsx": "^1.1.1", - "prop-types": "^15.7.2" - }, - "peerDependencies": { - "react": ">=16.13.1" + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" } }, - "node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, - "node_modules/react-jsonschema-form": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/react-jsonschema-form/-/react-jsonschema-form-1.8.1.tgz", - "integrity": "sha512-aaDloxNAcGXOOOcdKOxxqEEn5oDlPUZgWcs8unXXB9vjBRgCF8rCm/wVSv1u2G5ih0j/BX6Ewd/WjI2g00lPdg==", - "deprecated": "react-jsonschema-form has been moved to @rjsf/core", + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dev": true, "dependencies": { - "@babel/runtime-corejs2": "^7.4.5", - "ajv": "^6.7.0", - "core-js": "^2.5.7", - "lodash": "^4.17.15", - "prop-types": "^15.5.8", - "react-is": "^16.8.4", - "react-lifecycles-compat": "^3.0.4", - "shortid": "^2.2.14" + "get-intrinsic": "^1.1.1" }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "dev": true, "engines": { - "node": ">=6", - "npm": ">=2.14.7" + "node": ">= 0.4" }, - "peerDependencies": { - "react": ">=15" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/react-jsonschema-form/node_modules/core-js": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", - "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", - "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", - "hasInstallScript": true - }, - "node_modules/react-lifecycles-compat": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", - "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" - }, - "node_modules/react-markdown": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-6.0.3.tgz", - "integrity": "sha512-kQbpWiMoBHnj9myLlmZG9T1JdoT/OEyHK7hqM6CqFT14MAkgWiWBUYijLyBmxbntaN6dCDicPcUhWhci1QYodg==", - "dependencies": { - "@types/hast": "^2.0.0", - "@types/unist": "^2.0.3", - "comma-separated-tokens": "^1.0.0", - "prop-types": "^15.7.2", - "property-information": "^5.3.0", - "react-is": "^17.0.0", - "remark-parse": "^9.0.0", - "remark-rehype": "^8.0.0", - "space-separated-tokens": "^1.1.0", - "style-to-object": "^0.3.0", - "unified": "^9.0.0", - "unist-util-visit": "^2.0.0", - "vfile": "^4.0.0" + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - }, - "peerDependencies": { - "@types/react": ">=16", - "react": ">=16" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/react-markdown/node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" - }, - "node_modules/react-tag-autocomplete": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/react-tag-autocomplete/-/react-tag-autocomplete-6.3.0.tgz", - "integrity": "sha512-MUBVUFh5eOqshUm5NM20qp7zXk8TzSiKO4GoktlFzBLIOLs356npaMKtL02bm0nFV2f1zInUrXn1fq6+i5YX0w==", + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, "engines": { - "node": ">= 10.0.0" + "node": ">= 0.4" }, - "peerDependencies": { - "prop-types": "^15.5.0", - "react": "^16.5.0 || ^17.0.0", - "react-dom": "^16.5.0 || ^17.0.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==", + "dev": true, + "dependencies": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "node_modules/has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==", "dev": true, "dependencies": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" + "is-number": "^3.0.0", + "kind-of": "^4.0.0" }, "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/read-pkg-up": { + "node_modules/has-values/node_modules/kind-of": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", - "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==", "dev": true, "dependencies": { - "find-up": "^3.0.0", - "read-pkg": "^3.0.0" + "is-buffer": "^1.1.5" }, "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" + "react-is": "^16.7.0" } }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "node_modules/home-or-tmp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", + "integrity": "sha512-ycURW7oUxE2sNiPVw1HVEFsW+ecOpJ5zaj7eC0RlwhibhRBod20muUN8qu/gzx956YrLolVvs1MTXwKgC2rVEg==", "dev": true, - "optional": true, + "peer": true, "dependencies": { - "picomatch": "^2.2.1" + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.1" }, "engines": { - "node": ">=8.10.0" + "node": ">=0.10.0" } }, - "node_modules/realpath-native": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.1.0.tgz", - "integrity": "sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA==", + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/html-encoding-sniffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", + "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", "dev": true, "dependencies": { - "util.promisify": "^1.0.0" - }, - "engines": { - "node": ">=4" + "whatwg-encoding": "^1.0.1" } }, - "node_modules/rechoir": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", - "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", - "dev": true, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", "dependencies": { - "resolve": "^1.9.0" - }, - "engines": { - "node": ">= 0.10" + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" } }, - "node_modules/redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "dependencies": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" }, "engines": { - "node": ">=8" + "node": ">= 0.8" } }, - "node_modules/redeyed": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-2.1.1.tgz", - "integrity": "sha512-FNpGGo1DycYAdnrKFxCMmKYgo/mILAqtRYbkdQD8Ep/Hk2PQ5+aEAEx+IU713RTDmuBaH0c8P5ZozurNu5ObRQ==", - "peer": true, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", "dependencies": { - "esprima": "~4.0.0" + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" } }, - "node_modules/redis": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/redis/-/redis-2.8.0.tgz", - "integrity": "sha512-M1OkonEQwtRmZv4tEWF2VgpG0JWJ8Fv1PhlgT5+B+uNq2cA3Rt1Yt/ryoR+vQNOQcIEgdCdfH0jr3bDpihAw1A==", + "node_modules/humanize-duration": { + "version": "3.28.0", + "resolved": "https://registry.npmjs.org/humanize-duration/-/humanize-duration-3.28.0.tgz", + "integrity": "sha512-jMAxraOOmHuPbffLVDKkEKi/NeG8dMqP8lGRd6Tbf7JgAeG33jjgPWDbXXU7ypCI0o+oNKJFgbSB9FKVdWNI2A==" + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dependencies": { - "double-ended-queue": "^2.1.0-0", - "redis-commands": "^1.2.0", - "redis-parser": "^2.6.0" + "safer-buffer": ">= 2.1.2 < 3" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/redis-commands": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", - "integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==" + "node_modules/icss-replace-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", + "integrity": "sha512-chIaY3Vh2mh2Q3RGXttaDIzeiPvaVXJ+C4DAh/w3c37SKZ/U6PGMmuicR2EQQp9bKG8zLMCl7I+PtIoOOPp8Gg==" }, - "node_modules/redis-parser": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-2.6.0.tgz", - "integrity": "sha512-9Hdw19gwXFBJdN8ENUoNVJFRyMDFrE/ZBClPicKYDPwNPJ4ST1TedAHYNSiGKElwh2vrmRGMoJYbVdJd+WQXIw==", + "node_modules/icss-utils": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.1.tgz", + "integrity": "sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==", + "dev": true, + "dependencies": { + "postcss": "^7.0.14" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 6" } }, - "node_modules/regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", "dev": true }, - "node_modules/regenerate-unicode-properties": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", - "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", + "node_modules/immer": { + "version": "9.0.21", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", + "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha512-m7ZEHgtw69qOGw+jwxXkHlrlIPdTGkyh66zXZ1ajZbxkDBNjSY/LGbmjc7h0s2ELsUDTAhFr55TrPSSqJGPG0A==", "dev": true, - "dependencies": { - "regenerate": "^1.4.2" - }, "engines": { "node": ">=4" } }, - "node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + "node_modules/import-local": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "dev": true, + "dependencies": { + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=6" + } }, - "node_modules/regenerator-transform": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz", - "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==", + "node_modules/import-local/node_modules/pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", "dev": true, "dependencies": { - "@babel/runtime": "^7.8.4" + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=6" } }, - "node_modules/regex-cache": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", - "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, - "dependencies": { - "is-equal-shallow": "^0.1.3" - }, "engines": { - "node": ">=0.10.0" + "node": ">=0.8.19" } }, - "node_modules/regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dev": true, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dependencies": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" + "once": "^1.3.0", + "wrappy": "1" } }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", - "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "node_modules/inline-style-parser": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", + "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==" + }, + "node_modules/internal-slot": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", + "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "functions-have-names": "^1.2.3" + "get-intrinsic": "^1.2.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" }, "engines": { "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/regexpu-core": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", - "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", + "node_modules/interpret": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", + "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", "dev": true, - "dependencies": { - "@babel/regjsgen": "^0.8.0", - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.1.0", - "regjsparser": "^0.9.1", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" - }, "engines": { - "node": ">=4" + "node": ">= 0.10" } }, - "node_modules/registry-auth-token": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz", - "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==", + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", "dev": true, "dependencies": { - "rc": "^1.1.6", - "safe-buffer": "^5.0.1" + "loose-envify": "^1.0.0" } }, - "node_modules/registry-url": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", - "integrity": "sha512-ZbgR5aZEdf4UKZVBPYIgaglBmSF2Hi94s2PcIHhRGFjKYu+chjJdYfHn4rt3hB6eCKLJ8giVIIfgMa1ehDfZKA==", - "dev": true, - "dependencies": { - "rc": "^1.0.1" - }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "engines": { - "node": ">=0.10.0" + "node": ">= 0.10" } }, - "node_modules/regjsparser": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", - "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "dependencies": { - "jsesc": "~0.5.0" + "kind-of": "^6.0.0" }, - "bin": { - "regjsparser": "bin/parser" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" + "node_modules/is-alphabetical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", + "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/remark-parse": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-9.0.0.tgz", - "integrity": "sha512-geKatMwSzEXKHuzBNU1z676sGcDcFoChMK38TgdHJNAYfFtsfHDQG7MoJAjs6sgYMqyLduCYWDIWZIxiPeafEw==", + "node_modules/is-alphanumerical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", + "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", "dependencies": { - "mdast-util-from-markdown": "^0.8.0" + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/remark-rehype": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-8.1.0.tgz", - "integrity": "sha512-EbCu9kHgAxKmW1yEYjx3QafMyGY3q8noUbNUI5xyKbaFP89wbhDrKxyIQNukNYthzjNHZu6J7hwFg7hRm1svYA==", + "node_modules/is-array-buffer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "dev": true, "dependencies": { - "mdast-util-to-hast": "^10.2.0" + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "is-typed-array": "^1.1.10" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true }, - "node_modules/repeat-element": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", - "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", - "dev": true, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/repeating": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", - "integrity": "sha512-ZqtSMuVybkISo2OWvqvm7iHSWngvdaW3IpsT9/uP8v4gMi591LY6h35wdOfvQdWCKFWZWm2Y1Opp4kV7vQKT6A==", + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", "dev": true, - "peer": true, - "dependencies": { - "is-finite": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", "dependencies": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" + "has-bigints": "^1.0.1" }, - "engines": { - "node": ">= 6" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/request-promise-core": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", - "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, + "optional": true, "dependencies": { - "lodash": "^4.17.19" + "binary-extensions": "^2.0.0" }, "engines": { - "node": ">=0.10.0" - }, - "peerDependencies": { - "request": "^2.34" + "node": ">=8" } }, - "node_modules/request-promise-native": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz", - "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==", - "deprecated": "request-promise-native has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142", + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", "dev": true, "dependencies": { - "request-promise-core": "1.1.4", - "stealthy-require": "^1.1.1", - "tough-cookie": "^2.3.3" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">=0.12.0" + "node": ">= 0.4" }, - "peerDependencies": { - "request": "^2.34" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/request/node_modules/qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", - "engines": { - "node": ">=0.6" - } + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/require-main-filename": { + "node_modules/is-ci": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "node_modules/resolve": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", - "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", "dependencies": { - "is-core-module": "^2.11.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" + "ci-info": "^2.0.0" }, "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "is-ci": "bin.js" } }, - "node_modules/resolve-cwd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", - "integrity": "sha512-ccu8zQTrzVr954472aUVPLEcB3YpKSYR3cg/3lo1okzobPBM+1INXBbBZlDbnI/hbEocnf8j0QVo43hQKrbchg==", + "node_modules/is-core-module": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.0.tgz", + "integrity": "sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==", "dev": true, "dependencies": { - "resolve-from": "^3.0.0" + "has": "^1.0.3" }, - "engines": { - "node": ">=4" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", + "node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==", - "deprecated": "https://github.com/lydell/resolve-url#deprecated", - "dev": true - }, - "node_modules/ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, "engines": { - "node": ">=0.12" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/retry": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", - "engines": { - "node": ">= 4" + "node_modules/is-decimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", + "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, "engines": { - "iojs": ">=1.0.0", "node": ">=0.10.0" } }, - "node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dependencies": { - "glob": "^7.1.3" - }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", "bin": { - "rimraf": "bin.js" + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/rsvp": { - "version": "4.8.5", - "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", - "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", + "node_modules/is-dotfile": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "integrity": "sha512-9YclgOGtN/f8zx0Pr4FQYMdibBiTaH3sn52vjYip4ZSf6C4/6RfTEZ+MR4GvKhCxdPh21Bg42/WL55f6KSnKpg==", "dev": true, "engines": { - "node": "6.* || >= 7.*" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" + "node": ">=0.10.0" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==", + "node_modules/is-equal-shallow": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", + "integrity": "sha512-0EygVC5qPvIyb+gSz7zdD5/AAoS6Qrx1e//6N4yv4oNm30kqvdmG66oZFWVlQHUWe5OjP08FuTw2IdT0EOTcYA==", "dev": true, "dependencies": { - "ret": "~0.1.10" + "is-primitive": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-regex": "^1.1.4" + "is-plain-object": "^2.0.4" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/safe-stable-stringify": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", - "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, "engines": { - "node": ">=10" + "node": ">=0.10.0" } }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/sane": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", - "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", - "deprecated": "some dependency vulnerabilities fixed, support for node < 10 dropped, and newer ECMAScript syntax/features added", - "dev": true, - "dependencies": { - "@cnakazawa/watch": "^1.0.3", - "anymatch": "^2.0.0", - "capture-exit": "^2.0.0", - "exec-sh": "^0.3.2", - "execa": "^1.0.0", - "fb-watchman": "^2.0.0", - "micromatch": "^3.1.4", - "minimist": "^1.1.1", - "walker": "~1.0.5" - }, - "bin": { - "sane": "src/cli.js" - }, + "node_modules/is-finite": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", + "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", + "dev": true, + "peer": true, "engines": { - "node": "6.* || 8.* || >= 10.*" + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/sane/node_modules/anymatch": { + "node_modules/is-fullwidth-code-point": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", "dev": true, - "dependencies": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" + "engines": { + "node": ">=4" } }, - "node_modules/sane/node_modules/normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "dependencies": { - "remove-trailing-separator": "^1.0.1" + "is-extglob": "^2.1.1" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/sanitize-html": { - "version": "1.27.5", - "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.27.5.tgz", - "integrity": "sha512-M4M5iXDAUEcZKLXkmk90zSYWEtk5NH3JmojQxKxV371fnMh+x9t1rqdmXaGoyEHw3z/X/8vnFhKjGL5xFGOJ3A==", - "dependencies": { - "htmlparser2": "^4.1.0", - "lodash": "^4.17.15", - "parse-srcset": "^1.0.2", - "postcss": "^7.0.27" + "node_modules/is-hexadecimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", + "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/sanitize-html/node_modules/dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "node_modules/is-hotkey": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/is-hotkey/-/is-hotkey-0.1.8.tgz", + "integrity": "sha512-qs3NZ1INIS+H+yeo7cD9pDfwYV/jqRh1JG9S9zYrNudkoUQg7OL7ziXqRKu+InFjUIDoP2o6HIkLYMh1pcWgyQ==" + }, + "node_modules/is-installed-globally": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", + "integrity": "sha512-ERNhMg+i/XgDwPIPF3u24qpajVreaiSuvpb1Uu0jugw7KKcxGyCX8cgp8P5fwTmAuXku6beDHHECdKArjlg7tw==", + "dev": true, "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" + "global-dirs": "^0.1.0", + "is-path-inside": "^1.0.0" }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + "engines": { + "node": ">=4" } }, - "node_modules/sanitize-html/node_modules/dom-serializer/node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "dependencies": { - "domelementtype": "^2.2.0" - }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true, "engines": { - "node": ">= 4" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/sanitize-html/node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ] + "node_modules/is-npm": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", + "integrity": "sha512-9r39FIr3d+KD9SbX0sfMsHzb5PP3uimOiwr3YupUaUFG4W0l1U57Rx3utpttV7qz5U3jmrO5auUa04LU9pyHsg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/sanitize-html/node_modules/domhandler": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.3.0.tgz", - "integrity": "sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==", + "node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dev": true, "dependencies": { - "domelementtype": "^2.0.1" + "kind-of": "^3.0.2" }, "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" + "node": ">=0.10.0" } }, - "node_modules/sanitize-html/node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/sanitize-html/node_modules/domutils/node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, "dependencies": { - "domelementtype": "^2.2.0" + "is-buffer": "^1.1.5" }, "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" + "node": ">=0.10.0" } }, - "node_modules/sanitize-html/node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "node_modules/is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/sanitize-html/node_modules/htmlparser2": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-4.1.0.tgz", - "integrity": "sha512-4zDq1a1zhE4gQso/c5LP1OtrhYTncXNSpvJYtWJBtXAETPlMfi3IFNjGuQbYLuVY4ZR0QMqRVvo4Pdy9KLyP8Q==", + "node_modules/is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha512-qhsCR/Esx4U4hg/9I19OVUAJkGWtjRYHMRgUMZE2TDdj+Ag+kttZanLupfddNyglzz50cUlmWzUaI37GDfNx/g==", + "dev": true, "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^3.0.0", - "domutils": "^2.0.0", - "entities": "^2.0.0" + "path-is-inside": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true - }, - "node_modules/scheduler": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", - "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==", - "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "engines": { + "node": ">=8" } }, - "node_modules/schema-utils": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", - "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, "dependencies": { - "@types/json-schema": "^7.0.5", - "ajv": "^6.12.4", - "ajv-keywords": "^3.5.2" + "isobject": "^3.0.1" }, "engines": { - "node": ">= 8.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "node": ">=0.10.0" } }, - "node_modules/scroll-into-view-if-needed": { - "version": "2.2.31", - "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.31.tgz", - "integrity": "sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==", - "dependencies": { - "compute-scroll-into-view": "^1.0.20" + "node_modules/is-posix-bracket": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", + "integrity": "sha512-Yu68oeXJ7LeWNmZ3Zov/xg/oDBnBK2RNxwYY1ilNJX+tKKZqgPK+qOn/Gs9jEu66KDY9Netf5XLKNGzas/vPfQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/seekout": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/seekout/-/seekout-1.0.2.tgz", - "integrity": "sha512-eB4fRqHZCE8gmwFOVHC9tHzLkZJ2Y12qJvAJQox8kWjGObA++tgVeXHACsDCPajfNGnDaK2Juv+WzNlMuO43wQ==", + "node_modules/is-primitive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", + "integrity": "sha512-N3w1tFaRfk3UrPfqeRyD+GYDASU3W5VinKhlORy8EWVf/sIdDL9GAcew85XmktCfH+ngG7SRXEVDoO18WMdB/Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-redirect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", + "integrity": "sha512-cr/SlUEe5zOGmzvj9bUyC4LVvkNVAXu4GytXLNMr1pny+a65MpQ9IJzFHD5vi7FyJgb4qt27+eS3TuQnqB+RQw==", + "dev": true, "engines": { - "node": ">=0.12" + "node": ">=0.10.0" } }, - "node_modules/semantic-release": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-21.1.1.tgz", - "integrity": "sha512-OCIazQnaCHdq1F6zfmKS0P7jZakYq0weiqW2mxUWo4H2CDnxelUoa/0Bs/dQatoHc6JFh6lG2HWpusdl93bFcw==", - "peer": true, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, "dependencies": { - "@semantic-release/commit-analyzer": "^10.0.0", - "@semantic-release/error": "^4.0.0", - "@semantic-release/github": "^9.0.0", - "@semantic-release/npm": "^10.0.2", - "@semantic-release/release-notes-generator": "^11.0.0", - "aggregate-error": "^4.0.1", - "cosmiconfig": "^8.0.0", - "debug": "^4.0.0", - "env-ci": "^9.0.0", - "execa": "^8.0.0", - "figures": "^5.0.0", - "find-versions": "^5.1.0", - "get-stream": "^6.0.0", - "git-log-parser": "^1.2.0", - "hook-std": "^3.0.0", - "hosted-git-info": "^7.0.0", - "lodash-es": "^4.17.21", - "marked": "^5.0.0", - "marked-terminal": "^5.1.1", - "micromatch": "^4.0.2", - "p-each-series": "^3.0.0", - "p-reduce": "^3.0.0", - "read-pkg-up": "^10.0.0", - "resolve-from": "^5.0.0", - "semver": "^7.3.2", - "semver-diff": "^4.0.0", - "signale": "^1.2.1", - "yargs": "^17.5.1" - }, - "bin": { - "semantic-release": "bin/semantic-release.js" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">=18" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/semantic-release/node_modules/@octokit/auth-token": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz", - "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==", - "peer": true, + "node_modules/is-retry-allowed": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", + "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==", + "dev": true, "engines": { - "node": ">= 18" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/@octokit/core": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.0.0.tgz", - "integrity": "sha512-YbAtMWIrbZ9FCXbLwT9wWB8TyLjq9mxpKdgB3dUNxQcIVTf9hJ70gRPwAcqGZdY6WdJPZ0I7jLaaNDCiloGN2A==", - "peer": true, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, "dependencies": { - "@octokit/auth-token": "^4.0.0", - "@octokit/graphql": "^7.0.0", - "@octokit/request": "^8.0.2", - "@octokit/request-error": "^5.0.0", - "@octokit/types": "^11.0.0", - "before-after-hook": "^2.2.0", - "universal-user-agent": "^6.0.0" + "call-bind": "^1.0.2" }, - "engines": { - "node": ">= 18" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/semantic-release/node_modules/@octokit/endpoint": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.0.tgz", - "integrity": "sha512-szrQhiqJ88gghWY2Htt8MqUDO6++E/EIXqJ2ZEp5ma3uGS46o7LZAzSLt49myB7rT+Hfw5Y6gO3LmOxGzHijAQ==", - "peer": true, - "dependencies": { - "@octokit/types": "^11.0.0", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" - }, + "node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "dev": true, "engines": { - "node": ">= 18" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/@octokit/graphql": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.0.1.tgz", - "integrity": "sha512-T5S3oZ1JOE58gom6MIcrgwZXzTaxRnxBso58xhozxHpOqSTgDS6YNeEUvZ/kRvXgPrRz/KHnZhtb7jUMRi9E6w==", - "peer": true, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, "dependencies": { - "@octokit/request": "^8.0.1", - "@octokit/types": "^11.0.0", - "universal-user-agent": "^6.0.0" + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">= 18" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/semantic-release/node_modules/@octokit/plugin-paginate-rest": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-8.0.0.tgz", - "integrity": "sha512-2xZ+baZWUg+qudVXnnvXz7qfrTmDeYPCzangBVq/1gXxii/OiS//4shJp9dnCCvj1x+JAm9ji1Egwm1BA47lPQ==", - "peer": true, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, "dependencies": { - "@octokit/types": "^11.0.0" + "has-symbols": "^1.0.2" }, "engines": { - "node": ">= 18" + "node": ">= 0.4" }, - "peerDependencies": { - "@octokit/core": ">=5" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/semantic-release/node_modules/@octokit/plugin-retry": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-6.0.0.tgz", - "integrity": "sha512-a1/A4A+PB1QoAHQfLJxGHhLfSAT03bR1jJz3GgQJZvty2ozawFWs93MiBQXO7SL2YbO7CIq0Goj4qLOBj8JeMQ==", - "peer": true, + "node_modules/is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "dev": true, "dependencies": { - "@octokit/request-error": "^5.0.0", - "@octokit/types": "^11.0.0", - "bottleneck": "^2.15.3" + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">= 18" + "node": ">= 0.4" }, - "peerDependencies": { - "@octokit/core": ">=5" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/semantic-release/node_modules/@octokit/plugin-throttling": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-7.0.0.tgz", - "integrity": "sha512-KL2k/d0uANc8XqP5S64YcNFCudR3F5AaKO39XWdUtlJIjT9Ni79ekWJ6Kj5xvAw87udkOMEPcVf9xEge2+ahew==", - "peer": true, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, "dependencies": { - "@octokit/types": "^11.0.0", - "bottleneck": "^2.15.3" + "call-bind": "^1.0.2" }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true, "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@octokit/core": "^5.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/@octokit/request": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.1.1.tgz", - "integrity": "sha512-8N+tdUz4aCqQmXl8FpHYfKG9GelDFd7XGVzyN8rc6WxVlYcfpHECnuRkgquzz+WzvHTK62co5di8gSXnzASZPQ==", - "peer": true, - "dependencies": { - "@octokit/endpoint": "^9.0.0", - "@octokit/request-error": "^5.0.0", - "@octokit/types": "^11.1.0", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" - }, + "node_modules/is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", + "dev": true, "engines": { - "node": ">= 18" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/@octokit/request-error": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.0.0.tgz", - "integrity": "sha512-1ue0DH0Lif5iEqT52+Rf/hf0RmGO9NWFjrzmrkArpG9trFfDM/efx00BJHdLGuro4BR/gECxCU2Twf5OKrRFsQ==", - "peer": true, - "dependencies": { - "@octokit/types": "^11.0.0", - "deprecation": "^2.0.0", - "once": "^1.4.0" - }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, "engines": { - "node": ">= 18" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/@octokit/types": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-11.1.0.tgz", - "integrity": "sha512-Fz0+7GyLm/bHt8fwEqgvRBWwIV1S6wRRyq+V6exRKLVWaKGsuy6H9QFYeBVDV7rK6fO3XwHgQOPxv+cLj2zpXQ==", - "peer": true, - "dependencies": { - "@octokit/openapi-types": "^18.0.0" + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" + }, + "node_modules/istanbul-lib-coverage": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "dev": true, + "engines": { + "node": ">=6" } }, - "node_modules/semantic-release/node_modules/@semantic-release/commit-analyzer": { - "version": "10.0.4", - "resolved": "https://registry.npmjs.org/@semantic-release/commit-analyzer/-/commit-analyzer-10.0.4.tgz", - "integrity": "sha512-pFGn99fn8w4/MHE0otb2A/l5kxgOuxaaauIh4u30ncoTJuqWj4hXTgEJ03REqjS+w1R2vPftSsO26WC61yOcpw==", - "peer": true, + "node_modules/istanbul-lib-instrument": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", + "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", + "dev": true, "dependencies": { - "conventional-changelog-angular": "^6.0.0", - "conventional-commits-filter": "^3.0.0", - "conventional-commits-parser": "^5.0.0", - "debug": "^4.0.0", - "import-from": "^4.0.0", - "lodash-es": "^4.17.21", - "micromatch": "^4.0.2" + "@babel/generator": "^7.4.0", + "@babel/parser": "^7.4.3", + "@babel/template": "^7.4.0", + "@babel/traverse": "^7.4.3", + "@babel/types": "^7.4.0", + "istanbul-lib-coverage": "^2.0.5", + "semver": "^6.0.0" }, "engines": { - "node": ">=18" - }, - "peerDependencies": { - "semantic-release": ">=20.1.0" + "node": ">=6" } }, - "node_modules/semantic-release/node_modules/@semantic-release/github": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-9.0.4.tgz", - "integrity": "sha512-kQCGFAsBErvCR6hzNuzu63cj4erQN2krm9zQlg8vl4j5X0mL0d/Ras0wmL5Gkr1TuSS2lweME7M4J5zvtDDDSA==", - "peer": true, + "node_modules/istanbul-lib-report": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", + "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", + "dev": true, "dependencies": { - "@octokit/core": "^5.0.0", - "@octokit/plugin-paginate-rest": "^8.0.0", - "@octokit/plugin-retry": "^6.0.0", - "@octokit/plugin-throttling": "^7.0.0", - "@semantic-release/error": "^4.0.0", - "aggregate-error": "^4.0.1", - "debug": "^4.3.4", - "dir-glob": "^3.0.1", - "globby": "^13.1.4", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.0", - "issue-parser": "^6.0.0", - "lodash-es": "^4.17.21", - "mime": "^3.0.0", - "p-filter": "^3.0.0", - "url-join": "^5.0.0" - }, - "engines": { - "node": ">=18" + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "supports-color": "^6.1.0" }, - "peerDependencies": { - "semantic-release": ">=20.1.0" + "engines": { + "node": ">=6" } }, - "node_modules/semantic-release/node_modules/@semantic-release/npm": { - "version": "10.0.5", - "resolved": "https://registry.npmjs.org/@semantic-release/npm/-/npm-10.0.5.tgz", - "integrity": "sha512-cJnQ2M5pxJRwZEkb0A/+U3TG4UNmjrrLwV2PxJKljn5OPT0yJB8GzGgWbbKACayvxrT06YdTa4Amtq/piJcOIA==", - "peer": true, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, "dependencies": { - "@semantic-release/error": "^4.0.0", - "aggregate-error": "^4.0.1", - "execa": "^8.0.0", - "fs-extra": "^11.0.0", - "lodash-es": "^4.17.21", - "nerf-dart": "^1.0.0", - "normalize-url": "^8.0.0", - "npm": "^9.5.0", - "rc": "^1.2.8", - "read-pkg": "^8.0.0", - "registry-auth-token": "^5.0.0", - "semver": "^7.1.2", - "tempy": "^3.0.0" + "has-flag": "^3.0.0" }, "engines": { - "node": ">=18" - }, - "peerDependencies": { - "semantic-release": ">=20.1.0" + "node": ">=6" } }, - "node_modules/semantic-release/node_modules/@semantic-release/release-notes-generator": { - "version": "11.0.7", - "resolved": "https://registry.npmjs.org/@semantic-release/release-notes-generator/-/release-notes-generator-11.0.7.tgz", - "integrity": "sha512-T09QB9ImmNx7Q6hY6YnnEbw/rEJ6a+22LBxfZq+pSAXg/OL/k0siwEm5cK4k1f9dE2Z2mPIjJKKohzUm0jbxcQ==", - "peer": true, + "node_modules/istanbul-lib-source-maps": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", + "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", + "dev": true, "dependencies": { - "conventional-changelog-angular": "^6.0.0", - "conventional-changelog-writer": "^6.0.0", - "conventional-commits-filter": "^4.0.0", - "conventional-commits-parser": "^5.0.0", - "debug": "^4.0.0", - "get-stream": "^7.0.0", - "import-from": "^4.0.0", - "into-stream": "^7.0.0", - "lodash-es": "^4.17.21", - "read-pkg-up": "^10.0.0" + "debug": "^4.1.1", + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "rimraf": "^2.6.3", + "source-map": "^0.6.1" }, "engines": { - "node": ">=18" - }, - "peerDependencies": { - "semantic-release": ">=20.1.0" + "node": ">=6" } }, - "node_modules/semantic-release/node_modules/@semantic-release/release-notes-generator/node_modules/conventional-commits-filter": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-4.0.0.tgz", - "integrity": "sha512-rnpnibcSOdFcdclpFwWa+pPlZJhXE7l+XK04zxhbWrhgpR96h33QLz8hITTXbcYICxVr3HZFtbtUAQ+4LdBo9A==", - "peer": true, + "node_modules/istanbul-lib-source-maps/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, "engines": { - "node": ">=16" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/@semantic-release/release-notes-generator/node_modules/get-stream": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-7.0.1.tgz", - "integrity": "sha512-3M8C1EOFN6r8AMUhwUAACIoXZJEOufDU5+0gFFN5uNs6XYOralD2Pqkl7m046va6x77FwposWXbAhPPIOus7mQ==", - "peer": true, + "node_modules/istanbul-reports": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.7.tgz", + "integrity": "sha512-uu1F/L1o5Y6LzPVSVZXNOoD/KXpJue9aeLRd0sM9uMXfZvzomB0WxVamWb5ue8kA2vVWEmW7EG+A5n3f1kqHKg==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0" + }, "engines": { - "node": ">=16" + "node": ">=6" + } + }, + "node_modules/jest": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-24.9.0.tgz", + "integrity": "sha512-YvkBL1Zm7d2B1+h5fHEOdyjCG+sGMz4f8D86/0HiqJ6MB4MnDc8FgP5vdWsGnemOQro7lnYo8UakZ3+5A0jxGw==", + "dev": true, + "dependencies": { + "import-local": "^2.0.0", + "jest-cli": "^24.9.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/agent-base": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", - "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", - "peer": true, + "node_modules/jest-changed-files": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-24.9.0.tgz", + "integrity": "sha512-6aTWpe2mHF0DhL28WjdkO8LyGjs3zItPET4bMSeXU6T3ub4FPMw+mcOcbdGXQOAfmLcxofD23/5Bl9Z4AkFwqg==", + "dev": true, "dependencies": { - "debug": "^4.3.4" + "@jest/types": "^24.9.0", + "execa": "^1.0.0", + "throat": "^4.0.0" }, "engines": { - "node": ">= 14" - } - }, - "node_modules/semantic-release/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "peer": true, - "engines": { - "node": ">=8" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "peer": true, + "node_modules/jest-cli": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-24.9.0.tgz", + "integrity": "sha512-+VLRKyitT3BWoMeSUIHRxV/2g8y9gw91Jh5z2UmXZzkZKpbC08CSehVxgHUwTpy+HwGcns/tqafQDJW7imYvGg==", + "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "@jest/core": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "import-local": "^2.0.0", + "is-ci": "^2.0.0", + "jest-config": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "prompts": "^2.0.1", + "realpath-native": "^1.1.0", + "yargs": "^13.3.0" }, - "engines": { - "node": ">=8" + "bin": { + "jest": "bin/jest.js" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "engines": { + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "peer": true, + "node_modules/jest-config": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.9.0.tgz", + "integrity": "sha512-RATtQJtVYQrp7fvWg6f5y3pEFj9I+H8sWw4aKxnDZ96mob5i5SD6ZEGWgMLXQ4LE8UurrjbdlLWdUeo+28QpfQ==", + "dev": true, "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" + "@babel/core": "^7.1.0", + "@jest/test-sequencer": "^24.9.0", + "@jest/types": "^24.9.0", + "babel-jest": "^24.9.0", + "chalk": "^2.0.1", + "glob": "^7.1.1", + "jest-environment-jsdom": "^24.9.0", + "jest-environment-node": "^24.9.0", + "jest-get-type": "^24.9.0", + "jest-jasmine2": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "micromatch": "^3.1.10", + "pretty-format": "^24.9.0", + "realpath-native": "^1.1.0" }, "engines": { - "node": ">=12" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "peer": true, + "node_modules/jest-diff": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.9.0.tgz", + "integrity": "sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ==", + "dev": true, "dependencies": { - "color-name": "~1.1.4" + "chalk": "^2.0.1", + "diff-sequences": "^24.9.0", + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" }, "engines": { - "node": ">=7.0.0" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "peer": true - }, - "node_modules/semantic-release/node_modules/conventional-changelog-angular": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-6.0.0.tgz", - "integrity": "sha512-6qLgrBF4gueoC7AFVHu51nHL9pF9FRjXrH+ceVf7WmAfH3gs+gEYOkvxhjMPjZu57I4AGUGoNTY8V7Hrgf1uqg==", - "peer": true, + "node_modules/jest-docblock": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-24.9.0.tgz", + "integrity": "sha512-F1DjdpDMJMA1cN6He0FNYNZlo3yYmOtRUnktrT9Q37njYzC5WEaDdmbynIgy0L/IvXvvgsG8OsqhLPXTpfmZAA==", + "dev": true, "dependencies": { - "compare-func": "^2.0.0" + "detect-newline": "^2.1.0" }, "engines": { - "node": ">=14" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-6.0.1.tgz", - "integrity": "sha512-359t9aHorPw+U+nHzUXHS5ZnPBOizRxfQsWT5ZDHBfvfxQOAik+yfuhKXG66CN5LEWPpMNnIMHUTCKeYNprvHQ==", - "peer": true, + "node_modules/jest-each": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.9.0.tgz", + "integrity": "sha512-ONi0R4BvW45cw8s2Lrx8YgbeXL1oCQ/wIDwmsM3CqM/nlblNCPmnC3IPQlMbRFZu3wKdQ2U8BqM6lh3LJ5Bsog==", + "dev": true, "dependencies": { - "conventional-commits-filter": "^3.0.0", - "dateformat": "^3.0.3", - "handlebars": "^4.7.7", - "json-stringify-safe": "^5.0.1", - "meow": "^8.1.2", - "semver": "^7.0.0", - "split": "^1.0.1" - }, - "bin": { - "conventional-changelog-writer": "cli.js" + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "jest-get-type": "^24.9.0", + "jest-util": "^24.9.0", + "pretty-format": "^24.9.0" }, "engines": { - "node": ">=14" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "peer": true, + "node_modules/jest-environment-jsdom": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.9.0.tgz", + "integrity": "sha512-Zv9FV9NBRzLuALXjvRijO2351DRQeLYXtpD4xNvfoVFw21IOKNhZAEUKcbiEtjTkm2GsJ3boMVgkaR7rN8qetA==", + "dev": true, "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "@jest/environment": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0", + "jest-util": "^24.9.0", + "jsdom": "^11.5.1" }, "engines": { - "node": ">=8" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer/node_modules/hosted-git-info": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", - "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", - "peer": true, + "node_modules/jest-environment-node": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-24.9.0.tgz", + "integrity": "sha512-6d4V2f4nxzIzwendo27Tr0aFm+IXWa0XEUnaH6nU0FMaozxovt+sfRvh4J47wL1OvF83I3SSTu0XK+i4Bqe7uA==", + "dev": true, "dependencies": { - "lru-cache": "^6.0.0" + "@jest/environment": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0", + "jest-util": "^24.9.0" }, "engines": { - "node": ">=10" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer/node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "peer": true - }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer/node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "peer": true + "node_modules/jest-get-type": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", + "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", + "dev": true, + "engines": { + "node": ">= 6" + } }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "peer": true, + "node_modules/jest-haste-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.9.0.tgz", + "integrity": "sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ==", + "dev": true, "dependencies": { - "p-locate": "^4.1.0" + "@jest/types": "^24.9.0", + "anymatch": "^2.0.0", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.1.15", + "invariant": "^2.2.4", + "jest-serializer": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.9.0", + "micromatch": "^3.1.10", + "sane": "^4.0.3", + "walker": "^1.0.7" }, "engines": { - "node": ">=8" + "node": ">= 6" + }, + "optionalDependencies": { + "fsevents": "^1.2.7" } }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "peer": true, + "node_modules/jest-haste-map/node_modules/anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" } }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer/node_modules/meow": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", - "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", - "peer": true, + "node_modules/jest-haste-map/node_modules/fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "deprecated": "fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], "dependencies": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.18.0", - "yargs-parser": "^20.2.3" + "bindings": "^1.5.0", + "nan": "^2.12.1" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 4.0" } }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer/node_modules/normalize-package-data": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", - "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", - "peer": true, + "node_modules/jest-haste-map/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "dev": true, "dependencies": { - "hosted-git-info": "^4.0.1", - "is-core-module": "^2.5.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" + "remove-trailing-separator": "^1.0.1" }, "engines": { - "node": ">=10" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "peer": true, + "node_modules/jest-jasmine2": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-24.9.0.tgz", + "integrity": "sha512-Cq7vkAgaYKp+PsX+2/JbTarrk0DmNhsEtqBXNwUHkdlbrTBLtMJINADf2mf5FkowNsq8evbPc07/qFO0AdKTzw==", + "dev": true, "dependencies": { - "p-try": "^2.0.0" + "@babel/traverse": "^7.1.0", + "@jest/environment": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "co": "^4.6.0", + "expect": "^24.9.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-snapshot": "^24.9.0", + "jest-util": "^24.9.0", + "pretty-format": "^24.9.0", + "throat": "^4.0.0" }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "peer": true, + "node_modules/jest-leak-detector": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-24.9.0.tgz", + "integrity": "sha512-tYkFIDsiKTGwb2FG1w8hX9V0aUb2ot8zY/2nFg087dUageonw1zrLMP4W6zsRO59dPkTSKie+D4rhMuP9nRmrA==", + "dev": true, "dependencies": { - "p-limit": "^2.2.0" + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" }, "engines": { - "node": ">=8" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer/node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "peer": true, + "node_modules/jest-matcher-utils": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz", + "integrity": "sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA==", + "dev": true, "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" + "chalk": "^2.0.1", + "jest-diff": "^24.9.0", + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "peer": true, "engines": { - "node": ">=8" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer/node_modules/read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "peer": true, + "node_modules/jest-message-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", + "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "dev": true, "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^2.0.1", + "micromatch": "^3.1.10", + "slash": "^2.0.0", + "stack-utils": "^1.0.1" }, "engines": { - "node": ">=8" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer/node_modules/read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "peer": true, + "node_modules/jest-mock": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", + "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", + "dev": true, "dependencies": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, - "engines": { - "node": ">=8" + "@jest/types": "^24.9.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer/node_modules/read-pkg-up/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer/node_modules/read-pkg/node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "peer": true - }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer/node_modules/read-pkg/node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "peer": true, - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer/node_modules/read-pkg/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "peer": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer/node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "peer": true, "engines": { - "node": ">=8" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer/node_modules/type-fest": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", - "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", - "peer": true, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, "engines": { - "node": ">=10" + "node": ">=6" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } } }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "peer": true, + "node_modules/jest-regex-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", + "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", + "dev": true, "engines": { - "node": ">=10" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/conventional-commits-filter": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-3.0.0.tgz", - "integrity": "sha512-1ymej8b5LouPx9Ox0Dw/qAO2dVdfpRFq28e5Y0jJEU8ZrLdy0vOSkkIInwmxErFGhg6SALro60ZrwYFVTUDo4Q==", - "peer": true, + "node_modules/jest-resolve": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.9.0.tgz", + "integrity": "sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ==", + "dev": true, "dependencies": { - "lodash.ismatch": "^4.4.0", - "modify-values": "^1.0.1" + "@jest/types": "^24.9.0", + "browser-resolve": "^1.11.3", + "chalk": "^2.0.1", + "jest-pnp-resolver": "^1.2.1", + "realpath-native": "^1.1.0" }, "engines": { - "node": ">=14" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/conventional-commits-parser": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz", - "integrity": "sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==", - "peer": true, + "node_modules/jest-resolve-dependencies": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-24.9.0.tgz", + "integrity": "sha512-Fm7b6AlWnYhT0BXy4hXpactHIqER7erNgIsIozDXWl5dVm+k8XdGVe1oTg1JyaFnOxarMEbax3wyRJqGP2Pq+g==", + "dev": true, "dependencies": { - "is-text-path": "^2.0.0", - "JSONStream": "^1.3.5", - "meow": "^12.0.1", - "split2": "^4.0.0" - }, - "bin": { - "conventional-commits-parser": "cli.mjs" + "@jest/types": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-snapshot": "^24.9.0" }, "engines": { - "node": ">=16" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "peer": true, + "node_modules/jest-runner": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-24.9.0.tgz", + "integrity": "sha512-KksJQyI3/0mhcfspnxxEOBueGrd5E4vV7ADQLT9ESaCzz02WnbdbKWIf5Mkaucoaj7obQckYPVX6JJhgUcoWWg==", + "dev": true, "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "@jest/console": "^24.7.1", + "@jest/environment": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "chalk": "^2.4.2", + "exit": "^0.1.2", + "graceful-fs": "^4.1.15", + "jest-config": "^24.9.0", + "jest-docblock": "^24.3.0", + "jest-haste-map": "^24.9.0", + "jest-jasmine2": "^24.9.0", + "jest-leak-detector": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-resolve": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.6.0", + "source-map-support": "^0.5.6", + "throat": "^4.0.0" }, "engines": { - "node": ">= 8" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/crypto-random-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", - "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", - "peer": true, - "dependencies": { - "type-fest": "^1.0.1" - }, + "node_modules/jest-runner/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/crypto-random-string/node_modules/type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", - "peer": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node_modules/jest-runner/node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, - "node_modules/semantic-release/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "peer": true - }, - "node_modules/semantic-release/node_modules/execa": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", - "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", - "peer": true, + "node_modules/jest-runtime": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-24.9.0.tgz", + "integrity": "sha512-8oNqgnmF3v2J6PVRM2Jfuj8oX3syKmaynlDMMKQ4iyzbQzIG6th5ub/lM2bCMTmoTKM3ykcUYI2Pw9xwNtjMnw==", + "dev": true, "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^8.0.1", - "human-signals": "^5.0.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^4.1.0", - "strip-final-newline": "^3.0.0" + "@jest/console": "^24.7.1", + "@jest/environment": "^24.9.0", + "@jest/source-map": "^24.3.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/yargs": "^13.0.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.1.15", + "jest-config": "^24.9.0", + "jest-haste-map": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-mock": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.9.0", + "jest-snapshot": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "realpath-native": "^1.1.0", + "slash": "^2.0.0", + "strip-bom": "^3.0.0", + "yargs": "^13.3.0" + }, + "bin": { + "jest-runtime": "bin/jest-runtime.js" }, "engines": { - "node": ">=16.17" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/execa/node_modules/get-stream": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", - "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", - "peer": true, + "node_modules/jest-serializer": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.9.0.tgz", + "integrity": "sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==", + "dev": true, "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/find-up": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", - "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", - "peer": true, + "node_modules/jest-snapshot": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.9.0.tgz", + "integrity": "sha512-uI/rszGSs73xCM0l+up7O7a40o90cnrk429LOiK3aeTvfC0HHmldbd81/B7Ix81KSFe1lwkbl7GnBGG4UfuDew==", + "dev": true, "dependencies": { - "locate-path": "^7.1.0", - "path-exists": "^5.0.0" + "@babel/types": "^7.0.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "expect": "^24.9.0", + "jest-diff": "^24.9.0", + "jest-get-type": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-resolve": "^24.9.0", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^24.9.0", + "semver": "^6.2.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/fs-extra": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", - "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", - "peer": true, + "node_modules/jest-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", + "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "dev": true, "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "@jest/console": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/source-map": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" }, "engines": { - "node": ">=14.14" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "peer": true, + "node_modules/jest-util/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/globby": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", - "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", - "peer": true, + "node_modules/jest-validate": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.9.0.tgz", + "integrity": "sha512-HPIt6C5ACwiqSiwi+OfSSHbK8sG7akG8eATl+IPKaeIjtPOeBUd/g3J7DghugzxrGjI93qS/+RPKe1H6PqvhRQ==", + "dev": true, "dependencies": { - "dir-glob": "^3.0.1", - "fast-glob": "^3.3.0", - "ignore": "^5.2.4", - "merge2": "^1.4.1", - "slash": "^4.0.0" + "@jest/types": "^24.9.0", + "camelcase": "^5.3.1", + "chalk": "^2.0.1", + "jest-get-type": "^24.9.0", + "leven": "^3.1.0", + "pretty-format": "^24.9.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/hosted-git-info": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.0.tgz", - "integrity": "sha512-ICclEpTLhHj+zCuSb2/usoNXSVkxUSIopre+b1w8NDY9Dntp9LO4vLdHYI336TH8sAqwrRgnSfdkBG2/YpisHA==", - "peer": true, + "node_modules/jest-watcher": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-24.9.0.tgz", + "integrity": "sha512-+/fLOfKPXXYJDYlks62/4R4GoT+GU1tYZed99JSCOsmzkkF7727RqKrjNAxtfO4YpGv11wybgRvCjR73lK2GZw==", + "dev": true, "dependencies": { - "lru-cache": "^10.0.1" + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/yargs": "^13.0.0", + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.1", + "jest-util": "^24.9.0", + "string-length": "^2.0.0" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/http-proxy-agent": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz", - "integrity": "sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==", - "peer": true, + "node_modules/jest-worker": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", + "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "dev": true, "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" + "merge-stream": "^2.0.0", + "supports-color": "^6.1.0" }, "engines": { - "node": ">= 14" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/https-proxy-agent": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.1.tgz", - "integrity": "sha512-Eun8zV0kcYS1g19r78osiQLEFIRspRUDd9tIfBCTBPBeMieF/EsJNL8VI3xOIdYRDEkjQnqOYPsZ2DsWsVsFwQ==", - "peer": true, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, "dependencies": { - "agent-base": "^7.0.2", - "debug": "4" + "has-flag": "^3.0.0" }, "engines": { - "node": ">= 14" + "node": ">=6" } }, - "node_modules/semantic-release/node_modules/into-stream": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-7.0.0.tgz", - "integrity": "sha512-2dYz766i9HprMBasCMvHMuazJ7u4WzhJwo5kb3iPSiW/iRYV6uPari3zHoqZlnuaR7V1bEiNMxikhp37rdBXbw==", - "peer": true, + "node_modules/jquery": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-2.2.4.tgz", + "integrity": "sha512-lBHj60ezci2u1v2FqnZIraShGgEXq35qCzMv4lITyHGppTnA13rwR0MgwyNJh9TnDs3aXUvd1xjAotfraMHX/Q==" + }, + "node_modules/jquery-ui": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/jquery-ui/-/jquery-ui-1.12.1.tgz", + "integrity": "sha512-K/kDBMXkTky5LH+gqbMvttU1ipqCTaecKyAFjwHjUnPTVfm5I5PZC7We31iNR3yWtAHNqoxkLoit06lR/gKVlA==" + }, + "node_modules/jquery.cookie": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jquery.cookie/-/jquery.cookie-1.4.1.tgz", + "integrity": "sha512-c/hZOOL+8VSw/FkTVH637gS1/6YzMSCROpTZ2qBYwJ7s7sHajU7uBkSSiE5+GXWwrfCCyO+jsYjUQ7Hs2rIxAA==" + }, + "node_modules/jquery.scrollto": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/jquery.scrollto/-/jquery.scrollto-2.1.3.tgz", + "integrity": "sha512-4M+t5R4QRnq1CSRU9gswZ7Z4zRfcoWmgD31HRrLYCCI4J7tLWeE0ZoT+0JpI7GcT0YKTqcfw+p8t2yYEKFfrHA==", "dependencies": { - "from2": "^2.3.0", - "p-is-promise": "^3.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "jquery": ">=1.8" } }, - "node_modules/semantic-release/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "peer": true, - "engines": { - "node": ">=8" - } + "node_modules/js-cookie": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz", + "integrity": "sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==" }, - "node_modules/semantic-release/node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "peer": true, - "engines": { - "node": ">=0.10.0" + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" + }, + "node_modules/jsdom": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz", + "integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==", + "dev": true, + "dependencies": { + "abab": "^2.0.0", + "acorn": "^5.5.3", + "acorn-globals": "^4.1.0", + "array-equal": "^1.0.0", + "cssom": ">= 0.3.2 < 0.4.0", + "cssstyle": "^1.0.0", + "data-urls": "^1.0.0", + "domexception": "^1.0.1", + "escodegen": "^1.9.1", + "html-encoding-sniffer": "^1.0.2", + "left-pad": "^1.3.0", + "nwsapi": "^2.0.7", + "parse5": "4.0.0", + "pn": "^1.1.0", + "request": "^2.87.0", + "request-promise-native": "^1.0.5", + "sax": "^1.2.4", + "symbol-tree": "^3.2.2", + "tough-cookie": "^2.3.4", + "w3c-hr-time": "^1.0.1", + "webidl-conversions": "^4.0.2", + "whatwg-encoding": "^1.0.3", + "whatwg-mimetype": "^2.1.0", + "whatwg-url": "^6.4.1", + "ws": "^5.2.0", + "xml-name-validator": "^3.0.0" } }, - "node_modules/semantic-release/node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "peer": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/is-text-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-2.0.0.tgz", - "integrity": "sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==", - "peer": true, - "dependencies": { - "text-extensions": "^2.0.0" + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" }, "engines": { - "node": ">=8" - } - }, - "node_modules/semantic-release/node_modules/json-parse-even-better-errors": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.0.tgz", - "integrity": "sha512-iZbGHafX/59r39gPwVPRBGw0QQKnA7tte5pSMrhWOW7swGsVvVTjmfyAV9pNqk8YGT7tRCdxRu8uzcgZwoDooA==", - "peer": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=6" } }, - "node_modules/semantic-release/node_modules/locate-path": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", - "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", - "peer": true, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dependencies": { - "p-locate": "^6.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "universalify": "^2.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "optionalDependencies": { + "graceful-fs": "^4.1.6" } }, - "node_modules/semantic-release/node_modules/lru-cache": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.1.tgz", - "integrity": "sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==", - "peer": true, + "node_modules/jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + }, "engines": { - "node": "14 || >=16.14" + "node": ">=0.6.0" } }, - "node_modules/semantic-release/node_modules/meow": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz", - "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==", - "peer": true, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, "engines": { - "node": ">=16.10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "peer": true, + "node_modules/klaw-sync": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz", + "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==", "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" + "graceful-fs": "^4.1.11" } }, - "node_modules/semantic-release/node_modules/mime": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", - "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", - "peer": true, - "bin": { - "mime": "cli.js" - }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, "engines": { - "node": ">=10.0.0" + "node": ">=6" } }, - "node_modules/semantic-release/node_modules/normalize-package-data": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.0.tgz", - "integrity": "sha512-UL7ELRVxYBHBgYEtZCXjxuD5vPxnmvMGq0jp/dGPKKrN7tfsBh2IY7TlJ15WWwdjRWD3RJbnsygUurTK3xkPkg==", - "peer": true, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" + }, + "node_modules/latest-version": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", + "integrity": "sha512-Be1YRHWWlZaSsrz2U+VInk+tO0EwLIyV+23RhWLINJYwg/UIikxjlj3MhH37/6/EDCAusjajvMkMMUXRaMWl/w==", + "dev": true, "dependencies": { - "hosted-git-info": "^7.0.0", - "is-core-module": "^2.8.1", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4" + "package-json": "^4.0.0" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/normalize-url": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.0.tgz", - "integrity": "sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==", - "peer": true, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "node_modules/left-pad": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", + "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==", + "deprecated": "use String.prototype.padStart()", + "dev": true }, - "node_modules/semantic-release/node_modules/npm": { - "version": "9.8.1", - "resolved": "https://registry.npmjs.org/npm/-/npm-9.8.1.tgz", - "integrity": "sha512-AfDvThQzsIXhYgk9zhbk5R+lh811lKkLAeQMMhSypf1BM7zUafeIIBzMzespeuVEJ0+LvY36oRQYf7IKLzU3rw==", - "bundleDependencies": [ - "@isaacs/string-locale-compare", - "@npmcli/arborist", - "@npmcli/config", - "@npmcli/fs", - "@npmcli/map-workspaces", - "@npmcli/package-json", - "@npmcli/promise-spawn", - "@npmcli/run-script", - "abbrev", - "archy", - "cacache", - "chalk", - "ci-info", - "cli-columns", - "cli-table3", - "columnify", - "fastest-levenshtein", - "fs-minipass", - "glob", - "graceful-fs", - "hosted-git-info", - "ini", - "init-package-json", - "is-cidr", - "json-parse-even-better-errors", - "libnpmaccess", - "libnpmdiff", - "libnpmexec", - "libnpmfund", - "libnpmhook", - "libnpmorg", - "libnpmpack", - "libnpmpublish", - "libnpmsearch", - "libnpmteam", - "libnpmversion", - "make-fetch-happen", - "minimatch", - "minipass", - "minipass-pipeline", - "ms", - "node-gyp", - "nopt", - "npm-audit-report", - "npm-install-checks", - "npm-package-arg", - "npm-pick-manifest", - "npm-profile", - "npm-registry-fetch", - "npm-user-validate", - "npmlog", - "p-map", - "pacote", - "parse-conflict-json", - "proc-log", - "qrcode-terminal", - "read", - "semver", - "sigstore", - "ssri", - "supports-color", - "tar", - "text-table", - "tiny-relative-date", - "treeverse", - "validate-npm-package-name", - "which", - "write-file-atomic" - ], - "peer": true, - "dependencies": { - "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/arborist": "^6.3.0", - "@npmcli/config": "^6.2.1", - "@npmcli/fs": "^3.1.0", - "@npmcli/map-workspaces": "^3.0.4", - "@npmcli/package-json": "^4.0.1", - "@npmcli/promise-spawn": "^6.0.2", - "@npmcli/run-script": "^6.0.2", - "abbrev": "^2.0.0", - "archy": "~1.0.0", - "cacache": "^17.1.3", - "chalk": "^5.3.0", - "ci-info": "^3.8.0", - "cli-columns": "^4.0.0", - "cli-table3": "^0.6.3", - "columnify": "^1.6.0", - "fastest-levenshtein": "^1.0.16", - "fs-minipass": "^3.0.2", - "glob": "^10.2.7", - "graceful-fs": "^4.2.11", - "hosted-git-info": "^6.1.1", - "ini": "^4.1.1", - "init-package-json": "^5.0.0", - "is-cidr": "^4.0.2", - "json-parse-even-better-errors": "^3.0.0", - "libnpmaccess": "^7.0.2", - "libnpmdiff": "^5.0.19", - "libnpmexec": "^6.0.3", - "libnpmfund": "^4.0.19", - "libnpmhook": "^9.0.3", - "libnpmorg": "^5.0.4", - "libnpmpack": "^5.0.19", - "libnpmpublish": "^7.5.0", - "libnpmsearch": "^6.0.2", - "libnpmteam": "^5.0.3", - "libnpmversion": "^4.0.2", - "make-fetch-happen": "^11.1.1", - "minimatch": "^9.0.3", - "minipass": "^5.0.0", - "minipass-pipeline": "^1.2.4", - "ms": "^2.1.2", - "node-gyp": "^9.4.0", - "nopt": "^7.2.0", - "npm-audit-report": "^5.0.0", - "npm-install-checks": "^6.1.1", - "npm-package-arg": "^10.1.0", - "npm-pick-manifest": "^8.0.1", - "npm-profile": "^7.0.1", - "npm-registry-fetch": "^14.0.5", - "npm-user-validate": "^2.0.0", - "npmlog": "^7.0.1", - "p-map": "^4.0.0", - "pacote": "^15.2.0", - "parse-conflict-json": "^3.0.1", - "proc-log": "^3.0.0", - "qrcode-terminal": "^0.12.0", - "read": "^2.1.0", - "semver": "^7.5.4", - "sigstore": "^1.7.0", - "ssri": "^10.0.4", - "supports-color": "^9.4.0", - "tar": "^6.1.15", - "text-table": "~0.2.0", - "tiny-relative-date": "^1.3.0", - "treeverse": "^3.0.0", - "validate-npm-package-name": "^5.0.0", - "which": "^3.0.1", - "write-file-atomic": "^5.0.1" - }, - "bin": { - "npm": "bin/npm-cli.js", - "npx": "bin/npx-cli.js" - }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=6" } }, - "node_modules/semantic-release/node_modules/npm-run-path": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", - "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", - "peer": true, + "node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "dev": true, "dependencies": { - "path-key": "^4.0.0" + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.8.0" } }, - "node_modules/semantic-release/node_modules/npm-run-path/node_modules/path-key": { + "node_modules/load-json-file": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "peer": true, - "engines": { - "node": ">=12" + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/semantic-release/node_modules/npm/node_modules/@colors/colors": { - "version": "1.5.0", - "inBundle": true, - "license": "MIT", - "optional": true, - "peer": true, "engines": { - "node": ">=0.1.90" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/@isaacs/cliui": { - "version": "8.0.2", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, + "node_modules/load-json-file/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, "engines": { - "node": ">=12" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.0.1", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "node": ">=6.11.5" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "inBundle": true, - "license": "MIT", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8.9.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, "dependencies": { - "ansi-regex": "^6.0.1" + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "node": ">=6" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/@isaacs/string-locale-compare": { - "version": "1.1.0", - "inBundle": true, - "license": "ISC", - "peer": true + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "node_modules/semantic-release/node_modules/npm/node_modules/@npmcli/arborist": { - "version": "6.3.0", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/fs": "^3.1.0", - "@npmcli/installed-package-contents": "^2.0.2", - "@npmcli/map-workspaces": "^3.0.2", - "@npmcli/metavuln-calculator": "^5.0.0", - "@npmcli/name-from-folder": "^2.0.0", - "@npmcli/node-gyp": "^3.0.0", - "@npmcli/package-json": "^4.0.0", - "@npmcli/query": "^3.0.0", - "@npmcli/run-script": "^6.0.0", - "bin-links": "^4.0.1", - "cacache": "^17.0.4", - "common-ancestor-path": "^1.0.1", - "hosted-git-info": "^6.1.1", - "json-parse-even-better-errors": "^3.0.0", - "json-stringify-nice": "^1.1.4", - "minimatch": "^9.0.0", - "nopt": "^7.0.0", - "npm-install-checks": "^6.0.0", - "npm-package-arg": "^10.1.0", - "npm-pick-manifest": "^8.0.1", - "npm-registry-fetch": "^14.0.3", - "npmlog": "^7.0.1", - "pacote": "^15.0.8", - "parse-conflict-json": "^3.0.0", - "proc-log": "^3.0.0", - "promise-all-reject-late": "^1.0.0", - "promise-call-limit": "^1.0.2", - "read-package-json-fast": "^3.0.2", - "semver": "^7.3.7", - "ssri": "^10.0.1", - "treeverse": "^3.0.0", - "walk-up-path": "^3.0.1" - }, - "bin": { - "arborist": "bin/index.js" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } + "node_modules/lodash.assign": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", + "integrity": "sha512-hFuH8TY+Yji7Eja3mGiuAxBqLagejScbG8GbG0j6o9vzn0YL14My+ktnqtZgFTosKymC9/44wP6s7xyuLfnClw==", + "dev": true + }, + "node_modules/lodash.assignin": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz", + "integrity": "sha512-yX/rx6d/UTVh7sSVWVSIMjfnz95evAgDFdb1ZozC35I9mSFCkmzptOzevxjgbQUsc78NR44LVHWjsoMQXy9FDg==" + }, + "node_modules/lodash.bind": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz", + "integrity": "sha512-lxdsn7xxlCymgLYo1gGvVrfHmkjDiyqVv62FAeF2i5ta72BipE1SLxw8hPEPLhD4/247Ijw07UQH7Hq/chT5LA==" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true + }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" + }, + "node_modules/lodash.filter": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz", + "integrity": "sha512-pXYUy7PR8BCLwX5mgJ/aNtyOvuJTdZAo9EQFUvMIYugqmJxnrYaANvTbgndOzHSCSR0wnlBBfRXJL5SbWxo3FQ==" + }, + "node_modules/lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==" + }, + "node_modules/lodash.foreach": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", + "integrity": "sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ==" + }, + "node_modules/lodash.frompairs": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.frompairs/-/lodash.frompairs-4.0.1.tgz", + "integrity": "sha512-dvqe2I+cO5MzXCMhUnfYFa9MD+/760yx2aTAN1lqEcEkf896TxgrX373igVdqSJj6tQd0jnSLE1UMuKufqqxFw==", + "dev": true + }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "dev": true }, - "node_modules/semantic-release/node_modules/npm/node_modules/@npmcli/config": { - "version": "6.2.1", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "@npmcli/map-workspaces": "^3.0.2", - "ci-info": "^3.8.0", - "ini": "^4.1.0", - "nopt": "^7.0.0", - "proc-log": "^3.0.0", - "read-package-json-fast": "^3.0.2", - "semver": "^7.3.5", - "walk-up-path": "^3.0.1" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } + "node_modules/lodash.map": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", + "integrity": "sha512-worNHGKLDetmcEYDvh2stPCrrQRkP20E4l0iIS7F8EvzMqBBi7ltvFN5m1HvTf1P7Jk1txKhvFcmYsCr8O2F1Q==" }, - "node_modules/semantic-release/node_modules/npm/node_modules/@npmcli/disparity-colors": { - "version": "3.0.0", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "ansi-styles": "^4.3.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, - "node_modules/semantic-release/node_modules/npm/node_modules/@npmcli/fs": { - "version": "3.1.0", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } + "node_modules/lodash.pick": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", + "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==" }, - "node_modules/semantic-release/node_modules/npm/node_modules/@npmcli/git": { - "version": "4.1.0", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "@npmcli/promise-spawn": "^6.0.0", - "lru-cache": "^7.4.4", - "npm-pick-manifest": "^8.0.0", - "proc-log": "^3.0.0", - "promise-inflight": "^1.0.1", - "promise-retry": "^2.0.1", - "semver": "^7.3.5", - "which": "^3.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } + "node_modules/lodash.reduce": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz", + "integrity": "sha512-6raRe2vxCYBhpBu+B+TtNGUzah+hQjVdu3E17wfusjyrXBka2nBS8OH/gjVZ5PvHOhWmIZTYri09Z6n/QfnNMw==" }, - "node_modules/semantic-release/node_modules/npm/node_modules/@npmcli/installed-package-contents": { - "version": "2.0.2", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "npm-bundled": "^3.0.0", - "npm-normalize-package-bin": "^3.0.0" - }, - "bin": { - "installed-package-contents": "lib/index.js" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } + "node_modules/lodash.reject": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz", + "integrity": "sha512-qkTuvgEzYdyhiJBx42YPzPo71R1aEr0z79kAv7Ixg8wPFEjgRgJdUsGMG3Hf3OYSF/kHI79XhNlt+5Ar6OzwxQ==" }, - "node_modules/semantic-release/node_modules/npm/node_modules/@npmcli/map-workspaces": { - "version": "3.0.4", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "@npmcli/name-from-folder": "^2.0.0", - "glob": "^10.2.2", - "minimatch": "^9.0.0", - "read-package-json-fast": "^3.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } + "node_modules/lodash.some": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz", + "integrity": "sha512-j7MJE+TuT51q9ggt4fSgVqro163BEFjAt3u97IqU+JA2DkWl80nFTrowzLpZ/BnpN7rrl0JA/593NAdd8p/scQ==" }, - "node_modules/semantic-release/node_modules/npm/node_modules/@npmcli/metavuln-calculator": { - "version": "5.0.1", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "cacache": "^17.0.0", - "json-parse-even-better-errors": "^3.0.0", - "pacote": "^15.0.0", - "semver": "^7.3.5" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } + "node_modules/lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", + "dev": true }, - "node_modules/semantic-release/node_modules/npm/node_modules/@npmcli/name-from-folder": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC", - "peer": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } + "node_modules/lodash.topairs": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.topairs/-/lodash.topairs-4.3.0.tgz", + "integrity": "sha512-qrRMbykBSEGdOgQLJJqVSdPWMD7Q+GJJ5jMRfQYb+LTLsw3tYVIabnCzRqTJb2WTo17PG5gNzXuFaZgYH/9SAQ==", + "dev": true }, - "node_modules/semantic-release/node_modules/npm/node_modules/@npmcli/node-gyp": { - "version": "3.0.0", - "inBundle": true, - "license": "ISC", - "peer": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node_modules/logform": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.5.1.tgz", + "integrity": "sha512-9FyqAm9o9NKKfiAKfZoYo9bGXXuwMkxQiQttkT4YjjVtQVIQtK6LmVtlxmCaFswo6N4AfEkHqZTV0taDtPotNg==", + "dependencies": { + "@colors/colors": "1.5.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/@npmcli/package-json": { - "version": "4.0.1", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "dependencies": { - "@npmcli/git": "^4.1.0", - "glob": "^10.2.2", - "hosted-git-info": "^6.1.1", - "json-parse-even-better-errors": "^3.0.0", - "normalize-package-data": "^5.0.0", - "proc-log": "^3.0.0", - "semver": "^7.5.3" + "js-tokens": "^3.0.0 || ^4.0.0" }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "bin": { + "loose-envify": "cli.js" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/@npmcli/promise-spawn": { - "version": "6.0.2", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "which": "^3.0.0" - }, + "node_modules/lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/@npmcli/query": { - "version": "3.0.0", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, "dependencies": { - "postcss-selector-parser": "^6.0.10" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "yallist": "^3.0.2" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/@npmcli/run-script": { - "version": "6.0.2", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, "dependencies": { - "@npmcli/node-gyp": "^3.0.0", - "@npmcli/promise-spawn": "^6.0.0", - "node-gyp": "^9.0.0", - "read-package-json-fast": "^3.0.0", - "which": "^3.0.0" + "pify": "^4.0.1", + "semver": "^5.6.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/semantic-release/node_modules/npm/node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "inBundle": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=14" + "node": ">=6" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/@sigstore/protobuf-specs": { - "version": "0.1.0", - "inBundle": true, - "license": "Apache-2.0", - "peer": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node_modules/make-dir/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/@sigstore/tuf": { - "version": "1.0.2", - "inBundle": true, - "license": "Apache-2.0", - "peer": true, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, "dependencies": { - "@sigstore/protobuf-specs": "^0.1.0", - "tuf-js": "^1.1.7" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "tmpl": "1.0.5" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/@tootallnate/once": { - "version": "2.0.0", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", + "dev": true, "engines": { - "node": ">= 10" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/@tufjs/canonical-json": { + "node_modules/map-visit": { "version": "1.0.0", - "inBundle": true, - "license": "MIT", - "peer": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/semantic-release/node_modules/npm/node_modules/@tufjs/models": { - "version": "1.0.4", - "inBundle": true, - "license": "MIT", - "peer": true, + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==", + "dev": true, "dependencies": { - "@tufjs/canonical-json": "1.0.0", - "minimatch": "^9.0.0" + "object-visit": "^1.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/abbrev": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC", - "peer": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } + "node_modules/math-random": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz", + "integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==", + "dev": true }, - "node_modules/semantic-release/node_modules/npm/node_modules/abort-controller": { - "version": "3.0.0", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/mdast-util-definitions": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-4.0.0.tgz", + "integrity": "sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ==", "dependencies": { - "event-target-shim": "^5.0.0" + "unist-util-visit": "^2.0.0" }, - "engines": { - "node": ">=6.5" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/agent-base": { - "version": "6.0.2", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/mdast-util-from-markdown": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-0.8.5.tgz", + "integrity": "sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==", "dependencies": { - "debug": "4" + "@types/mdast": "^3.0.0", + "mdast-util-to-string": "^2.0.0", + "micromark": "~2.11.0", + "parse-entities": "^2.0.0", + "unist-util-stringify-position": "^2.0.0" }, - "engines": { - "node": ">= 6.0.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/agentkeepalive": { - "version": "4.3.0", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/mdast-util-to-hast": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-10.2.0.tgz", + "integrity": "sha512-JoPBfJ3gBnHZ18icCwHR50orC9kNH81tiR1gs01D8Q5YpV6adHNO9nKNuFBCJQ941/32PT1a63UF/DitmS3amQ==", "dependencies": { - "debug": "^4.1.0", - "depd": "^2.0.0", - "humanize-ms": "^1.2.1" + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "mdast-util-definitions": "^4.0.0", + "mdurl": "^1.0.0", + "unist-builder": "^2.0.0", + "unist-util-generated": "^1.0.0", + "unist-util-position": "^3.0.0", + "unist-util-visit": "^2.0.0" }, - "engines": { - "node": ">= 8.0.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/aggregate-error": { - "version": "3.1.0", - "inBundle": true, - "license": "MIT", - "peer": true, - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" + "node_modules/mdast-util-to-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz", + "integrity": "sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/ansi-regex": { - "version": "5.0.1", - "inBundle": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=8" - } + "node_modules/mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==" }, - "node_modules/semantic-release/node_modules/npm/node_modules/ansi-styles": { - "version": "4.3.0", - "inBundle": true, - "license": "MIT", - "peer": true, - "dependencies": { - "color-convert": "^2.0.1" - }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">= 0.6" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/aproba": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC", - "peer": true + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" }, - "node_modules/semantic-release/node_modules/npm/node_modules/archy": { - "version": "1.0.0", - "inBundle": true, - "license": "MIT", - "peer": true + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true }, - "node_modules/semantic-release/node_modules/npm/node_modules/are-we-there-yet": { - "version": "4.0.0", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^4.1.0" - }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">= 0.6" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/balanced-match": { - "version": "1.0.2", - "inBundle": true, - "license": "MIT", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/base64-js": { - "version": "1.5.1", + "node_modules/micromark": { + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-2.11.4.tgz", + "integrity": "sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA==", "funding": [ { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" }, { - "type": "consulting", - "url": "https://feross.org/support" + "type": "OpenCollective", + "url": "https://opencollective.com/unified" } ], - "inBundle": true, - "license": "MIT", - "peer": true + "dependencies": { + "debug": "^4.0.0", + "parse-entities": "^2.0.0" + } }, - "node_modules/semantic-release/node_modules/npm/node_modules/bin-links": { - "version": "4.0.2", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, "dependencies": { - "cmd-shim": "^6.0.0", - "npm-normalize-package-bin": "^3.0.0", - "read-cmd-shim": "^4.0.0", - "write-file-atomic": "^5.0.0" + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/binary-extensions": { - "version": "2.2.0", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/micromatch/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/brace-expansion": { + "node_modules/micromatch/node_modules/braces/node_modules/extend-shallow": { "version": "2.0.1", - "inBundle": true, - "license": "MIT", - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/semantic-release/node_modules/npm/node_modules/buffer": { - "version": "6.0.3", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "inBundle": true, - "license": "MIT", - "peer": true, + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/builtins": { - "version": "5.0.1", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/micromatch/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dev": true, "dependencies": { - "semver": "^7.0.0" + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/cacache": { - "version": "17.1.3", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/micromatch/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, "dependencies": { - "@npmcli/fs": "^3.1.0", - "fs-minipass": "^3.0.0", - "glob": "^10.2.2", - "lru-cache": "^7.7.1", - "minipass": "^5.0.0", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "p-map": "^4.0.0", - "ssri": "^10.0.0", - "tar": "^6.1.11", - "unique-filename": "^3.0.0" + "is-extendable": "^0.1.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/chalk": { - "version": "5.3.0", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/micromatch/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/chownr": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/micromatch/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, "engines": { - "node": ">=10" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/ci-info": { - "version": "3.8.0", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/cidr-regex": { - "version": "3.1.1", - "inBundle": true, - "license": "BSD-2-Clause", - "peer": true, - "dependencies": { - "ip-regex": "^4.1.0" - }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "engines": { - "node": ">=10" + "node": ">= 0.6" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/clean-stack": { - "version": "2.2.0", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, "engines": { - "node": ">=6" + "node": ">= 0.6" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/cli-columns": { - "version": "4.0.0", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dependencies": { - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">= 10" + "node": "*" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/cli-table3": { - "version": "0.6.3", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, "dependencies": { - "string-width": "^4.2.0" + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" }, "engines": { - "node": "10.* || >= 12.*" - }, - "optionalDependencies": { - "@colors/colors": "1.5.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/clone": { - "version": "1.0.4", - "inBundle": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=0.8" + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/cmd-shim": { - "version": "6.0.1", - "inBundle": true, - "license": "ISC", - "peer": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, - "node_modules/semantic-release/node_modules/npm/node_modules/color-convert": { - "version": "2.0.1", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/nan": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", + "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==", + "dev": true, + "optional": true + }, + "node_modules/nanoid": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-2.1.11.tgz", + "integrity": "sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA==" + }, + "node_modules/nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, "dependencies": { - "color-name": "~1.1.4" + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" }, "engines": { - "node": ">=7.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/color-name": { - "version": "1.1.4", - "inBundle": true, - "license": "MIT", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/color-support": { - "version": "1.1.3", - "inBundle": true, - "license": "ISC", - "peer": true, - "bin": { - "color-support": "bin.js" - } + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true }, - "node_modules/semantic-release/node_modules/npm/node_modules/columnify": { - "version": "1.6.0", - "inBundle": true, - "license": "MIT", - "peer": true, - "dependencies": { - "strip-ansi": "^6.0.1", - "wcwidth": "^1.0.0" - }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "engines": { - "node": ">=8.0.0" + "node": ">= 0.6" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/common-ancestor-path": { - "version": "1.0.1", - "inBundle": true, - "license": "ISC", - "peer": true + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true }, - "node_modules/semantic-release/node_modules/npm/node_modules/concat-map": { - "version": "0.0.1", - "inBundle": true, - "license": "MIT", - "peer": true + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" }, - "node_modules/semantic-release/node_modules/npm/node_modules/console-control-strings": { - "version": "1.1.0", - "inBundle": true, - "license": "ISC", - "peer": true + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true }, - "node_modules/semantic-release/node_modules/npm/node_modules/cross-spawn": { - "version": "7.0.3", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/node-notifier": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.5.tgz", + "integrity": "sha512-tVbHs7DyTLtzOiN78izLA85zRqB9NvEXkAf014Vx3jtSvn/xBl6bR8ZYifj+dFcFrKI21huSQgJZ6ZtL3B4HfQ==", + "dev": true, "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" + "growly": "^1.3.0", + "is-wsl": "^1.1.0", + "semver": "^5.5.0", + "shellwords": "^0.1.1", + "which": "^1.3.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/cross-spawn/node_modules/which": { - "version": "2.0.2", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "isexe": "^2.0.0" - }, + "node_modules/node-notifier/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" + "semver": "bin/semver" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/cssesc": { - "version": "3.0.0", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/node-releases": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", + "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", + "dev": true + }, + "node_modules/nodemon": { + "version": "1.19.4", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-1.19.4.tgz", + "integrity": "sha512-VGPaqQBNk193lrJFotBU8nvWZPqEZY2eIzymy2jjY0fJ9qIsxA0sxQ8ATPl0gZC645gijYEc1jtZvpS8QWzJGQ==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "chokidar": "^2.1.8", + "debug": "^3.2.6", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.0.4", + "pstree.remy": "^1.1.7", + "semver": "^5.7.1", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.2", + "update-notifier": "^2.5.0" + }, "bin": { - "cssesc": "bin/cssesc" + "nodemon": "bin/nodemon.js" }, "engines": { "node": ">=4" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/debug": { - "version": "4.3.4", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/nodemon/node_modules/anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "inBundle": true, - "license": "MIT", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/defaults": { - "version": "1.0.4", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/nodemon/node_modules/anymatch/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "dev": true, "dependencies": { - "clone": "^1.0.2" + "remove-trailing-separator": "^1.0.1" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/semantic-release/node_modules/npm/node_modules/delegates": { - "version": "1.0.0", - "inBundle": true, - "license": "MIT", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/depd": { - "version": "2.0.0", - "inBundle": true, - "license": "MIT", - "peer": true, "engines": { - "node": ">= 0.8" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/diff": { - "version": "5.1.0", - "inBundle": true, - "license": "BSD-3-Clause", - "peer": true, + "node_modules/nodemon/node_modules/binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true, "engines": { - "node": ">=0.3.1" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/eastasianwidth": { - "version": "0.2.0", - "inBundle": true, - "license": "MIT", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/emoji-regex": { - "version": "8.0.0", - "inBundle": true, - "license": "MIT", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/encoding": { - "version": "0.1.13", - "inBundle": true, - "license": "MIT", - "optional": true, - "peer": true, + "node_modules/nodemon/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, "dependencies": { - "iconv-lite": "^0.6.2" - } - }, - "node_modules/semantic-release/node_modules/npm/node_modules/env-paths": { - "version": "2.2.1", - "inBundle": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/semantic-release/node_modules/npm/node_modules/err-code": { - "version": "2.0.3", - "inBundle": true, - "license": "MIT", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/event-target-shim": { - "version": "5.0.1", - "inBundle": true, - "license": "MIT", - "peer": true, + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/events": { - "version": "3.3.0", - "inBundle": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=0.8.x" + "node_modules/nodemon/node_modules/chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "deprecated": "Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies", + "dev": true, + "dependencies": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + }, + "optionalDependencies": { + "fsevents": "^1.2.7" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/exponential-backoff": { - "version": "3.1.1", - "inBundle": true, - "license": "Apache-2.0", - "peer": true + "node_modules/nodemon/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } }, - "node_modules/semantic-release/node_modules/npm/node_modules/fastest-levenshtein": { - "version": "1.0.16", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/nodemon/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, "engines": { - "node": ">= 4.9.1" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/foreground-child": { - "version": "3.1.1", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/nodemon/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dev": true, "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" }, "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/fs-minipass": { - "version": "3.0.2", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/nodemon/node_modules/fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "deprecated": "fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], "dependencies": { - "minipass": "^5.0.0" + "bindings": "^1.5.0", + "nan": "^2.12.1" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">= 4.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/fs.realpath": { - "version": "1.0.0", - "inBundle": true, - "license": "ISC", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/function-bind": { - "version": "1.1.1", - "inBundle": true, - "license": "MIT", - "peer": true + "node_modules/nodemon/node_modules/glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", + "dev": true, + "dependencies": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } }, - "node_modules/semantic-release/node_modules/npm/node_modules/gauge": { - "version": "5.0.1", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/nodemon/node_modules/glob-parent/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", + "dev": true, "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.3", - "console-control-strings": "^1.1.0", - "has-unicode": "^2.0.1", - "signal-exit": "^4.0.1", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.5" + "is-extglob": "^2.1.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/glob": { - "version": "10.2.7", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/nodemon/node_modules/is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==", + "dev": true, "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.0.3", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2", - "path-scurry": "^1.7.0" - }, - "bin": { - "glob": "dist/cjs/src/bin.js" + "binary-extensions": "^1.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/graceful-fs": { - "version": "4.2.11", - "inBundle": true, - "license": "ISC", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/has": { - "version": "1.0.3", - "inBundle": true, - "license": "MIT", - "peer": true, - "dependencies": { - "function-bind": "^1.1.1" - }, + "node_modules/nodemon/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, "engines": { - "node": ">= 0.4.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/has-unicode": { - "version": "2.0.1", - "inBundle": true, - "license": "ISC", - "peer": true + "node_modules/nodemon/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } }, - "node_modules/semantic-release/node_modules/npm/node_modules/hosted-git-info": { - "version": "6.1.1", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/nodemon/node_modules/readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, "dependencies": { - "lru-cache": "^7.5.1" + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/http-cache-semantics": { - "version": "4.1.1", - "inBundle": true, - "license": "BSD-2-Clause", - "peer": true + "node_modules/nodemon/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true }, - "node_modules/semantic-release/node_modules/npm/node_modules/http-proxy-agent": { - "version": "5.0.0", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/nodemon/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/nodemon/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/nodemon/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dev": true, "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" }, "engines": { - "node": ">= 6" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/https-proxy-agent": { - "version": "5.0.1", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", + "dev": true, "dependencies": { - "agent-base": "6", - "debug": "4" + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" }, "engines": { - "node": ">= 6" + "node": "*" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/humanize-ms": { - "version": "1.2.1", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, "dependencies": { - "ms": "^2.0.0" + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/iconv-lite": { - "version": "0.6.3", - "inBundle": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/ieee754": { - "version": "1.2.1", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "inBundle": true, - "license": "BSD-3-Clause", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/ignore-walk": { - "version": "6.0.3", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", + "dev": true, "dependencies": { - "minimatch": "^9.0.0" + "path-key": "^2.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/imurmurhash": { - "version": "0.1.4", - "inBundle": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=0.8.19" + "node_modules/nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "dependencies": { + "boolbase": "~1.0.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/indent-string": { - "version": "4.0.0", - "inBundle": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=8" - } + "node_modules/nwsapi": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.4.tgz", + "integrity": "sha512-NHj4rzRo0tQdijE9ZqAx6kYDcoRwYwSYzCA8MY3JzfxlrvEU0jhnhJT9BhqhJs7I/dKcrDm6TyulaRqZPIhN5g==", + "dev": true }, - "node_modules/semantic-release/node_modules/npm/node_modules/inflight": { - "version": "1.0.6", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "engines": { + "node": "*" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/inherits": { - "version": "2.0.4", - "inBundle": true, - "license": "ISC", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/ini": { + "node_modules/object-assign": { "version": "4.1.1", - "inBundle": true, - "license": "ISC", - "peer": true, + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/init-package-json": { - "version": "5.0.0", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==", + "dev": true, "dependencies": { - "npm-package-arg": "^10.0.0", - "promzard": "^1.0.0", - "read": "^2.0.0", - "read-package-json": "^6.0.0", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4", - "validate-npm-package-name": "^5.0.0" + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/ip": { - "version": "2.0.0", - "inBundle": true, - "license": "MIT", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/ip-regex": { - "version": "4.3.0", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/object-copy/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/is-cidr": { - "version": "4.0.2", - "inBundle": true, - "license": "BSD-2-Clause", - "peer": true, + "node_modules/object-copy/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", + "dev": true, "dependencies": { - "cidr-regex": "^3.1.1" + "kind-of": "^3.0.2" }, "engines": { - "node": ">=10" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/is-core-module": { - "version": "2.12.1", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/object-copy/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", + "dev": true, "dependencies": { - "has": "^1.0.3" + "kind-of": "^3.0.2" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/semantic-release/node_modules/npm/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "inBundle": true, - "license": "MIT", - "peer": true, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/is-lambda": { - "version": "1.0.1", - "inBundle": true, - "license": "MIT", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/isexe": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/jackspeak": { - "version": "2.2.1", - "inBundle": true, - "license": "BlueOak-1.0.0", - "peer": true, + "node_modules/object-copy/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, "dependencies": { - "@isaacs/cliui": "^8.0.2" + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" }, "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/json-parse-even-better-errors": { - "version": "3.0.0", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/object-copy/node_modules/is-descriptor/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/semantic-release/node_modules/npm/node_modules/json-stringify-nice": { - "version": "1.1.4", - "inBundle": true, - "license": "ISC", - "peer": true, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/jsonparse": { - "version": "1.3.1", - "engines": [ - "node >= 0.2.0" - ], - "inBundle": true, - "license": "MIT", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/just-diff": { - "version": "6.0.2", - "inBundle": true, - "license": "MIT", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/just-diff-apply": { - "version": "5.5.0", - "inBundle": true, - "license": "MIT", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/libnpmaccess": { - "version": "7.0.2", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/object-copy/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, "dependencies": { - "npm-package-arg": "^10.1.0", - "npm-registry-fetch": "^14.0.3" + "is-buffer": "^1.1.5" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/libnpmdiff": { - "version": "5.0.19", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "@npmcli/arborist": "^6.3.0", - "@npmcli/disparity-colors": "^3.0.0", - "@npmcli/installed-package-contents": "^2.0.2", - "binary-extensions": "^2.2.0", - "diff": "^5.1.0", - "minimatch": "^9.0.0", - "npm-package-arg": "^10.1.0", - "pacote": "^15.0.8", - "tar": "^6.1.13" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node_modules/object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/libnpmexec": { - "version": "6.0.3", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "@npmcli/arborist": "^6.3.0", - "@npmcli/run-script": "^6.0.0", - "ci-info": "^3.7.1", - "npm-package-arg": "^10.1.0", - "npmlog": "^7.0.1", - "pacote": "^15.0.8", - "proc-log": "^3.0.0", - "read": "^2.0.0", - "read-package-json-fast": "^3.0.2", - "semver": "^7.3.7", - "walk-up-path": "^3.0.1" - }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">= 0.4" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/libnpmfund": { - "version": "4.0.19", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==", + "dev": true, "dependencies": { - "@npmcli/arborist": "^6.3.0" + "isobject": "^3.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/libnpmhook": { - "version": "9.0.3", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dev": true, "dependencies": { - "aproba": "^2.0.0", - "npm-registry-fetch": "^14.0.3" + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/libnpmorg": { - "version": "5.0.4", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/object.getownpropertydescriptors": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.5.tgz", + "integrity": "sha512-yDNzckpM6ntyQiGTik1fKV1DcVDRS+w8bvpWNCBanvH5LfRX9O8WTHqQzG4RZwRAM4I0oU7TV11Lj5v0g20ibw==", + "dev": true, "dependencies": { - "aproba": "^2.0.0", - "npm-registry-fetch": "^14.0.3" + "array.prototype.reduce": "^1.0.5", + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">= 0.8" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/libnpmpack": { - "version": "5.0.19", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/object.omit": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "integrity": "sha512-UiAM5mhmIuKLsOvrL+B0U2d1hXHF3bFYWIuH1LMpuV2EJEHG1Ntz06PgLEHjm6VFd87NpH8rastvPoyv6UW2fA==", + "dev": true, "dependencies": { - "@npmcli/arborist": "^6.3.0", - "@npmcli/run-script": "^6.0.0", - "npm-package-arg": "^10.1.0", - "pacote": "^15.0.8" + "for-own": "^0.1.4", + "is-extendable": "^0.1.1" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/libnpmpublish": { - "version": "7.5.0", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "ci-info": "^3.6.1", - "normalize-package-data": "^5.0.0", - "npm-package-arg": "^10.1.0", - "npm-registry-fetch": "^14.0.3", - "proc-log": "^3.0.0", - "semver": "^7.3.7", - "sigstore": "^1.4.0", - "ssri": "^10.0.1" - }, + "node_modules/object.omit/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/libnpmsearch": { - "version": "6.0.2", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", + "dev": true, "dependencies": { - "npm-registry-fetch": "^14.0.3" + "isobject": "^3.0.1" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/libnpmteam": { - "version": "5.0.3", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "dependencies": { - "aproba": "^2.0.0", - "npm-registry-fetch": "^14.0.3" + "ee-first": "1.1.1" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">= 0.8" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/libnpmversion": { - "version": "4.0.2", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dependencies": { - "@npmcli/git": "^4.0.1", - "@npmcli/run-script": "^6.0.0", - "json-parse-even-better-errors": "^3.0.0", - "proc-log": "^3.0.0", - "semver": "^7.3.7" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "wrappy": "1" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/lru-cache": { - "version": "7.18.3", - "inBundle": true, - "license": "ISC", - "peer": true, - "engines": { - "node": ">=12" + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "dependencies": { + "fn.name": "1.x.x" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/make-fetch-happen": { - "version": "11.1.1", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "agentkeepalive": "^4.2.1", - "cacache": "^17.0.0", - "http-cache-semantics": "^4.1.1", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^7.7.1", - "minipass": "^5.0.0", - "minipass-fetch": "^3.0.0", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.3", - "promise-retry": "^2.0.1", - "socks-proxy-agent": "^7.0.0", - "ssri": "^10.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/semantic-release/node_modules/npm/node_modules/minimatch": { - "version": "9.0.3", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/open": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", "dependencies": { - "brace-expansion": "^2.0.1" + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/semantic-release/node_modules/npm/node_modules/minipass": { - "version": "5.0.0", - "inBundle": true, - "license": "ISC", - "peer": true, - "engines": { - "node": ">=8" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/minipass-collect": { - "version": "1.0.2", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/open/node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", "dependencies": { - "minipass": "^3.0.0" + "is-docker": "^2.0.0" }, "engines": { - "node": ">= 8" + "node": ">=8" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/minipass-collect/node_modules/minipass": { - "version": "3.3.6", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, "dependencies": { - "yallist": "^4.0.0" + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" }, "engines": { - "node": ">=8" + "node": ">= 0.8.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/minipass-fetch": { - "version": "3.0.3", - "inBundle": true, - "license": "MIT", + "node_modules/os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==", + "dev": true, "peer": true, - "dependencies": { - "minipass": "^5.0.0", - "minipass-sized": "^1.0.3", - "minizlib": "^2.1.2" - }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - }, - "optionalDependencies": { - "encoding": "^0.1.13" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/minipass-flush": { - "version": "1.0.5", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "minipass": "^3.0.0" - }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", "engines": { - "node": ">= 8" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/minipass-flush/node_modules/minipass": { - "version": "3.3.6", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/p-each-series": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-1.0.0.tgz", + "integrity": "sha512-J/e9xiZZQNrt+958FFzJ+auItsBGq+UrQ7nE89AUP7UOTtjHnkISANXLdayhVzh538UnLMCSlf13lFfRIAKQOA==", + "dev": true, "dependencies": { - "yallist": "^4.0.0" + "p-reduce": "^1.0.0" }, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/minipass-json-stream": { - "version": "1.0.1", - "inBundle": true, - "license": "MIT", - "peer": true, - "dependencies": { - "jsonparse": "^1.3.1", - "minipass": "^3.0.0" + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "dev": true, + "engines": { + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/minipass-json-stream/node_modules/minipass": { - "version": "3.3.6", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, "dependencies": { - "yallist": "^4.0.0" + "p-try": "^2.0.0" }, "engines": { - "node": ">=8" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/minipass-pipeline": { - "version": "1.2.4", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, "dependencies": { - "minipass": "^3.0.0" + "p-limit": "^2.0.0" }, "engines": { - "node": ">=8" + "node": ">=6" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/minipass-pipeline/node_modules/minipass": { - "version": "3.3.6", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "yallist": "^4.0.0" - }, + "node_modules/p-reduce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz", + "integrity": "sha512-3Tx1T3oM1xO/Y8Gj0sWyE78EIJZ+t+aEmXUdvQgvGmSMri7aPTHoovbXEreWKkL5j21Er60XAWLTzKbAKYOujQ==", + "dev": true, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/minipass-sized": { - "version": "1.0.3", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "minipass": "^3.0.0" - }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, "engines": { - "node": ">=8" + "node": ">=6" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/minipass-sized/node_modules/minipass": { - "version": "3.3.6", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/package-json": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", + "integrity": "sha512-q/R5GrMek0vzgoomq6rm9OX+3PQve8sLwTirmK30YB3Cu0Bbt9OX9M/SIUnroN5BGJkzwGsFwDaRGD9EwBOlCA==", + "dev": true, "dependencies": { - "yallist": "^4.0.0" + "got": "^6.7.1", + "registry-auth-token": "^3.0.1", + "registry-url": "^3.0.3", + "semver": "^5.1.0" }, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/minizlib": { - "version": "2.1.2", - "inBundle": true, - "license": "MIT", - "peer": true, - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" + "node_modules/package-json/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/minizlib/node_modules/minipass": { - "version": "3.3.6", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/parse-entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", + "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", "dependencies": { - "yallist": "^4.0.0" + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" }, - "engines": { - "node": ">=8" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/mkdirp": { - "version": "1.0.4", - "inBundle": true, - "license": "MIT", - "peer": true, - "bin": { - "mkdirp": "bin/cmd.js" + "node_modules/parse-glob": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "integrity": "sha512-FC5TeK0AwXzq3tUBFtH74naWkPQCEWs4K+xMxWZBlKDWu0bVHXGZa+KKqxKidd7xwhdZ19ZNuF2uO1M/r196HA==", + "dev": true, + "dependencies": { + "glob-base": "^0.3.0", + "is-dotfile": "^1.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.0" }, "engines": { - "node": ">=10" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/ms": { - "version": "2.1.3", - "inBundle": true, - "license": "MIT", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/mute-stream": { + "node_modules/parse-glob/node_modules/is-extglob": { "version": "1.0.0", - "inBundle": true, - "license": "ISC", - "peer": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/semantic-release/node_modules/npm/node_modules/negotiator": { - "version": "0.6.3", - "inBundle": true, - "license": "MIT", - "peer": true, + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww==", + "dev": true, "engines": { - "node": ">= 0.6" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/node-gyp": { - "version": "9.4.0", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/parse-glob/node_modules/is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg==", + "dev": true, "dependencies": { - "env-paths": "^2.2.0", - "exponential-backoff": "^3.1.1", - "glob": "^7.1.4", - "graceful-fs": "^4.2.6", - "make-fetch-happen": "^11.0.3", - "nopt": "^6.0.0", - "npmlog": "^6.0.0", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.2", - "which": "^2.0.2" - }, - "bin": { - "node-gyp": "bin/node-gyp.js" + "is-extglob": "^1.0.0" }, "engines": { - "node": "^12.13 || ^14.13 || >=16" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/node-gyp/node_modules/abbrev": { - "version": "1.1.1", - "inBundle": true, - "license": "ISC", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/node-gyp/node_modules/are-we-there-yet": { - "version": "3.0.1", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/node-gyp/node_modules/brace-expansion": { - "version": "1.1.11", - "inBundle": true, - "license": "MIT", - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "node_modules/parse-srcset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz", + "integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==" + }, + "node_modules/parse5": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", + "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", + "dev": true + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/node-gyp/node_modules/gauge": { - "version": "4.0.4", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.3", - "console-control-strings": "^1.1.0", - "has-unicode": "^2.0.1", - "signal-exit": "^3.0.7", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.5" - }, + "node_modules/pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==", + "dev": true, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/node-gyp/node_modules/glob": { - "version": "7.2.3", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/patch-package": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-6.5.1.tgz", + "integrity": "sha512-I/4Zsalfhc6bphmJTlrLoOcAF87jcxko4q0qsv4bGcurbr8IskEOtdnt9iCmsQVGL1B+iUhSQqweyTLJfCF9rA==", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "@yarnpkg/lockfile": "^1.1.0", + "chalk": "^4.1.2", + "cross-spawn": "^6.0.5", + "find-yarn-workspace-root": "^2.0.0", + "fs-extra": "^9.0.0", + "is-ci": "^2.0.0", + "klaw-sync": "^6.0.0", + "minimist": "^1.2.6", + "open": "^7.4.2", + "rimraf": "^2.6.3", + "semver": "^5.6.0", + "slash": "^2.0.0", + "tmp": "^0.0.33", + "yaml": "^1.10.2" + }, + "bin": { + "patch-package": "index.js" }, "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=10", + "npm": ">5" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/node-gyp/node_modules/minimatch": { - "version": "3.1.2", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/patch-package/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dependencies": { - "brace-expansion": "^1.1.7" + "color-convert": "^2.0.1" }, "engines": { - "node": "*" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/node-gyp/node_modules/nopt": { - "version": "6.0.0", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/patch-package/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dependencies": { - "abbrev": "^1.0.0" - }, - "bin": { - "nopt": "bin/nopt.js" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/node-gyp/node_modules/npmlog": { - "version": "6.0.2", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/patch-package/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dependencies": { - "are-we-there-yet": "^3.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^4.0.3", - "set-blocking": "^2.0.0" + "color-name": "~1.1.4" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=7.0.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/node-gyp/node_modules/readable-stream": { - "version": "3.6.2", - "inBundle": true, - "license": "MIT", - "peer": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, + "node_modules/patch-package/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/patch-package/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "engines": { - "node": ">= 6" + "node": ">=8" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/node-gyp/node_modules/signal-exit": { - "version": "3.0.7", - "inBundle": true, - "license": "ISC", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/node-gyp/node_modules/which": { - "version": "2.0.2", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "isexe": "^2.0.0" - }, + "node_modules/patch-package/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" + "semver": "bin/semver" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/nopt": { + "node_modules/patch-package/node_modules/supports-color": { "version": "7.2.0", - "inBundle": true, - "license": "ISC", - "peer": true, + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dependencies": { - "abbrev": "^2.0.0" - }, - "bin": { - "nopt": "bin/nopt.js" + "has-flag": "^4.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=8" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/normalize-package-data": { - "version": "5.0.0", - "inBundle": true, - "license": "BSD-2-Clause", - "peer": true, - "dependencies": { - "hosted-git-info": "^6.0.0", - "is-core-module": "^2.8.1", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4" - }, + "node_modules/path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==", + "dev": true + }, + "node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/npm-audit-report": { - "version": "5.0.0", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/npm-bundled": { - "version": "3.0.0", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "npm-normalize-package-bin": "^3.0.0" - }, + "node_modules/path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", + "dev": true + }, + "node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/npm-install-checks": { - "version": "6.1.1", - "inBundle": true, - "license": "BSD-2-Clause", - "peer": true, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, "dependencies": { - "semver": "^7.1.1" + "pify": "^3.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/npm-normalize-package-bin": { - "version": "3.0.1", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/path-type/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/npm-package-arg": { - "version": "10.1.0", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "hosted-git-info": "^6.0.0", - "proc-log": "^3.0.0", - "semver": "^7.3.5", - "validate-npm-package-name": "^5.0.0" + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" + }, + "node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=6" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/npm-packlist": { - "version": "7.0.4", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "ignore-walk": "^6.0.0" - }, + "node_modules/pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "dev": true, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/npm-pick-manifest": { - "version": "8.0.1", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, "dependencies": { - "npm-install-checks": "^6.0.0", - "npm-normalize-package-bin": "^3.0.0", - "npm-package-arg": "^10.0.0", - "semver": "^7.3.5" + "find-up": "^4.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=8" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/npm-profile": { - "version": "7.0.1", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, "dependencies": { - "npm-registry-fetch": "^14.0.0", - "proc-log": "^3.0.0" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=8" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/npm-registry-fetch": { - "version": "14.0.5", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, "dependencies": { - "make-fetch-happen": "^11.0.0", - "minipass": "^5.0.0", - "minipass-fetch": "^3.0.0", - "minipass-json-stream": "^1.0.1", - "minizlib": "^2.1.2", - "npm-package-arg": "^10.0.0", - "proc-log": "^3.0.0" + "p-locate": "^4.1.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=8" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/npm-user-validate": { - "version": "2.0.0", - "inBundle": true, - "license": "BSD-2-Clause", - "peer": true, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=8" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/npmlog": { - "version": "7.0.1", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "are-we-there-yet": "^4.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^5.0.0", - "set-blocking": "^2.0.0" - }, + "node_modules/pkg-dir/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=8" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/once": { - "version": "1.4.0", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "wrappy": "1" + "node_modules/pn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", + "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", + "dev": true + }, + "node_modules/posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/p-map": { - "version": "4.0.0", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dependencies": { - "aggregate-error": "^3.0.0" + "picocolors": "^0.2.1", + "source-map": "^0.6.1" }, "engines": { - "node": ">=10" + "node": ">=6.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/postcss/" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/pacote": { - "version": "15.2.0", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/postcss-modules-extract-imports": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz", + "integrity": "sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==", + "dev": true, "dependencies": { - "@npmcli/git": "^4.0.0", - "@npmcli/installed-package-contents": "^2.0.1", - "@npmcli/promise-spawn": "^6.0.1", - "@npmcli/run-script": "^6.0.0", - "cacache": "^17.0.0", - "fs-minipass": "^3.0.0", - "minipass": "^5.0.0", - "npm-package-arg": "^10.0.0", - "npm-packlist": "^7.0.0", - "npm-pick-manifest": "^8.0.0", - "npm-registry-fetch": "^14.0.0", - "proc-log": "^3.0.0", - "promise-retry": "^2.0.1", - "read-package-json": "^6.0.0", - "read-package-json-fast": "^3.0.0", - "sigstore": "^1.3.0", - "ssri": "^10.0.0", - "tar": "^6.1.11" - }, - "bin": { - "pacote": "lib/bin.js" + "postcss": "^7.0.5" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/parse-conflict-json": { - "version": "3.0.1", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/postcss-modules-local-by-default": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.3.tgz", + "integrity": "sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw==", + "dev": true, "dependencies": { - "json-parse-even-better-errors": "^3.0.0", - "just-diff": "^6.0.0", - "just-diff-apply": "^5.2.0" + "icss-utils": "^4.1.1", + "postcss": "^7.0.32", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/semantic-release/node_modules/npm/node_modules/path-is-absolute": { - "version": "1.0.1", - "inBundle": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/semantic-release/node_modules/npm/node_modules/path-key": { - "version": "3.1.1", - "inBundle": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=8" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/path-scurry": { - "version": "1.9.2", - "inBundle": true, - "license": "BlueOak-1.0.0", - "peer": true, + "node_modules/postcss-modules-scope": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz", + "integrity": "sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ==", + "dev": true, "dependencies": { - "lru-cache": "^9.1.1", - "minipass": "^5.0.0 || ^6.0.2" + "postcss": "^7.0.6", + "postcss-selector-parser": "^6.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/path-scurry/node_modules/lru-cache": { - "version": "9.1.1", - "inBundle": true, - "license": "ISC", - "peer": true, - "engines": { - "node": "14 || >=16.14" + "node_modules/postcss-modules-values": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz", + "integrity": "sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg==", + "dev": true, + "dependencies": { + "icss-utils": "^4.0.0", + "postcss": "^7.0.6" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/postcss-selector-parser": { - "version": "6.0.13", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/postcss-selector-parser": { + "version": "6.0.11", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz", + "integrity": "sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==", + "dev": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -20093,1243 +10351,1120 @@ "node": ">=4" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/proc-log": { - "version": "3.0.0", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/postcss/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/process": { - "version": "0.11.10", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "dev": true, "engines": { - "node": ">= 0.6.0" + "node": ">= 0.8.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/promise-all-reject-late": { - "version": "1.0.1", - "inBundle": true, - "license": "ISC", - "peer": true, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node_modules/prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha512-PhmXi5XmoyKw1Un4E+opM2KcsJInDvKyuOumcjjw3waw86ZNjHwVUOOWLc4bCzLdcKNaWBH9e99sbWzDQsVaYg==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/promise-call-limit": { - "version": "1.0.2", - "inBundle": true, - "license": "ISC", - "peer": true, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node_modules/preserve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", + "integrity": "sha512-s/46sYeylUfHNjI+sA/78FAHlmIuKqI9wNnzEOGehAlUUYeObv5C2mOinXBjyUyWmJ2SfcS2/ydApH4hTF4WXQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/promise-inflight": { - "version": "1.0.1", - "inBundle": true, - "license": "ISC", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/promise-retry": { - "version": "2.0.1", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/pretty-format": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", + "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "dev": true, "dependencies": { - "err-code": "^2.0.2", - "retry": "^0.12.0" + "@jest/types": "^24.9.0", + "ansi-regex": "^4.0.0", + "ansi-styles": "^3.2.0", + "react-is": "^16.8.4" }, "engines": { - "node": ">=10" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/promzard": { - "version": "1.0.0", - "inBundle": true, - "license": "ISC", + "node_modules/private": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", + "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", + "dev": true, "peer": true, - "dependencies": { - "read": "^2.0.0" - }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">= 0.6" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/qrcode-terminal": { - "version": "0.12.0", - "inBundle": true, - "peer": true, - "bin": { - "qrcode-terminal": "bin/qrcode-terminal.js" - } + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true }, - "node_modules/semantic-release/node_modules/npm/node_modules/read": { - "version": "2.1.0", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, "dependencies": { - "mute-stream": "~1.0.0" + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/read-cmd-shim": { - "version": "4.0.0", - "inBundle": true, - "license": "ISC", - "peer": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/read-package-json": { - "version": "6.0.4", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/property-information": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz", + "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==", "dependencies": { - "glob": "^10.2.2", - "json-parse-even-better-errors": "^3.0.0", - "normalize-package-data": "^5.0.0", - "npm-normalize-package-bin": "^3.0.0" + "xtend": "^4.0.0" }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/read-package-json-fast": { - "version": "3.0.2", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "json-parse-even-better-errors": "^3.0.0", - "npm-normalize-package-bin": "^3.0.0" - }, + "node_modules/property-information/node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.4" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/readable-stream": { - "version": "4.4.0", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10" + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">= 0.10" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/retry": { - "version": "0.12.0", - "inBundle": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 4" - } + "node_modules/pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", + "dev": true }, - "node_modules/semantic-release/node_modules/npm/node_modules/rimraf": { - "version": "3.0.2", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "end-of-stream": "^1.1.0", + "once": "^1.3.1" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/rimraf/node_modules/brace-expansion": { - "version": "1.1.11", - "inBundle": true, - "license": "MIT", - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "engines": { + "node": ">=6" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/rimraf/node_modules/glob": { - "version": "7.2.3", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" }, "engines": { - "node": "*" + "node": ">=0.6" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/rimraf/node_modules/minimatch": { - "version": "3.1.2", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, + "node_modules/querystring": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.1.tgz", + "integrity": "sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", "engines": { - "node": "*" + "node": ">=0.4.x" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/safe-buffer": { - "version": "5.2.1", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "inBundle": true, - "license": "MIT", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/safer-buffer": { - "version": "2.1.2", - "inBundle": true, - "license": "MIT", - "optional": true, - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/semver": { - "version": "7.5.4", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/randomatic": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", + "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", + "dev": true, "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" + "is-number": "^4.0.0", + "kind-of": "^6.0.0", + "math-random": "^1.0.1" }, "engines": { - "node": ">=10" + "node": ">= 0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "yallist": "^4.0.0" - }, + "node_modules/randomatic/node_modules/is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true, "engines": { - "node": ">=10" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/set-blocking": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/shebang-command": { - "version": "2.0.0", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" + "safe-buffer": "^5.1.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/shebang-regex": { - "version": "3.0.0", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "engines": { - "node": ">=8" + "node": ">= 0.6" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/signal-exit": { - "version": "4.0.2", - "inBundle": true, - "license": "ISC", - "peer": true, - "engines": { - "node": ">=14" + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "engines": { + "node": ">= 0.8" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/sigstore": { - "version": "1.7.0", - "inBundle": true, - "license": "Apache-2.0", - "peer": true, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, "dependencies": { - "@sigstore/protobuf-specs": "^0.1.0", - "@sigstore/tuf": "^1.0.1", - "make-fetch-happen": "^11.0.1" + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" }, "bin": { - "sigstore": "bin/sigstore.js" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "rc": "cli.js" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/smart-buffer": { - "version": "4.2.0", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/react": { + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", + "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2" + }, "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/socks": { - "version": "2.7.1", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/react-class": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/react-class/-/react-class-3.2.2.tgz", + "integrity": "sha512-6mGkYJjqsCyazXfIIjAe3vf+KMsSTgJnyj7UK7tgFYIfv2lBQT9MB7rs8DQxESPNhBDu6UZ1jnTLpLzA2wKn6w==", "dependencies": { - "ip": "^2.0.0", - "smart-buffer": "^4.2.0" + "object-assign": "^4.1.0" }, - "engines": { - "node": ">= 10.13.0", - "npm": ">= 3.0.0" + "peerDependencies": { + "react": ">=0.14.0 || >=15.0.0-0", + "react-dom": ">=0.14.0 || >=15.0.0-0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/socks-proxy-agent": { - "version": "7.0.0", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/react-dom": { + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz", + "integrity": "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==", "dependencies": { - "agent-base": "^6.0.2", - "debug": "^4.3.3", - "socks": "^2.6.2" + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "scheduler": "^0.19.1" }, - "engines": { - "node": ">= 10" + "peerDependencies": { + "react": "^16.14.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/spdx-correct": { - "version": "3.2.0", - "inBundle": true, - "license": "Apache-2.0", - "peer": true, + "node_modules/react-image-crop": { + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/react-image-crop/-/react-image-crop-8.6.12.tgz", + "integrity": "sha512-3CNz1xfsRRSH/iH023IDMXxzsb1M6RTHHUVsVcb8uFPcjGiA9WisvQ24G1eRDf2j4NlybupOEEdfK2vT0etN6A==", "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" + "clsx": "^1.1.1", + "prop-types": "^15.7.2" + }, + "peerDependencies": { + "react": ">=16.13.1" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/spdx-exceptions": { - "version": "2.3.0", - "inBundle": true, - "license": "CC-BY-3.0", - "peer": true + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, - "node_modules/semantic-release/node_modules/npm/node_modules/spdx-expression-parse": { - "version": "3.0.1", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/react-jsonschema-form": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/react-jsonschema-form/-/react-jsonschema-form-1.8.1.tgz", + "integrity": "sha512-aaDloxNAcGXOOOcdKOxxqEEn5oDlPUZgWcs8unXXB9vjBRgCF8rCm/wVSv1u2G5ih0j/BX6Ewd/WjI2g00lPdg==", + "deprecated": "react-jsonschema-form has been moved to @rjsf/core", "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" + "@babel/runtime-corejs2": "^7.4.5", + "ajv": "^6.7.0", + "core-js": "^2.5.7", + "lodash": "^4.17.15", + "prop-types": "^15.5.8", + "react-is": "^16.8.4", + "react-lifecycles-compat": "^3.0.4", + "shortid": "^2.2.14" + }, + "engines": { + "node": ">=6", + "npm": ">=2.14.7" + }, + "peerDependencies": { + "react": ">=15" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/spdx-license-ids": { - "version": "3.0.13", - "inBundle": true, - "license": "CC0-1.0", - "peer": true + "node_modules/react-jsonschema-form/node_modules/core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", + "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", + "hasInstallScript": true }, - "node_modules/semantic-release/node_modules/npm/node_modules/ssri": { - "version": "10.0.4", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, + "node_modules/react-markdown": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-6.0.3.tgz", + "integrity": "sha512-kQbpWiMoBHnj9myLlmZG9T1JdoT/OEyHK7hqM6CqFT14MAkgWiWBUYijLyBmxbntaN6dCDicPcUhWhci1QYodg==", "dependencies": { - "minipass": "^5.0.0" + "@types/hast": "^2.0.0", + "@types/unist": "^2.0.3", + "comma-separated-tokens": "^1.0.0", + "prop-types": "^15.7.2", + "property-information": "^5.3.0", + "react-is": "^17.0.0", + "remark-parse": "^9.0.0", + "remark-rehype": "^8.0.0", + "space-separated-tokens": "^1.1.0", + "style-to-object": "^0.3.0", + "unified": "^9.0.0", + "unist-util-visit": "^2.0.0", + "vfile": "^4.0.0" }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=16", + "react": ">=16" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/string_decoder": { - "version": "1.3.0", - "inBundle": true, - "license": "MIT", - "peer": true, - "dependencies": { - "safe-buffer": "~5.2.0" + "node_modules/react-markdown/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + }, + "node_modules/react-tag-autocomplete": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/react-tag-autocomplete/-/react-tag-autocomplete-6.3.0.tgz", + "integrity": "sha512-MUBVUFh5eOqshUm5NM20qp7zXk8TzSiKO4GoktlFzBLIOLs356npaMKtL02bm0nFV2f1zInUrXn1fq6+i5YX0w==", + "engines": { + "node": ">= 10.0.0" + }, + "peerDependencies": { + "prop-types": "^15.5.0", + "react": "^16.5.0 || ^17.0.0", + "react-dom": "^16.5.0 || ^17.0.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/string-width": { - "version": "4.2.3", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "dev": true, "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" }, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/read-pkg-up": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", + "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", + "dev": true, "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "find-up": "^3.0.0", + "read-pkg": "^3.0.0" }, "engines": { - "node": ">=8" + "node": ">=6" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/strip-ansi": { - "version": "6.0.1", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dependencies": { - "ansi-regex": "^5.0.1" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" }, "engines": { - "node": ">=8" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "optional": true, "dependencies": { - "ansi-regex": "^5.0.1" + "picomatch": "^2.2.1" }, "engines": { - "node": ">=8" - } - }, - "node_modules/semantic-release/node_modules/npm/node_modules/supports-color": { - "version": "9.4.0", - "inBundle": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "node": ">=8.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/tar": { - "version": "6.1.15", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/realpath-native": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.1.0.tgz", + "integrity": "sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA==", + "dev": true, "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" + "util.promisify": "^1.0.0" }, "engines": { - "node": ">=10" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/tar/node_modules/fs-minipass": { - "version": "2.1.0", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/rechoir": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", + "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", + "dev": true, "dependencies": { - "minipass": "^3.0.0" + "resolve": "^1.9.0" }, "engines": { - "node": ">= 8" + "node": ">= 0.10" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { - "version": "3.3.6", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/redis": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/redis/-/redis-2.8.0.tgz", + "integrity": "sha512-M1OkonEQwtRmZv4tEWF2VgpG0JWJ8Fv1PhlgT5+B+uNq2cA3Rt1Yt/ryoR+vQNOQcIEgdCdfH0jr3bDpihAw1A==", "dependencies": { - "yallist": "^4.0.0" + "double-ended-queue": "^2.1.0-0", + "redis-commands": "^1.2.0", + "redis-parser": "^2.6.0" }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/text-table": { - "version": "0.2.0", - "inBundle": true, - "license": "MIT", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/tiny-relative-date": { - "version": "1.3.0", - "inBundle": true, - "license": "MIT", - "peer": true + "node_modules/redis-commands": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", + "integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==" }, - "node_modules/semantic-release/node_modules/npm/node_modules/treeverse": { - "version": "3.0.0", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/redis-parser": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-2.6.0.tgz", + "integrity": "sha512-9Hdw19gwXFBJdN8ENUoNVJFRyMDFrE/ZBClPicKYDPwNPJ4ST1TedAHYNSiGKElwh2vrmRGMoJYbVdJd+WQXIw==", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/tuf-js": { - "version": "1.1.7", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", + "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", + "dev": true, "dependencies": { - "@tufjs/models": "1.0.4", - "debug": "^4.3.4", - "make-fetch-happen": "^11.1.1" + "regenerate": "^1.4.2" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/unique-filename": { - "version": "3.0.0", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + }, + "node_modules/regenerator-transform": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz", + "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==", + "dev": true, "dependencies": { - "unique-slug": "^4.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "@babel/runtime": "^7.8.4" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/unique-slug": { - "version": "4.0.0", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/regex-cache": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", + "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", + "dev": true, "dependencies": { - "imurmurhash": "^0.1.4" + "is-equal-shallow": "^0.1.3" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/util-deprecate": { + "node_modules/regex-not": { "version": "1.0.2", - "inBundle": true, - "license": "MIT", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/validate-npm-package-license": { - "version": "3.0.4", - "inBundle": true, - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/semantic-release/node_modules/npm/node_modules/validate-npm-package-name": { - "version": "5.0.0", - "inBundle": true, - "license": "ISC", - "peer": true, + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, "dependencies": { - "builtins": "^5.0.0" + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/walk-up-path": { - "version": "3.0.1", - "inBundle": true, - "license": "ISC", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/wcwidth": { - "version": "1.0.1", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/regexp.prototype.flags": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", + "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", + "dev": true, "dependencies": { - "defaults": "^1.0.3" + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "functions-have-names": "^1.2.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/which": { - "version": "3.0.1", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/regexpu-core": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", + "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", + "dev": true, "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/which.js" + "@babel/regjsgen": "^0.8.0", + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsparser": "^0.9.1", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/wide-align": { - "version": "1.1.5", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/registry-auth-token": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz", + "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==", + "dev": true, "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" + "rc": "^1.1.6", + "safe-buffer": "^5.0.1" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/wrap-ansi": { - "version": "8.1.0", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/registry-url": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", + "integrity": "sha512-ZbgR5aZEdf4UKZVBPYIgaglBmSF2Hi94s2PcIHhRGFjKYu+chjJdYfHn4rt3hB6eCKLJ8giVIIfgMa1ehDfZKA==", + "dev": true, "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" + "rc": "^1.0.1" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/regjsparser": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "dev": true, "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/semantic-release/node_modules/npm/node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "6.0.1", - "inBundle": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=12" + "jsesc": "~0.5.0" }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "bin": { + "regjsparser": "bin/parser" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "6.2.1", - "inBundle": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/wrap-ansi/node_modules/emoji-regex": { - "version": "9.2.2", - "inBundle": true, - "license": "MIT", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/wrap-ansi/node_modules/string-width": { - "version": "5.1.2", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/remark-parse": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-9.0.0.tgz", + "integrity": "sha512-geKatMwSzEXKHuzBNU1z676sGcDcFoChMK38TgdHJNAYfFtsfHDQG7MoJAjs6sgYMqyLduCYWDIWZIxiPeafEw==", "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" + "mdast-util-from-markdown": "^0.8.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "7.1.0", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/remark-rehype": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-8.1.0.tgz", + "integrity": "sha512-EbCu9kHgAxKmW1yEYjx3QafMyGY3q8noUbNUI5xyKbaFP89wbhDrKxyIQNukNYthzjNHZu6J7hwFg7hRm1svYA==", "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" + "mdast-util-to-hast": "^10.2.0" }, "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/wrappy": { - "version": "1.0.2", - "inBundle": true, - "license": "ISC", - "peer": true + "node_modules/remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", + "dev": true }, - "node_modules/semantic-release/node_modules/npm/node_modules/write-file-atomic": { - "version": "5.0.1", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^4.0.1" - }, + "node_modules/repeat-element": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", + "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", + "dev": true, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/yallist": { - "version": "4.0.0", - "inBundle": true, - "license": "ISC", - "peer": true - }, - "node_modules/semantic-release/node_modules/p-each-series": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-3.0.0.tgz", - "integrity": "sha512-lastgtAdoH9YaLyDa5i5z64q+kzOcQHsQ5SsZJD3q0VEyI8mq872S3geuNbRUQLVAE9siMfgKrpj7MloKFHruw==", - "peer": true, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "dev": true, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10" } }, - "node_modules/semantic-release/node_modules/p-filter": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-3.0.0.tgz", - "integrity": "sha512-QtoWLjXAW++uTX67HZQz1dbTpqBfiidsB6VtQUC9iR85S120+s0T5sO6s+B5MLzFcZkrEd/DGMmCjR+f2Qpxwg==", + "node_modules/repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha512-ZqtSMuVybkISo2OWvqvm7iHSWngvdaW3IpsT9/uP8v4gMi591LY6h35wdOfvQdWCKFWZWm2Y1Opp4kV7vQKT6A==", + "dev": true, "peer": true, "dependencies": { - "p-map": "^5.1.0" + "is-finite": "^1.0.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/p-limit": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", - "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", - "peer": true, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", "dependencies": { - "yocto-queue": "^1.0.0" + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/p-locate": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", - "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", - "peer": true, + "node_modules/request-promise-core": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", + "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", + "dev": true, "dependencies": { - "p-limit": "^4.0.0" + "lodash": "^4.17.19" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=0.10.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "request": "^2.34" } }, - "node_modules/semantic-release/node_modules/p-map": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-5.5.0.tgz", - "integrity": "sha512-VFqfGDHlx87K66yZrNdI4YGtD70IRyd+zSvgks6mzHPRNkoKy+9EKP4SFC77/vTTQYmRmti7dvqC+m5jBrBAcg==", - "peer": true, + "node_modules/request-promise-native": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz", + "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==", + "deprecated": "request-promise-native has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142", + "dev": true, "dependencies": { - "aggregate-error": "^4.0.0" + "request-promise-core": "1.1.4", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" }, "engines": { - "node": ">=12" + "node": ">=0.12.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "request": "^2.34" } }, - "node_modules/semantic-release/node_modules/p-reduce": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-3.0.0.tgz", - "integrity": "sha512-xsrIUgI0Kn6iyDYm9StOpOeK29XM1aboGji26+QEortiFST1hGZaUQOLhtEbqHErPpGW/aSz6allwK2qcptp0Q==", - "peer": true, + "node_modules/request/node_modules/qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.6" } }, - "node_modules/semantic-release/node_modules/parse-json": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-7.0.0.tgz", - "integrity": "sha512-kP+TQYAzAiVnzOlWOe0diD6L35s9bJh0SCn95PIbZFKrOYuIRQsQkeWEYxzVDuHTt9V9YqvYCJ2Qo4z9wdfZPw==", - "peer": true, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "node_modules/resolve": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", + "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "dev": true, "dependencies": { - "@babel/code-frame": "^7.21.4", - "error-ex": "^1.3.2", - "json-parse-even-better-errors": "^3.0.0", - "lines-and-columns": "^2.0.3", - "type-fest": "^3.8.0" + "is-core-module": "^2.11.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" }, - "engines": { - "node": ">=16" + "bin": { + "resolve": "bin/resolve" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/semantic-release/node_modules/parse-json/node_modules/type-fest": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", - "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", - "peer": true, - "engines": { - "node": ">=14.16" + "node_modules/resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha512-ccu8zQTrzVr954472aUVPLEcB3YpKSYR3cg/3lo1okzobPBM+1INXBbBZlDbnI/hbEocnf8j0QVo43hQKrbchg==", + "dev": true, + "dependencies": { + "resolve-from": "^3.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/path-exists": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", - "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", - "peer": true, + "node_modules/resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", + "dev": true, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "peer": true, + "node_modules/resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==", + "deprecated": "https://github.com/lydell/resolve-url#deprecated", + "dev": true + }, + "node_modules/ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true, "engines": { - "node": ">=8" + "node": ">=0.12" } }, - "node_modules/semantic-release/node_modules/read-pkg": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-8.1.0.tgz", - "integrity": "sha512-PORM8AgzXeskHO/WEv312k9U03B8K9JSiWF/8N9sUuFjBa+9SF2u6K7VClzXwDXab51jCd8Nd36CNM+zR97ScQ==", - "peer": true, + "node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", "dependencies": { - "@types/normalize-package-data": "^2.4.1", - "normalize-package-data": "^6.0.0", - "parse-json": "^7.0.0", - "type-fest": "^4.2.0" + "glob": "^7.1.3" }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/rsvp": { + "version": "4.8.5", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", + "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", + "dev": true, "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "6.* || >= 7.*" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==", + "dev": true, + "dependencies": { + "ret": "~0.1.10" } }, - "node_modules/semantic-release/node_modules/read-pkg-up": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-10.1.0.tgz", - "integrity": "sha512-aNtBq4jR8NawpKJQldrQcSW9y/d+KWH4v24HWkHljOZ7H0av+YTGANBzRh9A5pw7v/bLVsLVPpOhJ7gHNVy8lA==", - "peer": true, + "node_modules/safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dev": true, "dependencies": { - "find-up": "^6.3.0", - "read-pkg": "^8.1.0", - "type-fest": "^4.2.0" - }, - "engines": { - "node": ">=16" + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/semantic-release/node_modules/registry-auth-token": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.0.2.tgz", - "integrity": "sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==", - "peer": true, - "dependencies": { - "@pnpm/npm-conf": "^2.1.0" - }, + "node_modules/safe-stable-stringify": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", + "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", "engines": { - "node": ">=14" + "node": ">=10" } }, - "node_modules/semantic-release/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "peer": true, - "engines": { - "node": ">=8" - } + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, - "node_modules/semantic-release/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "peer": true, + "node_modules/sane": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", + "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", + "deprecated": "some dependency vulnerabilities fixed, support for node < 10 dropped, and newer ECMAScript syntax/features added", + "dev": true, "dependencies": { - "lru-cache": "^6.0.0" + "@cnakazawa/watch": "^1.0.3", + "anymatch": "^2.0.0", + "capture-exit": "^2.0.0", + "exec-sh": "^0.3.2", + "execa": "^1.0.0", + "fb-watchman": "^2.0.0", + "micromatch": "^3.1.4", + "minimist": "^1.1.1", + "walker": "~1.0.5" }, "bin": { - "semver": "bin/semver.js" + "sane": "src/cli.js" }, "engines": { - "node": ">=10" + "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/semantic-release/node_modules/semver-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-4.0.0.tgz", - "integrity": "sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==", - "peer": true, + "node_modules/sane/node_modules/anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" } }, - "node_modules/semantic-release/node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "peer": true, + "node_modules/sane/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "dev": true, "dependencies": { - "yallist": "^4.0.0" + "remove-trailing-separator": "^1.0.1" }, "engines": { - "node": ">=10" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "peer": true, + "node_modules/sanitize-html": { + "version": "1.27.5", + "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.27.5.tgz", + "integrity": "sha512-M4M5iXDAUEcZKLXkmk90zSYWEtk5NH3JmojQxKxV371fnMh+x9t1rqdmXaGoyEHw3z/X/8vnFhKjGL5xFGOJ3A==", "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/semantic-release/node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/semantic-release/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "peer": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "htmlparser2": "^4.1.0", + "lodash": "^4.17.15", + "parse-srcset": "^1.0.2", + "postcss": "^7.0.27" } }, - "node_modules/semantic-release/node_modules/slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", - "peer": true, - "engines": { - "node": ">=12" + "node_modules/sanitize-html/node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/semantic-release/node_modules/split2": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", - "peer": true, - "engines": { - "node": ">= 10.x" + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" } }, - "node_modules/semantic-release/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "peer": true, + "node_modules/sanitize-html/node_modules/dom-serializer/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "domelementtype": "^2.2.0" }, "engines": { - "node": ">=8" - } - }, - "node_modules/semantic-release/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "peer": true, - "dependencies": { - "ansi-regex": "^5.0.1" + "node": ">= 4" }, - "engines": { - "node": ">=8" + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" } }, - "node_modules/semantic-release/node_modules/temp-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-3.0.0.tgz", - "integrity": "sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==", - "peer": true, - "engines": { - "node": ">=14.16" - } + "node_modules/sanitize-html/node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] }, - "node_modules/semantic-release/node_modules/tempy": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/tempy/-/tempy-3.1.0.tgz", - "integrity": "sha512-7jDLIdD2Zp0bDe5r3D2qtkd1QOCacylBuL7oa4udvN6v2pqr4+LcCr67C8DR1zkpaZ8XosF5m1yQSabKAW6f2g==", - "peer": true, + "node_modules/sanitize-html/node_modules/domhandler": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.3.0.tgz", + "integrity": "sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==", "dependencies": { - "is-stream": "^3.0.0", - "temp-dir": "^3.0.0", - "type-fest": "^2.12.2", - "unique-string": "^3.0.0" + "domelementtype": "^2.0.1" }, "engines": { - "node": ">=14.16" + "node": ">= 4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/fb55/domhandler?sponsor=1" } }, - "node_modules/semantic-release/node_modules/tempy/node_modules/type-fest": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", - "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", - "peer": true, - "engines": { - "node": ">=12.20" + "node_modules/sanitize-html/node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/fb55/domutils?sponsor=1" } }, - "node_modules/semantic-release/node_modules/text-extensions": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.4.0.tgz", - "integrity": "sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==", - "peer": true, + "node_modules/sanitize-html/node_modules/domutils/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dependencies": { + "domelementtype": "^2.2.0" + }, "engines": { - "node": ">=8" + "node": ">= 4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/fb55/domhandler?sponsor=1" } }, - "node_modules/semantic-release/node_modules/type-fest": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.3.1.tgz", - "integrity": "sha512-pphNW/msgOUSkJbH58x8sqpq8uQj6b0ZKGxEsLKMUnGorRcDjrUaLS+39+/ub41JNTwrrMyJcUB8+YZs3mbwqw==", - "peer": true, - "engines": { - "node": ">=16" - }, + "node_modules/sanitize-html/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/semantic-release/node_modules/unique-string": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", - "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", - "peer": true, + "node_modules/sanitize-html/node_modules/htmlparser2": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-4.1.0.tgz", + "integrity": "sha512-4zDq1a1zhE4gQso/c5LP1OtrhYTncXNSpvJYtWJBtXAETPlMfi3IFNjGuQbYLuVY4ZR0QMqRVvo4Pdy9KLyP8Q==", "dependencies": { - "crypto-random-string": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "domelementtype": "^2.0.1", + "domhandler": "^3.0.0", + "domutils": "^2.0.0", + "entities": "^2.0.0" } }, - "node_modules/semantic-release/node_modules/url-join": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-5.0.0.tgz", - "integrity": "sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==", - "peer": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true }, - "node_modules/semantic-release/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "peer": true, + "node_modules/scheduler": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", + "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==", "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" } }, - "node_modules/semantic-release/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "peer": true, + "node_modules/schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dev": true, "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" }, "engines": { - "node": ">=10" + "node": ">= 8.9.0" }, "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/semantic-release/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "peer": true, - "engines": { - "node": ">=10" + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "node_modules/semantic-release/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "peer": true - }, - "node_modules/semantic-release/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "peer": true, + "node_modules/scroll-into-view-if-needed": { + "version": "2.2.31", + "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.31.tgz", + "integrity": "sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==", "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" + "compute-scroll-into-view": "^1.0.20" } }, - "node_modules/semantic-release/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "peer": true, + "node_modules/seekout": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/seekout/-/seekout-1.0.2.tgz", + "integrity": "sha512-eB4fRqHZCE8gmwFOVHC9tHzLkZJ2Y12qJvAJQox8kWjGObA++tgVeXHACsDCPajfNGnDaK2Juv+WzNlMuO43wQ==", "engines": { - "node": ">=12" + "node": ">=0.12" } }, "node_modules/semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, "bin": { "semver": "bin/semver.js" } @@ -21355,18 +11490,6 @@ "semver": "bin/semver" } }, - "node_modules/semver-regex": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-4.0.5.tgz", - "integrity": "sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw==", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/send": { "version": "0.18.0", "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", @@ -21539,33 +11662,8 @@ "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, - "node_modules/signale": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/signale/-/signale-1.4.0.tgz", - "integrity": "sha512-iuh+gPf28RkltuJC7W5MRi6XAjTDCAPC/prJUpQoG4vIP3MJZ+GTydVnodXA7pwvTKb2cA0m9OFZW/cdWy/I/w==", - "peer": true, - "dependencies": { - "chalk": "^2.3.2", - "figures": "^2.0.0", - "pkg-conf": "^2.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/signale/node_modules/figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==", - "peer": true, - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=4" - } + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true }, "node_modules/simple-swizzle": { "version": "0.2.2", @@ -21945,16 +12043,11 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/spawn-error-forwarder": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/spawn-error-forwarder/-/spawn-error-forwarder-1.0.0.tgz", - "integrity": "sha512-gRjMgK5uFjbCvdibeGJuy3I5OYz6VLoVdsOJdA6wV0WlfQVLFueoqMxwwYD9RODdgb6oUIvlRlsyFSiQkMKu0g==", - "peer": true - }, "node_modules/spdx-correct": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, "dependencies": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" @@ -21963,12 +12056,14 @@ "node_modules/spdx-exceptions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true }, "node_modules/spdx-expression-parse": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" @@ -21977,18 +12072,8 @@ "node_modules/spdx-license-ids": { "version": "3.0.13", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz", - "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==" - }, - "node_modules/split": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", - "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", - "dependencies": { - "through": "2" - }, - "engines": { - "node": "*" - } + "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==", + "dev": true }, "node_modules/split-string": { "version": "3.1.0", @@ -22002,14 +12087,6 @@ "node": ">=0.10.0" } }, - "node_modules/split2": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", - "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", - "dependencies": { - "readable-stream": "^3.0.0" - } - }, "node_modules/sshpk": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", @@ -22176,46 +12253,6 @@ "node": ">=0.10.0" } }, - "node_modules/stream-combiner2": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", - "integrity": "sha512-3PnJbYgS56AeWgtKF5jtJRT6uFJe56Z0Hc5Ngg/6sI6rIt8iiMBTa9cvdyFfpMQjaVHr8dusbNeFGIIonxOvKw==", - "peer": true, - "dependencies": { - "duplexer2": "~0.1.0", - "readable-stream": "^2.0.2" - } - }, - "node_modules/stream-combiner2/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "peer": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/stream-combiner2/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "peer": true - }, - "node_modules/stream-combiner2/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "peer": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -22353,6 +12390,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, "engines": { "node": ">=4" } @@ -22366,33 +12404,11 @@ "node": ">=0.10.0" } }, - "node_modules/strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dependencies": { - "min-indent": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -22441,44 +12457,11 @@ "node": ">=4" } }, - "node_modules/supports-hyperlinks": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", - "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", - "peer": true, - "dependencies": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-hyperlinks/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-hyperlinks/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "peer": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, "engines": { "node": ">= 0.4" }, @@ -22506,73 +12489,6 @@ "node": ">=6" } }, - "node_modules/temp-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", - "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/tempy": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tempy/-/tempy-1.0.1.tgz", - "integrity": "sha512-biM9brNqxSc04Ee71hzFbryD11nX7VPhQQY32AdDmjFvodsRFz/3ufeoTZ6uYkRFfGo188tENcASNs3vTdsM0w==", - "dependencies": { - "del": "^6.0.0", - "is-stream": "^2.0.0", - "temp-dir": "^2.0.0", - "type-fest": "^0.16.0", - "unique-string": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/tempy/node_modules/crypto-random-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/tempy/node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/tempy/node_modules/type-fest": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", - "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/tempy/node_modules/unique-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", - "dependencies": { - "crypto-random-string": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/term-size": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", @@ -22799,14 +12715,6 @@ "node": ">=6" } }, - "node_modules/text-extensions": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz", - "integrity": "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==", - "engines": { - "node": ">=0.10" - } - }, "node_modules/text-hex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", @@ -22818,19 +12726,6 @@ "integrity": "sha512-wCVxLDcFxw7ujDxaeJC6nfl2XfHJNYs8yUYJnvMgtPEFlttP9tHSfRUv2vBe6C4hkVFPWoP1P6ZccbYjmSEkKA==", "dev": true }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" - }, - "node_modules/through2": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", - "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", - "dependencies": { - "readable-stream": "3" - } - }, "node_modules/timed-out": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", @@ -22975,23 +12870,6 @@ "punycode": "^2.1.0" } }, - "node_modules/traverse": { - "version": "0.6.7", - "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.7.tgz", - "integrity": "sha512-/y956gpUo9ZNCb99YjxG7OaslxZWHfCHAUUfshwqOXmxUIvqLjVO581BT+gM59+QV9tFe6/CGG53tsA1Y7RSdg==", - "peer": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/trim-newlines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", - "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", - "engines": { - "node": ">=8" - } - }, "node_modules/trim-right": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", @@ -23049,17 +12927,6 @@ "node": ">= 0.8.0" } }, - "node_modules/type-fest": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", - "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -23086,18 +12953,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/uglify-js": { - "version": "3.17.4", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", - "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -23317,11 +13172,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/universal-user-agent": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", - "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" - }, "node_modules/universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", @@ -23495,11 +13345,6 @@ "deprecated": "Please see https://github.com/lydell/urix#deprecated", "dev": true }, - "node_modules/url-join": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", - "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==" - }, "node_modules/url-parse-lax": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", @@ -23563,6 +13408,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, "dependencies": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" @@ -24146,11 +13992,6 @@ "node": ">=0.10.0" } }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" - }, "node_modules/wrap-ansi": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", @@ -24188,6 +14029,7 @@ "version": "2.4.1", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.1.tgz", "integrity": "sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg==", + "dev": true, "dependencies": { "graceful-fs": "^4.1.11", "imurmurhash": "^0.1.4", @@ -24295,18 +14137,6 @@ "engines": { "node": ">=6" } - }, - "node_modules/yocto-queue": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", - "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", - "peer": true, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } } } } From 2517dae58e9c4e19946b4ba56e829d95a6eb3785 Mon Sep 17 00:00:00 2001 From: Akiva Berger <akiva10b@gmail.com> Date: Wed, 6 Sep 2023 15:37:19 +0300 Subject: [PATCH 199/756] chore(setup): add docker compose file to facilitate easy setup The docker-compose file sets up all the necessary requirements to run Sefaria-Project, bypassing a longstanding pain point that Sefaria is difficult to set up --- Dockerfile | 26 +++++++++++++++ README.mkd | 80 ++++++++++++++++++++++++++++++++++++++++++++++ docker-compose.yml | 49 ++++++++++++++++++++++++++++ 3 files changed, 155 insertions(+) create mode 100644 Dockerfile create mode 100644 docker-compose.yml diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000..63ba83b693 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,26 @@ +# Use an official Python runtime as a parent image +FROM python:3.7-slim + +# Set the working directory to /app +WORKDIR /app + +# Copy the current directory contents into the container at /app +COPY . /app + +# Install psycopg2 dependencies +RUN apt-get update && apt-get install -y libpq-dev + +# Install gcc +RUN apt-get update && apt-get install -y gcc + +# Install any needed packages specified in requirements.txt +RUN pip install --trusted-host pypi.python.org -r requirements.txt + +# Make port 8000 available to the world outside this container +EXPOSE 8000 + +# Define environment variable +ENV NAME Sefaria-Project + +# Run app.py when the container launches +CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"] diff --git a/README.mkd b/README.mkd index 6b248a75c7..582de41930 100644 --- a/README.mkd +++ b/README.mkd @@ -25,7 +25,87 @@ First clone the Sefaria-Project repository to a directory on your computer, then *Note for macOS users - Install `Homebrew`:* /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)" + + +There are two methods with which you can run the Sefaria-Project, docker-compose and local install + +#### Run with docker-compose +##### 1) Install Docker and Docker Compose + +Follow the instructions [here](https://docs.docker.com/docker-for-mac/install/) to install Docker and [here](https://docs.docker.com/compose/install/) to install Docker Compose. + +##### 2) Run the project +In your terminal run: + + docker-compose up + +This will build the project and run it. You should now have all the proper services set up, lets add some texts. + +##### 3) Connect to mongo and add texts: +Connect to mongo running on port 27018. We use 27018 instead of the standard port 27017 to avoid conflicts with any mongo instance you may already have running. + +Follow instructions in section 8 below to restore the mongo dump. + + +##### 4) Update your local settings file: +Copy the local settings file: + + cp sefaria/local_settings_example.py sefaria/local_settings.py + +Replace the following values: + + DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql', + 'NAME': 'sefaria', + 'USER': 'admin', + 'PASSWORD': 'admin', + 'HOST': 'postgres', + 'PORT': '', + } + } + + + MONGO_HOST = "db" + +Optionally, you can replace the cache values as well: + MULTISERVER_REDIS_SERVER = "cache" + REDIS_HOST = "cache" + +and the respective values in CACHES + + +##### 5) Connect to the django container and run migrations: +In a new terminal window run: + + docker exec -it sefaria-project_web_1 bash + +This will connect you to the django container. Now run: + + python manage.py migrate + +##### 6) Run webpack: +In a new terminal window run: + + docker exec -it sefaria-project-node-1 bash + +This will connect you to the django container. Now run: + + npm run build-client + +or + + npm run watch-client + + +##### 7) Visit the site: +In your browser go to http://localhost:8000 + +If the server isn't running, you may need to run `docker-compose up` again. + + +#### Run locally #### 1) Install Python 3.7 *We Recommend using the latest Python 3.7 as opposed to later versions of Python (esp 3.10 and up) since it has been known to cause some compatability issues. These are solvable, but for an easier install experience, we currently recommend 3.7* diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000000..725691f7b9 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,49 @@ +version: '3' + +services: + web: + build: . + ports: + - "8000:8000" + volumes: + - .:/app + depends_on: + - db + - cache + - postgres + + db: + image: "mongo:4.4" + ports: + - "27018:27017" + volumes: + - mongo-data:/data/db + + cache: + image: "redis:latest" + ports: + - "6379:6379" + + node: + image: "node:latest" + working_dir: /app + volumes: + - .:/app + command: npm start + ports: + - "3000:3000" + + postgres: + image: "postgres:latest" + environment: + POSTGRES_USER: admin + POSTGRES_PASSWORD: admin + POSTGRES_DB: sefaria + ports: + - "5433:5432" + volumes: + - postgres-data:/var/lib/postgresql/sefaria_data + +volumes: + mongo-data: + postgres-data: From 31148341a93d8edf66795090f1c64dff3b3188dc Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Wed, 6 Sep 2023 16:19:14 +0300 Subject: [PATCH 200/756] feat(api texts): make api work with lexicons. --- api/views.py | 2 +- sefaria/model/text.py | 10 ++++++++-- sefaria/model/text_manager.py | 5 +++-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/api/views.py b/api/views.py index 9577f5967d..a81189f564 100644 --- a/api/views.py +++ b/api/views.py @@ -40,7 +40,7 @@ def _handle_warnings(self, data): return data def get(self, request, *args, **kwargs): - if self.oref.is_empty(): + if self.oref.is_empty() and not self.oref.index_node.is_virtual: return jsonResponse({'error': f'We have no text for {self.oref}.'}, status=400) versions_params = request.GET.getlist('version', []) if not versions_params: diff --git a/sefaria/model/text.py b/sefaria/model/text.py index 1897d4c6d0..dac6b9a5df 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -1742,7 +1742,7 @@ def __call__(cls, *args, **kwargs): class TextRange: def __init__(self, oref, lang, vtitle): - if isinstance(oref.index_node, JaggedArrayNode): #text cannot be SchemaNode + if isinstance(oref.index_node, JaggedArrayNode) or isinstance(oref.index_node, DictionaryEntryNode): #text cannot be SchemaNode self.oref = oref elif oref.has_default_child(): #use default child: self.oref = oref.default_child_ref() @@ -1787,7 +1787,10 @@ def _trim_text(self, text): @property def text(self): if self._text is None: - self._text = self._trim_text(self.version.content_node(self.oref.index_node)) #todo if there is no version it will fail + if self.oref.index_node.is_virtual: + self._text = self.oref.index_node.get_text() + else: + self._text = self._trim_text(self.version.content_node(self.oref.index_node)) #todo if there is no version it will fail return self._text @@ -4497,6 +4500,9 @@ def part_projection(self): # todo: reimplement w/ aggregation pipeline (see above) # todo: special case string 0? + if self.index_node.is_virtual: + return + projection = {k: 1 for k in Version.required_attrs + Version.optional_attrs} del projection[Version.content_attr] # Version.content_attr == "chapter" projection["_id"] = 0 diff --git a/sefaria/model/text_manager.py b/sefaria/model/text_manager.py index c8508c3f5a..6ee0219b24 100644 --- a/sefaria/model/text_manager.py +++ b/sefaria/model/text_manager.py @@ -37,7 +37,7 @@ def _append_version(self, version): def _append_required_versions(self, lang: str, vtitle: str) -> None: if lang == self.BASE: - lang_condition = lambda v: getattr(v, 'isBaseText2', False) # temporal name + lang_condition = lambda v: getattr(v, 'isBaseText', False) elif lang == self.SOURCE: lang_condition = lambda v: getattr(v, 'isSource', False) elif lang == self.TRANSLATION: @@ -108,8 +108,9 @@ def _add_node_data_to_return_obj(self) -> None: 'heTitle': inode.full_title("he"), 'titleVariants': inode.all_tree_titles("en"), 'heTitleVariants': inode.all_tree_titles("he"), - 'index_offsets_by_depth': inode.trim_index_offsets_by_sections(self.oref.sections, self.oref.toSections), }) + if not inode.is_virtual: + self.return_obj['index_offsets_by_depth'] = inode.trim_index_offsets_by_sections(self.oref.sections, self.oref.toSections) def get_versions_for_query(self) -> dict: for lang, vtitle in self.versions_params: From 94536dcf54c6fb77ea9f68996520aac49f6e5691 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Wed, 6 Sep 2023 14:15:16 -0400 Subject: [PATCH 201/756] Simplify environment variable setup for local and development --- templates/base.html | 4 ---- 1 file changed, 4 deletions(-) diff --git a/templates/base.html b/templates/base.html index 6e0f0c820c..96e7ed04cd 100644 --- a/templates/base.html +++ b/templates/base.html @@ -238,11 +238,7 @@ }; {% if STRAPI_LOCATION and STRAPI_PORT %} - {% if STRAPI_PORT == "80" %} - var STRAPI_INSTANCE = "{{ STRAPI_LOCATION }}"; - {% else %} var STRAPI_INSTANCE = "{{ STRAPI_LOCATION }}:{{ STRAPI_PORT }}"; - {% endif %} {% endif %} {% endautoescape %} From 980b79f6125f46da8ae81e82dd8b2dbdacba3469 Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Wed, 6 Sep 2023 21:40:31 +0300 Subject: [PATCH 202/756] feat(Topic Image): First pass --- static/css/s2.css | 3 +++ static/js/TopicPage.jsx | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/static/css/s2.css b/static/css/s2.css index 25773f7575..2771099352 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -2204,6 +2204,9 @@ div.interfaceLinks-row a { font-family: "Taamey Frank", "adobe-garamond-pro", "Crimson Text", Georgia, "Times New Roman", serif; margin-bottom: -3px; } +.topicImage{ + border: 20px solid rgba(242, 15, 15, 1); +} .readerPanel .translationsPage h2 { margin: 40px 0 0 0; font-size: 24px; diff --git a/static/js/TopicPage.jsx b/static/js/TopicPage.jsx index 4e4f2c3e33..c04a7f9786 100644 --- a/static/js/TopicPage.jsx +++ b/static/js/TopicPage.jsx @@ -697,6 +697,15 @@ const TopicSideSection = ({ title, children, hasMore }) => { ); } +const TopicImage = ({ title, children, hasMore }) => { + const [showMore, setShowMore] = useState(false); + return ( + <div> + <img class="topicImage" src="https://museums.cjh.org/web/objects/common/webmedia.php?irn=11469&reftable=ecatalogue&refirn=6640" /> + </div> + ); +} + const ReadingsComponent = ({ parashaData, tref }) => ( <div className="readings link-section"> @@ -792,6 +801,7 @@ const TopicMetaData = ({ timePeriod, properties={} }) => { ) : null; return ( <> + <TopicImage /> { tpSection } { propsSection } </> From 97b03bce1a9404d519ea768da85d48cf784c45b8 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Wed, 6 Sep 2023 15:23:54 -0400 Subject: [PATCH 203/756] Flip order of clauses. An already logged in user should be considered a returning visitor when this new feature is deployed. Everyone else will be considered a new visitor after release --- static/js/ReaderApp.jsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index 003017184f..bb9f1693e9 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -200,12 +200,12 @@ class ReaderApp extends Component { document.addEventListener('click', this.handleInAppClickWithModifiers, {capture: true}); // Save all initial panels to recently viewed this.state.panels.map(this.saveLastPlace); - // Initialize entries for first-time visitors to determine if they are new or returning presently or in the future - if (Sefaria.isNewVisitor()) { - Sefaria.markUserAsNewVisitor(); - } else if (Sefaria._uid) { + if (Sefaria._uid) { // A logged in user is automatically a returning visitor Sefaria.markUserAsReturningVisitor(); + } else if (Sefaria.isNewVisitor()) { + // Initialize entries for first-time visitors to determine if they are new or returning presently or in the future + Sefaria.markUserAsNewVisitor(); } } componentWillUnmount() { From 31ff450537ace1d17f4e6ba61b9a2306d759b6b3 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Wed, 6 Sep 2023 15:54:22 -0400 Subject: [PATCH 204/756] Revise some documentation on OnInView component --- static/js/Misc.jsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 5413064307..70b4ef38d3 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -2056,15 +2056,20 @@ SignUpModal.propTypes = { modalContent: PropTypes.object.isRequired, }; -// Have a callback function run when its children (element or nested group of elements) become fully visible within the viewport + function OnInView({ children, onVisible }) { + /** + * Functional component that allows a callback function to run when its children become fully visible within the viewport + * `children` single element or nested group of elements wrapped in a div + * `onVisible` callback function that will be called when given component(s) are visible + */ const elementRef = useRef(); useEffect(() => { const observer = new IntersectionObserver( // Callback function will be invoked whenever the visibility of the observed element changes (entries) => { - const [entry] = entries; + const entry = entries[0]; // Check if the observed element is intersecting with the viewport (it's visible) // Invoke provided prop callback for analytics purposes if (entry.isIntersecting) { From 90dc1a1647d4b3b8aaf64a791fddef7c379e570a Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Wed, 6 Sep 2023 16:07:20 -0400 Subject: [PATCH 205/756] Further revise documentation --- static/js/Misc.jsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 70b4ef38d3..dedb36af76 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -2059,9 +2059,10 @@ SignUpModal.propTypes = { function OnInView({ children, onVisible }) { /** - * Functional component that allows a callback function to run when its children become fully visible within the viewport + * The functional component takes an existing element and wraps it in an IntersectionObserver and returns the children, only observed and with a callback for the observer. * `children` single element or nested group of elements wrapped in a div - * `onVisible` callback function that will be called when given component(s) are visible + * `onVisible` callback function that will be called when given component(s) are visible within the viewport + * Ex. <OnInView onVisible={handleImageIsVisible}><img src="..." /></OnInView> */ const elementRef = useRef(); From 04624f7d351d0ee64a9091c44ed929290f65e5ec Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Wed, 6 Sep 2023 16:46:49 -0400 Subject: [PATCH 206/756] Rename variables for clarity --- static/js/Promotions.jsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/static/js/Promotions.jsx b/static/js/Promotions.jsx index a8cb3ded22..b652a98863 100644 --- a/static/js/Promotions.jsx +++ b/static/js/Promotions.jsx @@ -38,8 +38,8 @@ const Promotions = () => { trigger: { showTo: sidebarAd.showTo, interfaceLang: "english", - dt_start: Date.parse(sidebarAd.startTime), - dt_end: Date.parse(sidebarAd.endTime), + startTimeDate: Date.parse(sidebarAd.startTime), + endTimeDate: Date.parse(sidebarAd.endTime), keywordTargets: keywordTargetsArray, excludeKeywordTargets: excludeKeywordTargets, }, @@ -66,8 +66,8 @@ const Promotions = () => { trigger: { showTo: sidebarAd.showTo, interfaceLang: "hebrew", - dt_start: Date.parse(sidebarAd.startTime), - dt_end: Date.parse(sidebarAd.endTime), + startTimeDate: Date.parse(sidebarAd.startTime), + endTimeDate: Date.parse(sidebarAd.endTime), keywordTargets: keywordTargetsArray, excludeKeywordTargets: excludeKeywordTargets, }, @@ -117,8 +117,8 @@ const Promotions = () => { showToUser(ad) && showGivenDebugMode(ad) && ad.trigger.interfaceLang === context.interfaceLang && - context.dt > ad.trigger.dt_start && - context.dt < ad.trigger.dt_end && + context.dt >= ad.trigger.startTimeDate && + context.dt <= ad.trigger.endTimeDate && (context.keywordTargets.some((kw) => ad.trigger.keywordTargets.includes(kw) ) || From 9f33bf4fe21a9645f1eb465b8d6a940c403563c5 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Thu, 7 Sep 2023 08:19:48 +0300 Subject: [PATCH 207/756] feat(Search Results): Normalize ref if Sefaria.parseRef can't find the index --- static/js/SearchTextResult.jsx | 69 +++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 30 deletions(-) diff --git a/static/js/SearchTextResult.jsx b/static/js/SearchTextResult.jsx index fc5c188399..571fb90b4c 100644 --- a/static/js/SearchTextResult.jsx +++ b/static/js/SearchTextResult.jsx @@ -14,9 +14,18 @@ class SearchTextResult extends Component { super(props); this.state = { - duplicatesShown: false + duplicatesShown: false, + ref: this.props.data._source.ref }; } + componentDidMount() { + const parsedRef = Sefaria.parseRef(this.state.ref); + if (parsedRef.index.length === 0) { // if URL has old title, there won't be an index, so normalize "Bereishit Rabbah 3" => "Bereshit Rabbah 3" by calling API + Sefaria.getRef(this.state.ref).then(d => { + this.setState({ref: d.ref}); + }); + } + } toggleDuplicates(event) { this.setState({ duplicatesShown: !this.state.duplicatesShown @@ -52,12 +61,12 @@ class SearchTextResult extends Component { const textHighlights = this.getHighlights(); //console.log(textHighlights); if (this.props.searchInBook) { - Sefaria.track.event("Search", "Sidebar Search Result Click", `${this.props.query} - ${s.ref}/${s.version}/${s.lang}`); + Sefaria.track.event("Search", "Sidebar Search Result Click", `${this.props.query} - ${this.state.ref}/${s.version}/${s.lang}`); } else { - Sefaria.track.event("Search", "Search Result Text Click", `${this.props.query} - ${s.ref}/${s.version}/${s.lang}`); + Sefaria.track.event("Search", "Search Result Text Click", `${this.props.query} - ${this.state.ref}/${s.version}/${s.lang}`); } - this.props.onResultClick(s.ref, {[s.lang]: s.version}, { textHighlights }); + this.props.onResultClick(this.state.ref, {[s.lang]: s.version}, { textHighlights }); } } get_snippet_markup(data) { @@ -79,38 +88,38 @@ class SearchTextResult extends Component { snippet = snippet.replace(/^[ .,;:!-)\]]+/, ""); return { markup:{__html:snippet}, lang }; } - render () { + render() { var data = this.props.data; var s = this.props.data._source; - const href = `/${Sefaria.normRef(s.ref)}?v${s.lang}=${Sefaria.util.encodeVtitle(s.version)}&qh=${this.props.query}`; + const href = `/${Sefaria.normRef(this.state.ref)}?v${s.lang}=${Sefaria.util.encodeVtitle(s.version)}&qh=${this.props.query}`; const more_results_caret = (this.state.duplicatesShown) - ? <i className="fa fa-caret-down fa-angle-down"></i> - : <i className="fa fa-caret-down"></i>; + ? <i className="fa fa-caret-down fa-angle-down"></i> + : <i className="fa fa-caret-down"></i>; const more_results_indicator = (!(data.duplicates)) ? "" : - <div className='similar-trigger-box' onClick={this.toggleDuplicates}> + <div className='similar-trigger-box' onClick={this.toggleDuplicates}> <span className='similar-title int-he'> - { data.duplicates.length } {(data.duplicates.length > 1) ? " גרסאות נוספות" : " גרסה נוספת"} + {data.duplicates.length} {(data.duplicates.length > 1) ? " גרסאות נוספות" : " גרסה נוספת"} </span> - <span className='similar-title int-en'> - { data.duplicates.length } more version{(data.duplicates.length > 1) ? "s" : null} + <span className='similar-title int-en'> + {data.duplicates.length} more version{(data.duplicates.length > 1) ? "s" : null} </span> - {more_results_caret} - </div>; + {more_results_caret} + </div>; const shown_duplicates = (data.duplicates && this.state.duplicatesShown) ? (<div className='similar-results'> - {data.duplicates.filter(result => !!result._source.version).map(function(result) { - var key = result._source.ref + "-" + result._source.version; - return <SearchTextResult - data={result} - key={key} - query={this.props.query} - onResultClick={this.props.onResultClick} - />; - }.bind(this))} + {data.duplicates.filter(result => !!result._source.version).map(function (result) { + var key = result._source.ref + "-" + result._source.version; + return <SearchTextResult + data={result} + key={key} + query={this.props.query} + onResultClick={this.props.onResultClick} + />; + }.bind(this))} </div>) : null; const snippetMarkup = this.get_snippet_markup(data); @@ -119,15 +128,15 @@ class SearchTextResult extends Component { <div className="result textResult"> <a href={href} onClick={this.handleResultClick}> <div className="result-title"> - <InterfaceText text={{en: s.ref, he: s.heRef}} /> + <InterfaceText text={{en: this.state.ref, he: s.heRef}}/> </div> </a> - <ColorBarBox tref={s.ref}> - <div className={snippetClasses} dangerouslySetInnerHTML={snippetMarkup.markup} ></div> - </ColorBarBox> - <div className="version"> - {Sefaria.interfaceLang==='hebrew' && s.hebrew_version_title || s.version} - </div> + <ColorBarBox tref={this.state.ref}> + <div className={snippetClasses} dangerouslySetInnerHTML={snippetMarkup.markup}></div> + </ColorBarBox> + <div className="version"> + {Sefaria.interfaceLang === 'hebrew' && s.hebrew_version_title || s.version} + </div> {more_results_indicator} {shown_duplicates} From 30deacab3dae8cd424c96e1d05056e5e36d3a89b Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Thu, 7 Sep 2023 10:17:44 +0300 Subject: [PATCH 208/756] chore: moved normalization of search result ref into handleResultClick --- static/js/SearchTextResult.jsx | 38 ++++++++++++++++------------------ 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/static/js/SearchTextResult.jsx b/static/js/SearchTextResult.jsx index 571fb90b4c..6bc6f8ef4c 100644 --- a/static/js/SearchTextResult.jsx +++ b/static/js/SearchTextResult.jsx @@ -14,18 +14,9 @@ class SearchTextResult extends Component { super(props); this.state = { - duplicatesShown: false, - ref: this.props.data._source.ref + duplicatesShown: false }; } - componentDidMount() { - const parsedRef = Sefaria.parseRef(this.state.ref); - if (parsedRef.index.length === 0) { // if URL has old title, there won't be an index, so normalize "Bereishit Rabbah 3" => "Bereshit Rabbah 3" by calling API - Sefaria.getRef(this.state.ref).then(d => { - this.setState({ref: d.ref}); - }); - } - } toggleDuplicates(event) { this.setState({ duplicatesShown: !this.state.duplicatesShown @@ -54,19 +45,26 @@ class SearchTextResult extends Component { // highlights = highlights.filter(h => h.length === longestLength); return highlights; } - handleResultClick(event) { - if(this.props.onResultClick) { + async handleResultClick(event) { + if (this.props.onResultClick) { event.preventDefault(); const s = this.props.data._source; const textHighlights = this.getHighlights(); //console.log(textHighlights); - if (this.props.searchInBook) { - Sefaria.track.event("Search", "Sidebar Search Result Click", `${this.props.query} - ${this.state.ref}/${s.version}/${s.lang}`); + // in case a change to a title was made and ElasticSearch cronjob hasn't run, + // there won't be an index, so normalize "Bereishit Rabbah 3" => "Bereshit Rabbah 3" by calling API + let parsedRef = Sefaria.parseRef(s.ref); + if (parsedRef.index.length === 0) { + const d = await Sefaria.getRef(s.ref); + parsedRef.ref = d.ref; } - else { - Sefaria.track.event("Search", "Search Result Text Click", `${this.props.query} - ${this.state.ref}/${s.version}/${s.lang}`); + + if (this.props.searchInBook) { + Sefaria.track.event("Search", "Sidebar Search Result Click", `${this.props.query} - ${parsedRef.ref}/${s.version}/${s.lang}`); + } else { + Sefaria.track.event("Search", "Search Result Text Click", `${this.props.query} - ${parsedRef.ref}/${s.version}/${s.lang}`); } - this.props.onResultClick(this.state.ref, {[s.lang]: s.version}, { textHighlights }); + this.props.onResultClick(parsedRef.ref, {[s.lang]: s.version}, {textHighlights}); } } get_snippet_markup(data) { @@ -91,7 +89,7 @@ class SearchTextResult extends Component { render() { var data = this.props.data; var s = this.props.data._source; - const href = `/${Sefaria.normRef(this.state.ref)}?v${s.lang}=${Sefaria.util.encodeVtitle(s.version)}&qh=${this.props.query}`; + const href = `/${Sefaria.normRef(s.ref)}?v${s.lang}=${Sefaria.util.encodeVtitle(s.version)}&qh=${this.props.query}`; const more_results_caret = (this.state.duplicatesShown) @@ -128,10 +126,10 @@ class SearchTextResult extends Component { <div className="result textResult"> <a href={href} onClick={this.handleResultClick}> <div className="result-title"> - <InterfaceText text={{en: this.state.ref, he: s.heRef}}/> + <InterfaceText text={{en: s.ref, he: s.heRef}}/> </div> </a> - <ColorBarBox tref={this.state.ref}> + <ColorBarBox tref={s.ref}> <div className={snippetClasses} dangerouslySetInnerHTML={snippetMarkup.markup}></div> </ColorBarBox> <div className="version"> From 5ad1ceb9a8463735d109c972db5fe2cf6474ff0a Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Thu, 7 Sep 2023 12:31:44 +0300 Subject: [PATCH 209/756] feat(Topic Images): CSS Templating in place --- static/css/s2.css | 23 ++++++++++++++-- static/js/TopicPage.jsx | 58 ++++++++++++++++++++++++----------------- 2 files changed, 55 insertions(+), 26 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index 2771099352..3109d3c7bb 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -2204,8 +2204,27 @@ div.interfaceLinks-row a { font-family: "Taamey Frank", "adobe-garamond-pro", "Crimson Text", Georgia, "Times New Roman", serif; margin-bottom: -3px; } -.topicImage{ - border: 20px solid rgba(242, 15, 15, 1); +.topicImagePicture{ + border: 1px solid #EDEDEC; + width: 292px; + height: 292px; + top: 121px; + left: 835px; +} +.topicImageCaption{ +/* styleName: English System Small; */ + font-family: Roboto; + font-size: 14px; + font-weight: 400; + line-height: 18px; + letter-spacing: 0em; + text-align: left; + color: #666666; +} +.topicImageWrapper{ + padding-left: 44px; + padding-right: 44px; + padding-bottom: 30px; } .readerPanel .translationsPage h2 { margin: 40px 0 0 0; diff --git a/static/js/TopicPage.jsx b/static/js/TopicPage.jsx index c04a7f9786..c631ce87d1 100644 --- a/static/js/TopicPage.jsx +++ b/static/js/TopicPage.jsx @@ -700,9 +700,17 @@ const TopicSideSection = ({ title, children, hasMore }) => { const TopicImage = ({ title, children, hasMore }) => { const [showMore, setShowMore] = useState(false); return ( - <div> - <img class="topicImage" src="https://museums.cjh.org/web/objects/common/webmedia.php?irn=11469&reftable=ecatalogue&refirn=6640" /> - </div> + <div class="topicImageWrapper"> + <img class="topicImagePicture" src="https://museums.cjh.org/web/objects/common/webmedia.php?irn=11469&reftable=ecatalogue&refirn=6640" /> + <div class="topicImageCaption"> + Rosh Hashanah<br/> + Artist: Arthur Szyk (1894-1951)<br/> + Tempera and ink on paper<br/> + New Canaan, 1948<br/> + Collection of Yeshiva University Museum<br/> + Gift of Charles Frost<br/> + </div> + </div> ); } @@ -777,27 +785,29 @@ const TopicMetaData = ({ timePeriod, properties={} }) => { })); const hasProps = propValues.reduce((accum, curr) => accum || curr.url.en || curr.url.he, false); const propsSection = hasProps ? ( - <TopicSideSection title={{en: "Learn More", he: "לקריאה נוספת"}}> - { - propValues.map(propObj => { - let url, urlExists = true; - if (Sefaria.interfaceLang === 'hebrew') { - if (!propObj.url.he) { urlExists = false; } - url = propObj.url.he || propObj.url.en; - } else { - if (!propObj.url.en) { urlExists = false; } - url = propObj.url.en || propObj.url.he; - } - if (!url) { return null; } - return ( - <SimpleLinkedBlock - key={url} en={propObj.title + (urlExists ? "" : " (Hebrew)")} he={Sefaria._(propObj.title) + (urlExists ? "" : ` (${Sefaria._("English")})`)} - url={url} aclasses={"systemText topicMetaData"} openInNewTab - /> - ); - }) - } - </TopicSideSection> + <div> + <TopicSideSection title={{en: "Learn More", he: "לקריאה נוספת"}}> + { + propValues.map(propObj => { + let url, urlExists = true; + if (Sefaria.interfaceLang === 'hebrew') { + if (!propObj.url.he) { urlExists = false; } + url = propObj.url.he || propObj.url.en; + } else { + if (!propObj.url.en) { urlExists = false; } + url = propObj.url.en || propObj.url.he; + } + if (!url) { return null; } + return ( + <SimpleLinkedBlock + key={url} en={propObj.title + (urlExists ? "" : " (Hebrew)")} he={Sefaria._(propObj.title) + (urlExists ? "" : ` (${Sefaria._("English")})`)} + url={url} aclasses={"systemText topicMetaData"} openInNewTab + /> + ); + }) + } + </TopicSideSection> + </div> ) : null; return ( <> From 5c554b79dc4eb811a60391b1e9913451a2cb6cd4 Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Thu, 7 Sep 2023 14:56:09 +0300 Subject: [PATCH 210/756] feat(Topic Images): Working dynamically with hardcoded data, English only --- static/js/TopicPage.jsx | 60 ++++++++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 19 deletions(-) diff --git a/static/js/TopicPage.jsx b/static/js/TopicPage.jsx index c631ce87d1..7e03b94430 100644 --- a/static/js/TopicPage.jsx +++ b/static/js/TopicPage.jsx @@ -523,7 +523,8 @@ const TopicPage = ({ parashaData={parashaData} tref={topicData.ref} timePeriod={topicData.timePeriod} - properties={topicData.properties} /> + properties={topicData.properties} + topicTitle={topicTitle}/> : null } <Promotions adType="sidebar"/> </div> @@ -592,7 +593,7 @@ TopicLink.propTypes = { }; -const TopicSideColumn = ({ slug, links, clearAndSetTopic, parashaData, tref, setNavTopic, timePeriod, properties }) => { +const TopicSideColumn = ({ slug, links, clearAndSetTopic, parashaData, tref, setNavTopic, timePeriod, properties, topicTitle }) => { const category = Sefaria.topicTocCategory(slug); const linkTypeArray = links ? Object.values(links).filter(linkType => !!linkType && linkType.shouldDisplay && linkType.links.filter(l => l.shouldDisplay !== false).length > 0) : []; if (linkTypeArray.length === 0) { @@ -612,7 +613,7 @@ const TopicSideColumn = ({ slug, links, clearAndSetTopic, parashaData, tref, set const readingsComponent = hasReadings ? ( <ReadingsComponent parashaData={parashaData} tref={tref} /> ) : null; - const topicMetaData = <TopicMetaData timePeriod={timePeriod} properties={properties} />; + const topicMetaData = <TopicMetaData timePeriod={timePeriod} properties={properties} topicTitle={topicTitle}/>; const linksComponent = ( links ? linkTypeArray.sort((a, b) => { @@ -697,21 +698,42 @@ const TopicSideSection = ({ title, children, hasMore }) => { ); } -const TopicImage = ({ title, children, hasMore }) => { - const [showMore, setShowMore] = useState(false); - return ( - <div class="topicImageWrapper"> - <img class="topicImagePicture" src="https://museums.cjh.org/web/objects/common/webmedia.php?irn=11469&reftable=ecatalogue&refirn=6640" /> - <div class="topicImageCaption"> - Rosh Hashanah<br/> - Artist: Arthur Szyk (1894-1951)<br/> - Tempera and ink on paper<br/> - New Canaan, 1948<br/> - Collection of Yeshiva University Museum<br/> - Gift of Charles Frost<br/> +const TopicImage = ({ topicTitle }) => { + // const [showMore, setShowMore] = useState(false); + const key = topicTitle.en; + const hardcodedMap = { + 'Rosh Hashanah': {'photoLink':'https://museums.cjh.org/web/objects/common/webmedia.php?irn=11469&reftable=ecatalogue&refirn=6640', + 'enCaption':'Rosh Hashanah<br\>Artist: Arthur Szyk (1894-1951)<br/>Tempera and ink on paper<br/>New Canaan, 1948<br/>Collection of Yeshiva University Museum<br/>Gift of Charles Frost', + 'heCaption': 'ראש השנה, ארתור שיק, ארה״ב 1948. אוסף ישיבה יוניברסיטי'}, + + 'Yom Kippur': {'photoLink':'https://picryl.com/media/jonah-and-the-fish-from-bl-add-21160-f-292-510120', + 'enCaption':'Micrography of Jonah being swallowed by the fish, at the text of Jonah, the haftarah for the afternoon service of Yom Kippur. 1300-1500', + 'heCaption': 'מיקורגפיה של יונה בבטן הדג, מתוך ספר יונה ההפטרה של יום כיפור, 1300-1500'}, + + 'The Four Species': {'photoLink':'https://res.cloudinary.com/the-jewish-museum/image/fetch/q_auto,f_auto/v1/https%3A%2F%2Fthejm.netx.net%2Ffile%2Fasset%2F34234%2Fview%2F52568%2Fview_52568%3Ftoken%3D5d5cdc57-6399-40b5-afb0-93139921700e', + 'enCaption':'Etrog container, K B, late 19th century, Germany. Gift of Dr. Harry G. Friedman', + 'heCaption': 'תיבת אתרוג, סוף המאה ה19, גרמניה. מתנת הארי ג. פרידמן '}, + + 'Sukkot': {'photoLink':'https://picryl.com/media/sukkah-from-bl-add-26968-f-316v-d0c358', + 'enCaption':'Detail of a painting of a sukkah (booth built for Sukkot). Image taken from f. 316v of Prayer book (Forli Siddur) for the entire year, 1383, Italian rite.', + 'heCaption': 'פרט ציור של סוכה עם שולחן פרוס ושלוש דמויות. דימוי מתוך סידור פורלי, 1383 איטליה'}, + + 'Simchat Torah': {'photoLink':'https://www.flickr.com/photos/center_for_jewish_history/7974345646/', + 'enCaption':'Rosh Hashanah postcard: Hakafot<br/>Artist: Haim Yisroel Goldberg (1888-1943)<br/>Publisher: Williamsburg Post Card Co.<br/>Germany, ca. 1915<br/>Collection of Yeshiva University Museum', + 'heCaption': 'גלויה לראש השנה: הקפות, חיים גולדברג, גרמניה 1915, אוסף ישיבה יוניברסיטי'}, + + 'Shabbat': {'photoLink':'https://res.cloudinary.com/the-jewish-museum/image/fetch/q_auto,f_auto/v1/https%3A%2F%2Fthejm.netx.net%2Ffile%2Fasset%2F35064%2Fview%2F61838%2Fview_61838%3Ftoken%3D5d5cdc57-6399-40b5-afb0-93139921700e', + 'enCaption':'Friday Evening, Isidor Kaufmann, Austria c. 1920. Gift of Mr. and Mrs. M. R. Schweitzer', + 'heCaption': 'שישי בערב, איזידור קאופמן, וינה 1920. מתנת מר וגברת מ.ר. שוייצר'}, + }; + return ( key in hardcodedMap ? + (<div class="topicImageWrapper"> + <img class="topicImagePicture" src={hardcodedMap[key].photoLink}/> + <div class="topicImageCaption"> + {/*todo - ADD HEBREW INT FACE*/} + {hardcodedMap[key].enCaption} </div> - </div> - ); + </div>) : null); } @@ -769,7 +791,7 @@ const propKeys = [ {en: 'jeLink', he: 'jeLink', title: 'Jewish Encyclopedia'}, {en: 'enNliLink', he: 'heNliLink', title: 'National Library of Israel'}, ]; -const TopicMetaData = ({ timePeriod, properties={} }) => { +const TopicMetaData = ({ topicTitle, timePeriod, properties={} }) => { const tpSection = !!timePeriod ? ( <TopicSideSection title={{en: "Lived", he: "תקופת פעילות"}}> <div className="systemText topicMetaData"><InterfaceText text={timePeriod.name} /></div> @@ -811,7 +833,7 @@ const TopicMetaData = ({ timePeriod, properties={} }) => { ) : null; return ( <> - <TopicImage /> + <TopicImage topicTitle={topicTitle}/> { tpSection } { propsSection } </> From 1955023c01d6cd1a747fe61d2f02778c2d7d3c4f Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Thu, 7 Sep 2023 14:57:22 +0300 Subject: [PATCH 211/756] chore(Topic Images): Fix width for caption --- static/css/s2.css | 1 + 1 file changed, 1 insertion(+) diff --git a/static/css/s2.css b/static/css/s2.css index 3109d3c7bb..5edc62fcaf 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -2220,6 +2220,7 @@ div.interfaceLinks-row a { letter-spacing: 0em; text-align: left; color: #666666; + width: 292px; } .topicImageWrapper{ padding-left: 44px; From 0551050cbb3e5eda581dfc4d9f1e367c37d2d54f Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Thu, 7 Sep 2023 15:00:49 +0300 Subject: [PATCH 212/756] chore(Topic Images): Add Hebrew caption with InterfaceText --- static/js/TopicPage.jsx | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/static/js/TopicPage.jsx b/static/js/TopicPage.jsx index 7e03b94430..4af30c946a 100644 --- a/static/js/TopicPage.jsx +++ b/static/js/TopicPage.jsx @@ -728,12 +728,11 @@ const TopicImage = ({ topicTitle }) => { }; return ( key in hardcodedMap ? (<div class="topicImageWrapper"> - <img class="topicImagePicture" src={hardcodedMap[key].photoLink}/> - <div class="topicImageCaption"> - {/*todo - ADD HEBREW INT FACE*/} - {hardcodedMap[key].enCaption} - </div> - </div>) : null); + <img class="topicImagePicture" src={hardcodedMap[key].photoLink}/> + <div class="topicImageCaption"> + <InterfaceText text={{en:hardcodedMap[key].enCaption, he:hardcodedMap[key].heCaption}} /> + </div> + </div>) : null); } From b5226476087320aa192f75e5ef498c47b820f6f4 Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Thu, 7 Sep 2023 15:02:50 +0300 Subject: [PATCH 213/756] chore(Topic Images): Fix Hebrew text direction CSS conflict --- static/css/s2.css | 1 - 1 file changed, 1 deletion(-) diff --git a/static/css/s2.css b/static/css/s2.css index 5edc62fcaf..bef5a1f389 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -2218,7 +2218,6 @@ div.interfaceLinks-row a { font-weight: 400; line-height: 18px; letter-spacing: 0em; - text-align: left; color: #666666; width: 292px; } From 00d6b7c66390b2416d91d988ffeaf551ba2c5bbc Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Thu, 7 Sep 2023 15:55:41 +0300 Subject: [PATCH 214/756] chore(Topic Images): Fix InterfaceText CSS font conflict --- static/css/s2.css | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/static/css/s2.css b/static/css/s2.css index bef5a1f389..2be4d0ab7b 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -2211,9 +2211,15 @@ div.interfaceLinks-row a { top: 121px; left: 835px; } -.topicImageCaption{ +.topicImageCaption .int-en { /* styleName: English System Small; */ font-family: Roboto; +} +.topicImageCaption .int-he { + /* styleName: English System Small; */ + font-family: Roboto; + } +.topicImageCaption { font-size: 14px; font-weight: 400; line-height: 18px; From 0fd8d25e79874e97d6528bd45bbad0081321c18b Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Thu, 7 Sep 2023 15:59:49 +0300 Subject: [PATCH 215/756] fix(Topic Images): Fix <br> bug --- static/css/s2.css | 3 +++ static/js/TopicPage.jsx | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index 2be4d0ab7b..61ae8d9917 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -2232,6 +2232,9 @@ div.interfaceLinks-row a { padding-right: 44px; padding-bottom: 30px; } +.topicLinebreak { + white-space: pre-line; +} .readerPanel .translationsPage h2 { margin: 40px 0 0 0; font-size: 24px; diff --git a/static/js/TopicPage.jsx b/static/js/TopicPage.jsx index 4af30c946a..68c11f41e3 100644 --- a/static/js/TopicPage.jsx +++ b/static/js/TopicPage.jsx @@ -703,7 +703,7 @@ const TopicImage = ({ topicTitle }) => { const key = topicTitle.en; const hardcodedMap = { 'Rosh Hashanah': {'photoLink':'https://museums.cjh.org/web/objects/common/webmedia.php?irn=11469&reftable=ecatalogue&refirn=6640', - 'enCaption':'Rosh Hashanah<br\>Artist: Arthur Szyk (1894-1951)<br/>Tempera and ink on paper<br/>New Canaan, 1948<br/>Collection of Yeshiva University Museum<br/>Gift of Charles Frost', + 'enCaption':'Rosh Hashanah\nArtist: Arthur Szyk (1894-1951)\nTempera and ink on paper\nNew Canaan, 1948\nCollection of Yeshiva University Museum\nGift of Charles Frost', 'heCaption': 'ראש השנה, ארתור שיק, ארה״ב 1948. אוסף ישיבה יוניברסיטי'}, 'Yom Kippur': {'photoLink':'https://picryl.com/media/jonah-and-the-fish-from-bl-add-21160-f-292-510120', @@ -719,7 +719,7 @@ const TopicImage = ({ topicTitle }) => { 'heCaption': 'פרט ציור של סוכה עם שולחן פרוס ושלוש דמויות. דימוי מתוך סידור פורלי, 1383 איטליה'}, 'Simchat Torah': {'photoLink':'https://www.flickr.com/photos/center_for_jewish_history/7974345646/', - 'enCaption':'Rosh Hashanah postcard: Hakafot<br/>Artist: Haim Yisroel Goldberg (1888-1943)<br/>Publisher: Williamsburg Post Card Co.<br/>Germany, ca. 1915<br/>Collection of Yeshiva University Museum', + 'enCaption':'Rosh Hashanah postcard: Hakafot\nArtist: Haim Yisroel Goldberg (1888-1943)\nPublisher: Williamsburg Post Card Co.\nGermany, ca. 1915\nCollection of Yeshiva University Museum', 'heCaption': 'גלויה לראש השנה: הקפות, חיים גולדברג, גרמניה 1915, אוסף ישיבה יוניברסיטי'}, 'Shabbat': {'photoLink':'https://res.cloudinary.com/the-jewish-museum/image/fetch/q_auto,f_auto/v1/https%3A%2F%2Fthejm.netx.net%2Ffile%2Fasset%2F35064%2Fview%2F61838%2Fview_61838%3Ftoken%3D5d5cdc57-6399-40b5-afb0-93139921700e', @@ -729,7 +729,7 @@ const TopicImage = ({ topicTitle }) => { return ( key in hardcodedMap ? (<div class="topicImageWrapper"> <img class="topicImagePicture" src={hardcodedMap[key].photoLink}/> - <div class="topicImageCaption"> + <div class="topicImageCaption topicLinebreak"> <InterfaceText text={{en:hardcodedMap[key].enCaption, he:hardcodedMap[key].heCaption}} /> </div> </div>) : null); From c8d48a9545a7ec3e206134df707e77adce117213 Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Thu, 7 Sep 2023 16:06:04 +0300 Subject: [PATCH 216/756] chore(Topic Images): Rename linebreak CSS class to be more specific --- static/css/s2.css | 2 +- static/js/TopicPage.jsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index 61ae8d9917..ab1a6c33f6 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -2232,7 +2232,7 @@ div.interfaceLinks-row a { padding-right: 44px; padding-bottom: 30px; } -.topicLinebreak { +.topicImageCaptionLinebreak { white-space: pre-line; } .readerPanel .translationsPage h2 { diff --git a/static/js/TopicPage.jsx b/static/js/TopicPage.jsx index 68c11f41e3..f808f8079d 100644 --- a/static/js/TopicPage.jsx +++ b/static/js/TopicPage.jsx @@ -729,7 +729,7 @@ const TopicImage = ({ topicTitle }) => { return ( key in hardcodedMap ? (<div class="topicImageWrapper"> <img class="topicImagePicture" src={hardcodedMap[key].photoLink}/> - <div class="topicImageCaption topicLinebreak"> + <div class="topicImageCaption topicImageCaptionLinebreak"> <InterfaceText text={{en:hardcodedMap[key].enCaption, he:hardcodedMap[key].heCaption}} /> </div> </div>) : null); From a5f92ac32944a71aacd89733247a8a25e76fd37d Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Thu, 7 Sep 2023 16:25:12 +0300 Subject: [PATCH 217/756] fix(Topic Images): Update to Shabbat caption --- static/js/TopicPage.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/TopicPage.jsx b/static/js/TopicPage.jsx index f808f8079d..aa1c297d5f 100644 --- a/static/js/TopicPage.jsx +++ b/static/js/TopicPage.jsx @@ -723,7 +723,7 @@ const TopicImage = ({ topicTitle }) => { 'heCaption': 'גלויה לראש השנה: הקפות, חיים גולדברג, גרמניה 1915, אוסף ישיבה יוניברסיטי'}, 'Shabbat': {'photoLink':'https://res.cloudinary.com/the-jewish-museum/image/fetch/q_auto,f_auto/v1/https%3A%2F%2Fthejm.netx.net%2Ffile%2Fasset%2F35064%2Fview%2F61838%2Fview_61838%3Ftoken%3D5d5cdc57-6399-40b5-afb0-93139921700e', - 'enCaption':'Friday Evening, Isidor Kaufmann, Austria c. 1920. Gift of Mr. and Mrs. M. R. Schweitzer', + 'enCaption':'Friday Evening, Isidor Kaufmann, Austria c. 1920. The Jewish Museum, Gift of Mr. and Mrs. M. R. Schweitzer', 'heCaption': 'שישי בערב, איזידור קאופמן, וינה 1920. מתנת מר וגברת מ.ר. שוייצר'}, }; return ( key in hardcodedMap ? From 04ecce622abe5dd2aaea65da7de34d36b964d64b Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Thu, 7 Sep 2023 19:52:19 +0300 Subject: [PATCH 218/756] fix(Topic Images): Remove fixed height for proportion --- static/css/s2.css | 2 +- static/js/TopicPage.jsx | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index ab1a6c33f6..2cc1f1990a 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -2207,7 +2207,7 @@ div.interfaceLinks-row a { .topicImagePicture{ border: 1px solid #EDEDEC; width: 292px; - height: 292px; + /* height: 292px; */ top: 121px; left: 835px; } diff --git a/static/js/TopicPage.jsx b/static/js/TopicPage.jsx index aa1c297d5f..50688255e2 100644 --- a/static/js/TopicPage.jsx +++ b/static/js/TopicPage.jsx @@ -706,7 +706,7 @@ const TopicImage = ({ topicTitle }) => { 'enCaption':'Rosh Hashanah\nArtist: Arthur Szyk (1894-1951)\nTempera and ink on paper\nNew Canaan, 1948\nCollection of Yeshiva University Museum\nGift of Charles Frost', 'heCaption': 'ראש השנה, ארתור שיק, ארה״ב 1948. אוסף ישיבה יוניברסיטי'}, - 'Yom Kippur': {'photoLink':'https://picryl.com/media/jonah-and-the-fish-from-bl-add-21160-f-292-510120', + 'Yom Kippur': {'photoLink':'https://www.bl.uk/IllImages/BLCD/big/K900/K90075-77.jpg', 'enCaption':'Micrography of Jonah being swallowed by the fish, at the text of Jonah, the haftarah for the afternoon service of Yom Kippur. 1300-1500', 'heCaption': 'מיקורגפיה של יונה בבטן הדג, מתוך ספר יונה ההפטרה של יום כיפור, 1300-1500'}, @@ -714,11 +714,11 @@ const TopicImage = ({ topicTitle }) => { 'enCaption':'Etrog container, K B, late 19th century, Germany. Gift of Dr. Harry G. Friedman', 'heCaption': 'תיבת אתרוג, סוף המאה ה19, גרמניה. מתנת הארי ג. פרידמן '}, - 'Sukkot': {'photoLink':'https://picryl.com/media/sukkah-from-bl-add-26968-f-316v-d0c358', + 'Sukkot': {'photoLink':'https://www.bl.uk/IllImages/BLCD/big/d400/d40054-17a.jpg', 'enCaption':'Detail of a painting of a sukkah (booth built for Sukkot). Image taken from f. 316v of Prayer book (Forli Siddur) for the entire year, 1383, Italian rite.', 'heCaption': 'פרט ציור של סוכה עם שולחן פרוס ושלוש דמויות. דימוי מתוך סידור פורלי, 1383 איטליה'}, - 'Simchat Torah': {'photoLink':'https://www.flickr.com/photos/center_for_jewish_history/7974345646/', + 'Simchat Torah': {'photoLink':'https://upload.wikimedia.org/wikipedia/commons/4/4d/Rosh_Hashanah_greeting_card_%287974345646%29.jpg?20150712114334', 'enCaption':'Rosh Hashanah postcard: Hakafot\nArtist: Haim Yisroel Goldberg (1888-1943)\nPublisher: Williamsburg Post Card Co.\nGermany, ca. 1915\nCollection of Yeshiva University Museum', 'heCaption': 'גלויה לראש השנה: הקפות, חיים גולדברג, גרמניה 1915, אוסף ישיבה יוניברסיטי'}, From fba253370c324a1ebec0b05461beaeaf02fc8cba Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Thu, 7 Sep 2023 13:00:16 -0400 Subject: [PATCH 219/756] Fix bug where frontend crashes if STRAPI_LOCATION and STRAPI_PORT are both set to None on the backend --- templates/base.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/templates/base.html b/templates/base.html index 96e7ed04cd..71a90be76e 100644 --- a/templates/base.html +++ b/templates/base.html @@ -239,6 +239,8 @@ {% if STRAPI_LOCATION and STRAPI_PORT %} var STRAPI_INSTANCE = "{{ STRAPI_LOCATION }}:{{ STRAPI_PORT }}"; + {% else %} + var STRAPI_INSTANCE = null; {% endif %} {% endautoescape %} From 1526cf4ec76039d0562a2f371c773fc3f08f6a87 Mon Sep 17 00:00:00 2001 From: Russel Neiss <russel.neiss@gmail.com> Date: Thu, 7 Sep 2023 14:13:48 -0500 Subject: [PATCH 220/756] static: Update Yonadav on Team Page [sc-20443] --- templates/static/en/team.html | 2 +- templates/static/he/team.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/static/en/team.html b/templates/static/en/team.html index 242c871b80..1d52fa7b1d 100644 --- a/templates/static/en/team.html +++ b/templates/static/en/team.html @@ -261,7 +261,7 @@ <h1> <span class="int-en">Yonadav Leibowitz</span> </div> <div class="teamTitle"> - <span class="int-en">Engineering Intern</span> + <span class="int-en">Junior Research Engineer</span> </div> </div> </div> diff --git a/templates/static/he/team.html b/templates/static/he/team.html index f37c97453b..eaf538e783 100644 --- a/templates/static/he/team.html +++ b/templates/static/he/team.html @@ -248,7 +248,7 @@ <h1> <span class="int-he">יונדב ליבוביץ</span> </div> <div class="teamTitle"> - <span class="int-he">מהנדס תוכנה - מתמחה</span> + <span class="int-he">מהנדס מחקר זוטר</span> </div> </div> </div> From e584e4a63bfa512b0db6ce2de17bf0ad5dcaa164 Mon Sep 17 00:00:00 2001 From: Russel Neiss <russel.neiss@gmail.com> Date: Thu, 7 Sep 2023 14:30:30 -0500 Subject: [PATCH 221/756] static: Add job posting for Hebrew ed asst --- templates/static/jobs.html | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/templates/static/jobs.html b/templates/static/jobs.html index bf8e000f78..1f7d84ce39 100644 --- a/templates/static/jobs.html +++ b/templates/static/jobs.html @@ -67,15 +67,16 @@ <h2 class="anchorable">HR and Operations</h2> <section class="jobsListForDepartment"> <div class="job"><a class="" target="_blank" href=""></a></div> </section> - </section> + </section> --> <section class="section department englishOnly"> <header> <h2 class="anchorable">Learning</h2> </header> <section class="jobsListForDepartment"> - <div class="job"><a class="jobLink" target="_blank" href="">n</a></div> + <div class="job"><a class="jobLink" target="_blank" href="https://sefaria.breezy.hr/p/c693c3ab1b78-hebrew-editorial-associate-part-time">Hebrew Editorial Associate (Part-Time)</a></div> </section> </section> + <!-- <section class="section department englishOnly"> <header> <h2 class="anchorable">Product</h2> @@ -93,7 +94,8 @@ <h2 class="anchorable">Israel Team</h2> </header> </section>---> - <div class="section nothing"> + <!-- + <div class="section nothing"> <p> <span class="int-en"> Sefaria does not currently have any open positions. @@ -107,6 +109,7 @@ <h2 class="anchorable">Israel Team</h2> </span> </p> </div> + --> <aside class="solicitationNotice"> <p> From 38f735861e8aa439ca35149e33887035c8a433d3 Mon Sep 17 00:00:00 2001 From: Russel Neiss <russel.neiss@gmail.com> Date: Thu, 7 Sep 2023 14:40:56 -0500 Subject: [PATCH 222/756] static: Update Desiree Neissani on Team Page [sc-20512] --- static/img/headshots/desiree.png | Bin 0 -> 61030 bytes templates/static/en/team.html | 14 ++++++++++++++ templates/static/he/team.html | 13 +++++++++++++ 3 files changed, 27 insertions(+) create mode 100644 static/img/headshots/desiree.png diff --git a/static/img/headshots/desiree.png b/static/img/headshots/desiree.png new file mode 100644 index 0000000000000000000000000000000000000000..c52a7cdb437b2a0e2555938c5b0d7928975e7438 GIT binary patch literal 61030 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 b-r)ZMyp}9mnQeIm00000NkvXXu0mjf_>WBr literal 0 HcmV?d00001 diff --git a/templates/static/en/team.html b/templates/static/en/team.html index 1d52fa7b1d..8eb851b6b3 100644 --- a/templates/static/en/team.html +++ b/templates/static/en/team.html @@ -330,6 +330,20 @@ <h1> </div> </div> </div> + <div class="teamMember"> + <div class="teamMemberImage"> + <img src="{% static 'img/headshots/desiree.png' %}" alt="Headshot of Desiree Nissani"> + </div> + <div class="teamMemberDetails"> + <div class="teamName"> + <span class="int-en">Desiree Nissani</span> + </div> + <div class="teamTitle"> + <span class="int-en">Special Assistant, Office of the CEO</span> + </div> + </div> + </div> + <div class="teamMember"> <div class="teamMemberImage"> <img src="{% static 'img/headshots/rebecca.png' %}" alt="Headshot of Rebecca Remisn"> diff --git a/templates/static/he/team.html b/templates/static/he/team.html index eaf538e783..45345dd75a 100644 --- a/templates/static/he/team.html +++ b/templates/static/he/team.html @@ -317,6 +317,19 @@ <h1> </div> </div> </div> + <div class="teamMember"> + <div class="teamMemberImage"> + <img src="{% static 'img/headshots/desiree.png' %}" alt="Headshot of Desiree Neissani"> + </div> + <div class="teamMemberDetails"> + <div class="teamName sans"> + <span class="int-he">שרה ניסני</span> + </div> + <div class="teamTitle"> + <span class="int-he">עוזר מיוחד, משרד המנכ"ל</span> + </div> + </div> + </div> <div class="teamMember"> <div class="teamMemberImage"> <img src="{% static 'img/headshots/noah.png' %}" alt="Headshot of Noah Santacruz"> From a812c8cb4f887238e83558d71141bc8e6bf03312 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Fri, 8 Sep 2023 00:07:34 -0400 Subject: [PATCH 223/756] feat(strapi-cms): Add customizable background color attribute to banner --- static/js/Misc.jsx | 32 ++++++++++++++++++++++---------- static/js/context.js | 1 + 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index dedb36af76..8a5282af16 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -2278,7 +2278,8 @@ const InterruptingMessage = ({ InterruptingMessage.displayName = "InterruptingMessage"; const Banner = ({ onClose }) => { - const [bannerShowDelayHasElapsed, setBannerShowDelayHasElapsed] = useState(false); + const [bannerShowDelayHasElapsed, setBannerShowDelayHasElapsed] = + useState(false); const [hasInteractedWithBanner, setHasInteractedWithBanner] = useState(false); const strapi = useContext(StrapiDataContext); @@ -2306,7 +2307,11 @@ const Banner = ({ onClose }) => { const shouldShow = () => { if (!strapi.banner) return false; - if (Sefaria.interfaceLang === 'hebrew' && !strapi.banner.locales.includes('he')) return false; + if ( + Sefaria.interfaceLang === "hebrew" && + !strapi.banner.locales.includes("he") + ) + return false; if (hasBannerBeenInteractedWith(strapi.banner.internalBannerName)) return false; @@ -2340,15 +2345,11 @@ const Banner = ({ onClose }) => { if (onClose) onClose(); markBannerAsHasBeenInteractedWith(strapi.banner.internalBannerName); setHasInteractedWithBanner(true); - trackBannerInteraction( - strapi.banner.internalBannerName, - eventDescription - ); + trackBannerInteraction(strapi.banner.internalBannerName, eventDescription); }; useEffect(() => { if (shouldShow()) { - const timeoutId = setTimeout(() => { // s2 is the div that contains the React root and needs to be manipulated by traditional DOM methods if (document.getElementById("s2").classList.contains("headerOnly")) { @@ -2365,10 +2366,22 @@ const Banner = ({ onClose }) => { if (!hasInteractedWithBanner) { return ( <OnInView onVisible={trackBannerImpression}> - <div id="bannerMessage" className={bannerShowDelayHasElapsed ? "" : "hidden"}> + <div + id="bannerMessage" + className={bannerShowDelayHasElapsed ? "" : "hidden"} + style={ + strapi.banner.bannerBackgroundColor && { + backgroundColor: strapi.banner.bannerBackgroundColor, + } + } + > <div id="bannerMessageContent"> <div id="bannerTextBox"> - <InterfaceText markdown={replaceNewLinesWithLinebreaks(strapi.banner.bannerText)} /> + <InterfaceText + markdown={replaceNewLinesWithLinebreaks( + strapi.banner.bannerText + )} + /> </div> <div id="bannerButtonBox"> <a @@ -2385,7 +2398,6 @@ const Banner = ({ onClose }) => { </a> </div> </div> - <div id="bannerMessageClose">×</div> <div id="bannerMessageClose" onClick={closeBanner}> × </div> diff --git a/static/js/context.js b/static/js/context.js index 1784f811b3..f405e06ba5 100644 --- a/static/js/context.js +++ b/static/js/context.js @@ -56,6 +56,7 @@ function StrapiDataProvider({ children }) { buttonText buttonURL showDelay + bannerBackgroundColor createdAt locale localizations { From 7605dd1c4f56d83f8dd3f4968ed8511143b3652e Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Sun, 10 Sep 2023 10:28:48 +0300 Subject: [PATCH 224/756] chore(Topic Images): Update captions to new text --- static/js/TopicPage.jsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/static/js/TopicPage.jsx b/static/js/TopicPage.jsx index 50688255e2..9843f0384d 100644 --- a/static/js/TopicPage.jsx +++ b/static/js/TopicPage.jsx @@ -707,15 +707,15 @@ const TopicImage = ({ topicTitle }) => { 'heCaption': 'ראש השנה, ארתור שיק, ארה״ב 1948. אוסף ישיבה יוניברסיטי'}, 'Yom Kippur': {'photoLink':'https://www.bl.uk/IllImages/BLCD/big/K900/K90075-77.jpg', - 'enCaption':'Micrography of Jonah being swallowed by the fish, at the text of Jonah, the haftarah for the afternoon service of Yom Kippur. 1300-1500', + 'enCaption':'Micrography of Jonah being swallowed by the fish, at the text of Jonah, the haftarah for the afternoon service of Yom Kippur.\n 1300-1500, Germany (The British Library)', 'heCaption': 'מיקורגפיה של יונה בבטן הדג, מתוך ספר יונה ההפטרה של יום כיפור, 1300-1500'}, 'The Four Species': {'photoLink':'https://res.cloudinary.com/the-jewish-museum/image/fetch/q_auto,f_auto/v1/https%3A%2F%2Fthejm.netx.net%2Ffile%2Fasset%2F34234%2Fview%2F52568%2Fview_52568%3Ftoken%3D5d5cdc57-6399-40b5-afb0-93139921700e', - 'enCaption':'Etrog container, K B, late 19th century, Germany. Gift of Dr. Harry G. Friedman', + 'enCaption':'Etrog container, K B, late 19th century, Germany.\n The Jewish Museum, Gift of Dr. Harry G. Friedman', 'heCaption': 'תיבת אתרוג, סוף המאה ה19, גרמניה. מתנת הארי ג. פרידמן '}, 'Sukkot': {'photoLink':'https://www.bl.uk/IllImages/BLCD/big/d400/d40054-17a.jpg', - 'enCaption':'Detail of a painting of a sukkah (booth built for Sukkot). Image taken from f. 316v of Prayer book (Forli Siddur) for the entire year, 1383, Italian rite.', + 'enCaption':'Detail of a painting of a sukkah (booth built for Sukkot).\n Image taken from f. 316v of Prayer book (Forli Siddur) for the entire year, 1383, Italian rite.', 'heCaption': 'פרט ציור של סוכה עם שולחן פרוס ושלוש דמויות. דימוי מתוך סידור פורלי, 1383 איטליה'}, 'Simchat Torah': {'photoLink':'https://upload.wikimedia.org/wikipedia/commons/4/4d/Rosh_Hashanah_greeting_card_%287974345646%29.jpg?20150712114334', @@ -723,7 +723,7 @@ const TopicImage = ({ topicTitle }) => { 'heCaption': 'גלויה לראש השנה: הקפות, חיים גולדברג, גרמניה 1915, אוסף ישיבה יוניברסיטי'}, 'Shabbat': {'photoLink':'https://res.cloudinary.com/the-jewish-museum/image/fetch/q_auto,f_auto/v1/https%3A%2F%2Fthejm.netx.net%2Ffile%2Fasset%2F35064%2Fview%2F61838%2Fview_61838%3Ftoken%3D5d5cdc57-6399-40b5-afb0-93139921700e', - 'enCaption':'Friday Evening, Isidor Kaufmann, Austria c. 1920. The Jewish Museum, Gift of Mr. and Mrs. M. R. Schweitzer', + 'enCaption':'Friday Evening, Isidor Kaufmann, Austria c. 1920.\n The Jewish Museum, Gift of Mr. and Mrs. M. R. Schweitzer', 'heCaption': 'שישי בערב, איזידור קאופמן, וינה 1920. מתנת מר וגברת מ.ר. שוייצר'}, }; return ( key in hardcodedMap ? From 43b1facf11628275fba6b44d2ab34f4d883dc7b3 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Sun, 10 Sep 2023 13:37:30 +0300 Subject: [PATCH 225/756] chore: add comment for the sake of commit --- static/js/AboutBox.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/AboutBox.jsx b/static/js/AboutBox.jsx index 938b389e3e..2d7435a170 100644 --- a/static/js/AboutBox.jsx +++ b/static/js/AboutBox.jsx @@ -134,7 +134,7 @@ class AboutBox extends Component { dateTextEn = d.pubDateString.en; dateTextHe = d.pubDateString.he; } - const bookPageUrl = "/" + Sefaria.normRef(d.title); + const bookPageUrl = "/" + Sefaria.normRef(d.title); //comment for the sake of commit detailSection = ( <div className="detailsSection sans-serif"> <h2 className="aboutHeader"> From 8d418ce0b4e53498dae5484892fb40a33b4c537e Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Mon, 11 Sep 2023 16:13:56 +0300 Subject: [PATCH 226/756] refactor(Text Column): pull out code for text column banner into a reusable component. --- static/js/ReaderPanel.jsx | 56 +--------------------- static/js/TextColumnBanner.jsx | 85 ++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 55 deletions(-) create mode 100644 static/js/TextColumnBanner.jsx diff --git a/static/js/ReaderPanel.jsx b/static/js/ReaderPanel.jsx index 308099388f..3b3c037a12 100644 --- a/static/js/ReaderPanel.jsx +++ b/static/js/ReaderPanel.jsx @@ -31,6 +31,7 @@ import UserStats from './UserStats'; import ModeratorToolsPanel from './ModeratorToolsPanel'; import PublicCollectionsPage from './PublicCollectionsPage'; import TranslationsPage from './TranslationsPage'; +import { TranslationLanguagePreferenceSuggestionBanner } from './TextColumnBanner'; import { CloseButton, MenuButton, @@ -1437,61 +1438,6 @@ ReaderControls.propTypes = { }; -const TranslationLanguagePreferenceSuggestionBanner = ({ setTranslationLanguagePreference }) => { - const [accepted, setAccepted] = useState(false); - const [closed, setClosed] = useState(false); - - const cookie = Sefaria._inBrowser ? $.cookie : Sefaria.util.cookie; - const { translation_language_preference_suggestion } = Sefaria; - if (closed || (!accepted && cookie("translation_language_preference_suggested")) || !translation_language_preference_suggestion) { - return null; - } - const closeBanner = () => { - setClosed(true); - cookie("translation_language_preference_suggested", JSON.stringify(1), {path: "/"}); - Sefaria.editProfileAPI({settings: {translation_language_preference_suggested: true}}); - } - const accept = () => { - setAccepted(true); - setTranslationLanguagePreference(translation_language_preference_suggestion); - } - const lang = Sefaria.translateISOLanguageCode(translation_language_preference_suggestion); - - return ( - <div className="readerControls transLangPrefSuggBann"> - <div className="readerControlsInner transLangPrefSuggBannInner sans-serif"> - { - accepted ? ( - <div className="transLangPrefCentered"> - <InterfaceText> - <EnglishText> Thanks! We'll show you {lang} translations first when we have them. </EnglishText> - <HebrewText>תודה! כשנוכל, נציג לכם תרגומים בשפה ה<span className="bold">{Sefaria._(lang)}</span> כאשר אלו יהיו זמינים. </HebrewText> - </InterfaceText> - </div> - ) : ( - <div className="transLangPrefCentered"> - <InterfaceText> - <EnglishText> Prefer to see <span className="bold"> {lang} </span> translations when available? </EnglishText> - <HebrewText>האם תעדיפו לראות תרגומים בשפה ה<span className="bold">{Sefaria._(lang)}</span> כאשר הם זמינים?</HebrewText> - </InterfaceText> - <div className="yesNoGroup"> - <a className="yesNoButton" onClick={accept}> - <InterfaceText>Yes</InterfaceText> - </a> - <a className="yesNoButton" onClick={closeBanner}> - <InterfaceText>No</InterfaceText> - </a> - </div> - </div> - ) - } - <CloseButton onClick={closeBanner} /> - </div> - </div> - ); -} - - class ReaderDisplayOptionsMenu extends Component { renderAliyotToggle() { let torah = ["Genesis", "Exodus", "Leviticus", "Numbers", "Deuteronomy", "Onkelos Genesis", "Onkelos Exodus", "Onkelos Leviticus", "Onkelos Numbers", "Onkelos Deuteronomy"]; diff --git a/static/js/TextColumnBanner.jsx b/static/js/TextColumnBanner.jsx new file mode 100644 index 0000000000..8011b1a654 --- /dev/null +++ b/static/js/TextColumnBanner.jsx @@ -0,0 +1,85 @@ +import React, {useState} from "react"; +import Sefaria from './sefaria/sefaria'; +import $ from './sefaria/sefariaJquery'; +import { + CloseButton, InterfaceText, EnglishText, HebrewText +} from './Misc'; + +export const TranslationLanguagePreferenceSuggestionBanner = ({ setTranslationLanguagePreference }) => { + const [accepted, setAccepted] = useState(false); + + const cookie = Sefaria._inBrowser ? $.cookie : Sefaria.util.cookie; + const { translation_language_preference_suggestion } = Sefaria; + if ((!accepted && cookie("translation_language_preference_suggested")) || !translation_language_preference_suggestion) { + return null; + } + const reject = () => { + cookie("translation_language_preference_suggested", JSON.stringify(1), {path: "/"}); + Sefaria.editProfileAPI({settings: {translation_language_preference_suggested: true}}); + } + const accept = () => { + setAccepted(true); + setTranslationLanguagePreference(translation_language_preference_suggestion); + } + const lang = Sefaria.translateISOLanguageCode(translation_language_preference_suggestion); + const textElement = accepted ? ( + <InterfaceText> + <EnglishText> Thanks! We'll show you {lang} translations first when we have them. </EnglishText> + <HebrewText>תודה! כשנוכל, נציג לכם תרגומים בשפה ה<span className="bold">{Sefaria._(lang)}</span> כאשר אלו יהיו זמינים. </HebrewText> + </InterfaceText> + ) : ( + <InterfaceText> + <EnglishText> Prefer to see <span className="bold"> {lang} </span> translations when available? </EnglishText> + <HebrewText>האם תעדיפו לראות תרגומים בשפה ה<span className="bold">{Sefaria._(lang)}</span> כאשר הם זמינים?</HebrewText> + </InterfaceText> + ); + const buttons = accepted ? null : [{text: "Yes", onClick: accept}, {text: "No", onClick: reject, sideEffect: "close" }]; + + return ( + <TextColumnBanner textElement={textElement} buttons={buttons} onClose={reject}/> + ); +} + + + +/** + * Banner which appears right above text column and informs a user of an action they can take + * @param textElement: React element to display the call-to-action text. + * @param buttons: List of objects. Each object should have keys "text" and "onClick". Can optionally have key "sideEffect" whose value can be "close" if the button should close the banner. + * @param onClose: Optional callback that gets called when the banner is closed. + * @returns {JSX.Element|null} + * @constructor + */ +const TextColumnBanner = ({ textElement, buttons, onClose }) => { + const [closed, setClosed] = useState(false); + const closeBanner = () => { + setClosed(true); + onClose?.(); + }; + if (closed) { return null; } + return ( + <div className="readerControls transLangPrefSuggBann"> + <div className="readerControlsInner transLangPrefSuggBannInner sans-serif"> + <div className="transLangPrefCentered"> + { textElement } + <div className="yesNoGroup"> + { buttons.map(button => <TextColumnBannerButton key={button.text} button={button} setBannerClosed={setClosed}/>) } + </div> + </div> + <CloseButton onClick={closeBanner} /> + </div> + </div> + ); +} + +const TextColumnBannerButton = ({ button, setBannerClosed }) => { + const onClick = () => { + button.onClick(); + if (button.sideEffect === "close") { setBannerClosed(true); } + } + return ( + <a className="yesNoButton" onClick={onClick}> + <InterfaceText>{button.text}</InterfaceText> + </a> + ); +} From 95c6576a571f0b74d4e1ea310d4646b83ec72b1d Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Mon, 11 Sep 2023 19:57:00 +0300 Subject: [PATCH 227/756] fix(Topic Images): Polish notes - addressing image responsiveness and width --- static/css/s2.css | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/static/css/s2.css b/static/css/s2.css index 2cc1f1990a..47f8f36951 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -2206,8 +2206,10 @@ div.interfaceLinks-row a { } .topicImagePicture{ border: 1px solid #EDEDEC; - width: 292px; + max-width: 100%; + height: auto; /* height: 292px; */ + padding: 0 44; top: 121px; left: 835px; } From b303a79e768137e4e856efc522fa7eadafbb4bba Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Mon, 11 Sep 2023 21:36:12 +0300 Subject: [PATCH 228/756] feat: hard-code translations call-to-action to always show. --- static/js/ReaderApp.jsx | 4 ++-- static/js/ReaderPanel.jsx | 18 ++++++++++++++---- static/js/TextColumnBanner.jsx | 22 ++++++++++++++++++++++ 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index 02ec1aa695..71fad6fe5c 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -2058,7 +2058,7 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { var updateSearchFilter = this.updateSearchFilter.bind(null, i); var updateSearchOptionField = this.updateSearchOptionField.bind(null, i); var updateSearchOptionSort = this.updateSearchOptionSort.bind(null, i); - var onOpenConnectionsClick = this.openTextListAt.bind(null, i+1); + var openConnectionsPanel = this.openTextListAt.bind(null, i+1); var setTextListHighlight = this.setTextListHighlight.bind(null, i); var setSelectedWords = this.setSelectedWords.bind(null, i); var clearSelectedWords = this.clearSelectedWords.bind(null, i); @@ -2097,7 +2097,7 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { onSearchResultClick={onSearchResultClick} onSidebarSearchClick={onSidebarSearchClick} onNavigationClick={this.handleNavigationClick} - onOpenConnectionsClick={onOpenConnectionsClick} + openConnectionsPanel={openConnectionsPanel} openComparePanel={openComparePanel} setTextListHighlight={setTextListHighlight} setConnectionsFilter={setConnectionsFilter} diff --git a/static/js/ReaderPanel.jsx b/static/js/ReaderPanel.jsx index 3b3c037a12..103e35ad55 100644 --- a/static/js/ReaderPanel.jsx +++ b/static/js/ReaderPanel.jsx @@ -31,7 +31,7 @@ import UserStats from './UserStats'; import ModeratorToolsPanel from './ModeratorToolsPanel'; import PublicCollectionsPage from './PublicCollectionsPage'; import TranslationsPage from './TranslationsPage'; -import { TranslationLanguagePreferenceSuggestionBanner } from './TextColumnBanner'; +import {TranslationCallToActionBanner, TranslationLanguagePreferenceSuggestionBanner} from './TextColumnBanner'; import { CloseButton, MenuButton, @@ -1106,7 +1106,7 @@ class ReaderPanel extends Component { openDisplaySettings={this.openDisplaySettings} currentLayout={this.currentLayout} onError={this.onError} - openSidePanel={this.openSidePanel} + openConnectionsPanel={this.props.openConnectionsPanel} connectionsMode={this.state.filter.length && this.state.connectionsMode === "Connections" ? "Connection Text" : this.state.connectionsMode} connectionsCategory={this.state.connectionsCategory} closePanel={this.props.closePanel} @@ -1383,7 +1383,11 @@ class ReaderControls extends Component { /> <DisplaySettingsButton onClick={this.props.openDisplaySettings} /> </div>); - let transLangPrefSuggBann = hideHeader || connectionsHeader ? null : <TranslationLanguagePreferenceSuggestionBanner setTranslationLanguagePreference={this.props.setTranslationLanguagePreference} />; + const openTranslations = this.props.openConnectionsPanel.bind(null, [this.props.currentRef], null, {"connectionsMode": "Translations"}); + let banner = (hideHeader || connectionsHeader) ? null : getTextColumnBanner( + <TranslationLanguagePreferenceSuggestionBanner setTranslationLanguagePreference={this.props.setTranslationLanguagePreference} />, + <TranslationCallToActionBanner openTranslations={openTranslations} /> + ); const classes = classNames({ readerControls: 1, connectionsHeader: mode == "Connections", @@ -1403,7 +1407,7 @@ class ReaderControls extends Component { <div> {connectionsHeader ? null : <CategoryColorLine category={this.props.currentCategory()} />} {readerControls} - {transLangPrefSuggBann} + {banner} </div> ); } @@ -1438,6 +1442,12 @@ ReaderControls.propTypes = { }; +const getTextColumnBanner = (transPrefBanner, transCallToActionBanner) => { + return transCallToActionBanner; + // return transPrefBanner || transCallToActionBanner; +} + + class ReaderDisplayOptionsMenu extends Component { renderAliyotToggle() { let torah = ["Genesis", "Exodus", "Leviticus", "Numbers", "Deuteronomy", "Onkelos Genesis", "Onkelos Exodus", "Onkelos Leviticus", "Onkelos Numbers", "Onkelos Deuteronomy"]; diff --git a/static/js/TextColumnBanner.jsx b/static/js/TextColumnBanner.jsx index 8011b1a654..54e08fac7a 100644 --- a/static/js/TextColumnBanner.jsx +++ b/static/js/TextColumnBanner.jsx @@ -41,6 +41,28 @@ export const TranslationLanguagePreferenceSuggestionBanner = ({ setTranslationLa } +export const TranslationCallToActionBanner = ({ openTranslations }) => { + const textElement = ( + <InterfaceText> + <EnglishText> Want to <span className="bold">change</span> the translation?</EnglishText> + <HebrewText> Want to <span className="bold">change</span> the translation?</HebrewText> + </InterfaceText> + ); + const buttons = [ + { + text: "Go to translations", + onClick: () => { + openTranslations(); + // setConnectionsMode("Translations"); + } + }]; + const reject = () => {}; + return ( + <TextColumnBanner textElement={textElement} buttons={buttons} onClose={reject} /> + ); +}; + + /** * Banner which appears right above text column and informs a user of an action they can take From e07723e4039370a4e943bf931c827ac25baeb792 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Tue, 12 Sep 2023 10:49:08 +0300 Subject: [PATCH 229/756] chore: check if years is None or [] --- sefaria/model/text.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index 5044b6eabf..c174d682b9 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -441,11 +441,12 @@ def publication_time_period(self): def best_time_period(self): """ - :return: TimePeriod: First tries to return `compDate`. Deals with ranges and negative values for compDate - If no compDate, looks at author info + :return: TimePeriod: First tries to return `compDate`. + If no compDate or compDate is an empty list, _get_time_period returns None and it then looks at author info """ - if getattr(self, "compDate", None): - return self._get_time_period('compDate') + compDatePeriod = self._get_time_period('compDate') + if compDatePeriod: + return compDatePeriod else: author = self.author_objects()[0] if len(self.author_objects()) > 0 else None tp = author and author.most_accurate_time_period() @@ -456,8 +457,8 @@ def _get_time_period(self, date_field): Assumes that value of `date_field` ('pubDate' or 'compDate') is a list of integers. """ from . import timeperiod - years = getattr(self, date_field, None) - if years is not None: + years = getattr(self, date_field, []) + if len(years) > 0: startIsApprox = endIsApprox = getattr(self, 'hasErrorMargin', False) if len(years) > 1: start, end = years From 8fe27482b6ca626e3381c592fc37d8b79dcf2b5b Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Tue, 12 Sep 2023 11:07:06 +0300 Subject: [PATCH 230/756] chore: fix test_best_time_period --- sefaria/model/text.py | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index c174d682b9..6346fd5374 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -434,7 +434,7 @@ def publication_place(self): # This is similar to logic on GardenStop def composition_time_period(self): - return self._get_time_period("compDate") + return self._get_time_period("compDate", margin_field="hasErrorMargin") def publication_time_period(self): return self._get_time_period("pubDate") @@ -444,7 +444,7 @@ def best_time_period(self): :return: TimePeriod: First tries to return `compDate`. If no compDate or compDate is an empty list, _get_time_period returns None and it then looks at author info """ - compDatePeriod = self._get_time_period('compDate') + compDatePeriod = self._get_time_period('compDate', margin_field="hasErrorMargin") if compDatePeriod: return compDatePeriod else: @@ -452,24 +452,29 @@ def best_time_period(self): tp = author and author.most_accurate_time_period() return tp - def _get_time_period(self, date_field): + def _get_time_period(self, date_field, margin_field=""): """ Assumes that value of `date_field` ('pubDate' or 'compDate') is a list of integers. """ from . import timeperiod years = getattr(self, date_field, []) - if len(years) > 0: - startIsApprox = endIsApprox = getattr(self, 'hasErrorMargin', False) - if len(years) > 1: - start, end = years - else: - start = end = years[0] - return timeperiod.TimePeriod({ - "start": start, - "startIsApprox": startIsApprox, - "end": end, - "endIsApprox": endIsApprox - }) + if years is None or len(years) == 0: + return None + try: + error_margin = getattr(self, margin_field, False) if margin_field else False + except ValueError: + error_margin = False + startIsApprox = endIsApprox = error_margin + if len(years) > 1: + start, end = years + else: + start = end = years[0] + return timeperiod.TimePeriod({ + "start": start, + "startIsApprox": startIsApprox, + "end": end, + "endIsApprox": endIsApprox + }) # Index changes behavior of load_from_dict, so this circumvents that changed behavior to call load_from_dict on the abstract superclass def update_from_dict(self, d): From 83df2ece02b37e3fc28d77af1e9d8bbf27324172 Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Tue, 12 Sep 2023 12:17:25 +0300 Subject: [PATCH 231/756] fix(Topic Images): Addressing comments from review --- static/css/s2.css | 7 ++-- static/js/TopicPage.jsx | 72 ++++++++++++++++++++--------------------- 2 files changed, 38 insertions(+), 41 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index 47f8f36951..83945d7697 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -2208,17 +2208,14 @@ div.interfaceLinks-row a { border: 1px solid #EDEDEC; max-width: 100%; height: auto; - /* height: 292px; */ padding: 0 44; top: 121px; left: 835px; } .topicImageCaption .int-en { -/* styleName: English System Small; */ font-family: Roboto; } .topicImageCaption .int-he { - /* styleName: English System Small; */ font-family: Roboto; } .topicImageCaption { @@ -2226,8 +2223,8 @@ div.interfaceLinks-row a { font-weight: 400; line-height: 18px; letter-spacing: 0em; - color: #666666; - width: 292px; + color: var(--dark-grey); + width: 100%; } .topicImageWrapper{ padding-left: 44px; diff --git a/static/js/TopicPage.jsx b/static/js/TopicPage.jsx index 9843f0384d..38a5130bd5 100644 --- a/static/js/TopicPage.jsx +++ b/static/js/TopicPage.jsx @@ -400,7 +400,6 @@ const TopicPage = ({ const [showFilterHeader, setShowFilterHeader] = useState(false); const tabDisplayData = useTabDisplayData(translationLanguagePreference, versionPref); - const scrollableElement = useRef(); const clearAndSetTopic = (topic, topicTitle) => {setTopic(topic, topicTitle)}; @@ -698,41 +697,15 @@ const TopicSideSection = ({ title, children, hasMore }) => { ); } -const TopicImage = ({ topicTitle }) => { - // const [showMore, setShowMore] = useState(false); - const key = topicTitle.en; - const hardcodedMap = { - 'Rosh Hashanah': {'photoLink':'https://museums.cjh.org/web/objects/common/webmedia.php?irn=11469&reftable=ecatalogue&refirn=6640', - 'enCaption':'Rosh Hashanah\nArtist: Arthur Szyk (1894-1951)\nTempera and ink on paper\nNew Canaan, 1948\nCollection of Yeshiva University Museum\nGift of Charles Frost', - 'heCaption': 'ראש השנה, ארתור שיק, ארה״ב 1948. אוסף ישיבה יוניברסיטי'}, - - 'Yom Kippur': {'photoLink':'https://www.bl.uk/IllImages/BLCD/big/K900/K90075-77.jpg', - 'enCaption':'Micrography of Jonah being swallowed by the fish, at the text of Jonah, the haftarah for the afternoon service of Yom Kippur.\n 1300-1500, Germany (The British Library)', - 'heCaption': 'מיקורגפיה של יונה בבטן הדג, מתוך ספר יונה ההפטרה של יום כיפור, 1300-1500'}, - - 'The Four Species': {'photoLink':'https://res.cloudinary.com/the-jewish-museum/image/fetch/q_auto,f_auto/v1/https%3A%2F%2Fthejm.netx.net%2Ffile%2Fasset%2F34234%2Fview%2F52568%2Fview_52568%3Ftoken%3D5d5cdc57-6399-40b5-afb0-93139921700e', - 'enCaption':'Etrog container, K B, late 19th century, Germany.\n The Jewish Museum, Gift of Dr. Harry G. Friedman', - 'heCaption': 'תיבת אתרוג, סוף המאה ה19, גרמניה. מתנת הארי ג. פרידמן '}, - - 'Sukkot': {'photoLink':'https://www.bl.uk/IllImages/BLCD/big/d400/d40054-17a.jpg', - 'enCaption':'Detail of a painting of a sukkah (booth built for Sukkot).\n Image taken from f. 316v of Prayer book (Forli Siddur) for the entire year, 1383, Italian rite.', - 'heCaption': 'פרט ציור של סוכה עם שולחן פרוס ושלוש דמויות. דימוי מתוך סידור פורלי, 1383 איטליה'}, - - 'Simchat Torah': {'photoLink':'https://upload.wikimedia.org/wikipedia/commons/4/4d/Rosh_Hashanah_greeting_card_%287974345646%29.jpg?20150712114334', - 'enCaption':'Rosh Hashanah postcard: Hakafot\nArtist: Haim Yisroel Goldberg (1888-1943)\nPublisher: Williamsburg Post Card Co.\nGermany, ca. 1915\nCollection of Yeshiva University Museum', - 'heCaption': 'גלויה לראש השנה: הקפות, חיים גולדברג, גרמניה 1915, אוסף ישיבה יוניברסיטי'}, +const TopicImage = ({photoLink, enCaption, heCaption }) => { - 'Shabbat': {'photoLink':'https://res.cloudinary.com/the-jewish-museum/image/fetch/q_auto,f_auto/v1/https%3A%2F%2Fthejm.netx.net%2Ffile%2Fasset%2F35064%2Fview%2F61838%2Fview_61838%3Ftoken%3D5d5cdc57-6399-40b5-afb0-93139921700e', - 'enCaption':'Friday Evening, Isidor Kaufmann, Austria c. 1920.\n The Jewish Museum, Gift of Mr. and Mrs. M. R. Schweitzer', - 'heCaption': 'שישי בערב, איזידור קאופמן, וינה 1920. מתנת מר וגברת מ.ר. שוייצר'}, - }; - return ( key in hardcodedMap ? - (<div class="topicImageWrapper"> - <img class="topicImagePicture" src={hardcodedMap[key].photoLink}/> + return( + <div class="topicImageWrapper"> + <img class="topicImagePicture" src={photoLink}/> <div class="topicImageCaption topicImageCaptionLinebreak"> - <InterfaceText text={{en:hardcodedMap[key].enCaption, he:hardcodedMap[key].heCaption}} /> + <InterfaceText text={{en:enCaption, he:heCaption}} /> </div> - </div>) : null); + </div>); } @@ -790,7 +763,36 @@ const propKeys = [ {en: 'jeLink', he: 'jeLink', title: 'Jewish Encyclopedia'}, {en: 'enNliLink', he: 'heNliLink', title: 'National Library of Israel'}, ]; + + const TopicMetaData = ({ topicTitle, timePeriod, properties={} }) => { + const topicImageKey = topicTitle.en; + const hardcodedTopicImagesMap = { + 'Rosh Hashanah': {'photoLink':'https://museums.cjh.org/web/objects/common/webmedia.php?irn=11469&reftable=ecatalogue&refirn=6640', + 'enCaption':'Rosh Hashanah\nArtist: Arthur Szyk (1894-1951)\nTempera and ink on paper\nNew Canaan, 1948\nCollection of Yeshiva University Museum\nGift of Charles Frost', + 'heCaption': 'ראש השנה, ארתור שיק, ארה״ב 1948. אוסף ישיבה יוניברסיטי'}, + + 'Yom Kippur': {'photoLink':'https://www.bl.uk/IllImages/BLCD/big/K900/K90075-77.jpg', + 'enCaption':'Micrography of Jonah being swallowed by the fish, at the text of Jonah, the haftarah for the afternoon service of Yom Kippur.\n 1300-1500, Germany (The British Library)', + 'heCaption': 'מיקורגפיה של יונה בבטן הדג, מתוך ספר יונה ההפטרה של יום כיפור, 1300-1500'}, + + 'The Four Species': {'photoLink':'https://res.cloudinary.com/the-jewish-museum/image/fetch/q_auto,f_auto/v1/https%3A%2F%2Fthejm.netx.net%2Ffile%2Fasset%2F34234%2Fview%2F52568%2Fview_52568%3Ftoken%3D5d5cdc57-6399-40b5-afb0-93139921700e', + 'enCaption':'Etrog container, K B, late 19th century, Germany.\n The Jewish Museum, Gift of Dr. Harry G. Friedman', + 'heCaption': 'תיבת אתרוג, סוף המאה ה19, גרמניה. מתנת הארי ג. פרידמן '}, + + 'Sukkot': {'photoLink':'https://www.bl.uk/IllImages/BLCD/big/d400/d40054-17a.jpg', + 'enCaption':'Detail of a painting of a sukkah (booth built for Sukkot).\n Image taken from f. 316v of Prayer book (Forli Siddur) for the entire year, 1383, Italian rite.', + 'heCaption': 'פרט ציור של סוכה עם שולחן פרוס ושלוש דמויות. דימוי מתוך סידור פורלי, 1383 איטליה'}, + + 'Simchat Torah': {'photoLink':'https://upload.wikimedia.org/wikipedia/commons/4/4d/Rosh_Hashanah_greeting_card_%287974345646%29.jpg?20150712114334', + 'enCaption':'Rosh Hashanah postcard: Hakafot\nArtist: Haim Yisroel Goldberg (1888-1943)\nPublisher: Williamsburg Post Card Co.\nGermany, ca. 1915\nCollection of Yeshiva University Museum', + 'heCaption': 'גלויה לראש השנה: הקפות, חיים גולדברג, גרמניה 1915, אוסף ישיבה יוניברסיטי'}, + + 'Shabbat': {'photoLink':'https://res.cloudinary.com/the-jewish-museum/image/fetch/q_auto,f_auto/v1/https%3A%2F%2Fthejm.netx.net%2Ffile%2Fasset%2F35064%2Fview%2F61838%2Fview_61838%3Ftoken%3D5d5cdc57-6399-40b5-afb0-93139921700e', + 'enCaption':'Friday Evening, Isidor Kaufmann, Austria c. 1920.\n The Jewish Museum, Gift of Mr. and Mrs. M. R. Schweitzer', + 'heCaption': 'שישי בערב, איזידור קאופמן, וינה 1920. מתנת מר וגברת מ.ר. שוייצר'}, + + }; const tpSection = !!timePeriod ? ( <TopicSideSection title={{en: "Lived", he: "תקופת פעילות"}}> <div className="systemText topicMetaData"><InterfaceText text={timePeriod.name} /></div> @@ -806,7 +808,6 @@ const TopicMetaData = ({ topicTitle, timePeriod, properties={} }) => { })); const hasProps = propValues.reduce((accum, curr) => accum || curr.url.en || curr.url.he, false); const propsSection = hasProps ? ( - <div> <TopicSideSection title={{en: "Learn More", he: "לקריאה נוספת"}}> { propValues.map(propObj => { @@ -828,11 +829,10 @@ const TopicMetaData = ({ topicTitle, timePeriod, properties={} }) => { }) } </TopicSideSection> - </div> ) : null; return ( <> - <TopicImage topicTitle={topicTitle}/> + {topicImageKey in hardcodedTopicImagesMap && <TopicImage photoLink={hardcodedTopicImagesMap[topicImageKey].photoLink} enCaption={hardcodedTopicImagesMap[topicImageKey].enCaption} heCaption={hardcodedTopicImagesMap[topicImageKey].heCaption}/>} { tpSection } { propsSection } </> From e4240588dfe7af199a3ed44818ad41be8a0ec2e3 Mon Sep 17 00:00:00 2001 From: Russel Neiss <russel.neiss@gmail.com> Date: Tue, 12 Sep 2023 12:05:18 -0500 Subject: [PATCH 232/756] fix: hotfix strapi settings to get site up --- sefaria/settings.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sefaria/settings.py b/sefaria/settings.py index 7eea25ff70..0817a5c5f3 100644 --- a/sefaria/settings.py +++ b/sefaria/settings.py @@ -70,6 +70,9 @@ # Make this unique, and don't share it with anybody. SECRET_KEY = '' +STRAPI_LOCATION = 'https://credible-basketball-a14e66cd27.strapiapp.com' +STRAPI_PORT = 443 + TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', From 3233ec448f8f26aa3398a51bc209bc152b6e882c Mon Sep 17 00:00:00 2001 From: Russel Neiss <russel.neiss@gmail.com> Date: Tue, 12 Sep 2023 12:43:25 -0500 Subject: [PATCH 233/756] fix: Undo strapi hotfix for vars in helm --- sefaria/settings.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/sefaria/settings.py b/sefaria/settings.py index 0817a5c5f3..7eea25ff70 100644 --- a/sefaria/settings.py +++ b/sefaria/settings.py @@ -70,9 +70,6 @@ # Make this unique, and don't share it with anybody. SECRET_KEY = '' -STRAPI_LOCATION = 'https://credible-basketball-a14e66cd27.strapiapp.com' -STRAPI_PORT = 443 - TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', From 42b55f842061c853c6b15f03f3b25df5b95f91aa Mon Sep 17 00:00:00 2001 From: Russel Neiss <russel.neiss@gmail.com> Date: Tue, 12 Sep 2023 12:53:17 -0500 Subject: [PATCH 234/756] static: fix typo --- templates/static/en/team.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/static/en/team.html b/templates/static/en/team.html index 8eb851b6b3..13196684eb 100644 --- a/templates/static/en/team.html +++ b/templates/static/en/team.html @@ -332,11 +332,11 @@ <h1> </div> <div class="teamMember"> <div class="teamMemberImage"> - <img src="{% static 'img/headshots/desiree.png' %}" alt="Headshot of Desiree Nissani"> + <img src="{% static 'img/headshots/desiree.png' %}" alt="Headshot of Desiree Neissani"> </div> <div class="teamMemberDetails"> <div class="teamName"> - <span class="int-en">Desiree Nissani</span> + <span class="int-en">Desiree Neissani</span> </div> <div class="teamTitle"> <span class="int-en">Special Assistant, Office of the CEO</span> From ff8dc914454efa0f76d83260c30bce8234b539b6 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Wed, 13 Sep 2023 09:34:10 +0300 Subject: [PATCH 235/756] feat(Topic Editor): update props that change --- reader/views.py | 5 ++-- sefaria/helper/topic.py | 36 ++++++++++++++--------- sefaria/model/place.py | 17 +++++------ sefaria/model/topic.py | 7 +++-- static/js/Misc.jsx | 16 ++++++----- static/js/TopicEditor.jsx | 60 +++++++++++++++++++++++++-------------- static/js/TopicSearch.jsx | 2 +- 7 files changed, 86 insertions(+), 57 deletions(-) diff --git a/reader/views.py b/reader/views.py index 129d78f046..9a550ffaca 100644 --- a/reader/views.py +++ b/reader/views.py @@ -3112,7 +3112,8 @@ def add_new_topic_api(request): t.description_published = True t.data_source = "sefaria" # any topic edited manually should display automatically in the TOC and this flag ensures this - t.change_description(data["description"], data.get("catDescription", None)) + if "description" in data: + t.change_description(data["description"], data.get("categoryDescription", None)) t.save() library.build_topic_auto_completer() @@ -3165,8 +3166,6 @@ def topics_api(request, topic, v2=False): topic_obj = Topic().load({'slug': topic_data["origSlug"]}) topic_data["manual"] = True author_status_changed = (topic_data["category"] == "authors") ^ (topic_data["origCategory"] == "authors") - if topic_data["category"] == topic_data["origCategory"]: - topic_data.pop("category") # no need to check category in update_topic topic_obj = update_topic(topic_obj, **topic_data) if author_status_changed: library.build_topic_auto_completer() diff --git a/sefaria/helper/topic.py b/sefaria/helper/topic.py index e2ad46c361..36ebef5f48 100644 --- a/sefaria/helper/topic.py +++ b/sefaria/helper/topic.py @@ -1060,15 +1060,24 @@ def update_authors_place_and_time(topic_obj, data, dataSource='learning-team-edi return topic_obj +def update_properties(topic_obj, dataSource, k, v): + if v == '': + topic_obj.properties.pop(k, None) + else: + topic_obj.properties[k] = {'value': v, 'dataSource': dataSource} + def update_author_era(topic_obj, data, dataSource='learning-team-editing-tool'): for k in ["birthYear", "deathYear"]: - year = data.get(k, '') - topic_obj.properties[k] = {'value': year, 'dataSource': dataSource} - - prev_era = topic_obj.properties.get('era', {}).get('value') - if data.get('era', '') != '' and prev_era != data['era']: - topic_obj.properties['era'] = {'value': data['era'], 'dataSource': dataSource} - create_era_link(topic_obj, prev_era_to_delete=prev_era) + if k in data.keys(): # only change property value if key is in data, otherwise it indicates no change + year = data[k] + update_properties(topic_obj, dataSource, k, year) + + if 'era' in data.keys(): # only change property value if key is in data, otherwise it indicates no change + prev_era = topic_obj.properties.get('era', {}).get('value') + era = data['era'] + update_properties(topic_obj, dataSource, 'era', era) + if era != '': + create_era_link(topic_obj, prev_era_to_delete=prev_era) return topic_obj @@ -1076,19 +1085,18 @@ def update_topic(topic_obj, **kwargs): """ Can update topic object's title, hebrew title, category, description, and categoryDescription fields :param topic_obj: (Topic) The topic to update - :param **kwargs can be title, heTitle, category, description, catDescription, and rebuild_toc where `title`, `heTitle`, - and `category` are strings. `description` and `catDescription` are dictionaries where the fields are `en` and `he`. + :param **kwargs can be title, heTitle, category, description, categoryDescription, and rebuild_toc where `title`, `heTitle`, + and `category` are strings. `description` and `categoryDescription` are dictionaries where the fields are `en` and `he`. The `category` parameter should be the slug of the new category. `rebuild_topic_toc` is a boolean and is assumed to be True :return: (model.Topic) The modified topic """ old_category = "" orig_slug = topic_obj.slug - update_topic_titles(topic_obj, kwargs) - if kwargs.get('catSlug') == 'authors': + if kwargs.get('category') == 'authors': topic_obj = update_authors_place_and_time(topic_obj, kwargs) - if 'category' in kwargs: + if 'category' in kwargs and kwargs['category'] != kwargs['origCategory']: orig_link = IntraTopicLink().load({"linkType": "displays-under", "fromTopic": topic_obj.slug, "toTopic": {"$ne": topic_obj.slug}}) old_category = orig_link.toTopic if orig_link else Topic.ROOT if old_category != kwargs['category']: @@ -1098,8 +1106,8 @@ def update_topic(topic_obj, **kwargs): topic_obj.data_source = "sefaria" # any topic edited manually should display automatically in the TOC and this flag ensures this topic_obj.description_published = True - if "description" in kwargs: - topic_obj.change_description(kwargs["description"], kwargs.get("catDescription", None)) + if "description" in kwargs or "categoryDescription" in kwargs: + topic_obj.change_description(kwargs.get("description", None), kwargs.get("categoryDescription", None)) topic_obj.save() diff --git a/sefaria/model/place.py b/sefaria/model/place.py index ebeb8e6b2c..a39bf0e610 100644 --- a/sefaria/model/place.py +++ b/sefaria/model/place.py @@ -108,12 +108,13 @@ def process_index_place_change(indx, **kwargs): def process_topic_place_change(topic_obj, data): keys = ["birthPlace", "deathPlace"] for key in keys: - he_key = get_he_key(key) - he_new_val = data.get(he_key, '') - new_val = data.get(key, '') - if new_val != '': - place = Place.create_new_place(en=new_val, he=he_new_val) - topic_obj.properties[key] = {"value": place.primary_name('en'), 'dataSource': 'learning-team-editing-tool'} - else: - topic_obj.properties.pop(key, None) + if key in data.keys(): # only change property value if key is in data, otherwise it indicates no change + new_val = data[key] + if new_val != '': + he_key = get_he_key(key) + he_new_val = data.get(he_key, '') + place = Place.create_new_place(en=new_val, he=he_new_val) + topic_obj.properties[key] = {"value": place.primary_name('en'), 'dataSource': 'learning-team-editing-tool'} + else: + topic_obj.properties.pop(key, None) diff --git a/sefaria/model/topic.py b/sefaria/model/topic.py index 0cf82d38d8..0127bc723b 100644 --- a/sefaria/model/topic.py +++ b/sefaria/model/topic.py @@ -136,10 +136,13 @@ def change_description(self, desc, cat_desc=None): :param cat_desc: Optional. Dictionary of category descriptions, with keys being two letter language codes :return: """ - + if cat_desc is None: + cat_desc = {"en": "", "he": ""} + if desc is None: + desc = {"en": "", "he": ""} self.description = desc if getattr(self, "isTopLevelDisplay", False): - self.categoryDescription = cat_desc if cat_desc else {"en": "", "he": ""} + self.categoryDescription = cat_desc elif getattr(self, "categoryDescription", False): delattr(self, "categoryDescription") diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index ad36261364..840afe814c 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -1191,11 +1191,13 @@ const EditorForExistingTopic = ({ toggle, data }) => { const initCatSlug = TopicToCategorySlug(data); const origData = { origSlug: data.slug, - origCategorySlug: initCatSlug, - origEn: data.primaryTitle.en, - origHe: data.primaryTitle.he || "", - origDesc: data.description || {"en": "", "he": ""}, - origCategoryDesc: data.categoryDescription || {"en": "", "he": ""}, + origCatSlug: initCatSlug, + origEnTitle: data.primaryTitle.en, + origHeTitle: data.primaryTitle.he || "", + origEnDescription: data.description?.en || "", + origHeDescription: data.description?.he || "", + origEnCategoryDescription: data.categoryDescription?.en || "", + origHeCategoryDescription: data.categoryDescription?.he || "", origEnAltTitles: prepAltTitles('en'), origHeAltTitles: prepAltTitles('he'), origBirthPlace: data?.properties?.birthPlace?.value, @@ -1257,12 +1259,12 @@ const CategoryEditorWrapper = ({toggle, data, type}) => { } const CategoryAdderWrapper = ({toggle, data, type}) => { - const origData = {origEn: ""}; + const origData = {origEnTitle: ""}; switch (type) { case "cats": return <CategoryEditor origData={origData} close={toggle} origPath={data}/>; case "topics": - origData['origCategorySlug'] = data; + origData['origCatSlug'] = data; return <TopicEditor origData={origData} close={toggle} origWasCat={false}/>; } } diff --git a/static/js/TopicEditor.jsx b/static/js/TopicEditor.jsx index d962e88bd2..3d7dd86f74 100644 --- a/static/js/TopicEditor.jsx +++ b/static/js/TopicEditor.jsx @@ -7,11 +7,12 @@ import React, {useState, useEffect} from "react"; const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { - const [data, setData] = useState({...origData, catSlug: origData.origCategorySlug || "", - enTitle: origData.origEn, heTitle: origData.origHe || "", heDescription: origData?.origDesc?.he || "", - enDescription: origData?.origDesc?.en || "", - enCategoryDescription: origData?.origCategoryDesc?.en, - heCategoryDescription: origData?.origCategoryDesc?.he, + const [data, setData] = useState({...origData, catSlug: origData.origCatSlug || "", + enTitle: origData.origEnTitle, heTitle: origData.origHeTitle || "", + heDescription: origData?.origHeDescription || "", + enDescription: origData?.origEnDescription || "", + enCategoryDescription: origData?.origEnCategoryDescription || "", + heCategoryDescription: origData?.origHeCategoryDescription || "", enAltTitles: origData?.origEnAltTitles || [], heAltTitles: origData?.origHeAltTitles || [], birthPlace: origData.origBirthPlace || "", heBirthPlace: origData.origHeBirthPlace || "", @@ -21,7 +22,7 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { }); const isNew = !('origSlug' in origData); const [savingStatus, setSavingStatus] = useState(false); - const [isAuthor, setIsAuthor] = useState(origData.origCategorySlug === 'authors'); + const [isAuthor, setIsAuthor] = useState(origData.origCatSlug === 'authors'); const [isCategory, setIsCategory] = useState(origWasCat); // initialize to True if the topic originally was a category // isCategory determines whether user can edit categoryDescriptions of topic const subtopics = Sefaria.topicTocPage(origData.origSlug); @@ -40,6 +41,7 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { //logic is: if it starts out originally a category, isCategory should always be true, otherwise, it should depend solely on 'Main Menu' const newIsCategory = origWasCat || e.target.value === Sefaria._("Main Menu"); setIsCategory(newIsCategory); + setIsChanged(true); setIsAuthor(data.catSlug === 'authors'); setData({...data}); } @@ -92,25 +94,39 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { } const prepData = () => { - let postData = {...data, "description": {"en": data.enDescription, "he": data.heDescription}, "title": data.enTitle, - "heTitle": data.heTitle}; - if (isCategory) { - postData = {...postData, "catDescription": {"en": data.enCategoryDescription, "he": data.heCategoryDescription}}; + // always add category, title, heTitle, altTitles + let postData = { category: data.catSlug, title: data.enTitle, heTitle: data.heTitle, altTitles: {}}; + postData.altTitles.en = data.enAltTitles.map(x => x.name); // alt titles implemented using TitleVariants which contains list of objects with 'name' property. + postData.altTitles.he = data.heAltTitles.map(x => x.name); + + // add descriptions if they changed + const origDescription = {en: origData?.origEnDescription || "", he: origData?.origHeDescription || ""}; + const origCategoryDescription = {en: origData?.origEnCategoryDescription || "", he: origData?.origHeCategoryDescription || ""}; + const descriptionChanged = data.enDescription !== origDescription.en || data.heDescription !== origDescription.he; + if (descriptionChanged) { + postData.description = {en: data.enDescription, he: data.heDescription}; } - if (data?.era && Sefaria.util.inArray(data.era, Sefaria._eras) === -1) { - delete postData.era; + const categoryDescriptionChanged = data.enCategoryDescription !== origCategoryDescription.en || data.heCategoryDescription !== origCategoryDescription.he + if (isCategory && categoryDescriptionChanged) { + postData.categoryDescription = {en: data.enCategoryDescription, he: data.heCategoryDescription}; } - postData.altTitles = {}; - // alt titles implemented using TitleVariants which contains list of objects with 'name' property. - postData.altTitles.en = postData.enAltTitles.map(x => x.name); - postData.altTitles.he = postData.heAltTitles.map(x => x.name); - postData.category = data.catSlug; + + // add author keys if they changed + if (isAuthor) { + let authorKeys = ['era', 'birthPlace', 'heBirthPlace', 'birthYear', 'deathYear', 'deathPlace', 'heDeathPlace']; + authorKeys.map(k => { + const firstLetter = k.charAt(0).toUpperCase(); + const origKey = `orig${firstLetter}${k.slice(1)}`; + const origVal = origData[origKey] || ""; + const newVal = data[k] || ""; + if (origVal !== newVal) { + postData[k] = data[k]; + } + }) + } + if (!isNew) { - postData = {...postData, origCategory: data.origCategorySlug, origDescription: data.origDesc, - origSlug: data.origSlug}; - if (isCategory) { - postData.origCatDescription = data.origCategoryDesc; - } + postData = {...postData, origSlug: data.origSlug, origCategory: data.origCatSlug}; } return postData; diff --git a/static/js/TopicSearch.jsx b/static/js/TopicSearch.jsx index ece2971e6f..c736cc72cf 100644 --- a/static/js/TopicSearch.jsx +++ b/static/js/TopicSearch.jsx @@ -108,7 +108,7 @@ class TopicSearch extends Component { render() { if (this.state.showAdminEditor) { - const topicData = {origEn: this.state.value}; + const topicData = {origEnTitle: this.state.value}; return <TopicEditor origData={topicData} close={this.reset} onCreateSuccess={this.post}/>; } else { From c69e1a9733396d1dcf6c7d71807ac8d42bff4e2f Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Wed, 13 Sep 2023 12:50:18 +0300 Subject: [PATCH 236/756] feat(text api): add available_versions to the returned object. --- api/views.py | 4 ++-- sefaria/model/text_manager.py | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/api/views.py b/api/views.py index a81189f564..a4cfe8d294 100644 --- a/api/views.py +++ b/api/views.py @@ -32,11 +32,11 @@ def _handle_warnings(self, data): elif vtitle and vtitle != 'all': warning = APINoVersion(self.oref, vtitle, lang) else: - warning = APINoLanguageVersion(self.oref, data['availabe_langs']) + warning = APINoLanguageVersion(self.oref, data['available_langs']) representing_string = f'{lang}|{vtitle}' if vtitle else lang data['warnings'].append({representing_string: warning.get_message()}) data.pop('missings') - data.pop('availabe_langs') + data.pop('available_langs') return data def get(self, request, *args, **kwargs): diff --git a/sefaria/model/text_manager.py b/sefaria/model/text_manager.py index 6ee0219b24..6beb63c8af 100644 --- a/sefaria/model/text_manager.py +++ b/sefaria/model/text_manager.py @@ -15,10 +15,14 @@ def __init__(self, oref: Ref, versions_params: List[List[str]]): self.oref = oref self.handled_version_params = [] self.all_versions = self.oref.versionset() + + fields = Version.optional_attrs + Version.required_attrs + fields.remove('chapter') # not metadata self.return_obj = { 'versions': [], 'missings': [], - 'availabe_langs': sorted({v.actualLanguage for v in self.all_versions}) + 'available_langs': sorted({v.actualLanguage for v in self.all_versions}), + 'available_versions': [{f: getattr(v, f, "") for f in fields} for v in self.all_versions] } def _append_version(self, version): From 7c88c8d8f199f6702bbf0b066812e0933a6c9eff Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 13 Sep 2023 13:41:50 +0300 Subject: [PATCH 237/756] feat: implement TextColumnBannerChooser to decide what banner to render. --- static/js/ReaderPanel.jsx | 16 ++++------ static/js/TextColumnBanner.jsx | 53 ++++++++++++++++++++++++---------- 2 files changed, 44 insertions(+), 25 deletions(-) diff --git a/static/js/ReaderPanel.jsx b/static/js/ReaderPanel.jsx index 103e35ad55..6280d00279 100644 --- a/static/js/ReaderPanel.jsx +++ b/static/js/ReaderPanel.jsx @@ -31,7 +31,7 @@ import UserStats from './UserStats'; import ModeratorToolsPanel from './ModeratorToolsPanel'; import PublicCollectionsPage from './PublicCollectionsPage'; import TranslationsPage from './TranslationsPage'; -import {TranslationCallToActionBanner, TranslationLanguagePreferenceSuggestionBanner} from './TextColumnBanner'; +import { TextColumnBannerChooser } from './TextColumnBanner'; import { CloseButton, MenuButton, @@ -1384,9 +1384,11 @@ class ReaderControls extends Component { <DisplaySettingsButton onClick={this.props.openDisplaySettings} /> </div>); const openTranslations = this.props.openConnectionsPanel.bind(null, [this.props.currentRef], null, {"connectionsMode": "Translations"}); - let banner = (hideHeader || connectionsHeader) ? null : getTextColumnBanner( - <TranslationLanguagePreferenceSuggestionBanner setTranslationLanguagePreference={this.props.setTranslationLanguagePreference} />, - <TranslationCallToActionBanner openTranslations={openTranslations} /> + let banner = (hideHeader || connectionsHeader) ? null : ( + <TextColumnBannerChooser + setTranslationLanguagePreference={this.props.setTranslationLanguagePreference} + openTranslations={openTranslations} + /> ); const classes = classNames({ readerControls: 1, @@ -1442,12 +1444,6 @@ ReaderControls.propTypes = { }; -const getTextColumnBanner = (transPrefBanner, transCallToActionBanner) => { - return transCallToActionBanner; - // return transPrefBanner || transCallToActionBanner; -} - - class ReaderDisplayOptionsMenu extends Component { renderAliyotToggle() { let torah = ["Genesis", "Exodus", "Leviticus", "Numbers", "Deuteronomy", "Onkelos Genesis", "Onkelos Exodus", "Onkelos Leviticus", "Onkelos Numbers", "Onkelos Deuteronomy"]; diff --git a/static/js/TextColumnBanner.jsx b/static/js/TextColumnBanner.jsx index 54e08fac7a..2d5efc83f5 100644 --- a/static/js/TextColumnBanner.jsx +++ b/static/js/TextColumnBanner.jsx @@ -5,14 +5,42 @@ import { CloseButton, InterfaceText, EnglishText, HebrewText } from './Misc'; -export const TranslationLanguagePreferenceSuggestionBanner = ({ setTranslationLanguagePreference }) => { - const [accepted, setAccepted] = useState(false); +const cookie = Sefaria._inBrowser ? $.cookie : Sefaria.util.cookie; +const { translation_language_preference_suggestion } = Sefaria; - const cookie = Sefaria._inBrowser ? $.cookie : Sefaria.util.cookie; - const { translation_language_preference_suggestion } = Sefaria; - if ((!accepted && cookie("translation_language_preference_suggested")) || !translation_language_preference_suggestion) { - return null; +export const TextColumnBannerChooser = ({ setTranslationLanguagePreference, openTranslations }) => { + const [bannerAccepted, setBannerAccepted] = useState(false); + const shouldTransPrefBannerRender = () => { + // we haven't suggested yet and we have a suggestion + return !cookie("translation_language_preference_suggested") && translation_language_preference_suggestion + }; + if (shouldTransPrefBannerRender()) { + return (<TranslationLanguagePreferenceSuggestionBanner + setAccepted={setBannerAccepted} + setTranslationLanguagePreference={setTranslationLanguagePreference} + />); + } else if (bannerAccepted) { + return <TransLangPrefAcceptedBanner />; } + return <TranslationCallToActionBanner openTranslations={openTranslations} />; +}; + + +const TransLangPrefAcceptedBanner = () => { + const lang = Sefaria.translateISOLanguageCode(translation_language_preference_suggestion); + const textElement = ( + <InterfaceText> + <EnglishText> Thanks! We'll show you {lang} translations first when we have them. </EnglishText> + <HebrewText>תודה! כשנוכל, נציג לכם תרגומים בשפה ה<span className="bold">{Sefaria._(lang)}</span> כאשר אלו יהיו זמינים. </HebrewText> + </InterfaceText> + ); + return ( + <TextColumnBanner textElement={textElement} /> + ); +} + + +const TranslationLanguagePreferenceSuggestionBanner = ({ setAccepted, setTranslationLanguagePreference }) => { const reject = () => { cookie("translation_language_preference_suggested", JSON.stringify(1), {path: "/"}); Sefaria.editProfileAPI({settings: {translation_language_preference_suggested: true}}); @@ -22,18 +50,13 @@ export const TranslationLanguagePreferenceSuggestionBanner = ({ setTranslationLa setTranslationLanguagePreference(translation_language_preference_suggestion); } const lang = Sefaria.translateISOLanguageCode(translation_language_preference_suggestion); - const textElement = accepted ? ( - <InterfaceText> - <EnglishText> Thanks! We'll show you {lang} translations first when we have them. </EnglishText> - <HebrewText>תודה! כשנוכל, נציג לכם תרגומים בשפה ה<span className="bold">{Sefaria._(lang)}</span> כאשר אלו יהיו זמינים. </HebrewText> - </InterfaceText> - ) : ( + const textElement = ( <InterfaceText> <EnglishText> Prefer to see <span className="bold"> {lang} </span> translations when available? </EnglishText> <HebrewText>האם תעדיפו לראות תרגומים בשפה ה<span className="bold">{Sefaria._(lang)}</span> כאשר הם זמינים?</HebrewText> </InterfaceText> ); - const buttons = accepted ? null : [{text: "Yes", onClick: accept}, {text: "No", onClick: reject, sideEffect: "close" }]; + const buttons = [{text: "Yes", onClick: accept}, {text: "No", onClick: reject, sideEffect: "close" }]; return ( <TextColumnBanner textElement={textElement} buttons={buttons} onClose={reject}/> @@ -41,7 +64,7 @@ export const TranslationLanguagePreferenceSuggestionBanner = ({ setTranslationLa } -export const TranslationCallToActionBanner = ({ openTranslations }) => { +const TranslationCallToActionBanner = ({ openTranslations }) => { const textElement = ( <InterfaceText> <EnglishText> Want to <span className="bold">change</span> the translation?</EnglishText> @@ -85,7 +108,7 @@ const TextColumnBanner = ({ textElement, buttons, onClose }) => { <div className="transLangPrefCentered"> { textElement } <div className="yesNoGroup"> - { buttons.map(button => <TextColumnBannerButton key={button.text} button={button} setBannerClosed={setClosed}/>) } + { buttons?.map(button => <TextColumnBannerButton key={button.text} button={button} setBannerClosed={setClosed}/>) } </div> </div> <CloseButton onClick={closeBanner} /> From b426b1adb09589686f8bd05a48ebba1990c3afec Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 13 Sep 2023 13:44:15 +0300 Subject: [PATCH 238/756] refactor: shorten names of components --- static/js/TextColumnBanner.jsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/static/js/TextColumnBanner.jsx b/static/js/TextColumnBanner.jsx index 2d5efc83f5..097ed0d710 100644 --- a/static/js/TextColumnBanner.jsx +++ b/static/js/TextColumnBanner.jsx @@ -15,14 +15,14 @@ export const TextColumnBannerChooser = ({ setTranslationLanguagePreference, open return !cookie("translation_language_preference_suggested") && translation_language_preference_suggestion }; if (shouldTransPrefBannerRender()) { - return (<TranslationLanguagePreferenceSuggestionBanner + return (<TransLangPrefBanner setAccepted={setBannerAccepted} setTranslationLanguagePreference={setTranslationLanguagePreference} />); } else if (bannerAccepted) { return <TransLangPrefAcceptedBanner />; } - return <TranslationCallToActionBanner openTranslations={openTranslations} />; + return <TransCallToActionBanner openTranslations={openTranslations} />; }; @@ -40,7 +40,7 @@ const TransLangPrefAcceptedBanner = () => { } -const TranslationLanguagePreferenceSuggestionBanner = ({ setAccepted, setTranslationLanguagePreference }) => { +const TransLangPrefBanner = ({ setAccepted, setTranslationLanguagePreference }) => { const reject = () => { cookie("translation_language_preference_suggested", JSON.stringify(1), {path: "/"}); Sefaria.editProfileAPI({settings: {translation_language_preference_suggested: true}}); @@ -64,7 +64,7 @@ const TranslationLanguagePreferenceSuggestionBanner = ({ setAccepted, setTransla } -const TranslationCallToActionBanner = ({ openTranslations }) => { +const TransCallToActionBanner = ({ openTranslations }) => { const textElement = ( <InterfaceText> <EnglishText> Want to <span className="bold">change</span> the translation?</EnglishText> From f9512cdde306ad9417e634529d77682f03a11dcd Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Wed, 13 Sep 2023 14:01:28 +0300 Subject: [PATCH 239/756] fix(Gardens): derive metadata based on compDate being an array not a string --- sefaria/model/garden.py | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/sefaria/model/garden.py b/sefaria/model/garden.py index e55bcd6a86..b127aa58c1 100644 --- a/sefaria/model/garden.py +++ b/sefaria/model/garden.py @@ -441,24 +441,16 @@ def _derive_metadata(self): # Time # This is similar to logic on Index.composition_time_period() refactor if getattr(self, "start", None) is None or getattr(self, "end", None) is None: - if getattr(i, "compDate", None): - errorMargin = int(getattr(i, "errorMargin", 0)) - self.startIsApprox = self.endIsApprox = errorMargin > 0 - - try: - year = int(getattr(i, "compDate")) - self.start = year - errorMargin - self.end = year + errorMargin - except ValueError as e: - years = getattr(i, "compDate").split("-") - if years[0] == "" and len(years) == 3: #Fix for first value being negative - years[0] = -int(years[1]) - years[1] = int(years[2]) - self.start = int(years[0]) - errorMargin - self.end = int(years[1]) + errorMargin - - elif author and author.mostAccurateTimePeriod(): - tp = author.mostAccurateTimePeriod() + years = getattr(i, 'compDate', []) + if years and len(years) > 0: + self.startIsApprox = self.endIsApprox = getattr(i, "hasErrorMargin", False) + if len(years) > 1: + self.start = years[0] + self.end = years[1] + else: + self.start = self.end = years[0] + elif author and author.most_accurate_time_period(): + tp = author.most_accurate_time_period() self.start = tp.start self.end = tp.end self.startIsApprox = tp.startIsApprox From badc3732a43ce0753d0317906818cd678cb3d956 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 13 Sep 2023 14:55:17 +0300 Subject: [PATCH 240/756] feat: only show TransCallToActionBanner in appropriate cases --- static/js/ReaderPanel.jsx | 2 ++ static/js/TextColumnBanner.jsx | 10 ++++++++-- static/js/sefaria/sefaria.js | 9 +++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/static/js/ReaderPanel.jsx b/static/js/ReaderPanel.jsx index 6280d00279..4161704532 100644 --- a/static/js/ReaderPanel.jsx +++ b/static/js/ReaderPanel.jsx @@ -1384,10 +1384,12 @@ class ReaderControls extends Component { <DisplaySettingsButton onClick={this.props.openDisplaySettings} /> </div>); const openTranslations = this.props.openConnectionsPanel.bind(null, [this.props.currentRef], null, {"connectionsMode": "Translations"}); + const transCallToActionApplies = () => Sefaria.transCallToActionApplies(this.props.currentBook(), this.props.settings.language); let banner = (hideHeader || connectionsHeader) ? null : ( <TextColumnBannerChooser setTranslationLanguagePreference={this.props.setTranslationLanguagePreference} openTranslations={openTranslations} + transCallToActionApplies={transCallToActionApplies} /> ); const classes = classNames({ diff --git a/static/js/TextColumnBanner.jsx b/static/js/TextColumnBanner.jsx index 097ed0d710..f93b5e0df1 100644 --- a/static/js/TextColumnBanner.jsx +++ b/static/js/TextColumnBanner.jsx @@ -8,12 +8,15 @@ import { const cookie = Sefaria._inBrowser ? $.cookie : Sefaria.util.cookie; const { translation_language_preference_suggestion } = Sefaria; -export const TextColumnBannerChooser = ({ setTranslationLanguagePreference, openTranslations }) => { +export const TextColumnBannerChooser = ({ setTranslationLanguagePreference, openTranslations, transCallToActionApplies }) => { const [bannerAccepted, setBannerAccepted] = useState(false); const shouldTransPrefBannerRender = () => { // we haven't suggested yet and we have a suggestion return !cookie("translation_language_preference_suggested") && translation_language_preference_suggestion }; + const shouldTransCallToActionRender = () => { + return transCallToActionApplies() && !cookie("translation_call_to_action_shown"); // && textMode in (bilingual, english) && category in (Tanakh, Mishnah, Bavli) + } if (shouldTransPrefBannerRender()) { return (<TransLangPrefBanner setAccepted={setBannerAccepted} @@ -22,7 +25,10 @@ export const TextColumnBannerChooser = ({ setTranslationLanguagePreference, open } else if (bannerAccepted) { return <TransLangPrefAcceptedBanner />; } - return <TransCallToActionBanner openTranslations={openTranslations} />; + if (shouldTransCallToActionRender()) { + return <TransCallToActionBanner openTranslations={openTranslations} />; + } + return null; }; diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index 9698478df8..6663c65909 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -1046,6 +1046,15 @@ Sefaria = extend(Sefaria, { } return ref ? this.getRefFromCache(ref) : null; }, + transCallToActionApplies: (book, textLanguage) => { + /** + * Should we display the translaiton call-to-action banner? + * Return `true` if `book`s corpus is Tanakh, Mishnah or Bavli AND textLanguage isn't Hebrew + */ + const applicableCorpora = ["Tanakh", "Mishnah", "Bavli"]; + const currCorpus = Sefaria.index(book)?.corpus; + return textLanguage !== "hebrew" && applicableCorpora.indexOf(currCorpus) !== -1; + }, _lookups: {}, // getName w/ refOnly true should work as a replacement for parseRef - it uses a callback rather than return value. Besides that - same data. From 97e3aac79c5fc92da7eb20f16e177e6f2050c2a4 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 13 Sep 2023 15:05:21 +0300 Subject: [PATCH 241/756] refactor: use `children` for TextColumnBanner --- static/js/TextColumnBanner.jsx | 55 +++++++++++++++------------------- 1 file changed, 24 insertions(+), 31 deletions(-) diff --git a/static/js/TextColumnBanner.jsx b/static/js/TextColumnBanner.jsx index f93b5e0df1..bdfe5da061 100644 --- a/static/js/TextColumnBanner.jsx +++ b/static/js/TextColumnBanner.jsx @@ -34,14 +34,13 @@ export const TextColumnBannerChooser = ({ setTranslationLanguagePreference, open const TransLangPrefAcceptedBanner = () => { const lang = Sefaria.translateISOLanguageCode(translation_language_preference_suggestion); - const textElement = ( - <InterfaceText> - <EnglishText> Thanks! We'll show you {lang} translations first when we have them. </EnglishText> - <HebrewText>תודה! כשנוכל, נציג לכם תרגומים בשפה ה<span className="bold">{Sefaria._(lang)}</span> כאשר אלו יהיו זמינים. </HebrewText> - </InterfaceText> - ); return ( - <TextColumnBanner textElement={textElement} /> + <TextColumnBanner> + <InterfaceText> + <EnglishText> Thanks! We'll show you {lang} translations first when we have them. </EnglishText> + <HebrewText>תודה! כשנוכל, נציג לכם תרגומים בשפה ה<span className="bold">{Sefaria._(lang)}</span> כאשר אלו יהיו זמינים. </HebrewText> + </InterfaceText> + </TextColumnBanner> ); } @@ -56,38 +55,32 @@ const TransLangPrefBanner = ({ setAccepted, setTranslationLanguagePreference }) setTranslationLanguagePreference(translation_language_preference_suggestion); } const lang = Sefaria.translateISOLanguageCode(translation_language_preference_suggestion); - const textElement = ( - <InterfaceText> - <EnglishText> Prefer to see <span className="bold"> {lang} </span> translations when available? </EnglishText> - <HebrewText>האם תעדיפו לראות תרגומים בשפה ה<span className="bold">{Sefaria._(lang)}</span> כאשר הם זמינים?</HebrewText> - </InterfaceText> - ); const buttons = [{text: "Yes", onClick: accept}, {text: "No", onClick: reject, sideEffect: "close" }]; return ( - <TextColumnBanner textElement={textElement} buttons={buttons} onClose={reject}/> + <TextColumnBanner buttons={buttons} onClose={reject}> + <InterfaceText> + <EnglishText> Prefer to see <span className="bold"> {lang} </span> translations when available? </EnglishText> + <HebrewText>האם תעדיפו לראות תרגומים בשפה ה<span className="bold">{Sefaria._(lang)}</span> כאשר הם זמינים?</HebrewText> + </InterfaceText> + </TextColumnBanner> ); } const TransCallToActionBanner = ({ openTranslations }) => { - const textElement = ( - <InterfaceText> - <EnglishText> Want to <span className="bold">change</span> the translation?</EnglishText> - <HebrewText> Want to <span className="bold">change</span> the translation?</HebrewText> - </InterfaceText> - ); - const buttons = [ - { - text: "Go to translations", - onClick: () => { - openTranslations(); - // setConnectionsMode("Translations"); - } + const buttons = [{ + text: "Go to translations", + onClick: () => { openTranslations(); } }]; const reject = () => {}; return ( - <TextColumnBanner textElement={textElement} buttons={buttons} onClose={reject} /> + <TextColumnBanner buttons={buttons} onClose={reject}> + <InterfaceText> + <EnglishText> Want to <span className="bold">change</span> the translation?</EnglishText> + <HebrewText> Want to <span className="bold">change</span> the translation?</HebrewText> + </InterfaceText> + </TextColumnBanner> ); }; @@ -95,13 +88,13 @@ const TransCallToActionBanner = ({ openTranslations }) => { /** * Banner which appears right above text column and informs a user of an action they can take - * @param textElement: React element to display the call-to-action text. + * @param children: React element to display the call-to-action text. * @param buttons: List of objects. Each object should have keys "text" and "onClick". Can optionally have key "sideEffect" whose value can be "close" if the button should close the banner. * @param onClose: Optional callback that gets called when the banner is closed. * @returns {JSX.Element|null} * @constructor */ -const TextColumnBanner = ({ textElement, buttons, onClose }) => { +const TextColumnBanner = ({ children, buttons, onClose }) => { const [closed, setClosed] = useState(false); const closeBanner = () => { setClosed(true); @@ -112,7 +105,7 @@ const TextColumnBanner = ({ textElement, buttons, onClose }) => { <div className="readerControls transLangPrefSuggBann"> <div className="readerControlsInner transLangPrefSuggBannInner sans-serif"> <div className="transLangPrefCentered"> - { textElement } + { children } <div className="yesNoGroup"> { buttons?.map(button => <TextColumnBannerButton key={button.text} button={button} setBannerClosed={setClosed}/>) } </div> From 5671a5d871e216645d1ebd03d8585da3bd7fc717 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 13 Sep 2023 15:49:54 +0300 Subject: [PATCH 242/756] feat: add header to translations box --- static/css/s2.css | 19 +++++++++++++++++ static/js/TranslationsBox.jsx | 40 +++++++++++++++++++++++++---------- 2 files changed, 48 insertions(+), 11 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index 25773f7575..be75c8e040 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -8341,6 +8341,25 @@ h3.aboutSheetHeader { border-width: 1px; } +.translationsHeader { + margin-bottom: 35px; +} + +.translationsHeader h3 { + text-transform: none; + color: var(--dark-grey); + font-size: var(--sans-serif-h3-font-size); + --english-font: var(--english-sans-serif-font-family); + --hebrew-font: var(--hebrew-sans-serif-font-family); + margin-bottom: 12px; + margin-top: 0; +} + +.translationsHeader .translationsDesc { + font-size: var(--sans-serif-body-font-size); + color: var(--dark-grey); +} + .aboutSheetPanel hr { height: 0px; border: 1px solid var(--light-grey); diff --git a/static/js/TranslationsBox.jsx b/static/js/TranslationsBox.jsx index 55229d64e4..cef0a54ede 100644 --- a/static/js/TranslationsBox.jsx +++ b/static/js/TranslationsBox.jsx @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import Sefaria from './sefaria/sefaria'; import {VersionsBlocksList} from './VersionBlock'; import Component from 'react-class'; -import {LoadingMessage} from "./Misc"; +import {EnglishText, HebrewText, InterfaceText, LoadingMessage} from "./Misc"; import {RecentFilterSet} from "./ConnectionFilters"; import TextRange from "./TextRange"; import {AddConnectionToSheetButton, ConnectionButtons, OpenConnectionTabButton} from "./TextList"; @@ -78,16 +78,19 @@ class TranslationsBox extends Component { ); } return ( - <VersionsBlocksList - versionsByLanguages={this.state.versionLangMap} - currObjectVersions={this.props.currObjectVersions} - sortPrioritizeLanugage={"en"} - currentRef={this.props.srefs[0]} - openVersionInReader={this.props.openVersionInReader} - openVersionInSidebar={this.openVersionInSidebar} - viewExtendedNotes={this.props.viewExtendedNotes} - inTranslationBox={true} - /> + <> + <TranslationsHeader /> + <VersionsBlocksList + versionsByLanguages={this.state.versionLangMap} + currObjectVersions={this.props.currObjectVersions} + sortPrioritizeLanugage={"en"} + currentRef={this.props.srefs[0]} + openVersionInReader={this.props.openVersionInReader} + openVersionInSidebar={this.openVersionInSidebar} + viewExtendedNotes={this.props.viewExtendedNotes} + inTranslationBox={true} + /> + </> ); } } @@ -109,6 +112,21 @@ TranslationsBox.propTypes = { }; +const TranslationsHeader = () => ( + <div className="translationsHeader"> + <h3> + <InterfaceText>Translations</InterfaceText> + </h3> + <div className="translationsDesc sans-serif"> + <InterfaceText> + <EnglishText>Sefaria acquires translations to enrich your learning experience. Preview or choose a different translation below. Learn more…</EnglishText> + <HebrewText>TO DO</HebrewText> + </InterfaceText> + </div> + </div> +); + + class VersionsTextList extends Component { constructor(props) { super(props); From 744b404c3de837b0fb81085cf5839603adce85f8 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 13 Sep 2023 15:57:00 +0300 Subject: [PATCH 243/756] feat: remember if call to action is dismissed --- static/js/TextColumnBanner.jsx | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/static/js/TextColumnBanner.jsx b/static/js/TextColumnBanner.jsx index bdfe5da061..b553c0901f 100644 --- a/static/js/TextColumnBanner.jsx +++ b/static/js/TextColumnBanner.jsx @@ -71,11 +71,14 @@ const TransLangPrefBanner = ({ setAccepted, setTranslationLanguagePreference }) const TransCallToActionBanner = ({ openTranslations }) => { const buttons = [{ text: "Go to translations", - onClick: () => { openTranslations(); } + onClick: () => { openTranslations(); }, + sideEffect: "close", }]; - const reject = () => {}; + const onClose = () => { + cookie("translation_call_to_action_shown", JSON.stringify(1), {path: "/"}); + }; return ( - <TextColumnBanner buttons={buttons} onClose={reject}> + <TextColumnBanner buttons={buttons} onClose={onClose}> <InterfaceText> <EnglishText> Want to <span className="bold">change</span> the translation?</EnglishText> <HebrewText> Want to <span className="bold">change</span> the translation?</HebrewText> @@ -107,7 +110,7 @@ const TextColumnBanner = ({ children, buttons, onClose }) => { <div className="transLangPrefCentered"> { children } <div className="yesNoGroup"> - { buttons?.map(button => <TextColumnBannerButton key={button.text} button={button} setBannerClosed={setClosed}/>) } + { buttons?.map(button => <TextColumnBannerButton key={button.text} button={button} closeBanner={closeBanner}/>) } </div> </div> <CloseButton onClick={closeBanner} /> @@ -116,10 +119,10 @@ const TextColumnBanner = ({ children, buttons, onClose }) => { ); } -const TextColumnBannerButton = ({ button, setBannerClosed }) => { +const TextColumnBannerButton = ({ button, closeBanner }) => { const onClick = () => { button.onClick(); - if (button.sideEffect === "close") { setBannerClosed(true); } + if (button.sideEffect === "close") { closeBanner(true); } } return ( <a className="yesNoButton" onClick={onClick}> From 0c1d5f70187effccb220c247c851a506bd7a498d Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Thu, 14 Sep 2023 00:35:10 -0400 Subject: [PATCH 244/756] fix(strapi-cms): Fix bug where the entry for the current banner or modal in local storage to mark interaction is cleared after starting a new session or visiting a page in header-only mode --- static/js/context.js | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/static/js/context.js b/static/js/context.js index f405e06ba5..8c9f1f03f4 100644 --- a/static/js/context.js +++ b/static/js/context.js @@ -194,12 +194,15 @@ function StrapiDataProvider({ children }) { let modals = result.data?.modals?.data; let banners = result.data?.banners?.data; - let removeKeysFromLocalStorageWithPrefix = (prefix) => { + let removeContentKeysFromLocalStorage = ({ prefix = "", except = "" } = {}) => { let keysToRemove = []; // Removing keys while iterating affects the length of localStorage for (let i = 0; i < localStorage.length; i++) { let key = localStorage.key(i); - if (key.startsWith(prefix)) { + if ( + key.startsWith(prefix) && + (except === "" || key !== prefix + except) + ) { keysToRemove.push(key); } } @@ -217,8 +220,12 @@ function StrapiDataProvider({ children }) { currentDate <= new Date(modal.attributes.modalEndDate) ); if (modal) { - // Remove any other previous modals since there is a new modal to replace it - removeKeysFromLocalStorageWithPrefix("modal_"); + // Remove any other previous modals since there is potentially new modal to replace it + // However, do not remove the existing modal if the eligible one found is the same as the current one + removeContentKeysFromLocalStorage({ + prefix: "modal_", + except: modal.attributes.internalModalName, + }); // Check if there is a Hebrew translation for the modal if (modal.attributes.localizations?.data?.length) { @@ -262,8 +269,12 @@ function StrapiDataProvider({ children }) { ); if (banner) { - // Remove any other previous banners since there is a new banner to replace it - removeKeysFromLocalStorageWithPrefix("banner_"); + // Remove any other previous banner since there is potentially new modal to replace it + // However, do not remove the existing banner if the eligible one found is the same as the current one + removeContentKeysFromLocalStorage({ + prefix: "banner_", + except: banner.attributes.internalBannerName, + }); // Check if there is a Hebrew translation if (banner.attributes.localizations?.data?.length) { From 3830f834a9eca5fc04b79c50bfaf5b57b21b4104 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Thu, 14 Sep 2023 12:32:02 +0300 Subject: [PATCH 245/756] fix(TextRange): add underscore. --- sefaria/model/text.py | 50 +++++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index dac6b9a5df..a3f30edf07 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -1741,7 +1741,7 @@ def __call__(cls, *args, **kwargs): class TextRange: - def __init__(self, oref, lang, vtitle): + def __init__(self, oref, lang, vtitle, merge_versions=False): if isinstance(oref.index_node, JaggedArrayNode) or isinstance(oref.index_node, DictionaryEntryNode): #text cannot be SchemaNode self.oref = oref elif oref.has_default_child(): #use default child: @@ -1750,24 +1750,33 @@ def __init__(self, oref, lang, vtitle): raise InputError("Can not get TextRange at this level, please provide a more precise reference") self.lang = lang self.vtitle = vtitle - self._version = None #todo - what we want to do if the version doesnt exist. for now the getter will cause error + self.merge_versions = merge_versions + self._versions = [] self._text = None + self.sources = None @property - def version(self): - if self._version is None: # todo if there is no version it will run any time - self._version = Version().load({'title': self.oref.index.title, 'actualLanguage': self.lang, 'versionTitle': self.vtitle}, - self.oref.part_projection()) - return self._version - - @version.setter - def version(self, version): #version can be used if version is already in memory - self._validate_version(version) - self._version = version - - def _validate_version(self, version): - if self.lang != version.actualLanguage or self.vtitle != version.versionTitle: - raise InputError("Given version is not matching to given language and versionTitle") + def versions(self): + if self._versions == []: + condition_query = self.oref.condition_query(self.lang) if self.merge_versions else \ + {'title': self.oref.index.title, 'actualLanguage': self.lang, 'versionTitle': self.vtitle} + self._versions = VersionSet(condition_query, proj=self.oref.part_projection()) + return self._versions + + @versions.setter + def versions(self, versions): + self._validate_versions(versions) + self._versions = versions + + def _validate_versions(self, versions): + if not self.merge_versions and len(versions) > 1: + raise InputError("Got many versions instead of one") + for version in versions: + condition = version.title == self.oref.index.title and version.actualLanguage == self.lang + if not self.merge_versions: + condition = condition and version.versionTitle == self.vtitle + if not condition: + raise InputError(f"Given version, {version}, is not matching to title, language or versionTitle") def _trim_text(self, text): """ @@ -1787,10 +1796,15 @@ def _trim_text(self, text): @property def text(self): if self._text is None: - if self.oref.index_node.is_virtual: + if self.merge_versions and len(self.versions) > 1: + merged_text, sources = self.versions.merge(self.oref.index_node, prioritized_vtitle=self.vtitle) + self._text = self._trim_text(merged_text) + if len(sources) > 1: + self.sources = sources + elif self.oref.index_node.is_virtual: self._text = self.oref.index_node.get_text() else: - self._text = self._trim_text(self.version.content_node(self.oref.index_node)) #todo if there is no version it will fail + self._text = self._trim_text(self.versions[0].content_node(self.oref.index_node)) #todo if there is no version it will fail return self._text From 825081625c04816cae3773c16b77de321f922767 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Thu, 14 Sep 2023 14:25:47 +0300 Subject: [PATCH 246/756] feat(text api): add merge option to API. --- api/views.py | 3 ++- sefaria/model/text_manager.py | 16 +++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/api/views.py b/api/views.py index a4cfe8d294..241c95e290 100644 --- a/api/views.py +++ b/api/views.py @@ -46,7 +46,8 @@ def get(self, request, *args, **kwargs): if not versions_params: versions_params = ['base'] versions_params = [self.split_piped_params(param_str) for param_str in versions_params] - text_manager = TextManager(self.oref, versions_params) + fill_in_missing_segments = request.GET.get('fill_in_missing_segments', False) + text_manager = TextManager(self.oref, versions_params, fill_in_missing_segments) data = text_manager.get_versions_for_query() data = self._handle_warnings(data) return jsonResponse(data) diff --git a/sefaria/model/text_manager.py b/sefaria/model/text_manager.py index 6beb63c8af..c48c245c19 100644 --- a/sefaria/model/text_manager.py +++ b/sefaria/model/text_manager.py @@ -1,3 +1,5 @@ +import copy + import django django.setup() from sefaria.model import * @@ -10,9 +12,10 @@ class TextManager: SOURCE = 'source' TRANSLATION = 'translation' - def __init__(self, oref: Ref, versions_params: List[List[str]]): + def __init__(self, oref: Ref, versions_params: List[List[str]], fill_in_missing_segments=True): self.versions_params = versions_params self.oref = oref + self.fill_in_missing_segments = fill_in_missing_segments self.handled_version_params = [] self.all_versions = self.oref.versionset() @@ -31,8 +34,15 @@ def _append_version(self, version): for attr in ['chapter', 'title', 'language']: fields.remove(attr) version_details = {f: getattr(version, f, "") for f in fields} - text_range = TextRange(self.oref, version.actualLanguage, version.versionTitle) - text_range.version = version + text_range = TextRange(self.oref, version.actualLanguage, version.versionTitle, True) + + if self.fill_in_missing_segments: + # we need a new VersionSet of only the relevant versions for merging. copy should be better than calling for mongo + relevant_versions = copy.copy(self.all_versions) + relevant_versions.remove(lambda v: v.actualLanguage != version.actualLanguage) + else: + relevant_versions = [version] + text_range.versions = relevant_versions version_details['text'] = text_range.text if self.oref.is_book_level(): first_section_ref = version.first_section_ref() or version.get_index().nodes.first_leaf().first_section_ref() From 8468bc6eabffbc90cb6fc747ee152817e597ffe7 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Thu, 14 Sep 2023 14:45:10 +0300 Subject: [PATCH 247/756] test(text api): test the fill_in_missing_segments parameter. --- api/tests.py | 18 ++++++++++++++++++ sefaria/model/text_manager.py | 5 +++++ 2 files changed, 23 insertions(+) diff --git a/api/tests.py b/api/tests.py index 0bf2167dd9..84734d85d6 100644 --- a/api/tests.py +++ b/api/tests.py @@ -150,3 +150,21 @@ def test_api_get_text_no_version(self): self.assertEqual(data['warnings'][0]['he|Kishkoosh']['warning_code'], APIWarningCode.APINoVersion.value) self.assertEqual(data['warnings'][0]['he|Kishkoosh']['message'], 'We do not have version named Kishkoosh with language code he for The Book of Maccabees I 1') + + def test_fill_in_missing_segments(self): + vtitle = "Maimonides' Mishneh Torah, edited by Philip Birnbaum, New York, 1967" + response = c.get(f"/api/v3/texts/Mishneh_Torah,_Sabbath_1?version=en|{vtitle}&fill_in_missing_segments=true") + self.assertEqual(200, response.status_code) + data = json.loads(response.content) + self.assertTrue(len(data['versions'][0]['text']) > 2) + self.assertTrue(data['versions'][0].get('sources')) + self.assertEqual(data['versions'][0]['sources'][0], vtitle) + self.assertNotEqual(data['versions'][0]['sources'][2], vtitle) + + def test_without_fill_in_missing_segments(self): + vtitle = "Maimonides' Mishneh Torah, edited by Philip Birnbaum, New York, 1967" + response = c.get(f"/api/v3/texts/Mishneh_Torah,_Sabbath_1?version=en|{vtitle}") + self.assertEqual(200, response.status_code) + data = json.loads(response.content) + self.assertEqual(len(data['versions'][0]['text']), 2) + self.assertFalse(data['versions'][0].get('sources')) diff --git a/sefaria/model/text_manager.py b/sefaria/model/text_manager.py index c48c245c19..37641d0229 100644 --- a/sefaria/model/text_manager.py +++ b/sefaria/model/text_manager.py @@ -44,6 +44,11 @@ def _append_version(self, version): relevant_versions = [version] text_range.versions = relevant_versions version_details['text'] = text_range.text + + sources = getattr(text_range, 'sources', None) + if sources is not None: + version_details['sources'] = sources + if self.oref.is_book_level(): first_section_ref = version.first_section_ref() or version.get_index().nodes.first_leaf().first_section_ref() version_details['firstSectionRef'] = first_section_ref.normal() From 8af597e4e5b68132d59f87b81864aaca0de6da84 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Thu, 14 Sep 2023 13:43:57 -0400 Subject: [PATCH 248/756] fix(strapi-cms): Fix typo. This left the buttonURL attribute undefined and there was no URL for the button on Hebrew Sidebar Ads --- static/js/Promotions.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/Promotions.jsx b/static/js/Promotions.jsx index b652a98863..f845810b2a 100644 --- a/static/js/Promotions.jsx +++ b/static/js/Promotions.jsx @@ -59,7 +59,7 @@ const Promotions = () => { title: title, bodyText: bodyText, buttonText: buttonText, - buttonUrl: buttonURL, + buttonURL: buttonURL, buttonIcon: sidebarAd.buttonIcon, buttonLocation: sidebarAd.buttonAboveOrBelow, hasBlueBackground: sidebarAd.hasBlueBackground, From e6fc258441c7dab922e79fb14df332d70bbdfad3 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Mon, 18 Sep 2023 13:07:09 +0300 Subject: [PATCH 249/756] feat: add Hebrew strings --- static/js/TextColumnBanner.jsx | 2 +- static/js/TranslationsBox.jsx | 2 +- static/js/sefaria/strings.js | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/static/js/TextColumnBanner.jsx b/static/js/TextColumnBanner.jsx index b553c0901f..4f825f673e 100644 --- a/static/js/TextColumnBanner.jsx +++ b/static/js/TextColumnBanner.jsx @@ -81,7 +81,7 @@ const TransCallToActionBanner = ({ openTranslations }) => { <TextColumnBanner buttons={buttons} onClose={onClose}> <InterfaceText> <EnglishText> Want to <span className="bold">change</span> the translation?</EnglishText> - <HebrewText> Want to <span className="bold">change</span> the translation?</HebrewText> + <HebrewText> מעוניינים בתרגום אחר?</HebrewText> </InterfaceText> </TextColumnBanner> ); diff --git a/static/js/TranslationsBox.jsx b/static/js/TranslationsBox.jsx index cef0a54ede..22f24c7ccb 100644 --- a/static/js/TranslationsBox.jsx +++ b/static/js/TranslationsBox.jsx @@ -120,7 +120,7 @@ const TranslationsHeader = () => ( <div className="translationsDesc sans-serif"> <InterfaceText> <EnglishText>Sefaria acquires translations to enrich your learning experience. Preview or choose a different translation below. Learn more…</EnglishText> - <HebrewText>TO DO</HebrewText> + <HebrewText>ספריא עושה מאמצים להוסיף תרגומים שונים לספרים כדי להעשיר את חווית הלמידה שלכם. כאן ניתן להחליף לתרגום אחר או לראות תצוגה מקדימה שלו לצד הטקסט הנוכחי. למידע נוסף…</HebrewText> </InterfaceText> </div> </div> diff --git a/static/js/sefaria/strings.js b/static/js/sefaria/strings.js index fb9340b414..c1998554e2 100644 --- a/static/js/sefaria/strings.js +++ b/static/js/sefaria/strings.js @@ -401,6 +401,7 @@ const Strings = { "Describe the issue..." : "טקסט המשוב", "Report an issue with the text" : "דיווח על בעיה בטקסט", "Request translation" : "בקשה לתרגום", + "Go to translations": "לרשימת התרגומים", "Report a bug" : "דיווח על תקלה באתר", "Get help" : "עזרה", "Help": "עזרה", From c7104c913fa03016725760848888f72025e3361b Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Mon, 18 Sep 2023 13:14:20 +0300 Subject: [PATCH 250/756] feat: add Learn more link --- static/js/TranslationsBox.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/static/js/TranslationsBox.jsx b/static/js/TranslationsBox.jsx index 22f24c7ccb..efd702fdb3 100644 --- a/static/js/TranslationsBox.jsx +++ b/static/js/TranslationsBox.jsx @@ -119,8 +119,8 @@ const TranslationsHeader = () => ( </h3> <div className="translationsDesc sans-serif"> <InterfaceText> - <EnglishText>Sefaria acquires translations to enrich your learning experience. Preview or choose a different translation below. Learn more…</EnglishText> - <HebrewText>ספריא עושה מאמצים להוסיף תרגומים שונים לספרים כדי להעשיר את חווית הלמידה שלכם. כאן ניתן להחליף לתרגום אחר או לראות תצוגה מקדימה שלו לצד הטקסט הנוכחי. למידע נוסף…</HebrewText> + <EnglishText>Sefaria acquires translations to enrich your learning experience. Preview or choose a different translation below. <a href="https://www.sefaria.org/sheets/511573">Learn more…</a></EnglishText> + <HebrewText>ספריא עושה מאמצים להוסיף תרגומים שונים לספרים כדי להעשיר את חווית הלמידה שלכם. כאן ניתן להחליף לתרגום אחר או לראות תצוגה מקדימה שלו לצד הטקסט הנוכחי. <a href="https://www.sefaria.org/sheets/511573">למידע נוסף…</a></HebrewText> </InterfaceText> </div> </div> From 94f201395e8e554a9d11fee58eaad18018cfaf4d Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Mon, 18 Sep 2023 15:40:33 +0300 Subject: [PATCH 251/756] chore: change ellipsis to arrow --- static/js/TranslationsBox.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/static/js/TranslationsBox.jsx b/static/js/TranslationsBox.jsx index efd702fdb3..91d181a865 100644 --- a/static/js/TranslationsBox.jsx +++ b/static/js/TranslationsBox.jsx @@ -119,8 +119,8 @@ const TranslationsHeader = () => ( </h3> <div className="translationsDesc sans-serif"> <InterfaceText> - <EnglishText>Sefaria acquires translations to enrich your learning experience. Preview or choose a different translation below. <a href="https://www.sefaria.org/sheets/511573">Learn more…</a></EnglishText> - <HebrewText>ספריא עושה מאמצים להוסיף תרגומים שונים לספרים כדי להעשיר את חווית הלמידה שלכם. כאן ניתן להחליף לתרגום אחר או לראות תצוגה מקדימה שלו לצד הטקסט הנוכחי. <a href="https://www.sefaria.org/sheets/511573">למידע נוסף…</a></HebrewText> + <EnglishText>Sefaria acquires translations to enrich your learning experience. Preview or choose a different translation below. <a href="https://www.sefaria.org/sheets/511573" target="_blank">Learn more ›</a></EnglishText> + <HebrewText>ספריא עושה מאמצים להוסיף תרגומים שונים לספרים כדי להעשיר את חווית הלמידה שלכם. כאן ניתן להחליף לתרגום אחר או לראות תצוגה מקדימה שלו לצד הטקסט הנוכחי. <a href="https://www.sefaria.org/sheets/511573" target="_blank">למידע נוסף ›</a></HebrewText> </InterfaceText> </div> </div> From 62437b423399ef748133d8616358a9220fa9c0a5 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Mon, 18 Sep 2023 15:47:58 +0300 Subject: [PATCH 252/756] refactor: refactor learn more link to match structure of learn more link on home page. --- static/css/s2.css | 3 ++- static/js/TranslationsBox.jsx | 10 ++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index be75c8e040..ce7a0d57db 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -1782,7 +1782,8 @@ div.interfaceLinks-row a { color: inherit; cursor: pointer; } -.navSidebar a.inTextLink { +.navSidebar a.inTextLink, +.translationsDesc a.inTextLink { color: inherit; text-decoration: underline; margin-inline-start: 5px; diff --git a/static/js/TranslationsBox.jsx b/static/js/TranslationsBox.jsx index 91d181a865..c5e1a78bf6 100644 --- a/static/js/TranslationsBox.jsx +++ b/static/js/TranslationsBox.jsx @@ -119,9 +119,15 @@ const TranslationsHeader = () => ( </h3> <div className="translationsDesc sans-serif"> <InterfaceText> - <EnglishText>Sefaria acquires translations to enrich your learning experience. Preview or choose a different translation below. <a href="https://www.sefaria.org/sheets/511573" target="_blank">Learn more ›</a></EnglishText> - <HebrewText>ספריא עושה מאמצים להוסיף תרגומים שונים לספרים כדי להעשיר את חווית הלמידה שלכם. כאן ניתן להחליף לתרגום אחר או לראות תצוגה מקדימה שלו לצד הטקסט הנוכחי. <a href="https://www.sefaria.org/sheets/511573" target="_blank">למידע נוסף ›</a></HebrewText> + <EnglishText>Sefaria acquires translations to enrich your learning experience. Preview or choose a different translation below.</EnglishText> + <HebrewText>ספריא עושה מאמצים להוסיף תרגומים שונים לספרים כדי להעשיר את חווית הלמידה שלכם. כאן ניתן להחליף לתרגום אחר או לראות תצוגה מקדימה שלו לצד הטקסט הנוכחי.</HebrewText> </InterfaceText> + <a href="/sheets/511573" target="_blank" className="inTextLink"> + <InterfaceText> + <EnglishText>Learn more ›</EnglishText> + <HebrewText>למידע נוסף ›</HebrewText> + </InterfaceText> + </a> </div> </div> ); From 50ad0e8b682b36bad1f741a3801f3f89c10e4744 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Mon, 18 Sep 2023 15:50:36 +0300 Subject: [PATCH 253/756] refactor: move openTranslations out of the render function --- static/js/ReaderPanel.jsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/static/js/ReaderPanel.jsx b/static/js/ReaderPanel.jsx index 4161704532..2538277818 100644 --- a/static/js/ReaderPanel.jsx +++ b/static/js/ReaderPanel.jsx @@ -1254,6 +1254,9 @@ class ReaderControls extends Component { } }); } + openTranslations() { + this.props.openConnectionsPanel([this.props.currentRef], null, {"connectionsMode": "Translations"}); + } componentDidMount() { const title = this.props.currentRef; if (title) { @@ -1383,12 +1386,11 @@ class ReaderControls extends Component { /> <DisplaySettingsButton onClick={this.props.openDisplaySettings} /> </div>); - const openTranslations = this.props.openConnectionsPanel.bind(null, [this.props.currentRef], null, {"connectionsMode": "Translations"}); const transCallToActionApplies = () => Sefaria.transCallToActionApplies(this.props.currentBook(), this.props.settings.language); let banner = (hideHeader || connectionsHeader) ? null : ( <TextColumnBannerChooser setTranslationLanguagePreference={this.props.setTranslationLanguagePreference} - openTranslations={openTranslations} + openTranslations={this.openTranslations} transCallToActionApplies={transCallToActionApplies} /> ); From c8bca122345ce16526b2806606a900d16eace57f Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Mon, 18 Sep 2023 21:05:42 +0300 Subject: [PATCH 254/756] refactor: rename banner to OpenTransBanner which is more specific --- static/js/ReaderPanel.jsx | 4 ++-- static/js/TextColumnBanner.jsx | 20 +++++++++++++------- static/js/sefaria/sefaria.js | 4 ++-- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/static/js/ReaderPanel.jsx b/static/js/ReaderPanel.jsx index 2538277818..18cb939cce 100644 --- a/static/js/ReaderPanel.jsx +++ b/static/js/ReaderPanel.jsx @@ -1386,12 +1386,12 @@ class ReaderControls extends Component { /> <DisplaySettingsButton onClick={this.props.openDisplaySettings} /> </div>); - const transCallToActionApplies = () => Sefaria.transCallToActionApplies(this.props.currentBook(), this.props.settings.language); + const openTransBannerApplies = () => Sefaria.openTransBannerApplies(this.props.currentBook(), this.props.settings.language); let banner = (hideHeader || connectionsHeader) ? null : ( <TextColumnBannerChooser setTranslationLanguagePreference={this.props.setTranslationLanguagePreference} openTranslations={this.openTranslations} - transCallToActionApplies={transCallToActionApplies} + openTransBannerApplies={openTransBannerApplies} /> ); const classes = classNames({ diff --git a/static/js/TextColumnBanner.jsx b/static/js/TextColumnBanner.jsx index 4f825f673e..1f97b14e81 100644 --- a/static/js/TextColumnBanner.jsx +++ b/static/js/TextColumnBanner.jsx @@ -8,14 +8,14 @@ import { const cookie = Sefaria._inBrowser ? $.cookie : Sefaria.util.cookie; const { translation_language_preference_suggestion } = Sefaria; -export const TextColumnBannerChooser = ({ setTranslationLanguagePreference, openTranslations, transCallToActionApplies }) => { +export const TextColumnBannerChooser = ({ setTranslationLanguagePreference, openTranslations, openTransBannerApplies }) => { const [bannerAccepted, setBannerAccepted] = useState(false); const shouldTransPrefBannerRender = () => { // we haven't suggested yet and we have a suggestion return !cookie("translation_language_preference_suggested") && translation_language_preference_suggestion }; - const shouldTransCallToActionRender = () => { - return transCallToActionApplies() && !cookie("translation_call_to_action_shown"); // && textMode in (bilingual, english) && category in (Tanakh, Mishnah, Bavli) + const shouldOpenTransBannerRender = () => { + return openTransBannerApplies() && !cookie("open_trans_banner_shown"); // && textMode in (bilingual, english) && category in (Tanakh, Mishnah, Bavli) } if (shouldTransPrefBannerRender()) { return (<TransLangPrefBanner @@ -25,8 +25,8 @@ export const TextColumnBannerChooser = ({ setTranslationLanguagePreference, open } else if (bannerAccepted) { return <TransLangPrefAcceptedBanner />; } - if (shouldTransCallToActionRender()) { - return <TransCallToActionBanner openTranslations={openTranslations} />; + if (shouldOpenTransBannerRender()) { + return <OpenTransBanner openTranslations={openTranslations} />; } return null; }; @@ -68,14 +68,20 @@ const TransLangPrefBanner = ({ setAccepted, setTranslationLanguagePreference }) } -const TransCallToActionBanner = ({ openTranslations }) => { +/** + * + * @param openTranslations: function with no parameters that opens translations in the resources panel + * @returns {JSX.Element} + * @constructor + */ +const OpenTransBanner = ({ openTranslations }) => { const buttons = [{ text: "Go to translations", onClick: () => { openTranslations(); }, sideEffect: "close", }]; const onClose = () => { - cookie("translation_call_to_action_shown", JSON.stringify(1), {path: "/"}); + cookie("open_trans_banner_shown", JSON.stringify(1), {path: "/"}); }; return ( <TextColumnBanner buttons={buttons} onClose={onClose}> diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index 6663c65909..fed0f28110 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -1046,9 +1046,9 @@ Sefaria = extend(Sefaria, { } return ref ? this.getRefFromCache(ref) : null; }, - transCallToActionApplies: (book, textLanguage) => { + openTransBannerApplies: (book, textLanguage) => { /** - * Should we display the translaiton call-to-action banner? + * Should we display OpenTransBanner? * Return `true` if `book`s corpus is Tanakh, Mishnah or Bavli AND textLanguage isn't Hebrew */ const applicableCorpora = ["Tanakh", "Mishnah", "Bavli"]; From 971c3de2e0a54c39bd728cbd4a870b515ff125ca Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Mon, 18 Sep 2023 21:16:11 +0300 Subject: [PATCH 255/756] refactor: try to encapsulate logic for rendering TransLangPrefBanner a bit more --- static/js/TextColumnBanner.jsx | 36 ++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/static/js/TextColumnBanner.jsx b/static/js/TextColumnBanner.jsx index 1f97b14e81..0449b84cf1 100644 --- a/static/js/TextColumnBanner.jsx +++ b/static/js/TextColumnBanner.jsx @@ -9,29 +9,45 @@ const cookie = Sefaria._inBrowser ? $.cookie : Sefaria.util.cookie; const { translation_language_preference_suggestion } = Sefaria; export const TextColumnBannerChooser = ({ setTranslationLanguagePreference, openTranslations, openTransBannerApplies }) => { - const [bannerAccepted, setBannerAccepted] = useState(false); + const [transLangPrefAccepted, setTransLangPrefAccepted] = useState(false); const shouldTransPrefBannerRender = () => { // we haven't suggested yet and we have a suggestion - return !cookie("translation_language_preference_suggested") && translation_language_preference_suggestion + return transLangPrefAccepted || (!cookie("translation_language_preference_suggested") && translation_language_preference_suggestion); }; const shouldOpenTransBannerRender = () => { return openTransBannerApplies() && !cookie("open_trans_banner_shown"); // && textMode in (bilingual, english) && category in (Tanakh, Mishnah, Bavli) } + if (shouldTransPrefBannerRender()) { - return (<TransLangPrefBanner - setAccepted={setBannerAccepted} - setTranslationLanguagePreference={setTranslationLanguagePreference} - />); - } else if (bannerAccepted) { - return <TransLangPrefAcceptedBanner />; + return ( + <TransLangPrefBanner + accepted={transLangPrefAccepted} + setAccepted={setTransLangPrefAccepted} + setTranslationLanguagePreference={setTranslationLanguagePreference} + /> + ); } - if (shouldOpenTransBannerRender()) { + else if (shouldOpenTransBannerRender()) { return <OpenTransBanner openTranslations={openTranslations} />; } return null; }; +const TransLangPrefBanner = ({accepted, setAccepted, setTranslationLanguagePreference}) => { + if (accepted) { + return <TransLangPrefAcceptedBanner />; + } + + return ( + <TransLangPrefAskBanner + setAccepted={setAccepted} + setTranslationLanguagePreference={setTranslationLanguagePreference} + /> + ); +} + + const TransLangPrefAcceptedBanner = () => { const lang = Sefaria.translateISOLanguageCode(translation_language_preference_suggestion); return ( @@ -45,7 +61,7 @@ const TransLangPrefAcceptedBanner = () => { } -const TransLangPrefBanner = ({ setAccepted, setTranslationLanguagePreference }) => { +const TransLangPrefAskBanner = ({ setAccepted, setTranslationLanguagePreference }) => { const reject = () => { cookie("translation_language_preference_suggested", JSON.stringify(1), {path: "/"}); Sefaria.editProfileAPI({settings: {translation_language_preference_suggested: true}}); From a7bc876bf219a7728335d11f07c9781c97c096c9 Mon Sep 17 00:00:00 2001 From: yonadavGit <yonadav.le@gmail.com> Date: Mon, 18 Sep 2023 23:41:01 +0300 Subject: [PATCH 256/756] chore: refactor get urls for author indexes to include descriptions in return value --- sefaria/helper/topic.py | 7 +------ sefaria/model/topic.py | 18 ++++++++++++++---- static/js/TopicPage.jsx | 4 +++- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/sefaria/helper/topic.py b/sefaria/helper/topic.py index 36ebef5f48..07ca03e26a 100644 --- a/sefaria/helper/topic.py +++ b/sefaria/helper/topic.py @@ -50,12 +50,7 @@ def get_topic(v2, topic, with_html=True, with_links=True, annotate_links=True, w ref_links = group_links_by_type('refTopic', ref_links, False, False) response['refs'] = ref_links if with_indexes and isinstance(topic_obj, AuthorTopic): - response['indexes'] = [ - { - "text": text_dict, - "url": url - } for (url, text_dict) in topic_obj.get_aggregated_urls_for_authors_indexes() - ] + response['indexes'] = topic_obj.get_aggregated_urls_for_authors_indexes() if getattr(topic_obj, 'isAmbiguous', False): possibility_links = topic_obj.link_set(_class="intraTopic", query_kwargs={"linkType": TopicLinkType.possibility_type}) diff --git a/sefaria/model/topic.py b/sefaria/model/topic.py index 0127bc723b..5a185f9ac2 100644 --- a/sefaria/model/topic.py +++ b/sefaria/model/topic.py @@ -542,21 +542,31 @@ def get_aggregated_urls_for_authors_indexes(self) -> list: from .text import Index index_or_cat_list = self.aggregate_authors_indexes_by_category() - unique_urls = {} # {url: {lang: title}}. This dict arbitrarily chooses one title per URL. + unique_urls = [] for index_or_cat, collective_title_term, base_category in index_or_cat_list: + en_desc = getattr(index_or_cat, 'enShortDesc', None) + he_desc = getattr(index_or_cat, 'heShortDesc', None) if isinstance(index_or_cat, Index): - unique_urls[f'/{index_or_cat.title.replace(" ", "_")}'] = {"en": index_or_cat.get_title('en'), "he": index_or_cat.get_title('he')} + unique_urls.append({"url":f'/{index_or_cat.title.replace(" ", "_")}', + "title": {"en": index_or_cat.get_title('en'), "he": index_or_cat.get_title('he')}, + "description":{"en": en_desc, "he": he_desc} + }) else: if collective_title_term is None: cat_term = Term().load({"name": index_or_cat.sharedTitle}) en_text = cat_term.get_primary_title('en') he_text = cat_term.get_primary_title('he') + else: base_category_term = Term().load({"name": base_category.sharedTitle}) en_text = f'{collective_title_term.get_primary_title("en")} on {base_category_term.get_primary_title("en")}' he_text = f'{collective_title_term.get_primary_title("he")} על {base_category_term.get_primary_title("he")}' - unique_urls[f'/texts/{"/".join(index_or_cat.path)}'] = {"en": en_text, "he": he_text} - return list(unique_urls.items()) + + unique_urls.append({"url": f'/texts/{"/".join(index_or_cat.path)}', + "title": {"en": en_text, "he": he_text}, + "description":{"en": en_desc, "he": he_desc} + }) + return(unique_urls) @staticmethod def is_author(slug): diff --git a/static/js/TopicPage.jsx b/static/js/TopicPage.jsx index dc6293d958..08204a00e2 100644 --- a/static/js/TopicPage.jsx +++ b/static/js/TopicPage.jsx @@ -315,6 +315,7 @@ const TopicHeader = ({ topic, topicData, multiPanel, isCat, setNavTopic, openDis const { en, he } = !!topicData && topicData.primaryTitle ? topicData.primaryTitle : {en: "Loading...", he: "טוען..."}; const isTransliteration = !!topicData ? topicData.primaryTitleIsTransliteration : {en: false, he: false}; const category = !!topicData ? Sefaria.topicTocCategory(topicData.slug) : null; + console.log(topicData.indexes) return ( <div> <div className="navTitle tight"> @@ -351,7 +352,8 @@ const TopicHeader = ({ topic, topicData, multiPanel, isCat, setNavTopic, openDis <div> <div className="sectionTitleText authorIndexTitle"><InterfaceText>Works on Sefaria</InterfaceText></div> <div className="authorIndexList"> - {topicData.indexes.map(({text, url}) => <SimpleLinkedBlock key={url} {...text} url={url} classes="authorIndex" />)} + {/*{topicData.indexes.map(({text, url}) => <SimpleLinkedBlock key={url} {...text} url={url} classes="authorIndex" />)}*/} + {topicData.indexes.map(({url, title, description}) => <SimpleLinkedBlock key={url} {...title} url={url} classes="authorIndex" />)} </div> </div> : null} From 142ec14a35d6fcd9670fe175c7ccc8bf9bb45c7f Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Mon, 18 Sep 2023 22:15:06 -0400 Subject: [PATCH 257/756] feat(helm): Add redirects from the JSON files used for interrupting messages on mobile users to the new Strapi API endpoints --- helm-chart/sefaria-project/templates/configmap/nginx.yaml | 8 ++++++++ helm-chart/sefaria-project/templates/rollout/nginx.yaml | 4 +++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/helm-chart/sefaria-project/templates/configmap/nginx.yaml b/helm-chart/sefaria-project/templates/configmap/nginx.yaml index bda59de65d..0b118d9b24 100644 --- a/helm-chart/sefaria-project/templates/configmap/nginx.yaml +++ b/helm-chart/sefaria-project/templates/configmap/nginx.yaml @@ -152,6 +152,14 @@ data: proxy_pass http://varnish_upstream; } + location /static/mobile/message-en.json { + return 301 ${STRAPI_LOCATION}/api/mobile-message; + } + + location /static/mobile/message-he.json { + return 301 ${STRAPI_LOCATION}/api/mobile-message-he; + } + location /static/ { access_log off; alias /app/static/; diff --git a/helm-chart/sefaria-project/templates/rollout/nginx.yaml b/helm-chart/sefaria-project/templates/rollout/nginx.yaml index 94b1e007ed..3dc1cae434 100644 --- a/helm-chart/sefaria-project/templates/rollout/nginx.yaml +++ b/helm-chart/sefaria-project/templates/rollout/nginx.yaml @@ -54,7 +54,7 @@ spec: imagePullPolicy: Always command: ["bash", "-c"] # https://serverfault.com/questions/577370/how-can-i-use-environment-variables-in-nginx-conf - args: [ "envsubst '${ENV_NAME},${VARNISH_HOST},${SEARCH_HOST},${RELEASE_TAG}{{- if .Values.linker.enabled }},${LINKER_HOST}{{- end }}{{- if .Values.instrumentation.enabled }},${NGINX_VERSION}{{- end }}' < /conf/nginx.template.conf > /nginx.conf && exec nginx -c /nginx.conf -g 'daemon off;'" ] + args: [ "envsubst '${ENV_NAME},${VARNISH_HOST},${SEARCH_HOST},${RELEASE_TAG},${STRAPI_LOCATION}{{- if .Values.linker.enabled }},${LINKER_HOST}{{- end }}{{- if .Values.instrumentation.enabled }},${NGINX_VERSION}{{- end }}' < /conf/nginx.template.conf > /nginx.conf && exec nginx -c /nginx.conf -g 'daemon off;'" ] ports: - containerPort: 80 - containerPort: 443 @@ -96,6 +96,8 @@ spec: value: "varnish-{{ .Values.deployEnv }}-{{ .Release.Revision }}" - name: SEARCH_HOST value: "{{ .Values.nginx.SEARCH_HOST }}" + - name: STRAPI_LOCATION + value: "{{ .Values.localSettings.STRAPI_LOCATION }}" {{- if .Values.linker.enabled }} - name: LINKER_HOST value: "linker-{{ .Values.deployEnv }}-{{ .Release.Revision }}" From 5f85f3184a8dd5578a009197abfe86b953f789d5 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Tue, 19 Sep 2023 12:14:49 +0300 Subject: [PATCH 258/756] fix(TextRange): trim text in copy rather than on the version itself. --- sefaria/model/text.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index a3f30edf07..85221fd61a 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -1783,6 +1783,7 @@ def _trim_text(self, text): part_projection trims only the upper level of the jagged array. this function trims its lower levels and get rid of 1 element arrays wrappings """ #TODO can we get the specific text directly from mongo? + text = copy.deepcopy(text) for s, section in enumerate(self.oref.toSections[1:], 1): #start cut from end, for cutting from the start will change the indexes subtext = reduce(lambda x, _: x[-1], range(s), text) del subtext[section:] From ba03a0239c0e388e1f6a9ffc349c69a048296add Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Tue, 19 Sep 2023 12:19:45 +0300 Subject: [PATCH 259/756] fix(text manager): filling with other versions just when asked. --- sefaria/model/text_manager.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sefaria/model/text_manager.py b/sefaria/model/text_manager.py index 37641d0229..3395c1ac5b 100644 --- a/sefaria/model/text_manager.py +++ b/sefaria/model/text_manager.py @@ -34,7 +34,7 @@ def _append_version(self, version): for attr in ['chapter', 'title', 'language']: fields.remove(attr) version_details = {f: getattr(version, f, "") for f in fields} - text_range = TextRange(self.oref, version.actualLanguage, version.versionTitle, True) + text_range = TextRange(self.oref, version.actualLanguage, version.versionTitle, self.fill_in_missing_segments) if self.fill_in_missing_segments: # we need a new VersionSet of only the relevant versions for merging. copy should be better than calling for mongo @@ -42,6 +42,7 @@ def _append_version(self, version): relevant_versions.remove(lambda v: v.actualLanguage != version.actualLanguage) else: relevant_versions = [version] + print(self.oref, version.actualLanguage, version.versionTitle, self.fill_in_missing_segments, relevant_versions) text_range.versions = relevant_versions version_details['text'] = text_range.text From 9f51b4ebdbda912085f97804dc2aedc93b646278 Mon Sep 17 00:00:00 2001 From: yishai <yishai@sefaria.org> Date: Tue, 19 Sep 2023 12:28:37 +0300 Subject: [PATCH 260/756] fix(text manager): do not return the same version even if included in two different version params. --- sefaria/model/text_manager.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sefaria/model/text_manager.py b/sefaria/model/text_manager.py index 3395c1ac5b..6b782dc660 100644 --- a/sefaria/model/text_manager.py +++ b/sefaria/model/text_manager.py @@ -73,7 +73,8 @@ def _append_required_versions(self, lang: str, vtitle: str) -> None: if vtitle != self.ALL and versions: versions = [max(versions, key=lambda v: getattr(v, 'priority', 0))] for version in versions: - if version not in self.return_obj['versions']: #do not return the same version even if included in two different version params + if all(version.actualLanguage != v['actualLanguage'] and version.versionTitle != v['versionTitle'] for v in self.return_obj['versions']): + #do not return the same version even if included in two different version params self._append_version(version) if not versions: self.return_obj['missings'].append((lang, vtitle)) From 01bdf5aebaf5a6e555fb0b3dbbae5f3175347763 Mon Sep 17 00:00:00 2001 From: yonadavGit <yonadav.le@gmail.com> Date: Tue, 19 Sep 2023 13:53:32 +0300 Subject: [PATCH 261/756] feat: display short descriptions of works in author-topic pages --- sefaria/model/topic.py | 9 ++++----- static/css/s2.css | 4 ++++ static/js/TopicPage.jsx | 22 +++++++++++++++++++--- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/sefaria/model/topic.py b/sefaria/model/topic.py index 5a185f9ac2..f52d970248 100644 --- a/sefaria/model/topic.py +++ b/sefaria/model/topic.py @@ -536,7 +536,8 @@ def index_is_commentary(index): def get_aggregated_urls_for_authors_indexes(self) -> list: """ Aggregates author's works by category when possible and - returns list of tuples. Each tuple is of shape (url, {"en", "he"}) corresponding to an index or category of indexes of this author's works. + returns a dictionary. Each dictionary is of shape {"url": str, "title": {"en": str, "he": str}, "description": {"en": str, "he": str}} + corresponding to an index or category of indexes of this author's works. """ from .schema import Term from .text import Index @@ -549,8 +550,7 @@ def get_aggregated_urls_for_authors_indexes(self) -> list: if isinstance(index_or_cat, Index): unique_urls.append({"url":f'/{index_or_cat.title.replace(" ", "_")}', "title": {"en": index_or_cat.get_title('en'), "he": index_or_cat.get_title('he')}, - "description":{"en": en_desc, "he": he_desc} - }) + "description":{"en": en_desc, "he": he_desc}}) else: if collective_title_term is None: cat_term = Term().load({"name": index_or_cat.sharedTitle}) @@ -564,8 +564,7 @@ def get_aggregated_urls_for_authors_indexes(self) -> list: unique_urls.append({"url": f'/texts/{"/".join(index_or_cat.path)}', "title": {"en": en_text, "he": he_text}, - "description":{"en": en_desc, "he": he_desc} - }) + "description":{"en": en_desc, "he": he_desc}}) return(unique_urls) @staticmethod diff --git a/static/css/s2.css b/static/css/s2.css index 33757cdd4d..33dda1b34b 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -2468,6 +2468,10 @@ input.resourcesLink { padding-bottom: 12px; border-top: 1px solid #EDEDEC; } +.navBlockAuthorPage { + padding-bottom: 12px; + width: 50%; +} .navBlock.withColorLine { border-top: 4px solid transparent; } diff --git a/static/js/TopicPage.jsx b/static/js/TopicPage.jsx index 08204a00e2..dc1aec0d17 100644 --- a/static/js/TopicPage.jsx +++ b/static/js/TopicPage.jsx @@ -24,6 +24,7 @@ import { SimpleLinkedBlock, CategoryHeader, } from './Misc'; +import {ContentText} from "./ContentText"; /* @@ -315,7 +316,6 @@ const TopicHeader = ({ topic, topicData, multiPanel, isCat, setNavTopic, openDis const { en, he } = !!topicData && topicData.primaryTitle ? topicData.primaryTitle : {en: "Loading...", he: "טוען..."}; const isTransliteration = !!topicData ? topicData.primaryTitleIsTransliteration : {en: false, he: false}; const category = !!topicData ? Sefaria.topicTocCategory(topicData.slug) : null; - console.log(topicData.indexes) return ( <div> <div className="navTitle tight"> @@ -352,14 +352,30 @@ const TopicHeader = ({ topic, topicData, multiPanel, isCat, setNavTopic, openDis <div> <div className="sectionTitleText authorIndexTitle"><InterfaceText>Works on Sefaria</InterfaceText></div> <div className="authorIndexList"> - {/*{topicData.indexes.map(({text, url}) => <SimpleLinkedBlock key={url} {...text} url={url} classes="authorIndex" />)}*/} - {topicData.indexes.map(({url, title, description}) => <SimpleLinkedBlock key={url} {...title} url={url} classes="authorIndex" />)} + {topicData.indexes.map(({url, title, description}) => <AuthorDisplayItem key={url} url={url} enTitle={title.en} heTitle={title.he} enDesc={description.en} heDesc={description.he}/>)} </div> </div> : null} </div> );} +const AuthorDisplayItem = ({url, enTitle, heTitle, enDesc, heDesc}) => { + const classes = classNames({ navBlockTitle: 1 }); + return ( + <div className="navBlockAuthorPage" > + <a href={url} + className = {classes} + > + <InterfaceText text={{en: enTitle, he: heTitle}} /> + </a> + <div className="navBlockDescription"> + <InterfaceText text={{en: enDesc, he: heDesc}} /> + </div> + </div> + ); +}; + + const useTabDisplayData = (translationLanguagePreference) => { const getTabDisplayData = useCallback(() => [ { From 8261263e797bd1cfbd7898c61a3f735bdcaecba5 Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Tue, 19 Sep 2023 14:10:14 +0300 Subject: [PATCH 262/756] chore(Topic Images): Reduce font size to 11 px, condense caption lines --- static/css/s2.css | 4 ++-- static/js/TopicPage.jsx | 13 +++++++------ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index 83945d7697..d3bcadfb76 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -2219,9 +2219,9 @@ div.interfaceLinks-row a { font-family: Roboto; } .topicImageCaption { - font-size: 14px; + font-size: 11px; font-weight: 400; - line-height: 18px; + line-height: 15px; letter-spacing: 0em; color: var(--dark-grey); width: 100%; diff --git a/static/js/TopicPage.jsx b/static/js/TopicPage.jsx index 38a5130bd5..71527e2153 100644 --- a/static/js/TopicPage.jsx +++ b/static/js/TopicPage.jsx @@ -701,6 +701,7 @@ const TopicImage = ({photoLink, enCaption, heCaption }) => { return( <div class="topicImageWrapper"> + {/** Todo Break out the classes. add CSS for linebreak via parent */} <img class="topicImagePicture" src={photoLink}/> <div class="topicImageCaption topicImageCaptionLinebreak"> <InterfaceText text={{en:enCaption, he:heCaption}} /> @@ -769,27 +770,27 @@ const TopicMetaData = ({ topicTitle, timePeriod, properties={} }) => { const topicImageKey = topicTitle.en; const hardcodedTopicImagesMap = { 'Rosh Hashanah': {'photoLink':'https://museums.cjh.org/web/objects/common/webmedia.php?irn=11469&reftable=ecatalogue&refirn=6640', - 'enCaption':'Rosh Hashanah\nArtist: Arthur Szyk (1894-1951)\nTempera and ink on paper\nNew Canaan, 1948\nCollection of Yeshiva University Museum\nGift of Charles Frost', + 'enCaption':'Rosh Hashanah, Arthur Szyk (1894-1951) Tempera and ink on paper. New Canaan, 1948. Collection of Yeshiva University Museum. Gift of Charles Frost', 'heCaption': 'ראש השנה, ארתור שיק, ארה״ב 1948. אוסף ישיבה יוניברסיטי'}, 'Yom Kippur': {'photoLink':'https://www.bl.uk/IllImages/BLCD/big/K900/K90075-77.jpg', - 'enCaption':'Micrography of Jonah being swallowed by the fish, at the text of Jonah, the haftarah for the afternoon service of Yom Kippur.\n 1300-1500, Germany (The British Library)', + 'enCaption':'Micrography of Jonah being swallowed by the fish. Germany, 1300-1500, The British Library', 'heCaption': 'מיקורגפיה של יונה בבטן הדג, מתוך ספר יונה ההפטרה של יום כיפור, 1300-1500'}, 'The Four Species': {'photoLink':'https://res.cloudinary.com/the-jewish-museum/image/fetch/q_auto,f_auto/v1/https%3A%2F%2Fthejm.netx.net%2Ffile%2Fasset%2F34234%2Fview%2F52568%2Fview_52568%3Ftoken%3D5d5cdc57-6399-40b5-afb0-93139921700e', - 'enCaption':'Etrog container, K B, late 19th century, Germany.\n The Jewish Museum, Gift of Dr. Harry G. Friedman', + 'enCaption':'Etrog container, K B, late 19th century, Germany. The Jewish Museum, Gift of Dr. Harry G. Friedman', 'heCaption': 'תיבת אתרוג, סוף המאה ה19, גרמניה. מתנת הארי ג. פרידמן '}, 'Sukkot': {'photoLink':'https://www.bl.uk/IllImages/BLCD/big/d400/d40054-17a.jpg', - 'enCaption':'Detail of a painting of a sukkah (booth built for Sukkot).\n Image taken from f. 316v of Prayer book (Forli Siddur) for the entire year, 1383, Italian rite.', + 'enCaption':'Detail of a painting of a sukkah Image taken from f. 316v of Forli Siddur. 1383, Italian rite. The British Library', 'heCaption': 'פרט ציור של סוכה עם שולחן פרוס ושלוש דמויות. דימוי מתוך סידור פורלי, 1383 איטליה'}, 'Simchat Torah': {'photoLink':'https://upload.wikimedia.org/wikipedia/commons/4/4d/Rosh_Hashanah_greeting_card_%287974345646%29.jpg?20150712114334', - 'enCaption':'Rosh Hashanah postcard: Hakafot\nArtist: Haim Yisroel Goldberg (1888-1943)\nPublisher: Williamsburg Post Card Co.\nGermany, ca. 1915\nCollection of Yeshiva University Museum', + 'enCaption':'Rosh Hashanah postcard: Hakafot, Haim Yisroel Goldberg (1888-1943) Publisher: Williamsburg Post Card Co. Germany, ca. 1915 Collection of Yeshiva University Museum', 'heCaption': 'גלויה לראש השנה: הקפות, חיים גולדברג, גרמניה 1915, אוסף ישיבה יוניברסיטי'}, 'Shabbat': {'photoLink':'https://res.cloudinary.com/the-jewish-museum/image/fetch/q_auto,f_auto/v1/https%3A%2F%2Fthejm.netx.net%2Ffile%2Fasset%2F35064%2Fview%2F61838%2Fview_61838%3Ftoken%3D5d5cdc57-6399-40b5-afb0-93139921700e', - 'enCaption':'Friday Evening, Isidor Kaufmann, Austria c. 1920.\n The Jewish Museum, Gift of Mr. and Mrs. M. R. Schweitzer', + 'enCaption':'Friday Evening, Isidor Kaufmann, Austria c. 1920. The Jewish Museum, Gift of Mr. and Mrs. M. R. Schweitzer', 'heCaption': 'שישי בערב, איזידור קאופמן, וינה 1920. מתנת מר וגברת מ.ר. שוייצר'}, }; From 7b0214bb6733d48c790e90b2d5b79408616e8d50 Mon Sep 17 00:00:00 2001 From: Ephraim <ephraim@sefaria.org> Date: Tue, 19 Sep 2023 16:32:00 +0200 Subject: [PATCH 263/756] helm(fix): Change envvar in nginx to use the proper secret --- helm-chart/sefaria-project/templates/rollout/nginx.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/helm-chart/sefaria-project/templates/rollout/nginx.yaml b/helm-chart/sefaria-project/templates/rollout/nginx.yaml index 3dc1cae434..f1b24d995d 100644 --- a/helm-chart/sefaria-project/templates/rollout/nginx.yaml +++ b/helm-chart/sefaria-project/templates/rollout/nginx.yaml @@ -97,7 +97,10 @@ spec: - name: SEARCH_HOST value: "{{ .Values.nginx.SEARCH_HOST }}" - name: STRAPI_LOCATION - value: "{{ .Values.localSettings.STRAPI_LOCATION }}" + valueFrom: + secretKeyRef: + name: {{ .Values.secrets.localSettings.ref }} + key: STRAPI_LOCATION {{- if .Values.linker.enabled }} - name: LINKER_HOST value: "linker-{{ .Values.deployEnv }}-{{ .Release.Revision }}" From 4e58335a187ff47e33fae3740b6538a2dee9e425 Mon Sep 17 00:00:00 2001 From: Shanee <shanee@sefaria.org> Date: Tue, 19 Sep 2023 13:42:56 -0400 Subject: [PATCH 264/756] feat(gtag): resources panel open --- sefaria/helper/linker.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/sefaria/helper/linker.py b/sefaria/helper/linker.py index a0f09659a2..df81fef43b 100644 --- a/sefaria/helper/linker.py +++ b/sefaria/helper/linker.py @@ -41,6 +41,7 @@ def make_find_refs_response(request): request_text, options, meta_data = _unpack_find_refs_request(request) if meta_data: _add_webpage_hit_for_url(meta_data.get("url", None)) + print(options) return _make_find_refs_response_with_cache(request_text, options, meta_data) @@ -62,10 +63,11 @@ class _FindRefsTextOptions: class _FindRefsText: title: str body: str + lang: str - def __post_init__(self): - from sefaria.utils.hebrew import is_mostly_hebrew - self.lang = 'he' if is_mostly_hebrew(self.body) else 'en' + # def __post_init__(self): + # from sefaria.utils.hebrew import is_mostly_hebrew + # self.lang = 'he' if is_mostly_hebrew(self.body) else 'en' def _unpack_find_refs_request(request): @@ -75,9 +77,11 @@ def _unpack_find_refs_request(request): def _create_find_refs_text(post_body) -> _FindRefsText: + from sefaria.utils.hebrew import is_mostly_hebrew title = post_body['text']['title'] body = post_body['text']['body'] - return _FindRefsText(title, body) + lang = post_body['lang'] if 'lang' in post_body else 'he' if is_mostly_hebrew(body) else 'en' + return _FindRefsText(title, body, lang) def _create_find_refs_options(get_body: dict, post_body: dict) -> _FindRefsTextOptions: From 7bc1e01f6eb39b571851799c01922573905b405d Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Wed, 20 Sep 2023 08:48:41 +0300 Subject: [PATCH 265/756] chore: set years to 3000 ignore compDate --- sefaria/client/wrapper.py | 2 +- sefaria/model/garden.py | 24 ++++++++-------- sefaria/model/text.py | 60 +++++++++++++++++++-------------------- 3 files changed, 43 insertions(+), 43 deletions(-) diff --git a/sefaria/client/wrapper.py b/sefaria/client/wrapper.py index f5af482b56..a84cc77226 100644 --- a/sefaria/client/wrapper.py +++ b/sefaria/client/wrapper.py @@ -57,7 +57,7 @@ def format_link_object_for_client(link, with_text, ref, pos=None): compDate = getattr(linkRef.index, "compDate", None) if compDate: try: - com["compDate"] = int(compDate) + com["compDate"] = compDate[0] except ValueError: com["compDate"] = 3000 # default comp date to in the future try: diff --git a/sefaria/model/garden.py b/sefaria/model/garden.py index e55bcd6a86..38d6f9c75b 100644 --- a/sefaria/model/garden.py +++ b/sefaria/model/garden.py @@ -444,18 +444,18 @@ def _derive_metadata(self): if getattr(i, "compDate", None): errorMargin = int(getattr(i, "errorMargin", 0)) self.startIsApprox = self.endIsApprox = errorMargin > 0 - - try: - year = int(getattr(i, "compDate")) - self.start = year - errorMargin - self.end = year + errorMargin - except ValueError as e: - years = getattr(i, "compDate").split("-") - if years[0] == "" and len(years) == 3: #Fix for first value being negative - years[0] = -int(years[1]) - years[1] = int(years[2]) - self.start = int(years[0]) - errorMargin - self.end = int(years[1]) + errorMargin + self.start = self.end = 3000 + # try: + # year = int(getattr(i, "compDate")) + # self.start = year - errorMargin + # self.end = year + errorMargin + # except ValueError as e: + # years = getattr(i, "compDate").split("-") + # if years[0] == "" and len(years) == 3: #Fix for first value being negative + # years[0] = -int(years[1]) + # years[1] = int(years[2]) + # self.start = int(years[0]) - errorMargin + # self.end = int(years[1]) + errorMargin elif author and author.mostAccurateTimePeriod(): tp = author.mostAccurateTimePeriod() diff --git a/sefaria/model/text.py b/sefaria/model/text.py index 27babfefa0..9d87a1dca7 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -459,21 +459,21 @@ def best_time_period(self): if getattr(self, "compDate", None): errorMargin = int(getattr(self, "errorMargin", 0)) self.startIsApprox = self.endIsApprox = errorMargin > 0 - - try: - year = int(getattr(self, "compDate")) - start = year - errorMargin - end = year + errorMargin - except ValueError as e: - years = getattr(self, "compDate").split("-") - if years[0] == "" and len(years) == 3: #Fix for first value being negative - years[0] = -int(years[1]) - years[1] = int(years[2]) - try: - start = int(years[0]) - errorMargin - end = int(years[1]) + errorMargin - except UnicodeEncodeError as e: - pass + start = end = 3000 + # try: + # year = int(getattr(self, "compDate")) + # start = year - errorMargin + # end = year + errorMargin + # except ValueError as e: + # years = getattr(self, "compDate").split("-") + # if years[0] == "" and len(years) == 3: #Fix for first value being negative + # years[0] = -int(years[1]) + # years[1] = int(years[2]) + # try: + # start = int(years[0]) - errorMargin + # end = int(years[1]) + errorMargin + # except UnicodeEncodeError as e: + # pass else: author = self.author_objects()[0] if len(self.author_objects()) > 0 else None @@ -510,21 +510,21 @@ def _get_time_period(self, date_field, margin_field=None): except ValueError: error_margin = 0 startIsApprox = endIsApprox = error_margin > 0 - - try: - year = int(getattr(self, date_field)) - start = year - error_margin - end = year + error_margin - except ValueError as e: - try: - years = getattr(self, date_field).split("-") - if years[0] == "" and len(years) == 3: #Fix for first value being negative - years[0] = -int(years[1]) - years[1] = int(years[2]) - start = int(years[0]) - error_margin - end = int(years[1]) + error_margin - except ValueError as e: - return None + start = end = 3000 + # try: + # year = int(getattr(self, date_field)) + # start = year - error_margin + # end = year + error_margin + # except ValueError as e: + # try: + # years = getattr(self, date_field).split("-") + # if years[0] == "" and len(years) == 3: #Fix for first value being negative + # years[0] = -int(years[1]) + # years[1] = int(years[2]) + # start = int(years[0]) - error_margin + # end = int(years[1]) + error_margin + # except ValueError as e: + # return None return timeperiod.TimePeriod({ "start": start, "startIsApprox": startIsApprox, From 40cd092bc57febd4a36b661c52fec0ddace7addb Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Wed, 20 Sep 2023 09:39:43 +0300 Subject: [PATCH 266/756] chore: make compDate[0] to 3000 --- sefaria/client/wrapper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sefaria/client/wrapper.py b/sefaria/client/wrapper.py index a84cc77226..0525ebbb61 100644 --- a/sefaria/client/wrapper.py +++ b/sefaria/client/wrapper.py @@ -57,7 +57,7 @@ def format_link_object_for_client(link, with_text, ref, pos=None): compDate = getattr(linkRef.index, "compDate", None) if compDate: try: - com["compDate"] = compDate[0] + com["compDate"] = 3000 except ValueError: com["compDate"] = 3000 # default comp date to in the future try: From 44da07b51c34d6eb7ba90909d4db20e2c73f15c2 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 20 Sep 2023 10:20:31 +0300 Subject: [PATCH 267/756] feat(search): add + when there are more results than ES can calculate --- static/js/SearchPage.jsx | 6 ++-- static/js/SearchResultList.jsx | 57 ++++++++++++++++++++++++---------- 2 files changed, 43 insertions(+), 20 deletions(-) diff --git a/static/js/SearchPage.jsx b/static/js/SearchPage.jsx index ece9f8cf43..438b2bc3c9 100644 --- a/static/js/SearchPage.jsx +++ b/static/js/SearchPage.jsx @@ -46,9 +46,9 @@ class SearchPage extends Component { { this.props.query } <InterfaceText html={{en: "”", he: "״"}} /> </h1> - {this.state.totalResults ? + {this.state.totalResults?.getValue() > 0 ? <div className="searchResultCount sans-serif"> - <InterfaceText>{this.state.totalResults.addCommas()}</InterfaceText>  + <InterfaceText>{this.state.totalResults.asString()}</InterfaceText>  <InterfaceText>Results</InterfaceText> </div> : null } @@ -71,7 +71,7 @@ class SearchPage extends Component { {(Sefaria.multiPanel && !this.props.compare) || this.state.mobileFiltersOpen ? <div className={Sefaria.multiPanel && !this.props.compare ? "navSidebar" : "mobileSearchFilters"}> - {this.state.totalResults ? + {this.state.totalResults?.getValue() > 0 ? <SearchFilters query={this.props.query} searchState={this.props[`${this.props.tab}SearchState`]} diff --git a/static/js/SearchResultList.jsx b/static/js/SearchResultList.jsx index a05afe1f59..b09e91b170 100644 --- a/static/js/SearchResultList.jsx +++ b/static/js/SearchResultList.jsx @@ -76,6 +76,36 @@ const SearchTopic = (props) => { } +class SearchTotal { + constructor({value=0, relation="eq"} = {}) { + this._value = value; + this._relation = relation; + } + getValue = () => this._value; + add = (num) => this._value += num; + asString = () => `${this._value.addCommas()}${this._getRelationString()}`; + _getRelationString = () => this._relation === 'gte' ? '+' : ''; + combine = (other) => { + if (!(other instanceof SearchTotal)) { + throw new TypeError('Parameter must be an instance of SearchTotal.'); + } + const newValue = this.getValue() + other.getValue(); + let newRelation = this._relation; + if (other._relation === 'gte' || this._relation === 'gte') { + newRelation = 'gte'; + } + return new SearchTotal({value: newValue, relation: newRelation}); + }; +} + + +function createSearchTotal(total) { + /** + * this function ensures backwards compatibility between the way elasticsearch formats the total pre-v8 and post-v8 + */ + const totalObj = typeof(total) === 'number' ? {value: total} : {value: total.value, relation: total.relation}; + return new SearchTotal(totalObj) +} class SearchResultList extends Component { @@ -87,7 +117,7 @@ class SearchResultList extends Component { runningQueries: this._typeObjDefault(null), isQueryRunning: this._typeObjDefault(false), moreToLoad: this._typeObjDefault(true), - totals: this._typeObjDefault(0), + totals: this._typeObjDefault(new SearchTotal()), pagesLoaded: this._typeObjDefault(0), hits: this._typeObjDefault([]), error: false, @@ -104,7 +134,7 @@ class SearchResultList extends Component { //console.log("Loaded cached query for") //console.log(args); this.state.hits[t] = this.state.hits[t].concat(cachedQuery.hits.hits); - this.state.totals[t] = this._get_hits_total(cachedQuery.hits.total); + this.state.totals[t] = createSearchTotal(cachedQuery.hits.total); this.state.pagesLoaded[t] += 1; args.start = this.state.pagesLoaded[t] * this.querySize[t]; if (t === "text") { @@ -127,7 +157,7 @@ class SearchResultList extends Component { componentWillReceiveProps(newProps) { if(this.props.query !== newProps.query) { this.setState({ - totals: this._typeObjDefault(0), + totals: this._typeObjDefault(new SearchTotal()), hits: this._typeObjDefault([]), moreToLoad: this._typeObjDefault(true), }); @@ -245,7 +275,7 @@ class SearchResultList extends Component { this.setState(this.state); } totalResults() { - return this.types.reduce((accum, type) => (this.state.totals[type] + accum), 0); + return this.types.reduce((accum, type) => (this.state.totals[type].combine(accum)), new SearchTotal()); } updateTotalResults() { this.props.updateTotalResults(this.totalResults()); @@ -302,13 +332,6 @@ class SearchResultList extends Component { .zip(aggregation_field_array, aggregation_field_lang_suffix_array) .map(([agg, suffix_map]) => `${agg}${suffix_map ? suffix_map[Sefaria.interfaceLang] : ''}`); // add suffix based on interfaceLang to filter, if present in suffix_map } - _get_hits_total(totalObj) { - /** - * this function ensures backwards compatibility between the way elasticsearch formats the total pre-v8 and post-v8 - */ - if (typeof(totalObj) === 'number') { return totalObj; } - return totalObj.value; - } _executeQuery(props, type) { //This takes a props object, so as to be able to handle being called from componentWillReceiveProps with newProps props = props || this.props; @@ -331,11 +354,12 @@ class SearchResultList extends Component { args.success = data => { this.updateRunningQuery(type, null); if (this.state.pagesLoaded[type] === 0) { // Skip if pages have already been loaded from cache, but let aggregation processing below occur + const currTotal = createSearchTotal(data.hits.total); let state = { hits: extend(this.state.hits, {[type]: data.hits.hits}), - totals: extend(this.state.totals, {[type]: this._get_hits_total(data.hits.total)}), + totals: extend(this.state.totals, {[type]: currTotal}), pagesLoaded: extend(this.state.pagesLoaded, {[type]: 1}), - moreToLoad: extend(this.state.moreToLoad, {[type]: this._get_hits_total(data.hits.total) > this.querySize[type]}) + moreToLoad: extend(this.state.moreToLoad, {[type]: currTotal.getValue() > this.querySize[type]}) }; this.setState(state, () => { this.updateTotalResults(); @@ -343,7 +367,7 @@ class SearchResultList extends Component { }); const filter_label = (request_applied && request_applied.length > 0) ? (' - ' + request_applied.join('|')) : ''; const query_label = props.query + filter_label; - Sefaria.track.event("Search", `${this.props.searchInBook? "SidebarSearch ": ""}Query: ${type}`, query_label, this._get_hits_total(data.hits.total)); + Sefaria.track.event("Search", `${this.props.searchInBook? "SidebarSearch ": ""}Query: ${type}`, query_label, createSearchTotal(data.hits.total).getValue()); } if (data.aggregations) { @@ -402,7 +426,7 @@ class SearchResultList extends Component { this.state.hits[type] = nextHits; this.state.pagesLoaded[type] += 1; - if (this.state.pagesLoaded[type] * this.querySize[type] >= this.state.totals[type] ) { + if (this.state.pagesLoaded[type] * this.querySize[type] >= this.state.totals[type].getValue() ) { this.state.moreToLoad[type] = false; } @@ -529,14 +553,13 @@ const SearchTabs = ({clickTextButton, clickSheetButton, textTotal, sheetTotal, c const SearchTab = ({label, total, onClick, active}) => { - total = total.addCommas() const classes = classNames({"search-dropdown-button": 1, active}); return ( <div className={classes} onClick={onClick} onKeyPress={e => {e.charCode === 13 ? onClick(e) : null}} role="button" tabIndex="0"> <div className="type-button-title"> <InterfaceText>{label}</InterfaceText>  - <InterfaceText>{`(${total})`}</InterfaceText> + <InterfaceText>{`(${total.asString()})`}</InterfaceText> </div> </div> ); From 210723166f1363d35a389eefd94f1593b3c3a411 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 20 Sep 2023 10:28:00 +0300 Subject: [PATCH 268/756] chore: npm install --- package-lock.json | 23233 ++++++++++++-------------------------------- 1 file changed, 6296 insertions(+), 16937 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8adefa5077..2f6296b5f5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,17 +10,11 @@ "hasInstallScript": true, "license": "GPL-3.0", "dependencies": { - "@semantic-release/commit-analyzer": "^9.0.2", - "@semantic-release/git": "^10.0.1", - "@semantic-release/github": "^8.0.7", - "@semantic-release/npm": "^9.0.2", - "@semantic-release/release-notes-generator": "^10.0.3", "@sentry/react": "^7.57.0", "body-parser": "^1.15.1", "buffer": "^6.0.3", "cheerio": "^0.22.0", "classnames": "^2.2.5", - "conventional-changelog-conventionalcommits": "^6.1.0", "cookie-parser": "^1.4.2", "core-js": "^3.31.1", "css-modules-require-hook": "^4.2.3", @@ -135,6 +129,7 @@ "version": "7.21.4", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz", "integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==", + "dev": true, "dependencies": { "@babel/highlight": "^7.18.6" }, @@ -489,6 +484,7 @@ "version": "7.19.1", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true, "engines": { "node": ">=6.9.0" } @@ -535,6 +531,7 @@ "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.18.6", "chalk": "^2.0.0", @@ -2184,12425 +2181,3097 @@ "dev": true, "optional": true }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "node_modules/@sentry-internal/tracing": { + "version": "7.57.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.57.0.tgz", + "integrity": "sha512-tpViyDd8AhQGYYhI94xi2aaDopXOPfL2Apwrtb3qirWkomIQ2K86W1mPmkce+B0cFOnW2Dxv/ZTFKz6ghjK75A==", "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "@sentry/core": "7.57.0", + "@sentry/types": "7.57.0", + "@sentry/utils": "7.57.0", + "tslib": "^2.4.1 || ^1.9.3" }, "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "engines": { - "node": ">= 8" + "node": ">=8" } }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "node_modules/@sentry/browser": { + "version": "7.57.0", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.57.0.tgz", + "integrity": "sha512-E0HaYYlaqHFiIRZXxcvOO8Odvlt+TR1vFFXzqUWXPOvDRxURglTOCQ3EN/u6bxtAGJ6y/Zc2obgihTtypuel/w==", "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "@sentry-internal/tracing": "7.57.0", + "@sentry/core": "7.57.0", + "@sentry/replay": "7.57.0", + "@sentry/types": "7.57.0", + "@sentry/utils": "7.57.0", + "tslib": "^2.4.1 || ^1.9.3" }, "engines": { - "node": ">= 8" + "node": ">=8" } }, - "node_modules/@octokit/auth-token": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.4.tgz", - "integrity": "sha512-TWFX7cZF2LXoCvdmJWY7XVPi74aSY0+FfBZNSXEXFkMpjcqsQwDSYVv5FhRFaI0V1ECnwbz4j59T/G+rXNWaIQ==", + "node_modules/@sentry/core": { + "version": "7.57.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.57.0.tgz", + "integrity": "sha512-l014NudPH0vQlzybtXajPxYFfs9w762NoarjObC3gu76D1jzBBFzhdRelkGpDbSLNTIsKhEDDRpgAjBWJ9icfw==", + "dependencies": { + "@sentry/types": "7.57.0", + "@sentry/utils": "7.57.0", + "tslib": "^2.4.1 || ^1.9.3" + }, "engines": { - "node": ">= 14" + "node": ">=8" } }, - "node_modules/@octokit/core": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.2.4.tgz", - "integrity": "sha512-rYKilwgzQ7/imScn3M9/pFfUf4I1AZEH3KhyJmtPdE2zfaXAn2mFfUy4FbKewzc2We5y/LlKLj36fWJLKC2SIQ==", + "node_modules/@sentry/react": { + "version": "7.57.0", + "resolved": "https://registry.npmjs.org/@sentry/react/-/react-7.57.0.tgz", + "integrity": "sha512-XGNTjIoCG3naSmCU8qObd+y+CqAB6NQkGWOp2yyBwp2inyKF2ehJvDh6bIQloBYq2TmOJDa4NfXdMrkilxaLFQ==", "dependencies": { - "@octokit/auth-token": "^3.0.0", - "@octokit/graphql": "^5.0.0", - "@octokit/request": "^6.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^9.0.0", - "before-after-hook": "^2.2.0", - "universal-user-agent": "^6.0.0" + "@sentry/browser": "7.57.0", + "@sentry/types": "7.57.0", + "@sentry/utils": "7.57.0", + "hoist-non-react-statics": "^3.3.2", + "tslib": "^2.4.1 || ^1.9.3" }, "engines": { - "node": ">= 14" + "node": ">=8" + }, + "peerDependencies": { + "react": "15.x || 16.x || 17.x || 18.x" } }, - "node_modules/@octokit/endpoint": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.6.tgz", - "integrity": "sha512-5L4fseVRUsDFGR00tMWD/Trdeeihn999rTMGRMC1G/Ldi1uWlWJzI98H4Iak5DB/RVvQuyMYKqSK/R6mbSOQyg==", + "node_modules/@sentry/replay": { + "version": "7.57.0", + "resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.57.0.tgz", + "integrity": "sha512-pN4ryNS3J5EYbkXvR+O/+hseAJha7XDl8mPFtK0OGTHG10JzCi4tQJazblHQdpb5QBaMMPCeZ+isyfoQLDNXnw==", "dependencies": { - "@octokit/types": "^9.0.0", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" + "@sentry/core": "7.57.0", + "@sentry/types": "7.57.0", + "@sentry/utils": "7.57.0" }, "engines": { - "node": ">= 14" + "node": ">=12" } }, - "node_modules/@octokit/endpoint/node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "node_modules/@sentry/types": { + "version": "7.57.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.57.0.tgz", + "integrity": "sha512-D7ifoUfxuVCUyktIr5Gc+jXUbtcUMmfHdTtTbf1XCZHua5mJceK9wtl3YCg3eq/HK2Ppd52BKnTzEcS5ZKQM+w==", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/@octokit/graphql": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.6.tgz", - "integrity": "sha512-Fxyxdy/JH0MnIB5h+UQ3yCoh1FG4kWXfFKkpWqjZHw/p+Kc8Y44Hu/kCgNBT6nU1shNumEchmW/sUO1JuQnPcw==", + "node_modules/@sentry/utils": { + "version": "7.57.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.57.0.tgz", + "integrity": "sha512-YXrkMCiNklqkXctn4mKYkrzNCf/dfVcRUQrkXjeBC+PHXbcpPyaJgInNvztR7Skl8lE3JPGPN4v5XhLxK1bUUg==", "dependencies": { - "@octokit/request": "^6.0.0", - "@octokit/types": "^9.0.0", - "universal-user-agent": "^6.0.0" + "@sentry/types": "7.57.0", + "tslib": "^2.4.1 || ^1.9.3" }, "engines": { - "node": ">= 14" + "node": ">=8" } }, - "node_modules/@octokit/openapi-types": { - "version": "18.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-18.0.0.tgz", - "integrity": "sha512-V8GImKs3TeQRxRtXFpG2wl19V7444NIOTDF24AWuIbmNaNYOQMWRbjcGDXV5B+0n887fgDcuMNOmlul+k+oJtw==" + "node_modules/@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" }, - "node_modules/@octokit/plugin-paginate-rest": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-6.1.2.tgz", - "integrity": "sha512-qhrmtQeHU/IivxucOV1bbI/xZyC/iOBhclokv7Sut5vnejAIAEXVcGQeRpQlU39E0WwK9lNvJHphHri/DB6lbQ==", + "node_modules/@types/babel__core": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz", + "integrity": "sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==", + "dev": true, "dependencies": { - "@octokit/tsconfig": "^1.0.2", - "@octokit/types": "^9.2.3" - }, - "engines": { - "node": ">= 14" - }, - "peerDependencies": { - "@octokit/core": ">=4" - } - }, - "node_modules/@octokit/plugin-request-log": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", - "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", - "peerDependencies": { - "@octokit/core": ">=3" + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" } }, - "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-7.2.3.tgz", - "integrity": "sha512-I5Gml6kTAkzVlN7KCtjOM+Ruwe/rQppp0QU372K1GP7kNOYEKe8Xn5BW4sE62JAHdwpq95OQK/qGNyKQMUzVgA==", + "node_modules/@types/babel__generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "dev": true, "dependencies": { - "@octokit/types": "^10.0.0" - }, - "engines": { - "node": ">= 14" - }, - "peerDependencies": { - "@octokit/core": ">=3" + "@babel/types": "^7.0.0" } }, - "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-10.0.0.tgz", - "integrity": "sha512-Vm8IddVmhCgU1fxC1eyinpwqzXPEYu0NrYzD3YZjlGjyftdLBTeqNblRC0jmJmgxbJIsQlyogVeGnrNaaMVzIg==", + "node_modules/@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dev": true, "dependencies": { - "@octokit/openapi-types": "^18.0.0" + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" } }, - "node_modules/@octokit/request": { - "version": "6.2.8", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.8.tgz", - "integrity": "sha512-ow4+pkVQ+6XVVsekSYBzJC0VTVvh/FCTUUgTsboGq+DTeWdyIFV8WSCdo0RIxk6wSkBTHqIK1mYuY7nOBXOchw==", + "node_modules/@types/babel__traverse": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", + "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", + "dev": true, "dependencies": { - "@octokit/endpoint": "^7.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^9.0.0", - "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.7", - "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 14" + "@babel/types": "^7.3.0" } }, - "node_modules/@octokit/request-error": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.3.tgz", - "integrity": "sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ==", + "node_modules/@types/eslint": { + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.37.0.tgz", + "integrity": "sha512-Piet7dG2JBuDIfohBngQ3rCt7MgO9xCO4xIMKxBThCq5PNRB91IjlJ10eJVwfoNtvTErmxLzwBZ7rHZtbOMmFQ==", + "dev": true, "dependencies": { - "@octokit/types": "^9.0.0", - "deprecation": "^2.0.0", - "once": "^1.4.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/request/node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "engines": { - "node": ">=0.10.0" + "@types/estree": "*", + "@types/json-schema": "*" } }, - "node_modules/@octokit/rest": { - "version": "19.0.13", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.13.tgz", - "integrity": "sha512-/EzVox5V9gYGdbAI+ovYj3nXQT1TtTHRT+0eZPcuC05UFSWO3mdO9UY1C0i2eLF9Un1ONJkAk+IEtYGAC+TahA==", + "node_modules/@types/eslint-scope": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "dev": true, "dependencies": { - "@octokit/core": "^4.2.1", - "@octokit/plugin-paginate-rest": "^6.1.2", - "@octokit/plugin-request-log": "^1.0.4", - "@octokit/plugin-rest-endpoint-methods": "^7.1.2" - }, - "engines": { - "node": ">= 14" + "@types/eslint": "*", + "@types/estree": "*" } }, - "node_modules/@octokit/tsconfig": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@octokit/tsconfig/-/tsconfig-1.0.2.tgz", - "integrity": "sha512-I0vDR0rdtP8p2lGMzvsJzbhdOWy405HcGovrspJ8RRibHnyRgggUSNO5AIox5LmqiwmatHKYsvj6VGFHkqS7lA==" + "node_modules/@types/estree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz", + "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==", + "dev": true }, - "node_modules/@octokit/types": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.3.2.tgz", - "integrity": "sha512-D4iHGTdAnEEVsB8fl95m1hiz7D5YiRdQ9b/OEb3BYRVwbLsGHcRVPz+u+BgRLNk0Q0/4iZCBqDN96j2XNxfXrA==", + "node_modules/@types/hast": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.4.tgz", + "integrity": "sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==", "dependencies": { - "@octokit/openapi-types": "^18.0.0" - } - }, - "node_modules/@pnpm/config.env-replace": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", - "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==", - "engines": { - "node": ">=12.22.0" + "@types/unist": "*" } }, - "node_modules/@pnpm/network.ca-file": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz", - "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==", - "dependencies": { - "graceful-fs": "4.2.10" - }, - "engines": { - "node": ">=12.22.0" - } + "node_modules/@types/is-hotkey": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/@types/is-hotkey/-/is-hotkey-0.1.7.tgz", + "integrity": "sha512-yB5C7zcOM7idwYZZ1wKQ3pTfjA9BbvFqRWvKB46GFddxnJtHwi/b9y84ykQtxQPg5qhdpg4Q/kWU3EGoCTmLzQ==" }, - "node_modules/@pnpm/network.ca-file/node_modules/graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true }, - "node_modules/@pnpm/npm-conf": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.2.2.tgz", - "integrity": "sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==", + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, "dependencies": { - "@pnpm/config.env-replace": "^1.1.0", - "@pnpm/network.ca-file": "^1.0.1", - "config-chain": "^1.1.11" - }, - "engines": { - "node": ">=12" + "@types/istanbul-lib-coverage": "*" } }, - "node_modules/@semantic-release/commit-analyzer": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/@semantic-release/commit-analyzer/-/commit-analyzer-9.0.2.tgz", - "integrity": "sha512-E+dr6L+xIHZkX4zNMe6Rnwg4YQrWNXK+rNsvwOPpdFppvZO1olE2fIgWhv89TkQErygevbjsZFSIxp+u6w2e5g==", + "node_modules/@types/istanbul-reports": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", + "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", + "dev": true, "dependencies": { - "conventional-changelog-angular": "^5.0.0", - "conventional-commits-filter": "^2.0.0", - "conventional-commits-parser": "^3.2.3", - "debug": "^4.0.0", - "import-from": "^4.0.0", - "lodash": "^4.17.4", - "micromatch": "^4.0.2" - }, - "engines": { - "node": ">=14.17" - }, - "peerDependencies": { - "semantic-release": ">=18.0.0-beta.1" + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" } }, - "node_modules/@semantic-release/commit-analyzer/node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } + "node_modules/@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true }, - "node_modules/@semantic-release/error": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz", - "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==", - "peer": true, - "engines": { - "node": ">=18" - } + "node_modules/@types/lodash": { + "version": "4.14.194", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.194.tgz", + "integrity": "sha512-r22s9tAS7imvBt2lyHC9B8AGwWnXaYb1tY09oyLkXDs4vArpYJzw09nj8MLx5VfciBPGIb+ZwG0ssYnEPJxn/g==" }, - "node_modules/@semantic-release/git": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@semantic-release/git/-/git-10.0.1.tgz", - "integrity": "sha512-eWrx5KguUcU2wUPaO6sfvZI0wPafUKAMNC18aXY4EnNcrZL86dEmpNVnC9uMpGZkmZJ9EfCVJBQx4pV4EMGT1w==", + "node_modules/@types/mdast": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.11.tgz", + "integrity": "sha512-Y/uImid8aAwrEA24/1tcRZwpxX3pIFTSilcNDKSPn+Y2iDywSEachzRuvgAYYLR3wpGXAsMbv5lvKLDZLeYPAw==", "dependencies": { - "@semantic-release/error": "^3.0.0", - "aggregate-error": "^3.0.0", - "debug": "^4.0.0", - "dir-glob": "^3.0.0", - "execa": "^5.0.0", - "lodash": "^4.17.4", - "micromatch": "^4.0.0", - "p-reduce": "^2.0.0" - }, - "engines": { - "node": ">=14.17" - }, - "peerDependencies": { - "semantic-release": ">=18.0.0" + "@types/unist": "*" } }, - "node_modules/@semantic-release/git/node_modules/@semantic-release/error": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-3.0.0.tgz", - "integrity": "sha512-5hiM4Un+tpl4cKw3lV4UgzJj+SmfNIDCLLw0TepzQxz9ZGV5ixnqkzIVF+3tp0ZHgcMKE+VNGHJjEeyFG2dcSw==", - "engines": { - "node": ">=14.17" - } + "node_modules/@types/node": { + "version": "18.15.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.12.tgz", + "integrity": "sha512-Wha1UwsB3CYdqUm2PPzh/1gujGCNtWVUYF0mB00fJFoR4gTyWTDPjSm+zBF787Ahw8vSGgBja90MkgFwvB86Dg==", + "dev": true }, - "node_modules/@semantic-release/git/node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@semantic-release/git/node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "engines": { - "node": ">=6" - } - }, - "node_modules/@semantic-release/git/node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@semantic-release/git/node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/@semantic-release/git/node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@semantic-release/git/node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/@semantic-release/git/node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@semantic-release/git/node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/@semantic-release/git/node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/@semantic-release/git/node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@semantic-release/git/node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@semantic-release/git/node_modules/p-reduce": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-2.1.0.tgz", - "integrity": "sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@semantic-release/git/node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@semantic-release/git/node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@semantic-release/git/node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@semantic-release/git/node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/@semantic-release/git/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@semantic-release/github": { - "version": "8.0.7", - "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-8.0.7.tgz", - "integrity": "sha512-VtgicRIKGvmTHwm//iqTh/5NGQwsncOMR5vQK9pMT92Aem7dv37JFKKRuulUsAnUOIlO4G8wH3gPiBAA0iW0ww==", - "dependencies": { - "@octokit/rest": "^19.0.0", - "@semantic-release/error": "^3.0.0", - "aggregate-error": "^3.0.0", - "bottleneck": "^2.18.1", - "debug": "^4.0.0", - "dir-glob": "^3.0.0", - "fs-extra": "^11.0.0", - "globby": "^11.0.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "issue-parser": "^6.0.0", - "lodash": "^4.17.4", - "mime": "^3.0.0", - "p-filter": "^2.0.0", - "p-retry": "^4.0.0", - "url-join": "^4.0.0" - }, - "engines": { - "node": ">=14.17" - }, - "peerDependencies": { - "semantic-release": ">=18.0.0-beta.1" - } - }, - "node_modules/@semantic-release/github/node_modules/@semantic-release/error": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-3.0.0.tgz", - "integrity": "sha512-5hiM4Un+tpl4cKw3lV4UgzJj+SmfNIDCLLw0TepzQxz9ZGV5ixnqkzIVF+3tp0ZHgcMKE+VNGHJjEeyFG2dcSw==", - "engines": { - "node": ">=14.17" - } - }, - "node_modules/@semantic-release/github/node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@semantic-release/github/node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "engines": { - "node": ">=6" - } - }, - "node_modules/@semantic-release/github/node_modules/fs-extra": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", - "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, - "node_modules/@semantic-release/github/node_modules/mime": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", - "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/@semantic-release/npm": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/@semantic-release/npm/-/npm-9.0.2.tgz", - "integrity": "sha512-zgsynF6McdzxPnFet+a4iO9HpAlARXOM5adz7VGVCvj0ne8wtL2ZOQoDV2wZPDmdEotDIbVeJjafhelZjs9j6g==", - "dependencies": { - "@semantic-release/error": "^3.0.0", - "aggregate-error": "^3.0.0", - "execa": "^5.0.0", - "fs-extra": "^11.0.0", - "lodash": "^4.17.15", - "nerf-dart": "^1.0.0", - "normalize-url": "^6.0.0", - "npm": "^8.3.0", - "rc": "^1.2.8", - "read-pkg": "^5.0.0", - "registry-auth-token": "^5.0.0", - "semver": "^7.1.2", - "tempy": "^1.0.0" - }, - "engines": { - "node": ">=16 || ^14.17" - }, - "peerDependencies": { - "semantic-release": ">=19.0.0" - } - }, - "node_modules/@semantic-release/npm/node_modules/@semantic-release/error": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-3.0.0.tgz", - "integrity": "sha512-5hiM4Un+tpl4cKw3lV4UgzJj+SmfNIDCLLw0TepzQxz9ZGV5ixnqkzIVF+3tp0ZHgcMKE+VNGHJjEeyFG2dcSw==", - "engines": { - "node": ">=14.17" - } - }, - "node_modules/@semantic-release/npm/node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@semantic-release/npm/node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "engines": { - "node": ">=6" - } - }, - "node_modules/@semantic-release/npm/node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@semantic-release/npm/node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/@semantic-release/npm/node_modules/fs-extra": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", - "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, - "node_modules/@semantic-release/npm/node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@semantic-release/npm/node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/@semantic-release/npm/node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@semantic-release/npm/node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" - }, - "node_modules/@semantic-release/npm/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@semantic-release/npm/node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/@semantic-release/npm/node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@semantic-release/npm/node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@semantic-release/npm/node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@semantic-release/npm/node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@semantic-release/npm/node_modules/read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@semantic-release/npm/node_modules/registry-auth-token": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.0.2.tgz", - "integrity": "sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==", - "dependencies": { - "@pnpm/npm-conf": "^2.1.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@semantic-release/npm/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@semantic-release/npm/node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@semantic-release/npm/node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@semantic-release/npm/node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/@semantic-release/npm/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@semantic-release/npm/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@semantic-release/npm/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/@semantic-release/release-notes-generator": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/@semantic-release/release-notes-generator/-/release-notes-generator-10.0.3.tgz", - "integrity": "sha512-k4x4VhIKneOWoBGHkx0qZogNjCldLPRiAjnIpMnlUh6PtaWXp/T+C9U7/TaNDDtgDa5HMbHl4WlREdxHio6/3w==", - "dependencies": { - "conventional-changelog-angular": "^5.0.0", - "conventional-changelog-writer": "^5.0.0", - "conventional-commits-filter": "^2.0.0", - "conventional-commits-parser": "^3.2.3", - "debug": "^4.0.0", - "get-stream": "^6.0.0", - "import-from": "^4.0.0", - "into-stream": "^6.0.0", - "lodash": "^4.17.4", - "read-pkg-up": "^7.0.0" - }, - "engines": { - "node": ">=14.17" - }, - "peerDependencies": { - "semantic-release": ">=18.0.0-beta.1" - } - }, - "node_modules/@semantic-release/release-notes-generator/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@semantic-release/release-notes-generator/node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@semantic-release/release-notes-generator/node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" - }, - "node_modules/@semantic-release/release-notes-generator/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@semantic-release/release-notes-generator/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@semantic-release/release-notes-generator/node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@semantic-release/release-notes-generator/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@semantic-release/release-notes-generator/node_modules/read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@semantic-release/release-notes-generator/node_modules/read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dependencies": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@semantic-release/release-notes-generator/node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@semantic-release/release-notes-generator/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@sentry-internal/tracing": { - "version": "7.57.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.57.0.tgz", - "integrity": "sha512-tpViyDd8AhQGYYhI94xi2aaDopXOPfL2Apwrtb3qirWkomIQ2K86W1mPmkce+B0cFOnW2Dxv/ZTFKz6ghjK75A==", - "dependencies": { - "@sentry/core": "7.57.0", - "@sentry/types": "7.57.0", - "@sentry/utils": "7.57.0", - "tslib": "^2.4.1 || ^1.9.3" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@sentry/browser": { - "version": "7.57.0", - "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.57.0.tgz", - "integrity": "sha512-E0HaYYlaqHFiIRZXxcvOO8Odvlt+TR1vFFXzqUWXPOvDRxURglTOCQ3EN/u6bxtAGJ6y/Zc2obgihTtypuel/w==", - "dependencies": { - "@sentry-internal/tracing": "7.57.0", - "@sentry/core": "7.57.0", - "@sentry/replay": "7.57.0", - "@sentry/types": "7.57.0", - "@sentry/utils": "7.57.0", - "tslib": "^2.4.1 || ^1.9.3" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@sentry/core": { - "version": "7.57.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.57.0.tgz", - "integrity": "sha512-l014NudPH0vQlzybtXajPxYFfs9w762NoarjObC3gu76D1jzBBFzhdRelkGpDbSLNTIsKhEDDRpgAjBWJ9icfw==", - "dependencies": { - "@sentry/types": "7.57.0", - "@sentry/utils": "7.57.0", - "tslib": "^2.4.1 || ^1.9.3" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@sentry/react": { - "version": "7.57.0", - "resolved": "https://registry.npmjs.org/@sentry/react/-/react-7.57.0.tgz", - "integrity": "sha512-XGNTjIoCG3naSmCU8qObd+y+CqAB6NQkGWOp2yyBwp2inyKF2ehJvDh6bIQloBYq2TmOJDa4NfXdMrkilxaLFQ==", - "dependencies": { - "@sentry/browser": "7.57.0", - "@sentry/types": "7.57.0", - "@sentry/utils": "7.57.0", - "hoist-non-react-statics": "^3.3.2", - "tslib": "^2.4.1 || ^1.9.3" - }, - "engines": { - "node": ">=8" - }, - "peerDependencies": { - "react": "15.x || 16.x || 17.x || 18.x" - } - }, - "node_modules/@sentry/replay": { - "version": "7.57.0", - "resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.57.0.tgz", - "integrity": "sha512-pN4ryNS3J5EYbkXvR+O/+hseAJha7XDl8mPFtK0OGTHG10JzCi4tQJazblHQdpb5QBaMMPCeZ+isyfoQLDNXnw==", - "dependencies": { - "@sentry/core": "7.57.0", - "@sentry/types": "7.57.0", - "@sentry/utils": "7.57.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@sentry/types": { - "version": "7.57.0", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.57.0.tgz", - "integrity": "sha512-D7ifoUfxuVCUyktIr5Gc+jXUbtcUMmfHdTtTbf1XCZHua5mJceK9wtl3YCg3eq/HK2Ppd52BKnTzEcS5ZKQM+w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@sentry/utils": { - "version": "7.57.0", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.57.0.tgz", - "integrity": "sha512-YXrkMCiNklqkXctn4mKYkrzNCf/dfVcRUQrkXjeBC+PHXbcpPyaJgInNvztR7Skl8lE3JPGPN4v5XhLxK1bUUg==", - "dependencies": { - "@sentry/types": "7.57.0", - "tslib": "^2.4.1 || ^1.9.3" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@socket.io/component-emitter": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", - "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" - }, - "node_modules/@tootallnate/once": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", - "engines": { - "node": ">= 10" - } - }, - "node_modules/@types/babel__core": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz", - "integrity": "sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", - "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", - "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", - "dev": true, - "dependencies": { - "@babel/types": "^7.3.0" - } - }, - "node_modules/@types/eslint": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.37.0.tgz", - "integrity": "sha512-Piet7dG2JBuDIfohBngQ3rCt7MgO9xCO4xIMKxBThCq5PNRB91IjlJ10eJVwfoNtvTErmxLzwBZ7rHZtbOMmFQ==", - "dev": true, - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.4", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", - "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", - "dev": true, - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "node_modules/@types/estree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz", - "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==", - "dev": true - }, - "node_modules/@types/hast": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.4.tgz", - "integrity": "sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/is-hotkey": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/@types/is-hotkey/-/is-hotkey-0.1.7.tgz", - "integrity": "sha512-yB5C7zcOM7idwYZZ1wKQ3pTfjA9BbvFqRWvKB46GFddxnJtHwi/b9y84ykQtxQPg5qhdpg4Q/kWU3EGoCTmLzQ==" - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", - "dev": true - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", - "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "*", - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, - "node_modules/@types/lodash": { - "version": "4.14.194", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.194.tgz", - "integrity": "sha512-r22s9tAS7imvBt2lyHC9B8AGwWnXaYb1tY09oyLkXDs4vArpYJzw09nj8MLx5VfciBPGIb+ZwG0ssYnEPJxn/g==" - }, - "node_modules/@types/mdast": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.11.tgz", - "integrity": "sha512-Y/uImid8aAwrEA24/1tcRZwpxX3pIFTSilcNDKSPn+Y2iDywSEachzRuvgAYYLR3wpGXAsMbv5lvKLDZLeYPAw==", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/minimist": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", - "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==" - }, - "node_modules/@types/node": { - "version": "18.15.12", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.12.tgz", - "integrity": "sha512-Wha1UwsB3CYdqUm2PPzh/1gujGCNtWVUYF0mB00fJFoR4gTyWTDPjSm+zBF787Ahw8vSGgBja90MkgFwvB86Dg==", - "dev": true - }, - "node_modules/@types/normalize-package-data": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", - "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==" - }, - "node_modules/@types/prop-types": { - "version": "15.7.5", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", - "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", - "peer": true - }, - "node_modules/@types/react": { - "version": "18.2.14", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.14.tgz", - "integrity": "sha512-A0zjq+QN/O0Kpe30hA1GidzyFjatVvrpIvWLxD+xv67Vt91TWWgco9IvrJBkeyHm1trGaFS/FSGqPlhyeZRm0g==", - "peer": true, - "dependencies": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" - } - }, - "node_modules/@types/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" - }, - "node_modules/@types/scheduler": { - "version": "0.16.3", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", - "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==", - "peer": true - }, - "node_modules/@types/stack-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", - "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==", - "dev": true - }, - "node_modules/@types/triple-beam": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.2.tgz", - "integrity": "sha512-txGIh+0eDFzKGC25zORnswy+br1Ha7hj5cMVwKIU7+s0U2AxxJru/jZSMU6OC9MJWP6+pc/hc6ZjyZShpsyY2g==" - }, - "node_modules/@types/unist": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", - "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==" - }, - "node_modules/@types/yargs": { - "version": "13.0.12", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.12.tgz", - "integrity": "sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", - "dev": true - }, - "node_modules/@webassemblyjs/ast": { - "version": "1.11.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.5.tgz", - "integrity": "sha512-LHY/GSAZZRpsNQH+/oHqhRQ5FT7eoULcBqgfyTB5nQHogFnK3/7QoN7dLnwSE/JkUAF0SrRuclT7ODqMFtWxxQ==", - "dev": true, - "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.5", - "@webassemblyjs/helper-wasm-bytecode": "1.11.5" - } - }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.5.tgz", - "integrity": "sha512-1j1zTIC5EZOtCplMBG/IEwLtUojtwFVwdyVMbL/hwWqbzlQoJsWCOavrdnLkemwNoC/EOwtUFch3fuo+cbcXYQ==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.5.tgz", - "integrity": "sha512-L65bDPmfpY0+yFrsgz8b6LhXmbbs38OnwDCf6NpnMUYqa+ENfE5Dq9E42ny0qz/PdR0LJyq/T5YijPnU8AXEpA==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.11.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.5.tgz", - "integrity": "sha512-fDKo1gstwFFSfacIeH5KfwzjykIE6ldh1iH9Y/8YkAZrhmu4TctqYjSh7t0K2VyDSXOZJ1MLhht/k9IvYGcIxg==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.5.tgz", - "integrity": "sha512-DhykHXM0ZABqfIGYNv93A5KKDw/+ywBFnuWybZZWcuzWHfbp21wUfRkbtz7dMGwGgT4iXjWuhRMA2Mzod6W4WA==", - "dev": true, - "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.5", - "@webassemblyjs/helper-api-error": "1.11.5", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.5.tgz", - "integrity": "sha512-oC4Qa0bNcqnjAowFn7MPCETQgDYytpsfvz4ujZz63Zu/a/v71HeCAAmZsgZ3YVKec3zSPYytG3/PrRCqbtcAvA==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.11.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.5.tgz", - "integrity": "sha512-uEoThA1LN2NA+K3B9wDo3yKlBfVtC6rh0i4/6hvbz071E8gTNZD/pT0MsBf7MeD6KbApMSkaAK0XeKyOZC7CIA==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.5", - "@webassemblyjs/helper-buffer": "1.11.5", - "@webassemblyjs/helper-wasm-bytecode": "1.11.5", - "@webassemblyjs/wasm-gen": "1.11.5" - } - }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.5.tgz", - "integrity": "sha512-37aGq6qVL8A8oPbPrSGMBcp38YZFXcHfiROflJn9jxSdSMMM5dS5P/9e2/TpaJuhE+wFrbukN2WI6Hw9MH5acg==", - "dev": true, - "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.11.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.5.tgz", - "integrity": "sha512-ajqrRSXaTJoPW+xmkfYN6l8VIeNnR4vBOTQO9HzR7IygoCcKWkICbKFbVTNMjMgMREqXEr0+2M6zukzM47ZUfQ==", - "dev": true, - "dependencies": { - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.11.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.5.tgz", - "integrity": "sha512-WiOhulHKTZU5UPlRl53gHR8OxdGsSOxqfpqWeA2FmcwBMaoEdz6b2x2si3IwC9/fSPLfe8pBMRTHVMk5nlwnFQ==", - "dev": true - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.11.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.5.tgz", - "integrity": "sha512-C0p9D2fAu3Twwqvygvf42iGCQ4av8MFBLiTb+08SZ4cEdwzWx9QeAHDo1E2k+9s/0w1DM40oflJOpkZ8jW4HCQ==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.5", - "@webassemblyjs/helper-buffer": "1.11.5", - "@webassemblyjs/helper-wasm-bytecode": "1.11.5", - "@webassemblyjs/helper-wasm-section": "1.11.5", - "@webassemblyjs/wasm-gen": "1.11.5", - "@webassemblyjs/wasm-opt": "1.11.5", - "@webassemblyjs/wasm-parser": "1.11.5", - "@webassemblyjs/wast-printer": "1.11.5" - } - }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.11.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.5.tgz", - "integrity": "sha512-14vteRlRjxLK9eSyYFvw1K8Vv+iPdZU0Aebk3j6oB8TQiQYuO6hj9s4d7qf6f2HJr2khzvNldAFG13CgdkAIfA==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.5", - "@webassemblyjs/helper-wasm-bytecode": "1.11.5", - "@webassemblyjs/ieee754": "1.11.5", - "@webassemblyjs/leb128": "1.11.5", - "@webassemblyjs/utf8": "1.11.5" - } - }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.11.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.5.tgz", - "integrity": "sha512-tcKwlIXstBQgbKy1MlbDMlXaxpucn42eb17H29rawYLxm5+MsEmgPzeCP8B1Cl69hCice8LeKgZpRUAPtqYPgw==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.5", - "@webassemblyjs/helper-buffer": "1.11.5", - "@webassemblyjs/wasm-gen": "1.11.5", - "@webassemblyjs/wasm-parser": "1.11.5" - } - }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.11.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.5.tgz", - "integrity": "sha512-SVXUIwsLQlc8srSD7jejsfTU83g7pIGr2YYNb9oHdtldSxaOhvA5xwvIiWIfcX8PlSakgqMXsLpLfbbJ4cBYew==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.5", - "@webassemblyjs/helper-api-error": "1.11.5", - "@webassemblyjs/helper-wasm-bytecode": "1.11.5", - "@webassemblyjs/ieee754": "1.11.5", - "@webassemblyjs/leb128": "1.11.5", - "@webassemblyjs/utf8": "1.11.5" - } - }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.11.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.5.tgz", - "integrity": "sha512-f7Pq3wvg3GSPUPzR0F6bmI89Hdb+u9WXrSKc4v+N0aV0q6r42WoF92Jp2jEorBEBRoRNXgjp53nBniDXcqZYPA==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.5", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webpack-cli/configtest": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz", - "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==", - "dev": true, - "peerDependencies": { - "webpack": "4.x.x || 5.x.x", - "webpack-cli": "4.x.x" - } - }, - "node_modules/@webpack-cli/info": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz", - "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==", - "dev": true, - "dependencies": { - "envinfo": "^7.7.3" - }, - "peerDependencies": { - "webpack-cli": "4.x.x" - } - }, - "node_modules/@webpack-cli/serve": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz", - "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==", - "dev": true, - "peerDependencies": { - "webpack-cli": "4.x.x" - }, - "peerDependenciesMeta": { - "webpack-dev-server": { - "optional": true - } - } - }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true - }, - "node_modules/@yarnpkg/lockfile": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", - "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==" - }, - "node_modules/abab": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", - "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", - "dev": true - }, - "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "5.7.4", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", - "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-globals": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.4.tgz", - "integrity": "sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==", - "dev": true, - "dependencies": { - "acorn": "^6.0.1", - "acorn-walk": "^6.0.1" - } - }, - "node_modules/acorn-globals/node_modules/acorn": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", - "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-walk": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz", - "integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/aggregate-error": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-4.0.1.tgz", - "integrity": "sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w==", - "peer": true, - "dependencies": { - "clean-stack": "^4.0.0", - "indent-string": "^5.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/aggregate-error/node_modules/indent-string": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", - "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/ansi-align": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", - "integrity": "sha512-TdlOggdA/zURfMYa7ABC66j+oqfMew58KpJMbUlH3bcZP1b+cBHIHDDn5uH9INsxrHBPjsqM0tDB4jPTF/vgJA==", - "dev": true, - "dependencies": { - "string-width": "^2.0.0" - } - }, - "node_modules/ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/ansicolors": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz", - "integrity": "sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==", - "peer": true - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "optional": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "peer": true - }, - "node_modules/argv-formatter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/argv-formatter/-/argv-formatter-1.0.0.tgz", - "integrity": "sha512-F2+Hkm9xFaRg+GkaNnbwXNDV5O6pnCFEmqyhvfC/Ic5LbgOWjJh3L+mN/s91rxVL3znE7DYVpW0GJFT+4YBgWw==", - "peer": true - }, - "node_modules/arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", - "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "is-array-buffer": "^3.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", - "integrity": "sha512-H3LU5RLiSsGXPhN+Nipar0iR0IofH+8r89G2y1tBKxQ/agagKyAjhkAFDRBfodP2caPrNKHpAWNIM/c9yeL7uA==", - "dev": true - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" - }, - "node_modules/array-ify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", - "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==" - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/array.prototype.reduce": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.5.tgz", - "integrity": "sha512-kDdugMl7id9COE8R7MHF5jWk7Dqt/fs4Pv+JXoICnYwqpjjjbUurz6w5fT5IG6brLdJhv6/VoHB0H7oyIBXd+Q==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-array-method-boxes-properly": "^1.0.0", - "is-string": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/async": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" - }, - "node_modules/async-each": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.6.tgz", - "integrity": "sha512-c646jH1avxr+aVpndVMeAfYw7wAa6idufrlN3LPA4PmKS0QEGp6PIC9nwz0WQkkvBGAMEki3pFdtxaF39J9vvg==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ] - }, - "node_modules/async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", - "dev": true - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "node_modules/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true, - "bin": { - "atob": "bin/atob.js" - }, - "engines": { - "node": ">= 4.5.0" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", - "engines": { - "node": "*" - } - }, - "node_modules/aws4": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", - "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==" - }, - "node_modules/babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha512-XqYMR2dfdGMW+hd0IUZ2PwK+fGeFkOxZJ0wY+JaQAHzt1Zx8LcvpiZD2NiGkEG8qx0CfkAOr5xt76d1e8vG90g==", - "dev": true, - "peer": true, - "dependencies": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" - } - }, - "node_modules/babel-code-frame/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-code-frame/node_modules/ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-code-frame/node_modules/chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", - "dev": true, - "peer": true, - "dependencies": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-code-frame/node_modules/js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==", - "dev": true, - "peer": true - }, - "node_modules/babel-code-frame/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", - "dev": true, - "peer": true, - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-code-frame/node_modules/supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/babel-core": { - "version": "6.26.3", - "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", - "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", - "dev": true, - "peer": true, - "dependencies": { - "babel-code-frame": "^6.26.0", - "babel-generator": "^6.26.0", - "babel-helpers": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-register": "^6.26.0", - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "convert-source-map": "^1.5.1", - "debug": "^2.6.9", - "json5": "^0.5.1", - "lodash": "^4.17.4", - "minimatch": "^3.0.4", - "path-is-absolute": "^1.0.1", - "private": "^0.1.8", - "slash": "^1.0.0", - "source-map": "^0.5.7" - } - }, - "node_modules/babel-core/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "peer": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/babel-core/node_modules/json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha512-4xrs1aW+6N5DalkqSVA8fxh458CXvR99WU8WLKmq4v8eWAL86Xo3BVqyd3SkA9wEVjCMqyvvRRkshAdOnBp5rw==", - "dev": true, - "peer": true, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/babel-core/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true, - "peer": true - }, - "node_modules/babel-core/node_modules/slash": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "integrity": "sha512-3TYDR7xWt4dIqV2JauJr+EJeW356RXijHeUlO+8djJ+uBXPn8/2dpzBc8yQhh583sVvc9CvFAeQVgijsH+PNNg==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-generator": { - "version": "6.26.1", - "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", - "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", - "dev": true, - "peer": true, - "dependencies": { - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "detect-indent": "^4.0.0", - "jsesc": "^1.3.0", - "lodash": "^4.17.4", - "source-map": "^0.5.7", - "trim-right": "^1.0.1" - } - }, - "node_modules/babel-generator/node_modules/jsesc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", - "integrity": "sha512-Mke0DA0QjUWuJlhsE0ZPPhYiJkRap642SmI/4ztCFaUs6V2AiH1sfecc+57NgaryfAA2VR3v6O+CSjC1jZJKOA==", - "dev": true, - "peer": true, - "bin": { - "jsesc": "bin/jsesc" - } - }, - "node_modules/babel-helpers": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", - "integrity": "sha512-n7pFrqQm44TCYvrCDb0MqabAF+JUBq+ijBvNMUxpkLjJaAu32faIexewMumrH5KLLJ1HDyT0PTEqRyAe/GwwuQ==", - "dev": true, - "peer": true, - "dependencies": { - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" - } - }, - "node_modules/babel-jest": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.9.0.tgz", - "integrity": "sha512-ntuddfyiN+EhMw58PTNL1ph4C9rECiQXjI4nMMBKBaNjXvqLdkXpPRcMSr4iyBrJg/+wz9brFUD6RhOAT6r4Iw==", - "dev": true, - "dependencies": { - "@jest/transform": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/babel__core": "^7.1.0", - "babel-plugin-istanbul": "^5.1.0", - "babel-preset-jest": "^24.9.0", - "chalk": "^2.4.2", - "slash": "^2.0.0" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-loader": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.3.0.tgz", - "integrity": "sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q==", - "dev": true, - "dependencies": { - "find-cache-dir": "^3.3.1", - "loader-utils": "^2.0.0", - "make-dir": "^3.1.0", - "schema-utils": "^2.6.5" - }, - "engines": { - "node": ">= 8.9" - }, - "peerDependencies": { - "@babel/core": "^7.0.0", - "webpack": ">=2" - } - }, - "node_modules/babel-loader/node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/babel-messages": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", - "integrity": "sha512-Bl3ZiA+LjqaMtNYopA9TYE9HP1tQ+E5dLxE0XrAzcIJeK2UqF0/EaqXwBn9esd4UmTfEab+P+UYQ1GnioFIb/w==", - "dev": true, - "peer": true, - "dependencies": { - "babel-runtime": "^6.22.0" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz", - "integrity": "sha512-5LphC0USA8t4i1zCtjbbNb6jJj/9+X6P37Qfirc/70EQ34xKlMW+a1RHGwxGI+SwWpNwZ27HqvzAobeqaXwiZw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "find-up": "^3.0.0", - "istanbul-lib-instrument": "^3.3.0", - "test-exclude": "^5.2.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.9.0.tgz", - "integrity": "sha512-2EMA2P8Vp7lG0RAzr4HXqtYwacfMErOuv1U3wrvxHX6rD1sV6xS3WXG3r8TRQ2r6w8OhvSdWt+z41hQNwNm3Xw==", - "dev": true, - "dependencies": { - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", - "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.17.7", - "@babel/helper-define-polyfill-provider": "^0.3.3", - "semver": "^6.1.1" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz", - "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==", - "dev": true, - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.3", - "core-js-compat": "^3.25.1" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", - "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", - "dev": true, - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/babel-plugin-transform-remove-console": { - "version": "6.9.4", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-remove-console/-/babel-plugin-transform-remove-console-6.9.4.tgz", - "integrity": "sha512-88blrUrMX3SPiGkT1GnvVY8E/7A+k6oj3MNvUtTIxJflFzXTw1bHkuJ/y039ouhFMp2prRn5cQGzokViYi1dsg==", - "dev": true - }, - "node_modules/babel-preset-jest": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-24.9.0.tgz", - "integrity": "sha512-izTUuhE4TMfTRPF92fFwD2QfdXaZW08qvWTFCI51V8rW5x00UuPgc3ajRoWofXOuxjfcOM5zzSYsQS3H8KGCAg==", - "dev": true, - "dependencies": { - "@babel/plugin-syntax-object-rest-spread": "^7.0.0", - "babel-plugin-jest-hoist": "^24.9.0" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-register": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", - "integrity": "sha512-veliHlHX06wjaeY8xNITbveXSiI+ASFnOqvne/LaIJIqOWi2Ogmj91KOugEz/hoh/fwMhXNBJPCv8Xaz5CyM4A==", - "dev": true, - "peer": true, - "dependencies": { - "babel-core": "^6.26.0", - "babel-runtime": "^6.26.0", - "core-js": "^2.5.0", - "home-or-tmp": "^2.0.0", - "lodash": "^4.17.4", - "mkdirp": "^0.5.1", - "source-map-support": "^0.4.15" - } - }, - "node_modules/babel-register/node_modules/core-js": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", - "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", - "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", - "dev": true, - "hasInstallScript": true, - "peer": true - }, - "node_modules/babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==", - "dev": true, - "peer": true, - "dependencies": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" - } - }, - "node_modules/babel-runtime/node_modules/core-js": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", - "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", - "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", - "dev": true, - "hasInstallScript": true, - "peer": true - }, - "node_modules/babel-runtime/node_modules/regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", - "dev": true, - "peer": true - }, - "node_modules/babel-template": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", - "integrity": "sha512-PCOcLFW7/eazGUKIoqH97sO9A2UYMahsn/yRQ7uOk37iutwjq7ODtcTNF+iFDSHNfkctqsLRjLP7URnOx0T1fg==", - "dev": true, - "peer": true, - "dependencies": { - "babel-runtime": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "lodash": "^4.17.4" - } - }, - "node_modules/babel-traverse": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", - "integrity": "sha512-iSxeXx7apsjCHe9c7n8VtRXGzI2Bk1rBSOJgCCjfyXb6v1aCqE1KSEpq/8SXuVN8Ka/Rh1WDTF0MDzkvTA4MIA==", - "dev": true, - "peer": true, - "dependencies": { - "babel-code-frame": "^6.26.0", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "debug": "^2.6.8", - "globals": "^9.18.0", - "invariant": "^2.2.2", - "lodash": "^4.17.4" - } - }, - "node_modules/babel-traverse/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "peer": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/babel-traverse/node_modules/globals": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-traverse/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true, - "peer": true - }, - "node_modules/babel-types": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", - "integrity": "sha512-zhe3V/26rCWsEZK8kZN+HaQj5yQ1CilTObixFzKW1UWjqG7618Twz6YEsCnjfg5gBcJh02DrpCkS9h98ZqDY+g==", - "dev": true, - "peer": true, - "dependencies": { - "babel-runtime": "^6.26.0", - "esutils": "^2.0.2", - "lodash": "^4.17.4", - "to-fast-properties": "^1.0.3" - } - }, - "node_modules/babel-types/node_modules/to-fast-properties": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", - "integrity": "sha512-lxrWP8ejsq+7E3nNjwYmUBMAgjMTZoTI+sdBOpvNyijeDLa29LUn9QaoXAHv4+Z578hbmHHJKZknzxVtvo77og==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-watch": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/babel-watch/-/babel-watch-2.0.8.tgz", - "integrity": "sha512-dmuUumB5Mv0SOfDuQvJ2Dmx/4EYi76spzf/8wpH7D1DnL+3GEm4uIqY+/N+4CetAh0HLSZVl894bDD9pDdfzKw==", - "dev": true, - "dependencies": { - "chokidar": "^1.4.3", - "commander": "^2.9.0", - "lodash.debounce": "^4.0.8", - "source-map-support": "^0.4.0" - }, - "bin": { - "babel-watch": "babel-watch.js" - }, - "peerDependencies": { - "babel-core": "6.5.1 - 6.6.0 || ^6.6.x" - } - }, - "node_modules/babel-watch/node_modules/anymatch": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", - "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", - "dev": true, - "dependencies": { - "micromatch": "^2.1.5", - "normalize-path": "^2.0.0" - } - }, - "node_modules/babel-watch/node_modules/anymatch/node_modules/micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha512-LnU2XFEk9xxSJ6rfgAry/ty5qwUTyHYOBU0g4R6tIw5ljwgGIBmiKhRWLw5NpMOnrgUNcDJ4WMp8rl3sYVHLNA==", - "dev": true, - "dependencies": { - "arr-diff": "^2.0.0", - "array-unique": "^0.2.1", - "braces": "^1.8.2", - "expand-brackets": "^0.1.4", - "extglob": "^0.3.1", - "filename-regex": "^2.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.1", - "kind-of": "^3.0.2", - "normalize-path": "^2.0.1", - "object.omit": "^2.0.0", - "parse-glob": "^3.0.4", - "regex-cache": "^0.4.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-watch/node_modules/arr-diff": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "integrity": "sha512-dtXTVMkh6VkEEA7OhXnN1Ecb8aAGFdZ1LFxtOCoqj4qkyOJMt7+qs6Ahdy6p/NQCPYsRSXXivhSB/J5E9jmYKA==", - "dev": true, - "dependencies": { - "arr-flatten": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-watch/node_modules/array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha512-G2n5bG5fSUCpnsXz4+8FUkYsGPkNfLn9YvS66U5qbTIXI2Ynnlo4Bi42bWv+omKUCqz+ejzfClwne0alJWJPhg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-watch/node_modules/binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-watch/node_modules/braces": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "integrity": "sha512-xU7bpz2ytJl1bH9cgIurjpg/n8Gohy9GTw81heDYLJQ4RU60dlyJsa+atVF2pI0yMMvKxI9HkKwjePCj5XI1hw==", - "dev": true, - "dependencies": { - "expand-range": "^1.8.1", - "preserve": "^0.2.0", - "repeat-element": "^1.1.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-watch/node_modules/chokidar": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", - "integrity": "sha512-mk8fAWcRUOxY7btlLtitj3A45jOwSAxH4tOFOoEGbVsl6cL6pPMWUy7dwZ/canfj3QEdP6FHSnf/l1c6/WkzVg==", - "deprecated": "Chokidar 2 will break on node v14+. Upgrade to chokidar 3 with 15x less dependencies.", - "dev": true, - "dependencies": { - "anymatch": "^1.3.0", - "async-each": "^1.0.0", - "glob-parent": "^2.0.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^2.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0" - }, - "optionalDependencies": { - "fsevents": "^1.0.0" - } - }, - "node_modules/babel-watch/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "node_modules/babel-watch/node_modules/expand-brackets": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "integrity": "sha512-hxx03P2dJxss6ceIeri9cmYOT4SRs3Zk3afZwWpOsRqLqprhTR8u++SlC+sFGsQr7WGFPdMF7Gjc1njDLDK6UA==", - "dev": true, - "dependencies": { - "is-posix-bracket": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-watch/node_modules/extglob": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "integrity": "sha512-1FOj1LOwn42TMrruOHGt18HemVnbwAmAak7krWk+wa93KXxGbK+2jpezm+ytJYDaBX0/SPLZFHKM7m+tKobWGg==", - "dev": true, - "dependencies": { - "is-extglob": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-watch/node_modules/fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "deprecated": "fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "dependencies": { - "bindings": "^1.5.0", - "nan": "^2.12.1" - }, - "engines": { - "node": ">= 4.0" - } - }, - "node_modules/babel-watch/node_modules/glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha512-JDYOvfxio/t42HKdxkAYaCiBN7oYiuxykOxKxdaUW5Qn0zaYN3gRQWolrwdnf0shM9/EP0ebuuTmyoXNr1cC5w==", - "dev": true, - "dependencies": { - "is-glob": "^2.0.0" - } - }, - "node_modules/babel-watch/node_modules/is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==", - "dev": true, - "dependencies": { - "binary-extensions": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-watch/node_modules/is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-watch/node_modules/is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg==", - "dev": true, - "dependencies": { - "is-extglob": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-watch/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-watch/node_modules/normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", - "dev": true, - "dependencies": { - "remove-trailing-separator": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-watch/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/babel-watch/node_modules/readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/babel-watch/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/babel-watch/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/babylon": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", - "dev": true, - "peer": true, - "bin": { - "babylon": "bin/babylon.js" - } - }, - "node_modules/bail": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz", - "integrity": "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "node_modules/base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, - "dependencies": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/base/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", - "dev": true, - "dependencies": { - "is-descriptor": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "dependencies": { - "tweetnacl": "^0.14.3" - } - }, - "node_modules/before-after-hook": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", - "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==" - }, - "node_modules/big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, - "optional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "dev": true, - "optional": true, - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, - "node_modules/body-parser": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", - "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" - }, - "node_modules/bottleneck": { - "version": "2.19.5", - "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", - "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==" - }, - "node_modules/boxen": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", - "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", - "dev": true, - "dependencies": { - "ansi-align": "^2.0.0", - "camelcase": "^4.0.0", - "chalk": "^2.0.1", - "cli-boxes": "^1.0.0", - "string-width": "^2.0.0", - "term-size": "^1.2.0", - "widest-line": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/boxen/node_modules/camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browser-process-hrtime": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", - "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", - "dev": true - }, - "node_modules/browser-resolve": { - "version": "1.11.3", - "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", - "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", - "dev": true, - "dependencies": { - "resolve": "1.1.7" - } - }, - "node_modules/browser-resolve/node_modules/resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg==", - "dev": true - }, - "node_modules/browserslist": { - "version": "4.21.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", - "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001449", - "electron-to-chromium": "^1.4.284", - "node-releases": "^2.0.8", - "update-browserslist-db": "^1.0.10" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dev": true, - "dependencies": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase-keys": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", - "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", - "dependencies": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001480", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001480.tgz", - "integrity": "sha512-q7cpoPPvZYgtyC4VaBSN0Bt+PJ4c4EYRf0DrduInOz2SkFpHD5p3LnvEpqBp7UnJn+8x1Ogl1s38saUxe+ihQQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ] - }, - "node_modules/capture-exit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", - "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", - "dev": true, - "dependencies": { - "rsvp": "^4.8.4" - }, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/capture-stack-trace": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.2.tgz", - "integrity": "sha512-X/WM2UQs6VMHUtjUDnZTRI+i1crWteJySFzr9UpGoQa4WQffXVTTXuekjl7TjZRlcF2XfjgITT0HxZ9RnxeT0w==", - "dev": true, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cardinal": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-2.1.1.tgz", - "integrity": "sha512-JSr5eOgoEymtYHBjNWyjrMqet9Am2miJhlfKNdqLp6zoeAh0KN5dRAcxlecj5mAJrmQomgiOBj35xHLrFjqBpw==", - "peer": true, - "dependencies": { - "ansicolors": "~0.3.2", - "redeyed": "~2.1.0" - }, - "bin": { - "cdl": "bin/cdl.js" - } - }, - "node_modules/caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" - }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/character-entities": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", - "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-legacy": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", - "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-reference-invalid": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", - "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/cheerio": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz", - "integrity": "sha512-8/MzidM6G/TgRelkzDG13y3Y9LxBjCb+8yOEZ9+wwq5gVF2w2pV0wmHvjfT0RvuxGyR7UEuK36r+yYMbT4uKgA==", - "dependencies": { - "css-select": "~1.2.0", - "dom-serializer": "~0.1.0", - "entities": "~1.1.1", - "htmlparser2": "^3.9.1", - "lodash.assignin": "^4.0.9", - "lodash.bind": "^4.1.4", - "lodash.defaults": "^4.0.1", - "lodash.filter": "^4.4.0", - "lodash.flatten": "^4.2.0", - "lodash.foreach": "^4.3.0", - "lodash.map": "^4.4.0", - "lodash.merge": "^4.4.0", - "lodash.pick": "^4.2.1", - "lodash.reduce": "^4.4.0", - "lodash.reject": "^4.4.0", - "lodash.some": "^4.4.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "optional": true, - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", - "dev": true, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" - }, - "node_modules/class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dev": true, - "dependencies": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", - "dev": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/classnames": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", - "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" - }, - "node_modules/clean-stack": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-4.2.0.tgz", - "integrity": "sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg==", - "peer": true, - "dependencies": { - "escape-string-regexp": "5.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/clean-stack/node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-boxes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", - "integrity": "sha512-3Fo5wu8Ytle8q9iCzS4D2MWVL2X7JVWRiS1BnXbTFDhS9c/REkM9vd1AmabsoZoY5/dGi5TT9iKL8Kb6DeBRQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cli-table3": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", - "integrity": "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==", - "dependencies": { - "string-width": "^4.2.0" - }, - "engines": { - "node": "10.* || >= 12.*" - }, - "optionalDependencies": { - "@colors/colors": "1.5.0" - } - }, - "node_modules/cli-table3/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-table3/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "node_modules/cli-table3/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-table3/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-table3/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "dependencies": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, - "node_modules/cliui/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "dev": true, - "dependencies": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/clsx": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", - "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==", - "dev": true, - "dependencies": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/color": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", - "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", - "dependencies": { - "color-convert": "^1.9.3", - "color-string": "^1.6.0" - } - }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true - }, - "node_modules/colorspace": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", - "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", - "dependencies": { - "color": "^3.1.3", - "text-hex": "1.0.x" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/comma-separated-tokens": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", - "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", - "dev": true - }, - "node_modules/compare-func": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", - "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", - "dependencies": { - "array-ify": "^1.0.0", - "dot-prop": "^5.1.0" - } - }, - "node_modules/compare-func/node_modules/dot-prop": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", - "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", - "dependencies": { - "is-obj": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/compare-func/node_modules/is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true - }, - "node_modules/compute-scroll-into-view": { - "version": "1.0.20", - "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.20.tgz", - "integrity": "sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "node_modules/config-chain": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", - "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", - "dependencies": { - "ini": "^1.3.4", - "proto-list": "~1.2.1" - } - }, - "node_modules/configstore": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.5.tgz", - "integrity": "sha512-nlOhI4+fdzoK5xmJ+NY+1gZK56bwEaWZr8fYuXohZ9Vkc1o3a4T/R3M+yE/w7x/ZVJ1zF8c+oaOvF0dztdUgmA==", - "dev": true, - "dependencies": { - "dot-prop": "^4.2.1", - "graceful-fs": "^4.1.2", - "make-dir": "^1.0.0", - "unique-string": "^1.0.0", - "write-file-atomic": "^2.0.0", - "xdg-basedir": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/configstore/node_modules/make-dir": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", - "dev": true, - "dependencies": { - "pify": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/configstore/node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/conventional-changelog-angular": { - "version": "5.0.13", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.13.tgz", - "integrity": "sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA==", - "dependencies": { - "compare-func": "^2.0.0", - "q": "^1.5.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-changelog-conventionalcommits": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-6.1.0.tgz", - "integrity": "sha512-3cS3GEtR78zTfMzk0AizXKKIdN4OvSh7ibNz6/DPbhWWQu7LqE/8+/GqSodV+sywUR2gpJAdP/1JFf4XtN7Zpw==", - "dependencies": { - "compare-func": "^2.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/conventional-changelog-writer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-5.0.1.tgz", - "integrity": "sha512-5WsuKUfxW7suLblAbFnxAcrvf6r+0b7GvNaWUwUIk0bXMnENP/PEieGKVUQrjPqwPT4o3EPAASBXiY6iHooLOQ==", - "dependencies": { - "conventional-commits-filter": "^2.0.7", - "dateformat": "^3.0.0", - "handlebars": "^4.7.7", - "json-stringify-safe": "^5.0.1", - "lodash": "^4.17.15", - "meow": "^8.0.0", - "semver": "^6.0.0", - "split": "^1.0.0", - "through2": "^4.0.0" - }, - "bin": { - "conventional-changelog-writer": "cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-commits-filter": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.7.tgz", - "integrity": "sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA==", - "dependencies": { - "lodash.ismatch": "^4.4.0", - "modify-values": "^1.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-commits-parser": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.4.tgz", - "integrity": "sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q==", - "dependencies": { - "is-text-path": "^1.0.1", - "JSONStream": "^1.0.4", - "lodash": "^4.17.15", - "meow": "^8.0.0", - "split2": "^3.0.0", - "through2": "^4.0.0" - }, - "bin": { - "conventional-commits-parser": "cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, - "node_modules/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-parser": { - "version": "1.4.6", - "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz", - "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==", - "dependencies": { - "cookie": "0.4.1", - "cookie-signature": "1.0.6" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" - }, - "node_modules/copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/core-js": { - "version": "3.31.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.31.1.tgz", - "integrity": "sha512-2sKLtfq1eFST7l7v62zaqXacPc7uG8ZAya8ogijLhTtaKNcpzpB4TMoTw2Si+8GYKRwFPMMtUT0263QFWFfqyQ==", - "hasInstallScript": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/core-js-compat": { - "version": "3.30.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.30.1.tgz", - "integrity": "sha512-d690npR7MC6P0gq4npTl5n2VQeNAmUrJ90n+MHiKS7W2+xno4o3F5GDEuylSdi6EJ3VssibSGXOa1r3YXD3Mhw==", - "dev": true, - "dependencies": { - "browserslist": "^4.21.5" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" - }, - "node_modules/cosmiconfig": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.2.0.tgz", - "integrity": "sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==", - "peer": true, - "dependencies": { - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - } - }, - "node_modules/cosmiconfig/node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "peer": true - }, - "node_modules/cosmiconfig/node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "peer": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cosmiconfig/node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/create-error-class": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", - "integrity": "sha512-gYTKKexFO3kh200H1Nit76sRwRtOY32vQd3jpAQKpLtZqyNsSQNfI4N7o3eP2wUjV35pTWKRYqFUDBvUha/Pkw==", - "dev": true, - "dependencies": { - "capture-stack-trace": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "engines": { - "node": ">=4.8" - } - }, - "node_modules/cross-spawn/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/crypto-random-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", - "integrity": "sha512-GsVpkFPlycH7/fRR7Dhcmnoii54gV1nz7y4CWyeFS14N+JVBBhY+r8amRHE4BwSYal7BPTDp8isvAlCxyFt3Hg==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/css-loader": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.6.0.tgz", - "integrity": "sha512-M5lSukoWi1If8dhQAUCvj4H8vUt3vOnwbQBH9DdTm/s4Ym2B/3dPMtYZeJmq7Q3S3Pa+I94DcZ7pc9bP14cWIQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.3.1", - "cssesc": "^3.0.0", - "icss-utils": "^4.1.1", - "loader-utils": "^1.2.3", - "normalize-path": "^3.0.0", - "postcss": "^7.0.32", - "postcss-modules-extract-imports": "^2.0.0", - "postcss-modules-local-by-default": "^3.0.2", - "postcss-modules-scope": "^2.2.0", - "postcss-modules-values": "^3.0.0", - "postcss-value-parser": "^4.1.0", - "schema-utils": "^2.7.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">= 8.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" - } - }, - "node_modules/css-loader/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/css-loader/node_modules/loader-utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", - "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", - "dev": true, - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/css-modules-require-hook": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/css-modules-require-hook/-/css-modules-require-hook-4.2.3.tgz", - "integrity": "sha512-y3eGHAqmDl8JQdr1LYTwBkMxy7CSHCMy9YwpSjDqtGqCCEy9lKq/x/nmHVs+G7C1+xXmyIXjRu0q/MMn8w01mg==", - "dependencies": { - "debug": "^2.2.0", - "generic-names": "^1.0.1", - "glob-to-regexp": "^0.3.0", - "icss-replace-symbols": "^1.0.2", - "lodash": "^4.3.0", - "postcss": "^6.0.1", - "postcss-modules-extract-imports": "^1.0.0", - "postcss-modules-local-by-default": "^1.0.1", - "postcss-modules-resolve-imports": "^1.3.0", - "postcss-modules-scope": "^1.0.0", - "postcss-modules-values": "^1.1.1", - "seekout": "^1.0.1" - }, - "engines": { - "node": ">= 4" - } - }, - "node_modules/css-modules-require-hook/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/css-modules-require-hook/node_modules/icss-utils": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-3.0.1.tgz", - "integrity": "sha512-ANhVLoEfe0KoC9+z4yiTaXOneB49K6JIXdS+yAgH0NERELpdIT7kkj2XxUPuHafeHnn8umXnECSpsfk1RTaUew==", - "dependencies": { - "postcss": "^6.0.2" - } - }, - "node_modules/css-modules-require-hook/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/css-modules-require-hook/node_modules/postcss": { - "version": "6.0.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", - "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", - "dependencies": { - "chalk": "^2.4.1", - "source-map": "^0.6.1", - "supports-color": "^5.4.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/css-modules-require-hook/node_modules/postcss-modules-extract-imports": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.1.tgz", - "integrity": "sha512-6jt9XZwUhwmRUhb/CkyJY020PYaPJsCyt3UjbaWo6XEbH/94Hmv6MP7fG2C5NDU/BcHzyGYxNtHvM+LTf9HrYw==", - "dependencies": { - "postcss": "^6.0.1" - } - }, - "node_modules/css-modules-require-hook/node_modules/postcss-modules-local-by-default": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz", - "integrity": "sha512-X4cquUPIaAd86raVrBwO8fwRfkIdbwFu7CTfEOjiZQHVQwlHRSkTgH5NLDmMm5+1hQO8u6dZ+TOOJDbay1hYpA==", - "dependencies": { - "css-selector-tokenizer": "^0.7.0", - "postcss": "^6.0.1" - } - }, - "node_modules/css-modules-require-hook/node_modules/postcss-modules-resolve-imports": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/postcss-modules-resolve-imports/-/postcss-modules-resolve-imports-1.3.0.tgz", - "integrity": "sha512-9xECsLcuR1DRu7mVhK6GIkdEeg4sdZXbLEcuEkDh9mKiz+uxDBfDREYiVehINdW0UPF9gbHnb64ZQMuRsqqkDA==", - "dependencies": { - "css-selector-tokenizer": "^0.7.0", - "icss-utils": "^3.0.1", - "minimist": "^1.2.0" - }, - "engines": { - "node": ">= 4" - }, - "peerDependencies": { - "postcss": "^6.0.0" - } - }, - "node_modules/css-modules-require-hook/node_modules/postcss-modules-scope": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz", - "integrity": "sha512-LTYwnA4C1He1BKZXIx1CYiHixdSe9LWYVKadq9lK5aCCMkoOkFyZ7aigt+srfjlRplJY3gIol6KUNefdMQJdlw==", - "dependencies": { - "css-selector-tokenizer": "^0.7.0", - "postcss": "^6.0.1" - } - }, - "node_modules/css-modules-require-hook/node_modules/postcss-modules-values": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz", - "integrity": "sha512-i7IFaR9hlQ6/0UgFuqM6YWaCfA1Ej8WMg8A5DggnH1UGKJvTV/ugqq/KaULixzzOi3T/tF6ClBXcHGCzdd5unA==", - "dependencies": { - "icss-replace-symbols": "^1.1.0", - "postcss": "^6.0.1" - } - }, - "node_modules/css-modules-require-hook/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/css-select": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", - "integrity": "sha512-dUQOBoqdR7QwV90WysXPLXG5LO7nhYBgiWVfxF80DKPF8zx1t/pUd2FYy73emg3zrjtM6dzmYgbHKfV2rxiHQA==", - "dependencies": { - "boolbase": "~1.0.0", - "css-what": "2.1", - "domutils": "1.5.1", - "nth-check": "~1.0.1" - } - }, - "node_modules/css-selector-tokenizer": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.3.tgz", - "integrity": "sha512-jWQv3oCEL5kMErj4wRnK/OPoBi0D+P1FR2cDCKYPaMeD2eW3/mttav8HT4hT1CKopiJI/psEULjkClhvJo4Lvg==", - "dependencies": { - "cssesc": "^3.0.0", - "fastparse": "^1.1.2" - } - }, - "node_modules/css-what": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", - "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", - "engines": { - "node": "*" - } - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", - "dev": true - }, - "node_modules/cssstyle": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.4.0.tgz", - "integrity": "sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==", - "dev": true, - "dependencies": { - "cssom": "0.3.x" - } - }, - "node_modules/csstype": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", - "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==", - "peer": true - }, - "node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", - "dependencies": { - "assert-plus": "^1.0.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/data-urls": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz", - "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==", - "dev": true, - "dependencies": { - "abab": "^2.0.0", - "whatwg-mimetype": "^2.2.0", - "whatwg-url": "^7.0.0" - } - }, - "node_modules/data-urls/node_modules/whatwg-url": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", - "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", - "dev": true, - "dependencies": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" - } - }, - "node_modules/dateformat": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", - "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", - "engines": { - "node": "*" - } - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decamelize-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", - "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", - "dependencies": { - "decamelize": "^1.1.0", - "map-obj": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/decamelize-keys/node_modules/map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decode-uri-component": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", - "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", - "dev": true, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/deep-merge": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/deep-merge/-/deep-merge-1.0.0.tgz", - "integrity": "sha512-cVG7u0dA5OtrgliqM30OLE3OPrB59PjWEjtCG4Go78OI/f/kP6aK4J/cF4E2f0PpN7A1QmBVxh1dKJkrIU6jCQ==", - "dependencies": { - "xtend": "~1.0.3" - } - }, - "node_modules/define-properties": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", - "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", - "dev": true, - "dependencies": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dev": true, - "dependencies": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/del": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz", - "integrity": "sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==", - "dependencies": { - "globby": "^11.0.1", - "graceful-fs": "^4.2.4", - "is-glob": "^4.0.1", - "is-path-cwd": "^2.2.0", - "is-path-inside": "^3.0.2", - "p-map": "^4.0.0", - "rimraf": "^3.0.2", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/del/node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/del/node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "engines": { - "node": ">=6" - } - }, - "node_modules/del/node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/del/node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/del/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/del/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/deprecation": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", - "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/detect-indent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", - "integrity": "sha512-BDKtmHlOzwI7iRuEkhzsnPoi5ypEhWAJB5RvHWe1kMr06js3uK5B3734i3ui5Yd+wOJV1cpE4JnivPD283GU/A==", - "dev": true, - "peer": true, - "dependencies": { - "repeating": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/detect-newline": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", - "integrity": "sha512-CwffZFvlJffUg9zZA0uqrjQayUTC8ob94pnr5sFwaVv3IOmkfUHcWH+jXaQK3askE51Cqe8/9Ql/0uXNwqZ8Zg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/diff-match-patch": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz", - "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==" - }, - "node_modules/diff-sequences": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.9.0.tgz", - "integrity": "sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/dir-glob/node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/direction": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/direction/-/direction-1.0.4.tgz", - "integrity": "sha512-GYqKi1aH7PJXxdhTeZBFrg8vUBeKXi+cNprXsC1kpJcbcVnV9wBsrOu1cQEdG0WeQwlfHiy3XvnKfIrJ2R0NzQ==", - "bin": { - "direction": "cli.js" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/dom-serializer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", - "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", - "dependencies": { - "domelementtype": "^1.3.0", - "entities": "^1.1.1" - } - }, - "node_modules/domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" - }, - "node_modules/domexception": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", - "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", - "dev": true, - "dependencies": { - "webidl-conversions": "^4.0.2" - } - }, - "node_modules/domhandler": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", - "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", - "dependencies": { - "domelementtype": "1" - } - }, - "node_modules/dompurify": { - "version": "2.4.5", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.5.tgz", - "integrity": "sha512-jggCCd+8Iqp4Tsz0nIvpcb22InKEBrGz5dw3EQJMs8HPJDsKbFIO3STYtAvCfDx26Muevn1MHVI0XxjgFfmiSA==" - }, - "node_modules/domutils": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", - "integrity": "sha512-gSu5Oi/I+3wDENBsOWBiRK1eoGxcywYSqg3rR960/+EfY0CF4EX1VPkgHOZ3WiS/Jg2DtliF6BhWcHlfpYUcGw==", - "dependencies": { - "dom-serializer": "0", - "domelementtype": "1" - } - }, - "node_modules/dot-prop": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.1.tgz", - "integrity": "sha512-l0p4+mIuJIua0mhxGoh4a+iNL9bmeK5DvnSVQa6T0OhrVmaEa1XScX5Etc673FePCJOArq/4Pa2cLGODUWTPOQ==", - "dev": true, - "dependencies": { - "is-obj": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/double-ended-queue": { - "version": "2.1.0-0", - "resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz", - "integrity": "sha512-+BNfZ+deCo8hMNpDqDnvT+c0XpJ5cUa6mqYq89bho2Ifze4URTqRkcwR399hWoTrTkbZ/XJYDgP6rc7pRgffEQ==" - }, - "node_modules/draggabilly": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/draggabilly/-/draggabilly-3.0.0.tgz", - "integrity": "sha512-aEs+B6prbMZQMxc9lgTpCBfyCUhRur/VFucHhIOvlvvdARTj7TcDmX/cdOUtqbjJJUh7+agyJXR5Z6IFe1MxwQ==", - "dependencies": { - "get-size": "^3.0.0", - "unidragger": "^3.0.0" - } - }, - "node_modules/duplexer2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", - "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", - "peer": true, - "dependencies": { - "readable-stream": "^2.0.2" - } - }, - "node_modules/duplexer2/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "peer": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/duplexer2/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "peer": true - }, - "node_modules/duplexer2/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "peer": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/duplexer3": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz", - "integrity": "sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==", - "dev": true - }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", - "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" - }, - "node_modules/electron-to-chromium": { - "version": "1.4.368", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.368.tgz", - "integrity": "sha512-e2aeCAixCj9M7nJxdB/wDjO6mbYX+lJJxSJCXDzlr5YPGYVofuJwGN9nKg2o6wWInjX6XmxRinn3AeJMK81ltw==", - "dev": true - }, - "node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "node_modules/emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/enabled": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", - "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" - }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/engine.io-client": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.4.0.tgz", - "integrity": "sha512-GyKPDyoEha+XZ7iEqam49vz6auPnNJ9ZBfy89f+rMMas8AuiMWOZ9PVzu8xb9ZC6rafUqiGHSCfu22ih66E+1g==", - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1", - "engine.io-parser": "~5.0.3", - "ws": "~8.11.0", - "xmlhttprequest-ssl": "~2.0.0" - } - }, - "node_modules/engine.io-client/node_modules/ws": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", - "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/engine.io-parser": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz", - "integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/enhanced-resolve": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.13.0.tgz", - "integrity": "sha512-eyV8f0y1+bzyfh8xAwW/WTSZpLbjhqc4ne9eGSH4Zo2ejdyiNG9pU6mf9DG8a7+Auk6MFTlNOT4Y2y/9k8GKVg==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" - }, - "node_modules/env-ci": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/env-ci/-/env-ci-9.1.1.tgz", - "integrity": "sha512-Im2yEWeF4b2RAMAaWvGioXk6m0UNaIjD8hj28j2ij5ldnIFrDQT0+pzDvpbRkcjurhXhf/AsBKv8P2rtmGi9Aw==", - "peer": true, - "dependencies": { - "execa": "^7.0.0", - "java-properties": "^1.0.2" - }, - "engines": { - "node": "^16.14 || >=18" - } - }, - "node_modules/env-ci/node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "peer": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/env-ci/node_modules/execa": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", - "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", - "peer": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.1", - "human-signals": "^4.3.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^3.0.7", - "strip-final-newline": "^3.0.0" - }, - "engines": { - "node": "^14.18.0 || ^16.14.0 || >=18.0.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/env-ci/node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "peer": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/env-ci/node_modules/human-signals": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", - "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", - "peer": true, - "engines": { - "node": ">=14.18.0" - } - }, - "node_modules/env-ci/node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "peer": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/env-ci/node_modules/npm-run-path": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", - "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", - "peer": true, - "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/env-ci/node_modules/npm-run-path/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/env-ci/node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/env-ci/node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "peer": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/env-ci/node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/env-ci/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "peer": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/envinfo": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", - "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", - "dev": true, - "bin": { - "envinfo": "dist/cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-abstract": { - "version": "1.21.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz", - "integrity": "sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==", - "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-set-tostringtag": "^2.0.1", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.2.0", - "get-symbol-description": "^1.0.0", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.10", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.3", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", - "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.7", - "string.prototype.trimend": "^1.0.6", - "string.prototype.trimstart": "^1.0.6", - "typed-array-length": "^1.0.4", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.9" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-array-method-boxes-properly": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", - "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", - "dev": true - }, - "node_modules/es-module-lexer": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.2.1.tgz", - "integrity": "sha512-9978wrXM50Y4rTMmW5kXIC09ZdXQZqkE4mxhwkd8VbzsGkXGPgV4zWuqQJgCEzYngdo2dYDa0l8xhX4fkSwJSg==", - "dev": true - }, - "node_modules/es-set-tostringtag": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", - "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/escodegen": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", - "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", - "dev": true, - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=4.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, - "node_modules/escodegen/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/ev-emitter": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ev-emitter/-/ev-emitter-2.1.2.tgz", - "integrity": "sha512-jQ5Ql18hdCQ4qS+RCrbLfz1n+Pags27q5TwMKvZyhp5hh2UULUYZUy1keqj6k6SYsdqIYjnmz7xyyEY0V67B8Q==" - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/exec-sh": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.6.tgz", - "integrity": "sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w==", - "dev": true - }, - "node_modules/execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "dependencies": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==", - "dev": true, - "dependencies": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/expand-brackets/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", - "dev": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/expand-range": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", - "integrity": "sha512-AFASGfIlnIbkKPQwX1yHaDjFvh/1gyKJODme52V6IORh69uEYgZp0o9C+qsIGNVEiuuhQU0CSSl++Rlegg1qvA==", - "dev": true, - "dependencies": { - "fill-range": "^2.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-range/node_modules/fill-range": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", - "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", - "dev": true, - "dependencies": { - "is-number": "^2.1.0", - "isobject": "^2.0.0", - "randomatic": "^3.0.0", - "repeat-element": "^1.1.2", - "repeat-string": "^1.5.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-range/node_modules/is-number": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", - "integrity": "sha512-QUzH43Gfb9+5yckcrSA0VBDwEtDUchrk4F6tfJZQuNzDJbEDB9cZNzSfXGQ1jqmdDY/kl41lUOWM9syA8z8jlg==", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-range/node_modules/isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==", - "dev": true, - "dependencies": { - "isarray": "1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-range/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expect": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-24.9.0.tgz", - "integrity": "sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q==", - "dev": true, - "dependencies": { - "@jest/types": "^24.9.0", - "ansi-styles": "^3.2.0", - "jest-get-type": "^24.9.0", - "jest-matcher-utils": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-regex-util": "^24.9.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.1", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.5.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/express-winston": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/express-winston/-/express-winston-4.2.0.tgz", - "integrity": "sha512-EMD74g63nVHi7pFleQw7KHCxiA1pjF5uCwbCfzGqmFxs9KvlDPIVS3cMGpULm6MshExMT9TjC3SqmRGB9kb7yw==", - "dependencies": { - "chalk": "^2.4.2", - "lodash": "^4.17.21" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "winston": ">=3.x <4" - } - }, - "node_modules/express/node_modules/body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.1", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/express/node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/express/node_modules/raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "node_modules/extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", - "dev": true, - "dependencies": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "dependencies": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", - "dev": true, - "dependencies": { - "is-descriptor": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", - "engines": [ - "node >=0.6.0" - ] - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "node_modules/fast-glob": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", - "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "node_modules/fastest-levenshtein": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", - "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", - "engines": { - "node": ">= 4.9.1" - } - }, - "node_modules/fastparse": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", - "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==" - }, - "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/fecha": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", - "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" - }, - "node_modules/figures": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-5.0.0.tgz", - "integrity": "sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg==", - "peer": true, - "dependencies": { - "escape-string-regexp": "^5.0.0", - "is-unicode-supported": "^1.2.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/figures/node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/file-saver": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz", - "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==" - }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "dev": true, - "optional": true - }, - "node_modules/filename-regex": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", - "integrity": "sha512-BTCqyBaWBTsauvnHiE8i562+EdJj+oUpkqWp2R1iCoR8f6oo8STRu3of7WJJ0TqWtxN50a5YFpzYK4Jj9esYfQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/find-cache-dir": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", - "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", - "dev": true, - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/avajs/find-cache-dir?sponsor=1" - } - }, - "node_modules/find-cache-dir/node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/find-versions": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-5.1.0.tgz", - "integrity": "sha512-+iwzCJ7C5v5KgcBuueqVoNiHVoQpwiUK5XFLjf0affFTep+Wcw93tPvmb8tqujDNmzhBDPddnWV/qgWSXgq+Hg==", - "peer": true, - "dependencies": { - "semver-regex": "^4.0.5" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/find-yarn-workspace-root": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz", - "integrity": "sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ==", - "dependencies": { - "micromatch": "^4.0.2" - } - }, - "node_modules/find-yarn-workspace-root/node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/findandreplacedomtext": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/findandreplacedomtext/-/findandreplacedomtext-0.4.6.tgz", - "integrity": "sha512-CVRIKbEwfWoKTSnLrmyX26WjMY7o0aUUTm0RXN47fF/eHINJa8C+YHZxGagC7gMWVaAEBFwH7uVXuhwZcylWOQ==" - }, - "node_modules/fn.name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", - "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" - }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.3" - } - }, - "node_modules/for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/for-own": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", - "integrity": "sha512-SKmowqGTJoPzLO1T0BBJpkfp3EMacCMOuH40hOUbrbzElVktk4DioXVM99QkLCyKoiuOmyjgcWMpVz2xjE7LZw==", - "dev": true, - "dependencies": { - "for-in": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", - "engines": { - "node": "*" - } - }, - "node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==", - "dev": true, - "dependencies": { - "map-cache": "^0.2.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/from2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", - "dependencies": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" - } - }, - "node_modules/from2/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/from2/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/from2/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/fs-readdir-recursive": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", - "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", - "dev": true - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "node_modules/function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/generic-names": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/generic-names/-/generic-names-1.0.3.tgz", - "integrity": "sha512-b6OHfQuKasIKM9b6YPkX+KUj/TLBTx3B/1aT1T5F12FEuEqyFMdr59OMS53aoaSw8eVtapdqieX6lbg5opaOhA==", - "dependencies": { - "loader-utils": "^0.2.16" - } - }, - "node_modules/generic-names/node_modules/big.js": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", - "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", - "engines": { - "node": "*" - } - }, - "node_modules/generic-names/node_modules/emojis-list": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", - "integrity": "sha512-knHEZMgs8BB+MInokmNTg/OyPlAddghe1YBgNwJBc5zsJi/uyIcXoSDsL/W9ymOsBoBGdPIHXYJ9+qKFwRwDng==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/generic-names/node_modules/json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha512-4xrs1aW+6N5DalkqSVA8fxh458CXvR99WU8WLKmq4v8eWAL86Xo3BVqyd3SkA9wEVjCMqyvvRRkshAdOnBp5rw==", - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/generic-names/node_modules/loader-utils": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", - "integrity": "sha512-tiv66G0SmiOx+pLWMtGEkfSEejxvb6N6uRrQjfWJIT79W9GMpgKeCAmm9aVBKtd4WEgntciI8CsGqjpDoCWJug==", - "dependencies": { - "big.js": "^3.1.3", - "emojis-list": "^2.0.0", - "json5": "^0.5.0", - "object-assign": "^4.0.1" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", - "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-size": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-size/-/get-size-3.0.0.tgz", - "integrity": "sha512-Y8aiXLq4leR7807UY0yuKEwif5s3kbVp1nTv+i4jBeoUzByTLKkLWu/HorS6/pB+7gsB0o7OTogC8AoOOeT0Hw==" - }, - "node_modules/get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", - "dependencies": { - "assert-plus": "^1.0.0" - } - }, - "node_modules/git-log-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/git-log-parser/-/git-log-parser-1.2.0.tgz", - "integrity": "sha512-rnCVNfkTL8tdNryFuaY0fYiBWEBcgF748O6ZI61rslBvr2o7U65c2/6npCRqH40vuAhtgtDiqLTJjBVdrejCzA==", - "peer": true, - "dependencies": { - "argv-formatter": "~1.0.0", - "spawn-error-forwarder": "~1.0.0", - "split2": "~1.0.0", - "stream-combiner2": "~1.1.1", - "through2": "~2.0.0", - "traverse": "~0.6.6" - } - }, - "node_modules/git-log-parser/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "peer": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/git-log-parser/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "peer": true - }, - "node_modules/git-log-parser/node_modules/split2": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-1.0.0.tgz", - "integrity": "sha512-NKywug4u4pX/AZBB1FCPzZ6/7O+Xhz1qMVbzTvvKvikjO99oPN87SkK08mEY9P63/5lWjK+wgOOgApnTg5r6qg==", - "peer": true, - "dependencies": { - "through2": "~2.0.0" - } - }, - "node_modules/git-log-parser/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "peer": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/git-log-parser/node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "peer": true, - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "node_modules/git-log-parser/node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "peer": true, - "engines": { - "node": ">=0.4" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-base": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", - "integrity": "sha512-ab1S1g1EbO7YzauaJLkgLp7DZVAqj9M/dvKlTt8DkXA2tiOIcSMrlVI2J1RZyB5iJVccEscjGn+kpOG9788MHA==", - "dev": true, - "dependencies": { - "glob-parent": "^2.0.0", - "is-glob": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/glob-base/node_modules/glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha512-JDYOvfxio/t42HKdxkAYaCiBN7oYiuxykOxKxdaUW5Qn0zaYN3gRQWolrwdnf0shM9/EP0ebuuTmyoXNr1cC5w==", - "dev": true, - "dependencies": { - "is-glob": "^2.0.0" - } - }, - "node_modules/glob-base/node_modules/is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/glob-base/node_modules/is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg==", - "dev": true, - "dependencies": { - "is-extglob": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/glob-to-regexp": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz", - "integrity": "sha512-Iozmtbqv0noj0uDDqoL0zNq0VBEfK2YFoMAZoxJe4cwphvLR+JskfF30QhXHOR4m3KrE6NLRYw+U9MRXvifyig==" - }, - "node_modules/global-dirs": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", - "integrity": "sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==", - "dev": true, - "dependencies": { - "ini": "^1.3.4" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", - "dev": true, - "dependencies": { - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globby/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/got": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", - "integrity": "sha512-Y/K3EDuiQN9rTZhBvPRWMLXIKdeD1Rj0nzunfoi0Yyn5WBEbzxXKU9Ub2X41oZBagVWOBU3MuDonFMgPWQFnwg==", - "dev": true, - "dependencies": { - "create-error-class": "^3.0.0", - "duplexer3": "^0.1.4", - "get-stream": "^3.0.0", - "is-redirect": "^1.0.0", - "is-retry-allowed": "^1.0.0", - "is-stream": "^1.0.0", - "lowercase-keys": "^1.0.0", - "safe-buffer": "^5.0.1", - "timed-out": "^4.0.0", - "unzip-response": "^2.0.1", - "url-parse-lax": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/got/node_modules/get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" - }, - "node_modules/growly": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", - "integrity": "sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw==", - "dev": true - }, - "node_modules/handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, - "node_modules/handlebars/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", - "engines": { - "node": ">=4" - } - }, - "node_modules/har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "deprecated": "this library is no longer supported", - "dependencies": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/hard-rejection": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", - "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", - "dev": true, - "peer": true, - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-ansi/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "engines": { - "node": ">=4" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==", - "dev": true, - "dependencies": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==", - "dev": true, - "dependencies": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-values/node_modules/kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/hoist-non-react-statics": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", - "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "dependencies": { - "react-is": "^16.7.0" - } - }, - "node_modules/home-or-tmp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", - "integrity": "sha512-ycURW7oUxE2sNiPVw1HVEFsW+ecOpJ5zaj7eC0RlwhibhRBod20muUN8qu/gzx956YrLolVvs1MTXwKgC2rVEg==", - "dev": true, - "peer": true, - "dependencies": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/hook-std": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hook-std/-/hook-std-3.0.0.tgz", - "integrity": "sha512-jHRQzjSDzMtFy34AGj1DN+vq54WVuhSvKgrHf0OMiFQTwDD4L/qqofVEWjLOBMTn5+lCD3fPg32W9yOfnEJTTw==", - "peer": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" - }, - "node_modules/html-encoding-sniffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", - "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", - "dev": true, - "dependencies": { - "whatwg-encoding": "^1.0.1" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "node_modules/htmlparser2": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", - "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", - "dependencies": { - "domelementtype": "^1.3.1", - "domhandler": "^2.3.0", - "domutils": "^1.5.1", - "entities": "^1.1.1", - "inherits": "^2.0.1", - "readable-stream": "^3.1.1" - } - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/http-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", - "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", - "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - }, - "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" - } - }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/human-signals": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", - "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", - "peer": true, - "engines": { - "node": ">=16.17.0" - } - }, - "node_modules/humanize-duration": { - "version": "3.28.0", - "resolved": "https://registry.npmjs.org/humanize-duration/-/humanize-duration-3.28.0.tgz", - "integrity": "sha512-jMAxraOOmHuPbffLVDKkEKi/NeG8dMqP8lGRd6Tbf7JgAeG33jjgPWDbXXU7ypCI0o+oNKJFgbSB9FKVdWNI2A==" - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/icss-replace-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", - "integrity": "sha512-chIaY3Vh2mh2Q3RGXttaDIzeiPvaVXJ+C4DAh/w3c37SKZ/U6PGMmuicR2EQQp9bKG8zLMCl7I+PtIoOOPp8Gg==" - }, - "node_modules/icss-utils": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.1.tgz", - "integrity": "sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==", - "dev": true, - "dependencies": { - "postcss": "^7.0.14" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "engines": { - "node": ">= 4" - } - }, - "node_modules/ignore-by-default": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", - "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", - "dev": true - }, - "node_modules/immer": { - "version": "9.0.21", - "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", - "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/immer" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "peer": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-fresh/node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "peer": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/import-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/import-from/-/import-from-4.0.0.tgz", - "integrity": "sha512-P9J71vT5nLlDeV8FHs5nNxaLbrpfAV5cF5srvbZfpwpcJoM/xZR3hiv+q+SAnuSmuGbXMWud063iIMx/V/EWZQ==", - "engines": { - "node": ">=12.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-lazy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", - "integrity": "sha512-m7ZEHgtw69qOGw+jwxXkHlrlIPdTGkyh66zXZ1ajZbxkDBNjSY/LGbmjc7h0s2ELsUDTAhFr55TrPSSqJGPG0A==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/import-local": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", - "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", - "dev": true, - "dependencies": { - "pkg-dir": "^3.0.0", - "resolve-cwd": "^2.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/import-local/node_modules/pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dev": true, - "dependencies": { - "find-up": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" - }, - "node_modules/inline-style-parser": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", - "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==" - }, - "node_modules/internal-slot": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", - "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/interpret": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", - "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/into-stream": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-6.0.0.tgz", - "integrity": "sha512-XHbaOAvP+uFKUFsOgoNPRjLkwB+I22JFPFe5OjTkQ0nwgj6+pSjb4NmB6VMxaPshLiOf+zcpOCBQuLwC1KHhZA==", - "dependencies": { - "from2": "^2.3.0", - "p-is-promise": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dev": true, - "dependencies": { - "loose-envify": "^1.0.0" - } - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-alphabetical": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", - "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-alphanumerical": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", - "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", - "dependencies": { - "is-alphabetical": "^1.0.0", - "is-decimal": "^1.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-array-buffer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", - "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" - }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "dependencies": { - "has-bigints": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "optional": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dependencies": { - "ci-info": "^2.0.0" - }, - "bin": { - "is-ci": "bin.js" - } - }, - "node_modules/is-core-module": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.0.tgz", - "integrity": "sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==", - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-decimal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", - "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "dependencies": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-dotfile": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", - "integrity": "sha512-9YclgOGtN/f8zx0Pr4FQYMdibBiTaH3sn52vjYip4ZSf6C4/6RfTEZ+MR4GvKhCxdPh21Bg42/WL55f6KSnKpg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-equal-shallow": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", - "integrity": "sha512-0EygVC5qPvIyb+gSz7zdD5/AAoS6Qrx1e//6N4yv4oNm30kqvdmG66oZFWVlQHUWe5OjP08FuTw2IdT0EOTcYA==", - "dev": true, - "dependencies": { - "is-primitive": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "dependencies": { - "is-plain-object": "^2.0.4" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-finite": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", - "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-hexadecimal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", - "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-hotkey": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/is-hotkey/-/is-hotkey-0.1.8.tgz", - "integrity": "sha512-qs3NZ1INIS+H+yeo7cD9pDfwYV/jqRh1JG9S9zYrNudkoUQg7OL7ziXqRKu+InFjUIDoP2o6HIkLYMh1pcWgyQ==" - }, - "node_modules/is-installed-globally": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", - "integrity": "sha512-ERNhMg+i/XgDwPIPF3u24qpajVreaiSuvpb1Uu0jugw7KKcxGyCX8cgp8P5fwTmAuXku6beDHHECdKArjlg7tw==", - "dev": true, - "dependencies": { - "global-dirs": "^0.1.0", - "is-path-inside": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-npm": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", - "integrity": "sha512-9r39FIr3d+KD9SbX0sfMsHzb5PP3uimOiwr3YupUaUFG4W0l1U57Rx3utpttV7qz5U3jmrO5auUa04LU9pyHsg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-path-cwd": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", - "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/is-path-inside": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha512-qhsCR/Esx4U4hg/9I19OVUAJkGWtjRYHMRgUMZE2TDdj+Ag+kttZanLupfddNyglzz50cUlmWzUaI37GDfNx/g==", - "dev": true, - "dependencies": { - "path-is-inside": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-posix-bracket": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", - "integrity": "sha512-Yu68oeXJ7LeWNmZ3Zov/xg/oDBnBK2RNxwYY1ilNJX+tKKZqgPK+qOn/Gs9jEu66KDY9Netf5XLKNGzas/vPfQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-primitive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", - "integrity": "sha512-N3w1tFaRfk3UrPfqeRyD+GYDASU3W5VinKhlORy8EWVf/sIdDL9GAcew85XmktCfH+ngG7SRXEVDoO18WMdB/Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-redirect": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", - "integrity": "sha512-cr/SlUEe5zOGmzvj9bUyC4LVvkNVAXu4GytXLNMr1pny+a65MpQ9IJzFHD5vi7FyJgb4qt27+eS3TuQnqB+RQw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-retry-allowed": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", - "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-text-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", - "integrity": "sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==", - "dependencies": { - "text-extensions": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", - "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" - }, - "node_modules/is-unicode-supported": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", - "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" - }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" - }, - "node_modules/issue-parser": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-6.0.0.tgz", - "integrity": "sha512-zKa/Dxq2lGsBIXQ7CUZWTHfvxPC2ej0KfO7fIPqLlHB9J2hJ7rGhZ5rilhuufylr4RXYPzJUeFjKxz305OsNlA==", - "dependencies": { - "lodash.capitalize": "^4.2.1", - "lodash.escaperegexp": "^4.1.2", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.uniqby": "^4.7.0" - }, - "engines": { - "node": ">=10.13" - } - }, - "node_modules/istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", - "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", - "dev": true, - "dependencies": { - "@babel/generator": "^7.4.0", - "@babel/parser": "^7.4.3", - "@babel/template": "^7.4.0", - "@babel/traverse": "^7.4.3", - "@babel/types": "^7.4.0", - "istanbul-lib-coverage": "^2.0.5", - "semver": "^6.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/istanbul-lib-report": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", - "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", - "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/istanbul-lib-report/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", - "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "rimraf": "^2.6.3", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/istanbul-lib-source-maps/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/istanbul-reports": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.7.tgz", - "integrity": "sha512-uu1F/L1o5Y6LzPVSVZXNOoD/KXpJue9aeLRd0sM9uMXfZvzomB0WxVamWb5ue8kA2vVWEmW7EG+A5n3f1kqHKg==", - "dev": true, - "dependencies": { - "html-escaper": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/java-properties": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/java-properties/-/java-properties-1.0.2.tgz", - "integrity": "sha512-qjdpeo2yKlYTH7nFdK0vbZWuTCesk4o63v5iVOlhMQPfuIZQfW/HI35SjfhA+4qpg36rnFSvUK5b1m+ckIblQQ==", - "peer": true, - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/jest": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-24.9.0.tgz", - "integrity": "sha512-YvkBL1Zm7d2B1+h5fHEOdyjCG+sGMz4f8D86/0HiqJ6MB4MnDc8FgP5vdWsGnemOQro7lnYo8UakZ3+5A0jxGw==", - "dev": true, - "dependencies": { - "import-local": "^2.0.0", - "jest-cli": "^24.9.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-changed-files": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-24.9.0.tgz", - "integrity": "sha512-6aTWpe2mHF0DhL28WjdkO8LyGjs3zItPET4bMSeXU6T3ub4FPMw+mcOcbdGXQOAfmLcxofD23/5Bl9Z4AkFwqg==", - "dev": true, - "dependencies": { - "@jest/types": "^24.9.0", - "execa": "^1.0.0", - "throat": "^4.0.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-cli": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-24.9.0.tgz", - "integrity": "sha512-+VLRKyitT3BWoMeSUIHRxV/2g8y9gw91Jh5z2UmXZzkZKpbC08CSehVxgHUwTpy+HwGcns/tqafQDJW7imYvGg==", - "dev": true, - "dependencies": { - "@jest/core": "^24.9.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "chalk": "^2.0.1", - "exit": "^0.1.2", - "import-local": "^2.0.0", - "is-ci": "^2.0.0", - "jest-config": "^24.9.0", - "jest-util": "^24.9.0", - "jest-validate": "^24.9.0", - "prompts": "^2.0.1", - "realpath-native": "^1.1.0", - "yargs": "^13.3.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-config": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.9.0.tgz", - "integrity": "sha512-RATtQJtVYQrp7fvWg6f5y3pEFj9I+H8sWw4aKxnDZ96mob5i5SD6ZEGWgMLXQ4LE8UurrjbdlLWdUeo+28QpfQ==", - "dev": true, - "dependencies": { - "@babel/core": "^7.1.0", - "@jest/test-sequencer": "^24.9.0", - "@jest/types": "^24.9.0", - "babel-jest": "^24.9.0", - "chalk": "^2.0.1", - "glob": "^7.1.1", - "jest-environment-jsdom": "^24.9.0", - "jest-environment-node": "^24.9.0", - "jest-get-type": "^24.9.0", - "jest-jasmine2": "^24.9.0", - "jest-regex-util": "^24.3.0", - "jest-resolve": "^24.9.0", - "jest-util": "^24.9.0", - "jest-validate": "^24.9.0", - "micromatch": "^3.1.10", - "pretty-format": "^24.9.0", - "realpath-native": "^1.1.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-diff": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.9.0.tgz", - "integrity": "sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ==", - "dev": true, - "dependencies": { - "chalk": "^2.0.1", - "diff-sequences": "^24.9.0", - "jest-get-type": "^24.9.0", - "pretty-format": "^24.9.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-docblock": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-24.9.0.tgz", - "integrity": "sha512-F1DjdpDMJMA1cN6He0FNYNZlo3yYmOtRUnktrT9Q37njYzC5WEaDdmbynIgy0L/IvXvvgsG8OsqhLPXTpfmZAA==", - "dev": true, - "dependencies": { - "detect-newline": "^2.1.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-each": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.9.0.tgz", - "integrity": "sha512-ONi0R4BvW45cw8s2Lrx8YgbeXL1oCQ/wIDwmsM3CqM/nlblNCPmnC3IPQlMbRFZu3wKdQ2U8BqM6lh3LJ5Bsog==", - "dev": true, - "dependencies": { - "@jest/types": "^24.9.0", - "chalk": "^2.0.1", - "jest-get-type": "^24.9.0", - "jest-util": "^24.9.0", - "pretty-format": "^24.9.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-environment-jsdom": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.9.0.tgz", - "integrity": "sha512-Zv9FV9NBRzLuALXjvRijO2351DRQeLYXtpD4xNvfoVFw21IOKNhZAEUKcbiEtjTkm2GsJ3boMVgkaR7rN8qetA==", - "dev": true, - "dependencies": { - "@jest/environment": "^24.9.0", - "@jest/fake-timers": "^24.9.0", - "@jest/types": "^24.9.0", - "jest-mock": "^24.9.0", - "jest-util": "^24.9.0", - "jsdom": "^11.5.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-environment-node": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-24.9.0.tgz", - "integrity": "sha512-6d4V2f4nxzIzwendo27Tr0aFm+IXWa0XEUnaH6nU0FMaozxovt+sfRvh4J47wL1OvF83I3SSTu0XK+i4Bqe7uA==", - "dev": true, - "dependencies": { - "@jest/environment": "^24.9.0", - "@jest/fake-timers": "^24.9.0", - "@jest/types": "^24.9.0", - "jest-mock": "^24.9.0", - "jest-util": "^24.9.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-get-type": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", - "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-haste-map": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.9.0.tgz", - "integrity": "sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ==", - "dev": true, - "dependencies": { - "@jest/types": "^24.9.0", - "anymatch": "^2.0.0", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.1.15", - "invariant": "^2.2.4", - "jest-serializer": "^24.9.0", - "jest-util": "^24.9.0", - "jest-worker": "^24.9.0", - "micromatch": "^3.1.10", - "sane": "^4.0.3", - "walker": "^1.0.7" - }, - "engines": { - "node": ">= 6" - }, - "optionalDependencies": { - "fsevents": "^1.2.7" - } - }, - "node_modules/jest-haste-map/node_modules/anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "dependencies": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - } - }, - "node_modules/jest-haste-map/node_modules/fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "deprecated": "fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "dependencies": { - "bindings": "^1.5.0", - "nan": "^2.12.1" - }, - "engines": { - "node": ">= 4.0" - } - }, - "node_modules/jest-haste-map/node_modules/normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", - "dev": true, - "dependencies": { - "remove-trailing-separator": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/jest-jasmine2": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-24.9.0.tgz", - "integrity": "sha512-Cq7vkAgaYKp+PsX+2/JbTarrk0DmNhsEtqBXNwUHkdlbrTBLtMJINADf2mf5FkowNsq8evbPc07/qFO0AdKTzw==", - "dev": true, - "dependencies": { - "@babel/traverse": "^7.1.0", - "@jest/environment": "^24.9.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "chalk": "^2.0.1", - "co": "^4.6.0", - "expect": "^24.9.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^24.9.0", - "jest-matcher-utils": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-runtime": "^24.9.0", - "jest-snapshot": "^24.9.0", - "jest-util": "^24.9.0", - "pretty-format": "^24.9.0", - "throat": "^4.0.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-leak-detector": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-24.9.0.tgz", - "integrity": "sha512-tYkFIDsiKTGwb2FG1w8hX9V0aUb2ot8zY/2nFg087dUageonw1zrLMP4W6zsRO59dPkTSKie+D4rhMuP9nRmrA==", - "dev": true, - "dependencies": { - "jest-get-type": "^24.9.0", - "pretty-format": "^24.9.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-matcher-utils": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz", - "integrity": "sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA==", - "dev": true, - "dependencies": { - "chalk": "^2.0.1", - "jest-diff": "^24.9.0", - "jest-get-type": "^24.9.0", - "pretty-format": "^24.9.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-message-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", - "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/stack-utils": "^1.0.1", - "chalk": "^2.0.1", - "micromatch": "^3.1.10", - "slash": "^2.0.0", - "stack-utils": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-mock": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", - "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", - "dev": true, - "dependencies": { - "@jest/types": "^24.9.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", - "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-resolve": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.9.0.tgz", - "integrity": "sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ==", - "dev": true, - "dependencies": { - "@jest/types": "^24.9.0", - "browser-resolve": "^1.11.3", - "chalk": "^2.0.1", - "jest-pnp-resolver": "^1.2.1", - "realpath-native": "^1.1.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-24.9.0.tgz", - "integrity": "sha512-Fm7b6AlWnYhT0BXy4hXpactHIqER7erNgIsIozDXWl5dVm+k8XdGVe1oTg1JyaFnOxarMEbax3wyRJqGP2Pq+g==", - "dev": true, - "dependencies": { - "@jest/types": "^24.9.0", - "jest-regex-util": "^24.3.0", - "jest-snapshot": "^24.9.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-runner": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-24.9.0.tgz", - "integrity": "sha512-KksJQyI3/0mhcfspnxxEOBueGrd5E4vV7ADQLT9ESaCzz02WnbdbKWIf5Mkaucoaj7obQckYPVX6JJhgUcoWWg==", - "dev": true, - "dependencies": { - "@jest/console": "^24.7.1", - "@jest/environment": "^24.9.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "chalk": "^2.4.2", - "exit": "^0.1.2", - "graceful-fs": "^4.1.15", - "jest-config": "^24.9.0", - "jest-docblock": "^24.3.0", - "jest-haste-map": "^24.9.0", - "jest-jasmine2": "^24.9.0", - "jest-leak-detector": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-resolve": "^24.9.0", - "jest-runtime": "^24.9.0", - "jest-util": "^24.9.0", - "jest-worker": "^24.6.0", - "source-map-support": "^0.5.6", - "throat": "^4.0.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-runner/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/jest-runner/node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/jest-runtime": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-24.9.0.tgz", - "integrity": "sha512-8oNqgnmF3v2J6PVRM2Jfuj8oX3syKmaynlDMMKQ4iyzbQzIG6th5ub/lM2bCMTmoTKM3ykcUYI2Pw9xwNtjMnw==", - "dev": true, - "dependencies": { - "@jest/console": "^24.7.1", - "@jest/environment": "^24.9.0", - "@jest/source-map": "^24.3.0", - "@jest/transform": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/yargs": "^13.0.0", - "chalk": "^2.0.1", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.1.15", - "jest-config": "^24.9.0", - "jest-haste-map": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-mock": "^24.9.0", - "jest-regex-util": "^24.3.0", - "jest-resolve": "^24.9.0", - "jest-snapshot": "^24.9.0", - "jest-util": "^24.9.0", - "jest-validate": "^24.9.0", - "realpath-native": "^1.1.0", - "slash": "^2.0.0", - "strip-bom": "^3.0.0", - "yargs": "^13.3.0" - }, - "bin": { - "jest-runtime": "bin/jest-runtime.js" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-serializer": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.9.0.tgz", - "integrity": "sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-snapshot": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.9.0.tgz", - "integrity": "sha512-uI/rszGSs73xCM0l+up7O7a40o90cnrk429LOiK3aeTvfC0HHmldbd81/B7Ix81KSFe1lwkbl7GnBGG4UfuDew==", - "dev": true, - "dependencies": { - "@babel/types": "^7.0.0", - "@jest/types": "^24.9.0", - "chalk": "^2.0.1", - "expect": "^24.9.0", - "jest-diff": "^24.9.0", - "jest-get-type": "^24.9.0", - "jest-matcher-utils": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-resolve": "^24.9.0", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "pretty-format": "^24.9.0", - "semver": "^6.2.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", - "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", - "dev": true, - "dependencies": { - "@jest/console": "^24.9.0", - "@jest/fake-timers": "^24.9.0", - "@jest/source-map": "^24.9.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "callsites": "^3.0.0", - "chalk": "^2.0.1", - "graceful-fs": "^4.1.15", - "is-ci": "^2.0.0", - "mkdirp": "^0.5.1", - "slash": "^2.0.0", - "source-map": "^0.6.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-util/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/jest-validate": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.9.0.tgz", - "integrity": "sha512-HPIt6C5ACwiqSiwi+OfSSHbK8sG7akG8eATl+IPKaeIjtPOeBUd/g3J7DghugzxrGjI93qS/+RPKe1H6PqvhRQ==", - "dev": true, - "dependencies": { - "@jest/types": "^24.9.0", - "camelcase": "^5.3.1", - "chalk": "^2.0.1", - "jest-get-type": "^24.9.0", - "leven": "^3.1.0", - "pretty-format": "^24.9.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-watcher": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-24.9.0.tgz", - "integrity": "sha512-+/fLOfKPXXYJDYlks62/4R4GoT+GU1tYZed99JSCOsmzkkF7727RqKrjNAxtfO4YpGv11wybgRvCjR73lK2GZw==", - "dev": true, - "dependencies": { - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/yargs": "^13.0.0", - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.1", - "jest-util": "^24.9.0", - "string-length": "^2.0.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-worker": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", - "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", - "dev": true, - "dependencies": { - "merge-stream": "^2.0.0", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jquery": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-2.2.4.tgz", - "integrity": "sha512-lBHj60ezci2u1v2FqnZIraShGgEXq35qCzMv4lITyHGppTnA13rwR0MgwyNJh9TnDs3aXUvd1xjAotfraMHX/Q==" - }, - "node_modules/jquery-ui": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/jquery-ui/-/jquery-ui-1.12.1.tgz", - "integrity": "sha512-K/kDBMXkTky5LH+gqbMvttU1ipqCTaecKyAFjwHjUnPTVfm5I5PZC7We31iNR3yWtAHNqoxkLoit06lR/gKVlA==" - }, - "node_modules/jquery.cookie": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jquery.cookie/-/jquery.cookie-1.4.1.tgz", - "integrity": "sha512-c/hZOOL+8VSw/FkTVH637gS1/6YzMSCROpTZ2qBYwJ7s7sHajU7uBkSSiE5+GXWwrfCCyO+jsYjUQ7Hs2rIxAA==" - }, - "node_modules/jquery.scrollto": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/jquery.scrollto/-/jquery.scrollto-2.1.3.tgz", - "integrity": "sha512-4M+t5R4QRnq1CSRU9gswZ7Z4zRfcoWmgD31HRrLYCCI4J7tLWeE0ZoT+0JpI7GcT0YKTqcfw+p8t2yYEKFfrHA==", - "dependencies": { - "jquery": ">=1.8" - } - }, - "node_modules/js-cookie": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz", - "integrity": "sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==" - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "peer": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" - }, - "node_modules/jsdom": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz", - "integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==", - "dev": true, - "dependencies": { - "abab": "^2.0.0", - "acorn": "^5.5.3", - "acorn-globals": "^4.1.0", - "array-equal": "^1.0.0", - "cssom": ">= 0.3.2 < 0.4.0", - "cssstyle": "^1.0.0", - "data-urls": "^1.0.0", - "domexception": "^1.0.1", - "escodegen": "^1.9.1", - "html-encoding-sniffer": "^1.0.2", - "left-pad": "^1.3.0", - "nwsapi": "^2.0.7", - "parse5": "4.0.0", - "pn": "^1.1.0", - "request": "^2.87.0", - "request-promise-native": "^1.0.5", - "sax": "^1.2.4", - "symbol-tree": "^3.2.2", - "tough-cookie": "^2.3.4", - "w3c-hr-time": "^1.0.1", - "webidl-conversions": "^4.0.2", - "whatwg-encoding": "^1.0.3", - "whatwg-mimetype": "^2.1.0", - "whatwg-url": "^6.4.1", - "ws": "^5.2.0", - "xml-name-validator": "^3.0.0" - } - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" - }, - "node_modules/json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", - "engines": [ - "node >= 0.2.0" - ] - }, - "node_modules/JSONStream": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", - "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", - "dependencies": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" - }, - "bin": { - "JSONStream": "bin.js" - }, - "engines": { - "node": "*" - } - }, - "node_modules/jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/klaw-sync": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz", - "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==", - "dependencies": { - "graceful-fs": "^4.1.11" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/kuler": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", - "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" - }, - "node_modules/latest-version": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", - "integrity": "sha512-Be1YRHWWlZaSsrz2U+VInk+tO0EwLIyV+23RhWLINJYwg/UIikxjlj3MhH37/6/EDCAusjajvMkMMUXRaMWl/w==", - "dev": true, - "dependencies": { - "package-json": "^4.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/left-pad": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", - "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==", - "deprecated": "use String.prototype.padStart()", - "dev": true - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", - "dev": true, - "dependencies": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lines-and-columns": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.3.tgz", - "integrity": "sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w==", - "peer": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/load-json-file/node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", - "engines": { - "node": ">=4" - } - }, - "node_modules/loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", - "dev": true, - "engines": { - "node": ">=6.11.5" - } - }, - "node_modules/loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", - "dev": true, - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - }, - "engines": { - "node": ">=8.9.0" - } - }, - "node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", - "peer": true - }, - "node_modules/lodash.assign": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", - "integrity": "sha512-hFuH8TY+Yji7Eja3mGiuAxBqLagejScbG8GbG0j6o9vzn0YL14My+ktnqtZgFTosKymC9/44wP6s7xyuLfnClw==", - "dev": true - }, - "node_modules/lodash.assignin": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz", - "integrity": "sha512-yX/rx6d/UTVh7sSVWVSIMjfnz95evAgDFdb1ZozC35I9mSFCkmzptOzevxjgbQUsc78NR44LVHWjsoMQXy9FDg==" - }, - "node_modules/lodash.bind": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz", - "integrity": "sha512-lxdsn7xxlCymgLYo1gGvVrfHmkjDiyqVv62FAeF2i5ta72BipE1SLxw8hPEPLhD4/247Ijw07UQH7Hq/chT5LA==" - }, - "node_modules/lodash.capitalize": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz", - "integrity": "sha512-kZzYOKspf8XVX5AvmQF94gQW0lejFVgb80G85bU4ZWzoJ6C03PQg3coYAUpSTpQWelrZELd3XWgHzw4Ck5kaIw==" - }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "dev": true - }, - "node_modules/lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" - }, - "node_modules/lodash.escaperegexp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", - "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==" - }, - "node_modules/lodash.filter": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz", - "integrity": "sha512-pXYUy7PR8BCLwX5mgJ/aNtyOvuJTdZAo9EQFUvMIYugqmJxnrYaANvTbgndOzHSCSR0wnlBBfRXJL5SbWxo3FQ==" - }, - "node_modules/lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==" - }, - "node_modules/lodash.foreach": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", - "integrity": "sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ==" - }, - "node_modules/lodash.frompairs": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.frompairs/-/lodash.frompairs-4.0.1.tgz", - "integrity": "sha512-dvqe2I+cO5MzXCMhUnfYFa9MD+/760yx2aTAN1lqEcEkf896TxgrX373igVdqSJj6tQd0jnSLE1UMuKufqqxFw==", - "dev": true - }, - "node_modules/lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", - "dev": true - }, - "node_modules/lodash.ismatch": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", - "integrity": "sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==" - }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" - }, - "node_modules/lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" - }, - "node_modules/lodash.map": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", - "integrity": "sha512-worNHGKLDetmcEYDvh2stPCrrQRkP20E4l0iIS7F8EvzMqBBi7ltvFN5m1HvTf1P7Jk1txKhvFcmYsCr8O2F1Q==" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" - }, - "node_modules/lodash.pick": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", - "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==" - }, - "node_modules/lodash.reduce": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz", - "integrity": "sha512-6raRe2vxCYBhpBu+B+TtNGUzah+hQjVdu3E17wfusjyrXBka2nBS8OH/gjVZ5PvHOhWmIZTYri09Z6n/QfnNMw==" - }, - "node_modules/lodash.reject": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz", - "integrity": "sha512-qkTuvgEzYdyhiJBx42YPzPo71R1aEr0z79kAv7Ixg8wPFEjgRgJdUsGMG3Hf3OYSF/kHI79XhNlt+5Ar6OzwxQ==" - }, - "node_modules/lodash.some": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz", - "integrity": "sha512-j7MJE+TuT51q9ggt4fSgVqro163BEFjAt3u97IqU+JA2DkWl80nFTrowzLpZ/BnpN7rrl0JA/593NAdd8p/scQ==" - }, - "node_modules/lodash.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", - "dev": true - }, - "node_modules/lodash.topairs": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.topairs/-/lodash.topairs-4.3.0.tgz", - "integrity": "sha512-qrRMbykBSEGdOgQLJJqVSdPWMD7Q+GJJ5jMRfQYb+LTLsw3tYVIabnCzRqTJb2WTo17PG5gNzXuFaZgYH/9SAQ==", - "dev": true - }, - "node_modules/lodash.uniqby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", - "integrity": "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==" - }, - "node_modules/logform": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.5.1.tgz", - "integrity": "sha512-9FyqAm9o9NKKfiAKfZoYo9bGXXuwMkxQiQttkT4YjjVtQVIQtK6LmVtlxmCaFswo6N4AfEkHqZTV0taDtPotNg==", - "dependencies": { - "@colors/colors": "1.5.0", - "@types/triple-beam": "^1.3.2", - "fecha": "^4.2.0", - "ms": "^2.1.1", - "safe-stable-stringify": "^2.3.1", - "triple-beam": "^1.3.0" - } - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/map-obj": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", - "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==", - "dev": true, - "dependencies": { - "object-visit": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/marked": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/marked/-/marked-5.1.2.tgz", - "integrity": "sha512-ahRPGXJpjMjwSOlBoTMZAK7ATXkli5qCPxZ21TG44rx1KEo44bii4ekgTDQPNRQ4Kh7JMb9Ub1PVk1NxRSsorg==", - "peer": true, - "bin": { - "marked": "bin/marked.js" - }, - "engines": { - "node": ">= 16" - } - }, - "node_modules/marked-terminal": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-5.2.0.tgz", - "integrity": "sha512-Piv6yNwAQXGFjZSaiNljyNFw7jKDdGrw70FSbtxEyldLsyeuV5ZHm/1wW++kWbrOF1VPnUgYOhB2oLL0ZpnekA==", - "peer": true, - "dependencies": { - "ansi-escapes": "^6.2.0", - "cardinal": "^2.1.1", - "chalk": "^5.2.0", - "cli-table3": "^0.6.3", - "node-emoji": "^1.11.0", - "supports-hyperlinks": "^2.3.0" - }, - "engines": { - "node": ">=14.13.1 || >=16.0.0" - }, - "peerDependencies": { - "marked": "^1.0.0 || ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0" - } - }, - "node_modules/marked-terminal/node_modules/ansi-escapes": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.0.tgz", - "integrity": "sha512-kzRaCqXnpzWs+3z5ABPQiVke+iq0KXkHo8xiWV4RPTi5Yli0l97BEQuhXV1s7+aSU/fu1kUuxgS4MsQ0fRuygw==", - "peer": true, - "dependencies": { - "type-fest": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/marked-terminal/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "peer": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/marked-terminal/node_modules/type-fest": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", - "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", - "peer": true, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/math-random": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz", - "integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==", - "dev": true - }, - "node_modules/mdast-util-definitions": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-4.0.0.tgz", - "integrity": "sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ==", - "dependencies": { - "unist-util-visit": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-from-markdown": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-0.8.5.tgz", - "integrity": "sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==", - "dependencies": { - "@types/mdast": "^3.0.0", - "mdast-util-to-string": "^2.0.0", - "micromark": "~2.11.0", - "parse-entities": "^2.0.0", - "unist-util-stringify-position": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-hast": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-10.2.0.tgz", - "integrity": "sha512-JoPBfJ3gBnHZ18icCwHR50orC9kNH81tiR1gs01D8Q5YpV6adHNO9nKNuFBCJQ941/32PT1a63UF/DitmS3amQ==", - "dependencies": { - "@types/mdast": "^3.0.0", - "@types/unist": "^2.0.0", - "mdast-util-definitions": "^4.0.0", - "mdurl": "^1.0.0", - "unist-builder": "^2.0.0", - "unist-util-generated": "^1.0.0", - "unist-util-position": "^3.0.0", - "unist-util-visit": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz", - "integrity": "sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==" - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/meow": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", - "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", - "dependencies": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.18.0", - "yargs-parser": "^20.2.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/meow/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/meow/node_modules/hosted-git-info": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", - "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/meow/node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" - }, - "node_modules/meow/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/meow/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/meow/node_modules/normalize-package-data": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", - "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", - "dependencies": { - "hosted-git-info": "^4.0.1", - "is-core-module": "^2.5.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/meow/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/meow/node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/meow/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/meow/node_modules/read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/meow/node_modules/read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dependencies": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/meow/node_modules/read-pkg-up/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/meow/node_modules/read-pkg/node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" - }, - "node_modules/meow/node_modules/read-pkg/node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/meow/node_modules/read-pkg/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/meow/node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/meow/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/meow/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/meow/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "engines": { - "node": ">=10" - } - }, - "node_modules/merge-descriptors": { + "node_modules/@types/stack-utils": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", + "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==", + "dev": true }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "engines": { - "node": ">= 8" - } + "node_modules/@types/triple-beam": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.2.tgz", + "integrity": "sha512-txGIh+0eDFzKGC25zORnswy+br1Ha7hj5cMVwKIU7+s0U2AxxJru/jZSMU6OC9MJWP6+pc/hc6ZjyZShpsyY2g==" }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "engines": { - "node": ">= 0.6" - } + "node_modules/@types/unist": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", + "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==" }, - "node_modules/micromark": { - "version": "2.11.4", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-2.11.4.tgz", - "integrity": "sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], + "node_modules/@types/yargs": { + "version": "13.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.12.tgz", + "integrity": "sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ==", + "dev": true, "dependencies": { - "debug": "^4.0.0", - "parse-entities": "^2.0.0" + "@types/yargs-parser": "*" } }, - "node_modules/micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "node_modules/@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.5.tgz", + "integrity": "sha512-LHY/GSAZZRpsNQH+/oHqhRQ5FT7eoULcBqgfyTB5nQHogFnK3/7QoN7dLnwSE/JkUAF0SrRuclT7ODqMFtWxxQ==", "dev": true, "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" + "@webassemblyjs/helper-numbers": "1.11.5", + "@webassemblyjs/helper-wasm-bytecode": "1.11.5" } }, - "node_modules/micromatch/node_modules/braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.5.tgz", + "integrity": "sha512-1j1zTIC5EZOtCplMBG/IEwLtUojtwFVwdyVMbL/hwWqbzlQoJsWCOavrdnLkemwNoC/EOwtUFch3fuo+cbcXYQ==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.5.tgz", + "integrity": "sha512-L65bDPmfpY0+yFrsgz8b6LhXmbbs38OnwDCf6NpnMUYqa+ENfE5Dq9E42ny0qz/PdR0LJyq/T5YijPnU8AXEpA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.5.tgz", + "integrity": "sha512-fDKo1gstwFFSfacIeH5KfwzjykIE6ldh1iH9Y/8YkAZrhmu4TctqYjSh7t0K2VyDSXOZJ1MLhht/k9IvYGcIxg==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.5.tgz", + "integrity": "sha512-DhykHXM0ZABqfIGYNv93A5KKDw/+ywBFnuWybZZWcuzWHfbp21wUfRkbtz7dMGwGgT4iXjWuhRMA2Mzod6W4WA==", "dev": true, "dependencies": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" + "@webassemblyjs/floating-point-hex-parser": "1.11.5", + "@webassemblyjs/helper-api-error": "1.11.5", + "@xtuc/long": "4.2.2" } }, - "node_modules/micromatch/node_modules/braces/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.5.tgz", + "integrity": "sha512-oC4Qa0bNcqnjAowFn7MPCETQgDYytpsfvz4ujZz63Zu/a/v71HeCAAmZsgZ3YVKec3zSPYytG3/PrRCqbtcAvA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.5.tgz", + "integrity": "sha512-uEoThA1LN2NA+K3B9wDo3yKlBfVtC6rh0i4/6hvbz071E8gTNZD/pT0MsBf7MeD6KbApMSkaAK0XeKyOZC7CIA==", "dev": true, "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" + "@webassemblyjs/ast": "1.11.5", + "@webassemblyjs/helper-buffer": "1.11.5", + "@webassemblyjs/helper-wasm-bytecode": "1.11.5", + "@webassemblyjs/wasm-gen": "1.11.5" } }, - "node_modules/micromatch/node_modules/fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.5.tgz", + "integrity": "sha512-37aGq6qVL8A8oPbPrSGMBcp38YZFXcHfiROflJn9jxSdSMMM5dS5P/9e2/TpaJuhE+wFrbukN2WI6Hw9MH5acg==", "dev": true, "dependencies": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "engines": { - "node": ">=0.10.0" + "@xtuc/ieee754": "^1.2.0" } }, - "node_modules/micromatch/node_modules/fill-range/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.5.tgz", + "integrity": "sha512-ajqrRSXaTJoPW+xmkfYN6l8VIeNnR4vBOTQO9HzR7IygoCcKWkICbKFbVTNMjMgMREqXEr0+2M6zukzM47ZUfQ==", "dev": true, "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" + "@xtuc/long": "4.2.2" } }, - "node_modules/micromatch/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.5.tgz", + "integrity": "sha512-WiOhulHKTZU5UPlRl53gHR8OxdGsSOxqfpqWeA2FmcwBMaoEdz6b2x2si3IwC9/fSPLfe8pBMRTHVMk5nlwnFQ==", + "dev": true }, - "node_modules/micromatch/node_modules/to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.5.tgz", + "integrity": "sha512-C0p9D2fAu3Twwqvygvf42iGCQ4av8MFBLiTb+08SZ4cEdwzWx9QeAHDo1E2k+9s/0w1DM40oflJOpkZ8jW4HCQ==", "dev": true, "dependencies": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" + "@webassemblyjs/ast": "1.11.5", + "@webassemblyjs/helper-buffer": "1.11.5", + "@webassemblyjs/helper-wasm-bytecode": "1.11.5", + "@webassemblyjs/helper-wasm-section": "1.11.5", + "@webassemblyjs/wasm-gen": "1.11.5", + "@webassemblyjs/wasm-opt": "1.11.5", + "@webassemblyjs/wasm-parser": "1.11.5", + "@webassemblyjs/wast-printer": "1.11.5" } }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.5.tgz", + "integrity": "sha512-14vteRlRjxLK9eSyYFvw1K8Vv+iPdZU0Aebk3j6oB8TQiQYuO6hj9s4d7qf6f2HJr2khzvNldAFG13CgdkAIfA==", + "dev": true, "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "engines": { - "node": ">=4" + "@webassemblyjs/ast": "1.11.5", + "@webassemblyjs/helper-wasm-bytecode": "1.11.5", + "@webassemblyjs/ieee754": "1.11.5", + "@webassemblyjs/leb128": "1.11.5", + "@webassemblyjs/utf8": "1.11.5" } }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.5.tgz", + "integrity": "sha512-tcKwlIXstBQgbKy1MlbDMlXaxpucn42eb17H29rawYLxm5+MsEmgPzeCP8B1Cl69hCice8LeKgZpRUAPtqYPgw==", + "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" + "@webassemblyjs/ast": "1.11.5", + "@webassemblyjs/helper-buffer": "1.11.5", + "@webassemblyjs/wasm-gen": "1.11.5", + "@webassemblyjs/wasm-parser": "1.11.5" } }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.5.tgz", + "integrity": "sha512-SVXUIwsLQlc8srSD7jejsfTU83g7pIGr2YYNb9oHdtldSxaOhvA5xwvIiWIfcX8PlSakgqMXsLpLfbbJ4cBYew==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.5", + "@webassemblyjs/helper-api-error": "1.11.5", + "@webassemblyjs/helper-wasm-bytecode": "1.11.5", + "@webassemblyjs/ieee754": "1.11.5", + "@webassemblyjs/leb128": "1.11.5", + "@webassemblyjs/utf8": "1.11.5" } }, - "node_modules/minimist-options": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", - "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.5.tgz", + "integrity": "sha512-f7Pq3wvg3GSPUPzR0F6bmI89Hdb+u9WXrSKc4v+N0aV0q6r42WoF92Jp2jEorBEBRoRNXgjp53nBniDXcqZYPA==", + "dev": true, "dependencies": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0", - "kind-of": "^6.0.3" - }, - "engines": { - "node": ">= 6" + "@webassemblyjs/ast": "1.11.5", + "@xtuc/long": "4.2.2" } }, - "node_modules/minimist-options/node_modules/is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", - "engines": { - "node": ">=0.10.0" + "node_modules/@webpack-cli/configtest": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz", + "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==", + "dev": true, + "peerDependencies": { + "webpack": "4.x.x || 5.x.x", + "webpack-cli": "4.x.x" } }, - "node_modules/mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "node_modules/@webpack-cli/info": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz", + "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==", "dev": true, "dependencies": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" + "envinfo": "^7.7.3" }, - "engines": { - "node": ">=0.10.0" + "peerDependencies": { + "webpack-cli": "4.x.x" } }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dependencies": { - "minimist": "^1.2.6" + "node_modules/@webpack-cli/serve": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz", + "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==", + "dev": true, + "peerDependencies": { + "webpack-cli": "4.x.x" }, - "bin": { - "mkdirp": "bin/cmd.js" + "peerDependenciesMeta": { + "webpack-dev-server": { + "optional": true + } } }, - "node_modules/modify-values": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", - "integrity": "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==", - "engines": { - "node": ">=0.10.0" - } + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true }, - "node_modules/nan": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", - "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==", - "dev": true, - "optional": true + "node_modules/@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==" }, - "node_modules/nanoid": { - "version": "2.1.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-2.1.11.tgz", - "integrity": "sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA==" + "node_modules/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "dev": true }, - "node_modules/nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "dev": true, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "mime-types": "~2.1.34", + "negotiator": "0.6.3" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.6" } }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "node_modules/acorn": { + "version": "5.7.4", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", + "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, "engines": { - "node": ">= 0.6" + "node": ">=0.4.0" } }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" - }, - "node_modules/nerf-dart": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/nerf-dart/-/nerf-dart-1.0.0.tgz", - "integrity": "sha512-EZSPZB70jiVsivaBLYDCyntd5eH8NTSMOn3rB+HxwdmKThGELLdYv8qVIMWvZEFy9w8ZZpW9h9OB32l1rGtj7g==" - }, - "node_modules/nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" - }, - "node_modules/node-emoji": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", - "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", - "peer": true, + "node_modules/acorn-globals": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.4.tgz", + "integrity": "sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==", + "dev": true, "dependencies": { - "lodash": "^4.17.21" + "acorn": "^6.0.1", + "acorn-walk": "^6.0.1" } }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dependencies": { - "whatwg-url": "^5.0.0" + "node_modules/acorn-globals/node_modules/acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "dev": true, + "bin": { + "acorn": "bin/acorn" }, "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } + "node": ">=0.4.0" } }, - "node_modules/node-fetch/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "node_modules/node-fetch/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + "node_modules/acorn-walk": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz", + "integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } }, - "node_modules/node-fetch/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } }, - "node_modules/node-notifier": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.5.tgz", - "integrity": "sha512-tVbHs7DyTLtzOiN78izLA85zRqB9NvEXkAf014Vx3jtSvn/xBl6bR8ZYifj+dFcFrKI21huSQgJZ6ZtL3B4HfQ==", + "node_modules/ansi-align": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", + "integrity": "sha512-TdlOggdA/zURfMYa7ABC66j+oqfMew58KpJMbUlH3bcZP1b+cBHIHDDn5uH9INsxrHBPjsqM0tDB4jPTF/vgJA==", "dev": true, - "dependencies": { - "growly": "^1.3.0", - "is-wsl": "^1.1.0", - "semver": "^5.5.0", - "shellwords": "^0.1.1", - "which": "^1.3.0" + "dependencies": { + "string-width": "^2.0.0" } }, - "node_modules/node-notifier/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "node_modules/ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", "dev": true, - "bin": { - "semver": "bin/semver" + "engines": { + "node": ">=4" } }, - "node_modules/node-releases": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", - "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", - "dev": true - }, - "node_modules/nodemon": { - "version": "1.19.4", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-1.19.4.tgz", - "integrity": "sha512-VGPaqQBNk193lrJFotBU8nvWZPqEZY2eIzymy2jjY0fJ9qIsxA0sxQ8ATPl0gZC645gijYEc1jtZvpS8QWzJGQ==", + "node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", "dev": true, - "hasInstallScript": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dependencies": { - "chokidar": "^2.1.8", - "debug": "^3.2.6", - "ignore-by-default": "^1.0.1", - "minimatch": "^3.0.4", - "pstree.remy": "^1.1.7", - "semver": "^5.7.1", - "supports-color": "^5.5.0", - "touch": "^3.1.0", - "undefsafe": "^2.0.2", - "update-notifier": "^2.5.0" - }, - "bin": { - "nodemon": "bin/nodemon.js" + "color-convert": "^1.9.0" }, "engines": { "node": ">=4" } }, - "node_modules/nodemon/node_modules/anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, + "optional": true, "dependencies": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" } }, - "node_modules/nodemon/node_modules/anymatch/node_modules/normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "node_modules/arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", "dev": true, - "dependencies": { - "remove-trailing-separator": "^1.0.1" - }, "engines": { "node": ">=0.10.0" } }, - "node_modules/nodemon/node_modules/binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "node_modules/arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/nodemon/node_modules/braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "node_modules/arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", "dev": true, - "dependencies": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, "engines": { "node": ">=0.10.0" } }, - "node_modules/nodemon/node_modules/chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", - "deprecated": "Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies", + "node_modules/array-buffer-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", + "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", "dev": true, "dependencies": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" }, - "optionalDependencies": { - "fsevents": "^1.2.7" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/nodemon/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "node_modules/array-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", + "integrity": "sha512-H3LU5RLiSsGXPhN+Nipar0iR0IofH+8r89G2y1tBKxQ/agagKyAjhkAFDRBfodP2caPrNKHpAWNIM/c9yeL7uA==", + "dev": true + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==", "dev": true, - "dependencies": { - "ms": "^2.1.1" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/nodemon/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "node_modules/array.prototype.reduce": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.5.tgz", + "integrity": "sha512-kDdugMl7id9COE8R7MHF5jWk7Dqt/fs4Pv+JXoICnYwqpjjjbUurz6w5fT5IG6brLdJhv6/VoHB0H7oyIBXd+Q==", "dev": true, "dependencies": { - "is-extendable": "^0.1.0" + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-array-method-boxes-properly": "^1.0.0", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==", + "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/nodemon/node_modules/fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "node_modules/astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", "dev": true, - "dependencies": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" + "engines": { + "node": ">=4" + } + }, + "node_modules/async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" + }, + "node_modules/async-each": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.6.tgz", + "integrity": "sha512-c646jH1avxr+aVpndVMeAfYw7wAa6idufrlN3LPA4PmKS0QEGp6PIC9nwz0WQkkvBGAMEki3pFdtxaF39J9vvg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] + }, + "node_modules/async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", + "dev": true + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true, + "bin": { + "atob": "bin/atob.js" }, "engines": { - "node": ">=0.10.0" + "node": ">= 4.5.0" } }, - "node_modules/nodemon/node_modules/fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "deprecated": "fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.", + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "dependencies": { - "bindings": "^1.5.0", - "nan": "^2.12.1" - }, "engines": { - "node": ">= 4.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/nodemon/node_modules/glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", - "dev": true, - "dependencies": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", + "engines": { + "node": "*" } }, - "node_modules/nodemon/node_modules/glob-parent/node_modules/is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", + "node_modules/aws4": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", + "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==" + }, + "node_modules/babel-jest": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.9.0.tgz", + "integrity": "sha512-ntuddfyiN+EhMw58PTNL1ph4C9rECiQXjI4nMMBKBaNjXvqLdkXpPRcMSr4iyBrJg/+wz9brFUD6RhOAT6r4Iw==", "dev": true, "dependencies": { - "is-extglob": "^2.1.0" + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/babel__core": "^7.1.0", + "babel-plugin-istanbul": "^5.1.0", + "babel-preset-jest": "^24.9.0", + "chalk": "^2.4.2", + "slash": "^2.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">= 6" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/nodemon/node_modules/is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==", + "node_modules/babel-loader": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.3.0.tgz", + "integrity": "sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q==", "dev": true, "dependencies": { - "binary-extensions": "^1.0.0" + "find-cache-dir": "^3.3.1", + "loader-utils": "^2.0.0", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" }, "engines": { - "node": ">=0.10.0" + "node": ">= 8.9" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "webpack": ">=2" } }, - "node_modules/nodemon/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "node_modules/babel-loader/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/nodemon/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "node_modules/babel-plugin-istanbul": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz", + "integrity": "sha512-5LphC0USA8t4i1zCtjbbNb6jJj/9+X6P37Qfirc/70EQ34xKlMW+a1RHGwxGI+SwWpNwZ27HqvzAobeqaXwiZw==", "dev": true, "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "@babel/helper-plugin-utils": "^7.0.0", + "find-up": "^3.0.0", + "istanbul-lib-instrument": "^3.3.0", + "test-exclude": "^5.2.3" + }, + "engines": { + "node": ">=6" } }, - "node_modules/nodemon/node_modules/readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "node_modules/babel-plugin-jest-hoist": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.9.0.tgz", + "integrity": "sha512-2EMA2P8Vp7lG0RAzr4HXqtYwacfMErOuv1U3wrvxHX6rD1sV6xS3WXG3r8TRQ2r6w8OhvSdWt+z41hQNwNm3Xw==", "dev": true, "dependencies": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" + "@types/babel__traverse": "^7.0.6" }, "engines": { - "node": ">=0.10" - } - }, - "node_modules/nodemon/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/nodemon/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" + "node": ">= 6" } }, - "node_modules/nodemon/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", + "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", "dev": true, "dependencies": { - "safe-buffer": "~5.1.0" + "@babel/compat-data": "^7.17.7", + "@babel/helper-define-polyfill-provider": "^0.3.3", + "semver": "^6.1.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/nodemon/node_modules/to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz", + "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==", "dev": true, "dependencies": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "@babel/helper-define-polyfill-provider": "^0.3.3", + "core-js-compat": "^3.25.1" }, - "engines": { - "node": ">=0.10.0" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/nopt": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", - "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", + "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", + "dev": true, "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" + "@babel/helper-define-polyfill-provider": "^0.3.3" }, - "engines": { - "node": "*" - } - }, - "node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/normalize-package-data/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "bin": { - "semver": "bin/semver" - } + "node_modules/babel-plugin-transform-remove-console": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-remove-console/-/babel-plugin-transform-remove-console-6.9.4.tgz", + "integrity": "sha512-88blrUrMX3SPiGkT1GnvVY8E/7A+k6oj3MNvUtTIxJflFzXTw1bHkuJ/y039ouhFMp2prRn5cQGzokViYi1dsg==", + "dev": true }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "node_modules/babel-preset-jest": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-24.9.0.tgz", + "integrity": "sha512-izTUuhE4TMfTRPF92fFwD2QfdXaZW08qvWTFCI51V8rW5x00UuPgc3ajRoWofXOuxjfcOM5zzSYsQS3H8KGCAg==", "dev": true, + "dependencies": { + "@babel/plugin-syntax-object-rest-spread": "^7.0.0", + "babel-plugin-jest-hoist": "^24.9.0" + }, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", - "engines": { - "node": ">=10" + "node": ">= 6" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/npm": { - "version": "8.19.4", - "resolved": "https://registry.npmjs.org/npm/-/npm-8.19.4.tgz", - "integrity": "sha512-3HANl8i9DKnUA89P4KEgVNN28EjSeDCmvEqbzOAuxCFDzdBZzjUl99zgnGpOUumvW5lvJo2HKcjrsc+tfyv1Hw==", - "bundleDependencies": [ - "@isaacs/string-locale-compare", - "@npmcli/arborist", - "@npmcli/ci-detect", - "@npmcli/config", - "@npmcli/fs", - "@npmcli/map-workspaces", - "@npmcli/package-json", - "@npmcli/run-script", - "abbrev", - "archy", - "cacache", - "chalk", - "chownr", - "cli-columns", - "cli-table3", - "columnify", - "fastest-levenshtein", - "fs-minipass", - "glob", - "graceful-fs", - "hosted-git-info", - "ini", - "init-package-json", - "is-cidr", - "json-parse-even-better-errors", - "libnpmaccess", - "libnpmdiff", - "libnpmexec", - "libnpmfund", - "libnpmhook", - "libnpmorg", - "libnpmpack", - "libnpmpublish", - "libnpmsearch", - "libnpmteam", - "libnpmversion", - "make-fetch-happen", - "minimatch", - "minipass", - "minipass-pipeline", - "mkdirp", - "mkdirp-infer-owner", - "ms", - "node-gyp", - "nopt", - "npm-audit-report", - "npm-install-checks", - "npm-package-arg", - "npm-pick-manifest", - "npm-profile", - "npm-registry-fetch", - "npm-user-validate", - "npmlog", - "opener", - "p-map", - "pacote", - "parse-conflict-json", - "proc-log", - "qrcode-terminal", - "read", - "read-package-json", - "read-package-json-fast", - "readdir-scoped-modules", - "rimraf", - "semver", - "ssri", - "tar", - "text-table", - "tiny-relative-date", - "treeverse", - "validate-npm-package-name", - "which", - "write-file-atomic" - ], + "node_modules/babel-watch": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/babel-watch/-/babel-watch-2.0.8.tgz", + "integrity": "sha512-dmuUumB5Mv0SOfDuQvJ2Dmx/4EYi76spzf/8wpH7D1DnL+3GEm4uIqY+/N+4CetAh0HLSZVl894bDD9pDdfzKw==", + "dev": true, "dependencies": { - "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/arborist": "^5.6.3", - "@npmcli/ci-detect": "^2.0.0", - "@npmcli/config": "^4.2.1", - "@npmcli/fs": "^2.1.0", - "@npmcli/map-workspaces": "^2.0.3", - "@npmcli/package-json": "^2.0.0", - "@npmcli/run-script": "^4.2.1", - "abbrev": "~1.1.1", - "archy": "~1.0.0", - "cacache": "^16.1.3", - "chalk": "^4.1.2", - "chownr": "^2.0.0", - "cli-columns": "^4.0.0", - "cli-table3": "^0.6.2", - "columnify": "^1.6.0", - "fastest-levenshtein": "^1.0.12", - "fs-minipass": "^2.1.0", - "glob": "^8.0.1", - "graceful-fs": "^4.2.10", - "hosted-git-info": "^5.2.1", - "ini": "^3.0.1", - "init-package-json": "^3.0.2", - "is-cidr": "^4.0.2", - "json-parse-even-better-errors": "^2.3.1", - "libnpmaccess": "^6.0.4", - "libnpmdiff": "^4.0.5", - "libnpmexec": "^4.0.14", - "libnpmfund": "^3.0.5", - "libnpmhook": "^8.0.4", - "libnpmorg": "^4.0.4", - "libnpmpack": "^4.1.3", - "libnpmpublish": "^6.0.5", - "libnpmsearch": "^5.0.4", - "libnpmteam": "^4.0.4", - "libnpmversion": "^3.0.7", - "make-fetch-happen": "^10.2.0", - "minimatch": "^5.1.0", - "minipass": "^3.1.6", - "minipass-pipeline": "^1.2.4", - "mkdirp": "^1.0.4", - "mkdirp-infer-owner": "^2.0.0", - "ms": "^2.1.2", - "node-gyp": "^9.1.0", - "nopt": "^6.0.0", - "npm-audit-report": "^3.0.0", - "npm-install-checks": "^5.0.0", - "npm-package-arg": "^9.1.0", - "npm-pick-manifest": "^7.0.2", - "npm-profile": "^6.2.0", - "npm-registry-fetch": "^13.3.1", - "npm-user-validate": "^1.0.1", - "npmlog": "^6.0.2", - "opener": "^1.5.2", - "p-map": "^4.0.0", - "pacote": "^13.6.2", - "parse-conflict-json": "^2.0.2", - "proc-log": "^2.0.1", - "qrcode-terminal": "^0.12.0", - "read": "~1.0.7", - "read-package-json": "^5.0.2", - "read-package-json-fast": "^2.0.3", - "readdir-scoped-modules": "^1.1.0", - "rimraf": "^3.0.2", - "semver": "^7.3.7", - "ssri": "^9.0.1", - "tar": "^6.1.11", - "text-table": "~0.2.0", - "tiny-relative-date": "^1.3.0", - "treeverse": "^2.0.0", - "validate-npm-package-name": "^4.0.0", - "which": "^2.0.2", - "write-file-atomic": "^4.0.1" + "chokidar": "^1.4.3", + "commander": "^2.9.0", + "lodash.debounce": "^4.0.8", + "source-map-support": "^0.4.0" }, "bin": { - "npm": "bin/npm-cli.js", - "npx": "bin/npx-cli.js" + "babel-watch": "babel-watch.js" }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "peerDependencies": { + "babel-core": "6.5.1 - 6.6.0 || ^6.6.x" } }, - "node_modules/npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", + "node_modules/babel-watch/node_modules/anymatch": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", + "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", "dev": true, "dependencies": { - "path-key": "^2.0.0" + "micromatch": "^2.1.5", + "normalize-path": "^2.0.0" + } + }, + "node_modules/babel-watch/node_modules/anymatch/node_modules/micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha512-LnU2XFEk9xxSJ6rfgAry/ty5qwUTyHYOBU0g4R6tIw5ljwgGIBmiKhRWLw5NpMOnrgUNcDJ4WMp8rl3sYVHLNA==", + "dev": true, + "dependencies": { + "arr-diff": "^2.0.0", + "array-unique": "^0.2.1", + "braces": "^1.8.2", + "expand-brackets": "^0.1.4", + "extglob": "^0.3.1", + "filename-regex": "^2.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.1", + "kind-of": "^3.0.2", + "normalize-path": "^2.0.1", + "object.omit": "^2.0.0", + "parse-glob": "^3.0.4", + "regex-cache": "^0.4.2" }, "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/@colors/colors": { - "version": "1.5.0", - "inBundle": true, - "license": "MIT", - "optional": true, + "node_modules/babel-watch/node_modules/arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha512-dtXTVMkh6VkEEA7OhXnN1Ecb8aAGFdZ1LFxtOCoqj4qkyOJMt7+qs6Ahdy6p/NQCPYsRSXXivhSB/J5E9jmYKA==", + "dev": true, + "dependencies": { + "arr-flatten": "^1.0.1" + }, "engines": { - "node": ">=0.1.90" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/@gar/promisify": { - "version": "1.1.3", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/@isaacs/string-locale-compare": { - "version": "1.1.0", - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/@npmcli/arborist": { - "version": "5.6.3", - "inBundle": true, - "license": "ISC", - "dependencies": { - "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/installed-package-contents": "^1.0.7", - "@npmcli/map-workspaces": "^2.0.3", - "@npmcli/metavuln-calculator": "^3.0.1", - "@npmcli/move-file": "^2.0.0", - "@npmcli/name-from-folder": "^1.0.1", - "@npmcli/node-gyp": "^2.0.0", - "@npmcli/package-json": "^2.0.0", - "@npmcli/query": "^1.2.0", - "@npmcli/run-script": "^4.1.3", - "bin-links": "^3.0.3", - "cacache": "^16.1.3", - "common-ancestor-path": "^1.0.1", - "hosted-git-info": "^5.2.1", - "json-parse-even-better-errors": "^2.3.1", - "json-stringify-nice": "^1.1.4", - "minimatch": "^5.1.0", - "mkdirp": "^1.0.4", - "mkdirp-infer-owner": "^2.0.0", - "nopt": "^6.0.0", - "npm-install-checks": "^5.0.0", - "npm-package-arg": "^9.0.0", - "npm-pick-manifest": "^7.0.2", - "npm-registry-fetch": "^13.0.0", - "npmlog": "^6.0.2", - "pacote": "^13.6.1", - "parse-conflict-json": "^2.0.1", - "proc-log": "^2.0.0", - "promise-all-reject-late": "^1.0.0", - "promise-call-limit": "^1.0.1", - "read-package-json-fast": "^2.0.2", - "readdir-scoped-modules": "^1.1.0", - "rimraf": "^3.0.2", - "semver": "^7.3.7", - "ssri": "^9.0.0", - "treeverse": "^2.0.0", - "walk-up-path": "^1.0.0" - }, - "bin": { - "arborist": "bin/index.js" - }, + "node_modules/babel-watch/node_modules/array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha512-G2n5bG5fSUCpnsXz4+8FUkYsGPkNfLn9YvS66U5qbTIXI2Ynnlo4Bi42bWv+omKUCqz+ejzfClwne0alJWJPhg==", + "dev": true, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/@npmcli/ci-detect": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC", + "node_modules/babel-watch/node_modules/binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/@npmcli/config": { - "version": "4.2.2", - "inBundle": true, - "license": "ISC", + "node_modules/babel-watch/node_modules/braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha512-xU7bpz2ytJl1bH9cgIurjpg/n8Gohy9GTw81heDYLJQ4RU60dlyJsa+atVF2pI0yMMvKxI9HkKwjePCj5XI1hw==", + "dev": true, "dependencies": { - "@npmcli/map-workspaces": "^2.0.2", - "ini": "^3.0.0", - "mkdirp-infer-owner": "^2.0.0", - "nopt": "^6.0.0", - "proc-log": "^2.0.0", - "read-package-json-fast": "^2.0.3", - "semver": "^7.3.5", - "walk-up-path": "^1.0.0" + "expand-range": "^1.8.1", + "preserve": "^0.2.0", + "repeat-element": "^1.1.2" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/@npmcli/disparity-colors": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC", + "node_modules/babel-watch/node_modules/chokidar": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", + "integrity": "sha512-mk8fAWcRUOxY7btlLtitj3A45jOwSAxH4tOFOoEGbVsl6cL6pPMWUy7dwZ/canfj3QEdP6FHSnf/l1c6/WkzVg==", + "deprecated": "Chokidar 2 will break on node v14+. Upgrade to chokidar 3 with 15x less dependencies.", + "dev": true, "dependencies": { - "ansi-styles": "^4.3.0" + "anymatch": "^1.3.0", + "async-each": "^1.0.0", + "glob-parent": "^2.0.0", + "inherits": "^2.0.1", + "is-binary-path": "^1.0.0", + "is-glob": "^2.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.0.0" }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "optionalDependencies": { + "fsevents": "^1.0.0" } }, - "node_modules/npm/node_modules/@npmcli/fs": { - "version": "2.1.2", - "inBundle": true, - "license": "ISC", + "node_modules/babel-watch/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/babel-watch/node_modules/expand-brackets": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha512-hxx03P2dJxss6ceIeri9cmYOT4SRs3Zk3afZwWpOsRqLqprhTR8u++SlC+sFGsQr7WGFPdMF7Gjc1njDLDK6UA==", + "dev": true, "dependencies": { - "@gar/promisify": "^1.1.3", - "semver": "^7.3.5" + "is-posix-bracket": "^0.1.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/@npmcli/git": { - "version": "3.0.2", - "inBundle": true, - "license": "ISC", + "node_modules/babel-watch/node_modules/extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha512-1FOj1LOwn42TMrruOHGt18HemVnbwAmAak7krWk+wa93KXxGbK+2jpezm+ytJYDaBX0/SPLZFHKM7m+tKobWGg==", + "dev": true, "dependencies": { - "@npmcli/promise-spawn": "^3.0.0", - "lru-cache": "^7.4.4", - "mkdirp": "^1.0.4", - "npm-pick-manifest": "^7.0.0", - "proc-log": "^2.0.0", - "promise-inflight": "^1.0.1", - "promise-retry": "^2.0.1", - "semver": "^7.3.5", - "which": "^2.0.2" + "is-extglob": "^1.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/@npmcli/installed-package-contents": { - "version": "1.0.7", - "inBundle": true, - "license": "ISC", + "node_modules/babel-watch/node_modules/fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "deprecated": "fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], "dependencies": { - "npm-bundled": "^1.1.1", - "npm-normalize-package-bin": "^1.0.1" - }, - "bin": { - "installed-package-contents": "index.js" + "bindings": "^1.5.0", + "nan": "^2.12.1" }, "engines": { - "node": ">= 10" + "node": ">= 4.0" } }, - "node_modules/npm/node_modules/@npmcli/installed-package-contents/node_modules/npm-bundled": { - "version": "1.1.2", - "inBundle": true, - "license": "ISC", + "node_modules/babel-watch/node_modules/glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha512-JDYOvfxio/t42HKdxkAYaCiBN7oYiuxykOxKxdaUW5Qn0zaYN3gRQWolrwdnf0shM9/EP0ebuuTmyoXNr1cC5w==", + "dev": true, "dependencies": { - "npm-normalize-package-bin": "^1.0.1" + "is-glob": "^2.0.0" } }, - "node_modules/npm/node_modules/@npmcli/map-workspaces": { - "version": "2.0.4", - "inBundle": true, - "license": "ISC", + "node_modules/babel-watch/node_modules/is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==", + "dev": true, "dependencies": { - "@npmcli/name-from-folder": "^1.0.1", - "glob": "^8.0.1", - "minimatch": "^5.0.1", - "read-package-json-fast": "^2.0.3" + "binary-extensions": "^1.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { - "version": "3.1.1", - "inBundle": true, - "license": "ISC", - "dependencies": { - "cacache": "^16.0.0", - "json-parse-even-better-errors": "^2.3.1", - "pacote": "^13.0.3", - "semver": "^7.3.5" - }, + "node_modules/babel-watch/node_modules/is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww==", + "dev": true, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/@npmcli/move-file": { + "node_modules/babel-watch/node_modules/is-glob": { "version": "2.0.1", - "inBundle": true, - "license": "MIT", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg==", + "dev": true, "dependencies": { - "mkdirp": "^1.0.4", - "rimraf": "^3.0.2" + "is-extglob": "^1.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/@npmcli/name-from-folder": { - "version": "1.0.1", - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/@npmcli/node-gyp": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/@npmcli/package-json": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC", + "node_modules/babel-watch/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, "dependencies": { - "json-parse-even-better-errors": "^2.3.1" + "is-buffer": "^1.1.5" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/@npmcli/promise-spawn": { - "version": "3.0.0", - "inBundle": true, - "license": "ISC", + "node_modules/babel-watch/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "dev": true, "dependencies": { - "infer-owner": "^1.0.4" + "remove-trailing-separator": "^1.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/@npmcli/query": { - "version": "1.2.0", - "inBundle": true, - "license": "ISC", + "node_modules/babel-watch/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, "dependencies": { - "npm-package-arg": "^9.1.0", - "postcss-selector-parser": "^6.0.10", - "semver": "^7.3.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, - "node_modules/npm/node_modules/@npmcli/run-script": { - "version": "4.2.1", - "inBundle": true, - "license": "ISC", + "node_modules/babel-watch/node_modules/readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, "dependencies": { - "@npmcli/node-gyp": "^2.0.0", - "@npmcli/promise-spawn": "^3.0.0", - "node-gyp": "^9.0.0", - "read-package-json-fast": "^2.0.3", - "which": "^2.0.2" + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=0.10" } }, - "node_modules/npm/node_modules/@tootallnate/once": { - "version": "2.0.0", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 10" - } + "node_modules/babel-watch/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true }, - "node_modules/npm/node_modules/abbrev": { + "node_modules/babel-watch/node_modules/string_decoder": { "version": "1.1.1", - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/agent-base": { - "version": "6.0.2", - "inBundle": true, - "license": "MIT", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/npm/node_modules/agentkeepalive": { - "version": "4.2.1", - "inBundle": true, - "license": "MIT", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, "dependencies": { - "debug": "^4.1.0", - "depd": "^1.1.2", - "humanize-ms": "^1.2.1" - }, - "engines": { - "node": ">= 8.0.0" + "safe-buffer": "~5.1.0" } }, - "node_modules/npm/node_modules/aggregate-error": { - "version": "3.1.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" + "node_modules/bail": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz", + "integrity": "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/npm/node_modules/ansi-regex": { - "version": "5.0.1", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, - "node_modules/npm/node_modules/ansi-styles": { - "version": "4.3.0", - "inBundle": true, - "license": "MIT", + "node_modules/base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/aproba": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/archy": { + "node_modules/base/node_modules/define-property": { "version": "1.0.0", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/are-we-there-yet": { - "version": "3.0.1", - "inBundle": true, - "license": "ISC", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "dev": true, "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" + "is-descriptor": "^1.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/asap": { - "version": "2.0.6", - "inBundle": true, - "license": "MIT" + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, - "node_modules/npm/node_modules/balanced-match": { + "node_modules/bcrypt-pbkdf": { "version": "1.0.2", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/bin-links": { - "version": "3.0.3", - "inBundle": true, - "license": "ISC", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", "dependencies": { - "cmd-shim": "^5.0.0", - "mkdirp-infer-owner": "^2.0.0", - "npm-normalize-package-bin": "^2.0.0", - "read-cmd-shim": "^3.0.0", - "rimraf": "^3.0.0", - "write-file-atomic": "^4.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "tweetnacl": "^0.14.3" } }, - "node_modules/npm/node_modules/bin-links/node_modules/npm-normalize-package-bin": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC", + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "*" } }, - "node_modules/npm/node_modules/binary-extensions": { + "node_modules/binary-extensions": { "version": "2.2.0", - "inBundle": true, - "license": "MIT", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "optional": true, "engines": { "node": ">=8" } }, - "node_modules/npm/node_modules/brace-expansion": { - "version": "2.0.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/npm/node_modules/builtins": { - "version": "5.0.1", - "inBundle": true, - "license": "MIT", + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "optional": true, "dependencies": { - "semver": "^7.0.0" + "file-uri-to-path": "1.0.0" } }, - "node_modules/npm/node_modules/cacache": { - "version": "16.1.3", - "inBundle": true, - "license": "ISC", + "node_modules/body-parser": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", "dependencies": { - "@npmcli/fs": "^2.1.0", - "@npmcli/move-file": "^2.0.0", - "chownr": "^2.0.0", - "fs-minipass": "^2.1.0", - "glob": "^8.0.1", - "infer-owner": "^1.0.4", - "lru-cache": "^7.7.1", - "minipass": "^3.1.6", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "mkdirp": "^1.0.4", - "p-map": "^4.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^3.0.2", - "ssri": "^9.0.0", - "tar": "^6.1.11", - "unique-filename": "^2.0.0" + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/npm/node_modules/chalk": { - "version": "4.1.2", - "inBundle": true, - "license": "MIT", + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "ms": "2.0.0" } }, - "node_modules/npm/node_modules/chownr": { + "node_modules/body-parser/node_modules/ms": { "version": "2.0.0", - "inBundle": true, - "license": "ISC", - "engines": { - "node": ">=10" - } + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/npm/node_modules/cidr-regex": { - "version": "3.1.1", - "inBundle": true, - "license": "BSD-2-Clause", + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" + }, + "node_modules/boxen": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", + "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", + "dev": true, "dependencies": { - "ip-regex": "^4.1.0" + "ansi-align": "^2.0.0", + "camelcase": "^4.0.0", + "chalk": "^2.0.1", + "cli-boxes": "^1.0.0", + "string-width": "^2.0.0", + "term-size": "^1.2.0", + "widest-line": "^2.0.0" }, "engines": { - "node": ">=10" + "node": ">=4" } }, - "node_modules/npm/node_modules/clean-stack": { - "version": "2.2.0", - "inBundle": true, - "license": "MIT", + "node_modules/boxen/node_modules/camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw==", + "dev": true, "engines": { - "node": ">=6" + "node": ">=4" } }, - "node_modules/npm/node_modules/cli-columns": { - "version": "4.0.0", - "inBundle": true, - "license": "MIT", + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dependencies": { - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">= 10" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/npm/node_modules/cli-table3": { - "version": "0.6.2", - "inBundle": true, - "license": "MIT", + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dependencies": { - "string-width": "^4.2.0" + "fill-range": "^7.0.1" }, "engines": { - "node": "10.* || >= 12.*" - }, - "optionalDependencies": { - "@colors/colors": "1.5.0" + "node": ">=8" } }, - "node_modules/npm/node_modules/clone": { - "version": "1.0.4", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.8" - } + "node_modules/browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", + "dev": true }, - "node_modules/npm/node_modules/cmd-shim": { - "version": "5.0.0", - "inBundle": true, - "license": "ISC", + "node_modules/browser-resolve": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", + "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", + "dev": true, "dependencies": { - "mkdirp-infer-owner": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "resolve": "1.1.7" } }, - "node_modules/npm/node_modules/color-convert": { - "version": "2.0.1", - "inBundle": true, - "license": "MIT", + "node_modules/browser-resolve/node_modules/resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg==", + "dev": true + }, + "node_modules/browserslist": { + "version": "4.21.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", + "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], "dependencies": { - "color-name": "~1.1.4" + "caniuse-lite": "^1.0.30001449", + "electron-to-chromium": "^1.4.284", + "node-releases": "^2.0.8", + "update-browserslist-db": "^1.0.10" + }, + "bin": { + "browserslist": "cli.js" }, "engines": { - "node": ">=7.0.0" + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/npm/node_modules/color-name": { - "version": "1.1.4", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/color-support": { - "version": "1.1.3", - "inBundle": true, - "license": "ISC", - "bin": { - "color-support": "bin.js" + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "dependencies": { + "node-int64": "^0.4.0" } }, - "node_modules/npm/node_modules/columnify": { - "version": "1.6.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "strip-ansi": "^6.0.1", - "wcwidth": "^1.0.0" - }, - "engines": { - "node": ">=8.0.0" + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" } }, - "node_modules/npm/node_modules/common-ancestor-path": { - "version": "1.0.1", - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/concat-map": { - "version": "0.0.1", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/console-control-strings": { - "version": "1.1.0", - "inBundle": true, - "license": "ISC" + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true }, - "node_modules/npm/node_modules/cssesc": { - "version": "3.0.0", - "inBundle": true, - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "engines": { - "node": ">=4" + "node": ">= 0.8" } }, - "node_modules/npm/node_modules/debug": { - "version": "4.3.4", - "inBundle": true, - "license": "MIT", + "node_modules/cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/npm/node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/debuglog": { - "version": "1.0.1", - "inBundle": true, - "license": "MIT", "engines": { - "node": "*" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/defaults": { - "version": "1.0.3", - "inBundle": true, - "license": "MIT", + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", "dependencies": { - "clone": "^1.0.2" + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/npm/node_modules/delegates": { - "version": "1.0.0", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/depd": { - "version": "1.1.2", - "inBundle": true, - "license": "MIT", + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, "engines": { - "node": ">= 0.6" - } - }, - "node_modules/npm/node_modules/dezalgo": { - "version": "1.0.4", - "inBundle": true, - "license": "ISC", - "dependencies": { - "asap": "^2.0.0", - "wrappy": "1" + "node": ">=6" } }, - "node_modules/npm/node_modules/diff": { - "version": "5.1.0", - "inBundle": true, - "license": "BSD-3-Clause", + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, "engines": { - "node": ">=0.3.1" + "node": ">=6" } }, - "node_modules/npm/node_modules/emoji-regex": { - "version": "8.0.0", - "inBundle": true, - "license": "MIT" + "node_modules/caniuse-lite": { + "version": "1.0.30001480", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001480.tgz", + "integrity": "sha512-q7cpoPPvZYgtyC4VaBSN0Bt+PJ4c4EYRf0DrduInOz2SkFpHD5p3LnvEpqBp7UnJn+8x1Ogl1s38saUxe+ihQQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] }, - "node_modules/npm/node_modules/encoding": { - "version": "0.1.13", - "inBundle": true, - "license": "MIT", - "optional": true, + "node_modules/capture-exit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", + "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", + "dev": true, "dependencies": { - "iconv-lite": "^0.6.2" + "rsvp": "^4.8.4" + }, + "engines": { + "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/npm/node_modules/env-paths": { - "version": "2.2.1", - "inBundle": true, - "license": "MIT", + "node_modules/capture-stack-trace": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.2.tgz", + "integrity": "sha512-X/WM2UQs6VMHUtjUDnZTRI+i1crWteJySFzr9UpGoQa4WQffXVTTXuekjl7TjZRlcF2XfjgITT0HxZ9RnxeT0w==", + "dev": true, "engines": { - "node": ">=6" + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/npm/node_modules/err-code": { - "version": "2.0.3", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/fastest-levenshtein": { - "version": "1.0.12", - "inBundle": true, - "license": "MIT" + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" }, - "node_modules/npm/node_modules/fs-minipass": { - "version": "2.1.0", - "inBundle": true, - "license": "ISC", + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dependencies": { - "minipass": "^3.0.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" }, "engines": { - "node": ">= 8" + "node": ">=4" } }, - "node_modules/npm/node_modules/fs.realpath": { - "version": "1.0.0", - "inBundle": true, - "license": "ISC" + "node_modules/character-entities": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", + "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } }, - "node_modules/npm/node_modules/function-bind": { - "version": "1.1.1", - "inBundle": true, - "license": "MIT" + "node_modules/character-entities-legacy": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", + "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", + "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } }, - "node_modules/npm/node_modules/gauge": { - "version": "4.0.4", - "inBundle": true, - "license": "ISC", + "node_modules/cheerio": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz", + "integrity": "sha512-8/MzidM6G/TgRelkzDG13y3Y9LxBjCb+8yOEZ9+wwq5gVF2w2pV0wmHvjfT0RvuxGyR7UEuK36r+yYMbT4uKgA==", "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.3", - "console-control-strings": "^1.1.0", - "has-unicode": "^2.0.1", - "signal-exit": "^3.0.7", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.5" + "css-select": "~1.2.0", + "dom-serializer": "~0.1.0", + "entities": "~1.1.1", + "htmlparser2": "^3.9.1", + "lodash.assignin": "^4.0.9", + "lodash.bind": "^4.1.4", + "lodash.defaults": "^4.0.1", + "lodash.filter": "^4.4.0", + "lodash.flatten": "^4.2.0", + "lodash.foreach": "^4.3.0", + "lodash.map": "^4.4.0", + "lodash.merge": "^4.4.0", + "lodash.pick": "^4.2.1", + "lodash.reduce": "^4.4.0", + "lodash.reject": "^4.4.0", + "lodash.some": "^4.4.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">= 0.6" } }, - "node_modules/npm/node_modules/glob": { - "version": "8.0.3", - "inBundle": true, - "license": "ISC", + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "optional": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" }, "engines": { - "node": ">=12" + "node": ">= 8.10.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "optionalDependencies": { + "fsevents": "~2.3.2" } }, - "node_modules/npm/node_modules/graceful-fs": { - "version": "4.2.10", - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/has": { + "node_modules/chrome-trace-event": { "version": "1.0.3", - "inBundle": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.1" - }, + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true, "engines": { - "node": ">= 0.4.0" + "node": ">=6.0" } }, - "node_modules/npm/node_modules/has-flag": { - "version": "4.0.0", - "inBundle": true, - "license": "MIT", + "node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" + }, + "node_modules/class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "dependencies": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/has-unicode": { - "version": "2.0.1", - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/hosted-git-info": { - "version": "5.2.1", - "inBundle": true, - "license": "ISC", + "node_modules/class-utils/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, "dependencies": { - "lru-cache": "^7.5.1" + "is-descriptor": "^0.1.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/http-cache-semantics": { - "version": "4.1.1", - "inBundle": true, - "license": "BSD-2-Clause" - }, - "node_modules/npm/node_modules/http-proxy-agent": { - "version": "5.0.0", - "inBundle": true, - "license": "MIT", + "node_modules/class-utils/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", + "dev": true, "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" + "kind-of": "^3.0.2" }, "engines": { - "node": ">= 6" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/https-proxy-agent": { - "version": "5.0.1", - "inBundle": true, - "license": "MIT", + "node_modules/class-utils/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, "dependencies": { - "agent-base": "6", - "debug": "4" + "is-buffer": "^1.1.5" }, "engines": { - "node": ">= 6" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/humanize-ms": { - "version": "1.2.1", - "inBundle": true, - "license": "MIT", + "node_modules/class-utils/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", + "dev": true, "dependencies": { - "ms": "^2.0.0" + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/iconv-lite": { - "version": "0.6.3", - "inBundle": true, - "license": "MIT", - "optional": true, + "node_modules/class-utils/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" + "is-buffer": "^1.1.5" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/ignore-walk": { - "version": "5.0.1", - "inBundle": true, - "license": "ISC", + "node_modules/class-utils/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, "dependencies": { - "minimatch": "^5.0.1" + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/imurmurhash": { - "version": "0.1.4", - "inBundle": true, - "license": "MIT", + "node_modules/class-utils/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, "engines": { - "node": ">=0.8.19" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/indent-string": { - "version": "4.0.0", - "inBundle": true, - "license": "MIT", + "node_modules/classnames": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", + "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" + }, + "node_modules/cli-boxes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", + "integrity": "sha512-3Fo5wu8Ytle8q9iCzS4D2MWVL2X7JVWRiS1BnXbTFDhS9c/REkM9vd1AmabsoZoY5/dGi5TT9iKL8Kb6DeBRQg==", + "dev": true, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/infer-owner": { - "version": "1.0.4", - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/inflight": { - "version": "1.0.6", - "inBundle": true, - "license": "ISC", + "node_modules/cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, "dependencies": { - "once": "^1.3.0", - "wrappy": "1" + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" } }, - "node_modules/npm/node_modules/inherits": { - "version": "2.0.4", - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/ini": { - "version": "3.0.1", - "inBundle": true, - "license": "ISC", + "node_modules/cliui/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=6" } }, - "node_modules/npm/node_modules/init-package-json": { - "version": "3.0.2", - "inBundle": true, - "license": "ISC", + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, "dependencies": { - "npm-package-arg": "^9.0.1", - "promzard": "^0.3.0", - "read": "^1.0.7", - "read-package-json": "^5.0.0", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4", - "validate-npm-package-name": "^4.0.0" + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=6" } }, - "node_modules/npm/node_modules/ip": { - "version": "2.0.0", - "inBundle": true, - "license": "MIT" + "node_modules/clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "engines": { + "node": ">=6" + } }, - "node_modules/npm/node_modules/ip-regex": { - "version": "4.3.0", - "inBundle": true, - "license": "MIT", + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, "engines": { - "node": ">=8" + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" } }, - "node_modules/npm/node_modules/is-cidr": { - "version": "4.0.2", - "inBundle": true, - "license": "BSD-2-Clause", + "node_modules/collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==", + "dev": true, "dependencies": { - "cidr-regex": "^3.1.1" + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" }, "engines": { - "node": ">=10" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/is-core-module": { - "version": "2.10.0", - "inBundle": true, - "license": "MIT", + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "color-convert": "^1.9.3", + "color-string": "^1.6.0" } }, - "node_modules/npm/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" } }, - "node_modules/npm/node_modules/is-lambda": { - "version": "1.0.1", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/isexe": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "inBundle": true, - "license": "MIT" + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, - "node_modules/npm/node_modules/json-stringify-nice": { - "version": "1.1.4", - "inBundle": true, - "license": "ISC", - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" } }, - "node_modules/npm/node_modules/jsonparse": { - "version": "1.3.1", - "engines": [ - "node >= 0.2.0" - ], - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/just-diff": { - "version": "5.1.1", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/just-diff-apply": { - "version": "5.4.1", - "inBundle": true, - "license": "MIT" + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true }, - "node_modules/npm/node_modules/libnpmaccess": { - "version": "6.0.4", - "inBundle": true, - "license": "ISC", + "node_modules/colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", "dependencies": { - "aproba": "^2.0.0", - "minipass": "^3.1.1", - "npm-package-arg": "^9.0.1", - "npm-registry-fetch": "^13.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "color": "^3.1.3", + "text-hex": "1.0.x" } }, - "node_modules/npm/node_modules/libnpmdiff": { - "version": "4.0.5", - "inBundle": true, - "license": "ISC", + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dependencies": { - "@npmcli/disparity-colors": "^2.0.0", - "@npmcli/installed-package-contents": "^1.0.7", - "binary-extensions": "^2.2.0", - "diff": "^5.1.0", - "minimatch": "^5.0.1", - "npm-package-arg": "^9.0.1", - "pacote": "^13.6.1", - "tar": "^6.1.0" + "delayed-stream": "~1.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">= 0.8" } }, - "node_modules/npm/node_modules/libnpmexec": { - "version": "4.0.14", - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/arborist": "^5.6.3", - "@npmcli/ci-detect": "^2.0.0", - "@npmcli/fs": "^2.1.1", - "@npmcli/run-script": "^4.2.0", - "chalk": "^4.1.0", - "mkdirp-infer-owner": "^2.0.0", - "npm-package-arg": "^9.0.1", - "npmlog": "^6.0.2", - "pacote": "^13.6.1", - "proc-log": "^2.0.0", - "read": "^1.0.7", - "read-package-json-fast": "^2.0.2", - "semver": "^7.3.7", - "walk-up-path": "^1.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node_modules/comma-separated-tokens": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", + "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/npm/node_modules/libnpmfund": { - "version": "3.0.5", - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/arborist": "^5.6.3" - }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">= 6" } }, - "node_modules/npm/node_modules/libnpmhook": { - "version": "8.0.4", - "inBundle": true, - "license": "ISC", + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, + "node_modules/component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "node_modules/compute-scroll-into-view": { + "version": "1.0.20", + "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.20.tgz", + "integrity": "sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/configstore": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.5.tgz", + "integrity": "sha512-nlOhI4+fdzoK5xmJ+NY+1gZK56bwEaWZr8fYuXohZ9Vkc1o3a4T/R3M+yE/w7x/ZVJ1zF8c+oaOvF0dztdUgmA==", + "dev": true, "dependencies": { - "aproba": "^2.0.0", - "npm-registry-fetch": "^13.0.0" + "dot-prop": "^4.2.1", + "graceful-fs": "^4.1.2", + "make-dir": "^1.0.0", + "unique-string": "^1.0.0", + "write-file-atomic": "^2.0.0", + "xdg-basedir": "^3.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=4" } }, - "node_modules/npm/node_modules/libnpmorg": { - "version": "4.0.4", - "inBundle": true, - "license": "ISC", + "node_modules/configstore/node_modules/make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "dev": true, "dependencies": { - "aproba": "^2.0.0", - "npm-registry-fetch": "^13.0.0" + "pify": "^3.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=4" } }, - "node_modules/npm/node_modules/libnpmpack": { - "version": "4.1.3", - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/run-script": "^4.1.3", - "npm-package-arg": "^9.0.1", - "pacote": "^13.6.1" - }, + "node_modules/configstore/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=4" } }, - "node_modules/npm/node_modules/libnpmpublish": { - "version": "6.0.5", - "inBundle": true, - "license": "ISC", + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "dependencies": { - "normalize-package-data": "^4.0.0", - "npm-package-arg": "^9.0.1", - "npm-registry-fetch": "^13.0.0", - "semver": "^7.3.7", - "ssri": "^9.0.0" + "safe-buffer": "5.2.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">= 0.6" } }, - "node_modules/npm/node_modules/libnpmsearch": { - "version": "5.0.4", - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-registry-fetch": "^13.0.0" - }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">= 0.6" } }, - "node_modules/npm/node_modules/libnpmteam": { - "version": "4.0.4", - "inBundle": true, - "license": "ISC", - "dependencies": { - "aproba": "^2.0.0", - "npm-registry-fetch": "^13.0.0" - }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, + "node_modules/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">= 0.6" } }, - "node_modules/npm/node_modules/libnpmversion": { - "version": "3.0.7", - "inBundle": true, - "license": "ISC", + "node_modules/cookie-parser": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz", + "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==", "dependencies": { - "@npmcli/git": "^3.0.0", - "@npmcli/run-script": "^4.1.3", - "json-parse-even-better-errors": "^2.3.1", - "proc-log": "^2.0.0", - "semver": "^7.3.7" + "cookie": "0.4.1", + "cookie-signature": "1.0.6" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">= 0.8.0" } }, - "node_modules/npm/node_modules/lru-cache": { - "version": "7.13.2", - "inBundle": true, - "license": "ISC", - "engines": { - "node": ">=12" - } + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, - "node_modules/npm/node_modules/make-fetch-happen": { - "version": "10.2.1", - "inBundle": true, - "license": "ISC", - "dependencies": { - "agentkeepalive": "^4.2.1", - "cacache": "^16.1.0", - "http-cache-semantics": "^4.1.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^7.7.1", - "minipass": "^3.1.6", - "minipass-collect": "^1.0.2", - "minipass-fetch": "^2.0.3", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.3", - "promise-retry": "^2.0.1", - "socks-proxy-agent": "^7.0.0", - "ssri": "^9.0.0" - }, + "node_modules/copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==", + "dev": true, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/minimatch": { - "version": "5.1.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" + "node_modules/core-js": { + "version": "3.31.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.31.1.tgz", + "integrity": "sha512-2sKLtfq1eFST7l7v62zaqXacPc7uG8ZAya8ogijLhTtaKNcpzpB4TMoTw2Si+8GYKRwFPMMtUT0263QFWFfqyQ==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" } }, - "node_modules/npm/node_modules/minipass": { - "version": "3.3.4", - "inBundle": true, - "license": "ISC", + "node_modules/core-js-compat": { + "version": "3.30.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.30.1.tgz", + "integrity": "sha512-d690npR7MC6P0gq4npTl5n2VQeNAmUrJ90n+MHiKS7W2+xno4o3F5GDEuylSdi6EJ3VssibSGXOa1r3YXD3Mhw==", + "dev": true, "dependencies": { - "yallist": "^4.0.0" + "browserslist": "^4.21.5" }, - "engines": { - "node": ">=8" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" } }, - "node_modules/npm/node_modules/minipass-collect": { + "node_modules/core-util-is": { "version": "1.0.2", - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" }, - "node_modules/npm/node_modules/minipass-fetch": { - "version": "2.1.1", - "inBundle": true, - "license": "MIT", + "node_modules/create-error-class": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", + "integrity": "sha512-gYTKKexFO3kh200H1Nit76sRwRtOY32vQd3jpAQKpLtZqyNsSQNfI4N7o3eP2wUjV35pTWKRYqFUDBvUha/Pkw==", + "dev": true, "dependencies": { - "minipass": "^3.1.6", - "minipass-sized": "^1.0.3", - "minizlib": "^2.1.2" + "capture-stack-trace": "^1.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - }, - "optionalDependencies": { - "encoding": "^0.1.13" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/minipass-flush": { - "version": "1.0.5", - "inBundle": true, - "license": "ISC", + "node_modules/cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", "dependencies": { - "minipass": "^3.0.0" + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" }, "engines": { - "node": ">= 8" + "node": ">=4.8" } }, - "node_modules/npm/node_modules/minipass-json-stream": { - "version": "1.0.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "jsonparse": "^1.3.1", - "minipass": "^3.0.0" + "node_modules/cross-spawn/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" } }, - "node_modules/npm/node_modules/minipass-pipeline": { - "version": "1.2.4", - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, + "node_modules/crypto-random-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", + "integrity": "sha512-GsVpkFPlycH7/fRR7Dhcmnoii54gV1nz7y4CWyeFS14N+JVBBhY+r8amRHE4BwSYal7BPTDp8isvAlCxyFt3Hg==", + "dev": true, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/npm/node_modules/minipass-sized": { - "version": "1.0.3", - "inBundle": true, - "license": "ISC", + "node_modules/css-loader": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.6.0.tgz", + "integrity": "sha512-M5lSukoWi1If8dhQAUCvj4H8vUt3vOnwbQBH9DdTm/s4Ym2B/3dPMtYZeJmq7Q3S3Pa+I94DcZ7pc9bP14cWIQ==", + "dev": true, "dependencies": { - "minipass": "^3.0.0" + "camelcase": "^5.3.1", + "cssesc": "^3.0.0", + "icss-utils": "^4.1.1", + "loader-utils": "^1.2.3", + "normalize-path": "^3.0.0", + "postcss": "^7.0.32", + "postcss-modules-extract-imports": "^2.0.0", + "postcss-modules-local-by-default": "^3.0.2", + "postcss-modules-scope": "^2.2.0", + "postcss-modules-values": "^3.0.0", + "postcss-value-parser": "^4.1.0", + "schema-utils": "^2.7.0", + "semver": "^6.3.0" }, "engines": { - "node": ">=8" + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" } }, - "node_modules/npm/node_modules/minizlib": { - "version": "2.1.2", - "inBundle": true, - "license": "MIT", + "node_modules/css-loader/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" + "minimist": "^1.2.0" }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/npm/node_modules/mkdirp": { - "version": "1.0.4", - "inBundle": true, - "license": "MIT", "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" + "json5": "lib/cli.js" } }, - "node_modules/npm/node_modules/mkdirp-infer-owner": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC", + "node_modules/css-loader/node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, "dependencies": { - "chownr": "^2.0.0", - "infer-owner": "^1.0.4", - "mkdirp": "^1.0.3" + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" }, "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/ms": { - "version": "2.1.3", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/mute-stream": { - "version": "0.0.8", - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/negotiator": { - "version": "0.6.3", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" + "node": ">=4.0.0" } }, - "node_modules/npm/node_modules/node-gyp": { - "version": "9.1.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "env-paths": "^2.2.0", - "glob": "^7.1.4", - "graceful-fs": "^4.2.6", - "make-fetch-happen": "^10.0.3", - "nopt": "^5.0.0", - "npmlog": "^6.0.0", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.2", - "which": "^2.0.2" - }, - "bin": { - "node-gyp": "bin/node-gyp.js" + "node_modules/css-modules-require-hook": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/css-modules-require-hook/-/css-modules-require-hook-4.2.3.tgz", + "integrity": "sha512-y3eGHAqmDl8JQdr1LYTwBkMxy7CSHCMy9YwpSjDqtGqCCEy9lKq/x/nmHVs+G7C1+xXmyIXjRu0q/MMn8w01mg==", + "dependencies": { + "debug": "^2.2.0", + "generic-names": "^1.0.1", + "glob-to-regexp": "^0.3.0", + "icss-replace-symbols": "^1.0.2", + "lodash": "^4.3.0", + "postcss": "^6.0.1", + "postcss-modules-extract-imports": "^1.0.0", + "postcss-modules-local-by-default": "^1.0.1", + "postcss-modules-resolve-imports": "^1.3.0", + "postcss-modules-scope": "^1.0.0", + "postcss-modules-values": "^1.1.1", + "seekout": "^1.0.1" }, "engines": { - "node": "^12.22 || ^14.13 || >=16" + "node": ">= 4" } }, - "node_modules/npm/node_modules/node-gyp/node_modules/brace-expansion": { - "version": "1.1.11", - "inBundle": true, - "license": "MIT", + "node_modules/css-modules-require-hook/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "ms": "2.0.0" } }, - "node_modules/npm/node_modules/node-gyp/node_modules/glob": { - "version": "7.2.3", - "inBundle": true, - "license": "ISC", + "node_modules/css-modules-require-hook/node_modules/icss-utils": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-3.0.1.tgz", + "integrity": "sha512-ANhVLoEfe0KoC9+z4yiTaXOneB49K6JIXdS+yAgH0NERELpdIT7kkj2XxUPuHafeHnn8umXnECSpsfk1RTaUew==", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "postcss": "^6.0.2" } }, - "node_modules/npm/node_modules/node-gyp/node_modules/minimatch": { - "version": "3.1.2", - "inBundle": true, - "license": "ISC", + "node_modules/css-modules-require-hook/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/css-modules-require-hook/node_modules/postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", "dependencies": { - "brace-expansion": "^1.1.7" + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" }, "engines": { - "node": "*" + "node": ">=4.0.0" } }, - "node_modules/npm/node_modules/node-gyp/node_modules/nopt": { - "version": "5.0.0", - "inBundle": true, - "license": "ISC", + "node_modules/css-modules-require-hook/node_modules/postcss-modules-extract-imports": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.1.tgz", + "integrity": "sha512-6jt9XZwUhwmRUhb/CkyJY020PYaPJsCyt3UjbaWo6XEbH/94Hmv6MP7fG2C5NDU/BcHzyGYxNtHvM+LTf9HrYw==", "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": ">=6" + "postcss": "^6.0.1" } }, - "node_modules/npm/node_modules/nopt": { - "version": "6.0.0", - "inBundle": true, - "license": "ISC", + "node_modules/css-modules-require-hook/node_modules/postcss-modules-local-by-default": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz", + "integrity": "sha512-X4cquUPIaAd86raVrBwO8fwRfkIdbwFu7CTfEOjiZQHVQwlHRSkTgH5NLDmMm5+1hQO8u6dZ+TOOJDbay1hYpA==", "dependencies": { - "abbrev": "^1.0.0" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "css-selector-tokenizer": "^0.7.0", + "postcss": "^6.0.1" } }, - "node_modules/npm/node_modules/normalize-package-data": { - "version": "4.0.1", - "inBundle": true, - "license": "BSD-2-Clause", + "node_modules/css-modules-require-hook/node_modules/postcss-modules-resolve-imports": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/postcss-modules-resolve-imports/-/postcss-modules-resolve-imports-1.3.0.tgz", + "integrity": "sha512-9xECsLcuR1DRu7mVhK6GIkdEeg4sdZXbLEcuEkDh9mKiz+uxDBfDREYiVehINdW0UPF9gbHnb64ZQMuRsqqkDA==", "dependencies": { - "hosted-git-info": "^5.0.0", - "is-core-module": "^2.8.1", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4" + "css-selector-tokenizer": "^0.7.0", + "icss-utils": "^3.0.1", + "minimist": "^1.2.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">= 4" + }, + "peerDependencies": { + "postcss": "^6.0.0" } }, - "node_modules/npm/node_modules/npm-audit-report": { - "version": "3.0.0", - "inBundle": true, - "license": "ISC", + "node_modules/css-modules-require-hook/node_modules/postcss-modules-scope": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz", + "integrity": "sha512-LTYwnA4C1He1BKZXIx1CYiHixdSe9LWYVKadq9lK5aCCMkoOkFyZ7aigt+srfjlRplJY3gIol6KUNefdMQJdlw==", "dependencies": { - "chalk": "^4.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "css-selector-tokenizer": "^0.7.0", + "postcss": "^6.0.1" } }, - "node_modules/npm/node_modules/npm-bundled": { - "version": "2.0.1", - "inBundle": true, - "license": "ISC", + "node_modules/css-modules-require-hook/node_modules/postcss-modules-values": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz", + "integrity": "sha512-i7IFaR9hlQ6/0UgFuqM6YWaCfA1Ej8WMg8A5DggnH1UGKJvTV/ugqq/KaULixzzOi3T/tF6ClBXcHGCzdd5unA==", "dependencies": { - "npm-normalize-package-bin": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "icss-replace-symbols": "^1.1.0", + "postcss": "^6.0.1" } }, - "node_modules/npm/node_modules/npm-bundled/node_modules/npm-normalize-package-bin": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC", + "node_modules/css-modules-require-hook/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/npm-install-checks": { - "version": "5.0.0", - "inBundle": true, - "license": "BSD-2-Clause", + "node_modules/css-select": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "integrity": "sha512-dUQOBoqdR7QwV90WysXPLXG5LO7nhYBgiWVfxF80DKPF8zx1t/pUd2FYy73emg3zrjtM6dzmYgbHKfV2rxiHQA==", "dependencies": { - "semver": "^7.1.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "boolbase": "~1.0.0", + "css-what": "2.1", + "domutils": "1.5.1", + "nth-check": "~1.0.1" } }, - "node_modules/npm/node_modules/npm-normalize-package-bin": { - "version": "1.0.1", - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/npm-package-arg": { - "version": "9.1.0", - "inBundle": true, - "license": "ISC", + "node_modules/css-selector-tokenizer": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.3.tgz", + "integrity": "sha512-jWQv3oCEL5kMErj4wRnK/OPoBi0D+P1FR2cDCKYPaMeD2eW3/mttav8HT4hT1CKopiJI/psEULjkClhvJo4Lvg==", "dependencies": { - "hosted-git-info": "^5.0.0", - "proc-log": "^2.0.1", - "semver": "^7.3.5", - "validate-npm-package-name": "^4.0.0" - }, + "cssesc": "^3.0.0", + "fastparse": "^1.1.2" + } + }, + "node_modules/css-what": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", + "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "*" } }, - "node_modules/npm/node_modules/npm-packlist": { - "version": "5.1.3", - "inBundle": true, - "license": "ISC", - "dependencies": { - "glob": "^8.0.1", - "ignore-walk": "^5.0.1", - "npm-bundled": "^2.0.0", - "npm-normalize-package-bin": "^2.0.0" - }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", "bin": { - "npm-packlist": "bin/index.js" + "cssesc": "bin/cssesc" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=4" } }, - "node_modules/npm/node_modules/npm-packlist/node_modules/npm-normalize-package-bin": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + }, + "node_modules/cssstyle": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.4.0.tgz", + "integrity": "sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==", + "dev": true, + "dependencies": { + "cssom": "0.3.x" } }, - "node_modules/npm/node_modules/npm-pick-manifest": { - "version": "7.0.2", - "inBundle": true, - "license": "ISC", + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", "dependencies": { - "npm-install-checks": "^5.0.0", - "npm-normalize-package-bin": "^2.0.0", - "npm-package-arg": "^9.0.0", - "semver": "^7.3.5" + "assert-plus": "^1.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=0.10" } }, - "node_modules/npm/node_modules/npm-pick-manifest/node_modules/npm-normalize-package-bin": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node_modules/data-urls": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz", + "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==", + "dev": true, + "dependencies": { + "abab": "^2.0.0", + "whatwg-mimetype": "^2.2.0", + "whatwg-url": "^7.0.0" } }, - "node_modules/npm/node_modules/npm-profile": { - "version": "6.2.1", - "inBundle": true, - "license": "ISC", + "node_modules/data-urls/node_modules/whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "dev": true, "dependencies": { - "npm-registry-fetch": "^13.0.1", - "proc-log": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" } }, - "node_modules/npm/node_modules/npm-registry-fetch": { - "version": "13.3.1", - "inBundle": true, - "license": "ISC", + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dependencies": { - "make-fetch-happen": "^10.0.6", - "minipass": "^3.1.6", - "minipass-fetch": "^2.0.3", - "minipass-json-stream": "^1.0.1", - "minizlib": "^2.1.2", - "npm-package-arg": "^9.0.1", - "proc-log": "^2.0.0" + "ms": "2.1.2" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/npm/node_modules/npm-user-validate": { - "version": "1.0.1", - "inBundle": true, - "license": "BSD-2-Clause" + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/npm/node_modules/npmlog": { - "version": "6.0.2", - "inBundle": true, - "license": "ISC", - "dependencies": { - "are-we-there-yet": "^3.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^4.0.3", - "set-blocking": "^2.0.0" - }, + "node_modules/decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "dev": true, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=0.10" } }, - "node_modules/npm/node_modules/once": { - "version": "1.4.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "engines": { + "node": ">=4.0.0" } }, - "node_modules/npm/node_modules/opener": { - "version": "1.5.2", - "inBundle": true, - "license": "(WTFPL OR MIT)", - "bin": { - "opener": "bin/opener-bin.js" + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deep-merge": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/deep-merge/-/deep-merge-1.0.0.tgz", + "integrity": "sha512-cVG7u0dA5OtrgliqM30OLE3OPrB59PjWEjtCG4Go78OI/f/kP6aK4J/cF4E2f0PpN7A1QmBVxh1dKJkrIU6jCQ==", + "dependencies": { + "xtend": "~1.0.3" } }, - "node_modules/npm/node_modules/p-map": { - "version": "4.0.0", - "inBundle": true, - "license": "MIT", + "node_modules/define-properties": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", + "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", + "dev": true, "dependencies": { - "aggregate-error": "^3.0.0" + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/npm/node_modules/pacote": { - "version": "13.6.2", - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/git": "^3.0.0", - "@npmcli/installed-package-contents": "^1.0.7", - "@npmcli/promise-spawn": "^3.0.0", - "@npmcli/run-script": "^4.1.0", - "cacache": "^16.0.0", - "chownr": "^2.0.0", - "fs-minipass": "^2.1.0", - "infer-owner": "^1.0.4", - "minipass": "^3.1.6", - "mkdirp": "^1.0.4", - "npm-package-arg": "^9.0.0", - "npm-packlist": "^5.1.0", - "npm-pick-manifest": "^7.0.0", - "npm-registry-fetch": "^13.0.1", - "proc-log": "^2.0.0", - "promise-retry": "^2.0.1", - "read-package-json": "^5.0.0", - "read-package-json-fast": "^2.0.3", - "rimraf": "^3.0.2", - "ssri": "^9.0.0", - "tar": "^6.1.11" - }, - "bin": { - "pacote": "lib/bin.js" + "node_modules/define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/parse-conflict-json": { - "version": "2.0.2", - "inBundle": true, - "license": "ISC", - "dependencies": { - "json-parse-even-better-errors": "^2.3.1", - "just-diff": "^5.0.1", - "just-diff-apply": "^5.2.0" - }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=0.4.0" } }, - "node_modules/npm/node_modules/path-is-absolute": { - "version": "1.0.1", - "inBundle": true, - "license": "MIT", + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "engines": { - "node": ">=0.10.0" + "node": ">= 0.8" } }, - "node_modules/npm/node_modules/postcss-selector-parser": { - "version": "6.0.10", - "inBundle": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", "engines": { - "node": ">=4" + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-newline": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", + "integrity": "sha512-CwffZFvlJffUg9zZA0uqrjQayUTC8ob94pnr5sFwaVv3IOmkfUHcWH+jXaQK3askE51Cqe8/9Ql/0uXNwqZ8Zg==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/proc-log": { - "version": "2.0.1", - "inBundle": true, - "license": "ISC", + "node_modules/diff-match-patch": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz", + "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==" + }, + "node_modules/diff-sequences": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.9.0.tgz", + "integrity": "sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew==", + "dev": true, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">= 6" } }, - "node_modules/npm/node_modules/promise-all-reject-late": { - "version": "1.0.1", - "inBundle": true, - "license": "ISC", + "node_modules/direction": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/direction/-/direction-1.0.4.tgz", + "integrity": "sha512-GYqKi1aH7PJXxdhTeZBFrg8vUBeKXi+cNprXsC1kpJcbcVnV9wBsrOu1cQEdG0WeQwlfHiy3XvnKfIrJ2R0NzQ==", + "bin": { + "direction": "cli.js" + }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/npm/node_modules/promise-call-limit": { - "version": "1.0.1", - "inBundle": true, - "license": "ISC", - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node_modules/dom-serializer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", + "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", + "dependencies": { + "domelementtype": "^1.3.0", + "entities": "^1.1.1" } }, - "node_modules/npm/node_modules/promise-inflight": { - "version": "1.0.1", - "inBundle": true, - "license": "ISC" + "node_modules/domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" }, - "node_modules/npm/node_modules/promise-retry": { - "version": "2.0.1", - "inBundle": true, - "license": "MIT", + "node_modules/domexception": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", + "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", + "dev": true, "dependencies": { - "err-code": "^2.0.2", - "retry": "^0.12.0" - }, - "engines": { - "node": ">=10" + "webidl-conversions": "^4.0.2" } }, - "node_modules/npm/node_modules/promzard": { - "version": "0.3.0", - "inBundle": true, - "license": "ISC", + "node_modules/domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", "dependencies": { - "read": "1" + "domelementtype": "1" } }, - "node_modules/npm/node_modules/qrcode-terminal": { - "version": "0.12.0", - "inBundle": true, - "bin": { - "qrcode-terminal": "bin/qrcode-terminal.js" + "node_modules/dompurify": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.5.tgz", + "integrity": "sha512-jggCCd+8Iqp4Tsz0nIvpcb22InKEBrGz5dw3EQJMs8HPJDsKbFIO3STYtAvCfDx26Muevn1MHVI0XxjgFfmiSA==" + }, + "node_modules/domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha512-gSu5Oi/I+3wDENBsOWBiRK1eoGxcywYSqg3rR960/+EfY0CF4EX1VPkgHOZ3WiS/Jg2DtliF6BhWcHlfpYUcGw==", + "dependencies": { + "dom-serializer": "0", + "domelementtype": "1" } }, - "node_modules/npm/node_modules/read": { - "version": "1.0.7", - "inBundle": true, - "license": "ISC", + "node_modules/dot-prop": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.1.tgz", + "integrity": "sha512-l0p4+mIuJIua0mhxGoh4a+iNL9bmeK5DvnSVQa6T0OhrVmaEa1XScX5Etc673FePCJOArq/4Pa2cLGODUWTPOQ==", + "dev": true, "dependencies": { - "mute-stream": "~0.0.4" + "is-obj": "^1.0.0" }, "engines": { - "node": ">=0.8" + "node": ">=4" } }, - "node_modules/npm/node_modules/read-cmd-shim": { - "version": "3.0.0", - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } + "node_modules/double-ended-queue": { + "version": "2.1.0-0", + "resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz", + "integrity": "sha512-+BNfZ+deCo8hMNpDqDnvT+c0XpJ5cUa6mqYq89bho2Ifze4URTqRkcwR399hWoTrTkbZ/XJYDgP6rc7pRgffEQ==" }, - "node_modules/npm/node_modules/read-package-json": { - "version": "5.0.2", - "inBundle": true, - "license": "ISC", + "node_modules/draggabilly": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/draggabilly/-/draggabilly-3.0.0.tgz", + "integrity": "sha512-aEs+B6prbMZQMxc9lgTpCBfyCUhRur/VFucHhIOvlvvdARTj7TcDmX/cdOUtqbjJJUh7+agyJXR5Z6IFe1MxwQ==", "dependencies": { - "glob": "^8.0.1", - "json-parse-even-better-errors": "^2.3.1", - "normalize-package-data": "^4.0.0", - "npm-normalize-package-bin": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "get-size": "^3.0.0", + "unidragger": "^3.0.0" } }, - "node_modules/npm/node_modules/read-package-json-fast": { - "version": "2.0.3", - "inBundle": true, - "license": "ISC", + "node_modules/duplexer3": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz", + "integrity": "sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==", + "dev": true + }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", "dependencies": { - "json-parse-even-better-errors": "^2.3.0", - "npm-normalize-package-bin": "^1.0.1" - }, - "engines": { - "node": ">=10" + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" } }, - "node_modules/npm/node_modules/read-package-json/node_modules/npm-normalize-package-bin": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, - "node_modules/npm/node_modules/readable-stream": { - "version": "3.6.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, + "node_modules/electron-to-chromium": { + "version": "1.4.368", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.368.tgz", + "integrity": "sha512-e2aeCAixCj9M7nJxdB/wDjO6mbYX+lJJxSJCXDzlr5YPGYVofuJwGN9nKg2o6wWInjX6XmxRinn3AeJMK81ltw==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true, "engines": { - "node": ">= 6" + "node": ">= 4" } }, - "node_modules/npm/node_modules/readdir-scoped-modules": { - "version": "1.1.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "debuglog": "^1.0.1", - "dezalgo": "^1.0.0", - "graceful-fs": "^4.1.2", - "once": "^1.3.0" - } + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" }, - "node_modules/npm/node_modules/retry": { - "version": "0.12.0", - "inBundle": true, - "license": "MIT", + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", "engines": { - "node": ">= 4" + "node": ">= 0.8" } }, - "node_modules/npm/node_modules/rimraf": { - "version": "3.0.2", - "inBundle": true, - "license": "ISC", + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "once": "^1.4.0" } }, - "node_modules/npm/node_modules/rimraf/node_modules/brace-expansion": { - "version": "1.1.11", - "inBundle": true, - "license": "MIT", + "node_modules/engine.io-client": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.4.0.tgz", + "integrity": "sha512-GyKPDyoEha+XZ7iEqam49vz6auPnNJ9ZBfy89f+rMMas8AuiMWOZ9PVzu8xb9ZC6rafUqiGHSCfu22ih66E+1g==", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.3", + "ws": "~8.11.0", + "xmlhttprequest-ssl": "~2.0.0" } }, - "node_modules/npm/node_modules/rimraf/node_modules/glob": { - "version": "7.2.3", - "inBundle": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, + "node_modules/engine.io-client/node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", "engines": { - "node": "*" + "node": ">=10.0.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/rimraf/node_modules/minimatch": { - "version": "3.1.2", - "inBundle": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" }, - "engines": { - "node": "*" - } - }, - "node_modules/npm/node_modules/safe-buffer": { - "version": "5.2.1", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" + "peerDependenciesMeta": { + "bufferutil": { + "optional": true }, - { - "type": "consulting", - "url": "https://feross.org/support" + "utf-8-validate": { + "optional": true } - ], - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/safer-buffer": { - "version": "2.1.2", - "inBundle": true, - "license": "MIT", - "optional": true - }, - "node_modules/npm/node_modules/semver": { - "version": "7.3.7", - "inBundle": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" } }, - "node_modules/npm/node_modules/set-blocking": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/signal-exit": { - "version": "3.0.7", - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/smart-buffer": { - "version": "4.2.0", - "inBundle": true, - "license": "MIT", + "node_modules/engine.io-parser": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz", + "integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==", "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" + "node": ">=10.0.0" } }, - "node_modules/npm/node_modules/socks": { - "version": "2.7.0", - "inBundle": true, - "license": "MIT", + "node_modules/enhanced-resolve": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.13.0.tgz", + "integrity": "sha512-eyV8f0y1+bzyfh8xAwW/WTSZpLbjhqc4ne9eGSH4Zo2ejdyiNG9pU6mf9DG8a7+Auk6MFTlNOT4Y2y/9k8GKVg==", + "dev": true, "dependencies": { - "ip": "^2.0.0", - "smart-buffer": "^4.2.0" + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" }, "engines": { - "node": ">= 10.13.0", - "npm": ">= 3.0.0" + "node": ">=10.13.0" } }, - "node_modules/npm/node_modules/socks-proxy-agent": { - "version": "7.0.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "agent-base": "^6.0.2", - "debug": "^4.3.3", - "socks": "^2.6.2" + "node_modules/entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" + }, + "node_modules/envinfo": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", + "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", + "dev": true, + "bin": { + "envinfo": "dist/cli.js" }, "engines": { - "node": ">= 10" + "node": ">=4" } }, - "node_modules/npm/node_modules/spdx-correct": { - "version": "3.1.1", - "inBundle": true, - "license": "Apache-2.0", + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" + "is-arrayish": "^0.2.1" } }, - "node_modules/npm/node_modules/spdx-exceptions": { - "version": "2.3.0", - "inBundle": true, - "license": "CC-BY-3.0" - }, - "node_modules/npm/node_modules/spdx-expression-parse": { - "version": "3.0.1", - "inBundle": true, - "license": "MIT", + "node_modules/es-abstract": { + "version": "1.21.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz", + "integrity": "sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==", + "dev": true, "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" + "array-buffer-byte-length": "^1.0.0", + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-set-tostringtag": "^2.0.1", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.2.0", + "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.5", + "is-array-buffer": "^3.0.2", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.10", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "safe-regex-test": "^1.0.0", + "string.prototype.trim": "^1.2.7", + "string.prototype.trimend": "^1.0.6", + "string.prototype.trimstart": "^1.0.6", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/npm/node_modules/spdx-license-ids": { - "version": "3.0.11", - "inBundle": true, - "license": "CC0-1.0" + "node_modules/es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", + "dev": true + }, + "node_modules/es-module-lexer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.2.1.tgz", + "integrity": "sha512-9978wrXM50Y4rTMmW5kXIC09ZdXQZqkE4mxhwkd8VbzsGkXGPgV4zWuqQJgCEzYngdo2dYDa0l8xhX4fkSwJSg==", + "dev": true }, - "node_modules/npm/node_modules/ssri": { - "version": "9.0.1", - "inBundle": true, - "license": "ISC", + "node_modules/es-set-tostringtag": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", + "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "dev": true, "dependencies": { - "minipass": "^3.1.1" + "get-intrinsic": "^1.1.3", + "has": "^1.0.3", + "has-tostringtag": "^1.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/string_decoder": { - "version": "1.3.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" + "node": ">= 0.4" } }, - "node_modules/npm/node_modules/string-width": { - "version": "4.2.3", - "inBundle": true, - "license": "MIT", + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/npm/node_modules/strip-ansi": { - "version": "6.0.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, "engines": { - "node": ">=8" + "node": ">=6" } }, - "node_modules/npm/node_modules/supports-color": { - "version": "7.2.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "engines": { - "node": ">=8" + "node": ">=0.8.0" } }, - "node_modules/npm/node_modules/tar": { - "version": "6.1.11", - "inBundle": true, - "license": "ISC", + "node_modules/escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "dev": true, "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" }, "engines": { - "node": ">= 10" + "node": ">=4.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" } }, - "node_modules/npm/node_modules/text-table": { - "version": "0.2.0", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/tiny-relative-date": { - "version": "1.3.0", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/treeverse": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC", + "node_modules/escodegen/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/unique-filename": { - "version": "2.0.1", - "inBundle": true, - "license": "ISC", + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, "dependencies": { - "unique-slug": "^3.0.0" + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=8.0.0" } }, - "node_modules/npm/node_modules/unique-slug": { - "version": "3.0.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4" + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/util-deprecate": { - "version": "1.0.2", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/validate-npm-package-license": { - "version": "3.0.4", - "inBundle": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" + "node": ">=4" } }, - "node_modules/npm/node_modules/validate-npm-package-name": { - "version": "4.0.0", - "inBundle": true, - "license": "ISC", + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, "dependencies": { - "builtins": "^5.0.0" + "estraverse": "^5.2.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=4.0" } }, - "node_modules/npm/node_modules/walk-up-path": { - "version": "1.0.0", - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/wcwidth": { - "version": "1.0.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "defaults": "^1.0.3" + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" } }, - "node_modules/npm/node_modules/which": { - "version": "2.0.2", - "inBundle": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, "engines": { - "node": ">= 8" + "node": ">=4.0" } }, - "node_modules/npm/node_modules/wide-align": { - "version": "1.1.5", - "inBundle": true, - "license": "ISC", - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/wrappy": { - "version": "1.0.2", - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/write-file-atomic": { - "version": "4.0.2", - "inBundle": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">= 0.6" } }, - "node_modules/npm/node_modules/yallist": { - "version": "4.0.0", - "inBundle": true, - "license": "ISC" + "node_modules/ev-emitter": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ev-emitter/-/ev-emitter-2.1.2.tgz", + "integrity": "sha512-jQ5Ql18hdCQ4qS+RCrbLfz1n+Pags27q5TwMKvZyhp5hh2UULUYZUy1keqj6k6SYsdqIYjnmz7xyyEY0V67B8Q==" }, - "node_modules/nth-check": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", - "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", - "dependencies": { - "boolbase": "~1.0.0" + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "engines": { + "node": ">=0.8.x" } }, - "node_modules/nwsapi": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.4.tgz", - "integrity": "sha512-NHj4rzRo0tQdijE9ZqAx6kYDcoRwYwSYzCA8MY3JzfxlrvEU0jhnhJT9BhqhJs7I/dKcrDm6TyulaRqZPIhN5g==", + "node_modules/exec-sh": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.6.tgz", + "integrity": "sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w==", "dev": true }, - "node_modules/oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "node_modules/execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "dependencies": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, "engines": { - "node": "*" + "node": ">=6" } }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">= 0.8.0" } }, - "node_modules/object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==", + "node_modules/expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==", "dev": true, "dependencies": { - "copy-descriptor": "^0.1.0", + "debug": "^2.3.3", "define-property": "^0.2.5", - "kind-of": "^3.0.3" + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/object-copy/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "node_modules/expand-brackets/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" + "ms": "2.0.0" } }, - "node_modules/object-copy/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", + "node_modules/expand-brackets/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", "dev": true, "dependencies": { - "kind-of": "^3.0.2" + "is-descriptor": "^0.1.0" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/object-copy/node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", + "node_modules/expand-brackets/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", "dev": true, "dependencies": { - "kind-of": "^3.0.2" + "is-extendable": "^0.1.0" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/object-copy/node_modules/is-descriptor": { + "node_modules/expand-brackets/node_modules/is-accessor-descriptor": { "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", "dev": true, "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" + "kind-of": "^3.0.2" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/object-copy/node_modules/is-descriptor/node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/kind-of": { + "node_modules/expand-brackets/node_modules/is-accessor-descriptor/node_modules/kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", @@ -14614,85 +5283,45 @@ "node": ">=0.10.0" } }, - "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==", + "node_modules/expand-brackets/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", "dev": true, "dependencies": { - "isobject": "^3.0.0" + "kind-of": "^3.0.2" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.getownpropertydescriptors": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.5.tgz", - "integrity": "sha512-yDNzckpM6ntyQiGTik1fKV1DcVDRS+w8bvpWNCBanvH5LfRX9O8WTHqQzG4RZwRAM4I0oU7TV11Lj5v0g20ibw==", + "node_modules/expand-brackets/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", "dev": true, "dependencies": { - "array.prototype.reduce": "^1.0.5", - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "is-buffer": "^1.1.5" }, "engines": { - "node": ">= 0.8" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.10.0" } }, - "node_modules/object.omit": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", - "integrity": "sha512-UiAM5mhmIuKLsOvrL+B0U2d1hXHF3bFYWIuH1LMpuV2EJEHG1Ntz06PgLEHjm6VFd87NpH8rastvPoyv6UW2fA==", + "node_modules/expand-brackets/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, "dependencies": { - "for-own": "^0.1.4", - "is-extendable": "^0.1.1" + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/object.omit/node_modules/is-extendable": { + "node_modules/expand-brackets/node_modules/is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", @@ -14701,1402 +5330,1460 @@ "node": ">=0.10.0" } }, - "node_modules/object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", + "node_modules/expand-brackets/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", "dev": true, - "dependencies": { - "isobject": "^3.0.1" - }, "engines": { "node": ">=0.10.0" } }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "node_modules/expand-brackets/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/expand-range": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "integrity": "sha512-AFASGfIlnIbkKPQwX1yHaDjFvh/1gyKJODme52V6IORh69uEYgZp0o9C+qsIGNVEiuuhQU0CSSl++Rlegg1qvA==", + "dev": true, "dependencies": { - "ee-first": "1.1.1" + "fill-range": "^2.1.0" }, "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/one-time": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", - "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", - "dependencies": { - "fn.name": "1.x.x" + "node": ">=0.10.0" } }, - "node_modules/onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "peer": true, + "node_modules/expand-range/node_modules/fill-range": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", + "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", + "dev": true, "dependencies": { - "mimic-fn": "^4.0.0" + "is-number": "^2.1.0", + "isobject": "^2.0.0", + "randomatic": "^3.0.0", + "repeat-element": "^1.1.2", + "repeat-string": "^1.5.2" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/open": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", - "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "node_modules/expand-range/node_modules/is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha512-QUzH43Gfb9+5yckcrSA0VBDwEtDUchrk4F6tfJZQuNzDJbEDB9cZNzSfXGQ1jqmdDY/kl41lUOWM9syA8z8jlg==", + "dev": true, "dependencies": { - "is-docker": "^2.0.0", - "is-wsl": "^2.1.1" + "kind-of": "^3.0.2" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/open/node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "node_modules/expand-range/node_modules/isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==", + "dev": true, "dependencies": { - "is-docker": "^2.0.0" + "isarray": "1.0.0" }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "node_modules/expand-range/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", "dev": true, "dependencies": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" + "is-buffer": "^1.1.5" }, "engines": { - "node": ">= 0.8.0" + "node": ">=0.10.0" } }, - "node_modules/os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==", + "node_modules/expect": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-24.9.0.tgz", + "integrity": "sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q==", "dev": true, - "peer": true, + "dependencies": { + "@jest/types": "^24.9.0", + "ansi-styles": "^3.2.0", + "jest-get-type": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-regex-util": "^24.9.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 6" } }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "node_modules/express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.10.0" } }, - "node_modules/p-each-series": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-1.0.0.tgz", - "integrity": "sha512-J/e9xiZZQNrt+958FFzJ+auItsBGq+UrQ7nE89AUP7UOTtjHnkISANXLdayhVzh538UnLMCSlf13lFfRIAKQOA==", - "dev": true, + "node_modules/express-winston": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/express-winston/-/express-winston-4.2.0.tgz", + "integrity": "sha512-EMD74g63nVHi7pFleQw7KHCxiA1pjF5uCwbCfzGqmFxs9KvlDPIVS3cMGpULm6MshExMT9TjC3SqmRGB9kb7yw==", "dependencies": { - "p-reduce": "^1.0.0" + "chalk": "^2.4.2", + "lodash": "^4.17.21" }, "engines": { - "node": ">=4" + "node": ">= 6" + }, + "peerDependencies": { + "winston": ">=3.x <4" } }, - "node_modules/p-filter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-2.1.0.tgz", - "integrity": "sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==", + "node_modules/express/node_modules/body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", "dependencies": { - "p-map": "^2.0.0" + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" }, "engines": { - "node": ">=8" + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", - "dev": true, + "node_modules/express/node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", "engines": { - "node": ">=4" + "node": ">= 0.6" } }, - "node_modules/p-is-promise": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-3.0.0.tgz", - "integrity": "sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ==", - "engines": { - "node": ">=8" + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" } }, - "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/express/node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", "dependencies": { - "p-try": "^2.0.0" + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.8" } }, - "node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", "dev": true, "dependencies": { - "p-limit": "^2.0.0" + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" }, "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/p-map": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", - "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "node_modules/extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "dependencies": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/p-reduce": { + "node_modules/extglob/node_modules/define-property": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz", - "integrity": "sha512-3Tx1T3oM1xO/Y8Gj0sWyE78EIJZ+t+aEmXUdvQgvGmSMri7aPTHoovbXEreWKkL5j21Er60XAWLTzKbAKYOujQ==", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-retry": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", - "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", "dependencies": { - "@types/retry": "0.12.0", - "retry": "^0.13.1" + "is-descriptor": "^1.0.0" }, "engines": { - "node": ">=8" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/package-json": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", - "integrity": "sha512-q/R5GrMek0vzgoomq6rm9OX+3PQve8sLwTirmK30YB3Cu0Bbt9OX9M/SIUnroN5BGJkzwGsFwDaRGD9EwBOlCA==", + "node_modules/extglob/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", "dev": true, "dependencies": { - "got": "^6.7.1", - "registry-auth-token": "^3.0.1", - "registry-url": "^3.0.3", - "semver": "^5.1.0" + "is-extendable": "^0.1.0" }, "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/package-json/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "node_modules/extglob/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", "dev": true, - "bin": { - "semver": "bin/semver" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "peer": true, - "dependencies": { - "callsites": "^3.0.0" - }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "engines": [ + "node >=0.6.0" + ] + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true, "engines": { - "node": ">=6" + "node": ">= 4.9.1" } }, - "node_modules/parse-entities": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", - "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", - "dependencies": { - "character-entities": "^1.0.0", - "character-entities-legacy": "^1.0.0", - "character-reference-invalid": "^1.0.0", - "is-alphanumerical": "^1.0.0", - "is-decimal": "^1.0.0", - "is-hexadecimal": "^1.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } + "node_modules/fastparse": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", + "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==" }, - "node_modules/parse-glob": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", - "integrity": "sha512-FC5TeK0AwXzq3tUBFtH74naWkPQCEWs4K+xMxWZBlKDWu0bVHXGZa+KKqxKidd7xwhdZ19ZNuF2uO1M/r196HA==", + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", "dev": true, "dependencies": { - "glob-base": "^0.3.0", - "is-dotfile": "^1.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" + "bser": "2.1.1" } }, - "node_modules/parse-glob/node_modules/is-extglob": { + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" + }, + "node_modules/file-saver": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz", + "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==" + }, + "node_modules/file-uri-to-path": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww==", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "optional": true + }, + "node_modules/filename-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "integrity": "sha512-BTCqyBaWBTsauvnHiE8i562+EdJj+oUpkqWp2R1iCoR8f6oo8STRu3of7WJJ0TqWtxN50a5YFpzYK4Jj9esYfQ==", "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/parse-glob/node_modules/is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg==", - "dev": true, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dependencies": { - "is-extglob": "^1.0.0" + "to-regex-range": "^5.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" }, "engines": { - "node": ">=4" + "node": ">= 0.8" } }, - "node_modules/parse-srcset": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz", - "integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==" + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } }, - "node_modules/parse5": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", - "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", - "dev": true + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, "engines": { - "node": ">= 0.8" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" } }, - "node_modules/pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==", + "node_modules/find-cache-dir/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/patch-package": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-6.5.1.tgz", - "integrity": "sha512-I/4Zsalfhc6bphmJTlrLoOcAF87jcxko4q0qsv4bGcurbr8IskEOtdnt9iCmsQVGL1B+iUhSQqweyTLJfCF9rA==", + "node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, "dependencies": { - "@yarnpkg/lockfile": "^1.1.0", - "chalk": "^4.1.2", - "cross-spawn": "^6.0.5", - "find-yarn-workspace-root": "^2.0.0", - "fs-extra": "^9.0.0", - "is-ci": "^2.0.0", - "klaw-sync": "^6.0.0", - "minimist": "^1.2.6", - "open": "^7.4.2", - "rimraf": "^2.6.3", - "semver": "^5.6.0", - "slash": "^2.0.0", - "tmp": "^0.0.33", - "yaml": "^1.10.2" - }, - "bin": { - "patch-package": "index.js" + "locate-path": "^3.0.0" }, "engines": { - "node": ">=10", - "npm": ">5" + "node": ">=6" } }, - "node_modules/patch-package/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/find-yarn-workspace-root": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz", + "integrity": "sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ==", "dependencies": { - "color-convert": "^2.0.1" + "micromatch": "^4.0.2" + } + }, + "node_modules/find-yarn-workspace-root/node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=8.6" } }, - "node_modules/patch-package/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/findandreplacedomtext": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/findandreplacedomtext/-/findandreplacedomtext-0.4.6.tgz", + "integrity": "sha512-CVRIKbEwfWoKTSnLrmyX26WjMY7o0aUUTm0RXN47fF/eHINJa8C+YHZxGagC7gMWVaAEBFwH7uVXuhwZcylWOQ==" + }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "is-callable": "^1.1.3" + } + }, + "node_modules/for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==", + "dev": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=0.10.0" } }, - "node_modules/patch-package/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/for-own": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha512-SKmowqGTJoPzLO1T0BBJpkfp3EMacCMOuH40hOUbrbzElVktk4DioXVM99QkLCyKoiuOmyjgcWMpVz2xjE7LZw==", + "dev": true, "dependencies": { - "color-name": "~1.1.4" + "for-in": "^1.0.1" }, "engines": { - "node": ">=7.0.0" + "node": ">=0.10.0" } }, - "node_modules/patch-package/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/patch-package/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", "engines": { - "node": ">=8" - } - }, - "node_modules/patch-package/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "bin": { - "semver": "bin/semver" + "node": "*" } }, - "node_modules/patch-package/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", "dependencies": { - "has-flag": "^4.0.0" + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" }, "engines": { - "node": ">=8" + "node": ">= 0.12" } }, - "node_modules/path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==", - "dev": true - }, - "node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "engines": { - "node": ">=4" + "node": ">= 0.6" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "node_modules/fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==", + "dev": true, + "dependencies": { + "map-cache": "^0.2.2" + }, "engines": { "node": ">=0.10.0" } }, - "node_modules/path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", - "dev": true - }, - "node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "engines": { - "node": ">=4" + "node": ">= 0.6" } }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" - }, - "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" - }, - "node_modules/path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, + "node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dependencies": { - "pify": "^3.0.0" + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": ">=4" + "node": ">=10" } }, - "node_modules/path-type/node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "node_modules/fs-readdir-recursive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", + "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=4" + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" - }, - "node_modules/picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "node_modules/function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + }, "engines": { - "node": ">=8.6" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true, - "engines": { - "node": ">=6" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true, - "engines": { - "node": ">= 6" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/pkg-conf": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", - "integrity": "sha512-C+VUP+8jis7EsQZIhDYmS5qlNtjv2yP4SNtjXK9AP1ZcTRlnSfuumaTnRfYZnYgUUYVIKqL0fRvmUGDV2fmp6g==", - "peer": true, + "node_modules/generic-names": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/generic-names/-/generic-names-1.0.3.tgz", + "integrity": "sha512-b6OHfQuKasIKM9b6YPkX+KUj/TLBTx3B/1aT1T5F12FEuEqyFMdr59OMS53aoaSw8eVtapdqieX6lbg5opaOhA==", "dependencies": { - "find-up": "^2.0.0", - "load-json-file": "^4.0.0" - }, - "engines": { - "node": ">=4" + "loader-utils": "^0.2.16" } }, - "node_modules/pkg-conf/node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", - "peer": true, - "dependencies": { - "locate-path": "^2.0.0" - }, + "node_modules/generic-names/node_modules/big.js": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", + "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", "engines": { - "node": ">=4" + "node": "*" } }, - "node_modules/pkg-conf/node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", - "peer": true, - "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - }, + "node_modules/generic-names/node_modules/emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha512-knHEZMgs8BB+MInokmNTg/OyPlAddghe1YBgNwJBc5zsJi/uyIcXoSDsL/W9ymOsBoBGdPIHXYJ9+qKFwRwDng==", "engines": { - "node": ">=4" + "node": ">= 0.10" } }, - "node_modules/pkg-conf/node_modules/p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "peer": true, - "dependencies": { - "p-try": "^1.0.0" - }, - "engines": { - "node": ">=4" + "node_modules/generic-names/node_modules/json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha512-4xrs1aW+6N5DalkqSVA8fxh458CXvR99WU8WLKmq4v8eWAL86Xo3BVqyd3SkA9wEVjCMqyvvRRkshAdOnBp5rw==", + "bin": { + "json5": "lib/cli.js" } }, - "node_modules/pkg-conf/node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", - "peer": true, + "node_modules/generic-names/node_modules/loader-utils": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", + "integrity": "sha512-tiv66G0SmiOx+pLWMtGEkfSEejxvb6N6uRrQjfWJIT79W9GMpgKeCAmm9aVBKtd4WEgntciI8CsGqjpDoCWJug==", "dependencies": { - "p-limit": "^1.1.0" - }, - "engines": { - "node": ">=4" + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0", + "object-assign": "^4.0.1" } }, - "node_modules/pkg-conf/node_modules/p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", - "peer": true, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, "engines": { - "node": ">=4" + "node": ">=6.9.0" } }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, "engines": { - "node": ">=8" + "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, + "node_modules/get-intrinsic": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" }, - "engines": { - "node": ">=8" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "node_modules/get-size": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-size/-/get-size-3.0.0.tgz", + "integrity": "sha512-Y8aiXLq4leR7807UY0yuKEwif5s3kbVp1nTv+i4jBeoUzByTLKkLWu/HorS6/pB+7gsB0o7OTogC8AoOOeT0Hw==" + }, + "node_modules/get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", "dev": true, "dependencies": { - "p-locate": "^4.1.0" + "pump": "^3.0.0" }, "engines": { - "node": ">=8" + "node": ">=6" } }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", "dev": true, "dependencies": { - "p-limit": "^2.2.0" + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/pkg-dir/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "node_modules/get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==", "dev": true, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/pn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", - "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", - "dev": true - }, - "node_modules/posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==", - "dev": true, - "engines": { - "node": ">=0.10.0" + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "dependencies": { + "assert-plus": "^1.0.0" } }, - "node_modules/postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dependencies": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" }, "engines": { - "node": ">=6.0.0" + "node": "*" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/postcss-modules-extract-imports": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz", - "integrity": "sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==", + "node_modules/glob-base": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "integrity": "sha512-ab1S1g1EbO7YzauaJLkgLp7DZVAqj9M/dvKlTt8DkXA2tiOIcSMrlVI2J1RZyB5iJVccEscjGn+kpOG9788MHA==", "dev": true, "dependencies": { - "postcss": "^7.0.5" + "glob-parent": "^2.0.0", + "is-glob": "^2.0.0" }, "engines": { - "node": ">= 6" + "node": ">=0.10.0" } }, - "node_modules/postcss-modules-local-by-default": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.3.tgz", - "integrity": "sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw==", + "node_modules/glob-base/node_modules/glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha512-JDYOvfxio/t42HKdxkAYaCiBN7oYiuxykOxKxdaUW5Qn0zaYN3gRQWolrwdnf0shM9/EP0ebuuTmyoXNr1cC5w==", "dev": true, "dependencies": { - "icss-utils": "^4.1.1", - "postcss": "^7.0.32", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": ">= 6" + "is-glob": "^2.0.0" } }, - "node_modules/postcss-modules-scope": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz", - "integrity": "sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ==", + "node_modules/glob-base/node_modules/is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww==", "dev": true, - "dependencies": { - "postcss": "^7.0.6", - "postcss-selector-parser": "^6.0.0" - }, "engines": { - "node": ">= 6" + "node": ">=0.10.0" } }, - "node_modules/postcss-modules-values": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz", - "integrity": "sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg==", + "node_modules/glob-base/node_modules/is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg==", "dev": true, "dependencies": { - "icss-utils": "^4.0.0", - "postcss": "^7.0.6" + "is-extglob": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/postcss-selector-parser": { - "version": "6.0.11", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz", - "integrity": "sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==", + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, + "optional": true, "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" + "is-glob": "^4.0.1" }, "engines": { - "node": ">=4" + "node": ">= 6" } }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true + "node_modules/glob-to-regexp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz", + "integrity": "sha512-Iozmtbqv0noj0uDDqoL0zNq0VBEfK2YFoMAZoxJe4cwphvLR+JskfF30QhXHOR4m3KrE6NLRYw+U9MRXvifyig==" }, - "node_modules/postcss/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/global-dirs": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", + "integrity": "sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==", + "dev": true, + "dependencies": { + "ini": "^1.3.4" + }, "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "node_modules/prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true, "engines": { - "node": ">= 0.8.0" + "node": ">=4" } }, - "node_modules/prepend-http": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha512-PhmXi5XmoyKw1Un4E+opM2KcsJInDvKyuOumcjjw3waw86ZNjHwVUOOWLc4bCzLdcKNaWBH9e99sbWzDQsVaYg==", + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", "dev": true, + "dependencies": { + "define-properties": "^1.1.3" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/preserve": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", - "integrity": "sha512-s/46sYeylUfHNjI+sA/78FAHlmIuKqI9wNnzEOGehAlUUYeObv5C2mOinXBjyUyWmJ2SfcS2/ydApH4hTF4WXQ==", + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", "dev": true, - "engines": { - "node": ">=0.10.0" + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/pretty-format": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", - "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "node_modules/got": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", + "integrity": "sha512-Y/K3EDuiQN9rTZhBvPRWMLXIKdeD1Rj0nzunfoi0Yyn5WBEbzxXKU9Ub2X41oZBagVWOBU3MuDonFMgPWQFnwg==", "dev": true, "dependencies": { - "@jest/types": "^24.9.0", - "ansi-regex": "^4.0.0", - "ansi-styles": "^3.2.0", - "react-is": "^16.8.4" + "create-error-class": "^3.0.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "is-redirect": "^1.0.0", + "is-retry-allowed": "^1.0.0", + "is-stream": "^1.0.0", + "lowercase-keys": "^1.0.0", + "safe-buffer": "^5.0.1", + "timed-out": "^4.0.0", + "unzip-response": "^2.0.1", + "url-parse-lax": "^1.0.0" }, "engines": { - "node": ">= 6" + "node": ">=4" } }, - "node_modules/private": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", - "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", + "node_modules/got/node_modules/get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==", "dev": true, - "peer": true, "engines": { - "node": ">= 0.6" + "node": ">=4" } }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } + "node_modules/growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw==", + "dev": true }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", + "engines": { + "node": ">=4" } }, - "node_modules/property-information": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz", - "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==", + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", "dependencies": { - "xtend": "^4.0.0" + "ajv": "^6.12.3", + "har-schema": "^2.0.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/property-information/node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", "engines": { - "node": ">=0.4" + "node": ">=6" } }, - "node_modules/proto-list": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", - "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==" - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" + "function-bind": "^1.1.1" }, "engines": { - "node": ">= 0.10" + "node": ">= 0.4.0" } }, - "node_modules/pseudomap": { + "node_modules/has-bigints": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", - "dev": true - }, - "node_modules/psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" - }, - "node_modules/pstree.remy": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", - "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", - "dev": true + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/pump": { + "node_modules/has-flag": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", "dev": true, "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "dev": true, "engines": { - "node": ">=6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "engines": { - "node": ">=0.6.0", - "teleport": ">=0.2.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, "dependencies": { - "side-channel": "^1.0.4" + "has-symbols": "^1.0.2" }, "engines": { - "node": ">=0.6" + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/querystring": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.1.tgz", - "integrity": "sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==", - "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", - "engines": { - "node": ">=0.4.x" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/quick-lru": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", - "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "node_modules/has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==", + "dev": true, + "dependencies": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/randomatic": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", - "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", + "node_modules/has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==", "dev": true, "dependencies": { - "is-number": "^4.0.0", - "kind-of": "^6.0.0", - "math-random": "^1.0.1" + "is-number": "^3.0.0", + "kind-of": "^4.0.0" }, "engines": { - "node": ">= 0.10.0" + "node": ">=0.10.0" } }, - "node_modules/randomatic/node_modules/is-number": { + "node_modules/has-values/node_modules/kind-of": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==", "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, "engines": { "node": ">=0.10.0" } }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", "dependencies": { - "safe-buffer": "^5.1.0" + "react-is": "^16.7.0" } }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "engines": { - "node": ">= 0.6" - } + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true }, - "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "node_modules/html-encoding-sniffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", + "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", + "dev": true, "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" + "whatwg-encoding": "^1.0.1" } }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" } }, - "node_modules/react": { - "version": "16.14.0", - "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", - "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2" + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.8" } }, - "node_modules/react-class": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/react-class/-/react-class-3.2.2.tgz", - "integrity": "sha512-6mGkYJjqsCyazXfIIjAe3vf+KMsSTgJnyj7UK7tgFYIfv2lBQT9MB7rs8DQxESPNhBDu6UZ1jnTLpLzA2wKn6w==", + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", "dependencies": { - "object-assign": "^4.1.0" + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" }, - "peerDependencies": { - "react": ">=0.14.0 || >=15.0.0-0", - "react-dom": ">=0.14.0 || >=15.0.0-0" + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" } }, - "node_modules/react-dom": { - "version": "16.14.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz", - "integrity": "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==", - "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2", - "scheduler": "^0.19.1" - }, - "peerDependencies": { - "react": "^16.14.0" - } + "node_modules/humanize-duration": { + "version": "3.28.0", + "resolved": "https://registry.npmjs.org/humanize-duration/-/humanize-duration-3.28.0.tgz", + "integrity": "sha512-jMAxraOOmHuPbffLVDKkEKi/NeG8dMqP8lGRd6Tbf7JgAeG33jjgPWDbXXU7ypCI0o+oNKJFgbSB9FKVdWNI2A==" }, - "node_modules/react-image-crop": { - "version": "8.6.12", - "resolved": "https://registry.npmjs.org/react-image-crop/-/react-image-crop-8.6.12.tgz", - "integrity": "sha512-3CNz1xfsRRSH/iH023IDMXxzsb1M6RTHHUVsVcb8uFPcjGiA9WisvQ24G1eRDf2j4NlybupOEEdfK2vT0etN6A==", + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dependencies": { - "clsx": "^1.1.1", - "prop-types": "^15.7.2" + "safer-buffer": ">= 2.1.2 < 3" }, - "peerDependencies": { - "react": ">=16.13.1" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + "node_modules/icss-replace-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", + "integrity": "sha512-chIaY3Vh2mh2Q3RGXttaDIzeiPvaVXJ+C4DAh/w3c37SKZ/U6PGMmuicR2EQQp9bKG8zLMCl7I+PtIoOOPp8Gg==" }, - "node_modules/react-jsonschema-form": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/react-jsonschema-form/-/react-jsonschema-form-1.8.1.tgz", - "integrity": "sha512-aaDloxNAcGXOOOcdKOxxqEEn5oDlPUZgWcs8unXXB9vjBRgCF8rCm/wVSv1u2G5ih0j/BX6Ewd/WjI2g00lPdg==", - "deprecated": "react-jsonschema-form has been moved to @rjsf/core", + "node_modules/icss-utils": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.1.tgz", + "integrity": "sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==", + "dev": true, "dependencies": { - "@babel/runtime-corejs2": "^7.4.5", - "ajv": "^6.7.0", - "core-js": "^2.5.7", - "lodash": "^4.17.15", - "prop-types": "^15.5.8", - "react-is": "^16.8.4", - "react-lifecycles-compat": "^3.0.4", - "shortid": "^2.2.14" + "postcss": "^7.0.14" }, "engines": { - "node": ">=6", - "npm": ">=2.14.7" - }, - "peerDependencies": { - "react": ">=15" + "node": ">= 6" } }, - "node_modules/react-jsonschema-form/node_modules/core-js": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", - "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", - "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", - "hasInstallScript": true + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, - "node_modules/react-lifecycles-compat": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", - "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true }, - "node_modules/react-markdown": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-6.0.3.tgz", - "integrity": "sha512-kQbpWiMoBHnj9myLlmZG9T1JdoT/OEyHK7hqM6CqFT14MAkgWiWBUYijLyBmxbntaN6dCDicPcUhWhci1QYodg==", - "dependencies": { - "@types/hast": "^2.0.0", - "@types/unist": "^2.0.3", - "comma-separated-tokens": "^1.0.0", - "prop-types": "^15.7.2", - "property-information": "^5.3.0", - "react-is": "^17.0.0", - "remark-parse": "^9.0.0", - "remark-rehype": "^8.0.0", - "space-separated-tokens": "^1.1.0", - "style-to-object": "^0.3.0", - "unified": "^9.0.0", - "unist-util-visit": "^2.0.0", - "vfile": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - }, - "peerDependencies": { - "@types/react": ">=16", - "react": ">=16" + "node_modules/immer": { + "version": "9.0.21", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", + "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" } }, - "node_modules/react-markdown/node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" - }, - "node_modules/react-tag-autocomplete": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/react-tag-autocomplete/-/react-tag-autocomplete-6.3.0.tgz", - "integrity": "sha512-MUBVUFh5eOqshUm5NM20qp7zXk8TzSiKO4GoktlFzBLIOLs356npaMKtL02bm0nFV2f1zInUrXn1fq6+i5YX0w==", + "node_modules/import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha512-m7ZEHgtw69qOGw+jwxXkHlrlIPdTGkyh66zXZ1ajZbxkDBNjSY/LGbmjc7h0s2ELsUDTAhFr55TrPSSqJGPG0A==", + "dev": true, "engines": { - "node": ">= 10.0.0" - }, - "peerDependencies": { - "prop-types": "^15.5.0", - "react": "^16.5.0 || ^17.0.0", - "react-dom": "^16.5.0 || ^17.0.0" + "node": ">=4" } }, - "node_modules/read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "node_modules/import-local": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", "dev": true, "dependencies": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" }, "engines": { - "node": ">=4" + "node": ">=6" } }, - "node_modules/read-pkg-up": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", - "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", + "node_modules/import-local/node_modules/pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", "dev": true, "dependencies": { - "find-up": "^3.0.0", - "read-pkg": "^3.0.0" + "find-up": "^3.0.0" }, "engines": { "node": ">=6" } }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, "engines": { - "node": ">= 6" + "node": ">=0.8.19" } }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "node_modules/inline-style-parser": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", + "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==" + }, + "node_modules/internal-slot": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", + "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", "dev": true, - "optional": true, "dependencies": { - "picomatch": "^2.2.1" + "get-intrinsic": "^1.2.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" }, "engines": { - "node": ">=8.10.0" + "node": ">= 0.4" } }, - "node_modules/realpath-native": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.1.0.tgz", - "integrity": "sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA==", + "node_modules/interpret": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", + "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", "dev": true, - "dependencies": { - "util.promisify": "^1.0.0" - }, "engines": { - "node": ">=4" + "node": ">= 0.10" } }, - "node_modules/rechoir": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", - "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", "dev": true, "dependencies": { - "resolve": "^1.9.0" - }, + "loose-envify": "^1.0.0" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "engines": { "node": ">= 0.10" } }, - "node_modules/redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, "dependencies": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" + "kind-of": "^6.0.0" }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/redeyed": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-2.1.1.tgz", - "integrity": "sha512-FNpGGo1DycYAdnrKFxCMmKYgo/mILAqtRYbkdQD8Ep/Hk2PQ5+aEAEx+IU713RTDmuBaH0c8P5ZozurNu5ObRQ==", - "peer": true, + "node_modules/is-alphabetical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", + "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", + "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", "dependencies": { - "esprima": "~4.0.0" + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/redis": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/redis/-/redis-2.8.0.tgz", - "integrity": "sha512-M1OkonEQwtRmZv4tEWF2VgpG0JWJ8Fv1PhlgT5+B+uNq2cA3Rt1Yt/ryoR+vQNOQcIEgdCdfH0jr3bDpihAw1A==", + "node_modules/is-array-buffer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "dev": true, "dependencies": { - "double-ended-queue": "^2.1.0-0", - "redis-commands": "^1.2.0", - "redis-parser": "^2.6.0" + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "is-typed-array": "^1.1.10" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/redis-commands": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", - "integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==" + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true }, - "node_modules/redis-parser": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-2.6.0.tgz", - "integrity": "sha512-9Hdw19gwXFBJdN8ENUoNVJFRyMDFrE/ZBClPicKYDPwNPJ4ST1TedAHYNSiGKElwh2vrmRGMoJYbVdJd+WQXIw==", - "engines": { - "node": ">=0.10.0" + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "dev": true + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "optional": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } }, - "node_modules/regenerate-unicode-properties": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", - "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", "dev": true, "dependencies": { - "regenerate": "^1.4.2" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, "engines": { - "node": ">=4" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" - }, - "node_modules/regenerator-transform": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz", - "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==", - "dev": true, + "node_modules/is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", "dependencies": { - "@babel/runtime": "^7.8.4" + "ci-info": "^2.0.0" + }, + "bin": { + "is-ci": "bin.js" } }, - "node_modules/regex-cache": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", - "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", + "node_modules/is-core-module": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.0.tgz", + "integrity": "sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==", "dev": true, "dependencies": { - "is-equal-shallow": "^0.1.3" + "has": "^1.0.3" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "dependencies": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" + "kind-of": "^6.0.0" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", - "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "functions-have-names": "^1.2.3" + "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -16105,3986 +6792,3119 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/regexpu-core": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", - "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", + "node_modules/is-decimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", + "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "dependencies": { - "@babel/regjsgen": "^0.8.0", - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.1.0", - "regjsparser": "^0.9.1", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" }, "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/registry-auth-token": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz", - "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==", - "dev": true, - "dependencies": { - "rc": "^1.1.6", - "safe-buffer": "^5.0.1" + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/registry-url": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", - "integrity": "sha512-ZbgR5aZEdf4UKZVBPYIgaglBmSF2Hi94s2PcIHhRGFjKYu+chjJdYfHn4rt3hB6eCKLJ8giVIIfgMa1ehDfZKA==", + "node_modules/is-dotfile": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "integrity": "sha512-9YclgOGtN/f8zx0Pr4FQYMdibBiTaH3sn52vjYip4ZSf6C4/6RfTEZ+MR4GvKhCxdPh21Bg42/WL55f6KSnKpg==", "dev": true, - "dependencies": { - "rc": "^1.0.1" - }, "engines": { "node": ">=0.10.0" } }, - "node_modules/regjsparser": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", - "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "node_modules/is-equal-shallow": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", + "integrity": "sha512-0EygVC5qPvIyb+gSz7zdD5/AAoS6Qrx1e//6N4yv4oNm30kqvdmG66oZFWVlQHUWe5OjP08FuTw2IdT0EOTcYA==", "dev": true, "dependencies": { - "jsesc": "~0.5.0" + "is-primitive": "^2.0.0" }, - "bin": { - "regjsparser": "bin/parser" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, - "bin": { - "jsesc": "bin/jsesc" - } - }, - "node_modules/remark-parse": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-9.0.0.tgz", - "integrity": "sha512-geKatMwSzEXKHuzBNU1z676sGcDcFoChMK38TgdHJNAYfFtsfHDQG7MoJAjs6sgYMqyLduCYWDIWZIxiPeafEw==", "dependencies": { - "mdast-util-from-markdown": "^0.8.0" + "is-plain-object": "^2.0.4" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/remark-rehype": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-8.1.0.tgz", - "integrity": "sha512-EbCu9kHgAxKmW1yEYjx3QafMyGY3q8noUbNUI5xyKbaFP89wbhDrKxyIQNukNYthzjNHZu6J7hwFg7hRm1svYA==", - "dependencies": { - "mdast-util-to-hast": "^10.2.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", - "dev": true - }, - "node_modules/repeat-element": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", - "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", + "node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "node_modules/repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", "dev": true, "engines": { - "node": ">=0.10" + "node": ">=6" } }, - "node_modules/repeating": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", - "integrity": "sha512-ZqtSMuVybkISo2OWvqvm7iHSWngvdaW3IpsT9/uP8v4gMi591LY6h35wdOfvQdWCKFWZWm2Y1Opp4kV7vQKT6A==", + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, - "peer": true, "dependencies": { - "is-finite": "^1.0.0" + "is-extglob": "^2.1.1" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", - "dependencies": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "engines": { - "node": ">= 6" + "node_modules/is-hexadecimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", + "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/request-promise-core": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", - "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", + "node_modules/is-hotkey": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/is-hotkey/-/is-hotkey-0.1.8.tgz", + "integrity": "sha512-qs3NZ1INIS+H+yeo7cD9pDfwYV/jqRh1JG9S9zYrNudkoUQg7OL7ziXqRKu+InFjUIDoP2o6HIkLYMh1pcWgyQ==" + }, + "node_modules/is-installed-globally": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", + "integrity": "sha512-ERNhMg+i/XgDwPIPF3u24qpajVreaiSuvpb1Uu0jugw7KKcxGyCX8cgp8P5fwTmAuXku6beDHHECdKArjlg7tw==", "dev": true, "dependencies": { - "lodash": "^4.17.19" + "global-dirs": "^0.1.0", + "is-path-inside": "^1.0.0" }, "engines": { - "node": ">=0.10.0" - }, - "peerDependencies": { - "request": "^2.34" + "node": ">=4" } }, - "node_modules/request-promise-native": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz", - "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==", - "deprecated": "request-promise-native has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142", + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", "dev": true, - "dependencies": { - "request-promise-core": "1.1.4", - "stealthy-require": "^1.1.1", - "tough-cookie": "^2.3.3" - }, "engines": { - "node": ">=0.12.0" + "node": ">= 0.4" }, - "peerDependencies": { - "request": "^2.34" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/request/node_modules/qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "node_modules/is-npm": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", + "integrity": "sha512-9r39FIr3d+KD9SbX0sfMsHzb5PP3uimOiwr3YupUaUFG4W0l1U57Rx3utpttV7qz5U3jmrO5auUa04LU9pyHsg==", + "dev": true, "engines": { - "node": ">=0.6" + "node": ">=0.10.0" } }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, "engines": { "node": ">=0.10.0" } }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "node_modules/resolve": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", - "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, "dependencies": { - "is-core-module": "^2.11.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" + "has-tostringtag": "^1.0.0" }, - "bin": { - "resolve": "bin/resolve" + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/resolve-cwd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", - "integrity": "sha512-ccu8zQTrzVr954472aUVPLEcB3YpKSYR3cg/3lo1okzobPBM+1INXBbBZlDbnI/hbEocnf8j0QVo43hQKrbchg==", + "node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", "dev": true, "dependencies": { - "resolve-from": "^3.0.0" + "is-buffer": "^1.1.5" }, "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", + "node_modules/is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", "dev": true, "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==", - "deprecated": "https://github.com/lydell/resolve-url#deprecated", - "dev": true - }, - "node_modules/ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "node_modules/is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha512-qhsCR/Esx4U4hg/9I19OVUAJkGWtjRYHMRgUMZE2TDdj+Ag+kttZanLupfddNyglzz50cUlmWzUaI37GDfNx/g==", "dev": true, + "dependencies": { + "path-is-inside": "^1.0.1" + }, "engines": { - "node": ">=0.12" - } - }, - "node_modules/retry": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", - "engines": { - "node": ">= 4" + "node": ">=0.10.0" } }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, "dependencies": { - "glob": "^7.1.3" + "isobject": "^3.0.1" }, - "bin": { - "rimraf": "bin.js" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/rsvp": { - "version": "4.8.5", - "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", - "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", + "node_modules/is-posix-bracket": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", + "integrity": "sha512-Yu68oeXJ7LeWNmZ3Zov/xg/oDBnBK2RNxwYY1ilNJX+tKKZqgPK+qOn/Gs9jEu66KDY9Netf5XLKNGzas/vPfQ==", "dev": true, "engines": { - "node": "6.* || >= 7.*" + "node": ">=0.10.0" } }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" + "node_modules/is-primitive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", + "integrity": "sha512-N3w1tFaRfk3UrPfqeRyD+GYDASU3W5VinKhlORy8EWVf/sIdDL9GAcew85XmktCfH+ngG7SRXEVDoO18WMdB/Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==", + "node_modules/is-redirect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", + "integrity": "sha512-cr/SlUEe5zOGmzvj9bUyC4LVvkNVAXu4GytXLNMr1pny+a65MpQ9IJzFHD5vi7FyJgb4qt27+eS3TuQnqB+RQw==", "dev": true, - "dependencies": { - "ret": "~0.1.10" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-regex": "^1.1.4" + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/safe-stable-stringify": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", - "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", - "engines": { - "node": ">=10" - } - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/sane": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", - "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", - "deprecated": "some dependency vulnerabilities fixed, support for node < 10 dropped, and newer ECMAScript syntax/features added", - "dev": true, - "dependencies": { - "@cnakazawa/watch": "^1.0.3", - "anymatch": "^2.0.0", - "capture-exit": "^2.0.0", - "exec-sh": "^0.3.2", - "execa": "^1.0.0", - "fb-watchman": "^2.0.0", - "micromatch": "^3.1.4", - "minimist": "^1.1.1", - "walker": "~1.0.5" - }, - "bin": { - "sane": "src/cli.js" - }, + "node_modules/is-retry-allowed": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", + "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==", + "dev": true, "engines": { - "node": "6.* || 8.* || >= 10.*" + "node": ">=0.10.0" } }, - "node_modules/sane/node_modules/anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", "dev": true, "dependencies": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/sane/node_modules/normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", "dev": true, - "dependencies": { - "remove-trailing-separator": "^1.0.1" - }, "engines": { "node": ">=0.10.0" } }, - "node_modules/sanitize-html": { - "version": "1.27.5", - "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.27.5.tgz", - "integrity": "sha512-M4M5iXDAUEcZKLXkmk90zSYWEtk5NH3JmojQxKxV371fnMh+x9t1rqdmXaGoyEHw3z/X/8vnFhKjGL5xFGOJ3A==", - "dependencies": { - "htmlparser2": "^4.1.0", - "lodash": "^4.17.15", - "parse-srcset": "^1.0.2", - "postcss": "^7.0.27" - } - }, - "node_modules/sanitize-html/node_modules/dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/sanitize-html/node_modules/dom-serializer/node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, "dependencies": { - "domelementtype": "^2.2.0" + "has-symbols": "^1.0.2" }, "engines": { - "node": ">= 4" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/sanitize-html/node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ] - }, - "node_modules/sanitize-html/node_modules/domhandler": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.3.0.tgz", - "integrity": "sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==", + "node_modules/is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "dev": true, "dependencies": { - "domelementtype": "^2.0.1" + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">= 4" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/sanitize-html/node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" + "call-bind": "^1.0.2" }, "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/sanitize-html/node_modules/domutils/node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" + }, + "node_modules/istanbul-lib-coverage": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", + "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", + "dev": true, "dependencies": { - "domelementtype": "^2.2.0" + "@babel/generator": "^7.4.0", + "@babel/parser": "^7.4.3", + "@babel/template": "^7.4.0", + "@babel/traverse": "^7.4.3", + "@babel/types": "^7.4.0", + "istanbul-lib-coverage": "^2.0.5", + "semver": "^6.0.0" }, "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" + "node": ">=6" } }, - "node_modules/sanitize-html/node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "node_modules/istanbul-lib-report": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", + "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6" } }, - "node_modules/sanitize-html/node_modules/htmlparser2": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-4.1.0.tgz", - "integrity": "sha512-4zDq1a1zhE4gQso/c5LP1OtrhYTncXNSpvJYtWJBtXAETPlMfi3IFNjGuQbYLuVY4ZR0QMqRVvo4Pdy9KLyP8Q==", + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^3.0.0", - "domutils": "^2.0.0", - "entities": "^2.0.0" + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" } }, - "node_modules/sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true - }, - "node_modules/scheduler": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", - "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==", + "node_modules/istanbul-lib-source-maps": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", + "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", + "dev": true, "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" + "debug": "^4.1.1", + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "rimraf": "^2.6.3", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6" } }, - "node_modules/schema-utils": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", - "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "node_modules/istanbul-lib-source-maps/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-reports": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.7.tgz", + "integrity": "sha512-uu1F/L1o5Y6LzPVSVZXNOoD/KXpJue9aeLRd0sM9uMXfZvzomB0WxVamWb5ue8kA2vVWEmW7EG+A5n3f1kqHKg==", "dev": true, "dependencies": { - "@types/json-schema": "^7.0.5", - "ajv": "^6.12.4", - "ajv-keywords": "^3.5.2" + "html-escaper": "^2.0.0" }, "engines": { - "node": ">= 8.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "node": ">=6" } }, - "node_modules/scroll-into-view-if-needed": { - "version": "2.2.31", - "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.31.tgz", - "integrity": "sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==", + "node_modules/jest": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-24.9.0.tgz", + "integrity": "sha512-YvkBL1Zm7d2B1+h5fHEOdyjCG+sGMz4f8D86/0HiqJ6MB4MnDc8FgP5vdWsGnemOQro7lnYo8UakZ3+5A0jxGw==", + "dev": true, "dependencies": { - "compute-scroll-into-view": "^1.0.20" + "import-local": "^2.0.0", + "jest-cli": "^24.9.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": ">= 6" } }, - "node_modules/seekout": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/seekout/-/seekout-1.0.2.tgz", - "integrity": "sha512-eB4fRqHZCE8gmwFOVHC9tHzLkZJ2Y12qJvAJQox8kWjGObA++tgVeXHACsDCPajfNGnDaK2Juv+WzNlMuO43wQ==", + "node_modules/jest-changed-files": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-24.9.0.tgz", + "integrity": "sha512-6aTWpe2mHF0DhL28WjdkO8LyGjs3zItPET4bMSeXU6T3ub4FPMw+mcOcbdGXQOAfmLcxofD23/5Bl9Z4AkFwqg==", + "dev": true, + "dependencies": { + "@jest/types": "^24.9.0", + "execa": "^1.0.0", + "throat": "^4.0.0" + }, "engines": { - "node": ">=0.12" + "node": ">= 6" } }, - "node_modules/semantic-release": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-21.1.1.tgz", - "integrity": "sha512-OCIazQnaCHdq1F6zfmKS0P7jZakYq0weiqW2mxUWo4H2CDnxelUoa/0Bs/dQatoHc6JFh6lG2HWpusdl93bFcw==", - "peer": true, + "node_modules/jest-cli": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-24.9.0.tgz", + "integrity": "sha512-+VLRKyitT3BWoMeSUIHRxV/2g8y9gw91Jh5z2UmXZzkZKpbC08CSehVxgHUwTpy+HwGcns/tqafQDJW7imYvGg==", + "dev": true, "dependencies": { - "@semantic-release/commit-analyzer": "^10.0.0", - "@semantic-release/error": "^4.0.0", - "@semantic-release/github": "^9.0.0", - "@semantic-release/npm": "^10.0.2", - "@semantic-release/release-notes-generator": "^11.0.0", - "aggregate-error": "^4.0.1", - "cosmiconfig": "^8.0.0", - "debug": "^4.0.0", - "env-ci": "^9.0.0", - "execa": "^8.0.0", - "figures": "^5.0.0", - "find-versions": "^5.1.0", - "get-stream": "^6.0.0", - "git-log-parser": "^1.2.0", - "hook-std": "^3.0.0", - "hosted-git-info": "^7.0.0", - "lodash-es": "^4.17.21", - "marked": "^5.0.0", - "marked-terminal": "^5.1.1", - "micromatch": "^4.0.2", - "p-each-series": "^3.0.0", - "p-reduce": "^3.0.0", - "read-pkg-up": "^10.0.0", - "resolve-from": "^5.0.0", - "semver": "^7.3.2", - "semver-diff": "^4.0.0", - "signale": "^1.2.1", - "yargs": "^17.5.1" + "@jest/core": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "import-local": "^2.0.0", + "is-ci": "^2.0.0", + "jest-config": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "prompts": "^2.0.1", + "realpath-native": "^1.1.0", + "yargs": "^13.3.0" }, "bin": { - "semantic-release": "bin/semantic-release.js" + "jest": "bin/jest.js" }, "engines": { - "node": ">=18" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/@octokit/auth-token": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz", - "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==", - "peer": true, + "node_modules/jest-config": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.9.0.tgz", + "integrity": "sha512-RATtQJtVYQrp7fvWg6f5y3pEFj9I+H8sWw4aKxnDZ96mob5i5SD6ZEGWgMLXQ4LE8UurrjbdlLWdUeo+28QpfQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.1.0", + "@jest/test-sequencer": "^24.9.0", + "@jest/types": "^24.9.0", + "babel-jest": "^24.9.0", + "chalk": "^2.0.1", + "glob": "^7.1.1", + "jest-environment-jsdom": "^24.9.0", + "jest-environment-node": "^24.9.0", + "jest-get-type": "^24.9.0", + "jest-jasmine2": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "micromatch": "^3.1.10", + "pretty-format": "^24.9.0", + "realpath-native": "^1.1.0" + }, "engines": { - "node": ">= 18" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/@octokit/core": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.0.0.tgz", - "integrity": "sha512-YbAtMWIrbZ9FCXbLwT9wWB8TyLjq9mxpKdgB3dUNxQcIVTf9hJ70gRPwAcqGZdY6WdJPZ0I7jLaaNDCiloGN2A==", - "peer": true, + "node_modules/jest-diff": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.9.0.tgz", + "integrity": "sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ==", + "dev": true, "dependencies": { - "@octokit/auth-token": "^4.0.0", - "@octokit/graphql": "^7.0.0", - "@octokit/request": "^8.0.2", - "@octokit/request-error": "^5.0.0", - "@octokit/types": "^11.0.0", - "before-after-hook": "^2.2.0", - "universal-user-agent": "^6.0.0" + "chalk": "^2.0.1", + "diff-sequences": "^24.9.0", + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" }, "engines": { - "node": ">= 18" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/@octokit/endpoint": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.0.tgz", - "integrity": "sha512-szrQhiqJ88gghWY2Htt8MqUDO6++E/EIXqJ2ZEp5ma3uGS46o7LZAzSLt49myB7rT+Hfw5Y6gO3LmOxGzHijAQ==", - "peer": true, + "node_modules/jest-docblock": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-24.9.0.tgz", + "integrity": "sha512-F1DjdpDMJMA1cN6He0FNYNZlo3yYmOtRUnktrT9Q37njYzC5WEaDdmbynIgy0L/IvXvvgsG8OsqhLPXTpfmZAA==", + "dev": true, "dependencies": { - "@octokit/types": "^11.0.0", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" + "detect-newline": "^2.1.0" }, "engines": { - "node": ">= 18" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/@octokit/graphql": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.0.1.tgz", - "integrity": "sha512-T5S3oZ1JOE58gom6MIcrgwZXzTaxRnxBso58xhozxHpOqSTgDS6YNeEUvZ/kRvXgPrRz/KHnZhtb7jUMRi9E6w==", - "peer": true, + "node_modules/jest-each": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.9.0.tgz", + "integrity": "sha512-ONi0R4BvW45cw8s2Lrx8YgbeXL1oCQ/wIDwmsM3CqM/nlblNCPmnC3IPQlMbRFZu3wKdQ2U8BqM6lh3LJ5Bsog==", + "dev": true, "dependencies": { - "@octokit/request": "^8.0.1", - "@octokit/types": "^11.0.0", - "universal-user-agent": "^6.0.0" + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "jest-get-type": "^24.9.0", + "jest-util": "^24.9.0", + "pretty-format": "^24.9.0" }, "engines": { - "node": ">= 18" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/@octokit/plugin-paginate-rest": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-8.0.0.tgz", - "integrity": "sha512-2xZ+baZWUg+qudVXnnvXz7qfrTmDeYPCzangBVq/1gXxii/OiS//4shJp9dnCCvj1x+JAm9ji1Egwm1BA47lPQ==", - "peer": true, + "node_modules/jest-environment-jsdom": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.9.0.tgz", + "integrity": "sha512-Zv9FV9NBRzLuALXjvRijO2351DRQeLYXtpD4xNvfoVFw21IOKNhZAEUKcbiEtjTkm2GsJ3boMVgkaR7rN8qetA==", + "dev": true, "dependencies": { - "@octokit/types": "^11.0.0" + "@jest/environment": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0", + "jest-util": "^24.9.0", + "jsdom": "^11.5.1" }, "engines": { - "node": ">= 18" + "node": ">= 6" + } + }, + "node_modules/jest-environment-node": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-24.9.0.tgz", + "integrity": "sha512-6d4V2f4nxzIzwendo27Tr0aFm+IXWa0XEUnaH6nU0FMaozxovt+sfRvh4J47wL1OvF83I3SSTu0XK+i4Bqe7uA==", + "dev": true, + "dependencies": { + "@jest/environment": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0", + "jest-util": "^24.9.0" }, - "peerDependencies": { - "@octokit/core": ">=5" + "engines": { + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/@octokit/plugin-retry": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-6.0.0.tgz", - "integrity": "sha512-a1/A4A+PB1QoAHQfLJxGHhLfSAT03bR1jJz3GgQJZvty2ozawFWs93MiBQXO7SL2YbO7CIq0Goj4qLOBj8JeMQ==", - "peer": true, + "node_modules/jest-get-type": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", + "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-haste-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.9.0.tgz", + "integrity": "sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ==", + "dev": true, "dependencies": { - "@octokit/request-error": "^5.0.0", - "@octokit/types": "^11.0.0", - "bottleneck": "^2.15.3" + "@jest/types": "^24.9.0", + "anymatch": "^2.0.0", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.1.15", + "invariant": "^2.2.4", + "jest-serializer": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.9.0", + "micromatch": "^3.1.10", + "sane": "^4.0.3", + "walker": "^1.0.7" }, "engines": { - "node": ">= 18" + "node": ">= 6" }, - "peerDependencies": { - "@octokit/core": ">=5" + "optionalDependencies": { + "fsevents": "^1.2.7" } }, - "node_modules/semantic-release/node_modules/@octokit/plugin-throttling": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-7.0.0.tgz", - "integrity": "sha512-KL2k/d0uANc8XqP5S64YcNFCudR3F5AaKO39XWdUtlJIjT9Ni79ekWJ6Kj5xvAw87udkOMEPcVf9xEge2+ahew==", - "peer": true, + "node_modules/jest-haste-map/node_modules/anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "dependencies": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "node_modules/jest-haste-map/node_modules/fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "deprecated": "fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], "dependencies": { - "@octokit/types": "^11.0.0", - "bottleneck": "^2.15.3" + "bindings": "^1.5.0", + "nan": "^2.12.1" }, "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@octokit/core": "^5.0.0" + "node": ">= 4.0" } }, - "node_modules/semantic-release/node_modules/@octokit/request": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.1.1.tgz", - "integrity": "sha512-8N+tdUz4aCqQmXl8FpHYfKG9GelDFd7XGVzyN8rc6WxVlYcfpHECnuRkgquzz+WzvHTK62co5di8gSXnzASZPQ==", - "peer": true, + "node_modules/jest-haste-map/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "dev": true, "dependencies": { - "@octokit/endpoint": "^9.0.0", - "@octokit/request-error": "^5.0.0", - "@octokit/types": "^11.1.0", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" + "remove-trailing-separator": "^1.0.1" }, "engines": { - "node": ">= 18" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/@octokit/request-error": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.0.0.tgz", - "integrity": "sha512-1ue0DH0Lif5iEqT52+Rf/hf0RmGO9NWFjrzmrkArpG9trFfDM/efx00BJHdLGuro4BR/gECxCU2Twf5OKrRFsQ==", - "peer": true, + "node_modules/jest-jasmine2": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-24.9.0.tgz", + "integrity": "sha512-Cq7vkAgaYKp+PsX+2/JbTarrk0DmNhsEtqBXNwUHkdlbrTBLtMJINADf2mf5FkowNsq8evbPc07/qFO0AdKTzw==", + "dev": true, "dependencies": { - "@octokit/types": "^11.0.0", - "deprecation": "^2.0.0", - "once": "^1.4.0" + "@babel/traverse": "^7.1.0", + "@jest/environment": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "co": "^4.6.0", + "expect": "^24.9.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-snapshot": "^24.9.0", + "jest-util": "^24.9.0", + "pretty-format": "^24.9.0", + "throat": "^4.0.0" }, "engines": { - "node": ">= 18" - } - }, - "node_modules/semantic-release/node_modules/@octokit/types": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-11.1.0.tgz", - "integrity": "sha512-Fz0+7GyLm/bHt8fwEqgvRBWwIV1S6wRRyq+V6exRKLVWaKGsuy6H9QFYeBVDV7rK6fO3XwHgQOPxv+cLj2zpXQ==", - "peer": true, - "dependencies": { - "@octokit/openapi-types": "^18.0.0" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/@semantic-release/commit-analyzer": { - "version": "10.0.4", - "resolved": "https://registry.npmjs.org/@semantic-release/commit-analyzer/-/commit-analyzer-10.0.4.tgz", - "integrity": "sha512-pFGn99fn8w4/MHE0otb2A/l5kxgOuxaaauIh4u30ncoTJuqWj4hXTgEJ03REqjS+w1R2vPftSsO26WC61yOcpw==", - "peer": true, + "node_modules/jest-leak-detector": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-24.9.0.tgz", + "integrity": "sha512-tYkFIDsiKTGwb2FG1w8hX9V0aUb2ot8zY/2nFg087dUageonw1zrLMP4W6zsRO59dPkTSKie+D4rhMuP9nRmrA==", + "dev": true, "dependencies": { - "conventional-changelog-angular": "^6.0.0", - "conventional-commits-filter": "^3.0.0", - "conventional-commits-parser": "^5.0.0", - "debug": "^4.0.0", - "import-from": "^4.0.0", - "lodash-es": "^4.17.21", - "micromatch": "^4.0.2" + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" }, "engines": { - "node": ">=18" - }, - "peerDependencies": { - "semantic-release": ">=20.1.0" - } - }, - "node_modules/semantic-release/node_modules/@semantic-release/github": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-9.0.4.tgz", - "integrity": "sha512-kQCGFAsBErvCR6hzNuzu63cj4erQN2krm9zQlg8vl4j5X0mL0d/Ras0wmL5Gkr1TuSS2lweME7M4J5zvtDDDSA==", - "peer": true, - "dependencies": { - "@octokit/core": "^5.0.0", - "@octokit/plugin-paginate-rest": "^8.0.0", - "@octokit/plugin-retry": "^6.0.0", - "@octokit/plugin-throttling": "^7.0.0", - "@semantic-release/error": "^4.0.0", - "aggregate-error": "^4.0.1", - "debug": "^4.3.4", - "dir-glob": "^3.0.1", - "globby": "^13.1.4", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.0", - "issue-parser": "^6.0.0", - "lodash-es": "^4.17.21", - "mime": "^3.0.0", - "p-filter": "^3.0.0", - "url-join": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "semantic-release": ">=20.1.0" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/@semantic-release/npm": { - "version": "10.0.5", - "resolved": "https://registry.npmjs.org/@semantic-release/npm/-/npm-10.0.5.tgz", - "integrity": "sha512-cJnQ2M5pxJRwZEkb0A/+U3TG4UNmjrrLwV2PxJKljn5OPT0yJB8GzGgWbbKACayvxrT06YdTa4Amtq/piJcOIA==", - "peer": true, + "node_modules/jest-matcher-utils": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz", + "integrity": "sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA==", + "dev": true, "dependencies": { - "@semantic-release/error": "^4.0.0", - "aggregate-error": "^4.0.1", - "execa": "^8.0.0", - "fs-extra": "^11.0.0", - "lodash-es": "^4.17.21", - "nerf-dart": "^1.0.0", - "normalize-url": "^8.0.0", - "npm": "^9.5.0", - "rc": "^1.2.8", - "read-pkg": "^8.0.0", - "registry-auth-token": "^5.0.0", - "semver": "^7.1.2", - "tempy": "^3.0.0" + "chalk": "^2.0.1", + "jest-diff": "^24.9.0", + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" }, "engines": { - "node": ">=18" - }, - "peerDependencies": { - "semantic-release": ">=20.1.0" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/@semantic-release/release-notes-generator": { - "version": "11.0.7", - "resolved": "https://registry.npmjs.org/@semantic-release/release-notes-generator/-/release-notes-generator-11.0.7.tgz", - "integrity": "sha512-T09QB9ImmNx7Q6hY6YnnEbw/rEJ6a+22LBxfZq+pSAXg/OL/k0siwEm5cK4k1f9dE2Z2mPIjJKKohzUm0jbxcQ==", - "peer": true, + "node_modules/jest-message-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", + "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "dev": true, "dependencies": { - "conventional-changelog-angular": "^6.0.0", - "conventional-changelog-writer": "^6.0.0", - "conventional-commits-filter": "^4.0.0", - "conventional-commits-parser": "^5.0.0", - "debug": "^4.0.0", - "get-stream": "^7.0.0", - "import-from": "^4.0.0", - "into-stream": "^7.0.0", - "lodash-es": "^4.17.21", - "read-pkg-up": "^10.0.0" - }, - "engines": { - "node": ">=18" + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^2.0.1", + "micromatch": "^3.1.10", + "slash": "^2.0.0", + "stack-utils": "^1.0.1" }, - "peerDependencies": { - "semantic-release": ">=20.1.0" - } - }, - "node_modules/semantic-release/node_modules/@semantic-release/release-notes-generator/node_modules/conventional-commits-filter": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-4.0.0.tgz", - "integrity": "sha512-rnpnibcSOdFcdclpFwWa+pPlZJhXE7l+XK04zxhbWrhgpR96h33QLz8hITTXbcYICxVr3HZFtbtUAQ+4LdBo9A==", - "peer": true, - "engines": { - "node": ">=16" - } - }, - "node_modules/semantic-release/node_modules/@semantic-release/release-notes-generator/node_modules/get-stream": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-7.0.1.tgz", - "integrity": "sha512-3M8C1EOFN6r8AMUhwUAACIoXZJEOufDU5+0gFFN5uNs6XYOralD2Pqkl7m046va6x77FwposWXbAhPPIOus7mQ==", - "peer": true, "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/agent-base": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", - "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", - "peer": true, + "node_modules/jest-mock": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", + "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", + "dev": true, "dependencies": { - "debug": "^4.3.4" + "@jest/types": "^24.9.0" }, "engines": { - "node": ">= 14" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "peer": true, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, "engines": { - "node": ">=8" - } - }, - "node_modules/semantic-release/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "peer": true, - "dependencies": { - "color-convert": "^2.0.1" + "node": ">=6" }, - "engines": { - "node": ">=8" + "peerDependencies": { + "jest-resolve": "*" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } } }, - "node_modules/semantic-release/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "peer": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, + "node_modules/jest-regex-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", + "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", + "dev": true, "engines": { - "node": ">=12" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "peer": true, + "node_modules/jest-resolve": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.9.0.tgz", + "integrity": "sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ==", + "dev": true, "dependencies": { - "color-name": "~1.1.4" + "@jest/types": "^24.9.0", + "browser-resolve": "^1.11.3", + "chalk": "^2.0.1", + "jest-pnp-resolver": "^1.2.1", + "realpath-native": "^1.1.0" }, "engines": { - "node": ">=7.0.0" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "peer": true - }, - "node_modules/semantic-release/node_modules/conventional-changelog-angular": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-6.0.0.tgz", - "integrity": "sha512-6qLgrBF4gueoC7AFVHu51nHL9pF9FRjXrH+ceVf7WmAfH3gs+gEYOkvxhjMPjZu57I4AGUGoNTY8V7Hrgf1uqg==", - "peer": true, + "node_modules/jest-resolve-dependencies": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-24.9.0.tgz", + "integrity": "sha512-Fm7b6AlWnYhT0BXy4hXpactHIqER7erNgIsIozDXWl5dVm+k8XdGVe1oTg1JyaFnOxarMEbax3wyRJqGP2Pq+g==", + "dev": true, "dependencies": { - "compare-func": "^2.0.0" + "@jest/types": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-snapshot": "^24.9.0" }, "engines": { - "node": ">=14" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-6.0.1.tgz", - "integrity": "sha512-359t9aHorPw+U+nHzUXHS5ZnPBOizRxfQsWT5ZDHBfvfxQOAik+yfuhKXG66CN5LEWPpMNnIMHUTCKeYNprvHQ==", - "peer": true, - "dependencies": { - "conventional-commits-filter": "^3.0.0", - "dateformat": "^3.0.3", - "handlebars": "^4.7.7", - "json-stringify-safe": "^5.0.1", - "meow": "^8.1.2", - "semver": "^7.0.0", - "split": "^1.0.1" - }, - "bin": { - "conventional-changelog-writer": "cli.js" + "node_modules/jest-runner": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-24.9.0.tgz", + "integrity": "sha512-KksJQyI3/0mhcfspnxxEOBueGrd5E4vV7ADQLT9ESaCzz02WnbdbKWIf5Mkaucoaj7obQckYPVX6JJhgUcoWWg==", + "dev": true, + "dependencies": { + "@jest/console": "^24.7.1", + "@jest/environment": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "chalk": "^2.4.2", + "exit": "^0.1.2", + "graceful-fs": "^4.1.15", + "jest-config": "^24.9.0", + "jest-docblock": "^24.3.0", + "jest-haste-map": "^24.9.0", + "jest-jasmine2": "^24.9.0", + "jest-leak-detector": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-resolve": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.6.0", + "source-map-support": "^0.5.6", + "throat": "^4.0.0" }, "engines": { - "node": ">=14" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "peer": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, + "node_modules/jest-runner/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer/node_modules/hosted-git-info": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", - "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", - "peer": true, + "node_modules/jest-runner/node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, "dependencies": { - "lru-cache": "^6.0.0" - }, - "engines": { - "node": ">=10" + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer/node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "peer": true - }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer/node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "peer": true - }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "peer": true, + "node_modules/jest-runtime": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-24.9.0.tgz", + "integrity": "sha512-8oNqgnmF3v2J6PVRM2Jfuj8oX3syKmaynlDMMKQ4iyzbQzIG6th5ub/lM2bCMTmoTKM3ykcUYI2Pw9xwNtjMnw==", + "dev": true, "dependencies": { - "p-locate": "^4.1.0" + "@jest/console": "^24.7.1", + "@jest/environment": "^24.9.0", + "@jest/source-map": "^24.3.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/yargs": "^13.0.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.1.15", + "jest-config": "^24.9.0", + "jest-haste-map": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-mock": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.9.0", + "jest-snapshot": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "realpath-native": "^1.1.0", + "slash": "^2.0.0", + "strip-bom": "^3.0.0", + "yargs": "^13.3.0" + }, + "bin": { + "jest-runtime": "bin/jest-runtime.js" }, "engines": { - "node": ">=8" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "peer": true, - "dependencies": { - "yallist": "^4.0.0" - }, + "node_modules/jest-serializer": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.9.0.tgz", + "integrity": "sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==", + "dev": true, "engines": { - "node": ">=10" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer/node_modules/meow": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", - "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", - "peer": true, + "node_modules/jest-snapshot": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.9.0.tgz", + "integrity": "sha512-uI/rszGSs73xCM0l+up7O7a40o90cnrk429LOiK3aeTvfC0HHmldbd81/B7Ix81KSFe1lwkbl7GnBGG4UfuDew==", + "dev": true, "dependencies": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.18.0", - "yargs-parser": "^20.2.3" + "@babel/types": "^7.0.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "expect": "^24.9.0", + "jest-diff": "^24.9.0", + "jest-get-type": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-resolve": "^24.9.0", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^24.9.0", + "semver": "^6.2.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer/node_modules/normalize-package-data": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", - "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", - "peer": true, + "node_modules/jest-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", + "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "dev": true, "dependencies": { - "hosted-git-info": "^4.0.1", - "is-core-module": "^2.5.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" + "@jest/console": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/source-map": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" }, "engines": { - "node": ">=10" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "peer": true, - "dependencies": { - "p-try": "^2.0.0" - }, + "node_modules/jest-util/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "peer": true, + "node_modules/jest-validate": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.9.0.tgz", + "integrity": "sha512-HPIt6C5ACwiqSiwi+OfSSHbK8sG7akG8eATl+IPKaeIjtPOeBUd/g3J7DghugzxrGjI93qS/+RPKe1H6PqvhRQ==", + "dev": true, "dependencies": { - "p-limit": "^2.2.0" + "@jest/types": "^24.9.0", + "camelcase": "^5.3.1", + "chalk": "^2.0.1", + "jest-get-type": "^24.9.0", + "leven": "^3.1.0", + "pretty-format": "^24.9.0" }, "engines": { - "node": ">=8" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer/node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "peer": true, + "node_modules/jest-watcher": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-24.9.0.tgz", + "integrity": "sha512-+/fLOfKPXXYJDYlks62/4R4GoT+GU1tYZed99JSCOsmzkkF7727RqKrjNAxtfO4YpGv11wybgRvCjR73lK2GZw==", + "dev": true, "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/yargs": "^13.0.0", + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.1", + "jest-util": "^24.9.0", + "string-length": "^2.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "peer": true, "engines": { - "node": ">=8" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer/node_modules/read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "peer": true, + "node_modules/jest-worker": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", + "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "dev": true, "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" + "merge-stream": "^2.0.0", + "supports-color": "^6.1.0" }, "engines": { - "node": ">=8" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer/node_modules/read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "peer": true, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, "dependencies": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" + "has-flag": "^3.0.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=6" } }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer/node_modules/read-pkg-up/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "peer": true, - "engines": { - "node": ">=8" - } + "node_modules/jquery": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-2.2.4.tgz", + "integrity": "sha512-lBHj60ezci2u1v2FqnZIraShGgEXq35qCzMv4lITyHGppTnA13rwR0MgwyNJh9TnDs3aXUvd1xjAotfraMHX/Q==" }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer/node_modules/read-pkg/node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "peer": true + "node_modules/jquery-ui": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/jquery-ui/-/jquery-ui-1.12.1.tgz", + "integrity": "sha512-K/kDBMXkTky5LH+gqbMvttU1ipqCTaecKyAFjwHjUnPTVfm5I5PZC7We31iNR3yWtAHNqoxkLoit06lR/gKVlA==" }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer/node_modules/read-pkg/node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "peer": true, - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } + "node_modules/jquery.cookie": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jquery.cookie/-/jquery.cookie-1.4.1.tgz", + "integrity": "sha512-c/hZOOL+8VSw/FkTVH637gS1/6YzMSCROpTZ2qBYwJ7s7sHajU7uBkSSiE5+GXWwrfCCyO+jsYjUQ7Hs2rIxAA==" }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer/node_modules/read-pkg/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "peer": true, - "bin": { - "semver": "bin/semver" + "node_modules/jquery.scrollto": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/jquery.scrollto/-/jquery.scrollto-2.1.3.tgz", + "integrity": "sha512-4M+t5R4QRnq1CSRU9gswZ7Z4zRfcoWmgD31HRrLYCCI4J7tLWeE0ZoT+0JpI7GcT0YKTqcfw+p8t2yYEKFfrHA==", + "dependencies": { + "jquery": ">=1.8" } }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer/node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "peer": true, - "engines": { - "node": ">=8" - } + "node_modules/js-cookie": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz", + "integrity": "sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==" }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer/node_modules/type-fest": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", - "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", - "peer": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, - "node_modules/semantic-release/node_modules/conventional-changelog-writer/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "peer": true, - "engines": { - "node": ">=10" - } + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" }, - "node_modules/semantic-release/node_modules/conventional-commits-filter": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-3.0.0.tgz", - "integrity": "sha512-1ymej8b5LouPx9Ox0Dw/qAO2dVdfpRFq28e5Y0jJEU8ZrLdy0vOSkkIInwmxErFGhg6SALro60ZrwYFVTUDo4Q==", - "peer": true, + "node_modules/jsdom": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz", + "integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==", + "dev": true, "dependencies": { - "lodash.ismatch": "^4.4.0", - "modify-values": "^1.0.1" - }, - "engines": { - "node": ">=14" + "abab": "^2.0.0", + "acorn": "^5.5.3", + "acorn-globals": "^4.1.0", + "array-equal": "^1.0.0", + "cssom": ">= 0.3.2 < 0.4.0", + "cssstyle": "^1.0.0", + "data-urls": "^1.0.0", + "domexception": "^1.0.1", + "escodegen": "^1.9.1", + "html-encoding-sniffer": "^1.0.2", + "left-pad": "^1.3.0", + "nwsapi": "^2.0.7", + "parse5": "4.0.0", + "pn": "^1.1.0", + "request": "^2.87.0", + "request-promise-native": "^1.0.5", + "sax": "^1.2.4", + "symbol-tree": "^3.2.2", + "tough-cookie": "^2.3.4", + "w3c-hr-time": "^1.0.1", + "webidl-conversions": "^4.0.2", + "whatwg-encoding": "^1.0.3", + "whatwg-mimetype": "^2.1.0", + "whatwg-url": "^6.4.1", + "ws": "^5.2.0", + "xml-name-validator": "^3.0.0" } }, - "node_modules/semantic-release/node_modules/conventional-commits-parser": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz", - "integrity": "sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==", - "peer": true, - "dependencies": { - "is-text-path": "^2.0.0", - "JSONStream": "^1.3.5", - "meow": "^12.0.1", - "split2": "^4.0.0" - }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, "bin": { - "conventional-commits-parser": "cli.mjs" + "jsesc": "bin/jsesc" }, "engines": { - "node": ">=16" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "peer": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true }, - "node_modules/semantic-release/node_modules/crypto-random-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", - "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", - "peer": true, - "dependencies": { - "type-fest": "^1.0.1" + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=6" } }, - "node_modules/semantic-release/node_modules/crypto-random-string/node_modules/type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", - "peer": true, - "engines": { - "node": ">=10" + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dependencies": { + "universalify": "^2.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "optionalDependencies": { + "graceful-fs": "^4.1.6" } }, - "node_modules/semantic-release/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "peer": true - }, - "node_modules/semantic-release/node_modules/execa": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", - "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", - "peer": true, + "node_modules/jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^8.0.1", - "human-signals": "^5.0.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^4.1.0", - "strip-final-newline": "^3.0.0" + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" }, "engines": { - "node": ">=16.17" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" + "node": ">=0.6.0" } }, - "node_modules/semantic-release/node_modules/execa/node_modules/get-stream": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", - "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", - "peer": true, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/find-up": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", - "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", - "peer": true, + "node_modules/klaw-sync": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz", + "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==", "dependencies": { - "locate-path": "^7.1.0", - "path-exists": "^5.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "graceful-fs": "^4.1.11" } }, - "node_modules/semantic-release/node_modules/fs-extra": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", - "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", - "peer": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, "engines": { - "node": ">=14.14" + "node": ">=6" } }, - "node_modules/semantic-release/node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "peer": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" }, - "node_modules/semantic-release/node_modules/globby": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", - "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", - "peer": true, + "node_modules/latest-version": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", + "integrity": "sha512-Be1YRHWWlZaSsrz2U+VInk+tO0EwLIyV+23RhWLINJYwg/UIikxjlj3MhH37/6/EDCAusjajvMkMMUXRaMWl/w==", + "dev": true, "dependencies": { - "dir-glob": "^3.0.1", - "fast-glob": "^3.3.0", - "ignore": "^5.2.4", - "merge2": "^1.4.1", - "slash": "^4.0.0" + "package-json": "^4.0.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/hosted-git-info": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.0.tgz", - "integrity": "sha512-ICclEpTLhHj+zCuSb2/usoNXSVkxUSIopre+b1w8NDY9Dntp9LO4vLdHYI336TH8sAqwrRgnSfdkBG2/YpisHA==", - "peer": true, - "dependencies": { - "lru-cache": "^10.0.1" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } + "node_modules/left-pad": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", + "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==", + "deprecated": "use String.prototype.padStart()", + "dev": true }, - "node_modules/semantic-release/node_modules/http-proxy-agent": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz", - "integrity": "sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==", - "peer": true, - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, "engines": { - "node": ">= 14" + "node": ">=6" } }, - "node_modules/semantic-release/node_modules/https-proxy-agent": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.1.tgz", - "integrity": "sha512-Eun8zV0kcYS1g19r78osiQLEFIRspRUDd9tIfBCTBPBeMieF/EsJNL8VI3xOIdYRDEkjQnqOYPsZ2DsWsVsFwQ==", - "peer": true, + "node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "dev": true, "dependencies": { - "agent-base": "^7.0.2", - "debug": "4" + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" }, "engines": { - "node": ">= 14" + "node": ">= 0.8.0" } }, - "node_modules/semantic-release/node_modules/into-stream": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-7.0.0.tgz", - "integrity": "sha512-2dYz766i9HprMBasCMvHMuazJ7u4WzhJwo5kb3iPSiW/iRYV6uPari3zHoqZlnuaR7V1bEiNMxikhp37rdBXbw==", - "peer": true, + "node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "dev": true, "dependencies": { - "from2": "^2.3.0", - "p-is-promise": "^3.0.0" + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/is-fullwidth-code-point": { + "node_modules/load-json-file/node_modules/pify": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/semantic-release/node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "peer": true, + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "peer": true, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=6.11.5" } }, - "node_modules/semantic-release/node_modules/is-text-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-2.0.0.tgz", - "integrity": "sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==", - "peer": true, + "node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, "dependencies": { - "text-extensions": "^2.0.0" + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" }, "engines": { - "node": ">=8" + "node": ">=8.9.0" } }, - "node_modules/semantic-release/node_modules/json-parse-even-better-errors": { + "node_modules/locate-path": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.0.tgz", - "integrity": "sha512-iZbGHafX/59r39gPwVPRBGw0QQKnA7tte5pSMrhWOW7swGsVvVTjmfyAV9pNqk8YGT7tRCdxRu8uzcgZwoDooA==", - "peer": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/semantic-release/node_modules/locate-path": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", - "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", - "peer": true, + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, "dependencies": { - "p-locate": "^6.0.0" + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=6" } }, - "node_modules/semantic-release/node_modules/lru-cache": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.1.tgz", - "integrity": "sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==", - "peer": true, - "engines": { - "node": "14 || >=16.14" - } + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "node_modules/semantic-release/node_modules/meow": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz", - "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==", - "peer": true, - "engines": { - "node": ">=16.10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "node_modules/lodash.assign": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", + "integrity": "sha512-hFuH8TY+Yji7Eja3mGiuAxBqLagejScbG8GbG0j6o9vzn0YL14My+ktnqtZgFTosKymC9/44wP6s7xyuLfnClw==", + "dev": true }, - "node_modules/semantic-release/node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "peer": true, - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } + "node_modules/lodash.assignin": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz", + "integrity": "sha512-yX/rx6d/UTVh7sSVWVSIMjfnz95evAgDFdb1ZozC35I9mSFCkmzptOzevxjgbQUsc78NR44LVHWjsoMQXy9FDg==" }, - "node_modules/semantic-release/node_modules/mime": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", - "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", - "peer": true, - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=10.0.0" - } + "node_modules/lodash.bind": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz", + "integrity": "sha512-lxdsn7xxlCymgLYo1gGvVrfHmkjDiyqVv62FAeF2i5ta72BipE1SLxw8hPEPLhD4/247Ijw07UQH7Hq/chT5LA==" }, - "node_modules/semantic-release/node_modules/normalize-package-data": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.0.tgz", - "integrity": "sha512-UL7ELRVxYBHBgYEtZCXjxuD5vPxnmvMGq0jp/dGPKKrN7tfsBh2IY7TlJ15WWwdjRWD3RJbnsygUurTK3xkPkg==", - "peer": true, - "dependencies": { - "hosted-git-info": "^7.0.0", - "is-core-module": "^2.8.1", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true }, - "node_modules/semantic-release/node_modules/normalize-url": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.0.tgz", - "integrity": "sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==", - "peer": true, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" }, - "node_modules/semantic-release/node_modules/npm": { - "version": "9.8.1", - "resolved": "https://registry.npmjs.org/npm/-/npm-9.8.1.tgz", - "integrity": "sha512-AfDvThQzsIXhYgk9zhbk5R+lh811lKkLAeQMMhSypf1BM7zUafeIIBzMzespeuVEJ0+LvY36oRQYf7IKLzU3rw==", - "bundleDependencies": [ - "@isaacs/string-locale-compare", - "@npmcli/arborist", - "@npmcli/config", - "@npmcli/fs", - "@npmcli/map-workspaces", - "@npmcli/package-json", - "@npmcli/promise-spawn", - "@npmcli/run-script", - "abbrev", - "archy", - "cacache", - "chalk", - "ci-info", - "cli-columns", - "cli-table3", - "columnify", - "fastest-levenshtein", - "fs-minipass", - "glob", - "graceful-fs", - "hosted-git-info", - "ini", - "init-package-json", - "is-cidr", - "json-parse-even-better-errors", - "libnpmaccess", - "libnpmdiff", - "libnpmexec", - "libnpmfund", - "libnpmhook", - "libnpmorg", - "libnpmpack", - "libnpmpublish", - "libnpmsearch", - "libnpmteam", - "libnpmversion", - "make-fetch-happen", - "minimatch", - "minipass", - "minipass-pipeline", - "ms", - "node-gyp", - "nopt", - "npm-audit-report", - "npm-install-checks", - "npm-package-arg", - "npm-pick-manifest", - "npm-profile", - "npm-registry-fetch", - "npm-user-validate", - "npmlog", - "p-map", - "pacote", - "parse-conflict-json", - "proc-log", - "qrcode-terminal", - "read", - "semver", - "sigstore", - "ssri", - "supports-color", - "tar", - "text-table", - "tiny-relative-date", - "treeverse", - "validate-npm-package-name", - "which", - "write-file-atomic" - ], - "peer": true, - "dependencies": { - "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/arborist": "^6.3.0", - "@npmcli/config": "^6.2.1", - "@npmcli/fs": "^3.1.0", - "@npmcli/map-workspaces": "^3.0.4", - "@npmcli/package-json": "^4.0.1", - "@npmcli/promise-spawn": "^6.0.2", - "@npmcli/run-script": "^6.0.2", - "abbrev": "^2.0.0", - "archy": "~1.0.0", - "cacache": "^17.1.3", - "chalk": "^5.3.0", - "ci-info": "^3.8.0", - "cli-columns": "^4.0.0", - "cli-table3": "^0.6.3", - "columnify": "^1.6.0", - "fastest-levenshtein": "^1.0.16", - "fs-minipass": "^3.0.2", - "glob": "^10.2.7", - "graceful-fs": "^4.2.11", - "hosted-git-info": "^6.1.1", - "ini": "^4.1.1", - "init-package-json": "^5.0.0", - "is-cidr": "^4.0.2", - "json-parse-even-better-errors": "^3.0.0", - "libnpmaccess": "^7.0.2", - "libnpmdiff": "^5.0.19", - "libnpmexec": "^6.0.3", - "libnpmfund": "^4.0.19", - "libnpmhook": "^9.0.3", - "libnpmorg": "^5.0.4", - "libnpmpack": "^5.0.19", - "libnpmpublish": "^7.5.0", - "libnpmsearch": "^6.0.2", - "libnpmteam": "^5.0.3", - "libnpmversion": "^4.0.2", - "make-fetch-happen": "^11.1.1", - "minimatch": "^9.0.3", - "minipass": "^5.0.0", - "minipass-pipeline": "^1.2.4", - "ms": "^2.1.2", - "node-gyp": "^9.4.0", - "nopt": "^7.2.0", - "npm-audit-report": "^5.0.0", - "npm-install-checks": "^6.1.1", - "npm-package-arg": "^10.1.0", - "npm-pick-manifest": "^8.0.1", - "npm-profile": "^7.0.1", - "npm-registry-fetch": "^14.0.5", - "npm-user-validate": "^2.0.0", - "npmlog": "^7.0.1", - "p-map": "^4.0.0", - "pacote": "^15.2.0", - "parse-conflict-json": "^3.0.1", - "proc-log": "^3.0.0", - "qrcode-terminal": "^0.12.0", - "read": "^2.1.0", - "semver": "^7.5.4", - "sigstore": "^1.7.0", - "ssri": "^10.0.4", - "supports-color": "^9.4.0", - "tar": "^6.1.15", - "text-table": "~0.2.0", - "tiny-relative-date": "^1.3.0", - "treeverse": "^3.0.0", - "validate-npm-package-name": "^5.0.0", - "which": "^3.0.1", - "write-file-atomic": "^5.0.1" - }, - "bin": { - "npm": "bin/npm-cli.js", - "npx": "bin/npx-cli.js" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } + "node_modules/lodash.filter": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz", + "integrity": "sha512-pXYUy7PR8BCLwX5mgJ/aNtyOvuJTdZAo9EQFUvMIYugqmJxnrYaANvTbgndOzHSCSR0wnlBBfRXJL5SbWxo3FQ==" + }, + "node_modules/lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==" + }, + "node_modules/lodash.foreach": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", + "integrity": "sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ==" }, - "node_modules/semantic-release/node_modules/npm-run-path": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", - "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", - "peer": true, - "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "node_modules/lodash.frompairs": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.frompairs/-/lodash.frompairs-4.0.1.tgz", + "integrity": "sha512-dvqe2I+cO5MzXCMhUnfYFa9MD+/760yx2aTAN1lqEcEkf896TxgrX373igVdqSJj6tQd0jnSLE1UMuKufqqxFw==", + "dev": true }, - "node_modules/semantic-release/node_modules/npm-run-path/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "dev": true }, - "node_modules/semantic-release/node_modules/npm/node_modules/@colors/colors": { - "version": "1.5.0", - "inBundle": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=0.1.90" - } + "node_modules/lodash.map": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", + "integrity": "sha512-worNHGKLDetmcEYDvh2stPCrrQRkP20E4l0iIS7F8EvzMqBBi7ltvFN5m1HvTf1P7Jk1txKhvFcmYsCr8O2F1Q==" }, - "node_modules/semantic-release/node_modules/npm/node_modules/@isaacs/cliui": { - "version": "8.0.2", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, - "node_modules/semantic-release/node_modules/npm/node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.0.1", - "inBundle": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } + "node_modules/lodash.pick": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", + "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==" }, - "node_modules/semantic-release/node_modules/npm/node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "inBundle": true, - "license": "MIT", - "peer": true + "node_modules/lodash.reduce": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz", + "integrity": "sha512-6raRe2vxCYBhpBu+B+TtNGUzah+hQjVdu3E17wfusjyrXBka2nBS8OH/gjVZ5PvHOhWmIZTYri09Z6n/QfnNMw==" }, - "node_modules/semantic-release/node_modules/npm/node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "inBundle": true, - "license": "MIT", - "peer": true, - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "node_modules/lodash.reject": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz", + "integrity": "sha512-qkTuvgEzYdyhiJBx42YPzPo71R1aEr0z79kAv7Ixg8wPFEjgRgJdUsGMG3Hf3OYSF/kHI79XhNlt+5Ar6OzwxQ==" }, - "node_modules/semantic-release/node_modules/npm/node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "inBundle": true, - "license": "MIT", - "peer": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } + "node_modules/lodash.some": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz", + "integrity": "sha512-j7MJE+TuT51q9ggt4fSgVqro163BEFjAt3u97IqU+JA2DkWl80nFTrowzLpZ/BnpN7rrl0JA/593NAdd8p/scQ==" }, - "node_modules/semantic-release/node_modules/npm/node_modules/@isaacs/string-locale-compare": { - "version": "1.1.0", - "inBundle": true, - "license": "ISC", - "peer": true + "node_modules/lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", + "dev": true }, - "node_modules/semantic-release/node_modules/npm/node_modules/@npmcli/arborist": { - "version": "6.3.0", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/fs": "^3.1.0", - "@npmcli/installed-package-contents": "^2.0.2", - "@npmcli/map-workspaces": "^3.0.2", - "@npmcli/metavuln-calculator": "^5.0.0", - "@npmcli/name-from-folder": "^2.0.0", - "@npmcli/node-gyp": "^3.0.0", - "@npmcli/package-json": "^4.0.0", - "@npmcli/query": "^3.0.0", - "@npmcli/run-script": "^6.0.0", - "bin-links": "^4.0.1", - "cacache": "^17.0.4", - "common-ancestor-path": "^1.0.1", - "hosted-git-info": "^6.1.1", - "json-parse-even-better-errors": "^3.0.0", - "json-stringify-nice": "^1.1.4", - "minimatch": "^9.0.0", - "nopt": "^7.0.0", - "npm-install-checks": "^6.0.0", - "npm-package-arg": "^10.1.0", - "npm-pick-manifest": "^8.0.1", - "npm-registry-fetch": "^14.0.3", - "npmlog": "^7.0.1", - "pacote": "^15.0.8", - "parse-conflict-json": "^3.0.0", - "proc-log": "^3.0.0", - "promise-all-reject-late": "^1.0.0", - "promise-call-limit": "^1.0.2", - "read-package-json-fast": "^3.0.2", - "semver": "^7.3.7", - "ssri": "^10.0.1", - "treeverse": "^3.0.0", - "walk-up-path": "^3.0.1" - }, - "bin": { - "arborist": "bin/index.js" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } + "node_modules/lodash.topairs": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.topairs/-/lodash.topairs-4.3.0.tgz", + "integrity": "sha512-qrRMbykBSEGdOgQLJJqVSdPWMD7Q+GJJ5jMRfQYb+LTLsw3tYVIabnCzRqTJb2WTo17PG5gNzXuFaZgYH/9SAQ==", + "dev": true }, - "node_modules/semantic-release/node_modules/npm/node_modules/@npmcli/config": { - "version": "6.2.1", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/logform": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.5.1.tgz", + "integrity": "sha512-9FyqAm9o9NKKfiAKfZoYo9bGXXuwMkxQiQttkT4YjjVtQVIQtK6LmVtlxmCaFswo6N4AfEkHqZTV0taDtPotNg==", "dependencies": { - "@npmcli/map-workspaces": "^3.0.2", - "ci-info": "^3.8.0", - "ini": "^4.1.0", - "nopt": "^7.0.0", - "proc-log": "^3.0.0", - "read-package-json-fast": "^3.0.2", - "semver": "^7.3.5", - "walk-up-path": "^3.0.1" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "@colors/colors": "1.5.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/@npmcli/disparity-colors": { - "version": "3.0.0", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "dependencies": { - "ansi-styles": "^4.3.0" + "js-tokens": "^3.0.0 || ^4.0.0" }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "bin": { + "loose-envify": "cli.js" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/@npmcli/fs": { - "version": "3.1.0", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "semver": "^7.3.5" - }, + "node_modules/lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/@npmcli/git": { - "version": "4.1.0", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, "dependencies": { - "@npmcli/promise-spawn": "^6.0.0", - "lru-cache": "^7.4.4", - "npm-pick-manifest": "^8.0.0", - "proc-log": "^3.0.0", - "promise-inflight": "^1.0.1", - "promise-retry": "^2.0.1", - "semver": "^7.3.5", - "which": "^3.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "yallist": "^3.0.2" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/@npmcli/installed-package-contents": { - "version": "2.0.2", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, "dependencies": { - "npm-bundled": "^3.0.0", - "npm-normalize-package-bin": "^3.0.0" - }, - "bin": { - "installed-package-contents": "lib/index.js" + "pify": "^4.0.1", + "semver": "^5.6.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=6" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/@npmcli/map-workspaces": { - "version": "3.0.4", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "@npmcli/name-from-folder": "^2.0.0", - "glob": "^10.2.2", - "minimatch": "^9.0.0", - "read-package-json-fast": "^3.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node_modules/make-dir/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/@npmcli/metavuln-calculator": { - "version": "5.0.1", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, "dependencies": { - "cacache": "^17.0.0", - "json-parse-even-better-errors": "^3.0.0", - "pacote": "^15.0.0", - "semver": "^7.3.5" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/semantic-release/node_modules/npm/node_modules/@npmcli/name-from-folder": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC", - "peer": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "tmpl": "1.0.5" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/@npmcli/node-gyp": { - "version": "3.0.0", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", + "dev": true, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/@npmcli/package-json": { - "version": "4.0.1", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==", + "dev": true, "dependencies": { - "@npmcli/git": "^4.1.0", - "glob": "^10.2.2", - "hosted-git-info": "^6.1.1", - "json-parse-even-better-errors": "^3.0.0", - "normalize-package-data": "^5.0.0", - "proc-log": "^3.0.0", - "semver": "^7.5.3" + "object-visit": "^1.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/@npmcli/promise-spawn": { - "version": "6.0.2", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "which": "^3.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } + "node_modules/math-random": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz", + "integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==", + "dev": true }, - "node_modules/semantic-release/node_modules/npm/node_modules/@npmcli/query": { - "version": "3.0.0", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/mdast-util-definitions": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-4.0.0.tgz", + "integrity": "sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ==", "dependencies": { - "postcss-selector-parser": "^6.0.10" + "unist-util-visit": "^2.0.0" }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/@npmcli/run-script": { - "version": "6.0.2", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/mdast-util-from-markdown": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-0.8.5.tgz", + "integrity": "sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==", "dependencies": { - "@npmcli/node-gyp": "^3.0.0", - "@npmcli/promise-spawn": "^6.0.0", - "node-gyp": "^9.0.0", - "read-package-json-fast": "^3.0.0", - "which": "^3.0.0" + "@types/mdast": "^3.0.0", + "mdast-util-to-string": "^2.0.0", + "micromark": "~2.11.0", + "parse-entities": "^2.0.0", + "unist-util-stringify-position": "^2.0.0" }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/semantic-release/node_modules/npm/node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "inBundle": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/semantic-release/node_modules/npm/node_modules/@sigstore/protobuf-specs": { - "version": "0.1.0", - "inBundle": true, - "license": "Apache-2.0", - "peer": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/@sigstore/tuf": { - "version": "1.0.2", - "inBundle": true, - "license": "Apache-2.0", - "peer": true, + "node_modules/mdast-util-to-hast": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-10.2.0.tgz", + "integrity": "sha512-JoPBfJ3gBnHZ18icCwHR50orC9kNH81tiR1gs01D8Q5YpV6adHNO9nKNuFBCJQ941/32PT1a63UF/DitmS3amQ==", "dependencies": { - "@sigstore/protobuf-specs": "^0.1.0", - "tuf-js": "^1.1.7" + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "mdast-util-definitions": "^4.0.0", + "mdurl": "^1.0.0", + "unist-builder": "^2.0.0", + "unist-util-generated": "^1.0.0", + "unist-util-position": "^3.0.0", + "unist-util-visit": "^2.0.0" }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/@tootallnate/once": { + "node_modules/mdast-util-to-string": { "version": "2.0.0", - "inBundle": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 10" + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz", + "integrity": "sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/@tufjs/canonical-json": { - "version": "1.0.0", - "inBundle": true, - "license": "MIT", - "peer": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } + "node_modules/mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==" }, - "node_modules/semantic-release/node_modules/npm/node_modules/@tufjs/models": { - "version": "1.0.4", - "inBundle": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@tufjs/canonical-json": "1.0.0", - "minimatch": "^9.0.0" - }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">= 0.6" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/abbrev": { + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "node_modules/merge-stream": { "version": "2.0.0", - "inBundle": true, - "license": "ISC", - "peer": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true }, - "node_modules/semantic-release/node_modules/npm/node_modules/abort-controller": { - "version": "3.0.0", - "inBundle": true, - "license": "MIT", - "peer": true, - "dependencies": { - "event-target-shim": "^5.0.0" - }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", "engines": { - "node": ">=6.5" + "node": ">= 0.6" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/agent-base": { - "version": "6.0.2", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/micromark": { + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-2.11.4.tgz", + "integrity": "sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" + "debug": "^4.0.0", + "parse-entities": "^2.0.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/agentkeepalive": { - "version": "4.3.0", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, "dependencies": { - "debug": "^4.1.0", - "depd": "^2.0.0", - "humanize-ms": "^1.2.1" + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" }, "engines": { - "node": ">= 8.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/aggregate-error": { - "version": "3.1.0", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/micromatch/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" }, "engines": { - "node": ">=8" - } - }, - "node_modules/semantic-release/node_modules/npm/node_modules/ansi-regex": { - "version": "5.0.1", - "inBundle": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/ansi-styles": { - "version": "4.3.0", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/micromatch/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "is-extendable": "^0.1.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/aproba": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/archy": { - "version": "1.0.0", - "inBundle": true, - "license": "MIT", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/are-we-there-yet": { + "node_modules/micromatch/node_modules/fill-range": { "version": "4.0.0", - "inBundle": true, - "license": "ISC", - "peer": true, + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dev": true, "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^4.1.0" + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/balanced-match": { - "version": "1.0.2", - "inBundle": true, - "license": "MIT", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/base64-js": { - "version": "1.5.1", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "inBundle": true, - "license": "MIT", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/bin-links": { - "version": "4.0.2", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/micromatch/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, "dependencies": { - "cmd-shim": "^6.0.0", - "npm-normalize-package-bin": "^3.0.0", - "read-cmd-shim": "^4.0.0", - "write-file-atomic": "^5.0.0" + "is-extendable": "^0.1.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/binary-extensions": { - "version": "2.2.0", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/micromatch/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, "engines": { - "node": ">=8" - } - }, - "node_modules/semantic-release/node_modules/npm/node_modules/brace-expansion": { - "version": "2.0.1", - "inBundle": true, - "license": "MIT", - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/semantic-release/node_modules/npm/node_modules/buffer": { - "version": "6.0.3", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "inBundle": true, - "license": "MIT", - "peer": true, - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/semantic-release/node_modules/npm/node_modules/builtins": { - "version": "5.0.1", - "inBundle": true, - "license": "MIT", - "peer": true, - "dependencies": { - "semver": "^7.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/cacache": { - "version": "17.1.3", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/micromatch/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dev": true, "dependencies": { - "@npmcli/fs": "^3.1.0", - "fs-minipass": "^3.0.0", - "glob": "^10.2.2", - "lru-cache": "^7.7.1", - "minipass": "^5.0.0", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "p-map": "^4.0.0", - "ssri": "^10.0.0", - "tar": "^6.1.11", - "unique-filename": "^3.0.0" + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/chalk": { - "version": "5.3.0", - "inBundle": true, - "license": "MIT", - "peer": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "engines": { + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/chownr": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "engines": { - "node": ">=10" + "node": ">= 0.6" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/ci-info": { - "version": "3.8.0", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, "engines": { - "node": ">=8" + "node": ">= 0.6" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/cidr-regex": { - "version": "3.1.1", - "inBundle": true, - "license": "BSD-2-Clause", - "peer": true, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dependencies": { - "ip-regex": "^4.1.0" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=10" + "node": "*" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/clean-stack": { - "version": "2.2.0", - "inBundle": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6" + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/cli-columns": { - "version": "4.0.0", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, "dependencies": { - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" }, "engines": { - "node": ">= 10" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/cli-table3": { - "version": "0.6.3", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, "dependencies": { - "string-width": "^4.2.0" - }, - "engines": { - "node": "10.* || >= 12.*" + "minimist": "^1.2.6" }, - "optionalDependencies": { - "@colors/colors": "1.5.0" + "bin": { + "mkdirp": "bin/cmd.js" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/clone": { - "version": "1.0.4", - "inBundle": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=0.8" - } + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, - "node_modules/semantic-release/node_modules/npm/node_modules/cmd-shim": { - "version": "6.0.1", - "inBundle": true, - "license": "ISC", - "peer": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } + "node_modules/nan": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", + "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==", + "dev": true, + "optional": true }, - "node_modules/semantic-release/node_modules/npm/node_modules/color-convert": { - "version": "2.0.1", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/nanoid": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-2.1.11.tgz", + "integrity": "sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA==" + }, + "node_modules/nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, "dependencies": { - "color-name": "~1.1.4" + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" }, "engines": { - "node": ">=7.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/color-name": { - "version": "1.1.4", - "inBundle": true, - "license": "MIT", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/color-support": { - "version": "1.1.3", - "inBundle": true, - "license": "ISC", - "peer": true, - "bin": { - "color-support": "bin.js" - } + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true }, - "node_modules/semantic-release/node_modules/npm/node_modules/columnify": { - "version": "1.6.0", - "inBundle": true, - "license": "MIT", - "peer": true, - "dependencies": { - "strip-ansi": "^6.0.1", - "wcwidth": "^1.0.0" - }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "engines": { - "node": ">=8.0.0" + "node": ">= 0.6" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/common-ancestor-path": { - "version": "1.0.1", - "inBundle": true, - "license": "ISC", - "peer": true + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true }, - "node_modules/semantic-release/node_modules/npm/node_modules/concat-map": { - "version": "0.0.1", - "inBundle": true, - "license": "MIT", - "peer": true + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" }, - "node_modules/semantic-release/node_modules/npm/node_modules/console-control-strings": { - "version": "1.1.0", - "inBundle": true, - "license": "ISC", - "peer": true + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true }, - "node_modules/semantic-release/node_modules/npm/node_modules/cross-spawn": { - "version": "7.0.3", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/node-notifier": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.5.tgz", + "integrity": "sha512-tVbHs7DyTLtzOiN78izLA85zRqB9NvEXkAf014Vx3jtSvn/xBl6bR8ZYifj+dFcFrKI21huSQgJZ6ZtL3B4HfQ==", + "dev": true, "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" + "growly": "^1.3.0", + "is-wsl": "^1.1.0", + "semver": "^5.5.0", + "shellwords": "^0.1.1", + "which": "^1.3.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/cross-spawn/node_modules/which": { - "version": "2.0.2", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "isexe": "^2.0.0" - }, + "node_modules/node-notifier/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" + "semver": "bin/semver" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/cssesc": { - "version": "3.0.0", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/node-releases": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", + "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", + "dev": true + }, + "node_modules/nodemon": { + "version": "1.19.4", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-1.19.4.tgz", + "integrity": "sha512-VGPaqQBNk193lrJFotBU8nvWZPqEZY2eIzymy2jjY0fJ9qIsxA0sxQ8ATPl0gZC645gijYEc1jtZvpS8QWzJGQ==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "chokidar": "^2.1.8", + "debug": "^3.2.6", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.0.4", + "pstree.remy": "^1.1.7", + "semver": "^5.7.1", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.2", + "update-notifier": "^2.5.0" + }, "bin": { - "cssesc": "bin/cssesc" + "nodemon": "bin/nodemon.js" }, "engines": { "node": ">=4" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/debug": { - "version": "4.3.4", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/nodemon/node_modules/anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "inBundle": true, - "license": "MIT", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/defaults": { - "version": "1.0.4", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/nodemon/node_modules/anymatch/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "dev": true, "dependencies": { - "clone": "^1.0.2" + "remove-trailing-separator": "^1.0.1" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/semantic-release/node_modules/npm/node_modules/delegates": { - "version": "1.0.0", - "inBundle": true, - "license": "MIT", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/depd": { - "version": "2.0.0", - "inBundle": true, - "license": "MIT", - "peer": true, "engines": { - "node": ">= 0.8" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/diff": { - "version": "5.1.0", - "inBundle": true, - "license": "BSD-3-Clause", - "peer": true, + "node_modules/nodemon/node_modules/binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true, "engines": { - "node": ">=0.3.1" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/eastasianwidth": { - "version": "0.2.0", - "inBundle": true, - "license": "MIT", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/emoji-regex": { - "version": "8.0.0", - "inBundle": true, - "license": "MIT", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/encoding": { - "version": "0.1.13", - "inBundle": true, - "license": "MIT", - "optional": true, - "peer": true, + "node_modules/nodemon/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, "dependencies": { - "iconv-lite": "^0.6.2" - } - }, - "node_modules/semantic-release/node_modules/npm/node_modules/env-paths": { - "version": "2.2.1", - "inBundle": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/semantic-release/node_modules/npm/node_modules/err-code": { - "version": "2.0.3", - "inBundle": true, - "license": "MIT", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/event-target-shim": { - "version": "5.0.1", - "inBundle": true, - "license": "MIT", - "peer": true, + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/events": { - "version": "3.3.0", - "inBundle": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=0.8.x" + "node_modules/nodemon/node_modules/chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "deprecated": "Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies", + "dev": true, + "dependencies": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + }, + "optionalDependencies": { + "fsevents": "^1.2.7" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/exponential-backoff": { - "version": "3.1.1", - "inBundle": true, - "license": "Apache-2.0", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/fastest-levenshtein": { - "version": "1.0.16", - "inBundle": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 4.9.1" + "node_modules/nodemon/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/foreground-child": { - "version": "3.1.1", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/nodemon/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" + "is-extendable": "^0.1.0" }, "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/fs-minipass": { - "version": "3.0.2", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/nodemon/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dev": true, "dependencies": { - "minipass": "^5.0.0" + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/fs.realpath": { - "version": "1.0.0", - "inBundle": true, - "license": "ISC", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/function-bind": { - "version": "1.1.1", - "inBundle": true, - "license": "MIT", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/gauge": { - "version": "5.0.1", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/nodemon/node_modules/fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "deprecated": "fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.3", - "console-control-strings": "^1.1.0", - "has-unicode": "^2.0.1", - "signal-exit": "^4.0.1", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.5" + "bindings": "^1.5.0", + "nan": "^2.12.1" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">= 4.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/glob": { - "version": "10.2.7", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/nodemon/node_modules/glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", + "dev": true, "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.0.3", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2", - "path-scurry": "^1.7.0" - }, - "bin": { - "glob": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/graceful-fs": { - "version": "4.2.11", - "inBundle": true, - "license": "ISC", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/has": { - "version": "1.0.3", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/nodemon/node_modules/glob-parent/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", + "dev": true, "dependencies": { - "function-bind": "^1.1.1" + "is-extglob": "^2.1.0" }, "engines": { - "node": ">= 0.4.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/has-unicode": { - "version": "2.0.1", - "inBundle": true, - "license": "ISC", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/hosted-git-info": { - "version": "6.1.1", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/nodemon/node_modules/is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==", + "dev": true, "dependencies": { - "lru-cache": "^7.5.1" + "binary-extensions": "^1.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/http-cache-semantics": { - "version": "4.1.1", - "inBundle": true, - "license": "BSD-2-Clause", - "peer": true + "node_modules/nodemon/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/semantic-release/node_modules/npm/node_modules/http-proxy-agent": { - "version": "5.0.0", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/nodemon/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/https-proxy-agent": { - "version": "5.0.1", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/nodemon/node_modules/readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, "dependencies": { - "agent-base": "6", - "debug": "4" + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" }, "engines": { - "node": ">= 6" + "node": ">=0.10" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/humanize-ms": { - "version": "1.2.1", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/nodemon/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/nodemon/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/nodemon/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, "dependencies": { - "ms": "^2.0.0" + "safe-buffer": "~5.1.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/iconv-lite": { - "version": "0.6.3", - "inBundle": true, - "license": "MIT", - "optional": true, - "peer": true, + "node_modules/nodemon/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dev": true, "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/ieee754": { - "version": "1.2.1", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "inBundle": true, - "license": "BSD-3-Clause", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/ignore-walk": { - "version": "6.0.3", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", + "dev": true, "dependencies": { - "minimatch": "^9.0.0" + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "*" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/imurmurhash": { - "version": "0.1.4", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, "engines": { - "node": ">=0.8.19" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/indent-string": { - "version": "4.0.0", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", + "dev": true, + "dependencies": { + "path-key": "^2.0.0" + }, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/inflight": { - "version": "1.0.6", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", "dependencies": { - "once": "^1.3.0", - "wrappy": "1" + "boolbase": "~1.0.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/inherits": { - "version": "2.0.4", - "inBundle": true, - "license": "ISC", - "peer": true + "node_modules/nwsapi": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.4.tgz", + "integrity": "sha512-NHj4rzRo0tQdijE9ZqAx6kYDcoRwYwSYzCA8MY3JzfxlrvEU0jhnhJT9BhqhJs7I/dKcrDm6TyulaRqZPIhN5g==", + "dev": true + }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "engines": { + "node": "*" + } }, - "node_modules/semantic-release/node_modules/npm/node_modules/ini": { + "node_modules/object-assign": { "version": "4.1.1", - "inBundle": true, - "license": "ISC", - "peer": true, + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/init-package-json": { - "version": "5.0.0", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==", + "dev": true, "dependencies": { - "npm-package-arg": "^10.0.0", - "promzard": "^1.0.0", - "read": "^2.0.0", - "read-package-json": "^6.0.0", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4", - "validate-npm-package-name": "^5.0.0" + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/ip": { - "version": "2.0.0", - "inBundle": true, - "license": "MIT", - "peer": true + "node_modules/object-copy/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/semantic-release/node_modules/npm/node_modules/ip-regex": { - "version": "4.3.0", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/object-copy/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/is-cidr": { - "version": "4.0.2", - "inBundle": true, - "license": "BSD-2-Clause", - "peer": true, + "node_modules/object-copy/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", + "dev": true, "dependencies": { - "cidr-regex": "^3.1.1" + "kind-of": "^3.0.2" }, "engines": { - "node": ">=10" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/is-core-module": { - "version": "2.12.1", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/object-copy/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, "dependencies": { - "has": "^1.0.3" + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/object-copy/node_modules/is-descriptor/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/is-lambda": { - "version": "1.0.1", - "inBundle": true, - "license": "MIT", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/isexe": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/jackspeak": { - "version": "2.2.1", - "inBundle": true, - "license": "BlueOak-1.0.0", - "peer": true, + "node_modules/object-copy/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "is-buffer": "^1.1.5" }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/semantic-release/node_modules/npm/node_modules/json-parse-even-better-errors": { - "version": "3.0.0", - "inBundle": true, - "license": "MIT", - "peer": true, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/json-stringify-nice": { - "version": "1.1.4", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/jsonparse": { - "version": "1.3.1", - "engines": [ - "node >= 0.2.0" - ], - "inBundle": true, - "license": "MIT", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/just-diff": { - "version": "6.0.2", - "inBundle": true, - "license": "MIT", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/just-diff-apply": { - "version": "5.5.0", - "inBundle": true, - "license": "MIT", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/libnpmaccess": { - "version": "7.0.2", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "npm-package-arg": "^10.1.0", - "npm-registry-fetch": "^14.0.3" - }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">= 0.4" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/libnpmdiff": { - "version": "5.0.19", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==", + "dev": true, "dependencies": { - "@npmcli/arborist": "^6.3.0", - "@npmcli/disparity-colors": "^3.0.0", - "@npmcli/installed-package-contents": "^2.0.2", - "binary-extensions": "^2.2.0", - "diff": "^5.1.0", - "minimatch": "^9.0.0", - "npm-package-arg": "^10.1.0", - "pacote": "^15.0.8", - "tar": "^6.1.13" + "isobject": "^3.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/libnpmexec": { - "version": "6.0.3", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dev": true, "dependencies": { - "@npmcli/arborist": "^6.3.0", - "@npmcli/run-script": "^6.0.0", - "ci-info": "^3.7.1", - "npm-package-arg": "^10.1.0", - "npmlog": "^7.0.1", - "pacote": "^15.0.8", - "proc-log": "^3.0.0", - "read": "^2.0.0", - "read-package-json-fast": "^3.0.2", - "semver": "^7.3.7", - "walk-up-path": "^3.0.1" + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/libnpmfund": { - "version": "4.0.19", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/object.getownpropertydescriptors": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.5.tgz", + "integrity": "sha512-yDNzckpM6ntyQiGTik1fKV1DcVDRS+w8bvpWNCBanvH5LfRX9O8WTHqQzG4RZwRAM4I0oU7TV11Lj5v0g20ibw==", + "dev": true, "dependencies": { - "@npmcli/arborist": "^6.3.0" + "array.prototype.reduce": "^1.0.5", + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">= 0.8" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/libnpmhook": { - "version": "9.0.3", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/object.omit": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "integrity": "sha512-UiAM5mhmIuKLsOvrL+B0U2d1hXHF3bFYWIuH1LMpuV2EJEHG1Ntz06PgLEHjm6VFd87NpH8rastvPoyv6UW2fA==", + "dev": true, "dependencies": { - "aproba": "^2.0.0", - "npm-registry-fetch": "^14.0.3" + "for-own": "^0.1.4", + "is-extendable": "^0.1.1" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/libnpmorg": { - "version": "5.0.4", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "aproba": "^2.0.0", - "npm-registry-fetch": "^14.0.3" - }, + "node_modules/object.omit/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/libnpmpack": { - "version": "5.0.19", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", + "dev": true, "dependencies": { - "@npmcli/arborist": "^6.3.0", - "@npmcli/run-script": "^6.0.0", - "npm-package-arg": "^10.1.0", - "pacote": "^15.0.8" + "isobject": "^3.0.1" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/libnpmpublish": { - "version": "7.5.0", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "dependencies": { - "ci-info": "^3.6.1", - "normalize-package-data": "^5.0.0", - "npm-package-arg": "^10.1.0", - "npm-registry-fetch": "^14.0.3", - "proc-log": "^3.0.0", - "semver": "^7.3.7", - "sigstore": "^1.4.0", - "ssri": "^10.0.1" + "ee-first": "1.1.1" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">= 0.8" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/libnpmsearch": { - "version": "6.0.2", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dependencies": { - "npm-registry-fetch": "^14.0.3" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "wrappy": "1" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/libnpmteam": { - "version": "5.0.3", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", "dependencies": { - "aproba": "^2.0.0", - "npm-registry-fetch": "^14.0.3" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "fn.name": "1.x.x" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/libnpmversion": { - "version": "4.0.2", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/open": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", "dependencies": { - "@npmcli/git": "^4.0.1", - "@npmcli/run-script": "^6.0.0", - "json-parse-even-better-errors": "^3.0.0", - "proc-log": "^3.0.0", - "semver": "^7.3.7" + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/semantic-release/node_modules/npm/node_modules/lru-cache": { - "version": "7.18.3", - "inBundle": true, - "license": "ISC", - "peer": true, - "engines": { - "node": ">=12" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/make-fetch-happen": { - "version": "11.1.1", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/open/node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", "dependencies": { - "agentkeepalive": "^4.2.1", - "cacache": "^17.0.0", - "http-cache-semantics": "^4.1.1", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^7.7.1", - "minipass": "^5.0.0", - "minipass-fetch": "^3.0.0", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.3", - "promise-retry": "^2.0.1", - "socks-proxy-agent": "^7.0.0", - "ssri": "^10.0.0" + "is-docker": "^2.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=8" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/minimatch": { - "version": "9.0.3", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, "dependencies": { - "brace-expansion": "^2.0.1" + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">= 0.8.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/minipass": { - "version": "5.0.0", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/minipass-collect": { - "version": "1.0.2", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/p-each-series": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-1.0.0.tgz", + "integrity": "sha512-J/e9xiZZQNrt+958FFzJ+auItsBGq+UrQ7nE89AUP7UOTtjHnkISANXLdayhVzh538UnLMCSlf13lFfRIAKQOA==", + "dev": true, "dependencies": { - "minipass": "^3.0.0" + "p-reduce": "^1.0.0" }, "engines": { - "node": ">= 8" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/minipass-collect/node_modules/minipass": { - "version": "3.3.6", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "yallist": "^4.0.0" - }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "dev": true, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/minipass-fetch": { - "version": "3.0.3", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, "dependencies": { - "minipass": "^5.0.0", - "minipass-sized": "^1.0.3", - "minizlib": "^2.1.2" + "p-try": "^2.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=6" }, - "optionalDependencies": { - "encoding": "^0.1.13" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/minipass-flush": { - "version": "1.0.5", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, "dependencies": { - "minipass": "^3.0.0" + "p-limit": "^2.0.0" }, "engines": { - "node": ">= 8" + "node": ">=6" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/minipass-flush/node_modules/minipass": { - "version": "3.3.6", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "yallist": "^4.0.0" - }, + "node_modules/p-reduce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz", + "integrity": "sha512-3Tx1T3oM1xO/Y8Gj0sWyE78EIJZ+t+aEmXUdvQgvGmSMri7aPTHoovbXEreWKkL5j21Er60XAWLTzKbAKYOujQ==", + "dev": true, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/minipass-json-stream": { - "version": "1.0.1", - "inBundle": true, - "license": "MIT", - "peer": true, - "dependencies": { - "jsonparse": "^1.3.1", - "minipass": "^3.0.0" + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/minipass-json-stream/node_modules/minipass": { - "version": "3.3.6", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/package-json": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", + "integrity": "sha512-q/R5GrMek0vzgoomq6rm9OX+3PQve8sLwTirmK30YB3Cu0Bbt9OX9M/SIUnroN5BGJkzwGsFwDaRGD9EwBOlCA==", + "dev": true, "dependencies": { - "yallist": "^4.0.0" + "got": "^6.7.1", + "registry-auth-token": "^3.0.1", + "registry-url": "^3.0.3", + "semver": "^5.1.0" }, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/minipass-pipeline": { - "version": "1.2.4", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" + "node_modules/package-json/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/minipass-pipeline/node_modules/minipass": { - "version": "3.3.6", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/parse-entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", + "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", "dependencies": { - "yallist": "^4.0.0" + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" }, - "engines": { - "node": ">=8" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/minipass-sized": { - "version": "1.0.3", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/parse-glob": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "integrity": "sha512-FC5TeK0AwXzq3tUBFtH74naWkPQCEWs4K+xMxWZBlKDWu0bVHXGZa+KKqxKidd7xwhdZ19ZNuF2uO1M/r196HA==", + "dev": true, "dependencies": { - "minipass": "^3.0.0" + "glob-base": "^0.3.0", + "is-dotfile": "^1.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.0" }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/minipass-sized/node_modules/minipass": { - "version": "3.3.6", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "yallist": "^4.0.0" - }, + "node_modules/parse-glob/node_modules/is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww==", + "dev": true, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/minizlib": { - "version": "2.1.2", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/parse-glob/node_modules/is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg==", + "dev": true, "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" + "is-extglob": "^1.0.0" }, "engines": { - "node": ">= 8" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/minizlib/node_modules/minipass": { - "version": "3.3.6", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, "dependencies": { - "yallist": "^4.0.0" + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" }, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/mkdirp": { - "version": "1.0.4", - "inBundle": true, - "license": "MIT", - "peer": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } + "node_modules/parse-srcset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz", + "integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==" }, - "node_modules/semantic-release/node_modules/npm/node_modules/ms": { - "version": "2.1.3", - "inBundle": true, - "license": "MIT", - "peer": true + "node_modules/parse5": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", + "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", + "dev": true }, - "node_modules/semantic-release/node_modules/npm/node_modules/mute-stream": { - "version": "1.0.0", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">= 0.8" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/negotiator": { - "version": "0.6.3", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==", + "dev": true, "engines": { - "node": ">= 0.6" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/node-gyp": { - "version": "9.4.0", - "inBundle": true, - "license": "MIT", - "peer": true, - "dependencies": { - "env-paths": "^2.2.0", - "exponential-backoff": "^3.1.1", - "glob": "^7.1.4", - "graceful-fs": "^4.2.6", - "make-fetch-happen": "^11.0.3", - "nopt": "^6.0.0", - "npmlog": "^6.0.0", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.2", - "which": "^2.0.2" + "node_modules/patch-package": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-6.5.1.tgz", + "integrity": "sha512-I/4Zsalfhc6bphmJTlrLoOcAF87jcxko4q0qsv4bGcurbr8IskEOtdnt9iCmsQVGL1B+iUhSQqweyTLJfCF9rA==", + "dependencies": { + "@yarnpkg/lockfile": "^1.1.0", + "chalk": "^4.1.2", + "cross-spawn": "^6.0.5", + "find-yarn-workspace-root": "^2.0.0", + "fs-extra": "^9.0.0", + "is-ci": "^2.0.0", + "klaw-sync": "^6.0.0", + "minimist": "^1.2.6", + "open": "^7.4.2", + "rimraf": "^2.6.3", + "semver": "^5.6.0", + "slash": "^2.0.0", + "tmp": "^0.0.33", + "yaml": "^1.10.2" }, "bin": { - "node-gyp": "bin/node-gyp.js" + "patch-package": "index.js" }, "engines": { - "node": "^12.13 || ^14.13 || >=16" + "node": ">=10", + "npm": ">5" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/node-gyp/node_modules/abbrev": { - "version": "1.1.1", - "inBundle": true, - "license": "ISC", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/node-gyp/node_modules/are-we-there-yet": { - "version": "3.0.1", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/patch-package/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" + "color-convert": "^2.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/semantic-release/node_modules/npm/node_modules/node-gyp/node_modules/brace-expansion": { - "version": "1.1.11", - "inBundle": true, - "license": "MIT", - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/semantic-release/node_modules/npm/node_modules/node-gyp/node_modules/gauge": { - "version": "4.0.4", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.3", - "console-control-strings": "^1.1.0", - "has-unicode": "^2.0.1", - "signal-exit": "^3.0.7", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.5" + "node": ">=8" }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/node-gyp/node_modules/glob": { - "version": "7.2.3", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/patch-package/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": "*" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/node-gyp/node_modules/minimatch": { - "version": "3.1.2", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/patch-package/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dependencies": { - "brace-expansion": "^1.1.7" + "color-name": "~1.1.4" }, "engines": { - "node": "*" + "node": ">=7.0.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/node-gyp/node_modules/nopt": { - "version": "6.0.0", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "abbrev": "^1.0.0" - }, - "bin": { - "nopt": "bin/nopt.js" - }, + "node_modules/patch-package/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/patch-package/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=8" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/node-gyp/node_modules/npmlog": { - "version": "6.0.2", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "are-we-there-yet": "^3.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^4.0.3", - "set-blocking": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node_modules/patch-package/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/node-gyp/node_modules/readable-stream": { - "version": "3.6.2", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/patch-package/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "has-flag": "^4.0.0" }, "engines": { - "node": ">= 6" + "node": ">=8" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/node-gyp/node_modules/signal-exit": { - "version": "3.0.7", - "inBundle": true, - "license": "ISC", - "peer": true + "node_modules/path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==", + "dev": true }, - "node_modules/semantic-release/node_modules/npm/node_modules/node-gyp/node_modules/which": { - "version": "2.0.2", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, + "node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, "engines": { - "node": ">= 8" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/nopt": { - "version": "7.2.0", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "abbrev": "^2.0.0" - }, - "bin": { - "nopt": "bin/nopt.js" - }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", + "dev": true + }, + "node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/normalize-package-data": { - "version": "5.0.0", - "inBundle": true, - "license": "BSD-2-Clause", - "peer": true, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, "dependencies": { - "hosted-git-info": "^6.0.0", - "is-core-module": "^2.8.1", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4" + "pify": "^3.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/semantic-release/node_modules/npm/node_modules/npm-audit-report": { - "version": "5.0.0", - "inBundle": true, - "license": "ISC", - "peer": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/npm-bundled": { + "node_modules/path-type/node_modules/pify": { "version": "3.0.0", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "npm-normalize-package-bin": "^3.0.0" - }, + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/npm-install-checks": { - "version": "6.1.1", - "inBundle": true, - "license": "BSD-2-Clause", - "peer": true, - "dependencies": { - "semver": "^7.1.1" - }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" + }, + "node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/npm-normalize-package-bin": { - "version": "3.0.1", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=6" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/npm-package-arg": { - "version": "10.1.0", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "hosted-git-info": "^6.0.0", - "proc-log": "^3.0.0", - "semver": "^7.3.5", - "validate-npm-package-name": "^5.0.0" - }, + "node_modules/pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "dev": true, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/npm-packlist": { - "version": "7.0.4", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, "dependencies": { - "ignore-walk": "^6.0.0" + "find-up": "^4.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=8" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/npm-pick-manifest": { - "version": "8.0.1", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, "dependencies": { - "npm-install-checks": "^6.0.0", - "npm-normalize-package-bin": "^3.0.0", - "npm-package-arg": "^10.0.0", - "semver": "^7.3.5" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=8" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/npm-profile": { - "version": "7.0.1", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, "dependencies": { - "npm-registry-fetch": "^14.0.0", - "proc-log": "^3.0.0" + "p-locate": "^4.1.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=8" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/npm-registry-fetch": { - "version": "14.0.5", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, "dependencies": { - "make-fetch-happen": "^11.0.0", - "minipass": "^5.0.0", - "minipass-fetch": "^3.0.0", - "minipass-json-stream": "^1.0.1", - "minizlib": "^2.1.2", - "npm-package-arg": "^10.0.0", - "proc-log": "^3.0.0" + "p-limit": "^2.2.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=8" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/npm-user-validate": { - "version": "2.0.0", - "inBundle": true, - "license": "BSD-2-Clause", - "peer": true, + "node_modules/pkg-dir/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=8" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/npmlog": { - "version": "7.0.1", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "are-we-there-yet": "^4.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^5.0.0", - "set-blocking": "^2.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } + "node_modules/pn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", + "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", + "dev": true }, - "node_modules/semantic-release/node_modules/npm/node_modules/once": { - "version": "1.4.0", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "wrappy": "1" + "node_modules/posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/p-map": { - "version": "4.0.0", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dependencies": { - "aggregate-error": "^3.0.0" + "picocolors": "^0.2.1", + "source-map": "^0.6.1" }, "engines": { - "node": ">=10" + "node": ">=6.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/postcss/" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/pacote": { - "version": "15.2.0", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "@npmcli/git": "^4.0.0", - "@npmcli/installed-package-contents": "^2.0.1", - "@npmcli/promise-spawn": "^6.0.1", - "@npmcli/run-script": "^6.0.0", - "cacache": "^17.0.0", - "fs-minipass": "^3.0.0", - "minipass": "^5.0.0", - "npm-package-arg": "^10.0.0", - "npm-packlist": "^7.0.0", - "npm-pick-manifest": "^8.0.0", - "npm-registry-fetch": "^14.0.0", - "proc-log": "^3.0.0", - "promise-retry": "^2.0.1", - "read-package-json": "^6.0.0", - "read-package-json-fast": "^3.0.0", - "sigstore": "^1.3.0", - "ssri": "^10.0.0", - "tar": "^6.1.11" - }, - "bin": { - "pacote": "lib/bin.js" + "node_modules/postcss-modules-extract-imports": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz", + "integrity": "sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==", + "dev": true, + "dependencies": { + "postcss": "^7.0.5" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/parse-conflict-json": { - "version": "3.0.1", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/postcss-modules-local-by-default": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.3.tgz", + "integrity": "sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw==", + "dev": true, "dependencies": { - "json-parse-even-better-errors": "^3.0.0", - "just-diff": "^6.0.0", - "just-diff-apply": "^5.2.0" + "icss-utils": "^4.1.1", + "postcss": "^7.0.32", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/semantic-release/node_modules/npm/node_modules/path-is-absolute": { - "version": "1.0.1", - "inBundle": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/semantic-release/node_modules/npm/node_modules/path-key": { - "version": "3.1.1", - "inBundle": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=8" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/path-scurry": { - "version": "1.9.2", - "inBundle": true, - "license": "BlueOak-1.0.0", - "peer": true, + "node_modules/postcss-modules-scope": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz", + "integrity": "sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ==", + "dev": true, "dependencies": { - "lru-cache": "^9.1.1", - "minipass": "^5.0.0 || ^6.0.2" + "postcss": "^7.0.6", + "postcss-selector-parser": "^6.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/path-scurry/node_modules/lru-cache": { - "version": "9.1.1", - "inBundle": true, - "license": "ISC", - "peer": true, - "engines": { - "node": "14 || >=16.14" + "node_modules/postcss-modules-values": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz", + "integrity": "sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg==", + "dev": true, + "dependencies": { + "icss-utils": "^4.0.0", + "postcss": "^7.0.6" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/postcss-selector-parser": { - "version": "6.0.13", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/postcss-selector-parser": { + "version": "6.0.11", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz", + "integrity": "sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==", + "dev": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -20093,1243 +9913,1097 @@ "node": ">=4" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/proc-log": { - "version": "3.0.0", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/postcss/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/process": { - "version": "0.11.10", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "dev": true, "engines": { - "node": ">= 0.6.0" + "node": ">= 0.8.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/promise-all-reject-late": { - "version": "1.0.1", - "inBundle": true, - "license": "ISC", - "peer": true, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node_modules/prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha512-PhmXi5XmoyKw1Un4E+opM2KcsJInDvKyuOumcjjw3waw86ZNjHwVUOOWLc4bCzLdcKNaWBH9e99sbWzDQsVaYg==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/promise-call-limit": { - "version": "1.0.2", - "inBundle": true, - "license": "ISC", - "peer": true, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node_modules/preserve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", + "integrity": "sha512-s/46sYeylUfHNjI+sA/78FAHlmIuKqI9wNnzEOGehAlUUYeObv5C2mOinXBjyUyWmJ2SfcS2/ydApH4hTF4WXQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/promise-inflight": { - "version": "1.0.1", - "inBundle": true, - "license": "ISC", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/promise-retry": { - "version": "2.0.1", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/pretty-format": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", + "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "dev": true, "dependencies": { - "err-code": "^2.0.2", - "retry": "^0.12.0" + "@jest/types": "^24.9.0", + "ansi-regex": "^4.0.0", + "ansi-styles": "^3.2.0", + "react-is": "^16.8.4" }, "engines": { - "node": ">=10" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/promzard": { - "version": "1.0.0", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, "dependencies": { - "read": "^2.0.0" + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/qrcode-terminal": { - "version": "0.12.0", - "inBundle": true, - "peer": true, - "bin": { - "qrcode-terminal": "bin/qrcode-terminal.js" + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/read": { - "version": "2.1.0", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/property-information": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz", + "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==", "dependencies": { - "mute-stream": "~1.0.0" + "xtend": "^4.0.0" }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/read-cmd-shim": { - "version": "4.0.0", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/property-information/node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.4" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/read-package-json": { - "version": "6.0.4", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "dependencies": { - "glob": "^10.2.2", - "json-parse-even-better-errors": "^3.0.0", - "normalize-package-data": "^5.0.0", - "npm-normalize-package-bin": "^3.0.0" + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">= 0.10" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/read-package-json-fast": { - "version": "3.0.2", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "json-parse-even-better-errors": "^3.0.0", - "npm-normalize-package-bin": "^3.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } + "node_modules/pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", + "dev": true }, - "node_modules/semantic-release/node_modules/npm/node_modules/readable-stream": { - "version": "4.4.0", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "end-of-stream": "^1.1.0", + "once": "^1.3.1" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/retry": { - "version": "0.12.0", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", "engines": { - "node": ">= 4" + "node": ">=6" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/rimraf": { - "version": "3.0.2", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", "dependencies": { - "glob": "^7.1.3" + "side-channel": "^1.0.4" }, - "bin": { - "rimraf": "bin.js" + "engines": { + "node": ">=0.6" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/rimraf/node_modules/brace-expansion": { - "version": "1.1.11", - "inBundle": true, - "license": "MIT", - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "node_modules/querystring": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.1.tgz", + "integrity": "sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "engines": { + "node": ">=0.4.x" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/rimraf/node_modules/glob": { - "version": "7.2.3", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/randomatic": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", + "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", + "dev": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "is-number": "^4.0.0", + "kind-of": "^6.0.0", + "math-random": "^1.0.1" }, "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">= 0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/rimraf/node_modules/minimatch": { - "version": "3.1.2", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, + "node_modules/randomatic/node_modules/is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true, "engines": { - "node": "*" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/safe-buffer": { - "version": "5.2.1", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "inBundle": true, - "license": "MIT", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/safer-buffer": { - "version": "2.1.2", - "inBundle": true, - "license": "MIT", - "optional": true, - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/semver": { - "version": "7.5.4", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "engines": { - "node": ">=10" + "node": ">= 0.6" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "dependencies": { - "yallist": "^4.0.0" + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" }, "engines": { - "node": ">=10" + "node": ">= 0.8" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/set-blocking": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/shebang-command": { - "version": "2.0.0", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, "dependencies": { - "shebang-regex": "^3.0.0" + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" }, - "engines": { - "node": ">=8" + "bin": { + "rc": "cli.js" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/shebang-regex": { - "version": "3.0.0", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/react": { + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", + "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2" + }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/signal-exit": { - "version": "4.0.2", - "inBundle": true, - "license": "ISC", - "peer": true, - "engines": { - "node": ">=14" + "node_modules/react-class": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/react-class/-/react-class-3.2.2.tgz", + "integrity": "sha512-6mGkYJjqsCyazXfIIjAe3vf+KMsSTgJnyj7UK7tgFYIfv2lBQT9MB7rs8DQxESPNhBDu6UZ1jnTLpLzA2wKn6w==", + "dependencies": { + "object-assign": "^4.1.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "peerDependencies": { + "react": ">=0.14.0 || >=15.0.0-0", + "react-dom": ">=0.14.0 || >=15.0.0-0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/sigstore": { - "version": "1.7.0", - "inBundle": true, - "license": "Apache-2.0", - "peer": true, + "node_modules/react-dom": { + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz", + "integrity": "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==", "dependencies": { - "@sigstore/protobuf-specs": "^0.1.0", - "@sigstore/tuf": "^1.0.1", - "make-fetch-happen": "^11.0.1" - }, - "bin": { - "sigstore": "bin/sigstore.js" + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "scheduler": "^0.19.1" }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/semantic-release/node_modules/npm/node_modules/smart-buffer": { - "version": "4.2.0", - "inBundle": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" + "peerDependencies": { + "react": "^16.14.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/socks": { - "version": "2.7.1", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/react-image-crop": { + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/react-image-crop/-/react-image-crop-8.6.12.tgz", + "integrity": "sha512-3CNz1xfsRRSH/iH023IDMXxzsb1M6RTHHUVsVcb8uFPcjGiA9WisvQ24G1eRDf2j4NlybupOEEdfK2vT0etN6A==", "dependencies": { - "ip": "^2.0.0", - "smart-buffer": "^4.2.0" + "clsx": "^1.1.1", + "prop-types": "^15.7.2" }, - "engines": { - "node": ">= 10.13.0", - "npm": ">= 3.0.0" + "peerDependencies": { + "react": ">=16.13.1" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/socks-proxy-agent": { - "version": "7.0.0", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/react-jsonschema-form": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/react-jsonschema-form/-/react-jsonschema-form-1.8.1.tgz", + "integrity": "sha512-aaDloxNAcGXOOOcdKOxxqEEn5oDlPUZgWcs8unXXB9vjBRgCF8rCm/wVSv1u2G5ih0j/BX6Ewd/WjI2g00lPdg==", + "deprecated": "react-jsonschema-form has been moved to @rjsf/core", "dependencies": { - "agent-base": "^6.0.2", - "debug": "^4.3.3", - "socks": "^2.6.2" + "@babel/runtime-corejs2": "^7.4.5", + "ajv": "^6.7.0", + "core-js": "^2.5.7", + "lodash": "^4.17.15", + "prop-types": "^15.5.8", + "react-is": "^16.8.4", + "react-lifecycles-compat": "^3.0.4", + "shortid": "^2.2.14" }, "engines": { - "node": ">= 10" + "node": ">=6", + "npm": ">=2.14.7" + }, + "peerDependencies": { + "react": ">=15" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/spdx-correct": { - "version": "3.2.0", - "inBundle": true, - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } + "node_modules/react-jsonschema-form/node_modules/core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", + "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", + "hasInstallScript": true }, - "node_modules/semantic-release/node_modules/npm/node_modules/spdx-exceptions": { - "version": "2.3.0", - "inBundle": true, - "license": "CC-BY-3.0", - "peer": true + "node_modules/react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" }, - "node_modules/semantic-release/node_modules/npm/node_modules/spdx-expression-parse": { - "version": "3.0.1", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/react-markdown": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-6.0.3.tgz", + "integrity": "sha512-kQbpWiMoBHnj9myLlmZG9T1JdoT/OEyHK7hqM6CqFT14MAkgWiWBUYijLyBmxbntaN6dCDicPcUhWhci1QYodg==", "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" + "@types/hast": "^2.0.0", + "@types/unist": "^2.0.3", + "comma-separated-tokens": "^1.0.0", + "prop-types": "^15.7.2", + "property-information": "^5.3.0", + "react-is": "^17.0.0", + "remark-parse": "^9.0.0", + "remark-rehype": "^8.0.0", + "space-separated-tokens": "^1.1.0", + "style-to-object": "^0.3.0", + "unified": "^9.0.0", + "unist-util-visit": "^2.0.0", + "vfile": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=16", + "react": ">=16" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/spdx-license-ids": { - "version": "3.0.13", - "inBundle": true, - "license": "CC0-1.0", - "peer": true + "node_modules/react-markdown/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, - "node_modules/semantic-release/node_modules/npm/node_modules/ssri": { - "version": "10.0.4", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "minipass": "^5.0.0" - }, + "node_modules/react-tag-autocomplete": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/react-tag-autocomplete/-/react-tag-autocomplete-6.3.0.tgz", + "integrity": "sha512-MUBVUFh5eOqshUm5NM20qp7zXk8TzSiKO4GoktlFzBLIOLs356npaMKtL02bm0nFV2f1zInUrXn1fq6+i5YX0w==", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/semantic-release/node_modules/npm/node_modules/string_decoder": { - "version": "1.3.0", - "inBundle": true, - "license": "MIT", - "peer": true, - "dependencies": { - "safe-buffer": "~5.2.0" + "node": ">= 10.0.0" + }, + "peerDependencies": { + "prop-types": "^15.5.0", + "react": "^16.5.0 || ^17.0.0", + "react-dom": "^16.5.0 || ^17.0.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/string-width": { - "version": "4.2.3", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "dev": true, "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" }, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/read-pkg-up": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", + "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", + "dev": true, "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "find-up": "^3.0.0", + "read-pkg": "^3.0.0" }, "engines": { - "node": ">=8" + "node": ">=6" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/strip-ansi": { - "version": "6.0.1", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dependencies": { - "ansi-regex": "^5.0.1" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" }, "engines": { - "node": ">=8" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "optional": true, "dependencies": { - "ansi-regex": "^5.0.1" + "picomatch": "^2.2.1" }, "engines": { - "node": ">=8" - } - }, - "node_modules/semantic-release/node_modules/npm/node_modules/supports-color": { - "version": "9.4.0", - "inBundle": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "node": ">=8.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/tar": { - "version": "6.1.15", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/realpath-native": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.1.0.tgz", + "integrity": "sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA==", + "dev": true, "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" + "util.promisify": "^1.0.0" }, "engines": { - "node": ">=10" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/tar/node_modules/fs-minipass": { - "version": "2.1.0", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/rechoir": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", + "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", + "dev": true, "dependencies": { - "minipass": "^3.0.0" + "resolve": "^1.9.0" }, "engines": { - "node": ">= 8" + "node": ">= 0.10" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { - "version": "3.3.6", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/redis": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/redis/-/redis-2.8.0.tgz", + "integrity": "sha512-M1OkonEQwtRmZv4tEWF2VgpG0JWJ8Fv1PhlgT5+B+uNq2cA3Rt1Yt/ryoR+vQNOQcIEgdCdfH0jr3bDpihAw1A==", "dependencies": { - "yallist": "^4.0.0" + "double-ended-queue": "^2.1.0-0", + "redis-commands": "^1.2.0", + "redis-parser": "^2.6.0" }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/text-table": { - "version": "0.2.0", - "inBundle": true, - "license": "MIT", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/tiny-relative-date": { - "version": "1.3.0", - "inBundle": true, - "license": "MIT", - "peer": true + "node_modules/redis-commands": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", + "integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==" }, - "node_modules/semantic-release/node_modules/npm/node_modules/treeverse": { - "version": "3.0.0", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/redis-parser": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-2.6.0.tgz", + "integrity": "sha512-9Hdw19gwXFBJdN8ENUoNVJFRyMDFrE/ZBClPicKYDPwNPJ4ST1TedAHYNSiGKElwh2vrmRGMoJYbVdJd+WQXIw==", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/tuf-js": { - "version": "1.1.7", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", + "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", + "dev": true, "dependencies": { - "@tufjs/models": "1.0.4", - "debug": "^4.3.4", - "make-fetch-happen": "^11.1.1" + "regenerate": "^1.4.2" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/unique-filename": { - "version": "3.0.0", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + }, + "node_modules/regenerator-transform": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz", + "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==", + "dev": true, "dependencies": { - "unique-slug": "^4.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "@babel/runtime": "^7.8.4" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/unique-slug": { - "version": "4.0.0", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/regex-cache": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", + "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", + "dev": true, "dependencies": { - "imurmurhash": "^0.1.4" + "is-equal-shallow": "^0.1.3" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/util-deprecate": { + "node_modules/regex-not": { "version": "1.0.2", - "inBundle": true, - "license": "MIT", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/validate-npm-package-license": { - "version": "3.0.4", - "inBundle": true, - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/semantic-release/node_modules/npm/node_modules/validate-npm-package-name": { - "version": "5.0.0", - "inBundle": true, - "license": "ISC", - "peer": true, + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, "dependencies": { - "builtins": "^5.0.0" + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/walk-up-path": { - "version": "3.0.1", - "inBundle": true, - "license": "ISC", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/wcwidth": { - "version": "1.0.1", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/regexp.prototype.flags": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", + "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", + "dev": true, "dependencies": { - "defaults": "^1.0.3" + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "functions-have-names": "^1.2.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/which": { - "version": "3.0.1", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/regexpu-core": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", + "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", + "dev": true, "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/which.js" + "@babel/regjsgen": "^0.8.0", + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsparser": "^0.9.1", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/wide-align": { - "version": "1.1.5", - "inBundle": true, - "license": "ISC", - "peer": true, + "node_modules/registry-auth-token": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz", + "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==", + "dev": true, "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" + "rc": "^1.1.6", + "safe-buffer": "^5.0.1" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/wrap-ansi": { - "version": "8.1.0", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/registry-url": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", + "integrity": "sha512-ZbgR5aZEdf4UKZVBPYIgaglBmSF2Hi94s2PcIHhRGFjKYu+chjJdYfHn4rt3hB6eCKLJ8giVIIfgMa1ehDfZKA==", + "dev": true, "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" + "rc": "^1.0.1" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/regjsparser": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "dev": true, "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/semantic-release/node_modules/npm/node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "6.0.1", - "inBundle": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=12" + "jsesc": "~0.5.0" }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "bin": { + "regjsparser": "bin/parser" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "6.2.1", - "inBundle": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/wrap-ansi/node_modules/emoji-regex": { - "version": "9.2.2", - "inBundle": true, - "license": "MIT", - "peer": true - }, - "node_modules/semantic-release/node_modules/npm/node_modules/wrap-ansi/node_modules/string-width": { - "version": "5.1.2", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/remark-parse": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-9.0.0.tgz", + "integrity": "sha512-geKatMwSzEXKHuzBNU1z676sGcDcFoChMK38TgdHJNAYfFtsfHDQG7MoJAjs6sgYMqyLduCYWDIWZIxiPeafEw==", "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" + "mdast-util-from-markdown": "^0.8.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "7.1.0", - "inBundle": true, - "license": "MIT", - "peer": true, + "node_modules/remark-rehype": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-8.1.0.tgz", + "integrity": "sha512-EbCu9kHgAxKmW1yEYjx3QafMyGY3q8noUbNUI5xyKbaFP89wbhDrKxyIQNukNYthzjNHZu6J7hwFg7hRm1svYA==", "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" + "mdast-util-to-hast": "^10.2.0" }, "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/wrappy": { - "version": "1.0.2", - "inBundle": true, - "license": "ISC", - "peer": true + "node_modules/remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", + "dev": true }, - "node_modules/semantic-release/node_modules/npm/node_modules/write-file-atomic": { - "version": "5.0.1", - "inBundle": true, - "license": "ISC", - "peer": true, - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^4.0.1" - }, + "node_modules/repeat-element": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", + "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", + "dev": true, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/npm/node_modules/yallist": { - "version": "4.0.0", - "inBundle": true, - "license": "ISC", - "peer": true - }, - "node_modules/semantic-release/node_modules/p-each-series": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-3.0.0.tgz", - "integrity": "sha512-lastgtAdoH9YaLyDa5i5z64q+kzOcQHsQ5SsZJD3q0VEyI8mq872S3geuNbRUQLVAE9siMfgKrpj7MloKFHruw==", - "peer": true, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "dev": true, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10" } }, - "node_modules/semantic-release/node_modules/p-filter": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-3.0.0.tgz", - "integrity": "sha512-QtoWLjXAW++uTX67HZQz1dbTpqBfiidsB6VtQUC9iR85S120+s0T5sO6s+B5MLzFcZkrEd/DGMmCjR+f2Qpxwg==", - "peer": true, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", "dependencies": { - "p-map": "^5.1.0" + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 6" } }, - "node_modules/semantic-release/node_modules/p-limit": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", - "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", - "peer": true, + "node_modules/request-promise-core": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", + "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", + "dev": true, "dependencies": { - "yocto-queue": "^1.0.0" + "lodash": "^4.17.19" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=0.10.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "request": "^2.34" } }, - "node_modules/semantic-release/node_modules/p-locate": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", - "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", - "peer": true, + "node_modules/request-promise-native": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz", + "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==", + "deprecated": "request-promise-native has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142", + "dev": true, "dependencies": { - "p-limit": "^4.0.0" + "request-promise-core": "1.1.4", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=0.12.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "request": "^2.34" } }, - "node_modules/semantic-release/node_modules/p-map": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-5.5.0.tgz", - "integrity": "sha512-VFqfGDHlx87K66yZrNdI4YGtD70IRyd+zSvgks6mzHPRNkoKy+9EKP4SFC77/vTTQYmRmti7dvqC+m5jBrBAcg==", - "peer": true, - "dependencies": { - "aggregate-error": "^4.0.0" - }, + "node_modules/request/node_modules/qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.6" } }, - "node_modules/semantic-release/node_modules/p-reduce": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-3.0.0.tgz", - "integrity": "sha512-xsrIUgI0Kn6iyDYm9StOpOeK29XM1aboGji26+QEortiFST1hGZaUQOLhtEbqHErPpGW/aSz6allwK2qcptp0Q==", - "peer": true, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/parse-json": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-7.0.0.tgz", - "integrity": "sha512-kP+TQYAzAiVnzOlWOe0diD6L35s9bJh0SCn95PIbZFKrOYuIRQsQkeWEYxzVDuHTt9V9YqvYCJ2Qo4z9wdfZPw==", - "peer": true, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "node_modules/resolve": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", + "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "dev": true, "dependencies": { - "@babel/code-frame": "^7.21.4", - "error-ex": "^1.3.2", - "json-parse-even-better-errors": "^3.0.0", - "lines-and-columns": "^2.0.3", - "type-fest": "^3.8.0" + "is-core-module": "^2.11.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" }, - "engines": { - "node": ">=16" + "bin": { + "resolve": "bin/resolve" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/semantic-release/node_modules/parse-json/node_modules/type-fest": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", - "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", - "peer": true, - "engines": { - "node": ">=14.16" + "node_modules/resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha512-ccu8zQTrzVr954472aUVPLEcB3YpKSYR3cg/3lo1okzobPBM+1INXBbBZlDbnI/hbEocnf8j0QVo43hQKrbchg==", + "dev": true, + "dependencies": { + "resolve-from": "^3.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/semantic-release/node_modules/path-exists": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", - "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", - "peer": true, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "peer": true, + "node_modules/resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", + "dev": true, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/semantic-release/node_modules/read-pkg": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-8.1.0.tgz", - "integrity": "sha512-PORM8AgzXeskHO/WEv312k9U03B8K9JSiWF/8N9sUuFjBa+9SF2u6K7VClzXwDXab51jCd8Nd36CNM+zR97ScQ==", - "peer": true, - "dependencies": { - "@types/normalize-package-data": "^2.4.1", - "normalize-package-data": "^6.0.0", - "parse-json": "^7.0.0", - "type-fest": "^4.2.0" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "node_modules/resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==", + "deprecated": "https://github.com/lydell/resolve-url#deprecated", + "dev": true }, - "node_modules/semantic-release/node_modules/read-pkg-up": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-10.1.0.tgz", - "integrity": "sha512-aNtBq4jR8NawpKJQldrQcSW9y/d+KWH4v24HWkHljOZ7H0av+YTGANBzRh9A5pw7v/bLVsLVPpOhJ7gHNVy8lA==", - "peer": true, - "dependencies": { - "find-up": "^6.3.0", - "read-pkg": "^8.1.0", - "type-fest": "^4.2.0" - }, + "node_modules/ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true, "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.12" } }, - "node_modules/semantic-release/node_modules/registry-auth-token": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.0.2.tgz", - "integrity": "sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==", - "peer": true, + "node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", "dependencies": { - "@pnpm/npm-conf": "^2.1.0" + "glob": "^7.1.3" }, - "engines": { - "node": ">=14" + "bin": { + "rimraf": "bin.js" } }, - "node_modules/semantic-release/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "peer": true, + "node_modules/rsvp": { + "version": "4.8.5", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", + "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", + "dev": true, "engines": { - "node": ">=8" + "node": "6.* || >= 7.*" } }, - "node_modules/semantic-release/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "peer": true, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==", + "dev": true, "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" + "ret": "~0.1.10" } }, - "node_modules/semantic-release/node_modules/semver-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-4.0.0.tgz", - "integrity": "sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==", - "peer": true, + "node_modules/safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dev": true, "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": ">=12" + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/semantic-release/node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "peer": true, - "dependencies": { - "yallist": "^4.0.0" - }, + "node_modules/safe-stable-stringify": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", + "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", "engines": { "node": ">=10" } }, - "node_modules/semantic-release/node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "peer": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/semantic-release/node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "peer": true, - "engines": { - "node": ">=8" - } + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, - "node_modules/semantic-release/node_modules/signal-exit": { + "node_modules/sane": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "peer": true, - "engines": { - "node": ">=14" + "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", + "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", + "deprecated": "some dependency vulnerabilities fixed, support for node < 10 dropped, and newer ECMAScript syntax/features added", + "dev": true, + "dependencies": { + "@cnakazawa/watch": "^1.0.3", + "anymatch": "^2.0.0", + "capture-exit": "^2.0.0", + "exec-sh": "^0.3.2", + "execa": "^1.0.0", + "fb-watchman": "^2.0.0", + "micromatch": "^3.1.4", + "minimist": "^1.1.1", + "walker": "~1.0.5" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/semantic-release/node_modules/slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", - "peer": true, - "engines": { - "node": ">=12" + "bin": { + "sane": "src/cli.js" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/semantic-release/node_modules/split2": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", - "peer": true, "engines": { - "node": ">= 10.x" + "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/semantic-release/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "peer": true, + "node_modules/sane/node_modules/anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" } }, - "node_modules/semantic-release/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "peer": true, + "node_modules/sane/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "dev": true, "dependencies": { - "ansi-regex": "^5.0.1" + "remove-trailing-separator": "^1.0.1" }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/semantic-release/node_modules/temp-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-3.0.0.tgz", - "integrity": "sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==", - "peer": true, - "engines": { - "node": ">=14.16" + "node_modules/sanitize-html": { + "version": "1.27.5", + "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.27.5.tgz", + "integrity": "sha512-M4M5iXDAUEcZKLXkmk90zSYWEtk5NH3JmojQxKxV371fnMh+x9t1rqdmXaGoyEHw3z/X/8vnFhKjGL5xFGOJ3A==", + "dependencies": { + "htmlparser2": "^4.1.0", + "lodash": "^4.17.15", + "parse-srcset": "^1.0.2", + "postcss": "^7.0.27" } }, - "node_modules/semantic-release/node_modules/tempy": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/tempy/-/tempy-3.1.0.tgz", - "integrity": "sha512-7jDLIdD2Zp0bDe5r3D2qtkd1QOCacylBuL7oa4udvN6v2pqr4+LcCr67C8DR1zkpaZ8XosF5m1yQSabKAW6f2g==", - "peer": true, + "node_modules/sanitize-html/node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", "dependencies": { - "is-stream": "^3.0.0", - "temp-dir": "^3.0.0", - "type-fest": "^2.12.2", - "unique-string": "^3.0.0" - }, - "engines": { - "node": ">=14.16" + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" } }, - "node_modules/semantic-release/node_modules/tempy/node_modules/type-fest": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", - "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", - "peer": true, + "node_modules/sanitize-html/node_modules/dom-serializer/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dependencies": { + "domelementtype": "^2.2.0" + }, "engines": { - "node": ">=12.20" + "node": ">= 4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/fb55/domhandler?sponsor=1" } }, - "node_modules/semantic-release/node_modules/text-extensions": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.4.0.tgz", - "integrity": "sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==", - "peer": true, + "node_modules/sanitize-html/node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/sanitize-html/node_modules/domhandler": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.3.0.tgz", + "integrity": "sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==", + "dependencies": { + "domelementtype": "^2.0.1" + }, "engines": { - "node": ">=8" + "node": ">= 4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/fb55/domhandler?sponsor=1" } }, - "node_modules/semantic-release/node_modules/type-fest": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.3.1.tgz", - "integrity": "sha512-pphNW/msgOUSkJbH58x8sqpq8uQj6b0ZKGxEsLKMUnGorRcDjrUaLS+39+/ub41JNTwrrMyJcUB8+YZs3mbwqw==", - "peer": true, - "engines": { - "node": ">=16" + "node_modules/sanitize-html/node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/fb55/domutils?sponsor=1" } }, - "node_modules/semantic-release/node_modules/unique-string": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", - "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", - "peer": true, + "node_modules/sanitize-html/node_modules/domutils/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", "dependencies": { - "crypto-random-string": "^4.0.0" + "domelementtype": "^2.2.0" }, "engines": { - "node": ">=12" + "node": ">= 4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/fb55/domhandler?sponsor=1" } }, - "node_modules/semantic-release/node_modules/url-join": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-5.0.0.tgz", - "integrity": "sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==", - "peer": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node_modules/sanitize-html/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/semantic-release/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "peer": true, + "node_modules/sanitize-html/node_modules/htmlparser2": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-4.1.0.tgz", + "integrity": "sha512-4zDq1a1zhE4gQso/c5LP1OtrhYTncXNSpvJYtWJBtXAETPlMfi3IFNjGuQbYLuVY4ZR0QMqRVvo4Pdy9KLyP8Q==", "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" + "domelementtype": "^2.0.1", + "domhandler": "^3.0.0", + "domutils": "^2.0.0", + "entities": "^2.0.0" + } + }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, + "node_modules/scheduler": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", + "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" } }, - "node_modules/semantic-release/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "peer": true, + "node_modules/schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dev": true, "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" }, "engines": { - "node": ">=10" + "node": ">= 8.9.0" }, "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/semantic-release/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "peer": true, - "engines": { - "node": ">=10" + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "node_modules/semantic-release/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "peer": true - }, - "node_modules/semantic-release/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "peer": true, + "node_modules/scroll-into-view-if-needed": { + "version": "2.2.31", + "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.31.tgz", + "integrity": "sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==", "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" + "compute-scroll-into-view": "^1.0.20" } }, - "node_modules/semantic-release/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "peer": true, + "node_modules/seekout": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/seekout/-/seekout-1.0.2.tgz", + "integrity": "sha512-eB4fRqHZCE8gmwFOVHC9tHzLkZJ2Y12qJvAJQox8kWjGObA++tgVeXHACsDCPajfNGnDaK2Juv+WzNlMuO43wQ==", "engines": { - "node": ">=12" + "node": ">=0.12" } }, "node_modules/semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, "bin": { "semver": "bin/semver.js" } @@ -21355,18 +11029,6 @@ "semver": "bin/semver" } }, - "node_modules/semver-regex": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-4.0.5.tgz", - "integrity": "sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw==", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/send": { "version": "0.18.0", "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", @@ -21539,33 +11201,8 @@ "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, - "node_modules/signale": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/signale/-/signale-1.4.0.tgz", - "integrity": "sha512-iuh+gPf28RkltuJC7W5MRi6XAjTDCAPC/prJUpQoG4vIP3MJZ+GTydVnodXA7pwvTKb2cA0m9OFZW/cdWy/I/w==", - "peer": true, - "dependencies": { - "chalk": "^2.3.2", - "figures": "^2.0.0", - "pkg-conf": "^2.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/signale/node_modules/figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==", - "peer": true, - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=4" - } + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true }, "node_modules/simple-swizzle": { "version": "0.2.2", @@ -21945,16 +11582,11 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/spawn-error-forwarder": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/spawn-error-forwarder/-/spawn-error-forwarder-1.0.0.tgz", - "integrity": "sha512-gRjMgK5uFjbCvdibeGJuy3I5OYz6VLoVdsOJdA6wV0WlfQVLFueoqMxwwYD9RODdgb6oUIvlRlsyFSiQkMKu0g==", - "peer": true - }, "node_modules/spdx-correct": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, "dependencies": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" @@ -21963,12 +11595,14 @@ "node_modules/spdx-exceptions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true }, "node_modules/spdx-expression-parse": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" @@ -21977,18 +11611,8 @@ "node_modules/spdx-license-ids": { "version": "3.0.13", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz", - "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==" - }, - "node_modules/split": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", - "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", - "dependencies": { - "through": "2" - }, - "engines": { - "node": "*" - } + "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==", + "dev": true }, "node_modules/split-string": { "version": "3.1.0", @@ -22002,14 +11626,6 @@ "node": ">=0.10.0" } }, - "node_modules/split2": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", - "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", - "dependencies": { - "readable-stream": "^3.0.0" - } - }, "node_modules/sshpk": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", @@ -22176,46 +11792,6 @@ "node": ">=0.10.0" } }, - "node_modules/stream-combiner2": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", - "integrity": "sha512-3PnJbYgS56AeWgtKF5jtJRT6uFJe56Z0Hc5Ngg/6sI6rIt8iiMBTa9cvdyFfpMQjaVHr8dusbNeFGIIonxOvKw==", - "peer": true, - "dependencies": { - "duplexer2": "~0.1.0", - "readable-stream": "^2.0.2" - } - }, - "node_modules/stream-combiner2/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "peer": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/stream-combiner2/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "peer": true - }, - "node_modules/stream-combiner2/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "peer": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -22353,6 +11929,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, "engines": { "node": ">=4" } @@ -22366,33 +11943,11 @@ "node": ">=0.10.0" } }, - "node_modules/strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dependencies": { - "min-indent": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -22441,44 +11996,11 @@ "node": ">=4" } }, - "node_modules/supports-hyperlinks": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", - "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", - "peer": true, - "dependencies": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-hyperlinks/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-hyperlinks/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "peer": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, "engines": { "node": ">= 0.4" }, @@ -22506,73 +12028,6 @@ "node": ">=6" } }, - "node_modules/temp-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", - "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/tempy": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tempy/-/tempy-1.0.1.tgz", - "integrity": "sha512-biM9brNqxSc04Ee71hzFbryD11nX7VPhQQY32AdDmjFvodsRFz/3ufeoTZ6uYkRFfGo188tENcASNs3vTdsM0w==", - "dependencies": { - "del": "^6.0.0", - "is-stream": "^2.0.0", - "temp-dir": "^2.0.0", - "type-fest": "^0.16.0", - "unique-string": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/tempy/node_modules/crypto-random-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/tempy/node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/tempy/node_modules/type-fest": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", - "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/tempy/node_modules/unique-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", - "dependencies": { - "crypto-random-string": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/term-size": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", @@ -22799,14 +12254,6 @@ "node": ">=6" } }, - "node_modules/text-extensions": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz", - "integrity": "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==", - "engines": { - "node": ">=0.10" - } - }, "node_modules/text-hex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", @@ -22818,19 +12265,6 @@ "integrity": "sha512-wCVxLDcFxw7ujDxaeJC6nfl2XfHJNYs8yUYJnvMgtPEFlttP9tHSfRUv2vBe6C4hkVFPWoP1P6ZccbYjmSEkKA==", "dev": true }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" - }, - "node_modules/through2": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", - "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", - "dependencies": { - "readable-stream": "3" - } - }, "node_modules/timed-out": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", @@ -22975,33 +12409,6 @@ "punycode": "^2.1.0" } }, - "node_modules/traverse": { - "version": "0.6.7", - "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.7.tgz", - "integrity": "sha512-/y956gpUo9ZNCb99YjxG7OaslxZWHfCHAUUfshwqOXmxUIvqLjVO581BT+gM59+QV9tFe6/CGG53tsA1Y7RSdg==", - "peer": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/trim-newlines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", - "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/trim-right": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", - "integrity": "sha512-WZGXGstmCWgeevgTL54hrCuw1dyMQIzWy7ZfqRJfSmJZBwklI15egmQytFP6bPidmw3M8d5yEowl1niq4vmqZw==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/triple-beam": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", @@ -23049,17 +12456,6 @@ "node": ">= 0.8.0" } }, - "node_modules/type-fest": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", - "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -23086,18 +12482,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/uglify-js": { - "version": "3.17.4", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", - "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -23317,11 +12701,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/universal-user-agent": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", - "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" - }, "node_modules/universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", @@ -23495,11 +12874,6 @@ "deprecated": "Please see https://github.com/lydell/urix#deprecated", "dev": true }, - "node_modules/url-join": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", - "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==" - }, "node_modules/url-parse-lax": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", @@ -23563,6 +12937,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, "dependencies": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" @@ -24146,11 +13521,6 @@ "node": ">=0.10.0" } }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" - }, "node_modules/wrap-ansi": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", @@ -24188,6 +13558,7 @@ "version": "2.4.1", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.1.tgz", "integrity": "sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg==", + "dev": true, "dependencies": { "graceful-fs": "^4.1.11", "imurmurhash": "^0.1.4", @@ -24295,18 +13666,6 @@ "engines": { "node": ">=6" } - }, - "node_modules/yocto-queue": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", - "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", - "peer": true, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } } } } From a2f68b3371912f0559b61e69dc74182db4858dff Mon Sep 17 00:00:00 2001 From: yonadavGit <yonadav.le@gmail.com> Date: Wed, 20 Sep 2023 11:34:20 +0300 Subject: [PATCH 269/756] chore: cleaner code and better component naming --- static/css/s2.css | 4 ---- static/js/TopicPage.jsx | 15 ++++++--------- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index 33dda1b34b..33757cdd4d 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -2468,10 +2468,6 @@ input.resourcesLink { padding-bottom: 12px; border-top: 1px solid #EDEDEC; } -.navBlockAuthorPage { - padding-bottom: 12px; - width: 50%; -} .navBlock.withColorLine { border-top: 4px solid transparent; } diff --git a/static/js/TopicPage.jsx b/static/js/TopicPage.jsx index dc1aec0d17..c7e512a531 100644 --- a/static/js/TopicPage.jsx +++ b/static/js/TopicPage.jsx @@ -352,24 +352,21 @@ const TopicHeader = ({ topic, topicData, multiPanel, isCat, setNavTopic, openDis <div> <div className="sectionTitleText authorIndexTitle"><InterfaceText>Works on Sefaria</InterfaceText></div> <div className="authorIndexList"> - {topicData.indexes.map(({url, title, description}) => <AuthorDisplayItem key={url} url={url} enTitle={title.en} heTitle={title.he} enDesc={description.en} heDesc={description.he}/>)} + {topicData.indexes.map(({url, title, description}) => <AuthorIndexItem key={url} url={url} title={title} description={description}/>)} </div> </div> : null} </div> );} -const AuthorDisplayItem = ({url, enTitle, heTitle, enDesc, heDesc}) => { - const classes = classNames({ navBlockTitle: 1 }); +const AuthorIndexItem = ({url, title, description}) => { return ( - <div className="navBlockAuthorPage" > - <a href={url} - className = {classes} - > - <InterfaceText text={{en: enTitle, he: heTitle}} /> + <div className="authorIndex" > + <a href={url} className="navBlockTitle"> + <InterfaceText text={title} /> </a> <div className="navBlockDescription"> - <InterfaceText text={{en: enDesc, he: heDesc}} /> + <InterfaceText text={description} /> </div> </div> ); From e8dbbf8df5a6c257f02803c20144f79d78e3a3b1 Mon Sep 17 00:00:00 2001 From: yonadavGit <yonadav.le@gmail.com> Date: Wed, 20 Sep 2023 13:18:40 +0300 Subject: [PATCH 270/756] chore: remove space between "Works on Sefaria" title and indexes --- static/css/s2.css | 1 - 1 file changed, 1 deletion(-) diff --git a/static/css/s2.css b/static/css/s2.css index 33757cdd4d..b578b95ac1 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -2332,7 +2332,6 @@ h1 .languageToggle .he { } .authorIndexTitle { margin-top: 30px; - margin-bottom: 17px; } .sectionTitleText.authorIndexTitle .int-en { text-transform: none; From e82558133601d9b8b55899e88b4d238aacd63eb2 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 20 Sep 2023 14:39:35 +0300 Subject: [PATCH 271/756] helm: add refs to elastic secrets --- helm-chart/sefaria-project/values.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/helm-chart/sefaria-project/values.yaml b/helm-chart/sefaria-project/values.yaml index f4efcbb893..7ba1f6af3f 100644 --- a/helm-chart/sefaria-project/values.yaml +++ b/helm-chart/sefaria-project/values.yaml @@ -375,12 +375,12 @@ secrets: elasticUser: # If you're using a reference to an existing secret then the data: section # should be commented out and vice-versa. - ref: + ref: elastic-user # data: elasticAdmin: # If you're using a reference to an existing secret then the data: section # should be commented out and vice-versa. - ref: + ref: elastic-admin # data: From 56063c23f7a2d14a673c652adf64d67dbbc3299f Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 20 Sep 2023 14:46:08 +0300 Subject: [PATCH 272/756] helm: read SEARCH_HOST from env vars --- .../templates/configmap/local-settings-file.yaml | 2 +- .../sefaria-project/templates/configmap/local-settings.yaml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml b/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml index 5b4675672d..2fd02ba86d 100644 --- a/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml +++ b/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml @@ -137,7 +137,7 @@ data: SERVER_EMAIL = os.getenv("SERVER_EMAIL") auth_str = f'{os.getenv("ELASTIC_USERNAME")}:{os.getenv("ELASTIC_PASSWORD")}@' if os.getenv("ELASTIC_USERNAME") else '' - SEARCH_ADMIN = f'http://{auth_str}' + {{ .Values.nginx.SEARCH_HOST | quote }} + ":9200" + SEARCH_ADMIN = f'http://{auth_str}' + {os.getenv("SEARCH_HOST")} + ":9200" SEARCH_INDEX_ON_SAVE = True SEARCH_INDEX_NAME_TEXT = 'text' # name of the ElasticSearch index to use SEARCH_INDEX_NAME_SHEET = 'sheet' diff --git a/helm-chart/sefaria-project/templates/configmap/local-settings.yaml b/helm-chart/sefaria-project/templates/configmap/local-settings.yaml index 44f04c8de1..aa39d2ddc4 100644 --- a/helm-chart/sefaria-project/templates/configmap/local-settings.yaml +++ b/helm-chart/sefaria-project/templates/configmap/local-settings.yaml @@ -25,3 +25,4 @@ data: SENTRY_ENVIRONMENT: {{ .Values.deployEnv | quote }} SENTRY_CODE_VERSION: {{ .Values.web.containerImage.tag }} FAIL_GRACEFULLY: "{{ .Values.localSettings.FAIL_GRACEFULLY }}" + SEARCH_HOST: {{ .Values.nginx.SEARCH_HOST | quote }} From 841978ee6546b2e89a7d11a16ffdd329b0245268 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 20 Sep 2023 14:55:12 +0300 Subject: [PATCH 273/756] chore(search): duplicate elasticsearch reindex cronjob and associated files while migrating to elasticsearch 8 --- .../cronjob/reindex-elasticsearch-es6.yaml | 77 ++ scripts/reindex_elasticsearch_cronjob_ES6.py | 49 + sefaria/search_ES6.py | 844 ++++++++++++++++++ 3 files changed, 970 insertions(+) create mode 100644 helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch-es6.yaml create mode 100644 scripts/reindex_elasticsearch_cronjob_ES6.py create mode 100644 sefaria/search_ES6.py diff --git a/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch-es6.yaml b/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch-es6.yaml new file mode 100644 index 0000000000..2253eba8cc --- /dev/null +++ b/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch-es6.yaml @@ -0,0 +1,77 @@ +{{- if .Values.cronJobs.reindexElasticSearch.enabled }} +--- +apiVersion: batch/v1 +kind: CronJob +metadata: + name: {{ .Values.deployEnv }}-reindex-elastic-search-es6 + labels: + {{- include "sefaria.labels" . | nindent 4 }} +spec: + schedule: "20 13 * * 0" + jobTemplate: + spec: + backoffLimit: 1 + template: + spec: + affinity: + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: app + operator: In + values: + - mongo + topologyKey: kubernetes.io.hostname + containers: + - name: reindex-elastic-search-es6 + image: "{{ .Values.web.containerImage.imageRegistry }}:{{ .Values.web.containerImage.tag }}" + resources: + limits: + memory: 9Gi + requests: + memory: 7Gi + env: + - name: REDIS_HOST + value: "redis-{{ .Values.deployEnv }}" + - name: NODEJS_HOST + value: "node-{{ .Values.deployEnv }}-{{ .Release.Revision }}" + - name: VARNISH_HOST + value: "varnish-{{ .Values.deployEnv }}-{{ .Release.Revision }}" + - name: SLACK_URL + valueFrom: + secretKeyRef: + name: {{ template "sefaria.secrets.slackWebhook" . }} + key: slack-webhook + envFrom: + - secretRef: + name: {{ template "sefaria.secrets.elasticAdmin" . }} + - secretRef: + name: {{ .Values.secrets.localSettings.ref }} + optional: true + - configMapRef: + name: local-settings-{{ .Values.deployEnv }} + - secretRef: + name: local-settings-secrets-{{ .Values.deployEnv }} + optional: true + volumeMounts: + - mountPath: /app/sefaria/local_settings.py + name: local-settings + subPath: local_settings.py + readOnly: true + command: ["bash"] + args: [ + "-c", + "mkdir -p /log && touch /log/sefaria_book_errors.log && pip install numpy && /app/run /app/scripts/reindex_elasticsearch_cronjob_ES6.py" + ] + restartPolicy: Never + volumes: + - name: local-settings + configMap: + name: local-settings-file-{{ .Values.deployEnv }} + items: + - key: local_settings.py + path: local_settings.py + successfulJobsHistoryLimit: 1 + failedJobsHistoryLimit: 2 +{{- end }} diff --git a/scripts/reindex_elasticsearch_cronjob_ES6.py b/scripts/reindex_elasticsearch_cronjob_ES6.py new file mode 100644 index 0000000000..1a3f181eb2 --- /dev/null +++ b/scripts/reindex_elasticsearch_cronjob_ES6.py @@ -0,0 +1,49 @@ +""" +This file is meant to be temporary while we are migrating to elasticsearch 8 +""" +from datetime import datetime +import requests +import traceback +import os +import django +django.setup() +from sefaria.model import * +from sefaria.search_ES6 import index_all +from sefaria.local_settings import SEFARIA_BOT_API_KEY +from sefaria.pagesheetrank import update_pagesheetrank + +""" +Source sheets added after last_sheet_timestamp will be missing from the index process. We want to manually index all +source sheets created after this. Depending on the database being used to index the timestamp will be different. If +running against a production database, last_sheet_timestamp will be the time this script began running. Otherwise, this +value will need to be set to the time at which the last mongo dump was created (assuming the database is using the most +up-to-date mongo dump). +""" +# last_sheet_timestamp = datetime.fromtimestamp(os.path.getmtime("/var/data/sefaria_public/dump/sefaria")).isoformat() +try: + last_sheet_timestamp = datetime.now().isoformat() + update_pagesheetrank() + index_all() + r = requests.post("https://www.sefaria.org/admin/index-sheets-by-timestamp", data={"timestamp": last_sheet_timestamp, "apikey": SEFARIA_BOT_API_KEY}) + if "error" in r.text: + raise Exception("Error when calling admin/index-sheets-by-timestamp API: " + r.text) + else: + print("SUCCESS!", r.text) +except Exception as e: + tb_str = traceback.format_exc() + print("Caught exception") + post_object = { + "icon_emoji": ":facepalm:", + "username": "Reindex ElasticSearch", + "channel": "#engineering-discuss", + "attachments": [ + { + "fallback": tb_str, + "color": "#a30200", + "pretext": "Cronjob Error", + "text": tb_str + } + ] + } + requests.post(os.environ['SLACK_URL'], json=post_object) + raise e diff --git a/sefaria/search_ES6.py b/sefaria/search_ES6.py new file mode 100644 index 0000000000..fb0047e3af --- /dev/null +++ b/sefaria/search_ES6.py @@ -0,0 +1,844 @@ +# -*- coding: utf-8 -*- +""" +This file is meant to be temporary while we are migrating to elasticsearch 8 + +search.py - full-text search for Sefaria using ElasticSearch + +Writes to MongoDB Collection: index_queue +""" +import os +from datetime import datetime, timedelta +import re +import bleach +import pymongo + +# To allow these files to be run directly from command line (w/o Django shell) +os.environ['DJANGO_SETTINGS_MODULE'] = "settings" + +import structlog +import logging +from logging import NullHandler +from collections import defaultdict +import time as pytime +logger = structlog.get_logger(__name__) + +from elasticsearch import Elasticsearch +from elasticsearch.client import IndicesClient +from elasticsearch.helpers import bulk +from elasticsearch.exceptions import NotFoundError +from sefaria.model import * +from sefaria.model.text import AbstractIndex, AbstractTextRecord +from sefaria.model.user_profile import user_link, public_user_data +from sefaria.model.collection import CollectionSet +from sefaria.system.database import db +from sefaria.system.exceptions import InputError +from sefaria.utils.util import strip_tags +from .settings import SEARCH_ADMIN, SEARCH_INDEX_NAME_TEXT, SEARCH_INDEX_NAME_SHEET, STATICFILES_DIRS +from sefaria.site.site_settings import SITE_SETTINGS +from sefaria.utils.hebrew import strip_cantillation +import sefaria.model.queue as qu + +es_client = Elasticsearch(SEARCH_ADMIN) +index_client = IndicesClient(es_client) + +tracer = structlog.get_logger(__name__) +tracer.setLevel(logging.CRITICAL) +#tracer.addHandler(logging.FileHandler('/tmp/es_trace.log')) +tracer.addHandler(NullHandler()) + +doc_count = 0 + + +def delete_text(oref, version, lang): + try: + curr_index = get_new_and_current_index_names('text')['current'] + + id = make_text_doc_id(oref.normal(), version, lang) + es_client.delete(index=curr_index, doc_type='text', id=id) + except Exception as e: + logger.error("ERROR deleting {} / {} / {} : {}".format(oref.normal(), version, lang, e)) + + +def delete_version(index, version, lang): + assert isinstance(index, AbstractIndex) + + refs = [] + + if SITE_SETTINGS["TORAH_SPECIFIC"]: + all_gemara_indexes = library.get_indexes_in_category("Bavli") + davidson_indexes = all_gemara_indexes[:all_gemara_indexes.index("Horayot") + 1] + if Ref(index.title).is_bavli() and index.title not in davidson_indexes: + refs += index.all_section_refs() + + refs += index.all_segment_refs() + + for ref in refs: + delete_text(ref, version, lang) + + +def delete_sheet(index_name, id): + try: + es_client.delete(index=index_name, doc_type='sheet', id=id) + except Exception as e: + logger.error("ERROR deleting sheet {}".format(id)) + + +def make_text_doc_id(ref, version, lang): + """ + Returns a doc id string for indexing based on ref, versiona and lang. + + [HACK] Since Elasticsearch chokes on non-ascii ids, hebrew titles are converted + into a number using unicode_number. This mapping should be unique, but actually isn't. + (any tips welcome) + """ + if not version.isascii(): + version = str(unicode_number(version)) + + id = "%s (%s [%s])" % (ref, version, lang) + return id + + +def unicode_number(u): + """ + Returns a number corresponding to the sum value + of each unicode character in u + """ + n = 0 + for i in range(len(u)): + n += ord(u[i]) + return n + + +def index_sheet(index_name, id): + """ + Index source sheet with 'id'. + """ + + sheet = db.sheets.find_one({"id": id}) + if not sheet: return False + + pud = public_user_data(sheet["owner"]) + tag_terms_simple = make_sheet_tags(sheet) + tags = [t["en"] for t in tag_terms_simple] + topics = [] + for t in sheet.get('topics', []): + topic_obj = Topic.init(t['slug']) + if not topic_obj: + continue + topics += [topic_obj] + collections = CollectionSet({"sheets": id, "listed": True}) + collection_names = [c.name for c in collections] + try: + doc = { + "title": strip_tags(sheet["title"]), + "content": make_sheet_text(sheet, pud), + "owner_id": sheet["owner"], + "owner_name": pud["name"], + "owner_image": pud["imageUrl"], + "profile_url": pud["profileUrl"], + "version": "Source Sheet by " + user_link(sheet["owner"]), + "tags": tags, + "topic_slugs": [topic_obj.slug for topic_obj in topics], + "topics_en": [topic_obj.get_primary_title('en') for topic_obj in topics], + "topics_he": [topic_obj.get_primary_title('he') for topic_obj in topics], + "sheetId": id, + "summary": sheet.get("summary", None), + "collections": collection_names, + "datePublished": sheet.get("datePublished", None), + "dateCreated": sheet.get("dateCreated", None), + "dateModified": sheet.get("dateModified", None), + "views": sheet.get("views", 0) + } + es_client.create(index=index_name, doc_type='sheet', id=id, body=doc) + global doc_count + doc_count += 1 + return True + except Exception as e: + print("Error indexing sheet %d" % id) + print(e) + return False + + +def make_sheet_tags(sheet): + def get_primary_title(lang, titles): + return [t for t in titles if t.get("primary") and t.get("lang", "") == lang][0]["text"] + + tags = sheet.get('tags', []) + tag_terms = [(Term().load({'name': t}) or Term().load_by_title(t)) for t in tags] + tag_terms_simple = [ + { + 'en': tags[iterm], # save as en even if it's Hebrew + 'he': '' + } if term is None else + { + 'en': get_primary_title('en', term.titles), + 'he': get_primary_title('he', term.titles) + } for iterm, term in enumerate(tag_terms) + ] + #tags_en, tags_he = zip(*tag_terms_simple.values()) + return tag_terms_simple + +def make_sheet_text(sheet, pud): + """ + Returns a plain text representation of the content of sheet. + :param sheet: The sheet record + :param pud: Public User Database record for the author + """ + text = sheet["title"] + "\n{}".format(sheet.get("summary", '')) + if pud.get("name"): + text += "\nBy: " + pud["name"] + text += "\n" + if sheet.get("tags"): + text += " [" + ", ".join(sheet["tags"]) + "]\n" + for s in sheet["sources"]: + text += source_text(s) + " " + + text = bleach.clean(text, strip=True, tags=()) + + return text + + +def source_text(source): + """ + Recursive function to translate a source dictionary into text. + """ + str_fields = ["customTitle", "ref", "comment", "outsideText"] + dict_fields = ["text", "outsideBiText"] + content = [source.get(field, "") for field in str_fields] + content += [val for field in dict_fields for val in source.get(field, {}).values()] + text = " ".join([strip_tags(c) for c in content]) + + if "subsources" in source: + for s in source["subsources"]: + text += source_text(s) + + return text + + +def get_exact_english_analyzer(): + return { + "tokenizer": "standard", + "char_filter": [ + "icu_normalizer", + ], + "filter": [ + "standard", + "lowercase", + "icu_folding", + ], + } + + +def get_stemmed_english_analyzer(): + stemmed_english_analyzer = get_exact_english_analyzer() + stemmed_english_analyzer['filter'] += ["my_snow"] + return stemmed_english_analyzer + + +def create_index(index_name, type): + """ + Clears the indexes and creates it fresh with the below settings. + """ + clear_index(index_name) + + settings = { + "index": { + "blocks": { + "read_only_allow_delete": False + }, + "analysis": { + "analyzer": { + "stemmed_english": get_stemmed_english_analyzer(), + "exact_english": get_exact_english_analyzer(), + }, + "filter": { + "my_snow": { + "type": "snowball", + "language": "English" + } + } + } + } + } + print('Creating index {}'.format(index_name)) + index_client.create(index=index_name, body=settings) + + if type == 'text': + put_text_mapping(index_name) + elif type == 'sheet': + put_sheet_mapping(index_name) + + +def put_text_mapping(index_name): + """ + Settings mapping for the text document type. + """ + text_mapping = { + 'properties' : { + 'categories': { + 'type': 'keyword', + }, + "category": { + 'type': 'keyword', + }, + "he_category": { + 'type': 'keyword', + }, + "index_title": { + 'type': 'keyword', + }, + "path": { + 'type': 'keyword', + }, + "he_index_title": { + 'type': 'keyword', + }, + "he_path": { + 'type': 'keyword', + }, + "order": { + 'type': 'keyword', + }, + "pagesheetrank": { + 'type': 'double', + 'index': False + }, + "comp_date": { + 'type': 'integer', + 'index': False + }, + "version_priority": { + 'type': 'integer', + 'index': False + }, + "exact": { + 'type': 'text', + 'analyzer': 'exact_english' + }, + "naive_lemmatizer": { + 'type': 'text', + 'analyzer': 'sefaria-naive-lemmatizer', + 'search_analyzer': 'sefaria-naive-lemmatizer-less-prefixes', + 'fields': { + 'exact': { + 'type': 'text', + 'analyzer': 'exact_english' + } + } + } + } + } + index_client.put_mapping(doc_type='text', body=text_mapping, index=index_name) + + +def put_sheet_mapping(index_name): + """ + Sets mapping for the sheets document type. + """ + sheet_mapping = { + 'properties': { + 'owner_name': { + 'type': 'keyword' + }, + 'tags': { + 'type': 'keyword' + }, + "topics_en": { + "type": "keyword" + }, + "topics_he": { + "type": "keyword" + }, + "topic_slugs": { + "type": "keyword" + }, + 'owner_image': { + 'type': 'keyword' + }, + 'datePublished': { + 'type': 'date' + }, + 'dateCreated': { + 'type': 'date' + }, + 'dateModified': { + 'type': 'date' + }, + 'sheetId': { + 'type': 'integer' + }, + 'collections': { + 'type': 'keyword' + }, + 'title': { + 'type': 'keyword' + }, + 'views': { + 'type': 'integer' + }, + 'summary': { + 'type': 'keyword' + }, + 'content': { + 'type': 'text', + 'analyzer': 'stemmed_english' + }, + 'version': { + 'type': 'keyword' + }, + 'profile_url': { + 'type': 'keyword' + }, + 'owner_id': { + 'type': 'integer' + } + } + } + index_client.put_mapping(doc_type='sheet', body=sheet_mapping, index=index_name) + +def get_search_categories(oref, categories): + toc_tree = library.get_toc_tree() + cats = oref.index.categories + + indexed_categories = categories # the default + + # get the full path of every cat along the way. + # starting w/ the longest, + # check if they're root swapped. + paths = [cats[:i] for i in range(len(cats), 0, -1)] + for path in paths: + cnode = toc_tree.lookup(path) + if getattr(cnode, "searchRoot", None) is not None: + # Use the specified searchRoot, with the rest of the category path appended. + indexed_categories = [cnode.searchRoot] + cats[len(path) - 1:] + break + return indexed_categories + + +class TextIndexer(object): + + @classmethod + def clear_cache(cls): + cls.terms_dict = None + cls.version_priority_map = None + cls._bulk_actions = None + cls.best_time_period = None + + + @classmethod + def create_terms_dict(cls): + cls.terms_dict = {} + ts = TermSet() + for t in ts: + cls.terms_dict[t.name] = t.contents() + + @classmethod + def create_version_priority_map(cls): + toc = library.get_toc() + cls.version_priority_map = {} + + def traverse(mini_toc): + if type(mini_toc) == list: + for t in mini_toc: + traverse(t) + elif "contents" in mini_toc: + for t in mini_toc["contents"]: + traverse(t) + elif "title" in mini_toc and not mini_toc.get("isCollection", False): + title = mini_toc["title"] + try: + r = Ref(title) + except InputError: + print("Failed to parse ref, {}".format(title)) + return + vlist = cls.get_ref_version_list(r) + vpriorities = defaultdict(lambda: 0) + for i, v in enumerate(vlist): + lang = v.language + cls.version_priority_map[(title, v.versionTitle, lang)] = (vpriorities[lang], mini_toc["categories"]) + vpriorities[lang] += 1 + + traverse(toc) + + @staticmethod + def get_ref_version_list(oref, tries=0): + try: + return oref.index.versionSet().array() + except InputError as e: + print(f"InputError: {oref.normal()}") + return [] + except pymongo.errors.AutoReconnect as e: + if tries < 200: + pytime.sleep(5) + return TextIndexer.get_ref_version_list(oref, tries+1) + else: + print("get_ref_version_list -- Tried: {} times. Failed :(".format(tries)) + raise e + + @classmethod + def get_all_versions(cls, tries=0, versions=None, page=0): + versions = versions or [] + try: + version_limit = 10 + temp_versions = [] + first_run = True + while first_run or len(temp_versions) > 0: + temp_versions = VersionSet(limit=version_limit, page=page).array() + versions += temp_versions + page += 1 + first_run = False + return versions + except pymongo.errors.AutoReconnect as e: + if tries < 200: + pytime.sleep(5) + return cls.get_all_versions(tries+1, versions, page) + else: + print("Tried: {} times. Got {} versions".format(tries, len(versions))) + raise e + + @classmethod + def index_all(cls, index_name, debug=False, for_es=True, action=None): + cls.index_name = index_name + cls.create_version_priority_map() + cls.create_terms_dict() + Ref.clear_cache() # try to clear Ref cache to save RAM + + versions = sorted([x for x in cls.get_all_versions() if (x.title, x.versionTitle, x.language) in cls.version_priority_map], key=lambda x: cls.version_priority_map[(x.title, x.versionTitle, x.language)][0]) + versions_by_index = {} + # organizing by index for the merged case. There is no longer a merged case but keeping this logic b/c it seems fine + for v in versions: + key = (v.title, v.language) + if key in versions_by_index: + versions_by_index[key] += [v] + else: + versions_by_index[key] = [v] + print("Beginning index of {} versions.".format(len(versions))) + vcount = 0 + total_versions = len(versions) + versions = None # release RAM + for title, vlist in list(versions_by_index.items()): + cls.curr_index = vlist[0].get_index() if len(vlist) > 0 else None + if for_es: + cls._bulk_actions = [] + try: + cls.best_time_period = cls.curr_index.best_time_period() + except ValueError: + cls.best_time_period = None + for v in vlist: + if v.versionTitle == "Yehoyesh's Yiddish Tanakh Translation [yi]": + print("skipping yiddish. we don't like yiddish") + continue + + cls.index_version(v, action=action) + print("Indexed Version {}/{}".format(vcount, total_versions)) + vcount += 1 + if for_es: + bulk(es_client, cls._bulk_actions, stats_only=True, raise_on_error=False) + + @classmethod + def index_version(cls, version, tries=0, action=None): + if not action: + action = cls._cache_action + try: + version.walk_thru_contents(action, heTref=cls.curr_index.get_title('he'), schema=cls.curr_index.schema, terms_dict=cls.terms_dict) + except pymongo.errors.AutoReconnect as e: + # Adding this because there is a mongo call for dictionary words in walk_thru_contents() + if tries < 200: + pytime.sleep(5) + print("Retrying {}. Try {}".format(version.title, tries)) + cls.index_version(version, tries+1) + else: + print("Tried {} times to get {}. I have failed you...".format(tries, version.title)) + raise e + except StopIteration: + print("Could not find dictionary node in {}".format(version.title)) + + @classmethod + def index_ref(cls, index_name, oref, version_title, lang): + # slower than `cls.index_version` but useful when you don't want the overhead of loading all versions into cache + cls.index_name = index_name + cls.curr_index = oref.index + try: + cls.best_time_period = cls.curr_index.best_time_period() + except ValueError: + cls.best_time_period = None + version_priority = 0 + hebrew_version_title = None + for priority, v in enumerate(cls.get_ref_version_list(oref)): + if v.versionTitle == version_title: + version_priority = priority + hebrew_version_title = getattr(v, 'versionTitleInHebrew', None) + content = TextChunk(oref, lang, vtitle=version_title).ja().flatten_to_string() + categories = cls.curr_index.categories + tref = oref.normal() + doc = cls.make_text_index_document(tref, oref.he_normal(), version_title, lang, version_priority, content, categories, hebrew_version_title) + id = make_text_doc_id(tref, version_title, lang) + es_client.index(index_name, doc, id=id) + + @classmethod + def _cache_action(cls, segment_str, tref, heTref, version): + # Index this document as a whole + vtitle = version.versionTitle + vlang = version.language + hebrew_version_title = getattr(version, 'versionTitleInHebrew', None) + try: + version_priority, categories = cls.version_priority_map[(version.title, vtitle, vlang)] + #TODO include sgement_str in this func + doc = cls.make_text_index_document(tref, heTref, vtitle, vlang, version_priority, segment_str, categories, hebrew_version_title) + # print doc + except Exception as e: + logger.error("Error making index document {} / {} / {} : {}".format(tref, vtitle, vlang, str(e))) + return + + if doc: + try: + cls._bulk_actions += [ + { + "_index": cls.index_name, + "_type": "text", + "_id": make_text_doc_id(tref, vtitle, vlang), + "_source": doc + } + ] + except Exception as e: + logger.error("ERROR indexing {} / {} / {} : {}".format(tref, vtitle, vlang, e)) + + @classmethod + def remove_footnotes(cls, content): + ftnotes = AbstractTextRecord.find_all_itags(content, only_footnotes=True)[1] + if len(ftnotes) == 0: + return content + else: + for sup_tag in ftnotes: + i_tag = sup_tag.next_sibling + content += f" {sup_tag.text} {i_tag.text}" + content = AbstractTextRecord.strip_itags(content) + return content + + @classmethod + def modify_text_in_doc(cls, content): + content = AbstractTextRecord.strip_imgs(content) + content = cls.remove_footnotes(content) + content = strip_cantillation(content, strip_vowels=False).strip() + content = re.sub(r'<[^>]+>', ' ', content) # replace HTML tags with space so that words dont get smushed together + content = re.sub(r'\([^)]+\)', ' ', content) # remove all parens + while " " in content: # make sure there are not many spaces in a row + content = content.replace(" ", " ") + return content + + @classmethod + def make_text_index_document(cls, tref, heTref, version, lang, version_priority, content, categories, hebrew_version_title): + """ + Create a document for indexing from the text specified by ref/version/lang + """ + # Don't bother indexing if there's no content + if not content: + return False + content = cls.modify_text_in_doc(content) + if len(content) == 0: + return False + + oref = Ref(tref) + + indexed_categories = get_search_categories(oref, categories) + + tp = cls.best_time_period + if tp is not None: + comp_start_date = int(tp.start) + else: + comp_start_date = 3000 # far in the future + + ref_data = RefData().load({"ref": tref}) + pagesheetrank = ref_data.pagesheetrank if ref_data is not None else RefData.DEFAULT_PAGESHEETRANK + + return { + "ref": tref, + "heRef": heTref, + "version": version, + "lang": lang, + "version_priority": version_priority if version_priority is not None else 1000, + "titleVariants": oref.index_node.all_tree_titles("en"), + "categories": indexed_categories, + "order": oref.order_id(), + "path": "/".join(indexed_categories + [cls.curr_index.title]), + "pagesheetrank": pagesheetrank, + "comp_date": comp_start_date, + #"hebmorph_semi_exact": content, + "exact": content, + "naive_lemmatizer": content, + 'hebrew_version_title': hebrew_version_title, + } + + +def index_sheets_by_timestamp(timestamp): + """ + :param timestamp str: index all sheets modified after `timestamp` (in isoformat) + """ + + name_dict = get_new_and_current_index_names('sheet', debug=False) + curr_index_name = name_dict['current'] + try: + ids = db.sheets.find({"status": "public", "dateModified": {"$gt": timestamp}}).distinct("id") + except Exception as e: + print(e) + return str(e) + + succeeded = [] + failed = [] + + for id in ids: + did_succeed = index_sheet(curr_index_name, id) + if did_succeed: + succeeded += [id] + else: + failed += [id] + + return {"succeeded": {"num": len(succeeded), "ids": succeeded}, "failed": {"num": len(failed), "ids": failed}} + + +def index_public_sheets(index_name): + """ + Index all source sheets that are publicly listed. + """ + ids = db.sheets.find({"status": "public"}).distinct("id") + for id in ids: + index_sheet(index_name, id) + + +def index_public_notes(): + """ + Index all public notes. + + TODO + """ + pass + + +def clear_index(index_name): + """ + Delete the search index. + """ + try: + index_client.delete(index=index_name) + except Exception as e: + print("Error deleting Elasticsearch Index named %s" % index_name) + print(e) + + +def add_ref_to_index_queue(ref, version, lang): + """ + Adds a text to index queue to be indexed later. + """ + qu.IndexQueue({ + "ref": ref, + "lang": lang, + "version": version, + "type": "ref", + }).save() + + return True + + +def index_from_queue(): + """ + Index every ref/version/lang found in the index queue. + Delete queue records on success. + """ + index_name = get_new_and_current_index_names('text')['current'] + queue = db.index_queue.find() + for item in queue: + try: + TextIndexer.index_ref(index_name, Ref(item["ref"]), item["version"], item["lang"], False) + db.index_queue.remove(item) + except Exception as e: + logging.error("Error indexing from queue ({} / {} / {}) : {}".format(item["ref"], item["version"], item["lang"], e)) + + +def add_recent_to_queue(ndays): + """ + Look through the last ndays of the activitiy log, + add to the index queue any refs that had their text altered. + """ + cutoff = datetime.now() - timedelta(days=ndays) + query = { + "date": {"$gt": cutoff}, + "rev_type": {"$in": ["add text", "edit text"]} + } + activity = db.history.find(query) + refs = set() + for a in activity: + refs.add((a["ref"], a["version"], a["language"])) + for ref in list(refs): + add_ref_to_index_queue(ref[0], ref[1], ref[2]) + + +def get_new_and_current_index_names(type, debug=False): + base_index_name_dict = { + 'text': SEARCH_INDEX_NAME_TEXT, + 'sheet': SEARCH_INDEX_NAME_SHEET, + } + index_name_a = "{}-a{}".format(base_index_name_dict[type], '-debug' if debug else '') + index_name_b = "{}-b{}".format(base_index_name_dict[type], '-debug' if debug else '') + alias_name = "{}{}".format(base_index_name_dict[type], '-debug' if debug else '') + aliases = index_client.get_alias() + try: + a_alias = aliases[index_name_a]['aliases'] + choose_a = alias_name not in a_alias + except KeyError: + choose_a = True + + if choose_a: + new_index_name = index_name_a + old_index_name = index_name_b + else: + new_index_name = index_name_b + old_index_name = index_name_a + return {"new": new_index_name, "current": old_index_name, "alias": alias_name} + + +def index_all(skip=0, debug=False): + """ + Fully create the search index from scratch. + """ + start = datetime.now() + index_all_of_type('text', skip=skip, debug=debug) + index_all_of_type('sheet', skip=skip, debug=debug) + end = datetime.now() + db.index_queue.delete_many({}) # index queue is now stale + print("Elapsed time: %s" % str(end-start)) + + +def index_all_of_type(type, skip=0, debug=False): + index_names_dict = get_new_and_current_index_names(type=type, debug=debug) + print('CREATING / DELETING {}'.format(index_names_dict['new'])) + print('CURRENT {}'.format(index_names_dict['current'])) + for i in range(10): + print('STARTING IN T-MINUS {}'.format(10 - i)) + pytime.sleep(1) + + index_all_of_type_by_index_name(type, index_names_dict['new'], skip, debug) + + try: + #index_client.put_settings(index=index_names_dict['current'], body={"index": { "blocks": { "read_only_allow_delete": False }}}) + index_client.delete_alias(index=index_names_dict['current'], name=index_names_dict['alias']) + print("Successfully deleted alias {} for index {}".format(index_names_dict['alias'], index_names_dict['current'])) + except NotFoundError: + print("Failed to delete alias {} for index {}".format(index_names_dict['alias'], index_names_dict['current'])) + + clear_index(index_names_dict['alias']) # make sure there are no indexes with the alias_name + + #index_client.put_settings(index=index_names_dict['new'], body={"index": { "blocks": { "read_only_allow_delete": False }}}) + index_client.put_alias(index=index_names_dict['new'], name=index_names_dict['alias']) + + if index_names_dict['new'] != index_names_dict['current']: + clear_index(index_names_dict['current']) + + +def index_all_of_type_by_index_name(type, index_name, skip=0, debug=False): + if skip == 0: + create_index(index_name, type) + if type == 'text': + TextIndexer.clear_cache() + TextIndexer.index_all(index_name, debug=debug) + elif type == 'sheet': + index_public_sheets(index_name) \ No newline at end of file From 63a47d3684af7cb94cda315a868148aa117e3066 Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Wed, 20 Sep 2023 16:33:05 +0300 Subject: [PATCH 274/756] feat(Topic Images): Part one of mobile render --- static/js/TopicPage.jsx | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/static/js/TopicPage.jsx b/static/js/TopicPage.jsx index 71527e2153..d0e62ac83c 100644 --- a/static/js/TopicPage.jsx +++ b/static/js/TopicPage.jsx @@ -523,7 +523,8 @@ const TopicPage = ({ tref={topicData.ref} timePeriod={topicData.timePeriod} properties={topicData.properties} - topicTitle={topicTitle}/> + topicTitle={topicTitle} + multiPanel={multiPanel} /> : null } <Promotions adType="sidebar"/> </div> @@ -592,7 +593,7 @@ TopicLink.propTypes = { }; -const TopicSideColumn = ({ slug, links, clearAndSetTopic, parashaData, tref, setNavTopic, timePeriod, properties, topicTitle }) => { +const TopicSideColumn = ({ slug, links, clearAndSetTopic, parashaData, tref, setNavTopic, timePeriod, properties, topicTitle, multiPanel }) => { const category = Sefaria.topicTocCategory(slug); const linkTypeArray = links ? Object.values(links).filter(linkType => !!linkType && linkType.shouldDisplay && linkType.links.filter(l => l.shouldDisplay !== false).length > 0) : []; if (linkTypeArray.length === 0) { @@ -612,7 +613,7 @@ const TopicSideColumn = ({ slug, links, clearAndSetTopic, parashaData, tref, set const readingsComponent = hasReadings ? ( <ReadingsComponent parashaData={parashaData} tref={tref} /> ) : null; - const topicMetaData = <TopicMetaData timePeriod={timePeriod} properties={properties} topicTitle={topicTitle}/>; + const topicMetaData = <TopicMetaData timePeriod={timePeriod} properties={properties} topicTitle={topicTitle} multiPanel={multiPanel}/>; const linksComponent = ( links ? linkTypeArray.sort((a, b) => { @@ -698,8 +699,8 @@ const TopicSideSection = ({ title, children, hasMore }) => { } const TopicImage = ({photoLink, enCaption, heCaption }) => { - - return( + + return ( <div class="topicImageWrapper"> {/** Todo Break out the classes. add CSS for linebreak via parent */} <img class="topicImagePicture" src={photoLink}/> @@ -766,7 +767,7 @@ const propKeys = [ ]; -const TopicMetaData = ({ topicTitle, timePeriod, properties={} }) => { +const TopicMetaData = ({ topicTitle, timePeriod, multiPanel, properties={} }) => { const topicImageKey = topicTitle.en; const hardcodedTopicImagesMap = { 'Rosh Hashanah': {'photoLink':'https://museums.cjh.org/web/objects/common/webmedia.php?irn=11469&reftable=ecatalogue&refirn=6640', @@ -833,7 +834,8 @@ const TopicMetaData = ({ topicTitle, timePeriod, properties={} }) => { ) : null; return ( <> - {topicImageKey in hardcodedTopicImagesMap && <TopicImage photoLink={hardcodedTopicImagesMap[topicImageKey].photoLink} enCaption={hardcodedTopicImagesMap[topicImageKey].enCaption} heCaption={hardcodedTopicImagesMap[topicImageKey].heCaption}/>} + {/** Raise this up a level, add a ?: for null */} + {topicImageKey in hardcodedTopicImagesMap && multiPanel && <TopicImage photoLink={hardcodedTopicImagesMap[topicImageKey].photoLink} enCaption={hardcodedTopicImagesMap[topicImageKey].enCaption} heCaption={hardcodedTopicImagesMap[topicImageKey].heCaption}/>} { tpSection } { propsSection } </> From 907242d191cf4a6a44143154946ab75bfc307f1c Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Wed, 20 Sep 2023 16:58:32 +0300 Subject: [PATCH 275/756] chore(Topic Images): Refactor conditonal render --- static/js/TopicPage.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/static/js/TopicPage.jsx b/static/js/TopicPage.jsx index 0ea6f66360..f2ec6c4415 100644 --- a/static/js/TopicPage.jsx +++ b/static/js/TopicPage.jsx @@ -840,10 +840,10 @@ const TopicMetaData = ({ topicTitle, timePeriod, multiPanel, properties={} }) => } </TopicSideSection> ) : null; + const tpSidebarImg = topicImageKey in hardcodedTopicImagesMap && multiPanel ? <TopicImage photoLink={hardcodedTopicImagesMap[topicImageKey].photoLink} enCaption={hardcodedTopicImagesMap[topicImageKey].enCaption} heCaption={hardcodedTopicImagesMap[topicImageKey].heCaption}/> : null; return ( <> - {/** Raise this up a level, add a ?: for null */} - {topicImageKey in hardcodedTopicImagesMap && multiPanel && <TopicImage photoLink={hardcodedTopicImagesMap[topicImageKey].photoLink} enCaption={hardcodedTopicImagesMap[topicImageKey].enCaption} heCaption={hardcodedTopicImagesMap[topicImageKey].heCaption}/>} + {tpSidebarImg} { tpSection } { propsSection } </> From 9c88bc7a2da0283a81380b1a811d81b23b7a6f5f Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Wed, 20 Sep 2023 22:42:29 +0300 Subject: [PATCH 276/756] chore(Topic Images): Part 2 of images on mobile, css clean up --- static/css/s2.css | 3 -- static/js/TopicPage.jsx | 65 ++++++++++++++++++++++------------------- 2 files changed, 35 insertions(+), 33 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index 3be4f81afe..295e33262f 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -2304,9 +2304,6 @@ div.interfaceLinks-row a { padding-right: 44px; padding-bottom: 30px; } -.topicImageCaptionLinebreak { - white-space: pre-line; -} .readerPanel .translationsPage h2 { margin: 40px 0 0 0; font-size: 24px; diff --git a/static/js/TopicPage.jsx b/static/js/TopicPage.jsx index f2ec6c4415..1d3269692f 100644 --- a/static/js/TopicPage.jsx +++ b/static/js/TopicPage.jsx @@ -187,6 +187,34 @@ const sheetRenderWrapper = (toggleSignUpModal) => item => ( ); +const hardcodedTopicImagesMap = { + 'Rosh Hashanah': {'photoLink':'https://museums.cjh.org/web/objects/common/webmedia.php?irn=11469&reftable=ecatalogue&refirn=6640', + 'enCaption':'Rosh Hashanah, Arthur Szyk (1894-1951) Tempera and ink on paper. New Canaan, 1948. Collection of Yeshiva University Museum. Gift of Charles Frost', + 'heCaption': 'ראש השנה, ארתור שיק, ארה״ב 1948. אוסף ישיבה יוניברסיטי'}, + + 'Yom Kippur': {'photoLink':'https://www.bl.uk/IllImages/BLCD/big/K900/K90075-77.jpg', + 'enCaption':'Micrography of Jonah being swallowed by the fish. Germany, 1300-1500, The British Library', + 'heCaption': 'מיקורגפיה של יונה בבטן הדג, מתוך ספר יונה ההפטרה של יום כיפור, 1300-1500'}, + + 'The Four Species': {'photoLink':'https://res.cloudinary.com/the-jewish-museum/image/fetch/q_auto,f_auto/v1/https%3A%2F%2Fthejm.netx.net%2Ffile%2Fasset%2F34234%2Fview%2F52568%2Fview_52568%3Ftoken%3D5d5cdc57-6399-40b5-afb0-93139921700e', + 'enCaption':'Etrog container, K B, late 19th century, Germany. The Jewish Museum, Gift of Dr. Harry G. Friedman', + 'heCaption': 'תיבת אתרוג, סוף המאה ה19, גרמניה. מתנת הארי ג. פרידמן '}, + + 'Sukkot': {'photoLink':'https://www.bl.uk/IllImages/BLCD/big/d400/d40054-17a.jpg', + 'enCaption':'Detail of a painting of a sukkah Image taken from f. 316v of Forli Siddur. 1383, Italian rite. The British Library', + 'heCaption': 'פרט ציור של סוכה עם שולחן פרוס ושלוש דמויות. דימוי מתוך סידור פורלי, 1383 איטליה'}, + + 'Simchat Torah': {'photoLink':'https://upload.wikimedia.org/wikipedia/commons/4/4d/Rosh_Hashanah_greeting_card_%287974345646%29.jpg?20150712114334', + 'enCaption':'Rosh Hashanah postcard: Hakafot, Haim Yisroel Goldberg (1888-1943) Publisher: Williamsburg Post Card Co. Germany, ca. 1915 Collection of Yeshiva University Museum', + 'heCaption': 'גלויה לראש השנה: הקפות, חיים גולדברג, גרמניה 1915, אוסף ישיבה יוניברסיטי'}, + + 'Shabbat': {'photoLink':'https://res.cloudinary.com/the-jewish-museum/image/fetch/q_auto,f_auto/v1/https%3A%2F%2Fthejm.netx.net%2Ffile%2Fasset%2F35064%2Fview%2F61838%2Fview_61838%3Ftoken%3D5d5cdc57-6399-40b5-afb0-93139921700e', + 'enCaption':'Friday Evening, Isidor Kaufmann, Austria c. 1920. The Jewish Museum, Gift of Mr. and Mrs. M. R. Schweitzer', + 'heCaption': 'שישי בערב, איזידור קאופמן, וינה 1920. מתנת מר וגברת מ.ר. שוייצר'}, + +}; + + /* *** Components */ @@ -311,12 +339,15 @@ const TopicSponsorship = ({topic_slug}) => { ); } -const TopicHeader = ({ topic, topicData, multiPanel, isCat, setNavTopic, openDisplaySettings, openSearch }) => { +const TopicHeader = ({ topic, topicData, topicTitle, multiPanel, isCat, setNavTopic, openDisplaySettings, openSearch }) => { const { en, he } = !!topicData && topicData.primaryTitle ? topicData.primaryTitle : {en: "Loading...", he: "טוען..."}; const isTransliteration = !!topicData ? topicData.primaryTitleIsTransliteration : {en: false, he: false}; const category = !!topicData ? Sefaria.topicTocCategory(topicData.slug) : null; + const topicImageKey = topicTitle.en; + const tpTopImg = topicImageKey in hardcodedTopicImagesMap && !multiPanel ? <TopicImage photoLink={hardcodedTopicImagesMap[topicImageKey].photoLink} enCaption={hardcodedTopicImagesMap[topicImageKey].enCaption} heCaption={hardcodedTopicImagesMap[topicImageKey].heCaption}/> : null; return ( <div> + {tpTopImg} <div className="navTitle tight"> <CategoryHeader type="topics" data={topicData} buttonsToDisplay={["source", "edit", "reorder"]}> <h1> @@ -470,7 +501,7 @@ const TopicPage = ({ <div className="content noOverflowX" ref={scrollableElement}> <div className="columnLayout"> <div className="mainColumn storyFeedInner"> - <TopicHeader topic={topic} topicData={topicData} multiPanel={multiPanel} setNavTopic={setNavTopic} openSearch={openSearch} openDisplaySettings={openDisplaySettings} /> + <TopicHeader topic={topic} topicData={topicData} topicTitle={topicTitle} multiPanel={multiPanel} setNavTopic={setNavTopic} openSearch={openSearch} openDisplaySettings={openDisplaySettings} /> {(!topicData.isLoading && displayTabs.length) ? <TabView currTabName={tab} @@ -712,7 +743,7 @@ const TopicImage = ({photoLink, enCaption, heCaption }) => { <div class="topicImageWrapper"> {/** Todo Break out the classes. add CSS for linebreak via parent */} <img class="topicImagePicture" src={photoLink}/> - <div class="topicImageCaption topicImageCaptionLinebreak"> + <div class="topicImageCaption"> <InterfaceText text={{en:enCaption, he:heCaption}} /> </div> </div>); @@ -776,33 +807,6 @@ const propKeys = [ const TopicMetaData = ({ topicTitle, timePeriod, multiPanel, properties={} }) => { - const topicImageKey = topicTitle.en; - const hardcodedTopicImagesMap = { - 'Rosh Hashanah': {'photoLink':'https://museums.cjh.org/web/objects/common/webmedia.php?irn=11469&reftable=ecatalogue&refirn=6640', - 'enCaption':'Rosh Hashanah, Arthur Szyk (1894-1951) Tempera and ink on paper. New Canaan, 1948. Collection of Yeshiva University Museum. Gift of Charles Frost', - 'heCaption': 'ראש השנה, ארתור שיק, ארה״ב 1948. אוסף ישיבה יוניברסיטי'}, - - 'Yom Kippur': {'photoLink':'https://www.bl.uk/IllImages/BLCD/big/K900/K90075-77.jpg', - 'enCaption':'Micrography of Jonah being swallowed by the fish. Germany, 1300-1500, The British Library', - 'heCaption': 'מיקורגפיה של יונה בבטן הדג, מתוך ספר יונה ההפטרה של יום כיפור, 1300-1500'}, - - 'The Four Species': {'photoLink':'https://res.cloudinary.com/the-jewish-museum/image/fetch/q_auto,f_auto/v1/https%3A%2F%2Fthejm.netx.net%2Ffile%2Fasset%2F34234%2Fview%2F52568%2Fview_52568%3Ftoken%3D5d5cdc57-6399-40b5-afb0-93139921700e', - 'enCaption':'Etrog container, K B, late 19th century, Germany. The Jewish Museum, Gift of Dr. Harry G. Friedman', - 'heCaption': 'תיבת אתרוג, סוף המאה ה19, גרמניה. מתנת הארי ג. פרידמן '}, - - 'Sukkot': {'photoLink':'https://www.bl.uk/IllImages/BLCD/big/d400/d40054-17a.jpg', - 'enCaption':'Detail of a painting of a sukkah Image taken from f. 316v of Forli Siddur. 1383, Italian rite. The British Library', - 'heCaption': 'פרט ציור של סוכה עם שולחן פרוס ושלוש דמויות. דימוי מתוך סידור פורלי, 1383 איטליה'}, - - 'Simchat Torah': {'photoLink':'https://upload.wikimedia.org/wikipedia/commons/4/4d/Rosh_Hashanah_greeting_card_%287974345646%29.jpg?20150712114334', - 'enCaption':'Rosh Hashanah postcard: Hakafot, Haim Yisroel Goldberg (1888-1943) Publisher: Williamsburg Post Card Co. Germany, ca. 1915 Collection of Yeshiva University Museum', - 'heCaption': 'גלויה לראש השנה: הקפות, חיים גולדברג, גרמניה 1915, אוסף ישיבה יוניברסיטי'}, - - 'Shabbat': {'photoLink':'https://res.cloudinary.com/the-jewish-museum/image/fetch/q_auto,f_auto/v1/https%3A%2F%2Fthejm.netx.net%2Ffile%2Fasset%2F35064%2Fview%2F61838%2Fview_61838%3Ftoken%3D5d5cdc57-6399-40b5-afb0-93139921700e', - 'enCaption':'Friday Evening, Isidor Kaufmann, Austria c. 1920. The Jewish Museum, Gift of Mr. and Mrs. M. R. Schweitzer', - 'heCaption': 'שישי בערב, איזידור קאופמן, וינה 1920. מתנת מר וגברת מ.ר. שוייצר'}, - - }; const tpSection = !!timePeriod ? ( <TopicSideSection title={{en: "Lived", he: "תקופת פעילות"}}> <div className="systemText topicMetaData"><InterfaceText text={timePeriod.name} /></div> @@ -840,6 +844,7 @@ const TopicMetaData = ({ topicTitle, timePeriod, multiPanel, properties={} }) => } </TopicSideSection> ) : null; + const topicImageKey = topicTitle.en; const tpSidebarImg = topicImageKey in hardcodedTopicImagesMap && multiPanel ? <TopicImage photoLink={hardcodedTopicImagesMap[topicImageKey].photoLink} enCaption={hardcodedTopicImagesMap[topicImageKey].enCaption} heCaption={hardcodedTopicImagesMap[topicImageKey].heCaption}/> : null; return ( <> From 8078b9c3e7369206122db8dab9cd76990f529fc3 Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Thu, 21 Sep 2023 09:30:24 +0300 Subject: [PATCH 277/756] chore(Topic Images): Update Hebrew captions --- static/js/TopicPage.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/static/js/TopicPage.jsx b/static/js/TopicPage.jsx index 1d3269692f..25fe3fb8b2 100644 --- a/static/js/TopicPage.jsx +++ b/static/js/TopicPage.jsx @@ -198,7 +198,7 @@ const hardcodedTopicImagesMap = { 'The Four Species': {'photoLink':'https://res.cloudinary.com/the-jewish-museum/image/fetch/q_auto,f_auto/v1/https%3A%2F%2Fthejm.netx.net%2Ffile%2Fasset%2F34234%2Fview%2F52568%2Fview_52568%3Ftoken%3D5d5cdc57-6399-40b5-afb0-93139921700e', 'enCaption':'Etrog container, K B, late 19th century, Germany. The Jewish Museum, Gift of Dr. Harry G. Friedman', - 'heCaption': 'תיבת אתרוג, סוף המאה ה19, גרמניה. מתנת הארי ג. פרידמן '}, + 'heCaption': 'תיבת אתרוג, סוף המאה ה19, גרמניה. המוזיאון היהודי בניו יורק, מתנת דר. הארי ג. פרידמן '}, 'Sukkot': {'photoLink':'https://www.bl.uk/IllImages/BLCD/big/d400/d40054-17a.jpg', 'enCaption':'Detail of a painting of a sukkah Image taken from f. 316v of Forli Siddur. 1383, Italian rite. The British Library', @@ -210,7 +210,7 @@ const hardcodedTopicImagesMap = { 'Shabbat': {'photoLink':'https://res.cloudinary.com/the-jewish-museum/image/fetch/q_auto,f_auto/v1/https%3A%2F%2Fthejm.netx.net%2Ffile%2Fasset%2F35064%2Fview%2F61838%2Fview_61838%3Ftoken%3D5d5cdc57-6399-40b5-afb0-93139921700e', 'enCaption':'Friday Evening, Isidor Kaufmann, Austria c. 1920. The Jewish Museum, Gift of Mr. and Mrs. M. R. Schweitzer', - 'heCaption': 'שישי בערב, איזידור קאופמן, וינה 1920. מתנת מר וגברת מ.ר. שוייצר'}, + 'heCaption': 'שישי בערב, איזידור קאופמן, וינה 1920. המוזיאון היהודי בניו יורק, מתנת מר וגברת מ.ר. שוויצר'}, }; From b8931f68dda6914bb6f903cae6da3acc4a2af686 Mon Sep 17 00:00:00 2001 From: Lev Israel <eliezer.israel@gmail.com> Date: Thu, 21 Sep 2023 14:24:08 +0300 Subject: [PATCH 278/756] feat: Remove version notes from translation selection sidebar. Keep them in about-this-text and book page. --- static/js/TranslationsBox.jsx | 1 + static/js/VersionBlock.jsx | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/static/js/TranslationsBox.jsx b/static/js/TranslationsBox.jsx index c5e1a78bf6..9e0c84382b 100644 --- a/static/js/TranslationsBox.jsx +++ b/static/js/TranslationsBox.jsx @@ -89,6 +89,7 @@ class TranslationsBox extends Component { openVersionInSidebar={this.openVersionInSidebar} viewExtendedNotes={this.props.viewExtendedNotes} inTranslationBox={true} + showNotes={false} /> </> ); diff --git a/static/js/VersionBlock.jsx b/static/js/VersionBlock.jsx index b00655b980..40219735a3 100644 --- a/static/js/VersionBlock.jsx +++ b/static/js/VersionBlock.jsx @@ -171,6 +171,9 @@ class VersionBlock extends Component { } } makeVersionNotes(){ + if (!this.props.showNotes) { + return null; + } if(Sefaria.interfaceLang=="english" && !!this.props.version.versionNotes){ return this.props.version.versionNotes; }else if(Sefaria.interfaceLang=="hebrew" && !!this.props.version.versionNotesInHebrew){ @@ -459,6 +462,7 @@ class VersionsBlocksList extends Component{ viewExtendedNotes={this.props.viewExtendedNotes} isCurrent={this.isVersionCurrent(v)} inTranslationBox={this.props.inTranslationBox} + showNotes={this.props.showNotes} /> )) } @@ -480,6 +484,7 @@ VersionsBlocksList.propTypes={ viewExtendedNotes: PropTypes.func, showLanguageHeaders: PropTypes.bool, inTranslationBox: PropTypes.bool, + showNotes: PropTypes.bool, }; VersionsBlocksList.defaultProps = { displayCurrentVersions: true, From 7bb6f7532c4c1382e337cf6903b9d6d0c21171dd Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Thu, 21 Sep 2023 14:34:46 +0300 Subject: [PATCH 279/756] chore(Topic Images): Increase font size to 12px --- static/css/s2.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/css/s2.css b/static/css/s2.css index 295e33262f..185421c976 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -2292,7 +2292,7 @@ div.interfaceLinks-row a { font-family: Roboto; } .topicImageCaption { - font-size: 11px; + font-size: 12px; font-weight: 400; line-height: 15px; letter-spacing: 0em; From 63f37a198fd16b18c9dcfec813eda2f96cf6a80c Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Thu, 21 Sep 2023 15:22:45 +0300 Subject: [PATCH 280/756] chore(Topic Image): Addressing polish notes --- static/css/s2.css | 26 +++++++++++++++++++++++++- static/js/TopicPage.jsx | 13 ++++++++----- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index 185421c976..cee3d13920 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -2302,7 +2302,31 @@ div.interfaceLinks-row a { .topicImageWrapper{ padding-left: 44px; padding-right: 44px; - padding-bottom: 30px; +} +@media (max-width: 600px) { + .topicImagePicture { + max-height: 60vh; + height: auto; + max-width: 100%; + } + .topicImageWrapper{ + padding-left: 0; + padding-right: 0; + } + .topicImageCaptionWrapper{ + display: flex; + justify-content: center; + } + .topicImageCaption { + font-size: 12px; + font-weight: 400; + line-height: 15px; + letter-spacing: 0em; + color: var(--dark-grey); + width: 80vw; + margin-bottom: 30px; + text-align: center; + } } .readerPanel .translationsPage h2 { margin: 40px 0 0 0; diff --git a/static/js/TopicPage.jsx b/static/js/TopicPage.jsx index 25fe3fb8b2..f53265170b 100644 --- a/static/js/TopicPage.jsx +++ b/static/js/TopicPage.jsx @@ -201,7 +201,7 @@ const hardcodedTopicImagesMap = { 'heCaption': 'תיבת אתרוג, סוף המאה ה19, גרמניה. המוזיאון היהודי בניו יורק, מתנת דר. הארי ג. פרידמן '}, 'Sukkot': {'photoLink':'https://www.bl.uk/IllImages/BLCD/big/d400/d40054-17a.jpg', - 'enCaption':'Detail of a painting of a sukkah Image taken from f. 316v of Forli Siddur. 1383, Italian rite. The British Library', + 'enCaption':'Detail of a painting of a sukkah. Image taken from f. 316v of Forli Siddur. 1383, Italian rite. The British Library', 'heCaption': 'פרט ציור של סוכה עם שולחן פרוס ושלוש דמויות. דימוי מתוך סידור פורלי, 1383 איטליה'}, 'Simchat Torah': {'photoLink':'https://upload.wikimedia.org/wikipedia/commons/4/4d/Rosh_Hashanah_greeting_card_%287974345646%29.jpg?20150712114334', @@ -347,7 +347,7 @@ const TopicHeader = ({ topic, topicData, topicTitle, multiPanel, isCat, setNavTo const tpTopImg = topicImageKey in hardcodedTopicImagesMap && !multiPanel ? <TopicImage photoLink={hardcodedTopicImagesMap[topicImageKey].photoLink} enCaption={hardcodedTopicImagesMap[topicImageKey].enCaption} heCaption={hardcodedTopicImagesMap[topicImageKey].heCaption}/> : null; return ( <div> - {tpTopImg} + <div className="navTitle tight"> <CategoryHeader type="topics" data={topicData} buttonsToDisplay={["source", "edit", "reorder"]}> <h1> @@ -371,6 +371,7 @@ const TopicHeader = ({ topic, topicData, topicTitle, multiPanel, isCat, setNavTo <InterfaceText markdown={{en: topicData.description.en, he: topicData.description.he}}/> </div> : null} + {tpTopImg} {topicData && topicData.ref ? <a href={`/${topicData.ref.url}`} className="resourcesLink button blue"> <img src="/static/icons/book-icon-black.svg" alt="Book Icon" /> @@ -743,9 +744,11 @@ const TopicImage = ({photoLink, enCaption, heCaption }) => { <div class="topicImageWrapper"> {/** Todo Break out the classes. add CSS for linebreak via parent */} <img class="topicImagePicture" src={photoLink}/> - <div class="topicImageCaption"> - <InterfaceText text={{en:enCaption, he:heCaption}} /> - </div> + <div class="topicImageCaptionWrapper"> + <div class="topicImageCaption"> + <InterfaceText text={{en:enCaption, he:heCaption}} /> + </div> + </div> </div>); } From 0b05b897ac05f09ceb1245a4aa2cd373314fb045 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Thu, 21 Sep 2023 20:29:29 +0300 Subject: [PATCH 281/756] fix: open connections in panel on mobile --- static/js/ReaderPanel.jsx | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/static/js/ReaderPanel.jsx b/static/js/ReaderPanel.jsx index 18cb939cce..7d82430038 100644 --- a/static/js/ReaderPanel.jsx +++ b/static/js/ReaderPanel.jsx @@ -182,6 +182,17 @@ class ReaderPanel extends Component { if (Sefaria.util.object_equals(this.state.currVersions, newVersions)) { return; } this.conditionalSetState({ currVersions: newVersions }); } + openConnectionsPanel(ref, additionalState) { + /** + * Decides whether to open a new connections panel or to open connections in the current panel + * depending on whether we're in multi-panel mode + */ + if (this.props.multiPanel) { + this.props.openConnectionsPanel(ref, null, additionalState); + } else { + this.openConnectionsInPanel(ref, additionalState); + } + } openConnectionsInPanel(ref, additionalState) { let refs = typeof ref == "string" ? [ref] : ref; this.replaceHistory = this.state.mode === "TextAndConnections"; // Don't push history for change in Connections focus @@ -1106,7 +1117,7 @@ class ReaderPanel extends Component { openDisplaySettings={this.openDisplaySettings} currentLayout={this.currentLayout} onError={this.onError} - openConnectionsPanel={this.props.openConnectionsPanel} + openConnectionsPanel={this.openConnectionsPanel} connectionsMode={this.state.filter.length && this.state.connectionsMode === "Connections" ? "Connection Text" : this.state.connectionsMode} connectionsCategory={this.state.connectionsCategory} closePanel={this.props.closePanel} @@ -1255,7 +1266,7 @@ class ReaderControls extends Component { }); } openTranslations() { - this.props.openConnectionsPanel([this.props.currentRef], null, {"connectionsMode": "Translations"}); + this.props.openConnectionsPanel([this.props.currentRef], {"connectionsMode": "Translations"}); } componentDidMount() { const title = this.props.currentRef; From d1178402f7ad526fc37b2d8f698cb1d2dda547db Mon Sep 17 00:00:00 2001 From: yonadavGit <yonadav.le@gmail.com> Date: Tue, 26 Sep 2023 14:57:27 +0300 Subject: [PATCH 282/756] feat: portal model --- sefaria/model/abstract.py | 68 +++++ sefaria/model/portal.py | 135 ++++++++++ sefaria/model/tests/portal_test.py | 409 +++++++++++++++++++++++++++++ 3 files changed, 612 insertions(+) create mode 100644 sefaria/model/portal.py create mode 100644 sefaria/model/tests/portal_test.py diff --git a/sefaria/model/abstract.py b/sefaria/model/abstract.py index ad2d0ddc66..723baf1ace 100644 --- a/sefaria/model/abstract.py +++ b/sefaria/model/abstract.py @@ -637,3 +637,71 @@ def foo(obj, **kwargs): rec.save() return foo + +class SchemaValidationException(Exception): + def __init__(self, key, expected_type): + self.key = key + self.expected_type = expected_type + self.message = f"Invalid value for key '{key}'. Expected type: {expected_type}" + super().__init__(self.message) +class SchemaRequiredFieldException(Exception): + def __init__(self, key): + self.key = key + self.message = f"Required field '{key}' is missing." + super().__init__(self.message) + +class SchemaInvalidKeyException(Exception): + def __init__(self, key): + self.key = key + self.message = f"Invalid key '{key}' found in data dictionary." + super().__init__(self.message) + + +def validate_dictionary(data, schema): + """ + Validates that a given dictionary complies with the provided schema. + + Args: + data (dict): The dictionary to be validated. + schema (dict): The schema dictionary specifying the expected structure. + + Raises: + SchemaValidationException: If the data does not comply with the schema. + + Returns: + bool: True if the data complies with the schema, False otherwise. + """ + + for key, value_type in schema.items(): + if not (isinstance(value_type, tuple) and len(value_type) == 2 and value_type[1] in ["optional", "required"]): + raise ValueError(f"Invalid schema definition for key '{key}'. Use ('type', 'optional') or ('type', 'required').") + + # Check for keys in data that are not in schema + for key in data.keys(): + if key not in schema: + raise SchemaInvalidKeyException(key) + + for key, value_type in schema.items(): + # Check if the key exists in the data dictionary + if key not in data: + # Check if the field is optional (not required) + if isinstance(value_type, tuple) and len(value_type) == 2 and value_type[1] == "optional": + continue # Field is optional, so skip validation + else: + raise SchemaRequiredFieldException(key) + + # Check if the expected type is a nested dictionary + if isinstance(value_type[0], dict): + nested_data = data[key] + nested_schema = value_type[0] + try: + # Recursively validate the nested dictionary + validate_dictionary(nested_data, nested_schema) + except SchemaValidationException as e: + # If validation fails for the nested dictionary, re-raise the exception with the key + raise SchemaValidationException(f"{key}.{e.key}", e.expected_type) + + # Check the type of the value in the data dictionary + elif not isinstance(data[key], value_type[0]): + raise SchemaValidationException(key, value_type[0]) + return True diff --git a/sefaria/model/portal.py b/sefaria/model/portal.py new file mode 100644 index 0000000000..ba39e96d45 --- /dev/null +++ b/sefaria/model/portal.py @@ -0,0 +1,135 @@ +# coding=utf-8 +from urllib.parse import urlparse +from . import abstract as abst +import urllib.parse +import structlog +logger = structlog.get_logger(__name__) + +def get_nested_value(data, key): + """ + Get the value of a key in a dictionary or nested dictionaries. + + Args: + data (dict): The dictionary to search. + key (str): The key to retrieve the value for. + + Returns: + The value associated with the key, or None if the key is not found. + """ + if key in data: + return data[key] + + for value in data.values(): + if isinstance(value, dict): + nested_value = get_nested_value(value, key) + if nested_value is not None: + return nested_value + + return None +class InvalidURLException(Exception): + def __init__(self, url): + self.url = url + self.message = f"'{url}' is not a valid URL." + super().__init__(self.message) + +def validate_url(url): + try: + # Attempt to parse the URL + result = urllib.parse.urlparse(url) + + # Check if the scheme (e.g., http, https) and netloc (e.g., domain) are present + if result.scheme and result.netloc: + return True + else: + raise InvalidURLException(url) + except ValueError: + # URL parsing failed + raise InvalidURLException(url) +class InvalidHTTPMethodException(Exception): + def __init__(self, method): + self.method = method + self.message = f"'{method}' is not a valid HTTP API method." + super().__init__(self.message) + +def validate_http_method(method): + """ + Validate if a string represents a valid HTTP API method. + + Args: + method (str): The HTTP method to validate. + + Raises: + InvalidHTTPMethodException: If the method is not valid. + + Returns: + bool: True if the method is valid, False otherwise. + """ + valid_methods = ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"] + + # Convert the method to uppercase and check if it's in the list of valid methods + if method.upper() in valid_methods: + return True + else: + raise InvalidHTTPMethodException(method) + +class Portal(abst.AbstractMongoRecord): + collection = 'portals' + + required_attrs = [ + "about", + ] + optional_attrs = [ + "mobile", + 'api_schema', + ] + + def _validate(self): + super(Portal, self)._validate() + + about_schema = { + "title": ({"en": (str, "required"), "he": (str, "required")}, "required"), + "title_url": (str, "optional"), + "image_uri": (str, "optional"), + "description": ({"en": (str, "required"), "he": (str, "required")}, "optional"), + } + + mobile_schema = { + "title": ({"en": (str, "required"), "he": (str, "required")}, "required"), + "android_link": (str, "optional"), + "ios_link": (str, "optional") + } + + newsletter_schema = { + "title": ({"en": (str, "required"), "he": (str, "required")}, "required"), + "title_url": (str, "optional"), + "description": ({"en": (str, "required"), "he": (str, "required")}, "optional"), + "api_schema": ({"http_method": (str, "required"), + "payload": ({"first_name_key": (str, "optional"), "last_name_key": (str, "optional"), "email_key": (str, "optional")}, "optional")} + , "optional") + } + + if hasattr(self, "about"): + abst.validate_dictionary(self.about, about_schema) + title_url = get_nested_value(self.about, "title_url") + if title_url: + validate_url(title_url) + if hasattr(self, "mobile"): + abst.validate_dictionary(self.mobile, mobile_schema) + android_link = get_nested_value(self.mobile,"android_link") + if android_link: + validate_url(android_link) + ios_link = get_nested_value(self.mobile, "ios_link") + if ios_link: + validate_url(ios_link) + if hasattr(self, "newsletter"): + abst.validate_dictionary(self.newsletter, newsletter_schema) + http_method = get_nested_value(self.newsletter, "http_method") + if http_method: + validate_http_method(http_method) + return True + + + + + + diff --git a/sefaria/model/tests/portal_test.py b/sefaria/model/tests/portal_test.py new file mode 100644 index 0000000000..4f7209363a --- /dev/null +++ b/sefaria/model/tests/portal_test.py @@ -0,0 +1,409 @@ +import pytest +from sefaria.model.portal import Portal # Replace with your actual validation function + +valids = [ + { + "about": { + "title": { + "en": "English Title", + "he": "Hebrew Title" + }, + "title_url": "https://example.com", + "image_uri": "gs://your-bucket/image.jpg", + "description": { + "en": "English Description", + "he": "Hebrew Description" + } + }, + "mobile": { + "title": { + "en": "Mobile Title", + "he": "Mobile Hebrew Title" + }, + "android_link": "https://android-link.com", + "ios_link": "https://ios-link.com" + }, + "newsletter": { + "title": { + "en": "Newsletter Title", + "he": "Newsletter Hebrew Title" + }, + "title_url": "https://newsletter-url.com", + "description": { + "en": "Newsletter English Description", + "he": "Newsletter Hebrew Description" + }, + "api_schema": { + "http_method": "POST", + "payload": { + "first_name_key": "fname", + "last_name_key": "lname", + "email_key": "email" + } + } + } + }, + { + "about": { + "title": { + "en": "English Title", + "he": "Hebrew Title" + }, + "description": { + "en": "English Description", + "he": "Hebrew Description" + } + }, + "mobile": { + "title": { + "en": "Mobile Title", + "he": "Mobile Hebrew Title" + } + }, + "newsletter": { + "title": { + "en": "Newsletter Title", + "he": "Newsletter Hebrew Title" + }, + "api_schema": { + "http_method": "GET" + } + } + }, +{ + "about": { + "title": { + "en": "English Title", + "he": "Hebrew Title" + }, + "title_url": "https://example.com", + "image_uri": "gs://your-bucket/image.jpg", + "description": { + "en": "English Description", + "he": "Hebrew Description" + } + }, + "mobile": { + "title": { + "en": "Mobile Title", + "he": "Mobile Hebrew Title" + }, + "android_link": "https://android-link.com", + "ios_link": "https://ios-link.com" + }, + "newsletter": { + "title": { + "en": "Newsletter Title", + "he": "Newsletter Hebrew Title" + }, + "title_url": "https://newsletter-url.com", + "description": { + "en": "Newsletter English Description", + "he": "Newsletter Hebrew Description" + }, + "api_schema": { + "http_method": "POST", + "payload": { + "first_name_key": "fname", + "last_name_key": "lname", + "email_key": "email" + } + } + } + }, + { + "about": { + "title": { + "en": "English Title", + "he": "Hebrew Title" + } + }, + "mobile": { + "title": { + "en": "Mobile Title", + "he": "Mobile Hebrew Title" + } + }, + "newsletter": { + "title": { + "en": "Newsletter Title", + "he": "Newsletter Hebrew Title" + }, + "api_schema": { + "http_method": "GET" + } + } + }, + { + "about": { + "title": { + "en": "English Title", + "he": "Hebrew Title" + } + }, + "mobile": { + "title": { + "en": "Mobile Title", + "he": "Mobile Hebrew Title" + }, + "android_link": "https://android-link.com" + }, + "newsletter": { + "title": { + "en": "Newsletter Title", + "he": "Newsletter Hebrew Title" + } + } + }, + { + "about": { + "title": { + "en": "English Title", + "he": "Hebrew Title" + }, + "title_url": "https://example.com", + "image_uri": "gs://your-bucket/image.jpg", + "description": { + "en": "English Description", + "he": "Hebrew Description" + } + }, + "mobile": { + "title": { + "en": "Mobile Title", + "he": "Mobile Hebrew Title" + }, + "android_link": "https://android-link.com", + "ios_link": "https://ios-link.com" + }, + "newsletter": { + "title": { + "en": "Newsletter Title", + "he": "Newsletter Hebrew Title" + }, + "title_url": "https://newsletter-url.com", + "description": { + "en": "Newsletter English Description", + "he": "Newsletter Hebrew Description" + }, + "api_schema": { + "http_method": "POST", + "payload": { + "first_name_key": "fname", + "last_name_key": "lname", + "email_key": "email" + } + } + } + }, + { + "about": { + "title": { + "en": "English Title", + "he": "Hebrew Title" + } + }, + "mobile": { + "title": { + "en": "Mobile Title", + "he": "Mobile Hebrew Title" + } + }, + "newsletter": { + "title": { + "en": "Newsletter Title", + "he": "Newsletter Hebrew Title" + }, + "api_schema": { + "http_method": "GET" + } + } + }, + { + "about": { + "title": { + "en": "English Title", + "he": "Hebrew Title" + }, + "image_uri": "gs://your-bucket/image.jpg" + }, + "mobile": { + "title": { + "en": "Mobile Title", + "he": "Mobile Hebrew Title" + } + }, + "newsletter": { + "title": { + "en": "Newsletter Title", + "he": "Newsletter Hebrew Title" + }, + "api_schema": { + "http_method": "POST", + "payload": { + "first_name_key": "fname", + "last_name_key": "lname", + "email_key": "email" + } + } + } + } +] + +invalids = [ + # Missing "about" key + { + "mobile": { + "title": { + "en": "Mobile Title", + "he": "Mobile Hebrew Title" + }, + "android_link": "https://android-link.com", + "ios_link": "https://ios-link.com" + }, + "newsletter": { + "title": { + "en": "Newsletter Title", + "he": "Newsletter Hebrew Title" + }, + "api_schema": { + "http_method": "POST", + "payload": { + "first_name_key": "fname", + "last_name_key": "lname", + "email_key": "email" + } + } + } + }, + # Invalid "about.title_url" (not a URL) + { + "about": { + "title": { + "en": "English Title", + "he": "Hebrew Title" + }, + "title_url": "invalid-url", + "image_uri": "gs://your-bucket/image.jpg", + "description": { + "en": "English Description", + "he": "Hebrew Description" + } + }, + "mobile": { + "title": { + "en": "Mobile Title", + "he": "Mobile Hebrew Title" + }, + "android_link": "https://android-link.com", + "ios_link": "https://ios-link.com" + }, + "newsletter": { + "title": { + "en": "Newsletter Title", + "he": "Newsletter Hebrew Title" + }, + "title_url": "https://newsletter-url.com", + "description": { + "en": "Newsletter English Description", + "he": "Newsletter Hebrew Description" + }, + "api_schema": { + "http_method": "POST", + "payload": { + "first_name_key": "fname", + "last_name_key": "lname", + "email_key": "email" + } + } + } + }, + # Missing required "mobile.ios_link" + { + "about": { + "title": { + "en": "English Title", + "he": "Hebrew Title" + }, + "title_url": "https://example.com", + "image_uri": "gs://your-bucket/image.jpg", + "description": { + "en": "English Description", + "he": "Hebrew Description" + } + }, + "mobile": { + "title": { + "en": "Mobile Title", + "he": "Mobile Hebrew Title" + }, + "android_link": "https://android-link.com" + }, + "newsletter": { + "title": { + "en": "Newsletter Title", + "he": "Newsletter Hebrew Title" + }, + "title_url": "https://newsletter-url.com", + "description": { + "en": "Newsletter English Description", + "he": "Newsletter Hebrew Description" + }, + "api_schema": { + "http_method": "POST", + "payload": { + "first_name_key": "fname", + "last_name_key": "lname", + "email_key": "email" + } + } + } + }, + # Invalid "newsletter.api_schema.http_method" (not a valid HTTP method) + { + "about": { + "title": { + "en": "English Title", + "he": "Hebrew Title" + }, + "title_url": "https://example.com", + "image_uri": "gs://your-bucket/image.jpg", + "description": { + "en": "English Description", + "he": "Hebrew Description" + } + }, + "mobile": { + "title": { + "en": "Mobile Title", + "he": "Mobile Hebrew Title" + }, + "android_link": "https://android-link.com", + "ios_link": "https://ios-link.com" + }, + "newsletter": { + "title": { + "en": "Newsletter Title", + "he": "Newsletter Hebrew Title" + }, + "title_url": "https://newsletter-url.com", + "description": { + "en": "Newsletter English Description", + "he": "Newsletter Hebrew Description" + }, + "api_schema": { + "http_method": "INVALID_METHOD", + "payload": { + "first_name_key": "fname", + "last_name_key": "lname", + "email_key": "email" + } + } + } + } +] +def test_valid_schema(): + for case in valids: + p = Portal(case) + assert p._validate() == True + From 7532b474921accb154d1d7a57ecbe609986824f8 Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Tue, 26 Sep 2023 15:14:07 +0300 Subject: [PATCH 283/756] chore(Topic Images): Add 30 px margin above image --- static/css/s2.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/static/css/s2.css b/static/css/s2.css index cee3d13920..606625cc9d 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -2307,11 +2307,12 @@ div.interfaceLinks-row a { .topicImagePicture { max-height: 60vh; height: auto; - max-width: 100%; + max-width: 100%; } .topicImageWrapper{ padding-left: 0; padding-right: 0; + margin-top: 30px; } .topicImageCaptionWrapper{ display: flex; From 38d863fdc5b0af5ec9d4d05d8d5cd1c371d107e3 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Tue, 26 Sep 2023 15:51:04 +0300 Subject: [PATCH 284/756] feat(Portal): added slugs to Portal class --- sefaria/model/portal.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sefaria/model/portal.py b/sefaria/model/portal.py index ba39e96d45..640fbb80c0 100644 --- a/sefaria/model/portal.py +++ b/sefaria/model/portal.py @@ -72,8 +72,9 @@ def validate_http_method(method): else: raise InvalidHTTPMethodException(method) -class Portal(abst.AbstractMongoRecord): +class Portal(abst.SluggedAbstractMongoRecord): collection = 'portals' + slug_fields = ['slug'] required_attrs = [ "about", From be72245d283ac47b0707f6dfc343d2c6da456e9b Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Tue, 26 Sep 2023 15:52:35 +0300 Subject: [PATCH 285/756] feat(Portal): add portal_slug to topic model --- sefaria/model/topic.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sefaria/model/topic.py b/sefaria/model/topic.py index 0127bc723b..bf4c08bddf 100644 --- a/sefaria/model/topic.py +++ b/sefaria/model/topic.py @@ -43,7 +43,8 @@ class Topic(abst.SluggedAbstractMongoRecord, AbstractTitledObject): 'good_to_promote', 'description_published', # bool to keep track of which descriptions we've vetted 'isAmbiguous', # True if topic primary title can refer to multiple other topics - "data_source" #any topic edited manually should display automatically in the TOC and this flag ensures this + "data_source", #any topic edited manually should display automatically in the TOC and this flag ensures this + "portal_slug", # slug to relevant Portal object ] ROOT = "Main Menu" # the root of topic TOC is not a topic, so this is a fake slug. we know it's fake because it's not in normal form # this constant is helpful in the topic editor tool functions in this file From 263ded7257b002b282ed6408057107615b3eb23c Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Tue, 26 Sep 2023 16:00:02 +0300 Subject: [PATCH 286/756] chore(Portal): add Portal to __init__.py --- sefaria/model/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sefaria/model/__init__.py b/sefaria/model/__init__.py index b5acc4cc97..83661bc1cb 100644 --- a/sefaria/model/__init__.py +++ b/sefaria/model/__init__.py @@ -41,6 +41,7 @@ from .webpage import WebPage, WebPageSet from .media import Media, MediaSet from .topic import Topic, PersonTopic, AuthorTopic, TopicLinkType, IntraTopicLink, RefTopicLink, TopicLinkType, TopicDataSource, TopicSet, PersonTopicSet, AuthorTopicSet, TopicLinkTypeSet, RefTopicLinkSet, IntraTopicLinkSet, TopicLinkSetHelper +from .portal import Portal from .manuscript import Manuscript, ManuscriptSet, ManuscriptPage, ManuscriptPageSet from .linker.ref_part import RawRef from .linker.ref_resolver import RefResolver From 9b29dcfb2400b052e92530701cbedefe32bb539a Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Tue, 26 Sep 2023 16:35:17 +0300 Subject: [PATCH 287/756] chore(Topic Images): Fix image height --- static/css/s2.css | 18 +++++++++--------- static/js/TopicPage.jsx | 13 +++++-------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index 606625cc9d..32316b9768 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -2277,7 +2277,7 @@ div.interfaceLinks-row a { font-family: "Taamey Frank", "adobe-garamond-pro", "Crimson Text", Georgia, "Times New Roman", serif; margin-bottom: -3px; } -.topicImagePicture{ +.topicPhoto{ border: 1px solid #EDEDEC; max-width: 100%; height: auto; @@ -2299,24 +2299,24 @@ div.interfaceLinks-row a { color: var(--dark-grey); width: 100%; } -.topicImageWrapper{ +.topicImage{ padding-left: 44px; padding-right: 44px; } @media (max-width: 600px) { - .topicImagePicture { - max-height: 60vh; + .topicPhoto{ height: auto; - max-width: 100%; + max-width: calc(66.67vw); + max-height: calc(66.67vw); } - .topicImageWrapper{ + .topicImage{ padding-left: 0; padding-right: 0; - margin-top: 30px; - } - .topicImageCaptionWrapper{ + margin-top: 30px; display: flex; + flex-direction: column; justify-content: center; + align-items: center; } .topicImageCaption { font-size: 12px; diff --git a/static/js/TopicPage.jsx b/static/js/TopicPage.jsx index f53265170b..d4606854d0 100644 --- a/static/js/TopicPage.jsx +++ b/static/js/TopicPage.jsx @@ -741,14 +741,11 @@ const TopicSideSection = ({ title, children, hasMore }) => { const TopicImage = ({photoLink, enCaption, heCaption }) => { return ( - <div class="topicImageWrapper"> - {/** Todo Break out the classes. add CSS for linebreak via parent */} - <img class="topicImagePicture" src={photoLink}/> - <div class="topicImageCaptionWrapper"> - <div class="topicImageCaption"> - <InterfaceText text={{en:enCaption, he:heCaption}} /> - </div> - </div> + <div class="topicImage"> + <img class="topicPhoto" src={photoLink}/> + <div class="topicImageCaption"> + <InterfaceText text={{en:enCaption, he:heCaption}} /> + </div> </div>); } From 778418bf7eef494540dc50c8413f1c2f221b965d Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Tue, 26 Sep 2023 16:38:08 +0300 Subject: [PATCH 288/756] refactor: add validate_slug_exists method to clean up duplicated code --- sefaria/model/abstract.py | 8 +++++++- sefaria/model/topic.py | 26 ++++++++++++-------------- sefaria/system/exceptions.py | 4 ++++ 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/sefaria/model/abstract.py b/sefaria/model/abstract.py index 723baf1ace..2a1723bc17 100644 --- a/sefaria/model/abstract.py +++ b/sefaria/model/abstract.py @@ -15,7 +15,7 @@ from bson.objectid import ObjectId from sefaria.system.database import db -from sefaria.system.exceptions import InputError +from sefaria.system.exceptions import InputError, SluggedMongoRecordMissingError logger = structlog.get_logger(__name__) @@ -427,6 +427,12 @@ def _normalize(self): for slug_field in self.slug_fields: setattr(self, slug_field, self.normalize_slug_field(slug_field)) + @classmethod + def validate_slug_exists(cls, slug): + object = cls.init(slug) + if not object: + raise SluggedMongoRecordMissingError(f"{cls.__name__} with slug '{slug}' does not exist.") + class Cloneable: diff --git a/sefaria/model/topic.py b/sefaria/model/topic.py index bf4c08bddf..b0dd452877 100644 --- a/sefaria/model/topic.py +++ b/sefaria/model/topic.py @@ -660,14 +660,10 @@ def _validate(self): super(IntraTopicLink, self)._validate() # check everything exists - link_type = TopicLinkType().load({"slug": self.linkType}) - assert link_type is not None, "Link type '{}' does not exist".format(self.linkType) - from_topic = Topic.init(self.fromTopic) - assert from_topic is not None, "fromTopic '{}' does not exist".format(self.fromTopic) - to_topic = Topic.init(self.toTopic) - assert to_topic is not None, "toTopic '{}' does not exist".format(self.toTopic) - data_source = TopicDataSource().load({"slug": self.dataSource}) - assert data_source is not None, "dataSource '{}' does not exist".format(self.dataSource) + TopicLinkType.validate_slug_exists(self.linkType) + Topic.validate_slug_exists(self.fromTopic) + Topic.validate_slug_exists(self.toTopic) + TopicDataSource.validate_slug_exists(self.dataSource) # check for duplicates duplicate = IntraTopicLink().load({"linkType": self.linkType, "fromTopic": self.fromTopic, "toTopic": self.toTopic, @@ -677,6 +673,7 @@ def _validate(self): "Duplicate intra topic link for linkType '{}', fromTopic '{}', toTopic '{}'".format( self.linkType, self.fromTopic, self.toTopic)) + link_type = TopicLinkType.init(self.linkType) if link_type.slug == link_type.inverseSlug: duplicate_inverse = IntraTopicLink().load({"linkType": self.linkType, "toTopic": self.fromTopic, "fromTopic": self.toTopic, "class": getattr(self, 'class'), "_id": {"$ne": getattr(self, "_id", None)}}) @@ -686,6 +683,8 @@ def _validate(self): duplicate_inverse.linkType, duplicate_inverse.fromTopic, duplicate_inverse.toTopic)) # check types of topics are valid according to validFrom/To + from_topic = Topic.init(self.fromTopic) + to_topic = Topic.init(self.toTopic) if getattr(link_type, 'validFrom', False): assert from_topic.has_types(set(link_type.validFrom)), "from topic '{}' does not have valid types '{}' for link type '{}'. Instead, types are '{}'".format(self.fromTopic, ', '.join(link_type.validFrom), self.linkType, ', '.join(from_topic.get_types())) if getattr(link_type, 'validTo', False): @@ -781,10 +780,10 @@ def _normalize(self): self.expandedRefs = [r.normal() for r in Ref(self.ref).all_segment_refs()] def _validate(self): + Topic.validate_slug_exists(self.toTopic) + TopicLinkType.validate_slug_exists(self.linkType) to_topic = Topic.init(self.toTopic) - assert to_topic is not None, "toTopic '{}' does not exist".format(self.toTopic) - link_type = TopicLinkType().load({"slug": self.linkType}) - assert link_type is not None, "Link type '{}' does not exist".format(self.linkType) + link_type = TopicLinkType.init(self.linkType) if getattr(link_type, 'validTo', False): assert to_topic.has_types(set(link_type.validTo)), "to topic '{}' does not have valid types '{}' for link type '{}'. Instead, types are '{}'".format(self.toTopic, ', '.join(link_type.validTo), self.linkType, ', '.join(to_topic.get_types())) @@ -872,11 +871,10 @@ def _validate(self): # Check that validFrom and validTo contain valid topic slugs if exist for validToTopic in getattr(self, 'validTo', []): - assert Topic.init(validToTopic) is not None, "ValidTo topic '{}' does not exist".format(self.validToTopic) + Topic.validate_slug_exists(validToTopic) for validFromTopic in getattr(self, 'validFrom', []): - assert Topic.init(validFromTopic) is not None, "ValidTo topic '{}' does not exist".format( - self.validFrom) + Topic.validate_slug_exists(validFromTopic) def get(self, attr, is_inverse, default=None): attr = 'inverse{}{}'.format(attr[0].upper(), attr[1:]) if is_inverse else attr diff --git a/sefaria/system/exceptions.py b/sefaria/system/exceptions.py index ff64c89336..297a7e9b79 100644 --- a/sefaria/system/exceptions.py +++ b/sefaria/system/exceptions.py @@ -58,3 +58,7 @@ class ManuscriptError(Exception): class MissingKeyError(Exception): pass + + +class SluggedMongoRecordMissingError(Exception): + pass From a91758019302daacb91e0f7dc2822637676ff695 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Tue, 26 Sep 2023 16:39:14 +0300 Subject: [PATCH 289/756] chore(Portal): validate portal exists on topic model --- sefaria/model/topic.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sefaria/model/topic.py b/sefaria/model/topic.py index b0dd452877..235fc77a20 100644 --- a/sefaria/model/topic.py +++ b/sefaria/model/topic.py @@ -5,6 +5,7 @@ from .category import Category from sefaria.system.exceptions import InputError, DuplicateRecordError from sefaria.model.timeperiod import TimePeriod +from sefaria.model.portal import Portal from sefaria.system.database import db import structlog, bleach from sefaria.model.place import Place @@ -70,6 +71,8 @@ def _validate(self): if getattr(self, 'subclass', False): assert self.subclass in self.subclass_map, f"Field `subclass` set to {self.subclass} which is not one of the valid subclass keys in `Topic.subclass_map`. Valid keys are {', '.join(self.subclass_map.keys())}" + Portal.validate_slug_exists(self.portal_slug) + def _normalize(self): super()._normalize() for title in self.title_group.titles: From b6ed46ff24b9cb88759b2f0ba6cebc1b5c6e419d Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Tue, 26 Sep 2023 17:34:20 +0300 Subject: [PATCH 290/756] chore(Topic Images): Add 10px padding between image and caption on mobile --- static/css/s2.css | 1 + 1 file changed, 1 insertion(+) diff --git a/static/css/s2.css b/static/css/s2.css index 32316b9768..c9ee39e798 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -2308,6 +2308,7 @@ div.interfaceLinks-row a { height: auto; max-width: calc(66.67vw); max-height: calc(66.67vw); + margin-bottom: 10px; } .topicImage{ padding-left: 0; From a4606be80a9f864077f3c6b8fba18cac21b9e139 Mon Sep 17 00:00:00 2001 From: yonadavGit <yonadav.le@gmail.com> Date: Tue, 26 Sep 2023 17:54:44 +0300 Subject: [PATCH 291/756] refactor(portal): Remove recursive key function and handle URL validation using Django --- sefaria/model/portal.py | 46 +++++++++++++---------------------------- 1 file changed, 14 insertions(+), 32 deletions(-) diff --git a/sefaria/model/portal.py b/sefaria/model/portal.py index 640fbb80c0..b76493522e 100644 --- a/sefaria/model/portal.py +++ b/sefaria/model/portal.py @@ -2,30 +2,12 @@ from urllib.parse import urlparse from . import abstract as abst import urllib.parse +from django.core.validators import URLValidator +from django.core.exceptions import ValidationError import structlog logger = structlog.get_logger(__name__) -def get_nested_value(data, key): - """ - Get the value of a key in a dictionary or nested dictionaries. - - Args: - data (dict): The dictionary to search. - key (str): The key to retrieve the value for. - - Returns: - The value associated with the key, or None if the key is not found. - """ - if key in data: - return data[key] - for value in data.values(): - if isinstance(value, dict): - nested_value = get_nested_value(value, key) - if nested_value is not None: - return nested_value - - return None class InvalidURLException(Exception): def __init__(self, url): self.url = url @@ -35,14 +17,11 @@ def __init__(self, url): def validate_url(url): try: # Attempt to parse the URL - result = urllib.parse.urlparse(url) - - # Check if the scheme (e.g., http, https) and netloc (e.g., domain) are present - if result.scheme and result.netloc: - return True - else: - raise InvalidURLException(url) - except ValueError: + validator = URLValidator() + validator(url) + return True + + except ValidationError: # URL parsing failed raise InvalidURLException(url) class InvalidHTTPMethodException(Exception): @@ -111,20 +90,23 @@ def _validate(self): if hasattr(self, "about"): abst.validate_dictionary(self.about, about_schema) - title_url = get_nested_value(self.about, "title_url") + title_url = self.about.get("title_url") if title_url: validate_url(title_url) if hasattr(self, "mobile"): abst.validate_dictionary(self.mobile, mobile_schema) - android_link = get_nested_value(self.mobile,"android_link") + android_link = self.mobile.get("android_link") if android_link: validate_url(android_link) - ios_link = get_nested_value(self.mobile, "ios_link") + ios_link = self.mobile.get("ios_link") if ios_link: validate_url(ios_link) if hasattr(self, "newsletter"): abst.validate_dictionary(self.newsletter, newsletter_schema) - http_method = get_nested_value(self.newsletter, "http_method") + title_url = self.newsletter.get("title_url") + if title_url: + validate_url(title_url) + http_method = self.newsletter.get("api_schema", {}).get("http_method") if http_method: validate_http_method(http_method) return True From 346d446ac00b1bbfa6ac3f6f2a44d5894daae0c0 Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Tue, 26 Sep 2023 21:19:29 +0300 Subject: [PATCH 292/756] chore(Topic Images): fix typos --- static/js/TopicPage.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/static/js/TopicPage.jsx b/static/js/TopicPage.jsx index d4606854d0..a5bfb8df6f 100644 --- a/static/js/TopicPage.jsx +++ b/static/js/TopicPage.jsx @@ -194,7 +194,7 @@ const hardcodedTopicImagesMap = { 'Yom Kippur': {'photoLink':'https://www.bl.uk/IllImages/BLCD/big/K900/K90075-77.jpg', 'enCaption':'Micrography of Jonah being swallowed by the fish. Germany, 1300-1500, The British Library', - 'heCaption': 'מיקורגפיה של יונה בבטן הדג, מתוך ספר יונה ההפטרה של יום כיפור, 1300-1500'}, + 'heCaption': 'מיקרוגרפיה של יונה בבטן הדג, מתוך ספר יונה ההפטרה של יום כיפור, 1300-1500'}, 'The Four Species': {'photoLink':'https://res.cloudinary.com/the-jewish-museum/image/fetch/q_auto,f_auto/v1/https%3A%2F%2Fthejm.netx.net%2Ffile%2Fasset%2F34234%2Fview%2F52568%2Fview_52568%3Ftoken%3D5d5cdc57-6399-40b5-afb0-93139921700e', 'enCaption':'Etrog container, K B, late 19th century, Germany. The Jewish Museum, Gift of Dr. Harry G. Friedman', @@ -202,7 +202,7 @@ const hardcodedTopicImagesMap = { 'Sukkot': {'photoLink':'https://www.bl.uk/IllImages/BLCD/big/d400/d40054-17a.jpg', 'enCaption':'Detail of a painting of a sukkah. Image taken from f. 316v of Forli Siddur. 1383, Italian rite. The British Library', - 'heCaption': 'פרט ציור של סוכה עם שולחן פרוס ושלוש דמויות. דימוי מתוך סידור פורלי, 1383 איטליה'}, + 'heCaption': 'פרט ציור של סוכה עם שולחן פרוש ושלוש דמויות. דימוי מתוך סידור פורלי, 1383 איטליה'}, 'Simchat Torah': {'photoLink':'https://upload.wikimedia.org/wikipedia/commons/4/4d/Rosh_Hashanah_greeting_card_%287974345646%29.jpg?20150712114334', 'enCaption':'Rosh Hashanah postcard: Hakafot, Haim Yisroel Goldberg (1888-1943) Publisher: Williamsburg Post Card Co. Germany, ca. 1915 Collection of Yeshiva University Museum', From 8f9e35d5d920147c1b70058906c6c3093239d3f9 Mon Sep 17 00:00:00 2001 From: yonadavGit <yonadav.le@gmail.com> Date: Wed, 27 Sep 2023 10:06:29 +0300 Subject: [PATCH 293/756] Tests(portal): Added parametrized pytests for valid and invalid cases testing basic schema constraints --- sefaria/model/tests/portal_test.py | 65 +++++++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 6 deletions(-) diff --git a/sefaria/model/tests/portal_test.py b/sefaria/model/tests/portal_test.py index 4f7209363a..7b29f6e16b 100644 --- a/sefaria/model/tests/portal_test.py +++ b/sefaria/model/tests/portal_test.py @@ -247,6 +247,14 @@ } } } + }, + { + "about": { + "title": { + "en": "About Us", + "he": "עלינו" + } + } } ] @@ -318,7 +326,7 @@ } } }, - # Missing required "mobile.ios_link" + # Including invalid field "newsletter.description.fr" { "about": { "title": { @@ -341,7 +349,7 @@ }, "newsletter": { "title": { - "en": "Newsletter Title", + "fr": "Titre de la newsletter", "he": "Newsletter Hebrew Title" }, "title_url": "https://newsletter-url.com", @@ -400,10 +408,55 @@ } } } + }, + # Invalid data types: + { + "about": { + "title": { + "en": "About Us", + "he": "עלינו" + }, + "title_url": 12345, + "image_uri": 67890, + "description": { + "en": "Description in English", + "he": "תיאור בעברית" + } + } + }, +{ + # Incorrect field names + "about": { + "title": { + "en": "About Us", + "he": "עלינו" + }, + "image_uri": "gs://bucket/image.jpg", + "description": { + "en": "Description in English", + "he": "תיאור בעברית" + } + }, + "mobile": { + "title": { + "en": "Mobile App", + "he": "אפליקציה ניידת" + }, + "android_link": "https://play.google.com/store/apps/details?id=com.example.app", + "ios_link": "https://apps.apple.com/us/app/example-app/id1234567890", + "invalid_field": "This field should not be here" } +} + + ] -def test_valid_schema(): - for case in valids: - p = Portal(case) - assert p._validate() == True +@pytest.mark.parametrize("data", valids) +def test_valid_schema(data): + p = Portal(data) + assert p._validate() == True +@pytest.mark.parametrize("invalid_case", invalids) +def test_invalid_schema(invalid_case): + with pytest.raises(Exception): + p = Portal(invalid_case) + p._validate() From 631ef5113151041cabae9e23a8053a09ef7684e6 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 27 Sep 2023 14:00:48 +0300 Subject: [PATCH 294/756] fix(Portal): add slug to required attributes of Portal --- sefaria/model/portal.py | 1 + sefaria/model/tests/portal_test.py | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/sefaria/model/portal.py b/sefaria/model/portal.py index b76493522e..b6a2fa89b9 100644 --- a/sefaria/model/portal.py +++ b/sefaria/model/portal.py @@ -56,6 +56,7 @@ class Portal(abst.SluggedAbstractMongoRecord): slug_fields = ['slug'] required_attrs = [ + "slug", "about", ] optional_attrs = [ diff --git a/sefaria/model/tests/portal_test.py b/sefaria/model/tests/portal_test.py index 7b29f6e16b..47493911a8 100644 --- a/sefaria/model/tests/portal_test.py +++ b/sefaria/model/tests/portal_test.py @@ -3,6 +3,7 @@ valids = [ { + "slug": "English Title", "about": { "title": { "en": "English Title", @@ -44,6 +45,7 @@ } }, { + "slug": "English Title", "about": { "title": { "en": "English Title", @@ -71,6 +73,7 @@ } }, { + "slug": "English Title", "about": { "title": { "en": "English Title", @@ -112,6 +115,7 @@ } }, { + "slug": "English Title", "about": { "title": { "en": "English Title", @@ -135,6 +139,7 @@ } }, { + "slug": "English Title", "about": { "title": { "en": "English Title", @@ -156,6 +161,7 @@ } }, { + "slug": "English Title", "about": { "title": { "en": "English Title", @@ -197,6 +203,7 @@ } }, { + "slug": "English Title", "about": { "title": { "en": "English Title", @@ -220,6 +227,7 @@ } }, { + "slug": "English Title", "about": { "title": { "en": "English Title", @@ -249,6 +257,7 @@ } }, { + "slug": "English Title", "about": { "title": { "en": "About Us", @@ -261,6 +270,7 @@ invalids = [ # Missing "about" key { + "slug": "English Title", "mobile": { "title": { "en": "Mobile Title", @@ -286,6 +296,7 @@ }, # Invalid "about.title_url" (not a URL) { + "slug": "English Title", "about": { "title": { "en": "English Title", @@ -328,6 +339,7 @@ }, # Including invalid field "newsletter.description.fr" { + "slug": "English Title", "about": { "title": { "en": "English Title", @@ -369,6 +381,7 @@ }, # Invalid "newsletter.api_schema.http_method" (not a valid HTTP method) { + "slug": "English Title", "about": { "title": { "en": "English Title", @@ -411,6 +424,7 @@ }, # Invalid data types: { + "slug": "English Title", "about": { "title": { "en": "About Us", @@ -426,6 +440,7 @@ }, { # Incorrect field names + "slug": "English Title", "about": { "title": { "en": "About Us", From 479ea931351922047914670f8bc86f2eebf1ebf5 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 27 Sep 2023 14:20:44 +0300 Subject: [PATCH 295/756] test(Portal): add tests for portal creation and topic validation of portals. --- sefaria/model/tests/portal_test.py | 49 ++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/sefaria/model/tests/portal_test.py b/sefaria/model/tests/portal_test.py index 47493911a8..bece99fe96 100644 --- a/sefaria/model/tests/portal_test.py +++ b/sefaria/model/tests/portal_test.py @@ -1,5 +1,7 @@ import pytest from sefaria.model.portal import Portal # Replace with your actual validation function +from sefaria.model.topic import Topic +from sefaria.system.exceptions import SluggedMongoRecordMissingError valids = [ { @@ -475,3 +477,50 @@ def test_invalid_schema(invalid_case): with pytest.raises(Exception): p = Portal(invalid_case) p._validate() + + +@pytest.fixture() +def simple_portal(): + raw_portal = valids[0] + portal = Portal(raw_portal) + portal.save() + + yield portal + + portal.delete() + + +@pytest.fixture() +def simple_topic(simple_portal): + topic = Topic({ + "slug": "blah", + "titles": [{"text": "Blah", "lang": "en", "primary": True}], + "portal_slug": simple_portal.slug, + }) + topic.save() + + yield topic + + topic.delete() + + +def test_save_simple_portal(simple_portal): + """ + Tests that simple_portal was saved properly and has a normalized slug + """ + assert simple_portal.slug == "english-title" + + +def test_topic_validates_portal_exists(simple_topic): + assert simple_topic is not None + + +def test_topic_validation_fails_for_non_existent_portal(): + with pytest.raises(SluggedMongoRecordMissingError): + topic = Topic({ + "slug": "blah", + "titles": [{"text": "Blah", "lang": "en", "primary": True}], + "portal_slug": "non-existent-portal", + }) + topic.save() + From 7682f5e7542957a3a350ae01b476fd971b3ec0dc Mon Sep 17 00:00:00 2001 From: Ephraim <ephraim@sefaria.org> Date: Wed, 27 Sep 2023 15:19:26 +0200 Subject: [PATCH 296/756] fix(database): Check for db name existence in the db server before anything --- reader/views.py | 10 +++++++++- sefaria/system/database.py | 21 ++++++++++++++++----- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/reader/views.py b/reader/views.py index 86f6843254..c862905548 100644 --- a/reader/views.py +++ b/reader/views.py @@ -4543,11 +4543,19 @@ def isNodeJsReachable(): except Exception as e: logger.warn(f"Failed node healthcheck. Error: {e}") return False + + def is_database_reachable(): + try: + from sefaria.system.database import db + return True + except SystemError as ivne: + return False - allReady = isRedisReachable() and isMultiserverReachable() and isNodeJsReachable() + allReady = isRedisReachable() and isMultiserverReachable() and isNodeJsReachable() and is_database_reachable() resp = { 'allReady': allReady, + 'dbConnected': f'Database Connection: {is_database_reachable()}', 'multiserverReady': isMultiserverReachable(), 'redisReady': isRedisReachable(), 'nodejsReady': isNodeJsReachable(), diff --git a/sefaria/system/database.py b/sefaria/system/database.py index 45879f0823..ff0f07b66e 100644 --- a/sefaria/system/database.py +++ b/sefaria/system/database.py @@ -9,6 +9,20 @@ from sefaria.settings import * +def check_db_exists(db_name): + dbnames = client.list_database_names() + return db_name in dbnames + + +def connect_to_db(db_name): + if not check_db_exists(db_name): + raise SystemError(f'Database {db_name} does not exist!') + return client[db_name] + +def get_test_db(): + return client[TEST_DB] + + if hasattr(sys, '_doc_build'): db = "" else: @@ -37,13 +51,10 @@ # Now set the db variable to point to the Sefaria database in the server if not hasattr(sys, '_called_from_test'): - db = client[SEFARIA_DB] + db = connect_to_db(SEFARIA_DB) else: - db = client[TEST_DB] - + db = connect_to_db(TEST_DB) -def get_test_db(): - return client[TEST_DB] def drop_test(): From ecdee521bfe431e1532b82db1a005816d0e7fbee Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Wed, 27 Sep 2023 16:26:27 +0300 Subject: [PATCH 297/756] chore: started separating learning team links from one another --- sefaria/helper/topic.py | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/sefaria/helper/topic.py b/sefaria/helper/topic.py index 36ebef5f48..d3fc002158 100644 --- a/sefaria/helper/topic.py +++ b/sefaria/helper/topic.py @@ -114,7 +114,7 @@ def merge_props_for_similar_refs(curr_link, new_link): # as well as datasource and descriptions of all the similar refs data_source = new_link.get('dataSource', None) if data_source: - curr_link = update_refs(curr_link, new_link, data_source) + curr_link = update_refs(curr_link, new_link) curr_link = update_data_source_in_link(curr_link, new_link, data_source) if not curr_link['is_sheet']: @@ -128,19 +128,10 @@ def update_data_source_in_link(curr_link, new_link, data_source): del new_link['dataSource'] return curr_link -def is_data_source_learning_team(func): - def wrapper(curr_link, new_link, data_source): - if data_source == 'learning-team': - return func(curr_link, new_link) - else: - return curr_link - return wrapper - -@is_data_source_learning_team def update_refs(curr_link, new_link): # in case the new_link was created by the learning team, we want to use ref of learning team link - # in the case when both links are from the learning team, use whichever ref covers a smaller range - if 'learning-team' not in curr_link['dataSources'] or len(curr_link['expandedRefs']) > len( + # in the case when neither link is from the learning team, use whichever ref covers a smaller range + if is_learning_team(curr_link['dataSources']) or len(curr_link['expandedRefs']) > len( new_link['expandedRefs']): curr_link['ref'] = new_link['ref'] curr_link['expandedRefs'] = new_link['expandedRefs'] @@ -171,6 +162,20 @@ def update_curated_primacy(curr_link, new_link): curr_link['order']['curatedPrimacy'] = curr_curated_primacy return curr_link +def is_learning_team(dataSource): + return dataSource == 'learning-team' or dataSource == 'learning-team-editing-tool' + +def iterate_and_merge(new_ref_links, new_link, subset_ref_map, temp_subset_refs): + # temp_subset_refs contains the refs within link's expandedRefs that overlap with other refs + # subset_ref_map + new_ref_links contain mappings to get from the temp_subset_refs to the actual link objects + for seg_ref in temp_subset_refs: + for index in subset_ref_map[seg_ref]: + new_ref_links[index]['similarRefs'] += [new_link] + curr_link_learning_team = any([is_learning_team(dataSource) for dataSource in new_ref_links[index]['dataSources']]) + if not curr_link_learning_team: # if learning team, ignore overlapping refs + new_ref_links[index] = merge_props_for_similar_refs(new_ref_links[index], new_link) + return new_ref_links + def sort_and_group_similar_refs(ref_links): ref_links.sort(key=cmp_to_key(sort_refs_by_relevance)) subset_ref_map = defaultdict(list) @@ -178,11 +183,11 @@ def sort_and_group_similar_refs(ref_links): for link in ref_links: del link['topic'] temp_subset_refs = subset_ref_map.keys() & set(link.get('expandedRefs', [])) - for seg_ref in temp_subset_refs: - for index in subset_ref_map[seg_ref]: - new_ref_links[index]['similarRefs'] += [link] - new_ref_links[index] = merge_props_for_similar_refs(new_ref_links[index], link) - if len(temp_subset_refs) == 0: + new_data_source = link.get("dataSource", None) + should_merge = len(temp_subset_refs) > 0 and not is_learning_team(new_data_source) # learning team links should be handled separately from one another and not merged + if should_merge: + new_ref_links = iterate_and_merge(new_ref_links, link, subset_ref_map, temp_subset_refs) + else: link['similarRefs'] = [] link['dataSources'] = {} if link.get('dataSource', None): From ec7bce311873a87465a081c87aadabd39366c94e Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 27 Sep 2023 17:21:19 +0300 Subject: [PATCH 298/756] fix(Portal): add newsletter to optional attrs --- sefaria/model/portal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sefaria/model/portal.py b/sefaria/model/portal.py index b6a2fa89b9..8f8cc39d21 100644 --- a/sefaria/model/portal.py +++ b/sefaria/model/portal.py @@ -61,7 +61,7 @@ class Portal(abst.SluggedAbstractMongoRecord): ] optional_attrs = [ "mobile", - 'api_schema', + 'newsletter', ] def _validate(self): From 4c14a64633ae6d63498a631cef05ad7c582c4242 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 27 Sep 2023 17:22:05 +0300 Subject: [PATCH 299/756] test(Portal): add test to make sure we can load a portal --- sefaria/model/tests/portal_test.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/sefaria/model/tests/portal_test.py b/sefaria/model/tests/portal_test.py index bece99fe96..3467ad67bb 100644 --- a/sefaria/model/tests/portal_test.py +++ b/sefaria/model/tests/portal_test.py @@ -2,6 +2,7 @@ from sefaria.model.portal import Portal # Replace with your actual validation function from sefaria.model.topic import Topic from sefaria.system.exceptions import SluggedMongoRecordMissingError +from sefaria.system.database import db valids = [ { @@ -490,6 +491,16 @@ def simple_portal(): portal.delete() +@pytest.fixture() +def simple_portal_saved_directly_to_mongo(): + raw_portal = valids[0] + inserted_result = db.portals.insert_one(raw_portal) + + yield Portal(raw_portal) + + db.portals.delete_one({"_id": inserted_result.inserted_id}) + + @pytest.fixture() def simple_topic(simple_portal): topic = Topic({ @@ -524,3 +535,7 @@ def test_topic_validation_fails_for_non_existent_portal(): }) topic.save() + +def test_load_portal(simple_portal_saved_directly_to_mongo): + portal = Portal().load({"slug": simple_portal_saved_directly_to_mongo.slug}) + assert portal is not None From 6ca9f75cb073cc4ce9b037d122c933989eacb623 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 27 Sep 2023 17:22:50 +0300 Subject: [PATCH 300/756] feat(Portal): working portals api --- reader/views.py | 10 ++++++++++ sefaria/urls.py | 5 +++++ 2 files changed, 15 insertions(+) diff --git a/reader/views.py b/reader/views.py index 86f6843254..9d13648951 100644 --- a/reader/views.py +++ b/reader/views.py @@ -3292,6 +3292,16 @@ def recommend_topics_api(request, ref_list=None): return response +@api_view(["GET"]) +@catch_error_as_json +def portals_api(request, slug): + """ + API to get data for a Portal object by slug + """ + portal = Portal.init(slug) + return jsonResponse(portal.contents(), callback=request.GET.get("callback", None)) + + @ensure_csrf_cookie @sanitize_get_params def global_activity(request, page=1): diff --git a/sefaria/urls.py b/sefaria/urls.py index b97e18771a..38fea43faf 100644 --- a/sefaria/urls.py +++ b/sefaria/urls.py @@ -268,6 +268,11 @@ url(r'^api/recommend/topics(/(?P<ref_list>.+))?', reader_views.recommend_topics_api), ] +# Portals API +urlpatterns += [ + url(r'^api/portals/(?P<slug>.+)$', reader_views.portals_api), +] + # History API urlpatterns += [ url(r'^api/history/(?P<tref>.+)/(?P<lang>\w\w)/(?P<version>.+)$', reader_views.texts_history_api), From c11d4278f64133c2745d69b0a07542983825c711 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 27 Sep 2023 18:33:55 +0300 Subject: [PATCH 301/756] feat(Portal): add portals to frontend data --- static/js/NavSidebar.jsx | 10 ++++++ static/js/TopicPage.jsx | 61 +++++++++++++++++++++++++----------- static/js/sefaria/sefaria.js | 10 ++++++ 3 files changed, 63 insertions(+), 18 deletions(-) diff --git a/static/js/NavSidebar.jsx b/static/js/NavSidebar.jsx index 83643d7ca5..ba404c6a93 100644 --- a/static/js/NavSidebar.jsx +++ b/static/js/NavSidebar.jsx @@ -50,6 +50,7 @@ const Modules = ({type, props}) => { "WhoToFollow": WhoToFollow, "Image": Image, "Wrapper": Wrapper, + "PortalAbout": PortalAbout, }; if (!type) { return null; } const ModuleType = moduleTypes[type]; @@ -792,6 +793,15 @@ const DownloadVersions = ({sref}) => { }; +const PortalAbout = ({title, description}) => { + return ( + <div> + {title.en} + {description.en} + </div> + ) +}; + export { NavSidebar, Modules, diff --git a/static/js/TopicPage.jsx b/static/js/TopicPage.jsx index a5bfb8df6f..eb10fd8725 100644 --- a/static/js/TopicPage.jsx +++ b/static/js/TopicPage.jsx @@ -431,6 +431,7 @@ const TopicPage = ({ const [refsToFetchByTab, setRefsToFetchByTab] = useState({}); const [parashaData, setParashaData] = useState(null); const [showFilterHeader, setShowFilterHeader] = useState(false); + const [portal, setPortal] = useState(null); const tabDisplayData = useTabDisplayData(translationLanguagePreference, versionPref); const scrollableElement = useRef(); @@ -498,6 +499,47 @@ const TopicPage = ({ onClickFilterIndex = displayTabs.length - 1; } const classStr = classNames({topicPanel: 1, readerNavMenu: 1}); + let sidebar = null; + if (topicData) { + if (topicData.portal_slug) { + Sefaria.getPortal(topicData.portal_slug).then(setPortal); + if (portal) { + const portalModuleTypeMap = { + "about": "PortalAbout", + // "mobile": "PortalMobile", + // "newsletter": "PortalNewsletter", + } + const modules = []; + for (let [key, value] of Object.entries(portal)) { + if (!portalModuleTypeMap[key]) { continue; } + modules.push({ + type: portalModuleTypeMap[key], + props: value, + }); + } + sidebar = <NavSidebar modules={modules} />; + } + } else { + sidebar = ( + <> + <TopicSideColumn + key={topic} + slug={topic} + links={topicData.links} + clearAndSetTopic={clearAndSetTopic} + setNavTopic={setNavTopic} + parashaData={parashaData} + tref={topicData.ref} + timePeriod={topicData.timePeriod} + properties={topicData.properties} + topicTitle={topicTitle} + multiPanel={multiPanel} + /> + {!topicData.isLoading && <Promotions/>} + </> + ); + } + } return <div className={classStr}> <div className="content noOverflowX" ref={scrollableElement}> <div className="columnLayout"> @@ -547,24 +589,7 @@ const TopicPage = ({ : (topicData.isLoading ? <LoadingMessage /> : null) } </div> <div className="sideColumn"> - {topicData ? ( - <> - <TopicSideColumn - key={topic} - slug={topic} - links={topicData.links} - clearAndSetTopic={clearAndSetTopic} - setNavTopic={setNavTopic} - parashaData={parashaData} - tref={topicData.ref} - timePeriod={topicData.timePeriod} - properties={topicData.properties} - topicTitle={topicTitle} - multiPanel={multiPanel} - /> - {!topicData.isLoading && <Promotions/>} - </> - ) : null} + {sidebar} </div> </div> <Footer /> diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index 196dea6430..1b180ccfff 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -569,6 +569,16 @@ Sefaria = extend(Sefaria, { } return Promise.resolve(this._versions[ref]); }, + _portals: {}, + getPortal: async function(portalSlug) { + const cachedPortal = Sefaria._portals[portalSlug]; + if (cachedPortal) { + return cachedPortal; + } + const response = await this._ApiPromise(`${Sefaria.apiHost}/api/portals/${portalSlug}`); + Sefaria._portals[portalSlug] = response; + return response; + }, filterVersionsObjByLangs: function(versionsObj, langs, includeFilter) { /** * @versionsObj {object} whode keys are language codes ('he', 'en' etc.) and values are version objects (like the object that getVersions returns) From bf0e254941ce090837df51bf3b53f555c2fb5d6b Mon Sep 17 00:00:00 2001 From: Russel Neiss <russel.neiss@gmail.com> Date: Wed, 27 Sep 2023 12:51:37 -0500 Subject: [PATCH 302/756] chore: remove extra debugging statement --- sefaria/helper/linker.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sefaria/helper/linker.py b/sefaria/helper/linker.py index df81fef43b..998f346a05 100644 --- a/sefaria/helper/linker.py +++ b/sefaria/helper/linker.py @@ -41,7 +41,6 @@ def make_find_refs_response(request): request_text, options, meta_data = _unpack_find_refs_request(request) if meta_data: _add_webpage_hit_for_url(meta_data.get("url", None)) - print(options) return _make_find_refs_response_with_cache(request_text, options, meta_data) From c1cb1bdc9492e2517134f1db0133bced0cffe01f Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Thu, 28 Sep 2023 09:41:39 +0300 Subject: [PATCH 303/756] fix(Source Editor): couldn't delete sources without availableLangs or curatedPrimacy fields --- sefaria/helper/topic.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sefaria/helper/topic.py b/sefaria/helper/topic.py index d3fc002158..3ce157d8ea 100644 --- a/sefaria/helper/topic.py +++ b/sefaria/helper/topic.py @@ -1255,12 +1255,12 @@ def delete_ref_topic_link(tref, to_topic, link_type, lang): if link is None: return {"error": f"Link between {tref} and {to_topic} doesn't exist."} - if lang in link.order['availableLangs']: + if lang in link.order.get('availableLangs', []): link.order['availableLangs'].remove(lang) - if lang in link.order['curatedPrimacy']: + if lang in link.order.get('curatedPrimacy', []): link.order['curatedPrimacy'].pop(lang) - if len(link.order['availableLangs']) > 0: + if len(link.order.get('availableLangs', [])) > 0: link.save() return {"status": "ok"} else: # deleted in both hebrew and english so delete link object From 28f59e23fa362151c4aae370f2438ebd7464b9fe Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Thu, 28 Sep 2023 10:36:26 +0300 Subject: [PATCH 304/756] chore: fix edge case in clickhandler in reorder in case user clicks down arrow on the item at the bottom of the list --- static/js/CategoryEditor.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/CategoryEditor.jsx b/static/js/CategoryEditor.jsx index 5cfafa8180..61f5b2c80e 100644 --- a/static/js/CategoryEditor.jsx +++ b/static/js/CategoryEditor.jsx @@ -22,7 +22,7 @@ const Reorder = ({subcategoriesAndBooks, updateOrder, displayType, updateParentC const clickHandler = (dir, child) => { const index = subcategoriesAndBooks.indexOf(child); let index_to_swap = -1; - if (dir === 'down' && index < subcategoriesAndBooks.length) + if (dir === 'down' && index < subcategoriesAndBooks.length - 1) { index_to_swap = index + 1; } From 5de19996bcba64fb060bdd689ae0264f978befce Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Thu, 28 Sep 2023 11:34:16 +0300 Subject: [PATCH 305/756] chore: remove impossible case in update_refs --- sefaria/helper/topic.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/sefaria/helper/topic.py b/sefaria/helper/topic.py index 3ce157d8ea..a18f15cea0 100644 --- a/sefaria/helper/topic.py +++ b/sefaria/helper/topic.py @@ -129,10 +129,8 @@ def update_data_source_in_link(curr_link, new_link, data_source): return curr_link def update_refs(curr_link, new_link): - # in case the new_link was created by the learning team, we want to use ref of learning team link - # in the case when neither link is from the learning team, use whichever ref covers a smaller range - if is_learning_team(curr_link['dataSources']) or len(curr_link['expandedRefs']) > len( - new_link['expandedRefs']): + # use whichever ref covers a smaller range + if len(curr_link['expandedRefs']) > len(new_link['expandedRefs']): curr_link['ref'] = new_link['ref'] curr_link['expandedRefs'] = new_link['expandedRefs'] return curr_link @@ -172,7 +170,7 @@ def iterate_and_merge(new_ref_links, new_link, subset_ref_map, temp_subset_refs) for index in subset_ref_map[seg_ref]: new_ref_links[index]['similarRefs'] += [new_link] curr_link_learning_team = any([is_learning_team(dataSource) for dataSource in new_ref_links[index]['dataSources']]) - if not curr_link_learning_team: # if learning team, ignore overlapping refs + if not curr_link_learning_team: # if learning team, ignore source with overlapping refs new_ref_links[index] = merge_props_for_similar_refs(new_ref_links[index], new_link) return new_ref_links From 8927097808a75d4c0dabd793a6830019d777a70b Mon Sep 17 00:00:00 2001 From: yonadavGit <yonadav.le@gmail.com> Date: Thu, 28 Sep 2023 18:14:45 +0300 Subject: [PATCH 306/756] feat(portal): Added frontends NavSideBar modules to display portal data --- sefaria/model/portal.py | 1 + static/css/s2.css | 8 ++++++++ static/js/NavSidebar.jsx | 43 +++++++++++++++++++++++++++++++++++----- static/js/TopicPage.jsx | 12 ++++++----- 4 files changed, 54 insertions(+), 10 deletions(-) diff --git a/sefaria/model/portal.py b/sefaria/model/portal.py index 8f8cc39d21..8f32ba7a7d 100644 --- a/sefaria/model/portal.py +++ b/sefaria/model/portal.py @@ -76,6 +76,7 @@ def _validate(self): mobile_schema = { "title": ({"en": (str, "required"), "he": (str, "required")}, "required"), + "description": ({"en": (str, "optional"), "he": (str, "optional")}, "optional"), "android_link": (str, "optional"), "ios_link": (str, "optional") } diff --git a/static/css/s2.css b/static/css/s2.css index c9ee39e798..85174a482f 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -2303,6 +2303,13 @@ div.interfaceLinks-row a { padding-left: 44px; padding-right: 44px; } +.portalTopicImageWrapper { + padding-top: 10px; +} +.portalTopicImageWrapper .topicImage { + padding-left: 0; + padding-right: 0; +} @media (max-width: 600px) { .topicPhoto{ height: auto; @@ -2329,6 +2336,7 @@ div.interfaceLinks-row a { margin-bottom: 30px; text-align: center; } + } .readerPanel .translationsPage h2 { margin: 40px 0 0 0; diff --git a/static/js/NavSidebar.jsx b/static/js/NavSidebar.jsx index ba404c6a93..304d5bb8ab 100644 --- a/static/js/NavSidebar.jsx +++ b/static/js/NavSidebar.jsx @@ -5,6 +5,8 @@ import Sefaria from './sefaria/sefaria'; import {DonateLink, EnglishText, HebrewText, NewsletterSignUpForm} from './Misc' import {InterfaceText, ProfileListing, Dropdown} from './Misc'; import { Promotions } from './Promotions' +import {TopicImage} from './TopicPage' +import Component from "react-class"; const NavSidebar = ({modules}) => { return <div className="navSidebar sans-serif"> @@ -51,6 +53,8 @@ const Modules = ({type, props}) => { "Image": Image, "Wrapper": Wrapper, "PortalAbout": PortalAbout, + "PortalMobile": PortalMobile, + "PortalNewsletter": PortalNewsletter }; if (!type) { return null; } const ModuleType = moduleTypes[type]; @@ -793,15 +797,44 @@ const DownloadVersions = ({sref}) => { }; -const PortalAbout = ({title, description}) => { - return ( - <div> - {title.en} - {description.en} +const PortalAbout = ({title, description, image_uri}) => { + return( + <Module> + <ModuleTitle en={title.en} he={title.he} /> + <InterfaceText markdown={{en: description.en, he: description.he}} /> + <div class="portalTopicImageWrapper"> + <TopicImage photoLink={image_uri}/> </div> + </Module> + ) +}; +const PortalMobile = ({title, description, android_link, ios_link}) => { + return( + <Module> + <ModuleTitle en={title.en} he={title.he} /> + <InterfaceText markdown={{en: description.en, he: description.he}} /> + <a target="_blank" className="button small white appButton ios" href={ios_link}> + <img src="/static/icons/ios.svg"/> + <InterfaceText>iOS</InterfaceText> + </a> + <a target="_blank" className="button small white appButton" href={android_link}> + <img src="/static/icons/android.svg" /> + <InterfaceText>Android</InterfaceText> + </a> + </Module> + ) +}; +const PortalNewsletter = ({title, title_url, description}) => { + return( + <Module> + <a href={title_url}><ModuleTitle en={title.en} he={title.he} /></a> + <InterfaceText markdown={{en: description.en, he: description.he}} /> + <NewsletterSignUpForm/> + </Module> ) }; + export { NavSidebar, Modules, diff --git a/static/js/TopicPage.jsx b/static/js/TopicPage.jsx index eb10fd8725..a3e5988e86 100644 --- a/static/js/TopicPage.jsx +++ b/static/js/TopicPage.jsx @@ -506,10 +506,11 @@ const TopicPage = ({ if (portal) { const portalModuleTypeMap = { "about": "PortalAbout", - // "mobile": "PortalMobile", - // "newsletter": "PortalNewsletter", + "mobile": "PortalMobile", + "newsletter": "PortalNewsletter", } const modules = []; + console.log("portal", portal) for (let [key, value] of Object.entries(portal)) { if (!portalModuleTypeMap[key]) { continue; } modules.push({ @@ -764,11 +765,11 @@ const TopicSideSection = ({ title, children, hasMore }) => { } const TopicImage = ({photoLink, enCaption, heCaption }) => { - + return ( <div class="topicImage"> <img class="topicPhoto" src={photoLink}/> - <div class="topicImageCaption"> + <div class="topicImageCaption"> <InterfaceText text={{en:enCaption, he:heCaption}} /> </div> </div>); @@ -884,5 +885,6 @@ const TopicMetaData = ({ topicTitle, timePeriod, multiPanel, properties={} }) => export { TopicPage, TopicCategory, - refSort + refSort, + TopicImage } From 09c7d95616b47418099ecfa99665b26d773449e2 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Thu, 28 Sep 2023 19:12:16 +0300 Subject: [PATCH 307/756] chore(Portal): fix formatting --- static/js/NavSidebar.jsx | 42 +++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/static/js/NavSidebar.jsx b/static/js/NavSidebar.jsx index 304d5bb8ab..4247198444 100644 --- a/static/js/NavSidebar.jsx +++ b/static/js/NavSidebar.jsx @@ -1,12 +1,10 @@ import React, { useState, useEffect } from 'react'; import classNames from 'classnames'; -import PropTypes from 'prop-types'; import Sefaria from './sefaria/sefaria'; import {DonateLink, EnglishText, HebrewText, NewsletterSignUpForm} from './Misc' import {InterfaceText, ProfileListing, Dropdown} from './Misc'; import { Promotions } from './Promotions' import {TopicImage} from './TopicPage' -import Component from "react-class"; const NavSidebar = ({modules}) => { return <div className="navSidebar sans-serif"> @@ -800,36 +798,40 @@ const DownloadVersions = ({sref}) => { const PortalAbout = ({title, description, image_uri}) => { return( <Module> - <ModuleTitle en={title.en} he={title.he} /> - <InterfaceText markdown={{en: description.en, he: description.he}} /> - <div class="portalTopicImageWrapper"> - <TopicImage photoLink={image_uri}/> - </div> + <ModuleTitle en={title.en} he={title.he} /> + <InterfaceText markdown={{en: description.en, he: description.he}} /> + <div className="portalTopicImageWrapper"> + <TopicImage photoLink={image_uri}/> + </div> </Module> ) }; + + const PortalMobile = ({title, description, android_link, ios_link}) => { return( <Module> - <ModuleTitle en={title.en} he={title.he} /> - <InterfaceText markdown={{en: description.en, he: description.he}} /> - <a target="_blank" className="button small white appButton ios" href={ios_link}> - <img src="/static/icons/ios.svg"/> - <InterfaceText>iOS</InterfaceText> - </a> - <a target="_blank" className="button small white appButton" href={android_link}> - <img src="/static/icons/android.svg" /> - <InterfaceText>Android</InterfaceText> - </a> + <ModuleTitle en={title.en} he={title.he} /> + <InterfaceText markdown={{en: description.en, he: description.he}} /> + <a target="_blank" className="button small white appButton ios" href={ios_link}> + <img src="/static/icons/ios.svg"/> + <InterfaceText>iOS</InterfaceText> + </a> + <a target="_blank" className="button small white appButton" href={android_link}> + <img src="/static/icons/android.svg" /> + <InterfaceText>Android</InterfaceText> + </a> </Module> ) }; + + const PortalNewsletter = ({title, title_url, description}) => { return( <Module> - <a href={title_url}><ModuleTitle en={title.en} he={title.he} /></a> - <InterfaceText markdown={{en: description.en, he: description.he}} /> - <NewsletterSignUpForm/> + <a href={title_url}><ModuleTitle en={title.en} he={title.he} /></a> + <InterfaceText markdown={{en: description.en, he: description.he}} /> + <NewsletterSignUpForm/> </Module> ) }; From c8e417d79710dc4f744699c770f747996f9af07f Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Thu, 28 Sep 2023 19:57:31 +0300 Subject: [PATCH 308/756] fix(Portal): polish image --- static/css/s2.css | 3 ++- static/js/NavSidebar.jsx | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index 85174a482f..5c293f0346 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -2304,7 +2304,8 @@ div.interfaceLinks-row a { padding-right: 44px; } .portalTopicImageWrapper { - padding-top: 10px; + padding-top: 5px; + margin-bottom: 25px; } .portalTopicImageWrapper .topicImage { padding-left: 0; diff --git a/static/js/NavSidebar.jsx b/static/js/NavSidebar.jsx index 4247198444..d33e26025b 100644 --- a/static/js/NavSidebar.jsx +++ b/static/js/NavSidebar.jsx @@ -799,10 +799,10 @@ const PortalAbout = ({title, description, image_uri}) => { return( <Module> <ModuleTitle en={title.en} he={title.he} /> - <InterfaceText markdown={{en: description.en, he: description.he}} /> <div className="portalTopicImageWrapper"> <TopicImage photoLink={image_uri}/> </div> + <InterfaceText markdown={{en: description.en, he: description.he}} /> </Module> ) }; From 15af102209b6c6b956114e71953bbc18173a443b Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Thu, 28 Sep 2023 20:02:20 +0300 Subject: [PATCH 309/756] feat(Portal): add captions --- sefaria/model/portal.py | 1 + static/js/NavSidebar.jsx | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/sefaria/model/portal.py b/sefaria/model/portal.py index 8f32ba7a7d..54be1628ff 100644 --- a/sefaria/model/portal.py +++ b/sefaria/model/portal.py @@ -71,6 +71,7 @@ def _validate(self): "title": ({"en": (str, "required"), "he": (str, "required")}, "required"), "title_url": (str, "optional"), "image_uri": (str, "optional"), + "image_caption": ({"en": (str, "optional"), "he": (str, "optional")}, "optional"), "description": ({"en": (str, "required"), "he": (str, "required")}, "optional"), } diff --git a/static/js/NavSidebar.jsx b/static/js/NavSidebar.jsx index d33e26025b..0adbd3d7c5 100644 --- a/static/js/NavSidebar.jsx +++ b/static/js/NavSidebar.jsx @@ -795,12 +795,12 @@ const DownloadVersions = ({sref}) => { }; -const PortalAbout = ({title, description, image_uri}) => { +const PortalAbout = ({title, description, image_uri, image_caption}) => { return( <Module> <ModuleTitle en={title.en} he={title.he} /> <div className="portalTopicImageWrapper"> - <TopicImage photoLink={image_uri}/> + <TopicImage photoLink={image_uri} enCaption={image_caption.en} heCaption={image_caption.he} /> </div> <InterfaceText markdown={{en: description.en, he: description.he}} /> </Module> From b27bb4115d6872461c1c9ba2de6fc713644279be Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Thu, 28 Sep 2023 20:12:51 +0300 Subject: [PATCH 310/756] fix(Portal): remove mobile description --- static/css/s2.css | 3 +++ static/js/NavSidebar.jsx | 22 ++++++++++++---------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index 5c293f0346..9b7058e8fb 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -2303,6 +2303,9 @@ div.interfaceLinks-row a { padding-left: 44px; padding-right: 44px; } +.navSidebarModule .portalMobile .button { + margin-top: 0; +} .portalTopicImageWrapper { padding-top: 5px; margin-bottom: 25px; diff --git a/static/js/NavSidebar.jsx b/static/js/NavSidebar.jsx index 0adbd3d7c5..13119f04ec 100644 --- a/static/js/NavSidebar.jsx +++ b/static/js/NavSidebar.jsx @@ -811,16 +811,18 @@ const PortalAbout = ({title, description, image_uri, image_caption}) => { const PortalMobile = ({title, description, android_link, ios_link}) => { return( <Module> - <ModuleTitle en={title.en} he={title.he} /> - <InterfaceText markdown={{en: description.en, he: description.he}} /> - <a target="_blank" className="button small white appButton ios" href={ios_link}> - <img src="/static/icons/ios.svg"/> - <InterfaceText>iOS</InterfaceText> - </a> - <a target="_blank" className="button small white appButton" href={android_link}> - <img src="/static/icons/android.svg" /> - <InterfaceText>Android</InterfaceText> - </a> + <div className="portalMobile"> + <ModuleTitle en={title.en} he={title.he} /> + {description && <InterfaceText markdown={{en: description.en, he: description.he}} />} + <a target="_blank" className="button small white appButton ios" href={ios_link}> + <img src="/static/icons/ios.svg"/> + <InterfaceText>iOS</InterfaceText> + </a> + <a target="_blank" className="button small white appButton" href={android_link}> + <img src="/static/icons/android.svg" /> + <InterfaceText>Android</InterfaceText> + </a> + </div> </Module> ) }; From bd64ff2cf852ff34d6d0143357a7c14ff11a4aae Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Thu, 28 Sep 2023 20:39:56 +0300 Subject: [PATCH 311/756] fix(Portal): remove .sideColumn class from portal sidebar. This was interfering with styles of buttons. --- static/js/TopicPage.jsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/static/js/TopicPage.jsx b/static/js/TopicPage.jsx index a3e5988e86..af38fd6b9f 100644 --- a/static/js/TopicPage.jsx +++ b/static/js/TopicPage.jsx @@ -522,7 +522,7 @@ const TopicPage = ({ } } else { sidebar = ( - <> + <div className="sideColumn"> <TopicSideColumn key={topic} slug={topic} @@ -537,7 +537,7 @@ const TopicPage = ({ multiPanel={multiPanel} /> {!topicData.isLoading && <Promotions/>} - </> + </div> ); } } @@ -589,9 +589,7 @@ const TopicPage = ({ </TabView> : (topicData.isLoading ? <LoadingMessage /> : null) } </div> - <div className="sideColumn"> - {sidebar} - </div> + {sidebar} </div> <Footer /> </div> From e96d7614f6d929e6acf227c69ca0933a731b4048 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Thu, 28 Sep 2023 20:53:47 +0300 Subject: [PATCH 312/756] refactor(Portal): pull out code for app store buttons to own component. --- static/js/Misc.jsx | 16 ++++++++++++++++ static/js/NavSidebar.jsx | 30 +++++++++++++----------------- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 367a778584..64ab830325 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -3272,7 +3272,23 @@ const Autocompleter = ({getSuggestions, showSuggestionsOnSelect, inputPlaceholde ) } + +const AppStoreButton = ({ platform, href, altText }) => { + const isIOS = platform === 'ios'; + const aClasses = classNames({button: 1, small: 1, white: 1, appButton: 1, ios: isIOS}); + const iconSrc = `/static/icons/${isIOS ? 'ios' : 'android'}.svg`; + const text = isIOS ? 'iOS' : 'Android'; + return ( + <a target="_blank" className={aClasses} href={href}> + <img src={iconSrc} alt={altText} /> + <InterfaceText>{text}</InterfaceText> + </a> + ); +}; + + export { + AppStoreButton, CategoryHeader, SimpleInterfaceBlock, DangerousInterfaceBlock, diff --git a/static/js/NavSidebar.jsx b/static/js/NavSidebar.jsx index 13119f04ec..300276d52b 100644 --- a/static/js/NavSidebar.jsx +++ b/static/js/NavSidebar.jsx @@ -1,7 +1,7 @@ import React, { useState, useEffect } from 'react'; import classNames from 'classnames'; import Sefaria from './sefaria/sefaria'; -import {DonateLink, EnglishText, HebrewText, NewsletterSignUpForm} from './Misc' +import {AppStoreButton, DonateLink, EnglishText, HebrewText, NewsletterSignUpForm} from './Misc' import {InterfaceText, ProfileListing, Dropdown} from './Misc'; import { Promotions } from './Promotions' import {TopicImage} from './TopicPage' @@ -580,14 +580,16 @@ const GetTheApp = () => ( <ModuleTitle>Get the Mobile App</ModuleTitle> <InterfaceText>Access the Jewish library anywhere and anytime with the</InterfaceText> <a href="/mobile" className="inTextLink"><InterfaceText>Sefaria mobile app.</InterfaceText></a> <br /> - <a target="_blank" className="button small white appButton ios" href="https://itunes.apple.com/us/app/sefaria/id1163273965?ls=1&mt=8"> - <img src="/static/icons/ios.svg" alt={Sefaria._("Sefaria app on IOS")} /> - <InterfaceText>iOS</InterfaceText> - </a> - <a target="_blank" className="button small white appButton" href="https://play.google.com/store/apps/details?id=org.sefaria.sefaria"> - <img src="/static/icons/android.svg" alt={Sefaria._("Sefaria app on Android")} /> - <InterfaceText>Android</InterfaceText> - </a> + <AppStoreButton + href="https://itunes.apple.com/us/app/sefaria/id1163273965?ls=1&mt=8" + platform='ios' + altText={Sefaria._("Sefaria app on IOS")} + /> + <AppStoreButton + href="https://play.google.com/store/apps/details?id=org.sefaria.sefaria" + platform='android' + altText={Sefaria._("Sefaria app on Android")} + /> </Module> ); @@ -814,14 +816,8 @@ const PortalMobile = ({title, description, android_link, ios_link}) => { <div className="portalMobile"> <ModuleTitle en={title.en} he={title.he} /> {description && <InterfaceText markdown={{en: description.en, he: description.he}} />} - <a target="_blank" className="button small white appButton ios" href={ios_link}> - <img src="/static/icons/ios.svg"/> - <InterfaceText>iOS</InterfaceText> - </a> - <a target="_blank" className="button small white appButton" href={android_link}> - <img src="/static/icons/android.svg" /> - <InterfaceText>Android</InterfaceText> - </a> + <AppStoreButton href={ios_link} platform={'ios'} altText='Steinsaltz app on iOS' /> + <AppStoreButton href={android_link} platform={'android'} altText='Steinsaltz app on Android' /> </div> </Module> ) From c4f309ab4b1f4d1eaafb8064ae106df7527889db Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Thu, 28 Sep 2023 21:31:41 +0300 Subject: [PATCH 313/756] refactor(Portal): move NewsletterSignUpForm to its own file. --- static/js/Footer.jsx | 3 +- static/js/Misc.jsx | 140 ---------------------------- static/js/NavSidebar.jsx | 3 +- static/js/NewsletterSignUpForm.jsx | 143 +++++++++++++++++++++++++++++ static/js/StaticPages.jsx | 2 +- 5 files changed, 148 insertions(+), 143 deletions(-) create mode 100644 static/js/NewsletterSignUpForm.jsx diff --git a/static/js/Footer.jsx b/static/js/Footer.jsx index a5b2e6d6ae..ad341725f3 100644 --- a/static/js/Footer.jsx +++ b/static/js/Footer.jsx @@ -2,7 +2,8 @@ import React from 'react'; import Sefaria from './sefaria/sefaria'; import PropTypes from'prop-types'; import $ from './sefaria/sefariaJquery'; -import { InterfaceText, NewsletterSignUpForm, DonateLink } from './Misc'; +import { InterfaceText, DonateLink } from './Misc'; +import {NewsletterSignUpForm} from "./NewsletterSignUpForm"; import Component from 'react-class'; const Section = ({en, he, children}) => ( diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 64ab830325..d5e8188e5f 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -1884,145 +1884,6 @@ Note.propTypes = { isMyNote: PropTypes.bool, editNote: PropTypes.func }; -function NewsletterSignUpForm(props) { - const {contextName, includeEducatorOption} = props; - const [email, setEmail] = useState(''); - const [firstName, setFirstName] = useState(''); - const [lastName, setLastName] = useState(''); - const [educatorCheck, setEducatorCheck] = useState(false); - const [subscribeMessage, setSubscribeMessage] = useState(null); - const [showNameInputs, setShowNameInputs] = useState(false); - - function handleSubscribeKeyUp(e) { - if (e.keyCode === 13) { - handleSubscribe(); - } - } - - function handleSubscribe() { - if (showNameInputs === true) { // submit - if (firstName.length > 0 & lastName.length > 0) { - setSubscribeMessage("Subscribing..."); - const request = new Request( - '/api/subscribe/'+email, - {headers: {'X-CSRFToken': Cookies.get('csrftoken')}, - 'Content-Type': 'application/json'} - ); - fetch(request, - { - method: "POST", - mode: 'same-origin', - credentials: 'same-origin', - body: JSON.stringify({ - language: Sefaria.interfaceLang === "hebrew" ? "he" : "en", - educator: educatorCheck, - firstName: firstName, - lastName: lastName - }) - } - ).then(res => { - if ("error" in res) { - setSubscribeMessage(res.error); - setShowNameInputs(false); - } else { - setSubscribeMessage("Subscribed! Welcome to our list."); - Sefaria.track.event("Newsletter", "Subscribe from " + contextName, ""); - } - }).catch(data => { - setSubscribeMessage("Sorry, there was an error."); - setShowNameInputs(false); - }); - } else { - setSubscribeMessage("Please enter a valid first and last name");// get he copy - } - } else if (Sefaria.util.isValidEmailAddress(email)) { - setShowNameInputs(true); - } else { - setShowNameInputs(false); - setSubscribeMessage("Please enter a valid email address."); - } - } - - return ( - <div className="newsletterSignUpBox"> - <span className="int-en"> - <input - className="newsletterInput" - placeholder="Sign up for Newsletter" - value={email} - onChange={e => setEmail(e.target.value)} - onKeyUp={handleSubscribeKeyUp}/> - </span> - <span className="int-he"> - <input - className="newsletterInput" - placeholder="הרשמו לניוזלטר" - value={email} - onChange={e => setEmail(e.target.value)} - onKeyUp={handleSubscribeKeyUp}/> - </span> - {!showNameInputs ? <img src="/static/img/circled-arrow-right.svg" onClick={handleSubscribe}/> : null} - {showNameInputs ? - <><span className="int-en"> - <input - className="newsletterInput firstNameInput" - placeholder="First Name" - value={firstName} - autoFocus - onChange={e => setFirstName(e.target.value)} - onKeyUp={handleSubscribeKeyUp}/> - </span> - <span className="int-he"> - <input - className="newsletterInput firstNameInput" - placeholder="שם פרטי" - value={firstName} - onChange={e => setFirstName(e.target.value)} - onKeyUp={handleSubscribeKeyUp}/> - </span> - <span className="int-en"> - <input - className="newsletterInput" - placeholder="Last Name" - value={lastName} - onChange={e => setLastName(e.target.value)} - onKeyUp={handleSubscribeKeyUp}/> - </span> - <span className="int-he"> - <input - className="newsletterInput" - placeholder="שם משפחה" - value={lastName} - onChange={e => setLastName(e.target.value)} - onKeyUp={handleSubscribeKeyUp}/> - </span> - <div className="newsletterEducatorOption"> - <span className="int-en"> - <input - type="checkbox" - className="educatorNewsletterInput" - checked={educatorCheck} - onChange={e => setEducatorCheck(!!e.target.checked)}/> - <span> I am an educator</span> - </span> - <span className="int-he"> - <input - type="checkbox" - className="educatorNewsletterInput" - checked={educatorCheck} - onChange={e => setEducatorCheck(!!e.target.checked)}/> - <span> מורים/ אנשי הוראה</span> - </span> - <img src="/static/img/circled-arrow-right.svg" onClick={handleSubscribe}/> - </div> - </> - : null} - {subscribeMessage ? - <div className="subscribeMessage">{Sefaria._(subscribeMessage)}</div> - : null} - </div> - ); -} class LoginPrompt extends Component { @@ -3321,7 +3182,6 @@ export { LoadingRing, LoginPrompt, NBox, - NewsletterSignUpForm, Note, ProfileListing, ProfilePic, diff --git a/static/js/NavSidebar.jsx b/static/js/NavSidebar.jsx index 300276d52b..f072f6c3eb 100644 --- a/static/js/NavSidebar.jsx +++ b/static/js/NavSidebar.jsx @@ -1,7 +1,8 @@ import React, { useState, useEffect } from 'react'; import classNames from 'classnames'; import Sefaria from './sefaria/sefaria'; -import {AppStoreButton, DonateLink, EnglishText, HebrewText, NewsletterSignUpForm} from './Misc' +import {AppStoreButton, DonateLink, EnglishText, HebrewText} from './Misc' +import {NewsletterSignUpForm} from "./NewsletterSignUpForm"; import {InterfaceText, ProfileListing, Dropdown} from './Misc'; import { Promotions } from './Promotions' import {TopicImage} from './TopicPage' diff --git a/static/js/NewsletterSignUpForm.jsx b/static/js/NewsletterSignUpForm.jsx new file mode 100644 index 0000000000..4cb7890958 --- /dev/null +++ b/static/js/NewsletterSignUpForm.jsx @@ -0,0 +1,143 @@ +import React, {useState} from 'react'; +import Sefaria from './sefaria/sefaria'; +import Cookies from "js-cookie"; + +export function NewsletterSignUpForm(props) { + const {contextName, includeEducatorOption} = props; + const [email, setEmail] = useState(''); + const [firstName, setFirstName] = useState(''); + const [lastName, setLastName] = useState(''); + const [educatorCheck, setEducatorCheck] = useState(false); + const [subscribeMessage, setSubscribeMessage] = useState(null); + const [showNameInputs, setShowNameInputs] = useState(false); + + function handleSubscribeKeyUp(e) { + if (e.keyCode === 13) { + handleSubscribe(); + } + } + + function handleSubscribe() { + if (showNameInputs === true) { // submit + if (firstName.length > 0 & lastName.length > 0) { + setSubscribeMessage("Subscribing..."); + const request = new Request( + '/api/subscribe/'+email, + {headers: {'X-CSRFToken': Cookies.get('csrftoken')}, + 'Content-Type': 'application/json'} + ); + fetch(request, + { + method: "POST", + mode: 'same-origin', + credentials: 'same-origin', + body: JSON.stringify({ + language: Sefaria.interfaceLang === "hebrew" ? "he" : "en", + educator: educatorCheck, + firstName: firstName, + lastName: lastName + }) + } + ).then(res => { + if ("error" in res) { + setSubscribeMessage(res.error); + setShowNameInputs(false); + } else { + setSubscribeMessage("Subscribed! Welcome to our list."); + Sefaria.track.event("Newsletter", "Subscribe from " + contextName, ""); + } + }).catch(data => { + setSubscribeMessage("Sorry, there was an error."); + setShowNameInputs(false); + }); + } else { + setSubscribeMessage("Please enter a valid first and last name");// get he copy + } + } else if (Sefaria.util.isValidEmailAddress(email)) { + setShowNameInputs(true); + } else { + setShowNameInputs(false); + setSubscribeMessage("Please enter a valid email address."); + } + } + + return ( + <div className="newsletterSignUpBox"> + <span className="int-en"> + <input + className="newsletterInput" + placeholder="Sign up for Newsletter" + value={email} + onChange={e => setEmail(e.target.value)} + onKeyUp={handleSubscribeKeyUp}/> + </span> + <span className="int-he"> + <input + className="newsletterInput" + placeholder="הרשמו לניוזלטר" + value={email} + onChange={e => setEmail(e.target.value)} + onKeyUp={handleSubscribeKeyUp}/> + </span> + {!showNameInputs ? <img src="/static/img/circled-arrow-right.svg" onClick={handleSubscribe}/> : null} + {showNameInputs ? + <><span className="int-en"> + <input + className="newsletterInput firstNameInput" + placeholder="First Name" + value={firstName} + autoFocus + onChange={e => setFirstName(e.target.value)} + onKeyUp={handleSubscribeKeyUp}/> + </span> + <span className="int-he"> + <input + className="newsletterInput firstNameInput" + placeholder="שם פרטי" + value={firstName} + onChange={e => setFirstName(e.target.value)} + onKeyUp={handleSubscribeKeyUp}/> + </span> + <span className="int-en"> + <input + className="newsletterInput" + placeholder="Last Name" + value={lastName} + onChange={e => setLastName(e.target.value)} + onKeyUp={handleSubscribeKeyUp}/> + </span> + <span className="int-he"> + <input + className="newsletterInput" + placeholder="שם משפחה" + value={lastName} + onChange={e => setLastName(e.target.value)} + onKeyUp={handleSubscribeKeyUp}/> + </span> + <div className="newsletterEducatorOption"> + <span className="int-en"> + <input + type="checkbox" + className="educatorNewsletterInput" + checked={educatorCheck} + onChange={e => setEducatorCheck(!!e.target.checked)}/> + <span> I am an educator</span> + </span> + <span className="int-he"> + <input + type="checkbox" + className="educatorNewsletterInput" + checked={educatorCheck} + onChange={e => setEducatorCheck(!!e.target.checked)}/> + <span> מורים/ אנשי הוראה</span> + </span> + <img src="/static/img/circled-arrow-right.svg" onClick={handleSubscribe}/> + </div> + </> + : null} + {subscribeMessage ? + <div className="subscribeMessage">{Sefaria._(subscribeMessage)}</div> + : null} + </div> + ); +} diff --git a/static/js/StaticPages.jsx b/static/js/StaticPages.jsx index 4b2bc394d0..0af80e9a67 100644 --- a/static/js/StaticPages.jsx +++ b/static/js/StaticPages.jsx @@ -1,11 +1,11 @@ import React, {useState, useRef} from 'react'; import { SimpleInterfaceBlock, - NewsletterSignUpForm, TwoOrThreeBox, ResponsiveNBox, NBox, InterfaceText, } from './Misc'; +import {NewsletterSignUpForm} from "./NewsletterSignUpForm"; import palette from './sefaria/palette'; import classNames from 'classnames'; import Cookies from 'js-cookie'; From f41c4be04212edc02a3b07f264291fc6d09a6307 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Thu, 28 Sep 2023 21:46:49 +0300 Subject: [PATCH 314/756] refactor(Portal): pull educator checkbox out --- static/js/NewsletterSignUpForm.jsx | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/static/js/NewsletterSignUpForm.jsx b/static/js/NewsletterSignUpForm.jsx index 4cb7890958..72b8d63125 100644 --- a/static/js/NewsletterSignUpForm.jsx +++ b/static/js/NewsletterSignUpForm.jsx @@ -2,8 +2,7 @@ import React, {useState} from 'react'; import Sefaria from './sefaria/sefaria'; import Cookies from "js-cookie"; -export function NewsletterSignUpForm(props) { - const {contextName, includeEducatorOption} = props; +export function NewsletterSignUpForm({contextName, includeEducatorOption}) { const [email, setEmail] = useState(''); const [firstName, setFirstName] = useState(''); const [lastName, setLastName] = useState(''); @@ -114,7 +113,21 @@ export function NewsletterSignUpForm(props) { onChange={e => setLastName(e.target.value)} onKeyUp={handleSubscribeKeyUp}/> </span> - <div className="newsletterEducatorOption"> + <EducatorCheckbox educatorCheck={educatorCheck} setEducatorCheck={setEducatorCheck} /> + <img src="/static/img/circled-arrow-right.svg" onClick={handleSubscribe}/> + </> + : null} + {subscribeMessage ? + <div className="subscribeMessage">{Sefaria._(subscribeMessage)}</div> + : null} + </div> + ); +} + + +const EducatorCheckbox = ({ educatorCheck, setEducatorCheck }) => { + return ( + <div className="newsletterEducatorOption"> <span className="int-en"> <input type="checkbox" @@ -123,7 +136,7 @@ export function NewsletterSignUpForm(props) { onChange={e => setEducatorCheck(!!e.target.checked)}/> <span> I am an educator</span> </span> - <span className="int-he"> + <span className="int-he"> <input type="checkbox" className="educatorNewsletterInput" @@ -131,13 +144,6 @@ export function NewsletterSignUpForm(props) { onChange={e => setEducatorCheck(!!e.target.checked)}/> <span> מורים/ אנשי הוראה</span> </span> - <img src="/static/img/circled-arrow-right.svg" onClick={handleSubscribe}/> - </div> - </> - : null} - {subscribeMessage ? - <div className="subscribeMessage">{Sefaria._(subscribeMessage)}</div> - : null} </div> ); -} +}; From d1eb85e5da1f57d63ce9ee5b2d75d5c581a3eae3 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Thu, 28 Sep 2023 21:48:32 +0300 Subject: [PATCH 315/756] fix(Portal): remove educator checkbox from portal newsletter --- static/js/NavSidebar.jsx | 2 +- static/js/NewsletterSignUpForm.jsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/static/js/NavSidebar.jsx b/static/js/NavSidebar.jsx index f072f6c3eb..40f0c8c827 100644 --- a/static/js/NavSidebar.jsx +++ b/static/js/NavSidebar.jsx @@ -830,7 +830,7 @@ const PortalNewsletter = ({title, title_url, description}) => { <Module> <a href={title_url}><ModuleTitle en={title.en} he={title.he} /></a> <InterfaceText markdown={{en: description.en, he: description.he}} /> - <NewsletterSignUpForm/> + <NewsletterSignUpForm includeEducatorOption={false}/> </Module> ) }; diff --git a/static/js/NewsletterSignUpForm.jsx b/static/js/NewsletterSignUpForm.jsx index 72b8d63125..d78ecf724a 100644 --- a/static/js/NewsletterSignUpForm.jsx +++ b/static/js/NewsletterSignUpForm.jsx @@ -2,7 +2,7 @@ import React, {useState} from 'react'; import Sefaria from './sefaria/sefaria'; import Cookies from "js-cookie"; -export function NewsletterSignUpForm({contextName, includeEducatorOption}) { +export function NewsletterSignUpForm({contextName, includeEducatorOption=true}) { const [email, setEmail] = useState(''); const [firstName, setFirstName] = useState(''); const [lastName, setLastName] = useState(''); @@ -113,7 +113,7 @@ export function NewsletterSignUpForm({contextName, includeEducatorOption}) { onChange={e => setLastName(e.target.value)} onKeyUp={handleSubscribeKeyUp}/> </span> - <EducatorCheckbox educatorCheck={educatorCheck} setEducatorCheck={setEducatorCheck} /> + {includeEducatorOption ? <EducatorCheckbox educatorCheck={educatorCheck} setEducatorCheck={setEducatorCheck} /> : null} <img src="/static/img/circled-arrow-right.svg" onClick={handleSubscribe}/> </> : null} From 34c6a3e8bbc123147cbd3ef92062d679bd846fbe Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Thu, 28 Sep 2023 21:53:09 +0300 Subject: [PATCH 316/756] feat(Portal): add prop for setting email placeholder --- static/js/NavSidebar.jsx | 5 ++++- static/js/NewsletterSignUpForm.jsx | 10 +++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/static/js/NavSidebar.jsx b/static/js/NavSidebar.jsx index 40f0c8c827..bfe7c39fef 100644 --- a/static/js/NavSidebar.jsx +++ b/static/js/NavSidebar.jsx @@ -830,7 +830,10 @@ const PortalNewsletter = ({title, title_url, description}) => { <Module> <a href={title_url}><ModuleTitle en={title.en} he={title.he} /></a> <InterfaceText markdown={{en: description.en, he: description.he}} /> - <NewsletterSignUpForm includeEducatorOption={false}/> + <NewsletterSignUpForm + includeEducatorOption={false} + emailPlaceholder={{en: "Email Address", he: "מייל"}} + /> </Module> ) }; diff --git a/static/js/NewsletterSignUpForm.jsx b/static/js/NewsletterSignUpForm.jsx index d78ecf724a..0ee8362901 100644 --- a/static/js/NewsletterSignUpForm.jsx +++ b/static/js/NewsletterSignUpForm.jsx @@ -2,7 +2,11 @@ import React, {useState} from 'react'; import Sefaria from './sefaria/sefaria'; import Cookies from "js-cookie"; -export function NewsletterSignUpForm({contextName, includeEducatorOption=true}) { +export function NewsletterSignUpForm({ + contextName, + includeEducatorOption=true, + emailPlaceholder={en: 'Sign up for Newsletter', he: "הרשמו לניוזלטר"}, +}) { const [email, setEmail] = useState(''); const [firstName, setFirstName] = useState(''); const [lastName, setLastName] = useState(''); @@ -65,7 +69,7 @@ export function NewsletterSignUpForm({contextName, includeEducatorOption=true}) <span className="int-en"> <input className="newsletterInput" - placeholder="Sign up for Newsletter" + placeholder={emailPlaceholder.en} value={email} onChange={e => setEmail(e.target.value)} onKeyUp={handleSubscribeKeyUp}/> @@ -73,7 +77,7 @@ export function NewsletterSignUpForm({contextName, includeEducatorOption=true}) <span className="int-he"> <input className="newsletterInput" - placeholder="הרשמו לניוזלטר" + placeholder={emailPlaceholder.he} value={email} onChange={e => setEmail(e.target.value)} onKeyUp={handleSubscribeKeyUp}/> From e133d78afb3632241b2c06eae9ee419efa004ff3 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Thu, 28 Sep 2023 22:16:17 +0300 Subject: [PATCH 317/756] refactor(Portal): pull out subscribe post request --- static/js/NewsletterSignUpForm.jsx | 35 +++++++++--------------------- static/js/sefaria/sefaria.js | 22 +++++++++++++++++++ 2 files changed, 32 insertions(+), 25 deletions(-) diff --git a/static/js/NewsletterSignUpForm.jsx b/static/js/NewsletterSignUpForm.jsx index 0ee8362901..a53bd0887e 100644 --- a/static/js/NewsletterSignUpForm.jsx +++ b/static/js/NewsletterSignUpForm.jsx @@ -3,10 +3,11 @@ import Sefaria from './sefaria/sefaria'; import Cookies from "js-cookie"; export function NewsletterSignUpForm({ - contextName, - includeEducatorOption=true, - emailPlaceholder={en: 'Sign up for Newsletter', he: "הרשמו לניוזלטר"}, -}) { + contextName, + includeEducatorOption = true, + emailPlaceholder = {en: 'Sign up for Newsletter', he: "הרשמו לניוזלטר"}, + onSubscribe, + }) { const [email, setEmail] = useState(''); const [firstName, setFirstName] = useState(''); const [lastName, setLastName] = useState(''); @@ -22,26 +23,9 @@ export function NewsletterSignUpForm({ function handleSubscribe() { if (showNameInputs === true) { // submit - if (firstName.length > 0 & lastName.length > 0) { + if (firstName.length > 0 && lastName.length > 0) { setSubscribeMessage("Subscribing..."); - const request = new Request( - '/api/subscribe/'+email, - {headers: {'X-CSRFToken': Cookies.get('csrftoken')}, - 'Content-Type': 'application/json'} - ); - fetch(request, - { - method: "POST", - mode: 'same-origin', - credentials: 'same-origin', - body: JSON.stringify({ - language: Sefaria.interfaceLang === "hebrew" ? "he" : "en", - educator: educatorCheck, - firstName: firstName, - lastName: lastName - }) - } - ).then(res => { + Sefaria.subscribeSefariaNewsletter(firstName, lastName, email, educatorCheck).then(res => { if ("error" in res) { setSubscribeMessage(res.error); setShowNameInputs(false); @@ -117,7 +101,8 @@ export function NewsletterSignUpForm({ onChange={e => setLastName(e.target.value)} onKeyUp={handleSubscribeKeyUp}/> </span> - {includeEducatorOption ? <EducatorCheckbox educatorCheck={educatorCheck} setEducatorCheck={setEducatorCheck} /> : null} + {includeEducatorOption ? + <EducatorCheckbox educatorCheck={educatorCheck} setEducatorCheck={setEducatorCheck}/> : null} <img src="/static/img/circled-arrow-right.svg" onClick={handleSubscribe}/> </> : null} @@ -129,7 +114,7 @@ export function NewsletterSignUpForm({ } -const EducatorCheckbox = ({ educatorCheck, setEducatorCheck }) => { +const EducatorCheckbox = ({educatorCheck, setEducatorCheck}) => { return ( <div className="newsletterEducatorOption"> <span className="int-en"> diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index 1b180ccfff..77b00d6fe0 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -579,6 +579,28 @@ Sefaria = extend(Sefaria, { Sefaria._portals[portalSlug] = response; return response; }, + subscribeSefariaNewsletter: function(firstName, lastName, email, educatorCheck) { + const request = new Request( + '/api/subscribe/' + email, + { + headers: {'X-CSRFToken': Cookies.get('csrftoken')}, + 'Content-Type': 'application/json' + } + ); + return fetch(request, + { + method: "POST", + mode: 'same-origin', + credentials: 'same-origin', + body: JSON.stringify({ + language: Sefaria.interfaceLang === "hebrew" ? "he" : "en", + educator: educatorCheck, + firstName: firstName, + lastName: lastName + }) + } + ); + }, filterVersionsObjByLangs: function(versionsObj, langs, includeFilter) { /** * @versionsObj {object} whode keys are language codes ('he', 'en' etc.) and values are version objects (like the object that getVersions returns) From 84fcbc69a19d6c27cc2bb43ffcc17da073c73faa Mon Sep 17 00:00:00 2001 From: Russel Neiss <russel.neiss@gmail.com> Date: Fri, 29 Sep 2023 11:36:58 -0500 Subject: [PATCH 318/756] static: update job page --- templates/static/jobs.html | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/templates/static/jobs.html b/templates/static/jobs.html index 1f7d84ce39..3470078211 100644 --- a/templates/static/jobs.html +++ b/templates/static/jobs.html @@ -74,6 +74,11 @@ <h2 class="anchorable">Learning</h2> </header> <section class="jobsListForDepartment"> <div class="job"><a class="jobLink" target="_blank" href="https://sefaria.breezy.hr/p/c693c3ab1b78-hebrew-editorial-associate-part-time">Hebrew Editorial Associate (Part-Time)</a></div> + <div class="job"><a class="jobLink" target="_blank" href="https://docs.google.com/document/d/107rwA4LuShD-szPmYJfKqyqxxU6Ycnj2KWqEUEc9GoU/edit#heading=h.hqg5tv31xe3k">English Editorial Associate (Contractor)</a></div> + + + + </section> </section> <!-- From baa15ca513eb54a181bc30b92c5946fb91d9195b Mon Sep 17 00:00:00 2001 From: Russel Neiss <russel.neiss@gmail.com> Date: Fri, 29 Sep 2023 12:58:16 -0500 Subject: [PATCH 319/756] static: Update Elise Ringo on Team Page [sc-20925] --- templates/static/en/team.html | 2 +- templates/static/he/team.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/static/en/team.html b/templates/static/en/team.html index 13196684eb..057fef4798 100644 --- a/templates/static/en/team.html +++ b/templates/static/en/team.html @@ -366,7 +366,7 @@ <h1> <span class="int-en">Elise Ringo</span> </div> <div class="teamTitle"> - <span class="int-en">Associate Digital Marketing Manager</span> + <span class="int-en">Database Administrator</span> </div> </div> </div> diff --git a/templates/static/he/team.html b/templates/static/he/team.html index 45345dd75a..e15922a752 100644 --- a/templates/static/he/team.html +++ b/templates/static/he/team.html @@ -456,7 +456,7 @@ <h1> <span class="int-he">יעל רינגו</span> </div> <div class="teamTitle"> - <span class="int-he">רכזת שיווק דיגיטלי</span> + <span class="int-he">מנהלת מאגר נתונים</span> </div> </div> </div> From cf5cc3c60969bed5db45c78dbff24f99dae4134e Mon Sep 17 00:00:00 2001 From: Nissa Mai-Rose <nwymai@gmail.com> Date: Mon, 2 Oct 2023 15:56:12 -0400 Subject: [PATCH 320/756] static(team.html): remove nissa from team page & remove nissa's image from static image files --- static/img/headshots/nissa.jpg | Bin 36407 -> 0 bytes templates/static/en/team.html | 14 +------------- templates/static/he/team.html | 13 ------------- 3 files changed, 1 insertion(+), 26 deletions(-) delete mode 100644 static/img/headshots/nissa.jpg diff --git a/static/img/headshots/nissa.jpg b/static/img/headshots/nissa.jpg deleted file mode 100644 index fd5a3f24079f9bee57cd29ccbe82b61254a73700..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36407 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 P8EXqu<rkIvZ}oox$I-*j diff --git a/templates/static/en/team.html b/templates/static/en/team.html index 13196684eb..aa337c7f33 100644 --- a/templates/static/en/team.html +++ b/templates/static/en/team.html @@ -1,3 +1,4 @@ + {% extends "base.html" %} {% load i18n static %} @@ -278,19 +279,6 @@ <h1> </div> </div> </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/nissa.jpg' %}" alt="Headshot of Nissa Mai-Rose"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Nissa Mai-Rose</span> - </div> - <div class="teamTitle"> - <span class="int-en">Sr. Software Engineer</span> - </div> - </div> - </div> <div class="teamMember"> <div class="teamMemberImage"> <img src="{% static 'img/headshots/Amanda_Minsky_Photo.png' %}" alt="Headshot of Amanda Minsky"> diff --git a/templates/static/he/team.html b/templates/static/he/team.html index 45345dd75a..184ad4c31b 100644 --- a/templates/static/he/team.html +++ b/templates/static/he/team.html @@ -265,19 +265,6 @@ <h1> </div> </div> </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/nissa.jpg' %}" alt="Headshot of Nissa Mai-Rose"> - </div> - <div class="teamMemberDetails"> - <div class="teamName sans"> - <span class="int-he">נסה מאי-רוז</span> - </div> - <div class="teamTitle"> - <span class="int-he">מהנדסת תוכנה בכירה</span> - </div> - </div> - </div> <div class="teamMember"> <div class="teamMemberImage"> <img src="{% static 'img/headshots/Amanda_Minsky_Photo.png' %}" alt="Headshot of Amanda Minsky"> From 3889757361b03d0c7ca89c2cdae8b7f0bb6e004f Mon Sep 17 00:00:00 2001 From: Russel Neiss <russel.neiss@gmail.com> Date: Fri, 6 Oct 2023 11:24:23 -0500 Subject: [PATCH 321/756] feat: Allow staff to delete sheets --- sefaria/forms.py | 4 +++ sefaria/urls.py | 19 ++++++------ sefaria/views.py | 59 ++++++++++++++++++++++++++++++++++--- templates/delete-sheet.html | 24 +++++++++++++++ 4 files changed, 93 insertions(+), 13 deletions(-) create mode 100644 templates/delete-sheet.html diff --git a/sefaria/forms.py b/sefaria/forms.py index 8d72976778..4268968211 100644 --- a/sefaria/forms.py +++ b/sefaria/forms.py @@ -30,6 +30,10 @@ class SefariaDeleteUserForm(EmailAuthenticationForm): email = forms.EmailField(max_length=75, widget=forms.EmailInput(attrs={'placeholder': _("Email Address to delete")})) password = forms.CharField(widget=forms.PasswordInput(attrs={'placeholder': _("Admin Password")})) +class SefariaDeleteSheet(forms.Form): + sid = forms.CharField(max_length=20, widget=forms.TextInput(attrs={'placeholder': _("Sheet ID to delete")})) + password = forms.CharField(widget=forms.PasswordInput(attrs={'placeholder': _("Admin Password")})) + class SefariaLoginForm(EmailAuthenticationForm): email = forms.EmailField(max_length=75, widget=forms.EmailInput(attrs={'placeholder': _("Email Address")})) diff --git a/sefaria/urls.py b/sefaria/urls.py index b97e18771a..54e3342dfb 100644 --- a/sefaria/urls.py +++ b/sefaria/urls.py @@ -85,12 +85,12 @@ url(r'^settings/account?$', reader_views.account_settings), url(r'^settings/profile?$', reader_views.edit_profile), url(r'^interface/(?P<language>english|hebrew)$', reader_views.interface_language_redirect), - url(r'^api/profile/user_history$', reader_views.user_history_api), + url(r'^api/profile/user_history$', reader_views.user_history_api), url(r'^api/profile/sync$', reader_views.profile_sync_api), url(r'^api/profile/upload-photo$', reader_views.profile_upload_photo), url(r'^api/profile$', reader_views.profile_api), url(r'^settings/account/user$', reader_views.account_user_update), - url(r'^api/profile/(?P<slug>[^/]+)$', reader_views.profile_get_api), + url(r'^api/profile/(?P<slug>[^/]+)$', reader_views.profile_get_api), url(r'^api/profile/(?P<slug>[^/]+)/(?P<ftype>followers|following)$', reader_views.profile_follow_api), url(r'^api/user_history/saved$', reader_views.saved_history_for_ref), ] @@ -98,7 +98,7 @@ # Topics urlpatterns += [ url(r'^topics/category/(?P<topicCategory>.+)?$', reader_views.topics_category_page), - url(r'^topics/all/(?P<letter>.)$', reader_views.all_topics_page), + url(r'^topics/all/(?P<letter>.)$', reader_views.all_topics_page), url(r'^topics/?$', reader_views.topics_page), url(r'^topics/b/(?P<topic>.+)$', reader_views.topic_page_b), url(r'^topics/(?P<topic>.+)$', reader_views.topic_page), @@ -189,9 +189,9 @@ url(r'^api/sheets/?$', sheets_views.save_sheet_api), url(r'^api/sheets/(?P<sheet_id>\d+)/delete$', sheets_views.delete_sheet_api), url(r'^api/sheets/(?P<sheet_id>\d+)/add$', sheets_views.add_source_to_sheet_api), - url(r'^api/sheets/(?P<sheet_id>\d+)/add_ref$', sheets_views.add_ref_to_sheet_api), + url(r'^api/sheets/(?P<sheet_id>\d+)/add_ref$', sheets_views.add_ref_to_sheet_api), url(r'^api/sheets/(?P<parasha>.+)/get_aliyot$', sheets_views.get_aliyot_by_parasha_api), - url(r'^api/sheets/(?P<sheet_id>\d+)/copy_source$', sheets_views.copy_source_to_sheet_api), + url(r'^api/sheets/(?P<sheet_id>\d+)/copy_source$', sheets_views.copy_source_to_sheet_api), url(r'^api/sheets/(?P<sheet_id>\d+)/topics$', sheets_views.update_sheet_topics_api), url(r'^api/sheets/(?P<sheet_id>\d+)$', sheets_views.sheet_api), url(r'^api/sheets/(?P<sheet_id>\d+)\.(?P<node_id>\d+)$', sheets_views.sheet_node_api), @@ -229,7 +229,7 @@ url(r'^api/collections/for-sheet/(?P<sheet_id>\d+)$', sheets_views.collections_for_sheet_api), url(r'^api/collections(/(?P<slug>[^/]+))?$', sheets_views.collections_api), url(r'^api/collections/(?P<slug>[^/]+)/set-role/(?P<uid>\d+)/(?P<role>[^/]+)$', sheets_views.collections_role_api), - url(r'^api/collections/(?P<slug>[^/]+)/invite/(?P<uid_or_email>[^/]+)(?P<uninvite>\/uninvite)?$', sheets_views.collections_invite_api), + url(r'^api/collections/(?P<slug>[^/]+)/invite/(?P<uid_or_email>[^/]+)(?P<uninvite>\/uninvite)?$', sheets_views.collections_invite_api), url(r'^api/collections/(?P<slug>[^/]+)/(?P<action>(add|remove))/(?P<sheet_id>\d+)', sheets_views.collections_inclusion_api), url(r'^api/collections/(?P<slug>[^/]+)/(?P<action>(add|remove))/(?P<sheet_id>\d+)', sheets_views.collections_inclusion_api), url(r'^api/collections/(?P<slug>[^/]+)/pin-sheet/(?P<sheet_id>\d+)', sheets_views.collections_pin_sheet_api), @@ -285,8 +285,8 @@ urlpatterns += [ url(r'^api/locktext/(?P<title>.+)/(?P<lang>\w\w)/(?P<version>.+)$', reader_views.lock_text_api), url(r'^api/version/flags/(?P<title>.+)/(?P<lang>\w\w)/(?P<version>.+)$', reader_views.flag_text_api), -] -# SEC-AUDIT: do we also want to maybe move these to 'admin' +] +# SEC-AUDIT: do we also want to maybe move these to 'admin' # Discussions urlpatterns += [ @@ -389,7 +389,7 @@ ] # Admin -urlpatterns += [ +urlpatterns += [ url(r'^admin/reset/varnish/(?P<tref>.+)$', sefaria_views.reset_varnish), url(r'^admin/reset/cache$', sefaria_views.reset_cache), url(r'^admin/reset/cache/(?P<title>.+)$', sefaria_views.reset_index_cache_for_text), @@ -403,6 +403,7 @@ url(r'^admin/reset-websites-data', sefaria_views.reset_websites_data), url(r'^admin/delete/orphaned-counts', sefaria_views.delete_orphaned_counts), url(r'^admin/delete/user-account', sefaria_views.delete_user_by_email, name="delete/user-account"), + url(r'^admin/delete/sheet$', sefaria_views.delete_sheet_by_id, name="delete/sheet"), url(r'^admin/rebuild/auto-links/(?P<title>.+)$', sefaria_views.rebuild_auto_links), url(r'^admin/rebuild/citation-links/(?P<title>.+)$', sefaria_views.rebuild_citation_links), url(r'^admin/delete/citation-links/(?P<title>.+)$', sefaria_views.delete_citation_links), diff --git a/sefaria/views.py b/sefaria/views.py index ab42bd7ff0..2ec8c6648c 100644 --- a/sefaria/views.py +++ b/sefaria/views.py @@ -38,10 +38,11 @@ from sefaria.helper.crm.crm_mediator import CrmMediator from sefaria.system.cache import in_memory_cache from sefaria.client.util import jsonResponse, send_email, read_webpack_bundle -from sefaria.forms import SefariaNewUserForm, SefariaNewUserFormAPI, SefariaDeleteUserForm +from sefaria.forms import SefariaNewUserForm, SefariaNewUserFormAPI, SefariaDeleteUserForm, SefariaDeleteSheet from sefaria.settings import MAINTENANCE_MESSAGE, USE_VARNISH, MULTISERVER_ENABLED from sefaria.model.user_profile import UserProfile, user_link -from sefaria.model.collection import CollectionSet +from sefaria.model.collection import CollectionSet, process_sheet_deletion_in_collections +from sefaria.model.notification import process_sheet_deletion_in_notifications from sefaria.export import export_all as start_export_all from sefaria.datatype.jagged_array import JaggedTextArray # noinspection PyUnresolvedReferences @@ -58,7 +59,7 @@ from sefaria.system.multiserver.coordinator import server_coordinator from sefaria.google_storage_manager import GoogleStorageManager from sefaria.sheets import get_sheet_categorization_info -from reader.views import base_props, render_template +from reader.views import base_props, render_template from sefaria.helper.link import add_links_from_csv, delete_links_from_text, get_csv_links_by_refs if USE_VARNISH: @@ -202,7 +203,7 @@ def unlink_gauth(request): return redirect(f"/profile/{profile.slug}") else: return jsonResponse({"status": "ok"}) - except: + except: return jsonResponse({"error": "Failed to delete Google account"}) @@ -971,6 +972,56 @@ def delete_user_by_email(request): +@staff_member_required +def delete_sheet_by_id(request): + + from django.contrib.auth.models import User + from sefaria.utils.user import delete_user_account + if request.method == 'GET': + form = SefariaDeleteSheet() + return render_template(request, "delete-sheet.html", None, {'form': form, 'next': next}) + elif request.method == 'POST': + user = User.objects.get(id=request.user.id) + sheet_id = request.POST.get("sid") + password = request.POST.get("password") + try: + if not user.check_password(password): + return jsonResponse({"failure": "incorrect password"}) + except: + return jsonResponse({"failure": "incorrect password"}) + try: + + import sefaria.search as search + id = int(sheet_id) + sheet = db.sheets.find_one({"id": id}) + if not sheet: + return jsonResponse({"error": "Sheet %d not found." % id}) + + db.sheets.remove({"id": id}) + process_sheet_deletion_in_collections(id) + process_sheet_deletion_in_notifications(id) + + # try: + # es_index_name = search.get_new_and_current_index_names("sheet")['current'] + # search.delete_sheet(es_index_name, id) + # except NewConnectionError as e: + # logger.warn("Failed to connect to elastic search server on sheet delete.") + # except AuthorizationException as e: + # logger.warn("Failed to connect to elastic search server on sheet delete.") + + + return jsonResponse({"success": f"deleted sheet {sheet_id}"}) + + except: + return jsonResponse({"failure": "sheet not deleted: try again or contact a developer"}) + + + + + + + + def purge_spammer_account_data(spammer_id, delete_from_crm=True): from django.contrib.auth.models import User diff --git a/templates/delete-sheet.html b/templates/delete-sheet.html new file mode 100644 index 0000000000..7ea7165ab1 --- /dev/null +++ b/templates/delete-sheet.html @@ -0,0 +1,24 @@ +{% extends "base.html" %} +{% load i18n %} + +{% block title %}{% trans "Admin: Delete a Sheet" %} | {% trans "Sefaria" %}{% endblock %} + +{% block content %} + +<div id="deleteSheet" class="static"> + <div class="inner"> + <h1> + <span class="int-en">Delete Sheet</span> + </h1> + <h2> CAUTION THIS WILL DELETE THE SHEET ID YOU INPUT</h2> + <h3>IT CANNOT BE UNDONE.</h3> + <form method="post" autocomplete="off" if="delete-form" action="{% url "delete/sheet" %}"> + {% csrf_token %} + {{ form.as_p }} + <button type="submit" class="button control-elem"> + <span class="int-en">Delete Sheet</span> + </button> + </form> + </div> +</div> +{% endblock %} From 4dc9e02eb2863d6e835e0ff2dbe14cf9ee9afb37 Mon Sep 17 00:00:00 2001 From: Russel Neiss <russel.neiss@gmail.com> Date: Fri, 6 Oct 2023 11:37:51 -0500 Subject: [PATCH 322/756] fix: remove sheets from index when deleted --- sefaria/views.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sefaria/views.py b/sefaria/views.py index 2ec8c6648c..6aa958f36f 100644 --- a/sefaria/views.py +++ b/sefaria/views.py @@ -1001,13 +1001,13 @@ def delete_sheet_by_id(request): process_sheet_deletion_in_collections(id) process_sheet_deletion_in_notifications(id) - # try: - # es_index_name = search.get_new_and_current_index_names("sheet")['current'] - # search.delete_sheet(es_index_name, id) - # except NewConnectionError as e: - # logger.warn("Failed to connect to elastic search server on sheet delete.") - # except AuthorizationException as e: - # logger.warn("Failed to connect to elastic search server on sheet delete.") + try: + es_index_name = search.get_new_and_current_index_names("sheet")['current'] + search.delete_sheet(es_index_name, id) + except NewConnectionError as e: + logger.warn("Failed to connect to elastic search server on sheet delete.") + except AuthorizationException as e: + logger.warn("Failed to connect to elastic search server on sheet delete.") return jsonResponse({"success": f"deleted sheet {sheet_id}"}) From 0a1ccb01c8f601a1f4f6709929ebe0a7d2610330 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Wed, 11 Oct 2023 17:05:54 -0400 Subject: [PATCH 323/756] fix: Banners won't show up again after the banner button is clicked --- static/js/Misc.jsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 367a778584..29a181a6e9 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -2426,18 +2426,29 @@ const Banner = ({ onClose }) => { <a className="button white int-en" href={strapi.banner.buttonURL.en} + onClick={() => { + closeBanner("banner_button_clicked"); + }} > <span>{strapi.banner.buttonText.en}</span> </a> <a className="button white int-he" href={strapi.banner.buttonURL.he} + onClick={() => { + closeBanner("banner_button_clicked"); + }} > <span>{strapi.banner.buttonText.he}</span> </a> </div> </div> - <div id="bannerMessageClose" onClick={closeBanner}> + <div + id="bannerMessageClose" + onClick={() => { + closeBanner("close_clicked"); + }} + > × </div> </div> From 466e674d904774a1bc0acf7dfaed7a2748d5bbd2 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Wed, 11 Oct 2023 17:47:36 -0400 Subject: [PATCH 324/756] fix: Don't show banners on Sefaria pages where the banner or modal button link to. This will do nothing for external links --- static/js/Misc.jsx | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 29a181a6e9..7468ab72f4 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -2217,8 +2217,14 @@ const InterruptingMessage = ({ shouldShowModal = true; else if (noUserKindIsSet) shouldShowModal = true; if (!shouldShowModal) return false; - + // Don't show the modal on pages where the button link goes to since you're already there const excludedPaths = ["/donate", "/mobile", "/app", "/ways-to-give"]; + if (strapi.modal.buttonURL) { + excludedPaths.push( + new URL(strapi.modal.buttonURL.en).pathname, + new URL(strapi.modal.buttonURL.he).pathname + ); + } return excludedPaths.indexOf(window.location.pathname) === -1; }; @@ -2377,6 +2383,13 @@ const Banner = ({ onClose }) => { if (!shouldShowBanner) return false; const excludedPaths = ["/donate", "/mobile", "/app", "/ways-to-give"]; + // Don't show the banner on pages where the button link goes to since you're already there + if (strapi.banner.buttonURL) { + excludedPaths.push( + new URL(strapi.banner.buttonURL.en).pathname, + new URL(strapi.banner.buttonURL.he).pathname + ); + } return excludedPaths.indexOf(window.location.pathname) === -1; }; From 4a3ded1ae68a39b2a063c48aa6c444626c5adbaf Mon Sep 17 00:00:00 2001 From: Brendan Galloway <brendan@flanksource.com> Date: Mon, 16 Oct 2023 12:29:23 +0200 Subject: [PATCH 325/756] helm(fix): correct bug in mongo destroy logic --- .../sefaria-project/templates/configmap/mongo-destroy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helm-chart/sefaria-project/templates/configmap/mongo-destroy.yaml b/helm-chart/sefaria-project/templates/configmap/mongo-destroy.yaml index 6874c016c4..edbd30be88 100644 --- a/helm-chart/sefaria-project/templates/configmap/mongo-destroy.yaml +++ b/helm-chart/sefaria-project/templates/configmap/mongo-destroy.yaml @@ -37,8 +37,8 @@ data: DB_NAME=$SEFARIA_DB {{ end }} - URI="${URI}${MONGO_HOST}/${DATABASE}?ssl=false&authSource=admin" APSCHEDULER_URI="${URI}${MONGO_HOST}/${APSCHEDULER_NAME}?ssl=false&authSource=admin" + URI="${URI}${MONGO_HOST}/${DATABASE}?ssl=false&authSource=admin" if [[ ! -z "$MONGO_REPLICASET_NAME" ]]; then URI="${URI}&replicaSet=${MONGO_REPLICASET_NAME}" From 73dee9d4c01668088187396c8eb2f84525f5be4b Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Mon, 16 Oct 2023 14:03:53 +0300 Subject: [PATCH 326/756] fix(Index Save): validate compDate and pubDate are lists of integers --- sefaria/model/text.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index 6346fd5374..40c34ce3a8 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -687,6 +687,12 @@ def _validate(self): if not Category().load({"path": self.categories}): raise InputError("You must create category {} before adding texts to it.".format("/".join(self.categories))) + for dateKey in ['compDate', 'pubDate']: + if hasattr(self, dateKey): + val = getattr(self, dateKey) + if not isinstance(val, list) or not all([isinstance(x, int) for x in val]): + raise InputError(f"Optional attribute '{dateKey}' must be list of integers.") + ''' for cat in self.categories: if not hebrew_term(cat): From f23c3bba61605956bf261c943c9572504ffefc2a Mon Sep 17 00:00:00 2001 From: yonadavGit <yonadav.le@gmail.com> Date: Mon, 16 Oct 2023 21:17:45 +0300 Subject: [PATCH 327/756] static(topic page) - Update topic page descriptions --- static/js/NavSidebar.jsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/static/js/NavSidebar.jsx b/static/js/NavSidebar.jsx index 83643d7ca5..215459b3b5 100644 --- a/static/js/NavSidebar.jsx +++ b/static/js/NavSidebar.jsx @@ -486,12 +486,11 @@ const AboutTopics = ({hideTitle}) => ( <ModuleTitle>About Topics</ModuleTitle> } <InterfaceText> <HebrewText> -בדפי הנושא מלוקטים מקורות נבחרים ודפי מקורות של משתמשים על נושא מסוים. המקורות המופיעים בדפי הנושא נאספים ממאגרים קיימים של ספרות יהודית (דוגמת 'אספקלריא') ומתוך דפי מקורות פומביים של משתמשי ספריא. +דפי הנושא מציגים מקורות נבחרים מארון הספרים היהודי עבור אלפי נושאים. ניתן לדפדף לפי קטגוריה או לחפש לפי נושא ספציפי, ובסרגל הצד מוצגים הנושאים הפופולריים ביותר ואלה הקשורים אליהם. הקליקו ושוטטו בין הנושאים השונים כדי ללמוד עוד. </HebrewText> <EnglishText> - Topics bring you straight to selections of texts and user created source sheets about thousands of subjects. Sources that appear are drawn from existing indices of Jewish texts (like Aspaklaria) and from the sources our users include on their public source sheets. + Topics Pages present a curated selection of various genres of sources on thousands of chosen subjects. You can browse by category, search for something specific, or view the most popular topics — and related topics — on the sidebar. Explore and click through to learn more. </EnglishText> - </InterfaceText> </Module> ); From ac5503d6864333864a3c39632ad1815124480ab7 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Tue, 17 Oct 2023 10:12:03 +0300 Subject: [PATCH 328/756] chore: fix style --- sefaria/model/text.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index 40c34ce3a8..d8484b59b7 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -687,11 +687,11 @@ def _validate(self): if not Category().load({"path": self.categories}): raise InputError("You must create category {} before adding texts to it.".format("/".join(self.categories))) - for dateKey in ['compDate', 'pubDate']: - if hasattr(self, dateKey): - val = getattr(self, dateKey) + for date_key in ['compDate', 'pubDate']: + if hasattr(self, date_key): + val = getattr(self, date_key) if not isinstance(val, list) or not all([isinstance(x, int) for x in val]): - raise InputError(f"Optional attribute '{dateKey}' must be list of integers.") + raise InputError(f"Optional attribute '{date_key}' must be list of integers.") ''' for cat in self.categories: From d55a9fe81faec6e8ad44f6d879d46d441e96dfa8 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Tue, 17 Oct 2023 10:40:32 +0300 Subject: [PATCH 329/756] fix(Portal): fix some bugs in sefaria newsletter subscribe api request. --- static/js/NewsletterSignUpForm.jsx | 14 ++++-------- static/js/sefaria/sefaria.js | 35 ++++++++++++++++++++++-------- 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/static/js/NewsletterSignUpForm.jsx b/static/js/NewsletterSignUpForm.jsx index a53bd0887e..5c798bf856 100644 --- a/static/js/NewsletterSignUpForm.jsx +++ b/static/js/NewsletterSignUpForm.jsx @@ -1,6 +1,5 @@ import React, {useState} from 'react'; import Sefaria from './sefaria/sefaria'; -import Cookies from "js-cookie"; export function NewsletterSignUpForm({ contextName, @@ -26,15 +25,10 @@ export function NewsletterSignUpForm({ if (firstName.length > 0 && lastName.length > 0) { setSubscribeMessage("Subscribing..."); Sefaria.subscribeSefariaNewsletter(firstName, lastName, email, educatorCheck).then(res => { - if ("error" in res) { - setSubscribeMessage(res.error); - setShowNameInputs(false); - } else { - setSubscribeMessage("Subscribed! Welcome to our list."); - Sefaria.track.event("Newsletter", "Subscribe from " + contextName, ""); - } - }).catch(data => { - setSubscribeMessage("Sorry, there was an error."); + setSubscribeMessage("Subscribed! Welcome to our list."); + Sefaria.track.event("Newsletter", "Subscribe from " + contextName, ""); + }).catch(error => { + setSubscribeMessage(error?.error || "Sorry, there was an error."); setShowNameInputs(false); }); } else { diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index 77b00d6fe0..ce969d5640 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -9,6 +9,7 @@ import Track from './track'; import Hebrew from './hebrew'; import Util from './util'; import $ from './sefariaJquery'; +import Cookies from "js-cookie"; import {useContext} from "react"; import {ContentLanguageContext} from "../context"; @@ -579,18 +580,15 @@ Sefaria = extend(Sefaria, { Sefaria._portals[portalSlug] = response; return response; }, - subscribeSefariaNewsletter: function(firstName, lastName, email, educatorCheck) { - const request = new Request( - '/api/subscribe/' + email, - { - headers: {'X-CSRFToken': Cookies.get('csrftoken')}, - 'Content-Type': 'application/json' - } - ); - return fetch(request, + subscribeSefariaNewsletter: async function(firstName, lastName, email, educatorCheck) { + const response = await fetch(`/api/subscribe/${email}`, { method: "POST", mode: 'same-origin', + headers: { + 'Content-Type': 'application/json', + 'X-CSRFToken': Cookies.get('csrftoken'), + }, credentials: 'same-origin', body: JSON.stringify({ language: Sefaria.interfaceLang === "hebrew" ? "he" : "en", @@ -600,6 +598,25 @@ Sefaria = extend(Sefaria, { }) } ); + if (!response.ok) { throw "error"; } + const json = await response.json(); + if (json.error) { throw json; } + return json; + }, + subscribeSteinsaltzNewsletter: async function(firstName, lastName, email) { + const response = fetch('https://steinsaltz-center.org/api/mailer', + { + method: "POST", + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify({ + first_name: firstName, + last_name: lastName, + email: email, + }) + } + ); + if (!response.ok) { throw "error"; } + return await response.json(); }, filterVersionsObjByLangs: function(versionsObj, langs, includeFilter) { /** From 9eb192d0f815aa3ea090336e9d22183090c9176f Mon Sep 17 00:00:00 2001 From: Palash Dhabale <100768184+PalashDhabale@users.noreply.github.com> Date: Tue, 17 Oct 2023 19:17:17 +0530 Subject: [PATCH 330/756] remove typo --- README.mkd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.mkd b/README.mkd index 582de41930..49b49c8eef 100644 --- a/README.mkd +++ b/README.mkd @@ -108,7 +108,7 @@ If the server isn't running, you may need to run `docker-compose up` again. #### Run locally #### 1) Install Python 3.7 -*We Recommend using the latest Python 3.7 as opposed to later versions of Python (esp 3.10 and up) since it has been known to cause some compatability issues. These are solvable, but for an easier install experience, we currently recommend 3.7* +*We Recommend using the latest Python 3.7 as opposed to later versions of Python (esp 3.10 and up) since it has been known to cause some compatibility issues. These are solvable, but for an easier install experience, we currently recommend 3.7* ###### Linux and macOS Most UNIX systems come with a python interpreter pre-installed. However, this is generally still Python 2. The recommended way to get Python 3, and not mess up any software the OS is dependent on, is by using Pyenv. You can use the instructions [here](https://github.com/pyenv/pyenv#installation) and also [here](https://opensource.com/article/19/5/python-3-default-mac#what-we-should-do). From 17969d178d8d6bf2c21f3eb5e63d350df16e01de Mon Sep 17 00:00:00 2001 From: chavaerica <68666118+chavaerica@users.noreply.github.com> Date: Thu, 19 Oct 2023 14:11:26 -0400 Subject: [PATCH 331/756] Update jobs.html --- templates/static/jobs.html | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/templates/static/jobs.html b/templates/static/jobs.html index 3470078211..4ce8b1ff0f 100644 --- a/templates/static/jobs.html +++ b/templates/static/jobs.html @@ -16,7 +16,7 @@ <h1 class="serif"> </h1> <!-- Comment out when jobs page has no content --> -<!-- <h2> + <h2> <span class="int-en">About Sefaria</span> <span class="int-he">אודות ספריא</span> </h2> @@ -31,7 +31,7 @@ <h1 class="serif"> ספריא היא ארגון ללא מטרות רווח שמטרתו יצירת הדור הבא של לימוד התורה באופן פתוח ומשותף. אנחנו בספריא מרכיבים ספרייה חיה וחופשית של טקסטים יהודיים וכלל הקישורים ביניהם, בשפת המקור ובתרגומים. </span> - </p> --> + </p> <!-- Comment out when jobs page has no content --> </header> @@ -52,12 +52,13 @@ <h2 class="anchorable">Engineering</h2> <section class="jobsListForDepartment"> </section> </section> - <section class="section department englishOnly"> + <section class="section department englishOnly"> <header> - <h2 class="anchorable">Marketing and Communications</h2> + <h2 class="anchorable">Learning</h2> </header> <section class="jobsListForDepartment"> - <div class="job"><a class="" target="_blank" href=""></a></div> + <div class="job"><a class="jobLink" target="_blank" href=""></a></div> + <div class="job"><a class="jobLink" target="_blank" href=""></a></div> </section> </section> <section class="section department englishOnly"> @@ -68,19 +69,19 @@ <h2 class="anchorable">HR and Operations</h2> <div class="job"><a class="" target="_blank" href=""></a></div> </section> </section> --> - <section class="section department englishOnly"> + + <section class="section department englishOnly"> <header> - <h2 class="anchorable">Learning</h2> + <h2 class="anchorable">Marketing and Communications</h2> </header> <section class="jobsListForDepartment"> - <div class="job"><a class="jobLink" target="_blank" href="https://sefaria.breezy.hr/p/c693c3ab1b78-hebrew-editorial-associate-part-time">Hebrew Editorial Associate (Part-Time)</a></div> - <div class="job"><a class="jobLink" target="_blank" href="https://docs.google.com/document/d/107rwA4LuShD-szPmYJfKqyqxxU6Ycnj2KWqEUEc9GoU/edit#heading=h.hqg5tv31xe3k">English Editorial Associate (Contractor)</a></div> - + <div class="job"><a class="joblink" target="_blank" href="https://sefaria.breezy.hr/p/b11c89877ad6-communications-specialist?state=published">Communications Specialist</a></div> + </section> + </section> - </section> - </section> + <!-- <section class="section department englishOnly"> <header> From 2f8bc2b6eae6b0fb4c4d69c1b053296f45305e1f Mon Sep 17 00:00:00 2001 From: Yishai Glasner <yishaiglasner@Yishais-MacBook-Pro.local> Date: Sun, 22 Oct 2023 16:58:06 +0300 Subject: [PATCH 332/756] chore(requirements): requirements according to opreation system - do not install gevert on mac; use different versions of pillow for linux and non-linux --- requirements.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index f72e797543..09e88fc88e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -26,7 +26,7 @@ elasticsearch==7.9.1 elasticsearch_dsl==7.3.0 geojson==2.5.0 geopy==2.3.0 -gevent==20.12.0 +gevent==20.12.0; sys_platform != 'darwin' google-api-python-client==1.12.5 google-cloud-logging==1.15.1 google-cloud-storage==1.32.0 @@ -42,7 +42,8 @@ google-auth==1.24.0 google-auth-oauthlib==0.4.2 p929==0.6.1 pathos==0.2.6 -pillow==8.0.1 +pillow==8.0.1; sys_platform == 'linux' +pillow==10.0.1; sys_platform != 'linux' psycopg2==2.8.6 py2-py3-django-email-as-username==1.7.1 pymongo==3.12.* From 9c3261bf99fe594076e20c833c8110cd387e8abe Mon Sep 17 00:00:00 2001 From: Ephraim <ephraim@sefaria.org> Date: Mon, 23 Oct 2023 10:32:42 +0300 Subject: [PATCH 333/756] static(jobs): Fix some html spacing --- templates/static/jobs.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/static/jobs.html b/templates/static/jobs.html index 4ce8b1ff0f..859ee3fa9e 100644 --- a/templates/static/jobs.html +++ b/templates/static/jobs.html @@ -16,7 +16,7 @@ <h1 class="serif"> </h1> <!-- Comment out when jobs page has no content --> - <h2> + <h2> <span class="int-en">About Sefaria</span> <span class="int-he">אודות ספריא</span> </h2> @@ -70,7 +70,7 @@ <h2 class="anchorable">HR and Operations</h2> </section> </section> --> - <section class="section department englishOnly"> + <section class="section department englishOnly"> <header> <h2 class="anchorable">Marketing and Communications</h2> </header> From f0534ed00953105f37cac2a4b0cc22ef8f40765a Mon Sep 17 00:00:00 2001 From: Ephraim <ephraim@sefaria.org> Date: Mon, 23 Oct 2023 11:20:16 +0300 Subject: [PATCH 334/756] fix: Temporarily disable otel instrumentation on deploy --- build/ci/production-values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/ci/production-values.yaml b/build/ci/production-values.yaml index 65644e2863..f55f4fcc41 100644 --- a/build/ci/production-values.yaml +++ b/build/ci/production-values.yaml @@ -193,7 +193,7 @@ secrets: slackWebhook: ref: slack-webhook-production instrumentation: - enabled: true + enabled: false otelEndpoint: "http://otel-collector-collector.monitoring:4317" jaegerEndpoint: "jaeger-agent-dev.monitoring.svc.cluster.local:6831" From 3b5f63bf062ce8866f39f9ceb231116b0d8905eb Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Mon, 23 Oct 2023 12:35:30 +0300 Subject: [PATCH 335/756] fix(Topic Editor): fail gracefully when birthPlace doesnt correspond to a Place --- sefaria/model/topic.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sefaria/model/topic.py b/sefaria/model/topic.py index 0127bc723b..b259b88311 100644 --- a/sefaria/model/topic.py +++ b/sefaria/model/topic.py @@ -412,8 +412,9 @@ def annotate_place(self, d): if place and heKey not in properties: value, dataSource = place['value'], place['dataSource'] place_obj = Place().load({"key": value}) - name = place_obj.primary_name('he') - d['properties'][heKey] = {'value': name, 'dataSource': dataSource} + if place_obj: + name = place_obj.primary_name('he') + d['properties'][heKey] = {'value': name, 'dataSource': dataSource} return d def contents(self, **kwargs): From 80065ff2aa5df20c67ccdccbc778adcdf0c9152d Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Mon, 23 Oct 2023 12:38:45 +0300 Subject: [PATCH 336/756] feat(Portal): create proxy api for steinsaltz newsletter signup. This solves the issue of cross origin requests. --- requirements.txt | 1 + sefaria/urls.py | 3 +- sefaria/views.py | 52 ++++++++++++++++++++++++------ static/js/NewsletterSignUpForm.jsx | 2 +- static/js/sefaria/sefaria.js | 26 ++++++++++----- 5 files changed, 65 insertions(+), 19 deletions(-) diff --git a/requirements.txt b/requirements.txt index f72e797543..322ce938fd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -63,6 +63,7 @@ user-agents==2.2.0 sentry-sdk==1.26.0 babel python-bidi +requests opentelemetry-distro opentelemetry-exporter-otlp diff --git a/sefaria/urls.py b/sefaria/urls.py index 38fea43faf..0daf1bf364 100644 --- a/sefaria/urls.py +++ b/sefaria/urls.py @@ -390,7 +390,8 @@ # Email Subscribe urlpatterns += [ - url(r'^api/subscribe/(?P<email>.+)$', sefaria_views.subscribe), + url(r'^api/subscribe/steinsaltz/(?P<email>.+)$', sefaria_views.subscribe_steinsaltz_newsletter_view), + url(r'^api/subscribe/(?P<email>.+)$', sefaria_views.subscribe_sefaria_newsletter_view), ] # Admin diff --git a/sefaria/views.py b/sefaria/views.py index ab42bd7ff0..82c876bde3 100644 --- a/sefaria/views.py +++ b/sefaria/views.py @@ -169,28 +169,62 @@ def accounts(request): }) -def subscribe(request, email): +def generic_subscribe_to_newsletter_api(request, email, subscribe): """ - API for subscribing to mailing lists, in `lists` url param. - Currently active lists are: - "Announcements_General", "Announcements_General_Hebrew", "Announcements_Edu", "Announcements_Edu_Hebrew" + Generic view for subscribing a user to a newsletter """ body = json.loads(request.body) - language = body.get("language", "") - educator = body.get("educator", False) first_name = body.get("firstName", None) last_name = body.get("lastName", None) try: - crm_mediator = CrmMediator() - if crm_mediator.subscribe_to_lists(email, first_name, last_name, educator=educator, lang=language): + if subscribe(request, email, first_name, last_name): return jsonResponse({"status": "ok"}) else: - logger.error("Failed to subscribe to list") + logger.error(f"Failed to subscribe to list") return jsonResponse({"error": _("Sorry, there was an error.")}) except ValueError as e: logger.error(f"Failed to subscribe to list: {e}") return jsonResponse({"error": _("Sorry, there was an error.")}) + +def subscribe_sefaria_newsletter_view(request, email): + return generic_subscribe_to_newsletter_api(request, email, subscribe_sefaria_newsletter) + + +def subscribe_steinsaltz_newsletter_view(request, email): + return generic_subscribe_to_newsletter_api(request, email, subscribe_steinsaltz) + + +def subscribe_sefaria_newsletter(request, email, first_name, last_name): + """ + API for subscribing to mailing lists, in `lists` url param. + Currently active lists are: + "Announcements_General", "Announcements_General_Hebrew", "Announcements_Edu", "Announcements_Edu_Hebrew" + """ + body = json.loads(request.body) + language = body.get("language", "") + educator = body.get("educator", False) + crm_mediator = CrmMediator() + return crm_mediator.subscribe_to_lists(email, first_name, last_name, educator=educator, lang=language) + + +def subscribe_steinsaltz(request, email, first_name, last_name): + """ + API for subscribing to Steinsaltz newsletter + """ + import requests + + data = { + "first_name": first_name, + "last_name": last_name, + "email": email, + } + headers = {'Content-Type': 'application/json'} + response = requests.post('https://steinsaltz-center.org/api/mailer', + data=json.dumps(data), headers=headers) + return response.ok + + @login_required def unlink_gauth(request): profile = UserProfile(id=request.user.id) diff --git a/static/js/NewsletterSignUpForm.jsx b/static/js/NewsletterSignUpForm.jsx index 5c798bf856..8370c60049 100644 --- a/static/js/NewsletterSignUpForm.jsx +++ b/static/js/NewsletterSignUpForm.jsx @@ -24,7 +24,7 @@ export function NewsletterSignUpForm({ if (showNameInputs === true) { // submit if (firstName.length > 0 && lastName.length > 0) { setSubscribeMessage("Subscribing..."); - Sefaria.subscribeSefariaNewsletter(firstName, lastName, email, educatorCheck).then(res => { + Sefaria.subscribeSefariaAndSteinsaltzNewsletter(firstName, lastName, email, educatorCheck).then(res => { setSubscribeMessage("Subscribed! Welcome to our list."); Sefaria.track.event("Newsletter", "Subscribe from " + contextName, ""); }).catch(error => { diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index ce969d5640..913d607b33 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -604,19 +604,29 @@ Sefaria = extend(Sefaria, { return json; }, subscribeSteinsaltzNewsletter: async function(firstName, lastName, email) { - const response = fetch('https://steinsaltz-center.org/api/mailer', + const response = await fetch(`/api/subscribe/steinsaltz/${email}`, { method: "POST", - headers: {'Content-Type': 'application/json'}, - body: JSON.stringify({ - first_name: firstName, - last_name: lastName, - email: email, - }) + mode: 'same-origin', + headers: { + 'Content-Type': 'application/json', + 'X-CSRFToken': Cookies.get('csrftoken'), + }, + credentials: 'same-origin', + body: JSON.stringify({firstName, lastName}), } ); if (!response.ok) { throw "error"; } - return await response.json(); + const json = await response.json(); + if (json.error) { throw json; } + return json; + }, + subscribeSefariaAndSteinsaltzNewsletter: async function(firstName, lastName, email, educatorCheck) { + const responses = await Promise.all([ + Sefaria.subscribeSefariaNewsletter(firstName, lastName, email, educatorCheck), + Sefaria.subscribeSteinsaltzNewsletter(firstName, lastName, email), + ]); + return {status: "ok"}; }, filterVersionsObjByLangs: function(versionsObj, langs, includeFilter) { /** From 3720cf5ba458b8f6d06e0f2e18ee5fd8e43fcc83 Mon Sep 17 00:00:00 2001 From: Lev Israel <eliezer.israel@gmail.com> Date: Mon, 23 Oct 2023 15:49:07 +0300 Subject: [PATCH 337/756] chore: prevent search results from being indexed by google --- reader/views.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/reader/views.py b/reader/views.py index 86f6843254..385a569c2a 100644 --- a/reader/views.py +++ b/reader/views.py @@ -845,7 +845,8 @@ def search(request): } return render_template(request,'base.html', props, { "title": (search_params["query"] + " | " if search_params["query"] else "") + _("Sefaria Search"), - "desc": _("Search 3,000 years of Jewish texts in Hebrew and English translation.") + "desc": _("Search 3,000 years of Jewish texts in Hebrew and English translation."), + "noindex": True }) From 86b3f58e7e32f1e5ec0d2a8c98eaf4d93bf87dec Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Tue, 24 Oct 2023 12:38:41 +0300 Subject: [PATCH 338/756] fix(Admin Editors): verify places are real cities --- sefaria/model/place.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sefaria/model/place.py b/sefaria/model/place.py index a39bf0e610..c728a4c141 100644 --- a/sefaria/model/place.py +++ b/sefaria/model/place.py @@ -64,7 +64,11 @@ def create_new_place(cls, en, he=None): def city_to_coordinates(self, city): geolocator = Nominatim(user_agent='hello@sefaria.org') location = geolocator.geocode(city) - self.point_location(lon=location.longitude, lat=location.latitude) + if location and location.raw['type'] in ['administrative', 'city', 'town', 'municipality']: + self.point_location(lon=location.longitude, lat=location.latitude) + else: + raise InputError(f"{city} is not a real city.") + def point_location(self, lon=None, lat=None): if lat is None and lon is None: From 1a1925b1f515edd3284b1fcea9323c8b96ea3d5f Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Tue, 24 Oct 2023 16:34:51 +0300 Subject: [PATCH 339/756] =?UTF-8?q?fix(autospell):=20treat=20merkhaot=20("?= =?UTF-8?q?)=20and=20gershayim=20(=D7=B4)=20as=20the=20same=20in=20hebrew?= =?UTF-8?q?=20auto=20completion.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sefaria/model/autospell.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sefaria/model/autospell.py b/sefaria/model/autospell.py index 38d2b48c93..6315ca7a0e 100644 --- a/sefaria/model/autospell.py +++ b/sefaria/model/autospell.py @@ -36,7 +36,7 @@ def normalizer(lang): if lang == "he": - return lambda x: "".join([c if c in letter_scope else unidecode(c) for c in hebrew.normalize_final_letters_in_str(x)]) + return lambda x: "".join([c if c in letter_scope else unidecode(c) for c in hebrew.normalize_final_letters_in_str(x)]).replace('"', '״') return lambda x: "".join([c if c in letter_scope else unidecode(c) for c in str.lower(x)]) @@ -218,6 +218,7 @@ def complete(self, instring, limit=0, redirected=False): :return: completions list, completion objects list """ instring = instring.strip() # A terminal space causes some kind of awful "include everything" behavior + instring = self.normalizer(instring) if len(instring) >= self.max_completion_length: return [], [] cm = Completions(self, self.lang, instring, limit, From 999aeba707d7d406579d854a3383c53c0fe08e54 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Tue, 24 Oct 2023 18:12:09 +0300 Subject: [PATCH 340/756] =?UTF-8?q?fix(search):=20treat=20merkhaot=20(")?= =?UTF-8?q?=20and=20gershayim=20(=D7=B4)=20as=20the=20same=20in=20search.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/js/Header.jsx | 8 ++++++++ static/js/sefaria/sefaria.js | 11 +++++++++++ 2 files changed, 19 insertions(+) diff --git a/static/js/Header.jsx b/static/js/Header.jsx index e2b87d861a..1ff9ba0979 100644 --- a/static/js/Header.jsx +++ b/static/js/Header.jsx @@ -310,6 +310,14 @@ class SearchBar extends Component { this.submitSearch(Sefaria.repairCaseVariant(query, d)); return; } + if (Sefaria.isGershayimVariant(query, d)) { + if (query.includes('"')) { + this.submitSearch(query.replace('"', '״')); + } else { + this.submitSearch(query.replace('״', '"')); + } + return; + } if (d["is_ref"]) { var action = d["is_book"] ? "Search Box Navigation - Book" : "Search Box Navigation - Citation"; diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index 196dea6430..6ee114504b 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -1943,6 +1943,17 @@ _media: {}, data["completions"][0].toLowerCase().replace('״','"') == query.slice(0, data["completions"][0].length).toLowerCase().replace('״','"') && data["completions"][0] != query.slice(0, data["completions"][0].length)) }, + isGershayimVariant: function(query, data) { + function replace_gershayim_by_merkhaot(string) { + return string.replace('"', '״'); + } + return ( + !(data["is_ref"]) && + data.completions && + !data.completions.includes(query) && + data.completions.map(x => replace_gershayim_by_merkhaot(x)).includes(replace_gershayim_by_merkhaot(query)) + ); + }, repairCaseVariant: function(query, data) { // Used when isACaseVariant() is true to prepare the alternative return data["completions"][0] + query.slice(data["completions"][0].length); From a05b98529509afb5d8e456b95cd20bc171b74774 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 25 Oct 2023 11:02:25 +0300 Subject: [PATCH 341/756] fix: move to contentText so that author descriptions dont fallback on non-current language. --- static/js/TopicPage.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/static/js/TopicPage.jsx b/static/js/TopicPage.jsx index 9dba17c1b0..97f1748898 100644 --- a/static/js/TopicPage.jsx +++ b/static/js/TopicPage.jsx @@ -395,10 +395,10 @@ const AuthorIndexItem = ({url, title, description}) => { return ( <div className="authorIndex" > <a href={url} className="navBlockTitle"> - <InterfaceText text={title} /> + <ContentText text={title} defaultToInterfaceOnBilingual /> </a> <div className="navBlockDescription"> - <InterfaceText text={description} /> + <ContentText text={description} defaultToInterfaceOnBilingual /> </div> </div> ); From 741bef6faf521e9e99c3c23b7ca832cb8f1af401 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Thu, 26 Oct 2023 10:30:02 +0300 Subject: [PATCH 342/756] fix(portal): only sign up for steinsaltz newsletter on portal page, not for every newsletter form. --- static/js/NavSidebar.jsx | 1 + static/js/NewsletterSignUpForm.jsx | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/static/js/NavSidebar.jsx b/static/js/NavSidebar.jsx index db4e43790d..9cc3792ed4 100644 --- a/static/js/NavSidebar.jsx +++ b/static/js/NavSidebar.jsx @@ -832,6 +832,7 @@ const PortalNewsletter = ({title, title_url, description}) => { <NewsletterSignUpForm includeEducatorOption={false} emailPlaceholder={{en: "Email Address", he: "מייל"}} + subscribe={Sefaria.subscribeSefariaAndSteinsaltzNewsletter} /> </Module> ) diff --git a/static/js/NewsletterSignUpForm.jsx b/static/js/NewsletterSignUpForm.jsx index 8370c60049..e705109347 100644 --- a/static/js/NewsletterSignUpForm.jsx +++ b/static/js/NewsletterSignUpForm.jsx @@ -5,7 +5,7 @@ export function NewsletterSignUpForm({ contextName, includeEducatorOption = true, emailPlaceholder = {en: 'Sign up for Newsletter', he: "הרשמו לניוזלטר"}, - onSubscribe, + subscribe=Sefaria.subscribeSefariaNewsletter, // function which sends form data to API to subscribe }) { const [email, setEmail] = useState(''); const [firstName, setFirstName] = useState(''); @@ -24,7 +24,7 @@ export function NewsletterSignUpForm({ if (showNameInputs === true) { // submit if (firstName.length > 0 && lastName.length > 0) { setSubscribeMessage("Subscribing..."); - Sefaria.subscribeSefariaAndSteinsaltzNewsletter(firstName, lastName, email, educatorCheck).then(res => { + subscribe(firstName, lastName, email, educatorCheck).then(res => { setSubscribeMessage("Subscribed! Welcome to our list."); Sefaria.track.event("Newsletter", "Subscribe from " + contextName, ""); }).catch(error => { From dd2a7410072d9f662497663cefcac0312ec3a59a Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Thu, 26 Oct 2023 11:05:52 +0300 Subject: [PATCH 343/756] fix(autospell): remove merkha and merkhaot from letter_scope, so they will be removed by unidecode. --- sefaria/model/autospell.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sefaria/model/autospell.py b/sefaria/model/autospell.py index 6315ca7a0e..f9d8379bb9 100644 --- a/sefaria/model/autospell.py +++ b/sefaria/model/autospell.py @@ -29,14 +29,13 @@ + "\u05c1\u05c2" \ + "\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5\u05d6\u05d7\u05d8\u05d9\u05da\u05db\u05dc\u05dd\u05de\u05df" \ + "\u05e0\u05e1\u05e2\u05e3\u05e4\u05e5\u05e6\u05e7\u05e8\u05e9\u05ea" \ - + "\u05f3\u05f4" \ + "\u200e\u200f\u2013\u201c\u201d\ufeff" \ + " Iabcdefghijklmnopqrstuvwxyz1234567890[]`:;.-,*$()'&?/\"" def normalizer(lang): if lang == "he": - return lambda x: "".join([c if c in letter_scope else unidecode(c) for c in hebrew.normalize_final_letters_in_str(x)]).replace('"', '״') + return lambda x: "".join([c if c in letter_scope else unidecode(c) for c in hebrew.normalize_final_letters_in_str(x)]) return lambda x: "".join([c if c in letter_scope else unidecode(c) for c in str.lower(x)]) From 0992a28c7934e18b30c8e66b6d5320d000ae5a36 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Thu, 26 Oct 2023 12:31:25 +0300 Subject: [PATCH 344/756] chore(portal): remove console log --- static/js/TopicPage.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/static/js/TopicPage.jsx b/static/js/TopicPage.jsx index 97f1748898..9ca25bcf38 100644 --- a/static/js/TopicPage.jsx +++ b/static/js/TopicPage.jsx @@ -525,7 +525,6 @@ const TopicPage = ({ "newsletter": "PortalNewsletter", } const modules = []; - console.log("portal", portal) for (let [key, value] of Object.entries(portal)) { if (!portalModuleTypeMap[key]) { continue; } modules.push({ From c97d5fd5ba51afe3540cf636af42ed06ba01b8e4 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Thu, 26 Oct 2023 13:23:30 +0300 Subject: [PATCH 345/756] feat(portal): make title_url optional for portal newsletter --- static/js/NavSidebar.jsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/static/js/NavSidebar.jsx b/static/js/NavSidebar.jsx index 9cc3792ed4..1d95ba3a04 100644 --- a/static/js/NavSidebar.jsx +++ b/static/js/NavSidebar.jsx @@ -825,9 +825,12 @@ const PortalMobile = ({title, description, android_link, ios_link}) => { const PortalNewsletter = ({title, title_url, description}) => { + let titleElement = <ModuleTitle en={title.en} he={title.he} />; + if (title_url) { titleElement = <a href={title_url}>{titleElement}</a>; } + return( <Module> - <a href={title_url}><ModuleTitle en={title.en} he={title.he} /></a> + {titleElement} <InterfaceText markdown={{en: description.en, he: description.he}} /> <NewsletterSignUpForm includeEducatorOption={false} From 2139c009a6950ecfa973fc8131ac1b39da8b7576 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Thu, 26 Oct 2023 14:42:31 +0300 Subject: [PATCH 346/756] refactor(search): repair gershayim always rather than check. --- static/js/Header.jsx | 10 ++++------ static/js/sefaria/sefaria.js | 25 ++++++++++++++----------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/static/js/Header.jsx b/static/js/Header.jsx index 1ff9ba0979..c602818875 100644 --- a/static/js/Header.jsx +++ b/static/js/Header.jsx @@ -310,12 +310,10 @@ class SearchBar extends Component { this.submitSearch(Sefaria.repairCaseVariant(query, d)); return; } - if (Sefaria.isGershayimVariant(query, d)) { - if (query.includes('"')) { - this.submitSearch(query.replace('"', '״')); - } else { - this.submitSearch(query.replace('״', '"')); - } + const repairedQuery = Sefaria.repairGershayimVariant(query, d); + if (repairedQuery !== query) { + console.log(query, d); + this.submitSearch(repairedQuery); return; } diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index 6ee114504b..28aeffcae1 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -1943,21 +1943,24 @@ _media: {}, data["completions"][0].toLowerCase().replace('״','"') == query.slice(0, data["completions"][0].length).toLowerCase().replace('״','"') && data["completions"][0] != query.slice(0, data["completions"][0].length)) }, - isGershayimVariant: function(query, data) { - function replace_gershayim_by_merkhaot(string) { - return string.replace('"', '״'); - } - return ( - !(data["is_ref"]) && - data.completions && - !data.completions.includes(query) && - data.completions.map(x => replace_gershayim_by_merkhaot(x)).includes(replace_gershayim_by_merkhaot(query)) - ); - }, repairCaseVariant: function(query, data) { // Used when isACaseVariant() is true to prepare the alternative return data["completions"][0] + query.slice(data["completions"][0].length); }, + repairGershayimVariant: function(query, data) { + if (!data["is_ref"] && data.completions && !data.completions.includes(query)) { + function normalize_gershayim(string) { + return string.replace('״', '"'); + } + const normalized_query = normalize_gershayim(query); + for (let c of data.completions) { + if (normalize_gershayim(c) === normalized_query) { + return c; + } + } + } + return query; + }, makeSegments: function(data, withContext, sheets=false) { // Returns a flat list of annotated segment objects, // derived from the walking the text in data From da542b9a9bb2a971af47f2e94acbc8a2364945bf Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Thu, 26 Oct 2023 14:49:49 +0300 Subject: [PATCH 347/756] chore(search): remove console.log. --- static/js/Header.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/static/js/Header.jsx b/static/js/Header.jsx index c602818875..5f77a7fc69 100644 --- a/static/js/Header.jsx +++ b/static/js/Header.jsx @@ -312,7 +312,6 @@ class SearchBar extends Component { } const repairedQuery = Sefaria.repairGershayimVariant(query, d); if (repairedQuery !== query) { - console.log(query, d); this.submitSearch(repairedQuery); return; } From 3a1d42e5a4ac839b4d3a74ca12bf4ee33ae7d470 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Thu, 26 Oct 2023 12:58:58 -0400 Subject: [PATCH 348/756] fix(css): Fixed CSS styling so that when there is a banner it does not interfere with the navigation menu on mobile web --- static/css/s2.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/css/s2.css b/static/css/s2.css index c9ee39e798..853f37376d 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -606,7 +606,7 @@ input.noselect { font-size: 18px; } .mobileNavMenu { - position: fixed; + position: absolute; height: calc(100% - 60px); box-sizing: border-box; top: 60px; From 2decebdaf94bac486a906a9dc96fc37497c7564e Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Sun, 29 Oct 2023 12:16:01 +0200 Subject: [PATCH 349/756] refactor(text api): move 'value' from any warning class to the get_message function. --- api/api_warnings.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/api/api_warnings.py b/api/api_warnings.py index 7c8b5ccb44..aecf32b601 100644 --- a/api/api_warnings.py +++ b/api/api_warnings.py @@ -30,33 +30,33 @@ class for returning a message and an warning code """ def get_message(self) -> dict: - return {'warning_code': self.warning_code, + return {'warning_code': self.warning_code.value, 'message': self.message} class APINoVersion(TextsAPIResponseMessage): def __init__(self, oref: Ref, vtitle: str, lang: str): - self.warning_code = APIWarningCode.APINoVersion.value + self.warning_code = APIWarningCode.APINoVersion self.message = f'We do not have version named {vtitle} with language code {lang} for {oref}' class APINoLanguageVersion(TextsAPIResponseMessage): def __init__(self, oref: Ref, langs: List[str]): - self.warning_code = APIWarningCode.APINoLanguageVersion.value + self.warning_code = APIWarningCode.APINoLanguageVersion self.message = f'We do not have the code language you asked for {oref}. Available codes are {langs}' class APINoSourceText(TextsAPIResponseMessage): def __init__(self, oref: Ref): - self.warning_code = APIWarningCode.APINoSourceText.value + self.warning_code = APIWarningCode.APINoSourceText self.message = f'We do not have the source text for {oref}' class APINoTranslationText(TextsAPIResponseMessage): def __init__(self, oref: Ref): - self.warning_code = APIWarningCode.APINoTranslationText.value + self.warning_code = APIWarningCode.APINoTranslationText self.message = f'We do not have a translation for {oref}' From 84ee222ddc4fec35c058c7696df3eb6f5f0920d7 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Sun, 29 Oct 2023 12:16:23 +0200 Subject: [PATCH 350/756] chore(text api): remove print. --- sefaria/model/text_manager.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sefaria/model/text_manager.py b/sefaria/model/text_manager.py index 6b782dc660..66412d597c 100644 --- a/sefaria/model/text_manager.py +++ b/sefaria/model/text_manager.py @@ -42,7 +42,6 @@ def _append_version(self, version): relevant_versions.remove(lambda v: v.actualLanguage != version.actualLanguage) else: relevant_versions = [version] - print(self.oref, version.actualLanguage, version.versionTitle, self.fill_in_missing_segments, relevant_versions) text_range.versions = relevant_versions version_details['text'] = text_range.text From bb3e3b2bb99199eeccfc902d2449e970bcc1ee11 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Sun, 29 Oct 2023 14:41:26 +0200 Subject: [PATCH 351/756] test(text api): test api text for virtual node. --- api/tests.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/api/tests.py b/api/tests.py index 84734d85d6..df63d460ad 100644 --- a/api/tests.py +++ b/api/tests.py @@ -89,6 +89,13 @@ def test_api_get_text_range(self): self.assertEqual(data["sections"], ['5', '2']) self.assertEqual(data["toSections"], ['5', '4']) + def text_api_virtual_node(self): + response = c.get('/api/v3/texts/BDB, א') + self.assertEqual(200, response.status_code) + data = json.loads(response.content) + self.assertEqual(len(data['versions']), 1) + self.assertEqual(data['versions'][0]['text'], ['<big><span dir="rtl">א</span></big> <em>Āleph</em>, first letter; in post Biblical Hebrew = numeral 1 (and so in margin of printed MT); א̈= 1000; no evidence of this usage in OT times.']) + def test_api_get_text_bad_text(self): response = c.get('/api/v3/texts/Life_of_Pi.13.13') self.assertEqual(400, response.status_code) From 9a3e7bd0797a3db76373480b0f942256a3866f88 Mon Sep 17 00:00:00 2001 From: Ephraim <ephraim@sefaria.org> Date: Sun, 29 Oct 2023 15:30:14 +0200 Subject: [PATCH 352/756] fix(mobile nav): Good enough fix for mobile web navigation menu on Firefox Allows absolutely positioned mobile nav menu to actually render onto the screen with the correct height, the previous user of height percentage was ignored by browsers. --- static/css/s2.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/css/s2.css b/static/css/s2.css index 853f37376d..b0093b71c5 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -607,7 +607,7 @@ input.noselect { } .mobileNavMenu { position: absolute; - height: calc(100% - 60px); + height: calc(100vh - 60px); box-sizing: border-box; top: 60px; width: 100%; From e4e4e16ff74e5cc328a45d65793d29996cbf1e8d Mon Sep 17 00:00:00 2001 From: Ephraim <ephraim@sefaria.org> Date: Sun, 29 Oct 2023 16:13:05 +0200 Subject: [PATCH 353/756] fix(mobile nav): Better? fix for mobile nav pos using has() This targets the mobile nav and adds spacing to account for the banner, jsut like the previous css does, without changing the fixed position of the mobile nav menu to absolute. --- static/css/s2.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/static/css/s2.css b/static/css/s2.css index b0093b71c5..a10920c449 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -616,6 +616,12 @@ input.noselect { z-index: 1000; overflow-y: scroll; } +div:has(#bannerMessage) + .readerApp.singlePanel .mobileNavMenu { + position: fixed; /*This takes the 60px of the header plus 120px of the banner into account */ + height: calc(100vh - 180px); + top: 180px; +} + .mobileNavMenu.closed { display: none; } From 1653261bca31142ee6829e66772c536eef09533a Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Sun, 29 Oct 2023 17:22:58 -0400 Subject: [PATCH 354/756] feat(strapi-cms): Teams page now uses Strapi for its data --- sites/sefaria/urls.py | 2 +- static/js/strapi_helpers/team_page.js | 202 ++++++++++++++++++++++++++ templates/static/team.html | 49 +++++++ 3 files changed, 252 insertions(+), 1 deletion(-) create mode 100644 static/js/strapi_helpers/team_page.js create mode 100644 templates/static/team.html diff --git a/sites/sefaria/urls.py b/sites/sefaria/urls.py index 5cee2ad59c..45c2ed2a86 100644 --- a/sites/sefaria/urls.py +++ b/sites/sefaria/urls.py @@ -47,12 +47,12 @@ "word-by-word", "cloudflare_site_is_down_en", "cloudflare_site_is_down_he", + "team", ] static_pages_by_lang = [ "about", "ways-to-give", - "team" ] diff --git a/static/js/strapi_helpers/team_page.js b/static/js/strapi_helpers/team_page.js new file mode 100644 index 0000000000..6df692bfae --- /dev/null +++ b/static/js/strapi_helpers/team_page.js @@ -0,0 +1,202 @@ +const { el, mount, setChildren } = redom; + +const byLastName = (a, b) => { + const c = a.teamMemberDetails.teamName.en; + const d = b.teamMemberDetails.teamName.en; + const lastNameA = c.split(" ").pop(); + const lastNameB = d.split(" ").pop(); + return lastNameA.localeCompare(lastNameB); +}; + +const partition = (arr, prop) => + arr.reduce( + (accumulator, currentValue) => { + accumulator[prop(currentValue) ? 0 : 1].push(currentValue); + return accumulator; + }, + [[], []] + ); + +function LocalizedText(text) { + return [el("span.int-en", text.en), el("span.int-he", text.he)]; +} + +function TeamTitle(teamTitle) { + return el(".teamTitle", LocalizedText(teamTitle)); +} + +function TeamName(teamName) { + return el(".teamName", LocalizedText(teamName)); +} + +function TeamMemberDetails(teamMemberDetails) { + return el(".teamMemberDetails", [ + TeamName(teamMemberDetails.teamName), + TeamTitle(teamMemberDetails.teamTitle), + ]); +} + +function TeamMemberImage(teamMember) { + return el( + ".teamMemberImage", + el("img", { + src: teamMember.teamMemberImage, + alt: "Headshot of " + teamMember.teamMemberDetails.teamName.en, + }) + ); +} + +function TeamMember(teamMember) { + return el(".teamMember", [ + TeamMemberImage(teamMember), + TeamMemberDetails(teamMember.teamMemberDetails), + ]); +} + +function TeamMembers(teamMembers) { + return teamMembers.map((teamMember) => TeamMember(teamMember)); +} + +function Placeholders(teamMembersCount, cls) { + // Determine the number of empty spots to have as placeholders in the last row + placeholdersCount = 3 - (teamMembersCount - 3 * ~~(teamMembersCount / 3)); + return Array.from({ length: placeholdersCount }, () => + el("." + cls + ".placeholder") + ); +} + +function BoardMember(boardMember) { + return el( + ".teamBoardMember", + TeamMemberDetails(boardMember.teamMemberDetails) + ); +} + +function BoardMembers(boardMembers) { + // Separate out the chairman and co-founders for the correct ordering + let chairmanBoardMember; + let chairmanIndex = boardMembers.findIndex( + (boardMember) => + boardMember.teamMemberDetails.teamTitle.en.toLowerCase() === "chairman" + ); + if (chairmanIndex !== -1) { + chairmanBoardMember = boardMembers.splice(chairmanIndex, 1); + } + const [cofounderBoardMembers, regularBoardMembers] = partition( + boardMembers, + (boardMember) => + boardMember.teamMemberDetails.teamTitle.en.toLowerCase() === "co-founder" + ); + // Produce the nodes with the right order for the board members + // Chairman, Co-founders, rest of the board + return [ + ...(chairmanBoardMember ?? []), + ...(cofounderBoardMembers ?? []), + ...regularBoardMembers.sort(byLastName), + ].map((boardMember) => BoardMember(boardMember)); +} + +if (typeof STRAPI_INSTANCE !== "undefined" && STRAPI_INSTANCE) { + async function fetchTeamMembersJSON() { + const query = ` + query { + teamMembers(pagination: { limit: -1 }) { + data { + id + attributes { + teamName + teamTitle + isTeamBoardMember + teamMemberImage { + data { + attributes { + url + } + } + } + localizations { + data { + attributes { + locale + teamName + teamTitle + } + } + } + } + } + } + } + `; + try { + const response = await fetch(STRAPI_INSTANCE + "/graphql", { + method: "POST", + mode: "cors", + cache: "no-cache", + credentials: "omit", + headers: { + "Content-Type": "application/json", + }, + redirect: "follow", + referrerPolicy: "no-referrer", + body: JSON.stringify({ query }), + }); + if (!response.ok) { + throw new Error(`HTTP Error: ${response.statusText}`); + } + const data = await response.json(); + return data; + } catch (error) { + console.error("Fetch error:", error); + throw error; + } + } + + (async () => { + try { + const teamMembersData = await fetchTeamMembersJSON(); + + const teamMembersFromStrapi = teamMembersData.data.teamMembers.data.map( + (teamMember) => { + const heLocalization = teamMember.attributes.localizations.data[0]; + + return { + isTeamBoardMember: teamMember.attributes.isTeamBoardMember, + teamMemberImage: + teamMember.attributes.teamMemberImage?.data?.attributes?.url, + teamMemberDetails: { + teamName: { + en: teamMember.attributes.teamName, + he: heLocalization.attributes.teamName, + }, + teamTitle: { + en: teamMember.attributes.teamTitle, + he: heLocalization.attributes.teamTitle, + }, + }, + }; + } + ); + + const [ordinaryTeamMembers, teamBoardMembers] = partition( + teamMembersFromStrapi, + (teamMember) => !teamMember.isTeamBoardMember + ); + + setChildren(document.querySelector("section.main-text.team-members"), [ + ...TeamMembers(ordinaryTeamMembers.sort(byLastName)), + ...Placeholders(ordinaryTeamMembers.length, "teamMember"), + ]); + + setChildren(document.querySelector("section.main-text.board-members"), [ + ...BoardMembers(teamBoardMembers), + ...Placeholders(teamBoardMembers.length, "teamBoardMember"), + ]); + } catch (error) { + setChildren(document.querySelector("div.row.static-text"), el("h1", "Error: Sefaria's CMS cannot be reached")); + console.error(error); + } + })(); +} else { + setChildren(document.querySelector("div.row.static-text"), el("h1", "Error: Sefaria's CMS cannot be reached")); +} diff --git a/templates/static/team.html b/templates/static/team.html new file mode 100644 index 0000000000..009431411b --- /dev/null +++ b/templates/static/team.html @@ -0,0 +1,49 @@ + +{% extends "base.html" %} +{% load i18n static %} + +{% block title %}{% trans "The Sefaria Team" %}{% endblock %} + +{% block description %}{% trans "Sefaria's team is working to create the Jewish future." %}{% endblock %} + +{% block content %} + <script src="https://www.unpkg.com/redom@3.29.1/dist/redom.min.js"></script> + + <main id="teamPage" class="container static sans-serif"> + <div class="inner"> + <header> + <h1> + <span class="int-en serif">Team</span> + <span class="int-he">צוות ספריא</span> + </h1> + </header> + <div class="row static-text"> + <!-- Team members will be inserted through RE:DOM --> + <section class="main-text team-members"></section> + <header> + <h2> + <span class="int-en">BOARD OF DIRECTORS</span> + <span class="int-he">מועצת המנהלים</span> + </h2> + </header> + <!-- Board members will be inserted through RE:DOM --> + <section class="main-text board-members"></section> + + <script src="static/js/strapi_helpers/team_page.js" defer></script> + + <section id="teamContact"> + <div> + <span class="int-en">For general inquiries, please write to <a href="mailto:hello@sefaria.org">hello@sefaria.org</a>.</span> + <span class="int-he">לשאלות ובקשות כלליות, אנא צרו קשר בכתובת הדוא"ל <a href="mailto:hello@sefaria.org">hello@sefaria.org</a>.</span> + </div> + <div> + <span class="int-en">If you are interested in supporting Sefaria, please write to <a href="mailto:donate@sefaria.org">donate@sefaria.org</a>.</span> + <span class="int-he">אם הנכם מעוניינים לתמוך בספריא, אנא צרו קשר בכתובת הדוא"ל <a href="mailto:donate@sefaria.org">donate@sefaria.org</a>.</span> + </div> + </section> + + </div> + </div> + </main> + +{% endblock %} From 5b89b5fd4e23686088b7d1708dc8b3014304744b Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Mon, 30 Oct 2023 10:08:06 +0200 Subject: [PATCH 355/756] chore: just starting --- sefaria/model/text.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index d8484b59b7..f442da2336 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -2720,7 +2720,8 @@ def __clean_tref(tref, lang): try: # capitalize first letter (don't title case all to avoid e.g., "Song Of Songs") - tref = tref[0].upper() + tref[1:] + # tref = tref[0].upper() + tref[1:] + tref = tref.title() except IndexError: pass @@ -4903,6 +4904,8 @@ def _build_index_maps(self): tree_titles = tree.title_dict(lang) self._index_title_maps[lang][tree.key] = list(tree_titles.keys()) self._title_node_maps[lang].update(tree_titles) + this_node = list(self._title_node_maps[lang].values())[-1] + self._title_node_maps[lang].update({tree.index.title.title(): this_node}) # Add 'Song Of Songs' except IndexSchemaError as e: logger.error("Error in generating title node dictionary: {}".format(e)) From 81308038c944d7d38e0ce64c22fb2f4f0ee56916 Mon Sep 17 00:00:00 2001 From: Ephraim <ephraim@sefaria.org> Date: Mon, 30 Oct 2023 10:11:12 +0200 Subject: [PATCH 356/756] fix(mobile nav): Directly query whether browser supports has or not before changing position --- static/css/s2.css | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/static/css/s2.css b/static/css/s2.css index a10920c449..128071435d 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -606,7 +606,7 @@ input.noselect { font-size: 18px; } .mobileNavMenu { - position: absolute; + position: fixed; height: calc(100vh - 60px); box-sizing: border-box; top: 60px; @@ -621,6 +621,12 @@ div:has(#bannerMessage) + .readerApp.singlePanel .mobileNavMenu { height: calc(100vh - 180px); top: 180px; } +@supports not selector(:has(a, b)) { + /* Fallback for when :has() is unsupported */ + .mobileNavMenu { + position: absolute; + } +} .mobileNavMenu.closed { display: none; From 2ed4f210c5fe5157143091880b2d15a79ef43f57 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Mon, 30 Oct 2023 10:42:41 +0200 Subject: [PATCH 357/756] Revert "chore: just starting" This reverts commit 5b89b5fd4e23686088b7d1708dc8b3014304744b. --- sefaria/model/text.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index f442da2336..d8484b59b7 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -2720,8 +2720,7 @@ def __clean_tref(tref, lang): try: # capitalize first letter (don't title case all to avoid e.g., "Song Of Songs") - # tref = tref[0].upper() + tref[1:] - tref = tref.title() + tref = tref[0].upper() + tref[1:] except IndexError: pass @@ -4904,8 +4903,6 @@ def _build_index_maps(self): tree_titles = tree.title_dict(lang) self._index_title_maps[lang][tree.key] = list(tree_titles.keys()) self._title_node_maps[lang].update(tree_titles) - this_node = list(self._title_node_maps[lang].values())[-1] - self._title_node_maps[lang].update({tree.index.title.title(): this_node}) # Add 'Song Of Songs' except IndexSchemaError as e: logger.error("Error in generating title node dictionary: {}".format(e)) From c24e8e0738b1565dbd2e67998aeb941b8802aee7 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Mon, 30 Oct 2023 12:24:51 +0200 Subject: [PATCH 358/756] chore: capitalize non stop words in Ref --- sefaria/model/text.py | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index d8484b59b7..adac91e16c 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -2717,15 +2717,27 @@ def __clean_tref(tref, lang): return tref tref = tref.replace(":", ".") - - try: - # capitalize first letter (don't title case all to avoid e.g., "Song Of Songs") - tref = tref[0].upper() + tref[1:] - except IndexError: - pass - + tref = Ref.__capitalize_non_stop_words(tref) return tref + @staticmethod + def __capitalize_non_stop_words(tref): + stop_words = ['a', 'haRashba', 'in', 'leYom', 'as', 'min', 'footnotes', 'to', "d'Garmei", 'ben', 'di', 'on', + 'he', 'of', 'part', 'wont', 'haHalakhah', 'or', 'shel', 'by', "la'Nefesh", 'ibn', 'leCheker', + 'according', 'the', 'within', 'haLevanon', 'leYaakov', 'and', 'when', "d'Rav", 'al', 'uMeshiv', + 'with', 'haShamayim', 'who', 'into', 'their', 'is', 'veHaDeot', 'debei', 'other', 'his', 'from', + 'for', 'him'] # currently lowercased words in the titles of books + tref = tref.lower() + temp_tref = "" + prev_w = "" + for i, w in enumerate(tref.split()): + if i == 0 or w not in stop_words or prev_w.endswith(","): + # check previous word ends with comma so that we capitalize 'the' in 'Pesach Haggadah, Magid, The Four Sons 1' + w = w[0].upper() + w[1:] + temp_tref += w + " " + prev_w = w + return temp_tref.strip() + def __reinit_tref(self, new_tref): logger.debug("__reinit_tref from {} to {}".format(self.tref, new_tref)) self.tref = self.__clean_tref(new_tref, self._lang) From 5604e1f916450249eee12db0c7d4760f9a971e32 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Mon, 30 Oct 2023 12:31:35 +0200 Subject: [PATCH 359/756] refactor(text api): rename isBaseText2 to isPrimary. --- sefaria/model/text.py | 2 +- sefaria/model/text_manager.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index 85221fd61a..f4bd284960 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -1390,7 +1390,7 @@ class Version(AbstractTextRecord, abst.AbstractMongoRecord, AbstractSchemaConten "actualLanguage", "isBaseText", 'isSource', - 'isBaseText2', #temp + 'isPrimary', 'direction', # 1 for rtl, 2 for ltr ] diff --git a/sefaria/model/text_manager.py b/sefaria/model/text_manager.py index 66412d597c..c0ecffc039 100644 --- a/sefaria/model/text_manager.py +++ b/sefaria/model/text_manager.py @@ -56,7 +56,7 @@ def _append_version(self, version): def _append_required_versions(self, lang: str, vtitle: str) -> None: if lang == self.BASE: - lang_condition = lambda v: getattr(v, 'isBaseText', False) + lang_condition = lambda v: getattr(v, 'isPrimary', False) elif lang == self.SOURCE: lang_condition = lambda v: getattr(v, 'isSource', False) elif lang == self.TRANSLATION: From 75db98dfdbfcec906758069de35c060066da0b42 Mon Sep 17 00:00:00 2001 From: Brendan Galloway <brendan@flanksource.com> Date: Mon, 30 Oct 2023 12:35:09 +0200 Subject: [PATCH 360/756] ci: remove obfustication from dev build pipeline --- .github/workflows/continuous.yaml | 2 ++ .github/workflows/production-deploy.yaml | 2 ++ build/node/Dockerfile | 3 ++- build/web/Dockerfile | 3 ++- 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/continuous.yaml b/.github/workflows/continuous.yaml index 6c2a8153d0..3fa4611d49 100644 --- a/.github/workflows/continuous.yaml +++ b/.github/workflows/continuous.yaml @@ -93,6 +93,8 @@ jobs: # cache-to: type=registry,ref=gcr.io/${{ secrets.DEV_PROJECT }}/sefaria-${{ matrix.app }}/cache, mode=max context: . push: true + build-args: | + TYPE=build file: ./build/${{ matrix.app }}/Dockerfile tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} diff --git a/.github/workflows/production-deploy.yaml b/.github/workflows/production-deploy.yaml index 31bb7f228b..11d612b19c 100644 --- a/.github/workflows/production-deploy.yaml +++ b/.github/workflows/production-deploy.yaml @@ -97,6 +97,8 @@ jobs: # cache-to: type=registry,ref=$gcr.io/${{ secrets.PROD_GKE_PROJECT }}/{{ secrets.IMAGE_NAME }}-${{ matrix.app }}/cache,mode=max context: . push: true + build-args: | + TYPE=build-prod file: ./build/${{ matrix.app }}/Dockerfile tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} diff --git a/build/node/Dockerfile b/build/node/Dockerfile index f8f14097d4..4b00650316 100644 --- a/build/node/Dockerfile +++ b/build/node/Dockerfile @@ -1,4 +1,5 @@ FROM gcr.io/production-deployment/base-node:v13.0.0 +ARG TYPE=build-prod WORKDIR /app/ @@ -7,7 +8,7 @@ RUN npm install --unsafe-perm COPY ./node /app/node COPY ./static/js /app/static/js -RUN npm run build-prod +RUN npm run $TYPE COPY . /app/ EXPOSE 3000 diff --git a/build/web/Dockerfile b/build/web/Dockerfile index 07effb5324..7065e16cee 100644 --- a/build/web/Dockerfile +++ b/build/web/Dockerfile @@ -1,4 +1,5 @@ FROM gcr.io/production-deployment/base-web:v3.7-bullseye +ARG TYPE=build-prod WORKDIR /app/ # Copied separately to allow for caching of the `pip install` build step @@ -10,7 +11,7 @@ RUN npm install --unsafe-perm COPY ./node /app/node COPY ./static/js /app/static/js -RUN npm run build-prod +RUN npm run $TYPE COPY . /app/ From f75f7827cb6dc923e90225a5701b0b3ab878ebde Mon Sep 17 00:00:00 2001 From: Ephraim <ephraim@sefaria.org> Date: Mon, 30 Oct 2023 13:38:18 +0200 Subject: [PATCH 361/756] helm(fix): Remove collection not needed for database backup --- .../templates/configmap/create-mongo-dumps.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/helm-chart/sefaria-project/templates/configmap/create-mongo-dumps.yaml b/helm-chart/sefaria-project/templates/configmap/create-mongo-dumps.yaml index 20c5a5ee3d..40f79d01e5 100644 --- a/helm-chart/sefaria-project/templates/configmap/create-mongo-dumps.yaml +++ b/helm-chart/sefaria-project/templates/configmap/create-mongo-dumps.yaml @@ -67,7 +67,8 @@ data: sleep 2 done - until mongodump --uri="$URI" -v -d $DATABASE --excludeCollection=history --excludeCollection=texts --excludeCollection=sheets --excludeCollection=links --excludeCollection=user_history -o "${DATADIR}/dump" + until mongodump --uri="$URI" -v -d $DATABASE --excludeCollection=history --excludeCollection=texts --excludeCollection=sheets --excludeCollection=links + --excludeCollection=django_cache --excludeCollection=user_history -o "${DATADIR}/dump" do echo "trying to dump other stuff again" sleep 2 From 94e7ac9b8b6575f0b63e9b33e7e19b5bf0947b5c Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Mon, 30 Oct 2023 14:10:56 +0200 Subject: [PATCH 362/756] fix(portal): fix author titles cut off on mobile web --- static/css/s2.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/static/css/s2.css b/static/css/s2.css index 2f9ca2c8ca..c2be6ea138 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -11014,6 +11014,11 @@ body .homeFeedWrapper .content { border: 0; margin: 0; } + .topicPanel .navSidebar { + width: unset; + border-top: 30px solid #FBFBFA; + margin: 0; + } } .sideColumn .topicSideColumn { margin-bottom: 20px; From 08cdbadd80cb425af3b30f93916e43a191b89b8a Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Mon, 30 Oct 2023 14:22:24 +0200 Subject: [PATCH 363/756] fix(text api): fix condition for adding a version. --- sefaria/model/text_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sefaria/model/text_manager.py b/sefaria/model/text_manager.py index c0ecffc039..433f3be5c3 100644 --- a/sefaria/model/text_manager.py +++ b/sefaria/model/text_manager.py @@ -72,7 +72,7 @@ def _append_required_versions(self, lang: str, vtitle: str) -> None: if vtitle != self.ALL and versions: versions = [max(versions, key=lambda v: getattr(v, 'priority', 0))] for version in versions: - if all(version.actualLanguage != v['actualLanguage'] and version.versionTitle != v['versionTitle'] for v in self.return_obj['versions']): + if all(version.actualLanguage != v['actualLanguage'] or version.versionTitle != v['versionTitle'] for v in self.return_obj['versions']): #do not return the same version even if included in two different version params self._append_version(version) if not versions: From a9f703813a31aca595aabb4e62c4b85c113253f1 Mon Sep 17 00:00:00 2001 From: Brendan Galloway <brendan@flanksource.com> Date: Mon, 30 Oct 2023 15:29:48 +0200 Subject: [PATCH 364/756] helm(fix): correct DB Name variable in cleanup --- .../sefaria-project/templates/configmap/mongo-destroy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helm-chart/sefaria-project/templates/configmap/mongo-destroy.yaml b/helm-chart/sefaria-project/templates/configmap/mongo-destroy.yaml index edbd30be88..f68339f610 100644 --- a/helm-chart/sefaria-project/templates/configmap/mongo-destroy.yaml +++ b/helm-chart/sefaria-project/templates/configmap/mongo-destroy.yaml @@ -38,7 +38,7 @@ data: {{ end }} APSCHEDULER_URI="${URI}${MONGO_HOST}/${APSCHEDULER_NAME}?ssl=false&authSource=admin" - URI="${URI}${MONGO_HOST}/${DATABASE}?ssl=false&authSource=admin" + URI="${URI}${MONGO_HOST}/${DB_NAME}?ssl=false&authSource=admin" if [[ ! -z "$MONGO_REPLICASET_NAME" ]]; then URI="${URI}&replicaSet=${MONGO_REPLICASET_NAME}" From 5176b19cc397a1210935db0dd87cb5f9e83529d4 Mon Sep 17 00:00:00 2001 From: Brendan Galloway <brendan@flanksource.com> Date: Mon, 30 Oct 2023 15:35:12 +0200 Subject: [PATCH 365/756] helm(fix): force APSCHEDULER info to populate --- .../sefaria-project/templates/configmap/mongo-destroy.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/helm-chart/sefaria-project/templates/configmap/mongo-destroy.yaml b/helm-chart/sefaria-project/templates/configmap/mongo-destroy.yaml index f68339f610..a6c0b51e4d 100644 --- a/helm-chart/sefaria-project/templates/configmap/mongo-destroy.yaml +++ b/helm-chart/sefaria-project/templates/configmap/mongo-destroy.yaml @@ -37,6 +37,12 @@ data: DB_NAME=$SEFARIA_DB {{ end }} + echo "APSCHEDULER: ${APSCHEDULER_NAME}" + + if [[ -z "$APSCHEDULER_NAME" ]]; then + APSCHEDULER_NAME={{ tpl .Values.localSettings.APSCHEDULER_NAME . | quote }} + fi + APSCHEDULER_URI="${URI}${MONGO_HOST}/${APSCHEDULER_NAME}?ssl=false&authSource=admin" URI="${URI}${MONGO_HOST}/${DB_NAME}?ssl=false&authSource=admin" From 2a0009d55f9629d351979bf80da452caa32faf59 Mon Sep 17 00:00:00 2001 From: yonadavGit <yonadav.le@gmail.com> Date: Mon, 30 Oct 2023 16:34:52 +0200 Subject: [PATCH 366/756] =?UTF-8?q?static(navSideBarPortal)=20-=20change?= =?UTF-8?q?=20=D7=9E=D7=99=D7=99=D7=9C=20to=20=D7=9B=D7=AA=D7=95=D7=91?= =?UTF-8?q?=D7=AA=20=D7=9E=D7=99=D7=99=D7=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/js/NavSidebar.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/NavSidebar.jsx b/static/js/NavSidebar.jsx index 1d95ba3a04..e978c7a313 100644 --- a/static/js/NavSidebar.jsx +++ b/static/js/NavSidebar.jsx @@ -834,7 +834,7 @@ const PortalNewsletter = ({title, title_url, description}) => { <InterfaceText markdown={{en: description.en, he: description.he}} /> <NewsletterSignUpForm includeEducatorOption={false} - emailPlaceholder={{en: "Email Address", he: "מייל"}} + emailPlaceholder={{en: "Email Address", he: "כתובת מייל"}} subscribe={Sefaria.subscribeSefariaAndSteinsaltzNewsletter} /> </Module> From 6c42f3c5488385b55f8f2ac05800525771516c5d Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Tue, 31 Oct 2023 01:41:27 -0400 Subject: [PATCH 367/756] feat(strapi-cms): Rewrite Team page to use a StaticPages component --- static/js/ReaderApp.jsx | 6 +- static/js/StaticPages.jsx | 261 ++++++++++++++++++++++++++++++++++++- templates/static/team.html | 25 ++-- 3 files changed, 275 insertions(+), 17 deletions(-) diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index 3dd75d07eb..0f779a6f18 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -21,7 +21,8 @@ import { EducatorsPage, RabbisPage, DonatePage, - WordByWordPage + WordByWordPage, + TeamMembersPage } from './StaticPages'; import { SignUpModal, @@ -2266,5 +2267,6 @@ export { EducatorsPage, RabbisPage, DonatePage, - WordByWordPage + WordByWordPage, + TeamMembersPage }; diff --git a/static/js/StaticPages.jsx b/static/js/StaticPages.jsx index 4b2bc394d0..e90176b3d1 100644 --- a/static/js/StaticPages.jsx +++ b/static/js/StaticPages.jsx @@ -1,4 +1,4 @@ -import React, {useState, useRef} from 'react'; +import React, {useState, useRef, useEffect} from 'react'; import { SimpleInterfaceBlock, NewsletterSignUpForm, @@ -2636,6 +2636,262 @@ const StaticHR = () => const ConditionalLink = ({ link, children }) => link ? <a href={link} target="_blank">{children}</a> : children; +/* +* Team Page +*/ + +const byLastName = (a, b) => { + const lastNameA = a.teamMemberDetails.teamName.en.split(" ").pop(); + const lastNameB = b.teamMemberDetails.teamName.en.split(" ").pop(); + return lastNameA.localeCompare(lastNameB); +}; + +const partition = (arr, prop) => + arr.reduce( + (accumulator, currentValue) => { + accumulator[prop(currentValue) ? 0 : 1].push(currentValue); + return accumulator; + }, + [[], []] + ); + +const LocalizedText = ({ text }) => ( + <> + <span className="int-en">{text.en}</span> + <span className="int-he">{text.he}</span> + </> +); + +const TeamTitle = ({ teamTitle }) => ( + <div className="teamTitle"> + <LocalizedText text={teamTitle} /> + </div> +); + +const TeamName = ({ teamName }) => ( + <div className="teamName"> + <LocalizedText text={teamName} /> + </div> +); + +const TeamMemberDetails = ({ teamMemberDetails }) => ( + <div className="teamMemberDetails"> + <TeamName teamName={teamMemberDetails.teamName} /> + <TeamTitle teamTitle={teamMemberDetails.teamTitle} /> + </div> +); + +const TeamMemberImage = ({ teamMember }) => ( + <div className="teamMemberImage"> + <img + src={teamMember.teamMemberImage} + alt={`Headshot of ${teamMember.teamMemberDetails.teamName.en}`} + /> + </div> +); + +const TeamMember = ({ teamMember }) => ( + <div className="teamMember"> + <TeamMemberImage teamMember={teamMember} /> + <TeamMemberDetails teamMemberDetails={teamMember.teamMemberDetails} /> + </div> +); + +const TeamMembers = ({ teamMembers }) => ( + <> + {teamMembers.map((teamMember) => ( + <TeamMember key={teamMember.id} teamMember={teamMember} /> + ))} + </> +); + +const Placeholders = ({ teamMembersCount, cls }) => { + const placeholdersCount = + 3 - (teamMembersCount - 3 * Math.floor(teamMembersCount / 3)); + return ( + <> + {Array.from({ length: placeholdersCount }, (_, index) => ( + <div key={index} className={`${cls} placeholder`} /> + ))} + </> + ); +}; + +const BoardMember = ({ boardMember }) => ( + <div className="teamBoardMember"> + <TeamMemberDetails teamMemberDetails={boardMember.teamMemberDetails} /> + </div> +); + +const BoardMembers = ({ boardMembers }) => { + let chairmanBoardMember; + const chairmanIndex = boardMembers.findIndex( + (boardMember) => + boardMember.teamMemberDetails.teamTitle.en.toLowerCase() === "chairman" + ); + if (chairmanIndex !== -1) { + chairmanBoardMember = boardMembers.splice(chairmanIndex, 1); + } + const [cofounderBoardMembers, regularBoardMembers] = partition( + boardMembers, + (boardMember) => + boardMember.teamMemberDetails.teamTitle.en.toLowerCase() === "co-founder" + ); + + return ( + <> + {chairmanBoardMember && ( + <BoardMember boardMember={chairmanBoardMember[0]} /> + )} + {cofounderBoardMembers.map((boardMember) => ( + <BoardMember key={boardMember.id} boardMember={boardMember} /> + ))} + {regularBoardMembers.sort(byLastName).map((boardMember) => ( + <BoardMember key={boardMember.id} boardMember={boardMember} /> + ))} + </> + ); +}; + +const TeamMembersPage = () => { + const [ordinaryTeamMembers, setOrdinaryTeamMembers] = useState([]); + const [teamBoardMembers, setTeamBoardMembers] = useState([]); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchTeamMembersJSON = async () => { + const query = ` + query { + teamMembers(pagination: { limit: -1 }) { + data { + id + attributes { + teamName + teamTitle + isTeamBoardMember + teamMemberImage { + data { + attributes { + url + } + } + } + localizations { + data { + attributes { + locale + teamName + teamTitle + } + } + } + } + } + } + } + `; + try { + const response = await fetch(STRAPI_INSTANCE + "/graphql", { + method: "POST", + mode: "cors", + cache: "no-cache", + credentials: "omit", + headers: { + "Content-Type": "application/json", + }, + redirect: "follow", + referrerPolicy: "no-referrer", + body: JSON.stringify({ query }), + }); + if (!response.ok) { + throw new Error(`HTTP Error: ${response.statusText}`); + } + const data = await response.json(); + return data; + } catch (error) { + throw error; + } + }; + + const loadTeamMembers = async () => { + if (typeof STRAPI_INSTANCE !== "undefined" && STRAPI_INSTANCE) { + try { + const teamMembersData = await fetchTeamMembersJSON(); + + const teamMembersFromStrapi = + teamMembersData.data.teamMembers.data.map((teamMember) => { + const heLocalization = + teamMember.attributes.localizations.data[0]; + + return { + id: teamMember.id, + isTeamBoardMember: teamMember.attributes.isTeamBoardMember, + teamMemberImage: + teamMember.attributes.teamMemberImage?.data?.attributes?.url, + teamMemberDetails: { + teamName: { + en: teamMember.attributes.teamName, + he: heLocalization.attributes.teamName, + }, + teamTitle: { + en: teamMember.attributes.teamTitle, + he: heLocalization.attributes.teamTitle, + }, + }, + }; + }); + + const [ordinaryMembers, boardMembers] = partition( + teamMembersFromStrapi, + (teamMember) => !teamMember.isTeamBoardMember + ); + + setOrdinaryTeamMembers(ordinaryMembers); + setTeamBoardMembers(boardMembers); + } catch (error) { + console.error("Fetch error:", error); + setError("Error: Sefaria's CMS cannot be reached"); + } + } else { + setError("Error: Sefaria's CMS cannot be reached"); + } + }; + + loadTeamMembers(); + }, []); + + return ( + <div> + {error ? ( + <h1>{error}</h1> + ) : ( + <> + <section className="main-text team-members"> + <TeamMembers teamMembers={ordinaryTeamMembers.sort(byLastName)} /> + <Placeholders + teamMembersCount={ordinaryTeamMembers.length} + cls="teamMember" + /> + </section> + <header> + <h2> + <span class="int-en">BOARD OF DIRECTORS</span> + <span class="int-he">מועצת המנהלים</span> + </h2> + </header> + <section className="main-text board-members"> + <BoardMembers boardMembers={teamBoardMembers} /> + <Placeholders + teamMembersCount={teamBoardMembers.length} + cls="teamBoardMember" + /> + </section> + </> + )} + </div> + ); +}; + export { RemoteLearningPage, @@ -2648,5 +2904,6 @@ export { EducatorsPage, RabbisPage, DonatePage, - WordByWordPage + WordByWordPage, + TeamMembersPage } diff --git a/templates/static/team.html b/templates/static/team.html index 009431411b..bb70829b27 100644 --- a/templates/static/team.html +++ b/templates/static/team.html @@ -6,8 +6,16 @@ {% block description %}{% trans "Sefaria's team is working to create the Jewish future." %}{% endblock %} +{% block js %} +<script> + {% autoescape off %} + DJANGO_VARS.containerId = 'teamMembersPageContent'; + DJANGO_VARS.reactComponentName = 'TeamMembersPage'; + {% endautoescape %} +</script> +{% endblock %} + {% block content %} - <script src="https://www.unpkg.com/redom@3.29.1/dist/redom.min.js"></script> <main id="teamPage" class="container static sans-serif"> <div class="inner"> @@ -18,18 +26,9 @@ <h1> </h1> </header> <div class="row static-text"> - <!-- Team members will be inserted through RE:DOM --> - <section class="main-text team-members"></section> - <header> - <h2> - <span class="int-en">BOARD OF DIRECTORS</span> - <span class="int-he">מועצת המנהלים</span> - </h2> - </header> - <!-- Board members will be inserted through RE:DOM --> - <section class="main-text board-members"></section> - - <script src="static/js/strapi_helpers/team_page.js" defer></script> + <div id="teamMembersPageContent"> + <h1 id="appLoading">Loading...</h1> + </div> <section id="teamContact"> <div> From 512c91f7b1331c7148693492746e5c35eb1d66ee Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Tue, 31 Oct 2023 12:36:33 +0200 Subject: [PATCH 368/756] Revert "chore: capitalize non stop words in Ref" This reverts commit c24e8e0738b1565dbd2e67998aeb941b8802aee7. --- sefaria/model/text.py | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index adac91e16c..d8484b59b7 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -2717,26 +2717,14 @@ def __clean_tref(tref, lang): return tref tref = tref.replace(":", ".") - tref = Ref.__capitalize_non_stop_words(tref) - return tref - @staticmethod - def __capitalize_non_stop_words(tref): - stop_words = ['a', 'haRashba', 'in', 'leYom', 'as', 'min', 'footnotes', 'to', "d'Garmei", 'ben', 'di', 'on', - 'he', 'of', 'part', 'wont', 'haHalakhah', 'or', 'shel', 'by', "la'Nefesh", 'ibn', 'leCheker', - 'according', 'the', 'within', 'haLevanon', 'leYaakov', 'and', 'when', "d'Rav", 'al', 'uMeshiv', - 'with', 'haShamayim', 'who', 'into', 'their', 'is', 'veHaDeot', 'debei', 'other', 'his', 'from', - 'for', 'him'] # currently lowercased words in the titles of books - tref = tref.lower() - temp_tref = "" - prev_w = "" - for i, w in enumerate(tref.split()): - if i == 0 or w not in stop_words or prev_w.endswith(","): - # check previous word ends with comma so that we capitalize 'the' in 'Pesach Haggadah, Magid, The Four Sons 1' - w = w[0].upper() + w[1:] - temp_tref += w + " " - prev_w = w - return temp_tref.strip() + try: + # capitalize first letter (don't title case all to avoid e.g., "Song Of Songs") + tref = tref[0].upper() + tref[1:] + except IndexError: + pass + + return tref def __reinit_tref(self, new_tref): logger.debug("__reinit_tref from {} to {}".format(self.tref, new_tref)) From 73e4a2dabe360b4cfacfeb418c62275bb943ddfb Mon Sep 17 00:00:00 2001 From: Lev Israel <eliezer.israel@gmail.com> Date: Tue, 31 Oct 2023 13:11:35 +0200 Subject: [PATCH 369/756] fix: Performance improvement of Ref.contains If `other` Ref is a whole book, and `self` begins anywhere after first segment, `contains` is trivially false and we can avoid expensive code. --- sefaria/model/tests/ref_test.py | 11 +++++++++++ sefaria/model/text.py | 7 +++++-- sefaria/model/text_manager.py | 1 + 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/sefaria/model/tests/ref_test.py b/sefaria/model/tests/ref_test.py index 4bc8592d90..bb593f9f21 100644 --- a/sefaria/model/tests/ref_test.py +++ b/sefaria/model/tests/ref_test.py @@ -744,6 +744,17 @@ def test_contains(self): assert not Ref("Exodus 6:2").contains(Ref("Exodus")) assert not Ref("Exodus 6:2-12").contains(Ref("Exodus")) + assert Ref("Leviticus").contains(Ref("Leviticus 1:1-27.34")) + assert Ref("Leviticus").contains(Ref("Leviticus 1-27")) + assert Ref("Leviticus 1:1-27.34").contains(Ref("Leviticus")) + assert not Ref("Leviticus 1:1-27.30").contains(Ref("Leviticus")) + assert not Ref("Leviticus 1:2-27.30").contains(Ref("Leviticus")) + assert not Ref("Leviticus 2:2-27.30").contains(Ref("Leviticus")) + + # These fail, and always did + # assert not Ref("Leviticus").contains(Ref("Leviticus 1:1-27.35")) + # assert not Ref("Leviticus").contains(Ref("Leviticus 1-28")) + assert Ref("Rashi on Genesis 5:10-20").contains(Ref("Rashi on Genesis 5:18-20")) assert not Ref("Rashi on Genesis 5:10-20").contains(Ref("Rashi on Genesis 5:21-25")) assert not Ref("Rashi on Genesis 5:10-20").contains(Ref("Rashi on Genesis 5:15-25")) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index ba814657e0..c5cddf0bf6 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -4230,8 +4230,11 @@ def contains(self, other): if not self.index_node == other.index_node: return self.index_node.is_ancestor_of(other.index_node) - # If other is less specific than self, we need to get its true extent - if len(self.sections) > len(other.sections): + if len(self.sections) > len(other.sections): # other is less specific than self + if len(other.sections) == 0: # other is a whole book + if any([x != 1 for x in self.sections]): # self is not a whole book + return False # performance optimization to avoid call to as_ranged_segment_ref + # we need to get the true extent of other other = other.as_ranged_segment_ref() smallest_section_len = min([len(self.sections), len(other.sections)]) diff --git a/sefaria/model/text_manager.py b/sefaria/model/text_manager.py index 433f3be5c3..85640548aa 100644 --- a/sefaria/model/text_manager.py +++ b/sefaria/model/text_manager.py @@ -6,6 +6,7 @@ from sefaria.utils.hebrew import hebrew_term from typing import List + class TextManager: ALL = 'all' BASE = 'base' From bb9df7d7f15c4bf3ca32357950468845f5dbff81 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Tue, 31 Oct 2023 14:56:21 +0200 Subject: [PATCH 370/756] fix(version language): change actualLanguage when saving a Version that its versionTitle reflects another language. --- sefaria/model/text.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index d8484b59b7..581b7eee93 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -1302,7 +1302,7 @@ def _validate(self): """ languageCodeRe = re.search(r"\[([a-z]{2})\]$", getattr(self, "versionTitle", None)) if languageCodeRe and languageCodeRe.group(1) != getattr(self,"actualLanguage",None): - raise InputError("Version actualLanguage does not match bracketed language") + self.actualLanguage = languageCodeRe.group(1) if getattr(self,"language", None) not in ["en", "he"]: raise InputError("Version language must be either 'en' or 'he'") index = self.get_index() From 8c4b2093b418edab7d3a6ec03a470947df97560d Mon Sep 17 00:00:00 2001 From: yonadavGit <yonadav.le@gmail.com> Date: Tue, 31 Oct 2023 15:06:07 +0200 Subject: [PATCH 371/756] feat(portal model): add field "organization" --- sefaria/model/portal.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/sefaria/model/portal.py b/sefaria/model/portal.py index 54be1628ff..24a45c271f 100644 --- a/sefaria/model/portal.py +++ b/sefaria/model/portal.py @@ -61,7 +61,8 @@ class Portal(abst.SluggedAbstractMongoRecord): ] optional_attrs = [ "mobile", - 'newsletter', + "newsletter", + "organization" ] def _validate(self): @@ -82,6 +83,11 @@ def _validate(self): "ios_link": (str, "optional") } + organization_schema = { + "title": ({"en": (str, "required"), "he": (str, "required")}, "required"), + "description": ({"en": (str, "required"), "he": (str, "required")}, "optional"), + } + newsletter_schema = { "title": ({"en": (str, "required"), "he": (str, "required")}, "required"), "title_url": (str, "optional"), @@ -104,6 +110,8 @@ def _validate(self): ios_link = self.mobile.get("ios_link") if ios_link: validate_url(ios_link) + if hasattr(self, "organization"): + abst.validate_dictionary(self.organization, organization_schema) if hasattr(self, "newsletter"): abst.validate_dictionary(self.newsletter, newsletter_schema) title_url = self.newsletter.get("title_url") From f3dab55a8d8cc3aa3ea91105d2dc3a9839eb7143 Mon Sep 17 00:00:00 2001 From: yonadavGit <yonadav.le@gmail.com> Date: Tue, 31 Oct 2023 15:08:32 +0200 Subject: [PATCH 372/756] refactor(TopicPage): render portal sidebar using a designated component to enable enforcement of order on entries --- static/js/TopicPage.jsx | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/static/js/TopicPage.jsx b/static/js/TopicPage.jsx index 9ca25bcf38..d5421b5a1d 100644 --- a/static/js/TopicPage.jsx +++ b/static/js/TopicPage.jsx @@ -435,6 +435,27 @@ const useTabDisplayData = (translationLanguagePreference) => { return getTabDisplayData(); }; +const PortalNavSideBar = ({portal, entriesToDisplayList}) => { + const portalModuleTypeMap = { + "about": "PortalAbout", + "mobile": "PortalMobile", + "organization": "PortalOrganization", + "newsletter": "PortalNewsletter" + } + const modules = []; + for (let key of entriesToDisplayList) { + if (!portal[key]) { continue; } + modules.push({ + type: portalModuleTypeMap[key], + props: portal[key], + }); + } + console.log(modules) + return( + <NavSidebar modules={modules} /> + ) +}; + const TopicPage = ({ tab, topic, topicTitle, setTopic, setNavTopic, openTopics, multiPanel, showBaseText, navHome, toggleSignUpModal, openDisplaySettings, setTab, openSearch, translationLanguagePreference, versionPref, @@ -519,20 +540,7 @@ const TopicPage = ({ if (topicData.portal_slug) { Sefaria.getPortal(topicData.portal_slug).then(setPortal); if (portal) { - const portalModuleTypeMap = { - "about": "PortalAbout", - "mobile": "PortalMobile", - "newsletter": "PortalNewsletter", - } - const modules = []; - for (let [key, value] of Object.entries(portal)) { - if (!portalModuleTypeMap[key]) { continue; } - modules.push({ - type: portalModuleTypeMap[key], - props: value, - }); - } - sidebar = <NavSidebar modules={modules} />; + sidebar = <PortalNavSideBar portal={portal} entriesToDisplayList={["about", "mobile", "organization", "newsletter"]}/> } } else { sidebar = ( From 66597a48aba7fe398c4af566f8fabac0a13b9f9f Mon Sep 17 00:00:00 2001 From: yonadavGit <yonadav.le@gmail.com> Date: Tue, 31 Oct 2023 15:11:25 +0200 Subject: [PATCH 373/756] feat:(PortalOrganization): implemented PortalOrganization component --- static/js/NavSidebar.jsx | 11 ++++++++++- static/js/TopicPage.jsx | 1 - 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/static/js/NavSidebar.jsx b/static/js/NavSidebar.jsx index e978c7a313..9945afe21e 100644 --- a/static/js/NavSidebar.jsx +++ b/static/js/NavSidebar.jsx @@ -53,7 +53,8 @@ const Modules = ({type, props}) => { "Wrapper": Wrapper, "PortalAbout": PortalAbout, "PortalMobile": PortalMobile, - "PortalNewsletter": PortalNewsletter + "PortalOrganization": PortalOrganization, + "PortalNewsletter": PortalNewsletter, }; if (!type) { return null; } const ModuleType = moduleTypes[type]; @@ -822,6 +823,14 @@ const PortalMobile = ({title, description, android_link, ios_link}) => { </Module> ) }; +const PortalOrganization = ({title, description}) => { + return( + <Module> + <ModuleTitle en={title.en} he={title.he} /> + {description && <InterfaceText markdown={{en: description.en, he: description.he}} />} + </Module> + ) +}; const PortalNewsletter = ({title, title_url, description}) => { diff --git a/static/js/TopicPage.jsx b/static/js/TopicPage.jsx index d5421b5a1d..c5a86fef94 100644 --- a/static/js/TopicPage.jsx +++ b/static/js/TopicPage.jsx @@ -450,7 +450,6 @@ const PortalNavSideBar = ({portal, entriesToDisplayList}) => { props: portal[key], }); } - console.log(modules) return( <NavSidebar modules={modules} /> ) From 42d0bfa31757eb211ebd1a184307975b0c94ed6f Mon Sep 17 00:00:00 2001 From: Lev Israel <eliezer.israel@gmail.com> Date: Tue, 31 Oct 2023 16:59:31 +0200 Subject: [PATCH 374/756] fix: Add test for Ref.contains --- sefaria/model/tests/ref_test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sefaria/model/tests/ref_test.py b/sefaria/model/tests/ref_test.py index bb593f9f21..b769f5dd87 100644 --- a/sefaria/model/tests/ref_test.py +++ b/sefaria/model/tests/ref_test.py @@ -744,6 +744,7 @@ def test_contains(self): assert not Ref("Exodus 6:2").contains(Ref("Exodus")) assert not Ref("Exodus 6:2-12").contains(Ref("Exodus")) + assert Ref("Leviticus").contains(Ref("Leviticus")) assert Ref("Leviticus").contains(Ref("Leviticus 1:1-27.34")) assert Ref("Leviticus").contains(Ref("Leviticus 1-27")) assert Ref("Leviticus 1:1-27.34").contains(Ref("Leviticus")) From 9223f0a5dccac52e69bbfab4820f36fbb0850c27 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Tue, 31 Oct 2023 12:28:56 -0400 Subject: [PATCH 375/756] Change team member components to use InterfaceText and reformat to match rest of source file --- static/js/StaticPages.jsx | 388 +++++++++++++++++++------------------- 1 file changed, 195 insertions(+), 193 deletions(-) diff --git a/static/js/StaticPages.jsx b/static/js/StaticPages.jsx index e90176b3d1..7d0af1e4d2 100644 --- a/static/js/StaticPages.jsx +++ b/static/js/StaticPages.jsx @@ -1,4 +1,4 @@ -import React, {useState, useRef, useEffect} from 'react'; +import React, {useState, useRef, useEffect, memo} from 'react'; import { SimpleInterfaceBlock, NewsletterSignUpForm, @@ -2641,126 +2641,121 @@ const ConditionalLink = ({ link, children }) => */ const byLastName = (a, b) => { - const lastNameA = a.teamMemberDetails.teamName.en.split(" ").pop(); - const lastNameB = b.teamMemberDetails.teamName.en.split(" ").pop(); - return lastNameA.localeCompare(lastNameB); + const lastNameA = a.teamMemberDetails.teamName.en.split(" ").pop(); + const lastNameB = b.teamMemberDetails.teamName.en.split(" ").pop(); + return lastNameA.localeCompare(lastNameB); }; const partition = (arr, prop) => - arr.reduce( - (accumulator, currentValue) => { - accumulator[prop(currentValue) ? 0 : 1].push(currentValue); - return accumulator; - }, - [[], []] - ); - -const LocalizedText = ({ text }) => ( - <> - <span className="int-en">{text.en}</span> - <span className="int-he">{text.he}</span> - </> -); + arr.reduce( + (accumulator, currentValue) => { + accumulator[prop(currentValue) ? 0 : 1].push(currentValue); + return accumulator; + }, + [[], []] + ); const TeamTitle = ({ teamTitle }) => ( - <div className="teamTitle"> - <LocalizedText text={teamTitle} /> - </div> + <div className="teamTitle"> + <InterfaceText text={teamTitle} /> + </div> ); const TeamName = ({ teamName }) => ( - <div className="teamName"> - <LocalizedText text={teamName} /> - </div> + <div className="teamName"> + <InterfaceText text={teamName} /> + </div> ); const TeamMemberDetails = ({ teamMemberDetails }) => ( - <div className="teamMemberDetails"> - <TeamName teamName={teamMemberDetails.teamName} /> - <TeamTitle teamTitle={teamMemberDetails.teamTitle} /> - </div> + <div className="teamMemberDetails"> + <TeamName teamName={teamMemberDetails.teamName} /> + <TeamTitle teamTitle={teamMemberDetails.teamTitle} /> + </div> ); const TeamMemberImage = ({ teamMember }) => ( - <div className="teamMemberImage"> - <img - src={teamMember.teamMemberImage} - alt={`Headshot of ${teamMember.teamMemberDetails.teamName.en}`} - /> - </div> + <div className="teamMemberImage"> + <img + src={teamMember.teamMemberImage} + alt={`Headshot of ${teamMember.teamMemberDetails.teamName.en}`} + /> + </div> ); const TeamMember = ({ teamMember }) => ( - <div className="teamMember"> - <TeamMemberImage teamMember={teamMember} /> - <TeamMemberDetails teamMemberDetails={teamMember.teamMemberDetails} /> - </div> + <div className="teamMember"> + <TeamMemberImage teamMember={teamMember} /> + <TeamMemberDetails teamMemberDetails={teamMember.teamMemberDetails} /> + </div> ); const TeamMembers = ({ teamMembers }) => ( - <> - {teamMembers.map((teamMember) => ( - <TeamMember key={teamMember.id} teamMember={teamMember} /> - ))} - </> + <> + {teamMembers.map((teamMember) => ( + <TeamMember key={teamMember.id} teamMember={teamMember} /> + ))} + </> ); const Placeholders = ({ teamMembersCount, cls }) => { - const placeholdersCount = - 3 - (teamMembersCount - 3 * Math.floor(teamMembersCount / 3)); - return ( - <> - {Array.from({ length: placeholdersCount }, (_, index) => ( - <div key={index} className={`${cls} placeholder`} /> - ))} - </> - ); + const placeholdersCount = + 3 - (teamMembersCount - 3 * Math.floor(teamMembersCount / 3)); + return ( + <> + {Array.from({ length: placeholdersCount }, (_, index) => ( + <div key={index} className={`${cls} placeholder`} /> + ))} + </> + ); }; const BoardMember = ({ boardMember }) => ( - <div className="teamBoardMember"> - <TeamMemberDetails teamMemberDetails={boardMember.teamMemberDetails} /> - </div> + <div className="teamBoardMember"> + <TeamMemberDetails teamMemberDetails={boardMember.teamMemberDetails} /> + </div> ); const BoardMembers = ({ boardMembers }) => { - let chairmanBoardMember; - const chairmanIndex = boardMembers.findIndex( - (boardMember) => - boardMember.teamMemberDetails.teamTitle.en.toLowerCase() === "chairman" - ); - if (chairmanIndex !== -1) { - chairmanBoardMember = boardMembers.splice(chairmanIndex, 1); - } - const [cofounderBoardMembers, regularBoardMembers] = partition( - boardMembers, - (boardMember) => - boardMember.teamMemberDetails.teamTitle.en.toLowerCase() === "co-founder" - ); - - return ( - <> - {chairmanBoardMember && ( - <BoardMember boardMember={chairmanBoardMember[0]} /> - )} - {cofounderBoardMembers.map((boardMember) => ( - <BoardMember key={boardMember.id} boardMember={boardMember} /> - ))} - {regularBoardMembers.sort(byLastName).map((boardMember) => ( - <BoardMember key={boardMember.id} boardMember={boardMember} /> - ))} - </> - ); + let chairmanBoardMember; + const chairmanIndex = boardMembers.findIndex( + (boardMember) => + boardMember.teamMemberDetails.teamTitle.en.toLowerCase() === + "chairman" + ); + if (chairmanIndex !== -1) { + chairmanBoardMember = boardMembers.splice(chairmanIndex, 1); + } + const [cofounderBoardMembers, regularBoardMembers] = partition( + boardMembers, + (boardMember) => + boardMember.teamMemberDetails.teamTitle.en.toLowerCase() === + "co-founder" + ); + + return ( + <> + {chairmanBoardMember && ( + <BoardMember boardMember={chairmanBoardMember[0]} /> + )} + {cofounderBoardMembers.map((boardMember) => ( + <BoardMember key={boardMember.id} boardMember={boardMember} /> + ))} + {regularBoardMembers.sort(byLastName).map((boardMember) => ( + <BoardMember key={boardMember.id} boardMember={boardMember} /> + ))} + </> + ); }; -const TeamMembersPage = () => { - const [ordinaryTeamMembers, setOrdinaryTeamMembers] = useState([]); - const [teamBoardMembers, setTeamBoardMembers] = useState([]); - const [error, setError] = useState(null); +const TeamMembersPage = memo(() => { + const [ordinaryTeamMembers, setOrdinaryTeamMembers] = useState([]); + const [teamBoardMembers, setTeamBoardMembers] = useState([]); + const [error, setError] = useState(null); - useEffect(() => { - const fetchTeamMembersJSON = async () => { - const query = ` + useEffect(() => { + const fetchTeamMembersJSON = async () => { + const query = ` query { teamMembers(pagination: { limit: -1 }) { data { @@ -2790,108 +2785,115 @@ const TeamMembersPage = () => { } } `; - try { - const response = await fetch(STRAPI_INSTANCE + "/graphql", { - method: "POST", - mode: "cors", - cache: "no-cache", - credentials: "omit", - headers: { - "Content-Type": "application/json", - }, - redirect: "follow", - referrerPolicy: "no-referrer", - body: JSON.stringify({ query }), - }); - if (!response.ok) { - throw new Error(`HTTP Error: ${response.statusText}`); - } - const data = await response.json(); - return data; - } catch (error) { - throw error; - } - }; - - const loadTeamMembers = async () => { - if (typeof STRAPI_INSTANCE !== "undefined" && STRAPI_INSTANCE) { - try { - const teamMembersData = await fetchTeamMembersJSON(); - - const teamMembersFromStrapi = - teamMembersData.data.teamMembers.data.map((teamMember) => { - const heLocalization = - teamMember.attributes.localizations.data[0]; - - return { - id: teamMember.id, - isTeamBoardMember: teamMember.attributes.isTeamBoardMember, - teamMemberImage: - teamMember.attributes.teamMemberImage?.data?.attributes?.url, - teamMemberDetails: { - teamName: { - en: teamMember.attributes.teamName, - he: heLocalization.attributes.teamName, - }, - teamTitle: { - en: teamMember.attributes.teamTitle, - he: heLocalization.attributes.teamTitle, - }, - }, - }; - }); - - const [ordinaryMembers, boardMembers] = partition( - teamMembersFromStrapi, - (teamMember) => !teamMember.isTeamBoardMember - ); - - setOrdinaryTeamMembers(ordinaryMembers); - setTeamBoardMembers(boardMembers); - } catch (error) { - console.error("Fetch error:", error); - setError("Error: Sefaria's CMS cannot be reached"); - } - } else { - setError("Error: Sefaria's CMS cannot be reached"); - } - }; - - loadTeamMembers(); - }, []); - - return ( - <div> - {error ? ( - <h1>{error}</h1> - ) : ( - <> - <section className="main-text team-members"> - <TeamMembers teamMembers={ordinaryTeamMembers.sort(byLastName)} /> - <Placeholders - teamMembersCount={ordinaryTeamMembers.length} - cls="teamMember" - /> - </section> - <header> - <h2> - <span class="int-en">BOARD OF DIRECTORS</span> - <span class="int-he">מועצת המנהלים</span> - </h2> - </header> - <section className="main-text board-members"> - <BoardMembers boardMembers={teamBoardMembers} /> - <Placeholders - teamMembersCount={teamBoardMembers.length} - cls="teamBoardMember" - /> - </section> - </> - )} - </div> - ); -}; - + try { + const response = await fetch(STRAPI_INSTANCE + "/graphql", { + method: "POST", + mode: "cors", + cache: "no-cache", + credentials: "omit", + headers: { + "Content-Type": "application/json", + }, + redirect: "follow", + referrerPolicy: "no-referrer", + body: JSON.stringify({ query }), + }); + if (!response.ok) { + throw new Error(`HTTP Error: ${response.statusText}`); + } + const data = await response.json(); + return data; + } catch (error) { + throw error; + } + }; + + const loadTeamMembers = async () => { + if (typeof STRAPI_INSTANCE !== "undefined" && STRAPI_INSTANCE) { + try { + const teamMembersData = await fetchTeamMembersJSON(); + + const teamMembersFromStrapi = + teamMembersData.data.teamMembers.data.map( + (teamMember) => { + const heLocalization = + teamMember.attributes.localizations.data[0]; + + return { + id: teamMember.id, + isTeamBoardMember: + teamMember.attributes.isTeamBoardMember, + teamMemberImage: + teamMember.attributes.teamMemberImage + ?.data?.attributes?.url, + teamMemberDetails: { + teamName: { + en: teamMember.attributes.teamName, + he: heLocalization.attributes + .teamName, + }, + teamTitle: { + en: teamMember.attributes.teamTitle, + he: heLocalization.attributes + .teamTitle, + }, + }, + }; + } + ); + + const [ordinaryMembers, boardMembers] = partition( + teamMembersFromStrapi, + (teamMember) => !teamMember.isTeamBoardMember + ); + + setOrdinaryTeamMembers(ordinaryMembers); + setTeamBoardMembers(boardMembers); + } catch (error) { + console.error("Fetch error:", error); + setError("Error: Sefaria's CMS cannot be reached"); + } + } else { + setError("Error: Sefaria's CMS cannot be reached"); + } + }; + + loadTeamMembers(); + }, []); + + return ( + <div> + {error ? ( + <h1>{error}</h1> + ) : ( + <> + <section className="main-text team-members"> + <TeamMembers + teamMembers={ordinaryTeamMembers.sort(byLastName)} + /> + <Placeholders + teamMembersCount={ordinaryTeamMembers.length} + cls="teamMember" + /> + </section> + <header> + <h2> + <span class="int-en">BOARD OF DIRECTORS</span> + <span class="int-he">מועצת המנהלים</span> + </h2> + </header> + <section className="main-text board-members"> + <BoardMembers boardMembers={teamBoardMembers} /> + <Placeholders + teamMembersCount={teamBoardMembers.length} + cls="teamBoardMember" + /> + </section> + </> + )} + </div> + ); +}); export { RemoteLearningPage, @@ -2905,5 +2907,5 @@ export { RabbisPage, DonatePage, WordByWordPage, - TeamMembersPage -} + TeamMembersPage, +}; From d5c5dfb40049bf3be54a5192a83619a380621803 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Tue, 31 Oct 2023 12:37:13 -0400 Subject: [PATCH 376/756] Remove unnecessary files --- static/js/strapi_helpers/team_page.js | 202 --------- templates/static/en/team.html | 627 -------------------------- templates/static/he/team.html | 624 ------------------------- 3 files changed, 1453 deletions(-) delete mode 100644 static/js/strapi_helpers/team_page.js delete mode 100644 templates/static/en/team.html delete mode 100644 templates/static/he/team.html diff --git a/static/js/strapi_helpers/team_page.js b/static/js/strapi_helpers/team_page.js deleted file mode 100644 index 6df692bfae..0000000000 --- a/static/js/strapi_helpers/team_page.js +++ /dev/null @@ -1,202 +0,0 @@ -const { el, mount, setChildren } = redom; - -const byLastName = (a, b) => { - const c = a.teamMemberDetails.teamName.en; - const d = b.teamMemberDetails.teamName.en; - const lastNameA = c.split(" ").pop(); - const lastNameB = d.split(" ").pop(); - return lastNameA.localeCompare(lastNameB); -}; - -const partition = (arr, prop) => - arr.reduce( - (accumulator, currentValue) => { - accumulator[prop(currentValue) ? 0 : 1].push(currentValue); - return accumulator; - }, - [[], []] - ); - -function LocalizedText(text) { - return [el("span.int-en", text.en), el("span.int-he", text.he)]; -} - -function TeamTitle(teamTitle) { - return el(".teamTitle", LocalizedText(teamTitle)); -} - -function TeamName(teamName) { - return el(".teamName", LocalizedText(teamName)); -} - -function TeamMemberDetails(teamMemberDetails) { - return el(".teamMemberDetails", [ - TeamName(teamMemberDetails.teamName), - TeamTitle(teamMemberDetails.teamTitle), - ]); -} - -function TeamMemberImage(teamMember) { - return el( - ".teamMemberImage", - el("img", { - src: teamMember.teamMemberImage, - alt: "Headshot of " + teamMember.teamMemberDetails.teamName.en, - }) - ); -} - -function TeamMember(teamMember) { - return el(".teamMember", [ - TeamMemberImage(teamMember), - TeamMemberDetails(teamMember.teamMemberDetails), - ]); -} - -function TeamMembers(teamMembers) { - return teamMembers.map((teamMember) => TeamMember(teamMember)); -} - -function Placeholders(teamMembersCount, cls) { - // Determine the number of empty spots to have as placeholders in the last row - placeholdersCount = 3 - (teamMembersCount - 3 * ~~(teamMembersCount / 3)); - return Array.from({ length: placeholdersCount }, () => - el("." + cls + ".placeholder") - ); -} - -function BoardMember(boardMember) { - return el( - ".teamBoardMember", - TeamMemberDetails(boardMember.teamMemberDetails) - ); -} - -function BoardMembers(boardMembers) { - // Separate out the chairman and co-founders for the correct ordering - let chairmanBoardMember; - let chairmanIndex = boardMembers.findIndex( - (boardMember) => - boardMember.teamMemberDetails.teamTitle.en.toLowerCase() === "chairman" - ); - if (chairmanIndex !== -1) { - chairmanBoardMember = boardMembers.splice(chairmanIndex, 1); - } - const [cofounderBoardMembers, regularBoardMembers] = partition( - boardMembers, - (boardMember) => - boardMember.teamMemberDetails.teamTitle.en.toLowerCase() === "co-founder" - ); - // Produce the nodes with the right order for the board members - // Chairman, Co-founders, rest of the board - return [ - ...(chairmanBoardMember ?? []), - ...(cofounderBoardMembers ?? []), - ...regularBoardMembers.sort(byLastName), - ].map((boardMember) => BoardMember(boardMember)); -} - -if (typeof STRAPI_INSTANCE !== "undefined" && STRAPI_INSTANCE) { - async function fetchTeamMembersJSON() { - const query = ` - query { - teamMembers(pagination: { limit: -1 }) { - data { - id - attributes { - teamName - teamTitle - isTeamBoardMember - teamMemberImage { - data { - attributes { - url - } - } - } - localizations { - data { - attributes { - locale - teamName - teamTitle - } - } - } - } - } - } - } - `; - try { - const response = await fetch(STRAPI_INSTANCE + "/graphql", { - method: "POST", - mode: "cors", - cache: "no-cache", - credentials: "omit", - headers: { - "Content-Type": "application/json", - }, - redirect: "follow", - referrerPolicy: "no-referrer", - body: JSON.stringify({ query }), - }); - if (!response.ok) { - throw new Error(`HTTP Error: ${response.statusText}`); - } - const data = await response.json(); - return data; - } catch (error) { - console.error("Fetch error:", error); - throw error; - } - } - - (async () => { - try { - const teamMembersData = await fetchTeamMembersJSON(); - - const teamMembersFromStrapi = teamMembersData.data.teamMembers.data.map( - (teamMember) => { - const heLocalization = teamMember.attributes.localizations.data[0]; - - return { - isTeamBoardMember: teamMember.attributes.isTeamBoardMember, - teamMemberImage: - teamMember.attributes.teamMemberImage?.data?.attributes?.url, - teamMemberDetails: { - teamName: { - en: teamMember.attributes.teamName, - he: heLocalization.attributes.teamName, - }, - teamTitle: { - en: teamMember.attributes.teamTitle, - he: heLocalization.attributes.teamTitle, - }, - }, - }; - } - ); - - const [ordinaryTeamMembers, teamBoardMembers] = partition( - teamMembersFromStrapi, - (teamMember) => !teamMember.isTeamBoardMember - ); - - setChildren(document.querySelector("section.main-text.team-members"), [ - ...TeamMembers(ordinaryTeamMembers.sort(byLastName)), - ...Placeholders(ordinaryTeamMembers.length, "teamMember"), - ]); - - setChildren(document.querySelector("section.main-text.board-members"), [ - ...BoardMembers(teamBoardMembers), - ...Placeholders(teamBoardMembers.length, "teamBoardMember"), - ]); - } catch (error) { - setChildren(document.querySelector("div.row.static-text"), el("h1", "Error: Sefaria's CMS cannot be reached")); - console.error(error); - } - })(); -} else { - setChildren(document.querySelector("div.row.static-text"), el("h1", "Error: Sefaria's CMS cannot be reached")); -} diff --git a/templates/static/en/team.html b/templates/static/en/team.html deleted file mode 100644 index 7b94e3ca1c..0000000000 --- a/templates/static/en/team.html +++ /dev/null @@ -1,627 +0,0 @@ - -{% extends "base.html" %} -{% load i18n static %} - -{% block title %}{% trans "The Sefaria Team" %}{% endblock %} - -{% block description %}{% trans "Sefaria's team is working to create the Jewish future." %}{% endblock %} - -{% block content %} - <main id="teamPage" class="container static sans-serif"> - <div class="inner"> - <header> - <h1> - <span class="int-en serif">Team</span> - </h1> - </header> - <div class="row static-text"> - <section class="main-text team-members"> - <!-- - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/gabriel.png' %}" alt="Headshot of Gabriel Winer"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Gabriel Winer</span> - - </div> - <div class="teamTitle"> - <span class="int-en">Director of Product</span> - </div> - </div> - </div> - --> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/akiva.png' %}" alt="Headshot of Akiva Berger"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Akiva Berger</span> - </div> - <div class="teamTitle"> - <span class="int-en">Director of Engineering, Israel</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/rachel.png' %}" alt="Headshot of Rachel Buckman"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Rachel Buckman</span> - </div> - <div class="teamTitle"> - <span class="int-en">Sr. Learning & Support Coordinator</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/skyler.png' %}" alt="Headshot"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Skyler Cohen</span> - </div> - <div class="teamTitle"> - <span class="int-en">Junior Software Engineer</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/caitlyn.png' %}" alt="Headshot of Caitlyn Cushing"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Caitlyn Cushing</span> - </div> - <div class="teamTitle"> - <span class="int-en">Development Operations Associate</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/ephraim.png' %}" alt="Headshot of Ephraim Damboritz"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Ephraim Damboritz</span> - </div> - <div class="teamTitle"> - <span class="int-en">Sr. Engineering Specialist</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/sarit.png' %}" alt="Headshot of Sarit Dubin"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Sarit Dubin</span> - </div> - <div class="teamTitle"> - <span class="int-en">Senior Communications Manager</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/michael-f.png' %}" alt="Headshot of Michael Fankhauser"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Michael Fankhauser</span> - </div> - <div class="teamTitle"> - <span class="int-en">Senior Product Manager</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/olivia.png' %}" alt="Headshot of Olivia Gerber"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Olivia Gerber</span> - </div> - <div class="teamTitle"> - <span class="int-en">Marketing Associate</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/yishai.png' %}" alt="Headshot of Yishai Glasner"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Yishai Glasner</span> - </div> - <div class="teamTitle"> - <span class="int-en">Software Engineer</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/hannah.png' %}" alt="Headshot of Hannah Goldberger"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Hannah Goldberger</span> - </div> - <div class="teamTitle"> - <span class="int-en">Director of Digital Fundraising</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/justin.png' %}" alt="Headshot of Justin Goldstein"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Justin Goldstein</span> - </div> - <div class="teamTitle"> - <span class="int-en">Learning Coordinator</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/talia.png' %}" alt="Headshot of Talia Graff"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Talia Graff</span> - </div> - <div class="teamTitle"> - <span class="int-en">Chief of Staff</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/rachel-g.png' %}" alt="Headshot of Rachel Grossman"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Rachel Grossman</span> - </div> - <div class="teamTitle"> - <span class="int-en">Editorial Associate</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/tali.jpg' %}" alt="Headshot of Tali Herenstein"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Tali Herenstein</span> - </div> - <div class="teamTitle"> - <span class="int-en">Chief Strategy Officer</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/lev.png' %}" alt="Headshot of Lev Israel"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Lev Israel</span> - </div> - <div class="teamTitle"> - <span class="int-en">Chief Product Officer</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/steve.png' %}" alt="Headshot of Steve Kaplan"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Steve Kaplan</span> - </div> - <div class="teamTitle"> - <span class="int-en">Software Engineer</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/sarah.png' %}" alt="Headshot of Sarah Kreitman"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Sarah Kreitman</span> - </div> - <div class="teamTitle"> - <span class="int-en">Junior Software Engineer</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/yonadav.png' %}" alt="Headshot of Yonadav Leibowitz"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Yonadav Leibowitz</span> - </div> - <div class="teamTitle"> - <span class="int-en">Junior Research Engineer</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/annie.png' %}" alt="Headshot of Annie Lumerman"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Annie Lumerman</span> - </div> - <div class="teamTitle"> - <span class="int-en">Chief Operating Officer</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/Amanda_Minsky_Photo.png' %}" alt="Headshot of Amanda Minsky"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Amanda Minsky</span> - </div> - <div class="teamTitle"> - <span class="int-en">People & Operations Coordinator</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/francis.png' %}" alt="Headshot of Francis Nataf"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Francis Nataf</span> - </div> - <div class="teamTitle"> - <span class="int-en">Translation and Research Specialist</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/russel.png' %}" alt="Headshot of Russel Neiss"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Russel Neiss</span> - </div> - <div class="teamTitle"> - <span class="int-en">Product & Engineering Director</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/desiree.png' %}" alt="Headshot of Desiree Neissani"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Desiree Neissani</span> - </div> - <div class="teamTitle"> - <span class="int-en">Special Assistant, Office of the CEO</span> - </div> - </div> - </div> - - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/rebecca.png' %}" alt="Headshot of Rebecca Remisn"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Rebecca Remis</span> - </div> - <div class="teamTitle"> - <span class="int-en">Sr. People & Operations Manager</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/elise.png' %}" alt="Headshot of Elise Ringo"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Elise Ringo</span> - </div> - <div class="teamTitle"> - <span class="int-en">Database Administrator</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/shanee.png' %}" alt="Headshot of Shanee Rosen"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Shanee Rosen</span> - </div> - <div class="teamTitle"> - <span class="int-en">Sr. Software Engineer</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/noah.png' %}" alt="Headshot of Noah Santacruz"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Noah Santacruz</span> - </div> - <div class="teamTitle"> - <span class="int-en">Sr. Engineering Manager</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/daniel.png' %}" alt="Headshot of Daniel Septimus"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Daniel Septimus</span> - </div> - <div class="teamTitle"> - <span class="int-en">Chief Executive Officer</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/samantha.png' %}" alt="Headshot of Samantha Shokin"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Samantha Shokin</span> - </div> - <div class="teamTitle"> - <span class="int-en">Grants Coordinator</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/hadara.png' %}" alt="Headshot of Hadara Steinberg"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Hadara Steinberg</span> - </div> - <div class="teamTitle"> - <span class="int-en">Project Manager</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/israel.png' %}" alt="Headshot of Israel Tsadok"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Israel Tsadok</span> - </div> - <div class="teamTitle"> - <span class="int-en">Learning & Content Coordinator</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/chava.jpg' %}" alt="Headshot of Chava Tzemach"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Chava Tzemach</span> - </div> - <div class="teamTitle"> - <span class="int-en">Director of Marketing and Communications</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/shmuel.png' %}" alt="Headshot of Shmuel Weissman"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Shmuel Weissman</span> - </div> - <div class="teamTitle"> - <span class="int-en">Manager of Text Acquisition and Text Quality</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/sara.png' %}" alt="Headshot of Sara Wolkenfeld"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Sara Wolkenfeld</span> - </div> - <div class="teamTitle"> - <span class="int-en">Chief Learning Officer</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/hedva.png' %}" alt="Headshot of Hedva Yechieli"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Hedva Yechieli</span> - </div> - <div class="teamTitle"> - <span class="int-en">Learning & Engagement Coordinator</span> - </div> - </div> - </div> - <div class="teamMember placeholder"></div> - <div class="teamMember placeholder"></div> - </section> - <header> - <h2> - <span class="int-en">BOARD OF DIRECTORS</span> - </h2> - </header> - <section class="main-text board-members"> - <div class="teamBoardMember"> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Samuel Moed</span> - </div> - <div class="teamTitle"> - <span class="int-en">Chairman</span> - </div> - </div> - </div> - <div class="teamBoardMember"> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Joshua Foer</span> - </div> - <div class="teamTitle"> - <span class="int-en">Co-Founder</span> - </div> - </div> - </div> - <div class="teamBoardMember"> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Brett Lockspeiser</span> - </div> - <div class="teamTitle"> - <span class="int-en">Co-Founder</span> - </div> - </div> - </div> - <div class="teamBoardMember"> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Mo Koyfman</span> - </div> - <div class="teamTitle"> - <span class="int-en"></span> - </div> - </div> - </div> - <div class="teamBoardMember"> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Elana Stein Hain</span> - </div> - <div class="teamTitle"> - <span class="int-en"></span> - </div> - </div> - </div> - <div class="teamBoardMember"> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Jonathan Koschitzky</span> - </div> - <div class="teamTitle"> - <span class="int-en"></span> - </div> - </div> - </div> - <div class="teamBoardMember"> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Joshua Kushner</span> - </div> - <div class="teamTitle"> - <span class="int-en"></span> - </div> - </div> - </div> - <div class="teamBoardMember"> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Raanan Agus</span> - </div> - <div class="teamTitle"> - <span class="int-en"></span> - </div> - </div> - </div> - <div class="teamBoardMember"> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Rona Sheramy</span> - </div> - <div class="teamTitle"> - <span class="int-en"></span> - </div> - </div> - </div> - <div class="teamBoardMember"> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-en">Michael Englander</span> - </div> - <div class="teamTitle"> - <span class="int-en"></span> - </div> - </div> - </div> - <div class="teamBoardMember"> - <div class="teamMemberDetails"> - <div class="teamName sans"> - <span class="int-en">Deborah Shapira</span> - </div> - <div class="teamTitle"> - <span class="int-en"></span> - </div> - </div> - </div> - <div class="teamBoardMember placeholder"></div> - </section> - - - <section id="teamContact"> - <div> - <span class="int-en">For general inquiries, please write to <a href="mailto:hello@sefaria.org">hello@sefaria.org</a>.</span> - </div> - <div> - <span class="int-en">If you are interested in supporting Sefaria, please write to <a href="mailto:donate@sefaria.org">donate@sefaria.org</a>.</span> - </div> - </section> - - </div> - </div> - </main> - -{% endblock %} diff --git a/templates/static/he/team.html b/templates/static/he/team.html deleted file mode 100644 index d9049132fa..0000000000 --- a/templates/static/he/team.html +++ /dev/null @@ -1,624 +0,0 @@ -{% extends "base.html" %} -{% load i18n static %} - -{% block title %}{% trans "The Sefaria Team" %}{% endblock %} - -{% block description %}{% trans "Sefaria's team is working to create the Jewish future." %}{% endblock %} - -{% block content %} - <main id="teamPage" class="container static"> - <div class="inner"> - <header> - <h1> - <span class="int-he">צוות ספריא</span> - </h1> - </header> - <div class="row static-text"> - <section class="main-text team-members"> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/rachel.png' %}" alt="Headshot of Rachel Buckman"> - </div> - <div class="teamMemberDetails"> - <div class="teamName sans"> - <span class="int-he">רחל בקמן</span> - </div> - <div class="teamTitle"> - <span class="int-he">רכזת לימוד ותמיכה בכירה</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/akiva.png' %}" alt="Headshot of Akiva Berger"> - </div> - <div class="teamMemberDetails"> - <div class="teamName sans"> - <span class="int-he">עקיבא ברגר</span> - </div> - <div class="teamTitle"> - <span class="int-he">מנהל צוות הנדסה</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/hannah.png' %}" alt="Headshot of Hannah Goldberger"> - </div> - <div class="teamMemberDetails"> - <div class="teamName sans"> - <span class="int-he">חנה גולדברגר</span> - </div> - <div class="teamTitle"> - <span class="int-he">מנהלת אגף דיגיטל לגיוס כספים ומענקים</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/justin.png' %}" alt="Headshot of Justin Goldstein"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-he">ג'סטין גולדשטיין</span> - </div> - <div class="teamTitle"> - <span class="int-he">רכז לענייני לימוד</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/yishai.png' %}" alt="Headshot of Yishai Glasner"> - </div> - <div class="teamMemberDetails"> - <div class="teamName sans"> - <span class="int-he">ישי גלזנר</span> - </div> - <div class="teamTitle"> - <span class="int-he">מהנדס תוכנה</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/olivia.png' %}" alt="Headshot of Rachel Grossman"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-he">אוליביה גרבר</span> - </div> - <div class="teamTitle"> - <span class="int-he">מנהלת שיווק</span> - </div> - </div> - </div> - - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/rachel-g.png' %}" alt="Headshot of Rachel Grossman"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-he">רחל גרוסמן</span> - </div> - <div class="teamTitle"> - <span class="int-he">רכזת עריכת תוכן</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/talia.png' %}" alt="Headshot of Talia Graff"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-he">טליה גרף</span> - </div> - <div class="teamTitle"> - <span class="int-he">ראש הסגל</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/sarit.png' %}" alt="Headshot of Sarit Dubin"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-he">שרית דובין</span> - </div> - <div class="teamTitle"> - <span class="int-he">מנהלת תקשורת בכירה</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/ephraim.png' %}" alt="Headshot of Ephraim Damboritz"> - </div> - <div class="teamMemberDetails"> - <div class="teamName sans"> - <span class="int-he">אפרים דמבוריץ</span> - </div> - <div class="teamTitle"> - <span class="int-he">מומחה הנדסה בכיר</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/tali.jpg' %}" alt="Headshot of Tali Herenstein"> - </div> - <div class="teamMemberDetails"> - <div class="teamName sans"> - <span class="int-he">טלי הרנשטיין</span> - </div> - <div class="teamTitle"> - <span class="int-he">מנהלת אסטרטגיה בכירה</span> - </div> - </div> - </div> - <!-- - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/gabriel.png' %}" alt="Headshot of Gabriel Winer"> - </div> - <div class="teamMemberDetails"> - <div class="teamName sans"> - <span class="int-he">גבריאל ווינר</span> - </div> - <div class="teamTitle"> - <span class="int-he">מנהל מוצר</span> - </div> - </div> - </div> - --> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/sara.png' %}" alt="Headshot of Sara Wolkenfeld"> - </div> - <div class="teamMemberDetails"> - <div class="teamName sans"> - <span class="int-he">שרה וולקנפלד</span> - </div> - <div class="teamTitle"> - <span class="int-he">מנהלת חינוך והוראה</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/shmuel.png' %}" alt="Headshot of Shmuel Weissman"> - </div> - <div class="teamMemberDetails"> - <div class="teamName sans"> - <span class="int-he">שמואל וייסמן</span> - </div> - <div class="teamTitle"> - <span class="int-he">אחראי רכש טקסטים ואיכות</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/hedva.png' %}" alt="Headshot of Hedva Yechieli"> - </div> - <div class="teamMemberDetails"> - <div class="teamName sans"> - <span class="int-he">חדוה יחיאלי</span> - </div> - <div class="teamTitle"> - <span class="int-he">רכזת חינוך ומעורבות</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/lev.png' %}" alt="Headshot of Lev Israel"> - </div> - <div class="teamMemberDetails"> - <div class="teamName sans"> - <span class="int-he">לב ישראל</span> - </div> - <div class="teamTitle"> - <span class="int-he">מנהל מוצר ראשי</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/skyler.png' %}" alt="Headshot"> - </div> - <div class="teamMemberDetails"> - <div class="teamName sans"> - <span class="int-he">זלמן כהן</span> - </div> - <div class="teamTitle"> - <span class="int-he">מהנדס תוכנה זוטר</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/yonadav.png' %}" alt="Headshot of Yonadav Leibowitz"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-he">יונדב ליבוביץ</span> - </div> - <div class="teamTitle"> - <span class="int-he">מהנדס מחקר זוטר</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/annie.png' %}" alt="Headshot of Annie Lumerman"> - </div> - <div class="teamMemberDetails"> - <div class="teamName sans"> - <span class="int-he">אנני לומרמן</span> - </div> - <div class="teamTitle"> - <span class="int-he">מנהלת תפעול ראשית</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/Amanda_Minsky_Photo.png' %}" alt="Headshot of Amanda Minsky"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-he">אמנדה מינסקי</span> - </div> - <div class="teamTitle"> - <span class="int-he">רכזת אנשים ותפעול</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/francis.png' %}" alt="Headshot of Francis Nataf"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-he">יעקב (פרנסיס) נטף</span> - </div> - <div class="teamTitle"> - <span class="int-he">מומחה תרגום ומחקר</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/russel.png' %}" alt="Headshot of Russel Neiss"> - </div> - <div class="teamMemberDetails"> - <div class="teamName sans"> - <span class="int-he">רזיאל ניס</span> - </div> - <div class="teamTitle"> - <span class="int-he">מנהל מוצר והנדסה</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/desiree.png' %}" alt="Headshot of Desiree Neissani"> - </div> - <div class="teamMemberDetails"> - <div class="teamName sans"> - <span class="int-he">שרה ניסני</span> - </div> - <div class="teamTitle"> - <span class="int-he">עוזר מיוחד, משרד המנכ"ל</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/noah.png' %}" alt="Headshot of Noah Santacruz"> - </div> - <div class="teamMemberDetails"> - <div class="teamName sans"> - <span class="int-he">נח סנטקרוז</span> - </div> - <div class="teamTitle"> - <span class="int-he">מנהל הנדסה בכיר</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/daniel.png' %}" alt="Headshot of Daniel Septimus"> - </div> - <div class="teamMemberDetails"> - <div class="teamName sans"> - <span class="int-he">דניאל ספטימוס</span> - </div> - <div class="teamTitle"> - <span class="int-he">מנהל כללי</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/michael-f.png' %}" alt="Headshot of Michael Fankhauser"> - </div> - <div class="teamMemberDetails"> - <div class="teamName sans"> - <span class="int-he">מיכאל פאנקהאוזר</span> - </div> - <div class="teamTitle"> - <span class="int-he">מנהל מוצר בכיר</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/israel.png' %}" alt="Headshot of Israel Tsadok"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-he">ישראל צדוק</span> - </div> - <div class="teamTitle"> - <span class="int-he">רכז חינוך ותוכן</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/chava.jpg' %}" alt="Headshot of Chava Tzemach"> - </div> - <div class="teamMemberDetails"> - <div class="teamName sans"> - <span class="int-he">חוה צמח</span> - </div> - <div class="teamTitle"> - <span class="int-he">מנהלת אגף תקשורת ושיווק</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/caitlyn.png' %}" alt="Headshot of Caitlyn Cushing"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-he">קייטלין קושינג</span> - </div> - <div class="teamTitle"> - <span class="int-he">רכזת פיתוח משאבים</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/steve.png' %}" alt="Headshot of Steve Kaplan"> - </div> - <div class="teamMemberDetails"> - <div class="teamName sans"> - <span class="int-he">סטיב קפלן</span> - </div> - <div class="teamTitle"> - <span class="int-he">מהנדס תוכנה</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/sarah.png' %}" alt="Headshot of Sarah Kreitman"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-he">שרה קרייטמן</span> - </div> - <div class="teamTitle"> - <span class="int-he">מהנדסת תוכנה זוטרה</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/shanee.png' %}" alt="Headshot of Shanee Rosen"> - </div> - <div class="teamMemberDetails"> - <div class="teamName sans"> - <span class="int-he">שני רוזן</span> - </div> - <div class="teamTitle"> - <span class="int-he">מהנדסת תוכנה בכירה</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/elise.png' %}" alt="Headshot of Elise Ringo"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-he">יעל רינגו</span> - </div> - <div class="teamTitle"> - <span class="int-he">מנהלת מאגר נתונים</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/rebecca.png' %}" alt="Headshot of Rebecca Remis"> - </div> - <div class="teamMemberDetails"> - <div class="teamName sans"> - <span class="int-he">רבקה רמיס</span> - </div> - <div class="teamTitle"> - <span class="int-he">מנהלת אנשים ותפעול בכירה</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/samantha.png' %}" alt="Headshot of Samantha Shokin"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-he">סמנתה שוקין</span> - </div> - <div class="teamTitle"> - <span class="int-he">רכזת גרנטים</span> - </div> - </div> - </div> - <div class="teamMember"> - <div class="teamMemberImage"> - <img src="{% static 'img/headshots/hadara.png' %}" alt="Headshot of Hadara Steinberg"> - </div> - <div class="teamMemberDetails"> - <div class="teamName"> - <span class="int-he">הדרה שטיינברג</span> - </div> - <div class="teamTitle"> - <span class="int-he">מנהלת פרוייקטים</span> - </div> - </div> - </div> - <div class="teamMember placeholder"></div> - <div class="teamMember placeholder"></div> - </section> - <header> - <h2> - <span class="int-he">מועצת המנהלים</span> - </h2> - </header> - <section class="main-text board-members"> - <div class="teamBoardMember"> - <div class="teamMemberDetails"> - <div class="teamName sans"> - <span class="int-he">שמואל מועד</span> - </div> - <div class="teamTitle"> - <span class="int-he">יושב ראש</span> - </div> - </div> - </div> - <div class="teamBoardMember"> - <div class="teamMemberDetails"> - <div class="teamName sans"> - <span class="int-he">ג'ושוע פוייר</span> - </div> - <div class="teamTitle"> - <span class="int-he">מייסד-שותף</span> - </div> - </div> - </div> - <div class="teamBoardMember"> - <div class="teamMemberDetails"> - <div class="teamName sans"> - <span class="int-he">ברט לקספיזר</span> - </div> - <div class="teamTitle"> - <span class="int-he">מייסד-שותף</span> - </div> - </div> - </div> - <div class="teamBoardMember"> - <div class="teamMemberDetails"> - <div class="teamName sans"> - <span class="int-he">מו קויפמן</span> - </div> - <div class="teamTitle"> - <span class="int-he"></span> - </div> - </div> - </div> - <div class="teamBoardMember"> - <div class="teamMemberDetails"> - <div class="teamName sans"> - <span class="int-he">אלנה שטיין היין</span> - </div> - <div class="teamTitle"> - <span class="int-he"></span> - </div> - </div> - </div> - <div class="teamBoardMember"> - <div class="teamMemberDetails"> - <div class="teamName sans"> - <span class="int-he">יונתן קושיצקי</span> - </div> - <div class="teamTitle"> - <span class="int-he"></span> - </div> - </div> - </div> - <div class="teamBoardMember"> - <div class="teamMemberDetails"> - <div class="teamName sans"> - <span class="int-he">ג'ושוע קושנר</span> - </div> - <div class="teamTitle"> - <span class="int-he"></span> - </div> - </div> - </div> - <div class="teamBoardMember"> - <div class="teamMemberDetails"> - <div class="teamName sans"> - <span class="int-he">רענן אגוס</span> - </div> - <div class="teamTitle"> - <span class="int-he"></span> - </div> - </div> - </div> - <div class="teamBoardMember"> - <div class="teamMemberDetails"> - <div class="teamName sans"> - <span class="int-he">רונה שרמי</span> - </div> - <div class="teamTitle"> - <span class="int-he"></span> - </div> - </div> - </div> - <div class="teamBoardMember"> - <div class="teamMemberDetails"> - <div class="teamName sans"> - <span class="int-he">מיכאל ענגלענדער</span> - </div> - <div class="teamTitle"> - <span class="int-he"></span> - </div> - </div> - </div> - <div class="teamBoardMember"> - <div class="teamMemberDetails"> - <div class="teamName sans"> - <span class="int-he">דברה שפירא</span> - </div> - <div class="teamTitle"> - <span class="int-he"></span> - </div> - </div> - </div> - <div class="teamBoardMember placeholder"></div> - </section> - - - <section id="teamContact"> - <div> - <span class="int-he">לשאלות ובקשות כלליות, אנא צרו קשר בכתובת הדוא"ל <a href="mailto:hello@sefaria.org">hello@sefaria.org</a>.</span> - </div> - <div> - <span class="int-he">אם הנכם מעוניינים לתמוך בספריא, אנא צרו קשר בכתובת הדוא"ל <a href="mailto:donate@sefaria.org">donate@sefaria.org</a>.</span> - </div> - </section> - </div> - </div> - </main> - -{% endblock %} From 37dc55fd37d3a2e8c416d7784ce4e05219e45356 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Wed, 1 Nov 2023 10:48:21 +0200 Subject: [PATCH 377/756] fix(Search): case variant repair function Instead of using repairCaseVariant, which only changes "bereshit rabbah 3:15" into "Bereshit rabbah 3:15", I wrote a new function titleCaseExceptStopWords that will capitalize "Rabbah" as well. Also continues to work for "Pirkei avot 4" and "guide for the perplexed" which already work on the site --- static/js/Header.jsx | 2 +- static/js/sefaria/sefaria.js | 20 +++++++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/static/js/Header.jsx b/static/js/Header.jsx index e2b87d861a..3cc4309e50 100644 --- a/static/js/Header.jsx +++ b/static/js/Header.jsx @@ -307,7 +307,7 @@ class SearchBar extends Component { .then(d => { // If the query isn't recognized as a ref, but only for reasons of capitalization. Resubmit with recognizable caps. if (Sefaria.isACaseVariant(query, d)) { - this.submitSearch(Sefaria.repairCaseVariant(query, d)); + this.submitSearch(Sefaria.titleCaseExceptStopWords(query, d)); return; } diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index 196dea6430..71d92dbfd1 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -1944,9 +1944,27 @@ _media: {}, data["completions"][0] != query.slice(0, data["completions"][0].length)) }, repairCaseVariant: function(query, data) { - // Used when isACaseVariant() is true to prepare the alternative + // Used by getCaseVariants() to prepare the alternative return data["completions"][0] + query.slice(data["completions"][0].length); }, + titleCaseExceptStopWords: function(str) { + const stopWords = ["and", "or", "the", "a", "in", "an", "is", "of", "for"]; + let result = []; + if (str[0] === ' ') { + result.push(""); + str = str.trim(); + } + const words = str.split(' '); + for (let i = 0; i < words.length; i++) { + // title case each word except for stop words. + if (stopWords.includes(words[i])) { + result.push(words[i].replace(words[i][0], words[i][0].toLowerCase())); + } else { + result.push(words[i].replace(words[i][0], words[i][0].toUpperCase())); + } + } + return result.join(' '); + }, makeSegments: function(data, withContext, sheets=false) { // Returns a flat list of annotated segment objects, // derived from the walking the text in data From a6ed96d30d68d14b40472fd1c9669fd0dc10c412 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Wed, 1 Nov 2023 10:55:03 +0200 Subject: [PATCH 378/756] chore: restore comment --- static/js/sefaria/sefaria.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index 002fbc3dc2..784b8b51ae 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -1944,7 +1944,7 @@ _media: {}, data["completions"][0] != query.slice(0, data["completions"][0].length)) }, repairCaseVariant: function(query, data) { - // Used by isACaseVariant() to prepare the alternative + // Used when isACaseVariant() is true to prepare the alternative return data["completions"][0] + query.slice(data["completions"][0].length); }, titleCaseExceptStopWords: function(str) { From 1b350682fcefaf4b57d7849f3deb9daddd3bfdc5 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Wed, 1 Nov 2023 14:46:20 -0400 Subject: [PATCH 379/756] feat(strapi-cms): Rewrite Jobs page to use StaticPages component and to be editable through Strapi --- static/css/static.css | 5 + static/js/ReaderApp.jsx | 6 +- static/js/StaticPages.jsx | 203 ++++++++++++++++++++++++++++++++++++- templates/static/jobs.html | 117 ++------------------- 4 files changed, 221 insertions(+), 110 deletions(-) diff --git a/static/css/static.css b/static/css/static.css index eec62cdfa3..02eded1691 100644 --- a/static/css/static.css +++ b/static/css/static.css @@ -1266,6 +1266,11 @@ p.registration-links a:hover{ margin-bottom: 40px; font-weight: bold; } + +#jobsPage .nothing a { + display: inline-block; +} + #jobsPage section.department h2{ font-weight: 500; font-size: 22px; diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index 3dd75d07eb..d18da105ec 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -21,7 +21,8 @@ import { EducatorsPage, RabbisPage, DonatePage, - WordByWordPage + WordByWordPage, + JobsPage } from './StaticPages'; import { SignUpModal, @@ -2266,5 +2267,6 @@ export { EducatorsPage, RabbisPage, DonatePage, - WordByWordPage + WordByWordPage, + JobsPage }; diff --git a/static/js/StaticPages.jsx b/static/js/StaticPages.jsx index 4b2bc394d0..a933720a79 100644 --- a/static/js/StaticPages.jsx +++ b/static/js/StaticPages.jsx @@ -1,4 +1,4 @@ -import React, {useState, useRef} from 'react'; +import React, {useState, useRef, useEffect, memo} from 'react'; import { SimpleInterfaceBlock, NewsletterSignUpForm, @@ -2636,6 +2636,204 @@ const StaticHR = () => const ConditionalLink = ({ link, children }) => link ? <a href={link} target="_blank">{children}</a> : children; +const JobsPageHeader = ({ jobsAreAvailable }) => { + return ( + <> + <header> + <h1 className="serif"> + <span className="int-en">Jobs at Sefaria</span> + <span className="int-he">משרות פנויות בספריא</span> + </h1> + + {jobsAreAvailable ? ( + <> + <h2> + <span className="int-en">About Sefaria</span> + <span className="int-he">אודות ספריא</span> + </h2> + <p> + <span className="int-en"> + Sefaria is a non-profit organization dedicated to creating the + future of Torah in an open and participatory way. We are assembling + a free, living library of Jewish texts and their interconnections, + in Hebrew and in translation. + </span> + <span className="int-he"> + ספריא היא ארגון ללא מטרות רווח שמטרתו יצירת הדור הבא של לימוד התורה + באופן פתוח ומשותף. אנחנו בספריא מרכיבים ספרייה חיה וחופשית של טקסטים + יהודיים וכלל הקישורים ביניהם, בשפת המקור ובתרגומים. + </span> + </p> + </> + ) : null} + </header> + </> + ); +}; + +const Job = ({ job }) => { + return ( + <div className="job"> + <a className="joblink" target="_blank" href={job.jobLink}> + {job.jobDescription} + </a> + </div> + ); +}; + +const JobsListForDepartment = ({ jobsList }) => { + return ( + <section className="jobsListForDepartment"> + {jobsList.map((job) => ( + <Job key={job.id} job={job} /> + ))} + </section> + ); +}; + +const DepartmentJobPostings = ({ department, departmentJobPostings }) => { + return ( + <section className="section department englishOnly"> + <header> + <h2 className="anchorable">{department}</h2> + </header> + <JobsListForDepartment key={department} jobsList={departmentJobPostings} /> + </section> + ); +}; + +const JobPostingsByDepartment = ({ jobPostings }) => { + const groupedJobPostings = jobPostings.reduce((jobPostingsGroupedByDepartment, jobPosting) => { + const category = jobPosting.jobDepartmentCategory; + if (!jobPostingsGroupedByDepartment[category]) { + jobPostingsGroupedByDepartment[category] = []; + } + jobPostingsGroupedByDepartment[category].push(jobPosting); + return jobPostingsGroupedByDepartment; + }, {}); + + return ( + Object.entries(groupedJobPostings).map(([department, departmentJobPostings]) => { + return ( + <DepartmentJobPostings + key={department} + department={department} + departmentJobPostings={departmentJobPostings} + /> + ); + }) + ); +}; + + +const NoJobsNotice = () => { + return ( + <div className="section nothing"> + <p> + <span className="int-en"> + Sefaria does not currently have any open positions. + Please follow us on <a target="_blank" href="http://www.facebook.com/sefaria.org" >Facebook</a> + to hear about our next openings. + </span> + <span className="int-he"> + ספריא איננה מחפשת כעת עובדים חדשים. + עקבו אחרינו ב<a target="_blank" href="http://www.facebook.com/sefaria.org" >פייסבוק</a>  + כדי להשאר מעודכנים במשרות עתידיות. + </span> + </p> + </div> + ); +}; + +const JobsPage = memo(() => { + const [jobPostings, setJobPostings] = useState([]); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchJobsJSON = async () => { + const query = ` + query { + jobPostings(pagination: { limit: -1 }) { + data { + id + attributes { + jobLink + jobDescription + jobDepartmentCategory + } + } + } + } + `; + try { + const response = await fetch(STRAPI_INSTANCE + "/graphql", { + method: "POST", + mode: "cors", + cache: "no-cache", + credentials: "omit", + headers: { + "Content-Type": "application/json", + }, + redirect: "follow", + referrerPolicy: "no-referrer", + body: JSON.stringify({ query }), + }); + if (!response.ok) { + throw new Error(`HTTP Error: ${response.statusText}`); + } + const data = await response.json(); + return data; + } catch (error) { + throw error; + } + }; + + const loadJobPostings = async () => { + if (typeof STRAPI_INSTANCE !== "undefined" && STRAPI_INSTANCE) { + try { + const jobsData = await fetchJobsJSON(); + + const jobsFromStrapi = jobsData.data?.jobPostings?.data?.map((jobPosting) => { + return { + id: jobPosting.id, + jobLink: jobPosting.attributes.jobLink, + jobDescription: jobPosting.attributes.jobDescription, + jobDepartmentCategory: jobPosting.attributes.jobDepartmentCategory + .split("_") + .join(" "), + }; + }); + + setJobPostings(jobsFromStrapi); + } catch (error) { + console.error("Fetch error:", error); + setError("Error: Sefaria's CMS cannot be reached"); + } + } else { + setError("Error: Sefaria's CMS cannot be reached"); + } + }; + + loadJobPostings(); + }, []); + + return ( + <div> + {error ? ( + <h1>{error}</h1> + ) : ( + <> + <JobsPageHeader jobsAreAvailable={jobPostings?.length} /> + {jobPostings?.length ? ( + <JobPostingsByDepartment jobPostings={jobPostings} /> + ) : ( + <NoJobsNotice /> + )} + </> + )} + </div> + ); +}); export { RemoteLearningPage, @@ -2648,5 +2846,6 @@ export { EducatorsPage, RabbisPage, DonatePage, - WordByWordPage + WordByWordPage, + JobsPage } diff --git a/templates/static/jobs.html b/templates/static/jobs.html index 859ee3fa9e..fafc387197 100644 --- a/templates/static/jobs.html +++ b/templates/static/jobs.html @@ -5,117 +5,22 @@ {% block description %}{% trans "Job openings at Sefaria, a non-profit technology organization dedicated to building the Jewish future." %}{% endblock %} +{% block js %} +<script> + {% autoescape off %} + DJANGO_VARS.containerId = 'jobsPageContent'; + DJANGO_VARS.reactComponentName = 'JobsPage'; + {% endautoescape %} +</script> +{% endblock %} + {% block content %} <main id="jobsPage" class="container biReady static"> <div class="inner"> - <header> - <h1 class="serif"> - <span class="int-en">Jobs at Sefaria</span> - <span class="int-he">משרות פנויות בספריא</span> - </h1> - - <!-- Comment out when jobs page has no content --> - <h2> - <span class="int-en">About Sefaria</span> - <span class="int-he">אודות ספריא</span> - </h2> - <p> - <span class="int-en"> - Sefaria is a non-profit organization dedicated to creating the future of Torah in an open and - participatory way. - We are assembling a free, living library of Jewish texts and their interconnections, in Hebrew and - in translation. - </span> - <span class="int-he"> - ספריא היא ארגון ללא מטרות רווח שמטרתו יצירת הדור הבא של לימוד התורה באופן פתוח ומשותף. - אנחנו בספריא מרכיבים ספרייה חיה וחופשית של טקסטים יהודיים וכלל הקישורים ביניהם, בשפת המקור ובתרגומים. - </span> - </p> - <!-- Comment out when jobs page has no content --> - </header> - - - <!---<section class="section department englishOnly"> - <header> - <h2 class="anchorable">Operations</h2> - </header> - <section class="jobsListForDepartment"> - <div class="job"><a class="jobLink" target="_blank" href=""></a></div> - </section> - </section>---> - <!-- - <section class="section department englishOnly"> - <header> - <h2 class="anchorable">Engineering</h2> - </header> - <section class="jobsListForDepartment"> - </section> - </section> - <section class="section department englishOnly"> - <header> - <h2 class="anchorable">Learning</h2> - </header> - <section class="jobsListForDepartment"> - <div class="job"><a class="jobLink" target="_blank" href=""></a></div> - <div class="job"><a class="jobLink" target="_blank" href=""></a></div> - </section> - </section> - <section class="section department englishOnly"> - <header> - <h2 class="anchorable">HR and Operations</h2> - </header> - <section class="jobsListForDepartment"> - <div class="job"><a class="" target="_blank" href=""></a></div> - </section> - </section> --> - - <section class="section department englishOnly"> - <header> - <h2 class="anchorable">Marketing and Communications</h2> - </header> - <section class="jobsListForDepartment"> - <div class="job"><a class="joblink" target="_blank" href="https://sefaria.breezy.hr/p/b11c89877ad6-communications-specialist?state=published">Communications Specialist</a></div> - </section> - </section> - - - - - <!-- - <section class="section department englishOnly"> - <header> - <h2 class="anchorable">Product</h2> - </header> - <section class="jobsListForDepartment"> - <div class="job"><a class="jobLink" target="_blank" href=""></a></div> - </section> - <section class="jobsListForDepartment"> - <div class="job"><a class="jobLink" target="_blank" href=""></a></div> - </section> - </section> - <section class="section department englishOnly"> - <header> - <h2 class="anchorable">Israel Team</h2> - </header> - </section>---> - - <!-- - <div class="section nothing"> - <p> - <span class="int-en"> - Sefaria does not currently have any open positions. - Please follow us on <a _target="blank" href="http://www.facebook.com/sefaria.org" >Facebook</a> - to hear about our next openings. - </span> - <span class="int-he"> - ספריא איננה מחפשת כעת עובדים חדשים. - עקבו אחרינו ב<a _target="blank" href="http://www.facebook.com/sefaria.org" >פייסבוק</a> - כדי להשאר מעודכנים במשרות עתידיות. - </span> - </p> + <div id="jobsPageContent"> + <h1 id="appLoading">Loading...</h1> </div> - --> <aside class="solicitationNotice"> <p> From 08ceea5d542ff8103aa6df813e8e2a995b0cf9c2 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Wed, 1 Nov 2023 18:18:34 -0400 Subject: [PATCH 380/756] Fix small bug: change class to className --- static/js/StaticPages.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/static/js/StaticPages.jsx b/static/js/StaticPages.jsx index 7d0af1e4d2..f765b167c9 100644 --- a/static/js/StaticPages.jsx +++ b/static/js/StaticPages.jsx @@ -2878,8 +2878,8 @@ const TeamMembersPage = memo(() => { </section> <header> <h2> - <span class="int-en">BOARD OF DIRECTORS</span> - <span class="int-he">מועצת המנהלים</span> + <span className="int-en">BOARD OF DIRECTORS</span> + <span className="int-he">מועצת המנהלים</span> </h2> </header> <section className="main-text board-members"> From 3e92cfcb8e50bb64a7167e8b4e77c00bb4e98014 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Wed, 1 Nov 2023 19:16:47 -0400 Subject: [PATCH 381/756] feat(jobs): Sort team members in the correct order when the interface language is Hebrew --- static/js/StaticPages.jsx | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/static/js/StaticPages.jsx b/static/js/StaticPages.jsx index f765b167c9..2b603d67e9 100644 --- a/static/js/StaticPages.jsx +++ b/static/js/StaticPages.jsx @@ -2640,10 +2640,13 @@ const ConditionalLink = ({ link, children }) => * Team Page */ -const byLastName = (a, b) => { - const lastNameA = a.teamMemberDetails.teamName.en.split(" ").pop(); - const lastNameB = b.teamMemberDetails.teamName.en.split(" ").pop(); - return lastNameA.localeCompare(lastNameB); +const byLastName = () => { + const locale = Sefaria.interfaceLang === "hebrew" ? "he" : "en"; + return (a, b) => { + const lastNameA = a.teamMemberDetails.teamName[locale].split(" ").pop(); + const lastNameB = b.teamMemberDetails.teamName[locale].split(" ").pop(); + return lastNameA.localeCompare(lastNameB, locale); + }; }; const partition = (arr, prop) => @@ -2741,7 +2744,7 @@ const BoardMembers = ({ boardMembers }) => { {cofounderBoardMembers.map((boardMember) => ( <BoardMember key={boardMember.id} boardMember={boardMember} /> ))} - {regularBoardMembers.sort(byLastName).map((boardMember) => ( + {regularBoardMembers.sort(byLastName()).map((boardMember) => ( <BoardMember key={boardMember.id} boardMember={boardMember} /> ))} </> @@ -2869,7 +2872,7 @@ const TeamMembersPage = memo(() => { <> <section className="main-text team-members"> <TeamMembers - teamMembers={ordinaryTeamMembers.sort(byLastName)} + teamMembers={ordinaryTeamMembers.sort(byLastName())} /> <Placeholders teamMembersCount={ordinaryTeamMembers.length} From 222525f84eeef8e2d2ac74b7f923a330a5b44cf2 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Thu, 2 Nov 2023 09:31:14 +0200 Subject: [PATCH 382/756] =?UTF-8?q?feat(ref):=20remove=20stop=5Fparsing=20?= =?UTF-8?q?for=20identifying=20hebrew=20refs=20of=20talmud=20with=20line?= =?UTF-8?q?=20(e.g.=20=D7=A1=D7=95=D7=98=D7=94=20=D7=9C=D7=94=20=D7=90:?= =?UTF-8?q?=D7=99=D7=90).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sefaria/model/schema.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/sefaria/model/schema.py b/sefaria/model/schema.py index 305dda01b8..efd17d834b 100644 --- a/sefaria/model/schema.py +++ b/sefaria/model/schema.py @@ -2351,11 +2351,6 @@ def _core_regex(self, lang, group_id=None, **kwargs): return reg - def stop_parsing(self, lang): - if lang == "he": - return True - return False - def toNumber(self, lang, s, **kwargs): amud_b_list = ['b', 'B', 'ᵇ'] if lang == "en": From 926b17f4d28cedfd1eb8ccd7f85a0abd7a56bb63 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Thu, 2 Nov 2023 09:33:16 +0200 Subject: [PATCH 383/756] doc(ref): changing doc of stop_parsing to reflect the previous change. --- sefaria/model/schema.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sefaria/model/schema.py b/sefaria/model/schema.py index efd17d834b..b93a8bf84a 100644 --- a/sefaria/model/schema.py +++ b/sefaria/model/schema.py @@ -2075,7 +2075,7 @@ def hebrew_number_regex(): def stop_parsing(self, lang): """ If this is true, the regular expression will stop parsing at this address level for this language. - It is currently checked for only in the first address position, and is used for Hebrew Talmud addresses. + It is currently checked for only in the first address position, and is used for Hebrew Folio addresses (since Folios are just in alt_structs maybe it hos no effect)_. :param lang: "en" or "he" :return bool: """ From 92ecba8f58acd8c4ae79dd2683593bef5770bb9e Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Thu, 2 Nov 2023 10:27:40 +0200 Subject: [PATCH 384/756] fix(timeperiod): replace hyphen by dash in period strings. --- sefaria/model/timeperiod.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sefaria/model/timeperiod.py b/sefaria/model/timeperiod.py index bb0da09560..48db602435 100644 --- a/sefaria/model/timeperiod.py +++ b/sefaria/model/timeperiod.py @@ -144,7 +144,7 @@ def period_string(self, lang): if lang == "en": if getattr(self, "symbol", "") == "CO" or getattr(self, "end", None) is None: - name += " ({}{} {} - )".format( + name += " ({}{} {} – )".format( approxMarker[0], abs(int(self.start)), labels[1]) @@ -155,7 +155,7 @@ def period_string(self, lang): abs(int(self.start)), labels[1]) else: - name += " ({}{} {} - {}{} {})".format( + name += " ({}{} {} – {}{} {})".format( approxMarker[0], abs(int(self.start)), labels[0], @@ -164,7 +164,7 @@ def period_string(self, lang): labels[1]) if lang == "he": if getattr(self, "symbol", "") == "CO" or getattr(self, "end", None) is None: - name += " ({} {} {} - )".format( + name += " ({} {} {} – )".format( abs(int(self.start)), labels[1], approxMarker[0]) @@ -177,7 +177,7 @@ def period_string(self, lang): else: both_approx = approxMarker[0] and approxMarker[1] if both_approx: - name += " ({}{} - {}{} {})".format( + name += " ({}{} – {}{} {})".format( abs(int(self.start)), " " + labels[0] if labels[0] else "", abs(int(self.end)), @@ -185,7 +185,7 @@ def period_string(self, lang): approxMarker[1] ) else: - name += " ({}{}{} - {}{}{})".format( + name += " ({}{}{} – {}{}{})".format( abs(int(self.start)), " " + labels[0] if labels[0] else "", " " + approxMarker[0] if approxMarker[0] else "", From d34dd1bf6db31811d32c672bfbf6270e17937bed Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Thu, 2 Nov 2023 12:12:38 +0200 Subject: [PATCH 385/756] feat(timeperiod): new class for person timeperiod which has different method for period string. --- sefaria/model/timeperiod.py | 59 ++++++++++++++++++++++++++++++++----- sefaria/model/topic.py | 17 +++++++---- 2 files changed, 63 insertions(+), 13 deletions(-) diff --git a/sefaria/model/timeperiod.py b/sefaria/model/timeperiod.py index 48db602435..30ff0a1df9 100644 --- a/sefaria/model/timeperiod.py +++ b/sefaria/model/timeperiod.py @@ -56,6 +56,8 @@ +---------------+------------+-----------------+-------------------------------+-----------------------+ """ +DASH = '–' + class TimePeriod(abst.AbstractMongoRecord): """ TimePeriod is used both for the saved time periods - Eras and Generations @@ -144,10 +146,11 @@ def period_string(self, lang): if lang == "en": if getattr(self, "symbol", "") == "CO" or getattr(self, "end", None) is None: - name += " ({}{} {} – )".format( + name += " ({}{} {} {} )".format( approxMarker[0], abs(int(self.start)), - labels[1]) + labels[1], + DASH) return name elif int(self.start) == int(self.end): name += " ({}{} {})".format( @@ -155,19 +158,21 @@ def period_string(self, lang): abs(int(self.start)), labels[1]) else: - name += " ({}{} {} – {}{} {})".format( + name += " ({}{} {} {} {}{} {})".format( approxMarker[0], abs(int(self.start)), labels[0], + DASH, approxMarker[1], abs(int(self.end)), labels[1]) if lang == "he": if getattr(self, "symbol", "") == "CO" or getattr(self, "end", None) is None: - name += " ({} {} {} – )".format( + name += " ({} {} {} {} )".format( abs(int(self.start)), labels[1], - approxMarker[0]) + approxMarker[0], + DASH) return name elif int(self.start) == int(self.end): name += " ({}{}{})".format( @@ -177,18 +182,20 @@ def period_string(self, lang): else: both_approx = approxMarker[0] and approxMarker[1] if both_approx: - name += " ({}{} – {}{} {})".format( + name += " ({}{} {} {}{} {})".format( abs(int(self.start)), " " + labels[0] if labels[0] else "", + DASH, abs(int(self.end)), " " + labels[1] if labels[1] else "", approxMarker[1] ) else: - name += " ({}{}{} – {}{}{})".format( + name += " ({}{}{} { {}{}{})".format( abs(int(self.start)), " " + labels[0] if labels[0] else "", " " + approxMarker[0] if approxMarker[0] else "", + DASH, abs(int(self.end)), " " + labels[1] if labels[1] else "", " " + approxMarker[1] if approxMarker[1] else "" @@ -234,3 +241,41 @@ def get_generations(include_doubles = False): arg = {"$in": ["Generation", "Two Generations"]} if include_doubles else "Generation" return TimePeriodSet._get_typed_set(arg) +class PersonTimePeriod(TimePeriod): + + def period_string(self, lang): + + if getattr(self, "start", None) == None and getattr(self, "end", None) == None: + return + + labels = self.getYearLabels(lang) + approxMarker = self.getApproximateMarkers(lang) + abs_birth = abs(int(getattr(self, "start", 0))) + abs_death = abs(int(getattr(self, "end", 0))) + if lang == "en": + birth = 'b.' + death = 'd.' + order_vars_by_lang = lambda year, label, approx: (approx, '', year, label) + else: + birth = 'נו׳' + death = 'נפ׳' + order_vars_by_lang = lambda year, label, approx: (year, ' ', label, approx) + + if getattr(self, "symbol", "") == "CO" or getattr(self, "end", None) is None: + name = '{} {}{}{} {}'.format(birth, *order_vars_by_lang(abs_birth, labels[1], approxMarker[0])) + elif getattr(self, "start", None) is None: + name = '{} {}{}{} {}'.format(death, *order_vars_by_lang(abs_death, labels[1], approxMarker[0])) + elif int(self.start) == int(self.end): + name = '{}{}{} {}'.format(*order_vars_by_lang(abs_birth, labels[1], approxMarker[0])) + else: + both_approx = approxMarker[0] and approxMarker[1] + if lang == 'he' and both_approx: + birth_string = '{}{}{}'.format(*order_vars_by_lang(abs_birth, labels[0], approxMarker[0])[:-1]) + else: + birth_string = '{}{}{} {}'.format(*order_vars_by_lang(abs_birth, labels[0], approxMarker[0])) + death_string = '{}{}{} {}'.format(*order_vars_by_lang(abs_death, labels[1], approxMarker[0])) + name = f'{birth_string} {DASH} {death_string}' + + name = f' ({" ".join(name.split())})' + return name + diff --git a/sefaria/model/topic.py b/sefaria/model/topic.py index b259b88311..8b0f6ca283 100644 --- a/sefaria/model/topic.py +++ b/sefaria/model/topic.py @@ -4,7 +4,7 @@ from .text import Ref, IndexSet, AbstractTextRecord from .category import Category from sefaria.system.exceptions import InputError, DuplicateRecordError -from sefaria.model.timeperiod import TimePeriod +from sefaria.model.timeperiod import PersonTimePeriod from sefaria.system.database import db import structlog, bleach from sefaria.model.place import Place @@ -438,23 +438,28 @@ def contents(self, **kwargs): # A person may have an era, a generation, or a specific birth and death years, which each may be approximate. # They may also have none of these... - def most_accurate_time_period(self) -> Optional[TimePeriod]: + def most_accurate_time_period(self) -> Optional[PersonTimePeriod]: if self.get_property("birthYear") and self.get_property("deathYear"): - return TimePeriod({ + return PersonTimePeriod({ "start": self.get_property("birthYear"), "startIsApprox": self.get_property("birthYearIsApprox", False), "end": self.get_property("deathYear"), "endIsApprox": self.get_property("deathYearIsApprox", False) }) elif self.get_property("birthYear") and self.get_property("era", "CO"): - return TimePeriod({ + return PersonTimePeriod({ "start": self.get_property("birthYear"), "startIsApprox": self.get_property("birthYearIsApprox", False), }) + elif self.get_property("deathYear"): + return PersonTimePeriod({ + "end": self.get_property("deathYear"), + "endIsApprox": self.get_property("deathYearIsApprox", False) + }) elif self.get_property("generation"): - return TimePeriod().load({"symbol": self.get_property("generation")}) + return PersonTimePeriod().load({"symbol": self.get_property("generation")}) elif self.get_property("era"): - return TimePeriod().load({"symbol": self.get_property("era")}) + return PersonTimePeriod().load({"symbol": self.get_property("era")}) else: return None From 81ac893efc79c51b65b91fc524f52ec158308e3f Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Thu, 2 Nov 2023 12:12:57 +0200 Subject: [PATCH 386/756] test(timeperiod): change test to feat changes. --- sefaria/model/tests/text_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sefaria/model/tests/text_test.py b/sefaria/model/tests/text_test.py index 00192188e2..97565d2b9d 100644 --- a/sefaria/model/tests/text_test.py +++ b/sefaria/model/tests/text_test.py @@ -169,9 +169,9 @@ def test_invalid_index_save_no_category(): def test_best_time_period(): i = model.library.get_index("Rashi on Genesis") - assert i.best_time_period().period_string('en') == ' (c.1075 - c.1105 CE)' + assert i.best_time_period().period_string('en') == ' (c.1075 – c.1105 CE)' i.compDate = None - assert i.best_time_period().period_string('en') == ' (1040 - 1105 CE)' # now that compDate is None, period_string should return Rashi's birth to death years + assert i.best_time_period().period_string('en') == ' (1040 – 1105 CE)' # now that compDate is None, period_string should return Rashi's birth to death years def test_invalid_index_save_no_hebrew_collective_title(): title = 'Bartenura (The Next Generation)' From 89cd5bb9d39b7c1b9f7a82aa07336f7424b9acc3 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Thu, 2 Nov 2023 13:17:28 +0200 Subject: [PATCH 387/756] refactor(timeperiod): change PersionTimePeriod to LifePeriod, new function for getting TimePeriod from person topic, and use it when getting it for index comp date. --- sefaria/model/tests/text_test.py | 2 +- sefaria/model/timeperiod.py | 2 +- sefaria/model/topic.py | 21 +++++++++++++-------- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/sefaria/model/tests/text_test.py b/sefaria/model/tests/text_test.py index 97565d2b9d..6609c17354 100644 --- a/sefaria/model/tests/text_test.py +++ b/sefaria/model/tests/text_test.py @@ -171,7 +171,7 @@ def test_best_time_period(): i = model.library.get_index("Rashi on Genesis") assert i.best_time_period().period_string('en') == ' (c.1075 – c.1105 CE)' i.compDate = None - assert i.best_time_period().period_string('en') == ' (1040 – 1105 CE)' # now that compDate is None, period_string should return Rashi's birth to death years + assert i.best_time_period().period_string('en') == ' (1040 – 1105 CE)' # now that compDate is None, period_string should return Rashi's birth to death years def test_invalid_index_save_no_hebrew_collective_title(): title = 'Bartenura (The Next Generation)' diff --git a/sefaria/model/timeperiod.py b/sefaria/model/timeperiod.py index 30ff0a1df9..43c8836f54 100644 --- a/sefaria/model/timeperiod.py +++ b/sefaria/model/timeperiod.py @@ -241,7 +241,7 @@ def get_generations(include_doubles = False): arg = {"$in": ["Generation", "Two Generations"]} if include_doubles else "Generation" return TimePeriodSet._get_typed_set(arg) -class PersonTimePeriod(TimePeriod): +class LifePeriod(TimePeriod): def period_string(self, lang): diff --git a/sefaria/model/topic.py b/sefaria/model/topic.py index 8b0f6ca283..fe207945f5 100644 --- a/sefaria/model/topic.py +++ b/sefaria/model/topic.py @@ -4,7 +4,7 @@ from .text import Ref, IndexSet, AbstractTextRecord from .category import Category from sefaria.system.exceptions import InputError, DuplicateRecordError -from sefaria.model.timeperiod import PersonTimePeriod +from sefaria.model.timeperiod import TimePeriod, LifePeriod from sefaria.system.database import db import structlog, bleach from sefaria.model.place import Place @@ -422,7 +422,7 @@ def contents(self, **kwargs): d = super(PersonTopic, self).contents(**kwargs) if annotate_time_period: d = self.annotate_place(d) - tp = self.most_accurate_time_period() + tp = self.most_accurate_life_period() if tp is not None: d['timePeriod'] = { "name": { @@ -438,31 +438,36 @@ def contents(self, **kwargs): # A person may have an era, a generation, or a specific birth and death years, which each may be approximate. # They may also have none of these... - def most_accurate_time_period(self) -> Optional[PersonTimePeriod]: + def _most_accurate_period(self, obj) -> Optional[LifePeriod]: if self.get_property("birthYear") and self.get_property("deathYear"): - return PersonTimePeriod({ + return obj({ "start": self.get_property("birthYear"), "startIsApprox": self.get_property("birthYearIsApprox", False), "end": self.get_property("deathYear"), "endIsApprox": self.get_property("deathYearIsApprox", False) }) elif self.get_property("birthYear") and self.get_property("era", "CO"): - return PersonTimePeriod({ + return obj({ "start": self.get_property("birthYear"), "startIsApprox": self.get_property("birthYearIsApprox", False), }) elif self.get_property("deathYear"): - return PersonTimePeriod({ + return obj({ "end": self.get_property("deathYear"), "endIsApprox": self.get_property("deathYearIsApprox", False) }) elif self.get_property("generation"): - return PersonTimePeriod().load({"symbol": self.get_property("generation")}) + return obj().load({"symbol": self.get_property("generation")}) elif self.get_property("era"): - return PersonTimePeriod().load({"symbol": self.get_property("era")}) + return obj().load({"symbol": self.get_property("era")}) else: return None + def most_accurate_time_period(self): + return self._most_accurate_period(TimePeriod) + + def most_accurate_life_period(self): + return self._most_accurate_period(LifePeriod) class AuthorTopic(PersonTopic): """ From 1f28debc7d8b6a550dc770d7a911b1dc8b6f1583 Mon Sep 17 00:00:00 2001 From: yonadavGit <yonadav.le@gmail.com> Date: Thu, 2 Nov 2023 21:54:25 +0200 Subject: [PATCH 388/756] chore(Portal model, tests, client): remove newsletter.title_url field --- sefaria/model/portal.py | 4 ---- sefaria/model/tests/portal_test.py | 8 -------- static/js/NavSidebar.jsx | 3 +-- 3 files changed, 1 insertion(+), 14 deletions(-) diff --git a/sefaria/model/portal.py b/sefaria/model/portal.py index 24a45c271f..020083cdaa 100644 --- a/sefaria/model/portal.py +++ b/sefaria/model/portal.py @@ -90,7 +90,6 @@ def _validate(self): newsletter_schema = { "title": ({"en": (str, "required"), "he": (str, "required")}, "required"), - "title_url": (str, "optional"), "description": ({"en": (str, "required"), "he": (str, "required")}, "optional"), "api_schema": ({"http_method": (str, "required"), "payload": ({"first_name_key": (str, "optional"), "last_name_key": (str, "optional"), "email_key": (str, "optional")}, "optional")} @@ -114,9 +113,6 @@ def _validate(self): abst.validate_dictionary(self.organization, organization_schema) if hasattr(self, "newsletter"): abst.validate_dictionary(self.newsletter, newsletter_schema) - title_url = self.newsletter.get("title_url") - if title_url: - validate_url(title_url) http_method = self.newsletter.get("api_schema", {}).get("http_method") if http_method: validate_http_method(http_method) diff --git a/sefaria/model/tests/portal_test.py b/sefaria/model/tests/portal_test.py index 3467ad67bb..02f56407cc 100644 --- a/sefaria/model/tests/portal_test.py +++ b/sefaria/model/tests/portal_test.py @@ -32,7 +32,6 @@ "en": "Newsletter Title", "he": "Newsletter Hebrew Title" }, - "title_url": "https://newsletter-url.com", "description": { "en": "Newsletter English Description", "he": "Newsletter Hebrew Description" @@ -102,7 +101,6 @@ "en": "Newsletter Title", "he": "Newsletter Hebrew Title" }, - "title_url": "https://newsletter-url.com", "description": { "en": "Newsletter English Description", "he": "Newsletter Hebrew Description" @@ -190,7 +188,6 @@ "en": "Newsletter Title", "he": "Newsletter Hebrew Title" }, - "title_url": "https://newsletter-url.com", "description": { "en": "Newsletter English Description", "he": "Newsletter Hebrew Description" @@ -325,7 +322,6 @@ "en": "Newsletter Title", "he": "Newsletter Hebrew Title" }, - "title_url": "https://newsletter-url.com", "description": { "en": "Newsletter English Description", "he": "Newsletter Hebrew Description" @@ -367,7 +363,6 @@ "fr": "Titre de la newsletter", "he": "Newsletter Hebrew Title" }, - "title_url": "https://newsletter-url.com", "description": { "en": "Newsletter English Description", "he": "Newsletter Hebrew Description" @@ -390,7 +385,6 @@ "en": "English Title", "he": "Hebrew Title" }, - "title_url": "https://example.com", "image_uri": "gs://your-bucket/image.jpg", "description": { "en": "English Description", @@ -410,7 +404,6 @@ "en": "Newsletter Title", "he": "Newsletter Hebrew Title" }, - "title_url": "https://newsletter-url.com", "description": { "en": "Newsletter English Description", "he": "Newsletter Hebrew Description" @@ -433,7 +426,6 @@ "en": "About Us", "he": "עלינו" }, - "title_url": 12345, "image_uri": 67890, "description": { "en": "Description in English", diff --git a/static/js/NavSidebar.jsx b/static/js/NavSidebar.jsx index 9945afe21e..baeb12a952 100644 --- a/static/js/NavSidebar.jsx +++ b/static/js/NavSidebar.jsx @@ -833,9 +833,8 @@ const PortalOrganization = ({title, description}) => { }; -const PortalNewsletter = ({title, title_url, description}) => { +const PortalNewsletter = ({title, description}) => { let titleElement = <ModuleTitle en={title.en} he={title.he} />; - if (title_url) { titleElement = <a href={title_url}>{titleElement}</a>; } return( <Module> From e5f8a3d1ad69d88afe47045cd0c787fe06abc8c4 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Sun, 5 Nov 2023 08:43:43 +0200 Subject: [PATCH 389/756] chore(tests): remain tests db after done. --- build/ci/sandbox-values.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/build/ci/sandbox-values.yaml b/build/ci/sandbox-values.yaml index 776d232677..5cd20dc863 100644 --- a/build/ci/sandbox-values.yaml +++ b/build/ci/sandbox-values.yaml @@ -1,6 +1,8 @@ sandbox: "true" contentSandbox: "true" -deployEnv: +restore: + cleanup: false +deployEnv: previousServicesCount: "1" web: containerImage: From af70730c12469b43098f8fd7ba48f7c4f804d02c Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Sun, 5 Nov 2023 09:10:21 +0200 Subject: [PATCH 390/756] chore: started fixing failing tests --- sefaria/helper/tests/topic_test.py | 21 +++++++++++++-------- sefaria/helper/topic.py | 7 ++++--- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/sefaria/helper/tests/topic_test.py b/sefaria/helper/tests/topic_test.py index 66a635f394..b5d883d114 100644 --- a/sefaria/helper/tests/topic_test.py +++ b/sefaria/helper/tests/topic_test.py @@ -83,31 +83,36 @@ def child_of_root_wout_self_link(root_wout_self_link): def test_title_and_desc(root_wout_self_link, child_of_root_wout_self_link, root_with_self_link, child_of_root_with_self_link, grandchild_of_root_with_self_link): for t in [root_wout_self_link, child_of_root_wout_self_link, root_with_self_link, child_of_root_with_self_link, grandchild_of_root_with_self_link]: - new_values = {"title": "new title", "heTitle": "new hebrew title", "description": {"en": "desc", "he": "hebrew desc"}} + new_values = {"title": "new title", + "altTitles": {"en": ["New Alt title 1"], "he": ["New He Alt Title 1"]}, + "heTitle": "new hebrew title", "description": {"en": "new desc", "he": "new hebrew desc"}} update_topic(t["topic"], **new_values) + t["topic"] = Topic.init(t["topic"].slug) assert t["topic"].description == new_values["description"] - assert t["topic"].get_primary_title('en') == new_values['title'] assert t["topic"].get_primary_title('he') == new_values['heTitle'] + assert t["topic"].get_titles('en') == ['New Alt title 1', 'new title'] def test_change_categories_and_titles(root_wout_self_link, root_with_self_link): # tests moving both root categories down the tree and back up and asserting that moving down the tree changes the tree # and assert that moving it back to the root position yields the original tree. - # also tests - orig_tree_from_normal_root = library.get_topic_toc_json_recursive(root_wout_self_link["topic"]) orig_tree_from_root_with_self_link = library.get_topic_toc_json_recursive(root_with_self_link["topic"]) orig_trees = [orig_tree_from_normal_root, orig_tree_from_root_with_self_link] roots = [root_wout_self_link["topic"], root_with_self_link["topic"]] orig_titles = [roots[0].get_primary_title('en'), roots[1].get_primary_title('en')] + orig_he_titles = [roots[0].get_primary_title('he'), roots[1].get_primary_title('he')] for i, root in enumerate(roots): other_root = roots[1 - i] - update_topic(root, title=f"fake new title {i+1}", category=other_root.slug) # move root to be child of other root + update_topic(root, title=f"fake new title {i+1}", heTitle=f"fake new he title {i+1}", category=other_root.slug) # move root to be child of other root new_tree = library.get_topic_toc_json_recursive(other_root) assert new_tree != orig_trees[i] # assert that the changes in the tree have occurred - assert root.get_primary_title('en') != orig_titles[i] - update_topic(root, title=orig_titles[i], category=Topic.ROOT) # move it back to the main menu - assert root.get_primary_title('en') == orig_titles[i] + assert root.get_titles('en') != [orig_titles[i]] + assert root.get_titles('he') != [orig_he_titles[i]] + update_topic(root, title=orig_titles[i], heTitle=orig_he_titles[i], category=Topic.ROOT) # move it back to the main menu + assert root.get_titles('en') == [orig_titles[i]] + assert root.get_titles('he') == [orig_he_titles[i]] + final_tree_from_normal_root = library.get_topic_toc_json_recursive(roots[0]) final_tree_from_root_with_self_link = library.get_topic_toc_json_recursive(roots[1]) diff --git a/sefaria/helper/topic.py b/sefaria/helper/topic.py index a18f15cea0..53a24e22fe 100644 --- a/sefaria/helper/topic.py +++ b/sefaria/helper/topic.py @@ -1046,8 +1046,9 @@ def update_topic_titles(topic_obj, data): for lang in ['en', 'he']: for title in topic_obj.get_titles(lang): topic_obj.remove_title(title, lang) - for title in data['altTitles'][lang]: - topic_obj.add_title(title, lang) + if 'altTitles' in data: + for title in data['altTitles'][lang]: + topic_obj.add_title(title, lang) topic_obj.add_title(data['title'], 'en', True, True) topic_obj.add_title(data['heTitle'], 'he', True, True) @@ -1099,7 +1100,7 @@ def update_topic(topic_obj, **kwargs): if kwargs.get('category') == 'authors': topic_obj = update_authors_place_and_time(topic_obj, kwargs) - if 'category' in kwargs and kwargs['category'] != kwargs['origCategory']: + if 'category' in kwargs and kwargs['category'] != kwargs.get('origCategory', kwargs['category']): orig_link = IntraTopicLink().load({"linkType": "displays-under", "fromTopic": topic_obj.slug, "toTopic": {"$ne": topic_obj.slug}}) old_category = orig_link.toTopic if orig_link else Topic.ROOT if old_category != kwargs['category']: From e1f76add52b139eaec38745da4bd6afcbc30c713 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Sun, 5 Nov 2023 10:01:04 +0200 Subject: [PATCH 391/756] refactor(timeperiod): replace name obj by time_period_class and add typing. --- sefaria/model/topic.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/sefaria/model/topic.py b/sefaria/model/topic.py index fe207945f5..4b1e4f028f 100644 --- a/sefaria/model/topic.py +++ b/sefaria/model/topic.py @@ -9,6 +9,7 @@ import structlog, bleach from sefaria.model.place import Place import regex as re +from typing import Type logger = structlog.get_logger(__name__) @@ -438,28 +439,28 @@ def contents(self, **kwargs): # A person may have an era, a generation, or a specific birth and death years, which each may be approximate. # They may also have none of these... - def _most_accurate_period(self, obj) -> Optional[LifePeriod]: + def _most_accurate_period(self, time_period_class: Type[TimePeriod]) -> Optional[LifePeriod]: if self.get_property("birthYear") and self.get_property("deathYear"): - return obj({ + return time_period_class({ "start": self.get_property("birthYear"), "startIsApprox": self.get_property("birthYearIsApprox", False), "end": self.get_property("deathYear"), "endIsApprox": self.get_property("deathYearIsApprox", False) }) elif self.get_property("birthYear") and self.get_property("era", "CO"): - return obj({ + return time_period_class({ "start": self.get_property("birthYear"), "startIsApprox": self.get_property("birthYearIsApprox", False), }) elif self.get_property("deathYear"): - return obj({ + return time_period_class({ "end": self.get_property("deathYear"), "endIsApprox": self.get_property("deathYearIsApprox", False) }) elif self.get_property("generation"): - return obj().load({"symbol": self.get_property("generation")}) + return time_period_class().load({"symbol": self.get_property("generation")}) elif self.get_property("era"): - return obj().load({"symbol": self.get_property("era")}) + return time_period_class().load({"symbol": self.get_property("era")}) else: return None From 92d00bf55d17d393c4153edb93167016858e2079 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Sun, 5 Nov 2023 10:04:21 +0200 Subject: [PATCH 392/756] docs(timeperiod): doc strings for most_accurate_time_period and most_accurate_life_period. --- sefaria/model/topic.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sefaria/model/topic.py b/sefaria/model/topic.py index 4b1e4f028f..a6f9d7c549 100644 --- a/sefaria/model/topic.py +++ b/sefaria/model/topic.py @@ -465,9 +465,15 @@ def _most_accurate_period(self, time_period_class: Type[TimePeriod]) -> Optional return None def most_accurate_time_period(self): + ''' + :return: most accurate period as TimePeriod (used when a person's LifePeriod is using as a a general TimePeriod) + ''' return self._most_accurate_period(TimePeriod) def most_accurate_life_period(self): + ''' + :return: most accurate period as LifePeriod. currently the difference from TimePeriod is only the string + ''' return self._most_accurate_period(LifePeriod) class AuthorTopic(PersonTopic): From 16bdd240f2749b4d133edf69b9fab365c0b11b2a Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Sun, 5 Nov 2023 10:14:52 +0200 Subject: [PATCH 393/756] test(ref): test for hebrew talmud ref with line. --- sefaria/model/tests/he_ref_test.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sefaria/model/tests/he_ref_test.py b/sefaria/model/tests/he_ref_test.py index 55fa32975e..b5c615b9ed 100644 --- a/sefaria/model/tests/he_ref_test.py +++ b/sefaria/model/tests/he_ref_test.py @@ -100,6 +100,10 @@ def test_talmud(self): assert r.sections[0] == 58 assert len(r.sections) == 1 + r = m.Ref("סוטה לה א:יא") + assert r.book == 'Sotah' + assert r.sections == [69, 11] + def test_length_catching(self): with pytest.raises(InputError): r = m.Ref('דברים שם') From 7ada104eebf0cc65ccea91f512d99c26d172896e Mon Sep 17 00:00:00 2001 From: Noah Santacruz <noahssantacruz@gmail.com> Date: Sun, 5 Nov 2023 10:20:50 +0200 Subject: [PATCH 394/756] docs(timeperiod): clarify docs. fix typos. --- sefaria/model/topic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sefaria/model/topic.py b/sefaria/model/topic.py index a6f9d7c549..30e92c29b1 100644 --- a/sefaria/model/topic.py +++ b/sefaria/model/topic.py @@ -466,7 +466,7 @@ def _most_accurate_period(self, time_period_class: Type[TimePeriod]) -> Optional def most_accurate_time_period(self): ''' - :return: most accurate period as TimePeriod (used when a person's LifePeriod is using as a a general TimePeriod) + :return: most accurate period as TimePeriod (used when a person's LifePeriod should be formatted like a general TimePeriod) ''' return self._most_accurate_period(TimePeriod) From 3a00bf91f032128b3ce1d3e279e2641526cde822 Mon Sep 17 00:00:00 2001 From: Noah Santacruz <noahssantacruz@gmail.com> Date: Sun, 5 Nov 2023 10:20:59 +0200 Subject: [PATCH 395/756] docs(timeperiod): clarify docs. fix typos. --- sefaria/model/topic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sefaria/model/topic.py b/sefaria/model/topic.py index 30e92c29b1..37ffcae5b7 100644 --- a/sefaria/model/topic.py +++ b/sefaria/model/topic.py @@ -472,7 +472,7 @@ def most_accurate_time_period(self): def most_accurate_life_period(self): ''' - :return: most accurate period as LifePeriod. currently the difference from TimePeriod is only the string + :return: most accurate period as LifePeriod. currently the only difference from TimePeriod is the way the time period is formatted as a string. ''' return self._most_accurate_period(LifePeriod) From 4f1620138bcdac8ff4764eb577c76310731dc0bd Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Sun, 5 Nov 2023 12:31:37 +0200 Subject: [PATCH 396/756] feat(Backend topic images): Add optional attribute for image --- sefaria/model/topic.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sefaria/model/topic.py b/sefaria/model/topic.py index b259b88311..dbc865f83b 100644 --- a/sefaria/model/topic.py +++ b/sefaria/model/topic.py @@ -43,7 +43,8 @@ class Topic(abst.SluggedAbstractMongoRecord, AbstractTitledObject): 'good_to_promote', 'description_published', # bool to keep track of which descriptions we've vetted 'isAmbiguous', # True if topic primary title can refer to multiple other topics - "data_source" #any topic edited manually should display automatically in the TOC and this flag ensures this + "data_source", #any topic edited manually should display automatically in the TOC and this flag ensures this + 'image' ] ROOT = "Main Menu" # the root of topic TOC is not a topic, so this is a fake slug. we know it's fake because it's not in normal form # this constant is helpful in the topic editor tool functions in this file From da3dafa0f9cea71cfc5b79e4c4ec5610be695e48 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Sun, 5 Nov 2023 13:14:41 +0200 Subject: [PATCH 397/756] fix(Topic Tests): fix 2 failing tests --- sefaria/helper/tests/topic_test.py | 11 +++++------ sefaria/helper/topic.py | 9 +++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/sefaria/helper/tests/topic_test.py b/sefaria/helper/tests/topic_test.py index b5d883d114..4b33cd8c70 100644 --- a/sefaria/helper/tests/topic_test.py +++ b/sefaria/helper/tests/topic_test.py @@ -82,15 +82,14 @@ def child_of_root_wout_self_link(root_wout_self_link): def test_title_and_desc(root_wout_self_link, child_of_root_wout_self_link, root_with_self_link, child_of_root_with_self_link, grandchild_of_root_with_self_link): - for t in [root_wout_self_link, child_of_root_wout_self_link, root_with_self_link, child_of_root_with_self_link, grandchild_of_root_with_self_link]: - new_values = {"title": "new title", - "altTitles": {"en": ["New Alt title 1"], "he": ["New He Alt Title 1"]}, - "heTitle": "new hebrew title", "description": {"en": "new desc", "he": "new hebrew desc"}} + for count, t in enumerate([root_wout_self_link, child_of_root_wout_self_link, root_with_self_link, child_of_root_with_self_link, grandchild_of_root_with_self_link]): + new_values = {"title": f"new title {count+1}", + "altTitles": {"en": [f"New Alt title {count+1}"], "he": [f"New He Alt Title {count+1}"]}, + "heTitle": f"new hebrew title {count+1}", "description": {"en": f"new desc", "he": "new hebrew desc"}} update_topic(t["topic"], **new_values) - t["topic"] = Topic.init(t["topic"].slug) assert t["topic"].description == new_values["description"] assert t["topic"].get_primary_title('he') == new_values['heTitle'] - assert t["topic"].get_titles('en') == ['New Alt title 1', 'new title'] + assert t["topic"].get_titles('en') == [new_values["title"]]+new_values["altTitles"]['en'] def test_change_categories_and_titles(root_wout_self_link, root_with_self_link): diff --git a/sefaria/helper/topic.py b/sefaria/helper/topic.py index 53a24e22fe..126607fcb3 100644 --- a/sefaria/helper/topic.py +++ b/sefaria/helper/topic.py @@ -1043,15 +1043,16 @@ def topic_change_category(topic_obj, new_category, old_category="", rebuild=Fals return topic_obj def update_topic_titles(topic_obj, data): - for lang in ['en', 'he']: + new_primary = {"en": data['title'], "he": data["heTitle"]} + for lang in ['en', 'he']: # first add new primary titles then remove old alt titles and add new alt titles + topic_obj.add_title(new_primary[lang], lang, True, True) for title in topic_obj.get_titles(lang): - topic_obj.remove_title(title, lang) + if title != new_primary[lang]: + topic_obj.remove_title(title, lang) if 'altTitles' in data: for title in data['altTitles'][lang]: topic_obj.add_title(title, lang) - topic_obj.add_title(data['title'], 'en', True, True) - topic_obj.add_title(data['heTitle'], 'he', True, True) return topic_obj From 356765366b0be31d82493138ef709c220912c30d Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Sun, 5 Nov 2023 15:21:34 +0200 Subject: [PATCH 398/756] refactor(ref): remove stop_parsing since it has no use. --- sefaria/model/schema.py | 40 ++++++++++++---------------------------- 1 file changed, 12 insertions(+), 28 deletions(-) diff --git a/sefaria/model/schema.py b/sefaria/model/schema.py index b93a8bf84a..fe9537c7d3 100644 --- a/sefaria/model/schema.py +++ b/sefaria/model/schema.py @@ -1073,12 +1073,11 @@ def full_regex(self, title, lang, anchored=True, compiled=True, capture_title=Fa def address_regex(self, lang, **kwargs): group = "a0" reg = self._addressTypes[0].regex(lang, group, **kwargs) - if not self._addressTypes[0].stop_parsing(lang): - for i in range(1, self.depth): - group = "a{}".format(i) - reg += "(" + self.after_address_delimiter_ref + self._addressTypes[i].regex(lang, group, **kwargs) + ")" - if not kwargs.get("strict", False): - reg += "?" + for i in range(1, self.depth): + group = "a{}".format(i) + reg += "(" + self.after_address_delimiter_ref + self._addressTypes[i].regex(lang, group, **kwargs) + ")" + if not kwargs.get("strict", False): + reg += "?" if kwargs.get("match_range"): # TODO there is a potential error with this regex. it fills in toSections starting from highest depth and going to lowest. @@ -1089,14 +1088,13 @@ def address_regex(self, lang, **kwargs): reg += r"(?=\S)" # must be followed by something (Lookahead) group = "ar0" reg += self._addressTypes[0].regex(lang, group, **kwargs) - if not self._addressTypes[0].stop_parsing(lang): - reg += "?" - for i in range(1, self.depth): - reg += r"(?:(?:" + self.after_address_delimiter_ref + r")?" - group = "ar{}".format(i) - reg += "(" + self._addressTypes[i].regex(lang, group, **kwargs) + ")" - # assuming strict isn't relevant on ranges # if not kwargs.get("strict", False): - reg += ")?" + reg += "?" + for i in range(1, self.depth): + reg += r"(?:(?:" + self.after_address_delimiter_ref + r")?" + group = "ar{}".format(i) + reg += "(" + self._addressTypes[i].regex(lang, group, **kwargs) + ")" + # assuming strict isn't relevant on ranges # if not kwargs.get("strict", False): + reg += ")?" reg += r")?" # end range clause return reg @@ -2072,15 +2070,6 @@ def hebrew_number_regex(): [\u05d0-\u05d8]? # One or zero alef-tet (1-9) )""" - def stop_parsing(self, lang): - """ - If this is true, the regular expression will stop parsing at this address level for this language. - It is currently checked for only in the first address position, and is used for Hebrew Folio addresses (since Folios are just in alt_structs maybe it hos no effect)_. - :param lang: "en" or "he" - :return bool: - """ - return False - def toNumber(self, lang, s): """ Return the numerical form of s in this address scheme @@ -2485,11 +2474,6 @@ def _core_regex(self, lang, group_id=None, **kwargs): return reg - def stop_parsing(self, lang): - if lang == "he": - return True - return False - def toNumber(self, lang, s, **kwargs): if lang == "en": try: From 141106c059ed85cd8633c57fd6e0f456e4fdf279 Mon Sep 17 00:00:00 2001 From: Lev Israel <eliezer.israel@gmail.com> Date: Sun, 5 Nov 2023 17:30:14 +0200 Subject: [PATCH 399/756] chore: Update VersionState Docs --- sefaria/model/version_state.py | 81 +++++++++++----------------------- 1 file changed, 25 insertions(+), 56 deletions(-) diff --git a/sefaria/model/version_state.py b/sefaria/model/version_state.py index 62aea25423..8b1b4a95df 100644 --- a/sefaria/model/version_state.py +++ b/sefaria/model/version_state.py @@ -20,73 +20,42 @@ except ImportError: USE_VARNISH = False ''' -old count docs were: - c["allVersionCounts"] - c["availableTexts"] = { - "en": - "he": - } - - c["availableCounts"] = { # - "en": - "he": - } - - c["percentAvailable"] = { - "he": - "en": - } - - c["textComplete"] = { - "he": - "en" - } - - c['estimatedCompleteness'] = { - "he": { - 'estimatedPercent': - 'availableSegmentCount': # is availableCounts[-1] - 'percentAvailableInvalid': - 'percentAvailable': # duplicate - 'isSparse': - } - "en": - } - - -and now self.content is: - { - "_en": { - "availableTexts": - "availableCounts": - "percentAvailable": - "textComplete": - 'completenessPercent': - 'percentAvailableInvalid': - 'sparseness': # was isSparse - } - "_he": ... - "_all" { - "availableTexts": - "shape": - For depth 1: Integer - length - For depth 2: List of chapter lengths - For depth 3: List of list of chapter lengths? - } - } - ''' class VersionState(abst.AbstractMongoRecord, AbstractSchemaContent): """ This model overrides default init/load/save behavior, since there is one and only one VersionState record for each Index record. + + The `content` attribute is a dictionary which is the root of a tree, mirroring the shape of a Version, where the leaf nodes of the tree are dictionaries with a shape like the following:: + { + "_en": { + "availableTexts": + "availableCounts": + "percentAvailable": + "textComplete": + 'completenessPercent': + 'percentAvailableInvalid': + 'sparseness': # was isSparse + } + "_he": ... + "_all" { + "availableTexts": + "shape": + For depth 1: Integer - length + For depth 2: List of chapter lengths + For depth 3: List of list of chapter lengths? + } + } + + For example, the `content` attribute for `Pesach Haggadah` will be a dictionary with keys: "Kadesh", "Urchatz", "Karpas" ... each with a value of a dictionary like the above. They key "Magid" has a value of dictionary with keys "Ha Lachma Anya", etc. + """ collection = 'vstate' required_attrs = [ "title", # Index title - "content" # tree of data about nodes + "content" # tree of data about nodes. See above. ] optional_attrs = [ "flags", From 4ed4179fa8b01f0251c2d3dc5c293c2601a81a14 Mon Sep 17 00:00:00 2001 From: Ephraim <ephraim@sefaria.org> Date: Mon, 6 Nov 2023 09:13:33 +0200 Subject: [PATCH 400/756] refactor(js-general): Remove unused React dependencies from sefaria.js This is done to ensure that sefaria.js uses vanilla js and does not become dependent on React --- static/js/sefaria/sefaria.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index 28aeffcae1..ff019ec44c 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -9,8 +9,6 @@ import Track from './track'; import Hebrew from './hebrew'; import Util from './util'; import $ from './sefariaJquery'; -import {useContext} from "react"; -import {ContentLanguageContext} from "../context"; let Sefaria = Sefaria || { From a91a3e5962c06140ee753575c6d7392c61bb1678 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Mon, 6 Nov 2023 10:05:26 +0200 Subject: [PATCH 401/756] chore(portal): add new lines in portal.py --- sefaria/model/portal.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sefaria/model/portal.py b/sefaria/model/portal.py index 020083cdaa..a80e9d14f8 100644 --- a/sefaria/model/portal.py +++ b/sefaria/model/portal.py @@ -14,6 +14,7 @@ def __init__(self, url): self.message = f"'{url}' is not a valid URL." super().__init__(self.message) + def validate_url(url): try: # Attempt to parse the URL @@ -24,12 +25,15 @@ def validate_url(url): except ValidationError: # URL parsing failed raise InvalidURLException(url) + + class InvalidHTTPMethodException(Exception): def __init__(self, method): self.method = method self.message = f"'{method}' is not a valid HTTP API method." super().__init__(self.message) + def validate_http_method(method): """ Validate if a string represents a valid HTTP API method. @@ -51,6 +55,7 @@ def validate_http_method(method): else: raise InvalidHTTPMethodException(method) + class Portal(abst.SluggedAbstractMongoRecord): collection = 'portals' slug_fields = ['slug'] From ee880f2cab519a084ba6bacb54597ad2296d1309 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Mon, 6 Nov 2023 10:10:09 +0200 Subject: [PATCH 402/756] feat(portal): add name field to portal --- sefaria/model/portal.py | 3 +++ sefaria/model/tests/portal_test.py | 15 +++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/sefaria/model/portal.py b/sefaria/model/portal.py index a80e9d14f8..74038fd419 100644 --- a/sefaria/model/portal.py +++ b/sefaria/model/portal.py @@ -63,6 +63,7 @@ class Portal(abst.SluggedAbstractMongoRecord): required_attrs = [ "slug", "about", + "name", ] optional_attrs = [ "mobile", @@ -101,6 +102,8 @@ def _validate(self): , "optional") } + abst.validate_dictionary(self.name, {"en": (str, "required"), "he": (str, "required")}) + if hasattr(self, "about"): abst.validate_dictionary(self.about, about_schema) title_url = self.about.get("title_url") diff --git a/sefaria/model/tests/portal_test.py b/sefaria/model/tests/portal_test.py index 02f56407cc..dfe9aed7f0 100644 --- a/sefaria/model/tests/portal_test.py +++ b/sefaria/model/tests/portal_test.py @@ -7,6 +7,7 @@ valids = [ { "slug": "English Title", + "name": {"en": "a", "he": "b"}, "about": { "title": { "en": "English Title", @@ -48,6 +49,7 @@ }, { "slug": "English Title", + "name": {"en": "a", "he": "b"}, "about": { "title": { "en": "English Title", @@ -76,6 +78,7 @@ }, { "slug": "English Title", + "name": {"en": "a", "he": "b"}, "about": { "title": { "en": "English Title", @@ -117,6 +120,7 @@ }, { "slug": "English Title", + "name": {"en": "a", "he": "b"}, "about": { "title": { "en": "English Title", @@ -141,6 +145,7 @@ }, { "slug": "English Title", + "name": {"en": "a", "he": "b"}, "about": { "title": { "en": "English Title", @@ -163,6 +168,7 @@ }, { "slug": "English Title", + "name": {"en": "a", "he": "b"}, "about": { "title": { "en": "English Title", @@ -204,6 +210,7 @@ }, { "slug": "English Title", + "name": {"en": "a", "he": "b"}, "about": { "title": { "en": "English Title", @@ -228,6 +235,7 @@ }, { "slug": "English Title", + "name": {"en": "a", "he": "b"}, "about": { "title": { "en": "English Title", @@ -258,6 +266,7 @@ }, { "slug": "English Title", + "name": {"en": "a", "he": "b"}, "about": { "title": { "en": "About Us", @@ -271,6 +280,7 @@ # Missing "about" key { "slug": "English Title", + "name": {"en": "a", "he": "b"}, "mobile": { "title": { "en": "Mobile Title", @@ -297,6 +307,7 @@ # Invalid "about.title_url" (not a URL) { "slug": "English Title", + "name": {"en": "a", "he": "b"}, "about": { "title": { "en": "English Title", @@ -339,6 +350,7 @@ # Including invalid field "newsletter.description.fr" { "slug": "English Title", + "name": {"en": "a", "he": "b"}, "about": { "title": { "en": "English Title", @@ -380,6 +392,7 @@ # Invalid "newsletter.api_schema.http_method" (not a valid HTTP method) { "slug": "English Title", + "name": {"en": "a", "he": "b"}, "about": { "title": { "en": "English Title", @@ -421,6 +434,7 @@ # Invalid data types: { "slug": "English Title", + "name": {"en": "a", "he": "b"}, "about": { "title": { "en": "About Us", @@ -436,6 +450,7 @@ { # Incorrect field names "slug": "English Title", + "name": {"en": "a", "he": "b"}, "about": { "title": { "en": "About Us", From d07f6658ff5ef3f7c6431c8c18a47e86027f19a0 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Mon, 6 Nov 2023 10:40:28 +0200 Subject: [PATCH 403/756] fix(timePeriod): typo. --- sefaria/model/timeperiod.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sefaria/model/timeperiod.py b/sefaria/model/timeperiod.py index 43c8836f54..5e94b33fc8 100644 --- a/sefaria/model/timeperiod.py +++ b/sefaria/model/timeperiod.py @@ -191,7 +191,7 @@ def period_string(self, lang): approxMarker[1] ) else: - name += " ({}{}{} { {}{}{})".format( + name += " ({}{}{} {} {}{}{})".format( abs(int(self.start)), " " + labels[0] if labels[0] else "", " " + approxMarker[0] if approxMarker[0] else "", From ab7b5903e599c8f6c07d7661daf536e9eea67499 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Mon, 6 Nov 2023 10:51:25 +0200 Subject: [PATCH 404/756] chore: revert bad part of merge --- static/js/sefaria/sefaria.js | 58 ++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index 19fc267dff..f4733ae38b 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -568,6 +568,64 @@ Sefaria = extend(Sefaria, { } return Promise.resolve(this._versions[ref]); }, + _portals: {}, + getPortal: async function(portalSlug) { + const cachedPortal = Sefaria._portals[portalSlug]; + if (cachedPortal) { + return cachedPortal; + } + const response = await this._ApiPromise(`${Sefaria.apiHost}/api/portals/${portalSlug}`); + Sefaria._portals[portalSlug] = response; + return response; + }, + subscribeSefariaNewsletter: async function(firstName, lastName, email, educatorCheck) { + const response = await fetch(`/api/subscribe/${email}`, + { + method: "POST", + mode: 'same-origin', + headers: { + 'Content-Type': 'application/json', + 'X-CSRFToken': Cookies.get('csrftoken'), + }, + credentials: 'same-origin', + body: JSON.stringify({ + language: Sefaria.interfaceLang === "hebrew" ? "he" : "en", + educator: educatorCheck, + firstName: firstName, + lastName: lastName + }) + } + ); + if (!response.ok) { throw "error"; } + const json = await response.json(); + if (json.error) { throw json; } + return json; + }, + subscribeSteinsaltzNewsletter: async function(firstName, lastName, email) { + const response = await fetch(`/api/subscribe/steinsaltz/${email}`, + { + method: "POST", + mode: 'same-origin', + headers: { + 'Content-Type': 'application/json', + 'X-CSRFToken': Cookies.get('csrftoken'), + }, + credentials: 'same-origin', + body: JSON.stringify({firstName, lastName}), + } + ); + if (!response.ok) { throw "error"; } + const json = await response.json(); + if (json.error) { throw json; } + return json; + }, + subscribeSefariaAndSteinsaltzNewsletter: async function(firstName, lastName, email, educatorCheck) { + const responses = await Promise.all([ + Sefaria.subscribeSefariaNewsletter(firstName, lastName, email, educatorCheck), + Sefaria.subscribeSteinsaltzNewsletter(firstName, lastName, email), + ]); + return {status: "ok"}; + }, filterVersionsObjByLangs: function(versionsObj, langs, includeFilter) { /** * @versionsObj {object} whode keys are language codes ('he', 'en' etc.) and values are version objects (like the object that getVersions returns) From ec31bcbccca1ad37da00dd9fa9369114d1b1fd63 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Mon, 6 Nov 2023 11:10:29 +0200 Subject: [PATCH 405/756] feat(timePeriod): add determine_year_estimate to TimePeriod. --- sefaria/model/timeperiod.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sefaria/model/timeperiod.py b/sefaria/model/timeperiod.py index 5e94b33fc8..33bd3d389a 100644 --- a/sefaria/model/timeperiod.py +++ b/sefaria/model/timeperiod.py @@ -225,6 +225,16 @@ def get_people_in_generation(self, include_doubles = True): else: return topic.Topic({"properties.generation.value": self.symbol}) + def determine_year_estimate(self): + start = getattr(self, 'start', None) + end = getattr(self, 'end', None) + if start != None and end != None: + return round((int(start) + int(end)) / 2) + elif start != None: + return int(start) + elif end != None: + return int(end) + class TimePeriodSet(abst.AbstractMongoSet): recordClass = TimePeriod From 585b6a056a90c7a4b2de24dfe74caf5fd5e7e8de Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Mon, 6 Nov 2023 11:12:07 +0200 Subject: [PATCH 406/756] fix(elasticSearch): use TimePeriod's determine_year_estimate function rather than the start attribute. --- sefaria/pagesheetrank.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sefaria/pagesheetrank.py b/sefaria/pagesheetrank.py index 1c2087ed2b..603de805bc 100644 --- a/sefaria/pagesheetrank.py +++ b/sefaria/pagesheetrank.py @@ -212,8 +212,8 @@ def put_link_in_graph(ref1, ref2, weight=1.0): refs = [Ref(r) for r in link.refs] tp1 = refs[0].index.best_time_period() tp2 = refs[1].index.best_time_period() - start1 = int(tp1.start) if tp1 else 3000 - start2 = int(tp2.start) if tp2 else 3000 + start1 = int(tp1.determine_year_estimate()) if tp1 else 3000 + start2 = int(tp2.determine_year_estimate()) if tp2 else 3000 older_ref, newer_ref = (refs[0], refs[1]) if start1 < start2 else (refs[1], refs[0]) From a37c01d8c332aeddfa1229f26fc580982cf24c2c Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Mon, 6 Nov 2023 11:17:17 +0200 Subject: [PATCH 407/756] refactor: move exceptions and validators to their appropriate files --- sefaria/model/abstract.py | 68 -------------------------- sefaria/model/portal.py | 64 +++--------------------- sefaria/system/exceptions.py | 37 ++++++++++++++ sefaria/system/validators.py | 95 ++++++++++++++++++++++++++++++++++++ 4 files changed, 138 insertions(+), 126 deletions(-) create mode 100644 sefaria/system/validators.py diff --git a/sefaria/model/abstract.py b/sefaria/model/abstract.py index 2a1723bc17..9a2f53922c 100644 --- a/sefaria/model/abstract.py +++ b/sefaria/model/abstract.py @@ -643,71 +643,3 @@ def foo(obj, **kwargs): rec.save() return foo - -class SchemaValidationException(Exception): - def __init__(self, key, expected_type): - self.key = key - self.expected_type = expected_type - self.message = f"Invalid value for key '{key}'. Expected type: {expected_type}" - super().__init__(self.message) -class SchemaRequiredFieldException(Exception): - def __init__(self, key): - self.key = key - self.message = f"Required field '{key}' is missing." - super().__init__(self.message) - -class SchemaInvalidKeyException(Exception): - def __init__(self, key): - self.key = key - self.message = f"Invalid key '{key}' found in data dictionary." - super().__init__(self.message) - - -def validate_dictionary(data, schema): - """ - Validates that a given dictionary complies with the provided schema. - - Args: - data (dict): The dictionary to be validated. - schema (dict): The schema dictionary specifying the expected structure. - - Raises: - SchemaValidationException: If the data does not comply with the schema. - - Returns: - bool: True if the data complies with the schema, False otherwise. - """ - - for key, value_type in schema.items(): - if not (isinstance(value_type, tuple) and len(value_type) == 2 and value_type[1] in ["optional", "required"]): - raise ValueError(f"Invalid schema definition for key '{key}'. Use ('type', 'optional') or ('type', 'required').") - - # Check for keys in data that are not in schema - for key in data.keys(): - if key not in schema: - raise SchemaInvalidKeyException(key) - - for key, value_type in schema.items(): - # Check if the key exists in the data dictionary - if key not in data: - # Check if the field is optional (not required) - if isinstance(value_type, tuple) and len(value_type) == 2 and value_type[1] == "optional": - continue # Field is optional, so skip validation - else: - raise SchemaRequiredFieldException(key) - - # Check if the expected type is a nested dictionary - if isinstance(value_type[0], dict): - nested_data = data[key] - nested_schema = value_type[0] - try: - # Recursively validate the nested dictionary - validate_dictionary(nested_data, nested_schema) - except SchemaValidationException as e: - # If validation fails for the nested dictionary, re-raise the exception with the key - raise SchemaValidationException(f"{key}.{e.key}", e.expected_type) - - # Check the type of the value in the data dictionary - elif not isinstance(data[key], value_type[0]): - raise SchemaValidationException(key, value_type[0]) - return True diff --git a/sefaria/model/portal.py b/sefaria/model/portal.py index 74038fd419..56e0e2a7d4 100644 --- a/sefaria/model/portal.py +++ b/sefaria/model/portal.py @@ -1,61 +1,9 @@ -# coding=utf-8 -from urllib.parse import urlparse from . import abstract as abst -import urllib.parse -from django.core.validators import URLValidator -from django.core.exceptions import ValidationError +from sefaria.system.validators import validate_dictionary, validate_url, validate_http_method import structlog logger = structlog.get_logger(__name__) -class InvalidURLException(Exception): - def __init__(self, url): - self.url = url - self.message = f"'{url}' is not a valid URL." - super().__init__(self.message) - - -def validate_url(url): - try: - # Attempt to parse the URL - validator = URLValidator() - validator(url) - return True - - except ValidationError: - # URL parsing failed - raise InvalidURLException(url) - - -class InvalidHTTPMethodException(Exception): - def __init__(self, method): - self.method = method - self.message = f"'{method}' is not a valid HTTP API method." - super().__init__(self.message) - - -def validate_http_method(method): - """ - Validate if a string represents a valid HTTP API method. - - Args: - method (str): The HTTP method to validate. - - Raises: - InvalidHTTPMethodException: If the method is not valid. - - Returns: - bool: True if the method is valid, False otherwise. - """ - valid_methods = ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"] - - # Convert the method to uppercase and check if it's in the list of valid methods - if method.upper() in valid_methods: - return True - else: - raise InvalidHTTPMethodException(method) - - class Portal(abst.SluggedAbstractMongoRecord): collection = 'portals' slug_fields = ['slug'] @@ -102,15 +50,15 @@ def _validate(self): , "optional") } - abst.validate_dictionary(self.name, {"en": (str, "required"), "he": (str, "required")}) + validate_dictionary(self.name, {"en": (str, "required"), "he": (str, "required")}) if hasattr(self, "about"): - abst.validate_dictionary(self.about, about_schema) + validate_dictionary(self.about, about_schema) title_url = self.about.get("title_url") if title_url: validate_url(title_url) if hasattr(self, "mobile"): - abst.validate_dictionary(self.mobile, mobile_schema) + validate_dictionary(self.mobile, mobile_schema) android_link = self.mobile.get("android_link") if android_link: validate_url(android_link) @@ -118,9 +66,9 @@ def _validate(self): if ios_link: validate_url(ios_link) if hasattr(self, "organization"): - abst.validate_dictionary(self.organization, organization_schema) + validate_dictionary(self.organization, organization_schema) if hasattr(self, "newsletter"): - abst.validate_dictionary(self.newsletter, newsletter_schema) + validate_dictionary(self.newsletter, newsletter_schema) http_method = self.newsletter.get("api_schema", {}).get("http_method") if http_method: validate_http_method(http_method) diff --git a/sefaria/system/exceptions.py b/sefaria/system/exceptions.py index 297a7e9b79..72d3493f78 100644 --- a/sefaria/system/exceptions.py +++ b/sefaria/system/exceptions.py @@ -56,9 +56,46 @@ class SheetNotFoundError(InputError): class ManuscriptError(Exception): pass + class MissingKeyError(Exception): pass class SluggedMongoRecordMissingError(Exception): pass + + +class SchemaValidationException(Exception): + def __init__(self, key, expected_type): + self.key = key + self.expected_type = expected_type + self.message = f"Invalid value for key '{key}'. Expected type: {expected_type}" + super().__init__(self.message) + + +class SchemaRequiredFieldException(Exception): + def __init__(self, key): + self.key = key + self.message = f"Required field '{key}' is missing." + super().__init__(self.message) + + +class SchemaInvalidKeyException(Exception): + def __init__(self, key): + self.key = key + self.message = f"Invalid key '{key}' found in data dictionary." + super().__init__(self.message) + + +class InvalidURLException(Exception): + def __init__(self, url): + self.url = url + self.message = f"'{url}' is not a valid URL." + super().__init__(self.message) + + +class InvalidHTTPMethodException(Exception): + def __init__(self, method): + self.method = method + self.message = f"'{method}' is not a valid HTTP API method." + super().__init__(self.message) diff --git a/sefaria/system/validators.py b/sefaria/system/validators.py new file mode 100644 index 0000000000..6c060ab487 --- /dev/null +++ b/sefaria/system/validators.py @@ -0,0 +1,95 @@ +""" +Pre-written validation functions +Useful for validating model schemas when overriding AbstractMongoRecord._validate() +""" + +import urllib.parse +from urllib.parse import urlparse +from django.core.validators import URLValidator +from django.core.exceptions import ValidationError +from sefaria.system.exceptions import SchemaValidationException, SchemaInvalidKeyException, SchemaRequiredFieldException\ + , InvalidHTTPMethodException, InvalidURLException + + +def validate_dictionary(data, schema): + """ + Validates that a given dictionary complies with the provided schema. + + Args: + data (dict): The dictionary to be validated. + schema (dict): The schema dictionary specifying the expected structure. + + Raises: + SchemaValidationException: If the data does not comply with the schema. + + Returns: + bool: True if the data complies with the schema, False otherwise. + """ + + for key, value_type in schema.items(): + if not (isinstance(value_type, tuple) and len(value_type) == 2 and value_type[1] in ["optional", "required"]): + raise ValueError(f"Invalid schema definition for key '{key}'. Use ('type', 'optional') or ('type', 'required').") + + # Check for keys in data that are not in schema + for key in data.keys(): + if key not in schema: + raise SchemaInvalidKeyException(key) + + for key, value_type in schema.items(): + # Check if the key exists in the data dictionary + if key not in data: + # Check if the field is optional (not required) + if isinstance(value_type, tuple) and len(value_type) == 2 and value_type[1] == "optional": + continue # Field is optional, so skip validation + else: + raise SchemaRequiredFieldException(key) + + # Check if the expected type is a nested dictionary + if isinstance(value_type[0], dict): + nested_data = data[key] + nested_schema = value_type[0] + try: + # Recursively validate the nested dictionary + validate_dictionary(nested_data, nested_schema) + except SchemaValidationException as e: + # If validation fails for the nested dictionary, re-raise the exception with the key + raise SchemaValidationException(f"{key}.{e.key}", e.expected_type) + + # Check the type of the value in the data dictionary + elif not isinstance(data[key], value_type[0]): + raise SchemaValidationException(key, value_type[0]) + return True + + +def validate_url(url): + try: + # Attempt to parse the URL + validator = URLValidator() + validator(url) + return True + + except ValidationError: + # URL parsing failed + raise InvalidURLException(url) + + +def validate_http_method(method): + """ + Validate if a string represents a valid HTTP API method. + + Args: + method (str): The HTTP method to validate. + + Raises: + InvalidHTTPMethodException: If the method is not valid. + + Returns: + bool: True if the method is valid, False otherwise. + """ + valid_methods = ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"] + + # Convert the method to uppercase and check if it's in the list of valid methods + if method.upper() in valid_methods: + return True + else: + raise InvalidHTTPMethodException(method) From 05ecddca391a1129b225c294e93a205fbf253280 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Mon, 6 Nov 2023 12:19:58 +0200 Subject: [PATCH 408/756] chore: fix parts of bad merge --- sefaria/model/topic.py | 48 ++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/sefaria/model/topic.py b/sefaria/model/topic.py index 22c5bb4a09..0560c43b1e 100644 --- a/sefaria/model/topic.py +++ b/sefaria/model/topic.py @@ -45,7 +45,8 @@ class Topic(abst.SluggedAbstractMongoRecord, AbstractTitledObject): 'good_to_promote', 'description_published', # bool to keep track of which descriptions we've vetted 'isAmbiguous', # True if topic primary title can refer to multiple other topics - "data_source" #any topic edited manually should display automatically in the TOC and this flag ensures this + "data_source", #any topic edited manually should display automatically in the TOC and this flag ensures this + "portal_slug", # slug to relevant Portal object ] ROOT = "Main Menu" # the root of topic TOC is not a topic, so this is a fake slug. we know it's fake because it's not in normal form # this constant is helpful in the topic editor tool functions in this file @@ -71,6 +72,8 @@ def _validate(self): if getattr(self, 'subclass', False): assert self.subclass in self.subclass_map, f"Field `subclass` set to {self.subclass} which is not one of the valid subclass keys in `Topic.subclass_map`. Valid keys are {', '.join(self.subclass_map.keys())}" + Portal.validate_slug_exists(self.portal_slug) + def _normalize(self): super()._normalize() for title in self.title_group.titles: @@ -555,16 +558,21 @@ def index_is_commentary(index): def get_aggregated_urls_for_authors_indexes(self) -> list: """ Aggregates author's works by category when possible and - returns list of tuples. Each tuple is of shape (url, {"en", "he"}) corresponding to an index or category of indexes of this author's works. + returns a dictionary. Each dictionary is of shape {"url": str, "title": {"en": str, "he": str}, "description": {"en": str, "he": str}} + corresponding to an index or category of indexes of this author's works. """ from .schema import Term from .text import Index index_or_cat_list = self.aggregate_authors_indexes_by_category() - unique_urls = {} # {url: {lang: title}}. This dict arbitrarily chooses one title per URL. + unique_urls = [] for index_or_cat, collective_title_term, base_category in index_or_cat_list: + en_desc = getattr(index_or_cat, 'enShortDesc', None) + he_desc = getattr(index_or_cat, 'heShortDesc', None) if isinstance(index_or_cat, Index): - unique_urls[f'/{index_or_cat.title.replace(" ", "_")}'] = {"en": index_or_cat.get_title('en'), "he": index_or_cat.get_title('he')} + unique_urls.append({"url":f'/{index_or_cat.title.replace(" ", "_")}', + "title": {"en": index_or_cat.get_title('en'), "he": index_or_cat.get_title('he')}, + "description":{"en": en_desc, "he": he_desc}}) else: if collective_title_term is None: cat_term = Term().load({"name": index_or_cat.sharedTitle}) @@ -574,8 +582,10 @@ def get_aggregated_urls_for_authors_indexes(self) -> list: base_category_term = Term().load({"name": base_category.sharedTitle}) en_text = f'{collective_title_term.get_primary_title("en")} on {base_category_term.get_primary_title("en")}' he_text = f'{collective_title_term.get_primary_title("he")} על {base_category_term.get_primary_title("he")}' - unique_urls[f'/texts/{"/".join(index_or_cat.path)}'] = {"en": en_text, "he": he_text} - return list(unique_urls.items()) + unique_urls.append({"url": f'/texts/{"/".join(index_or_cat.path)}', + "title": {"en": en_text, "he": he_text}, + "description":{"en": en_desc, "he": he_desc}}) + return unique_urls @staticmethod def is_author(slug): @@ -678,14 +688,10 @@ def _validate(self): super(IntraTopicLink, self)._validate() # check everything exists - link_type = TopicLinkType().load({"slug": self.linkType}) - assert link_type is not None, "Link type '{}' does not exist".format(self.linkType) - from_topic = Topic.init(self.fromTopic) - assert from_topic is not None, "fromTopic '{}' does not exist".format(self.fromTopic) - to_topic = Topic.init(self.toTopic) - assert to_topic is not None, "toTopic '{}' does not exist".format(self.toTopic) - data_source = TopicDataSource().load({"slug": self.dataSource}) - assert data_source is not None, "dataSource '{}' does not exist".format(self.dataSource) + TopicLinkType.validate_slug_exists(self.linkType) + Topic.validate_slug_exists(self.fromTopic) + Topic.validate_slug_exists(self.toTopic) + TopicDataSource.validate_slug_exists(self.dataSource) # check for duplicates duplicate = IntraTopicLink().load({"linkType": self.linkType, "fromTopic": self.fromTopic, "toTopic": self.toTopic, @@ -695,6 +701,7 @@ def _validate(self): "Duplicate intra topic link for linkType '{}', fromTopic '{}', toTopic '{}'".format( self.linkType, self.fromTopic, self.toTopic)) + link_type = TopicLinkType.init(self.linkType) if link_type.slug == link_type.inverseSlug: duplicate_inverse = IntraTopicLink().load({"linkType": self.linkType, "toTopic": self.fromTopic, "fromTopic": self.toTopic, "class": getattr(self, 'class'), "_id": {"$ne": getattr(self, "_id", None)}}) @@ -704,6 +711,8 @@ def _validate(self): duplicate_inverse.linkType, duplicate_inverse.fromTopic, duplicate_inverse.toTopic)) # check types of topics are valid according to validFrom/To + from_topic = Topic.init(self.fromTopic) + to_topic = Topic.init(self.toTopic) if getattr(link_type, 'validFrom', False): assert from_topic.has_types(set(link_type.validFrom)), "from topic '{}' does not have valid types '{}' for link type '{}'. Instead, types are '{}'".format(self.fromTopic, ', '.join(link_type.validFrom), self.linkType, ', '.join(from_topic.get_types())) if getattr(link_type, 'validTo', False): @@ -799,10 +808,10 @@ def _normalize(self): self.expandedRefs = [r.normal() for r in Ref(self.ref).all_segment_refs()] def _validate(self): + Topic.validate_slug_exists(self.toTopic) + TopicLinkType.validate_slug_exists(self.linkType) to_topic = Topic.init(self.toTopic) - assert to_topic is not None, "toTopic '{}' does not exist".format(self.toTopic) - link_type = TopicLinkType().load({"slug": self.linkType}) - assert link_type is not None, "Link type '{}' does not exist".format(self.linkType) + link_type = TopicLinkType.init(self.linkType) if getattr(link_type, 'validTo', False): assert to_topic.has_types(set(link_type.validTo)), "to topic '{}' does not have valid types '{}' for link type '{}'. Instead, types are '{}'".format(self.toTopic, ', '.join(link_type.validTo), self.linkType, ', '.join(to_topic.get_types())) @@ -890,11 +899,10 @@ def _validate(self): # Check that validFrom and validTo contain valid topic slugs if exist for validToTopic in getattr(self, 'validTo', []): - assert Topic.init(validToTopic) is not None, "ValidTo topic '{}' does not exist".format(self.validToTopic) + Topic.validate_slug_exists(validToTopic) for validFromTopic in getattr(self, 'validFrom', []): - assert Topic.init(validFromTopic) is not None, "ValidTo topic '{}' does not exist".format( - self.validFrom) + Topic.validate_slug_exists(validFromTopic) def get(self, attr, is_inverse, default=None): attr = 'inverse{}{}'.format(attr[0].upper(), attr[1:]) if is_inverse else attr From f6807bec631286a75fe89caa02cfcc18654790d3 Mon Sep 17 00:00:00 2001 From: Brendan Galloway <brendan@flanksource.com> Date: Mon, 6 Nov 2023 13:36:17 +0200 Subject: [PATCH 409/756] helm(fix): increase backup pod storage size --- helm-chart/sefaria-project/templates/cronjob/mongo-backup.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/helm-chart/sefaria-project/templates/cronjob/mongo-backup.yaml b/helm-chart/sefaria-project/templates/cronjob/mongo-backup.yaml index ba6f6f37d7..0a0ea29f2e 100644 --- a/helm-chart/sefaria-project/templates/cronjob/mongo-backup.yaml +++ b/helm-chart/sefaria-project/templates/cronjob/mongo-backup.yaml @@ -58,6 +58,8 @@ spec: command: ["bash"] args: ["-c", "/scripts/create-dumps.sh"] resources: + requests: + ephemeral-storage: 20Gi limits: memory: "500Mi" containers: From d980b850e86b65ed97dbda168777a451cd4ada5a Mon Sep 17 00:00:00 2001 From: Lev Israel <eliezer.israel@gmail.com> Date: Mon, 6 Nov 2023 15:09:45 +0200 Subject: [PATCH 410/756] chore: Improve VersionState docs. Note potential refactors. --- sefaria/model/version_state.py | 35 ++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/sefaria/model/version_state.py b/sefaria/model/version_state.py index 8b1b4a95df..d5021cbd04 100644 --- a/sefaria/model/version_state.py +++ b/sefaria/model/version_state.py @@ -30,25 +30,28 @@ class VersionState(abst.AbstractMongoRecord, AbstractSchemaContent): The `content` attribute is a dictionary which is the root of a tree, mirroring the shape of a Version, where the leaf nodes of the tree are dictionaries with a shape like the following:: { "_en": { - "availableTexts": - "availableCounts": - "percentAvailable": - "textComplete": - 'completenessPercent': - 'percentAvailableInvalid': - 'sparseness': # was isSparse + "availableTexts": Mask of what texts are available in this language. Boolean values (0 or 1) in the shape of the JaggedArray + "availableCounts": Array, with length == depth of the node. Each element is the number of available elements at that depth. e.g [chapters, verses] + "percentAvailable": Percent of this text available in this language TODO: Only used on the dashboard. Remove? + 'percentAvailableInvalid': Boolean. Whether the value of "percentAvailable" can be trusted. TODO: Only used on the dashboard. Remove? + "textComplete": Boolean. Whether the text is complete in this language. TODO: Not used outside of this file. Should be removed. + 'completenessPercent': Percent of this text complete in this language TODO: Not used outside of this file. Should be removed. + 'sparseness': Legacy - present on some records, but no longer in code TODO: remove } - "_he": ... + "_he": {...} # same keys as _en "_all" { - "availableTexts": + "availableTexts": Mask what texts are available in this text overall. Boolean values (0 or 1) in the shape of the JaggedArray "shape": - For depth 1: Integer - length - For depth 2: List of chapter lengths - For depth 3: List of list of chapter lengths? + For depth 1: Integer -length + For depth 2: List of section lengths + For depth 3: List of list of section lengths } } - For example, the `content` attribute for `Pesach Haggadah` will be a dictionary with keys: "Kadesh", "Urchatz", "Karpas" ... each with a value of a dictionary like the above. They key "Magid" has a value of dictionary with keys "Ha Lachma Anya", etc. + For example: + - the `content` attribute for a simple text like `Genesis` will be a dictionary with keys "_en", "_he", and "_all", as above. + - the `content` attribute for `Pesach Haggadah` will be a dictionary with keys: "Kadesh", "Urchatz", "Karpas" ... each with a value of a dictionary like the above. + The key "Magid" has a value of dictionary with keys "Ha Lachma Anya", etc. """ collection = 'vstate' @@ -58,9 +61,9 @@ class VersionState(abst.AbstractMongoRecord, AbstractSchemaContent): "content" # tree of data about nodes. See above. ] optional_attrs = [ - "flags", - "linksCount", - "first_section_ref" + "flags", # "heComplete" : Bool, "enComplete" : Bool + "linksCount", # Integer + "first_section_ref" # Normal text Ref ] langs = ["en", "he"] From 6250d49d6ab485046750f121b9895eb59f348222 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Mon, 6 Nov 2023 15:41:06 +0200 Subject: [PATCH 411/756] fix(portal): only check if portal exists if there's a portal slug --- sefaria/model/topic.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sefaria/model/topic.py b/sefaria/model/topic.py index 0560c43b1e..78843df06f 100644 --- a/sefaria/model/topic.py +++ b/sefaria/model/topic.py @@ -72,7 +72,8 @@ def _validate(self): if getattr(self, 'subclass', False): assert self.subclass in self.subclass_map, f"Field `subclass` set to {self.subclass} which is not one of the valid subclass keys in `Topic.subclass_map`. Valid keys are {', '.join(self.subclass_map.keys())}" - Portal.validate_slug_exists(self.portal_slug) + if getattr(self, 'portal_slug', None): + Portal.validate_slug_exists(self.portal_slug) def _normalize(self): super()._normalize() From 74d5c1f9755e4bc4380538ce22db90647f0e6c15 Mon Sep 17 00:00:00 2001 From: yonadavGit <yonadav.le@gmail.com> Date: Mon, 6 Nov 2023 16:52:40 +0200 Subject: [PATCH 412/756] feat(DisplaySettingsButton): change button to A for English interface and aleph for Hebrew interface --- static/css/s2.css | 10 ++++++++++ static/js/Misc.jsx | 14 +++++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index 128071435d..3976351fc7 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -4702,6 +4702,16 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus display: inline-block; cursor: pointer; } +.readerOptions .int-en { + font-size: 25px; + line-height: 63px; + margin-right: 8px; +} +.readerOptions .int-he { + font-size: 28px; + line-height: 67px; + margin-left: 6px; +} .rightButtons .readerOptions { vertical-align: middle; } diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 7468ab72f4..65a7513316 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -1343,9 +1343,17 @@ class CloseButton extends Component { class DisplaySettingsButton extends Component { render() { var style = this.props.placeholder ? {visibility: "hidden"} : {}; - var icon = Sefaria._siteSettings.TORAH_SPECIFIC ? - <img src="/static/img/ayealeph.svg" alt="Toggle Reader Menu Display Settings" style={style} /> : - <span className="textIcon">Aa</span>; + var icon; + + if (Sefaria._siteSettings.TORAH_SPECIFIC) { + icon = + <InterfaceText> + <EnglishText>A</EnglishText> + <HebrewText>א</HebrewText> + </InterfaceText>; + } else { + icon = <span className="textIcon">Aa</span>; + } return (<a className="readerOptions" tabIndex="0" From 4dbe6c4e404705182433c5efc1e208be2dc0d656 Mon Sep 17 00:00:00 2001 From: yonadavGit <yonadav.le@gmail.com> Date: Mon, 6 Nov 2023 17:16:59 +0200 Subject: [PATCH 413/756] feat(css): changed color of saveButton and displayTextButton to darker grey --- static/css/s2.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/static/css/s2.css b/static/css/s2.css index 3976351fc7..b5a4d0f3db 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -4653,6 +4653,7 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus height: 18px; width: 18px; margin-top: 3px; + filter: grayscale(100%) brightness(80%) sepia(20%); } .rightButtons .saveButton.tooltip-toggle::before { top: 47px; @@ -4697,7 +4698,7 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus width: 40px; height: 56px; line-height: 56px; - color: #999; + color: #666666; font-size: 20px; display: inline-block; cursor: pointer; From a005786221e3b63c984e988b7ff5f97e69564e09 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Tue, 7 Nov 2023 11:01:55 +0200 Subject: [PATCH 414/756] fix(Title Group): _primary_title[lang] set to None on remove_title of a primary title --- reader/views.py | 10 ++--- sefaria/helper/tests/topic_test.py | 46 +++++++++++--------- sefaria/helper/topic.py | 68 ++++++++++++++---------------- sefaria/model/place.py | 8 ++-- sefaria/model/schema.py | 3 ++ 5 files changed, 71 insertions(+), 64 deletions(-) diff --git a/reader/views.py b/reader/views.py index 385a569c2a..8205705dd3 100644 --- a/reader/views.py +++ b/reader/views.py @@ -3103,14 +3103,14 @@ def add_new_topic_api(request): data = json.loads(request.POST["json"]) isTopLevelDisplay = data["category"] == Topic.ROOT t = Topic({'slug': "", "isTopLevelDisplay": isTopLevelDisplay, "data_source": "sefaria", "numSources": 0}) - update_topic_titles(t, data) + update_topic_titles(t, **data) if not isTopLevelDisplay: # not Top Level so create an IntraTopicLink to category new_link = IntraTopicLink({"toTopic": data["category"], "fromTopic": t.slug, "linkType": "displays-under", "dataSource": "sefaria"}) new_link.save() if data["category"] == 'authors': - t = update_authors_place_and_time(t, data) + t = update_authors_place_and_time(t, **data) t.description_published = True t.data_source = "sefaria" # any topic edited manually should display automatically in the TOC and this flag ensures this @@ -3165,15 +3165,15 @@ def topics_api(request, topic, v2=False): if not request.user.is_staff: return jsonResponse({"error": "Adding topics is locked.<br><br>Please email hello@sefaria.org if you believe edits are needed."}) topic_data = json.loads(request.POST["json"]) - topic_obj = Topic().load({'slug': topic_data["origSlug"]}) + topic = Topic().load({'slug': topic_data["origSlug"]}) topic_data["manual"] = True author_status_changed = (topic_data["category"] == "authors") ^ (topic_data["origCategory"] == "authors") - topic_obj = update_topic(topic_obj, **topic_data) + topic = update_topic(topic, **topic_data) if author_status_changed: library.build_topic_auto_completer() def protected_index_post(request): - return jsonResponse(topic_obj.contents()) + return jsonResponse(topic.contents()) return protected_index_post(request) diff --git a/sefaria/helper/tests/topic_test.py b/sefaria/helper/tests/topic_test.py index 4b33cd8c70..526bc88f5d 100644 --- a/sefaria/helper/tests/topic_test.py +++ b/sefaria/helper/tests/topic_test.py @@ -52,10 +52,10 @@ def grandchild_of_root_with_self_link(child_of_root_with_self_link): @pytest.fixture(autouse=True, scope='module') -def root_wout_self_link(): - # create second branch of tree starting with root_wout_self_link +def author_root(): + # create second branch of tree starting with author_root t = Topic({'slug': "", "isTopLevelDisplay": True, "data_source": "sefaria", "numSources": 0}) - title = "Normal Root" + title = "Authors" he_title = title[::-1] t.add_primary_titles(title, he_title) t.set_slug_to_primary_title() @@ -66,23 +66,23 @@ def root_wout_self_link(): @pytest.fixture(autouse=True, scope='module') -def child_of_root_wout_self_link(root_wout_self_link): +def actual_author(author_root): t = Topic({'slug': "", "isTopLevelDisplay": False, "data_source": "sefaria", "numSources": 0}) - title = "Normal Root Leaf Node" + title = "Author Dude" he_title = title[::-1] t.add_primary_titles(title, he_title) t.set_slug_to_primary_title() t.save() l = IntraTopicLink({"linkType": "displays-under", "fromTopic": t.slug, - "toTopic": root_wout_self_link["topic"].slug, "dataSource": "sefaria", - "class": "intraTopic"}).save() # root_wout_self_link has child leaf_node + "toTopic": author_root["topic"].slug, "dataSource": "sefaria", + "class": "intraTopic"}).save() # author_root has child leaf_node yield {"topic": t, "link": l} t.delete() l.delete() -def test_title_and_desc(root_wout_self_link, child_of_root_wout_self_link, root_with_self_link, child_of_root_with_self_link, grandchild_of_root_with_self_link): - for count, t in enumerate([root_wout_self_link, child_of_root_wout_self_link, root_with_self_link, child_of_root_with_self_link, grandchild_of_root_with_self_link]): +def test_title_and_desc(author_root, actual_author, root_with_self_link, child_of_root_with_self_link, grandchild_of_root_with_self_link): + for count, t in enumerate([author_root, actual_author, root_with_self_link, child_of_root_with_self_link, grandchild_of_root_with_self_link]): new_values = {"title": f"new title {count+1}", "altTitles": {"en": [f"New Alt title {count+1}"], "he": [f"New He Alt Title {count+1}"]}, "heTitle": f"new hebrew title {count+1}", "description": {"en": f"new desc", "he": "new hebrew desc"}} @@ -91,14 +91,22 @@ def test_title_and_desc(root_wout_self_link, child_of_root_wout_self_link, root_ assert t["topic"].get_primary_title('he') == new_values['heTitle'] assert t["topic"].get_titles('en') == [new_values["title"]]+new_values["altTitles"]['en'] +def test_author_root(author_root, actual_author): + new_values = {"category": "authors", "title": actual_author["topic"].get_primary_title('en'), + "heTitle": actual_author["topic"].get_primary_title('he'), + "birthPlace": "Kyoto, Japan", "birthYear": 1300} + assert Place().load({'key': new_values["birthPlace"]}) is None + update_topic(actual_author["topic"], **new_values) + assert Place().load({'key': new_values["birthPlace"]}) + assert actual_author["topic"].properties["birthYear"]["value"] == 1300 -def test_change_categories_and_titles(root_wout_self_link, root_with_self_link): +def test_change_categories_and_titles(author_root, root_with_self_link): # tests moving both root categories down the tree and back up and asserting that moving down the tree changes the tree # and assert that moving it back to the root position yields the original tree. - orig_tree_from_normal_root = library.get_topic_toc_json_recursive(root_wout_self_link["topic"]) + orig_tree_from_normal_root = library.get_topic_toc_json_recursive(author_root["topic"]) orig_tree_from_root_with_self_link = library.get_topic_toc_json_recursive(root_with_self_link["topic"]) orig_trees = [orig_tree_from_normal_root, orig_tree_from_root_with_self_link] - roots = [root_wout_self_link["topic"], root_with_self_link["topic"]] + roots = [author_root["topic"], root_with_self_link["topic"]] orig_titles = [roots[0].get_primary_title('en'), roots[1].get_primary_title('en')] orig_he_titles = [roots[0].get_primary_title('he'), roots[1].get_primary_title('he')] for i, root in enumerate(roots): @@ -119,24 +127,24 @@ def test_change_categories_and_titles(root_wout_self_link, root_with_self_link): assert final_tree_from_root_with_self_link == orig_tree_from_root_with_self_link -def test_change_categories(root_wout_self_link, child_of_root_wout_self_link, root_with_self_link, child_of_root_with_self_link, grandchild_of_root_with_self_link): +def test_change_categories(author_root, actual_author, root_with_self_link, child_of_root_with_self_link, grandchild_of_root_with_self_link): # tests moving topics across the tree to a different root - orig_tree_from_normal_root = library.get_topic_toc_json_recursive(root_wout_self_link["topic"]) + orig_tree_from_normal_root = library.get_topic_toc_json_recursive(author_root["topic"]) orig_tree_from_root_with_self_link = library.get_topic_toc_json_recursive(root_with_self_link["topic"]) - topic_change_category(child_of_root_with_self_link["topic"], root_wout_self_link["topic"].slug) - topic_change_category(child_of_root_wout_self_link["topic"], root_with_self_link["topic"].slug) + topic_change_category(child_of_root_with_self_link["topic"], author_root["topic"].slug) + topic_change_category(actual_author["topic"], root_with_self_link["topic"].slug) - new_tree_from_normal_root = library.get_topic_toc_json_recursive(root_wout_self_link["topic"]) + new_tree_from_normal_root = library.get_topic_toc_json_recursive(author_root["topic"]) new_tree_from_root_with_self_link = library.get_topic_toc_json_recursive(root_with_self_link["topic"]) assert new_tree_from_normal_root != orig_tree_from_normal_root assert new_tree_from_root_with_self_link != orig_tree_from_root_with_self_link topic_change_category(child_of_root_with_self_link["topic"], root_with_self_link["topic"].slug) - topic_change_category(child_of_root_wout_self_link["topic"], root_wout_self_link["topic"].slug) + topic_change_category(actual_author["topic"], author_root["topic"].slug) - new_tree_from_normal_root = library.get_topic_toc_json_recursive(root_wout_self_link["topic"]) + new_tree_from_normal_root = library.get_topic_toc_json_recursive(author_root["topic"]) new_tree_from_root_with_self_link = library.get_topic_toc_json_recursive(root_with_self_link["topic"]) assert new_tree_from_normal_root == orig_tree_from_normal_root assert new_tree_from_root_with_self_link == orig_tree_from_root_with_self_link diff --git a/sefaria/helper/topic.py b/sefaria/helper/topic.py index 126607fcb3..837c34b4a5 100644 --- a/sefaria/helper/topic.py +++ b/sefaria/helper/topic.py @@ -1042,28 +1042,24 @@ def topic_change_category(topic_obj, new_category, old_category="", rebuild=Fals rebuild_topic_toc(topic_obj, category_changed=True) return topic_obj -def update_topic_titles(topic_obj, data): - new_primary = {"en": data['title'], "he": data["heTitle"]} +def update_topic_titles(topic, **kwargs): + new_primary = {"en": kwargs['title'], "he": kwargs["heTitle"]} for lang in ['en', 'he']: # first add new primary titles then remove old alt titles and add new alt titles - topic_obj.add_title(new_primary[lang], lang, True, True) - for title in topic_obj.get_titles(lang): - if title != new_primary[lang]: - topic_obj.remove_title(title, lang) - if 'altTitles' in data: - for title in data['altTitles'][lang]: - topic_obj.add_title(title, lang) - - return topic_obj + for title in topic.get_titles(lang): + topic.remove_title(title, lang) + topic.add_title(new_primary[lang], lang, True, False) + if 'altTitles' in kwargs: + for title in kwargs['altTitles'][lang]: + topic.add_title(title, lang) + return topic -def update_authors_place_and_time(topic_obj, data, dataSource='learning-team-editing-tool'): +def update_authors_place_and_time(topic, dataSource='learning-team-editing-tool', **kwargs): # update place info added to author, then update year and era info - if not hasattr(topic_obj, 'properties'): - topic_obj.properties = {} - process_topic_place_change(topic_obj, data) - topic_obj = update_author_era(topic_obj, data, dataSource=dataSource) - - return topic_obj + if not hasattr(topic, 'properties'): + topic.properties = {} + process_topic_place_change(topic, **kwargs) + return update_author_era(topic, **kwargs, dataSource=dataSource) def update_properties(topic_obj, dataSource, k, v): if v == '': @@ -1071,54 +1067,54 @@ def update_properties(topic_obj, dataSource, k, v): else: topic_obj.properties[k] = {'value': v, 'dataSource': dataSource} -def update_author_era(topic_obj, data, dataSource='learning-team-editing-tool'): +def update_author_era(topic_obj, dataSource='learning-team-editing-tool', **kwargs): for k in ["birthYear", "deathYear"]: - if k in data.keys(): # only change property value if key is in data, otherwise it indicates no change - year = data[k] + if k in kwargs.keys(): # only change property value if key exists, otherwise it indicates no change + year = kwargs[k] update_properties(topic_obj, dataSource, k, year) - if 'era' in data.keys(): # only change property value if key is in data, otherwise it indicates no change + if 'era' in kwargs.keys(): # only change property value if key is in data, otherwise it indicates no change prev_era = topic_obj.properties.get('era', {}).get('value') - era = data['era'] + era = kwargs['era'] update_properties(topic_obj, dataSource, 'era', era) if era != '': create_era_link(topic_obj, prev_era_to_delete=prev_era) return topic_obj -def update_topic(topic_obj, **kwargs): +def update_topic(topic, **kwargs): """ Can update topic object's title, hebrew title, category, description, and categoryDescription fields - :param topic_obj: (Topic) The topic to update + :param topic: (Topic) The topic to update :param **kwargs can be title, heTitle, category, description, categoryDescription, and rebuild_toc where `title`, `heTitle`, and `category` are strings. `description` and `categoryDescription` are dictionaries where the fields are `en` and `he`. The `category` parameter should be the slug of the new category. `rebuild_topic_toc` is a boolean and is assumed to be True :return: (model.Topic) The modified topic """ old_category = "" - orig_slug = topic_obj.slug - update_topic_titles(topic_obj, kwargs) + orig_slug = topic.slug + update_topic_titles(topic, **kwargs) if kwargs.get('category') == 'authors': - topic_obj = update_authors_place_and_time(topic_obj, kwargs) + topic = update_authors_place_and_time(topic, **kwargs) if 'category' in kwargs and kwargs['category'] != kwargs.get('origCategory', kwargs['category']): - orig_link = IntraTopicLink().load({"linkType": "displays-under", "fromTopic": topic_obj.slug, "toTopic": {"$ne": topic_obj.slug}}) + orig_link = IntraTopicLink().load({"linkType": "displays-under", "fromTopic": topic.slug, "toTopic": {"$ne": topic.slug}}) old_category = orig_link.toTopic if orig_link else Topic.ROOT if old_category != kwargs['category']: - topic_obj = topic_change_category(topic_obj, kwargs["category"], old_category=old_category) # can change topic and intratopiclinks + topic = topic_change_category(topic, kwargs["category"], old_category=old_category) # can change topic and intratopiclinks if kwargs.get('manual', False): - topic_obj.data_source = "sefaria" # any topic edited manually should display automatically in the TOC and this flag ensures this - topic_obj.description_published = True + topic.data_source = "sefaria" # any topic edited manually should display automatically in the TOC and this flag ensures this + topic.description_published = True if "description" in kwargs or "categoryDescription" in kwargs: - topic_obj.change_description(kwargs.get("description", None), kwargs.get("categoryDescription", None)) + topic.change_description(kwargs.get("description", None), kwargs.get("categoryDescription", None)) - topic_obj.save() + topic.save() if kwargs.get('rebuild_topic_toc', True): - rebuild_topic_toc(topic_obj, orig_slug=orig_slug, category_changed=(old_category != kwargs.get('category', ""))) - return topic_obj + rebuild_topic_toc(topic, orig_slug=orig_slug, category_changed=(old_category != kwargs.get('category', ""))) + return topic def rebuild_topic_toc(topic_obj, orig_slug="", category_changed=False): diff --git a/sefaria/model/place.py b/sefaria/model/place.py index a39bf0e610..a956f1aa7f 100644 --- a/sefaria/model/place.py +++ b/sefaria/model/place.py @@ -105,14 +105,14 @@ def process_index_place_change(indx, **kwargs): if kwargs['new'] != '': Place.create_new_place(en=kwargs['new'], he=he_new_val) -def process_topic_place_change(topic_obj, data): +def process_topic_place_change(topic_obj, **kwargs): keys = ["birthPlace", "deathPlace"] for key in keys: - if key in data.keys(): # only change property value if key is in data, otherwise it indicates no change - new_val = data[key] + if key in kwargs.keys(): # only change property value if key is in data, otherwise it indicates no change + new_val = kwargs[key] if new_val != '': he_key = get_he_key(key) - he_new_val = data.get(he_key, '') + he_new_val = kwargs.get(he_key, '') place = Place.create_new_place(en=new_val, he=he_new_val) topic_obj.properties[key] = {"value": place.primary_name('en'), 'dataSource': 'learning-team-editing-tool'} else: diff --git a/sefaria/model/schema.py b/sefaria/model/schema.py index 305dda01b8..204e0acbac 100644 --- a/sefaria/model/schema.py +++ b/sefaria/model/schema.py @@ -125,6 +125,9 @@ def secondary_titles(self, lang=None): return [t for t in self.all_titles(lang) if t != self.primary_title(lang)] def remove_title(self, text, lang): + is_primary = [t for t in self.titles if not (t["lang"] == lang and t["text"] == text and t.get('primary'))] + if is_primary: + self._primary_title[lang] = None self.titles = [t for t in self.titles if not (t["lang"] == lang and t["text"] == text)] return self From 1e1779f4471e3cc3aca6447286f689cf9fd1471e Mon Sep 17 00:00:00 2001 From: Brendan Galloway <brendan@flanksource.com> Date: Tue, 7 Nov 2023 11:11:10 +0200 Subject: [PATCH 415/756] helm(fix): increase ephemeral storage for backup job --- .../sefaria-project/templates/cronjob/mongo-backup.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/helm-chart/sefaria-project/templates/cronjob/mongo-backup.yaml b/helm-chart/sefaria-project/templates/cronjob/mongo-backup.yaml index 0a0ea29f2e..9c4ec49e9f 100644 --- a/helm-chart/sefaria-project/templates/cronjob/mongo-backup.yaml +++ b/helm-chart/sefaria-project/templates/cronjob/mongo-backup.yaml @@ -59,7 +59,7 @@ spec: args: ["-c", "/scripts/create-dumps.sh"] resources: requests: - ephemeral-storage: 20Gi + ephemeral-storage: 30Gi limits: memory: "500Mi" containers: @@ -100,7 +100,8 @@ spec: name: upload-dumps-{{ .Values.deployEnv }} defaultMode: 0755 - name: shared-volume - emptyDir: {} + emptyDir: + sizeLimit: 30Gi successfulJobsHistoryLimit: 1 failedJobsHistoryLimit: 2 {{- end }} From 7b32327ed702114d7b6efbd66bdeea928a1cf028 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Tue, 7 Nov 2023 11:24:48 +0200 Subject: [PATCH 416/756] chore: cleanup is_primary semantics in remove_title --- sefaria/model/schema.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sefaria/model/schema.py b/sefaria/model/schema.py index 204e0acbac..10da95f291 100644 --- a/sefaria/model/schema.py +++ b/sefaria/model/schema.py @@ -125,7 +125,7 @@ def secondary_titles(self, lang=None): return [t for t in self.all_titles(lang) if t != self.primary_title(lang)] def remove_title(self, text, lang): - is_primary = [t for t in self.titles if not (t["lang"] == lang and t["text"] == text and t.get('primary'))] + is_primary = len([t for t in self.titles if not (t["lang"] == lang and t["text"] == text and t.get('primary'))]) if is_primary: self._primary_title[lang] = None self.titles = [t for t in self.titles if not (t["lang"] == lang and t["text"] == text)] From 3ffdac3e37f0e1c0b4c9fe7fa5d685646d59ba12 Mon Sep 17 00:00:00 2001 From: Lev Israel <eliezer.israel@gmail.com> Date: Tue, 7 Nov 2023 11:31:47 +0200 Subject: [PATCH 417/756] chore: Improve version_state docs --- sefaria/model/version_state.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sefaria/model/version_state.py b/sefaria/model/version_state.py index d5021cbd04..2fef65b10e 100644 --- a/sefaria/model/version_state.py +++ b/sefaria/model/version_state.py @@ -27,7 +27,7 @@ class VersionState(abst.AbstractMongoRecord, AbstractSchemaContent): """ This model overrides default init/load/save behavior, since there is one and only one VersionState record for each Index record. - The `content` attribute is a dictionary which is the root of a tree, mirroring the shape of a Version, where the leaf nodes of the tree are dictionaries with a shape like the following:: + The `content` attribute is a dictionary which is the root of a tree, mirroring the shape of a Version, where the leaf nodes of the tree are dictionaries with a shape like the following: { "_en": { "availableTexts": Mask of what texts are available in this language. Boolean values (0 or 1) in the shape of the JaggedArray @@ -51,8 +51,11 @@ class VersionState(abst.AbstractMongoRecord, AbstractSchemaContent): For example: - the `content` attribute for a simple text like `Genesis` will be a dictionary with keys "_en", "_he", and "_all", as above. - the `content` attribute for `Pesach Haggadah` will be a dictionary with keys: "Kadesh", "Urchatz", "Karpas" ... each with a value of a dictionary like the above. - The key "Magid" has a value of dictionary with keys "Ha Lachma Anya", etc. + The key "Magid" has a value of a dictionary, where each key is a different sub-section of Magid. + The value for each key is a dictionary as detailed above, specific to each sub-section. + So for example, one key will be "Ha Lachma Anya" and the value will be a dictionary, like the above, specific to the details of "Ha Lachma Anya". + Every JaggedArrayNode has a corresponding vstate dictionary. So for complex texts, each leaf node (and leaf nodes by definition must be JaggedArrayNodes) has this corresponding dictionary. """ collection = 'vstate' From eeb56f91019f1b78537e1cdb18c0dc42a832f892 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Tue, 7 Nov 2023 11:44:40 +0200 Subject: [PATCH 418/756] feat(portal): allow instantiating a slugged mongo record that has more than one slug field --- sefaria/model/abstract.py | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/sefaria/model/abstract.py b/sefaria/model/abstract.py index 9a2f53922c..a2c1cddb79 100644 --- a/sefaria/model/abstract.py +++ b/sefaria/model/abstract.py @@ -382,17 +382,20 @@ class SluggedAbstractMongoRecord(AbstractMongoRecord, metaclass=SluggedAbstractM cacheable = False @classmethod - def init(cls, slug: str) -> 'AbstractMongoRecord': + def init(cls, slug: str, slug_field_idx: int = None) -> 'AbstractMongoRecord': """ Convenience func to avoid using .load() when you're only passing a slug Applicable only if class defines `slug_fields` - :param slug: - :return: + @param slug: + @param slug_field_idx: Optional index of slug field in case `cls` has multiple slug fields. Index should be between 0 and len(cls.slug_fields) - 1 + @return: instance of `cls` with slug `slug` """ - if len(cls.slug_fields) != 1: - raise Exception("Can only call init() if exactly one slug field is defined.") + if len(cls.slug_fields) != 1 and slug_field_idx is None: + raise Exception("Can only call init() if exactly one slug field is defined or `slug_field_idx` is passed as" + " a parameter.") + slug_field_idx = slug_field_idx or 0 if not cls.cacheable or slug not in cls._init_cache: - instance = cls().load({cls.slug_fields[0]: slug}) + instance = cls().load({cls.slug_fields[slug_field_idx]: slug}) if cls.cacheable: cls._init_cache[slug] = instance else: @@ -428,9 +431,17 @@ def _normalize(self): setattr(self, slug_field, self.normalize_slug_field(slug_field)) @classmethod - def validate_slug_exists(cls, slug): - object = cls.init(slug) - if not object: + def validate_slug_exists(cls, slug: str, slug_field_idx: int = None): + """ + Validate that `slug` points to an existing object of type `cls`. Pass `slug_field` if `cls` has multiple slugs + associated with it (e.g. TopicLinkType) + @param slug: Slug to look up + @param slug_field_idx: Optional index of slug field in case `cls` has multiple slug fields. Index should be + between 0 and len(cls.slug_fields) - 1 + @return: raises SluggedMongoRecordMissingError is slug doesn't match an existing object + """ + instance = cls.init(slug, slug_field_idx) + if not instance: raise SluggedMongoRecordMissingError(f"{cls.__name__} with slug '{slug}' does not exist.") From e7603fe43d46c027f5c835bcf1f7dd7d48573a41 Mon Sep 17 00:00:00 2001 From: Lev Israel <eliezer.israel@gmail.com> Date: Tue, 7 Nov 2023 11:45:03 +0200 Subject: [PATCH 419/756] chore: Remove count model --- sefaria/model/count.py | 93 ------------------------------------------ 1 file changed, 93 deletions(-) delete mode 100644 sefaria/model/count.py diff --git a/sefaria/model/count.py b/sefaria/model/count.py deleted file mode 100644 index 63b31a9552..0000000000 --- a/sefaria/model/count.py +++ /dev/null @@ -1,93 +0,0 @@ -""" -count.py -Writes to MongoDB Collection: counts -""" -import structlog -logger = structlog.get_logger(__name__) - -from . import abstract as abst -import sefaria.datatype.jagged_array as ja -from sefaria.system.exceptions import BookNameError - - -class Count(abst.AbstractMongoRecord): - """ - """ - collection = 'counts' - - required_attrs = [ - "textComplete", - "percentAvailable", - "availableCounts" - ] - optional_attrs = [ - "categories", - "availableTexts", - "title", - "linksCount", - "estimatedCompleteness", - "flags", - "allVersionCounts" - ] - - def _set_derived_attributes(self): - from . import text - - if getattr(self, "title", None): - try: - indx = text.library.get_index(self.title) - attrs = indx.contents() - #del attrs["_id"] - self.index_attr_keys = list(attrs.keys()) - self.__dict__.update(attrs) - except BookNameError as e: - logger.warning("Count object failed to get Index for {} : {} Normal right after Index name change.".format(self.title, e)) - - #todo: this needs to be considered. What happens when the data is modified? etc. - if getattr(self, "allVersionCounts", None) is not None: - self._allVersionCountsJA = ja.JaggedIntArray(self.allVersionCounts) - - #remove uneccesary and dangerous categories attr from text counts - #This assumes that category nodes have no title element - #todo: review this. Do we need to subclass text and category counts? - def _saveable_attr_keys(self): - attrs = super(Count, self)._saveable_attr_keys() - if getattr(self, "title", None): - attrs.remove("categories") - return attrs - - def contents(self, **kwargs): - attrs = super(Count, self).contents() - for key in self.index_attr_keys: - attrs[key] = getattr(self, key, None) - return attrs - - #deprecated - use JA directly - def next_address(self, starting_points=None): - starting_points = starting_points or [] - if len(starting_points) > 0: - starting_points[-1] += 1 - return self._allVersionCountsJA.next_index(starting_points) - - #deprecated - use JA directly - def prev_address(self, starting_points=None): - starting_points = starting_points or [] - if len(starting_points) > 0: - starting_points[-1] -= 1 - return self._allVersionCountsJA.prev_index(starting_points) - - #deprecated - use JA directly - def section_length(self, section_numbers): - """ - :param section_numbers: The list of 1-based (E.g. Chapter 5 is section_number 5) section numbers - :return: The length of that section - """ - return self._allVersionCountsJA.sub_array_length([s - 1 for s in section_numbers]) - - -class CountSet(abst.AbstractMongoSet): - recordClass = Count - - -def process_index_delete_in_counts(indx, **kwargs): - CountSet({"title":indx.title}).delete() From 676da199c71d08240536837cf993bcce54465204 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Tue, 7 Nov 2023 11:45:13 +0200 Subject: [PATCH 420/756] test(portal): fix failing tests --- sefaria/model/tests/abstract_test.py | 10 ++++++++-- sefaria/model/tests/topic_test.py | 9 +++++---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/sefaria/model/tests/abstract_test.py b/sefaria/model/tests/abstract_test.py index ac6d876eeb..0b1efd1031 100644 --- a/sefaria/model/tests/abstract_test.py +++ b/sefaria/model/tests/abstract_test.py @@ -9,6 +9,7 @@ # cascade functions are tested in person_test.py + def setup_module(module): global record_classes global set_classes @@ -21,7 +22,12 @@ def setup_module(module): print(record_classes) -class Test_Mongo_Record_Models(object): +def get_record_classes_with_slugs(): + classes = abstract.get_record_classes() + return filter(lambda x: getattr(x, 'slug_fields', None) is not None and x.__name__ != "Portal", classes) + + +class TestMongoRecordModels(object): def test_class_attribute_collection(self): for sub in record_classes: @@ -59,7 +65,7 @@ def test_slug(slug, final_slug): test_slug('blah/blah', 'blah-blah') test_slug('blah == בלה', 'blah-בלה') - @pytest.mark.parametrize("sub", filter(lambda x: getattr(x, 'slug_fields', None) is not None, abstract.get_record_classes())) + @pytest.mark.parametrize("sub", get_record_classes_with_slugs()) def test_normalize_slug_field(self, sub): """ diff --git a/sefaria/model/tests/topic_test.py b/sefaria/model/tests/topic_test.py index 78e8a89ec5..59f49d205b 100644 --- a/sefaria/model/tests/topic_test.py +++ b/sefaria/model/tests/topic_test.py @@ -2,6 +2,7 @@ from sefaria.model.topic import Topic, TopicSet, IntraTopicLink, RefTopicLink, TopicLinkHelper, IntraTopicLinkSet, RefTopicLinkSet from sefaria.model.text import Ref from sefaria.system.database import db +from sefaria.system.exceptions import SluggedMongoRecordMissingError def make_topic(slug): @@ -219,7 +220,7 @@ def test_validate(self, topic_graph): 'dataSource': 'blahblah' } l = IntraTopicLink(attrs) - with pytest.raises(AssertionError): + with pytest.raises(SluggedMongoRecordMissingError): l.save() # non-existant toTopic @@ -230,7 +231,7 @@ def test_validate(self, topic_graph): 'dataSource': 'sefaria' } l = IntraTopicLink(attrs) - with pytest.raises(AssertionError): + with pytest.raises(SluggedMongoRecordMissingError): l.save() # non-existant fromTopic @@ -241,7 +242,7 @@ def test_validate(self, topic_graph): 'dataSource': 'sefaria' } l = IntraTopicLink(attrs) - with pytest.raises(AssertionError): + with pytest.raises(SluggedMongoRecordMissingError): l.save() # non-existant linkType @@ -252,7 +253,7 @@ def test_validate(self, topic_graph): 'dataSource': 'sefaria' } l = IntraTopicLink(attrs) - with pytest.raises(AssertionError): + with pytest.raises(SluggedMongoRecordMissingError): l.save() # duplicate for symmetric linkType From ae889c2358a44b639b53cc242e732c0a3adad2de Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Tue, 7 Nov 2023 11:45:28 +0200 Subject: [PATCH 421/756] fix(portal): add missing field to schema --- sefaria/model/portal.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sefaria/model/portal.py b/sefaria/model/portal.py index 56e0e2a7d4..57b83312b0 100644 --- a/sefaria/model/portal.py +++ b/sefaria/model/portal.py @@ -45,6 +45,7 @@ def _validate(self): newsletter_schema = { "title": ({"en": (str, "required"), "he": (str, "required")}, "required"), "description": ({"en": (str, "required"), "he": (str, "required")}, "optional"), + "title_url": (str, "optional"), "api_schema": ({"http_method": (str, "required"), "payload": ({"first_name_key": (str, "optional"), "last_name_key": (str, "optional"), "email_key": (str, "optional")}, "optional")} , "optional") @@ -75,6 +76,11 @@ def _validate(self): return True +class PortalSet(abst.AbstractMongoSet): + recordClass = Portal + + + From 53d06a23e64db9905526d22ec4a828087c322d9b Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Tue, 7 Nov 2023 11:45:48 +0200 Subject: [PATCH 422/756] fix(portal): add slug_field_idx for loading TopicLinkType. --- sefaria/model/topic.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sefaria/model/topic.py b/sefaria/model/topic.py index 78843df06f..1873759ca5 100644 --- a/sefaria/model/topic.py +++ b/sefaria/model/topic.py @@ -689,7 +689,7 @@ def _validate(self): super(IntraTopicLink, self)._validate() # check everything exists - TopicLinkType.validate_slug_exists(self.linkType) + TopicLinkType.validate_slug_exists(self.linkType, 0) Topic.validate_slug_exists(self.fromTopic) Topic.validate_slug_exists(self.toTopic) TopicDataSource.validate_slug_exists(self.dataSource) @@ -702,7 +702,7 @@ def _validate(self): "Duplicate intra topic link for linkType '{}', fromTopic '{}', toTopic '{}'".format( self.linkType, self.fromTopic, self.toTopic)) - link_type = TopicLinkType.init(self.linkType) + link_type = TopicLinkType.init(self.linkType, 0) if link_type.slug == link_type.inverseSlug: duplicate_inverse = IntraTopicLink().load({"linkType": self.linkType, "toTopic": self.fromTopic, "fromTopic": self.toTopic, "class": getattr(self, 'class'), "_id": {"$ne": getattr(self, "_id", None)}}) @@ -810,9 +810,9 @@ def _normalize(self): def _validate(self): Topic.validate_slug_exists(self.toTopic) - TopicLinkType.validate_slug_exists(self.linkType) + TopicLinkType.validate_slug_exists(self.linkType, 0) to_topic = Topic.init(self.toTopic) - link_type = TopicLinkType.init(self.linkType) + link_type = TopicLinkType.init(self.linkType, 0) if getattr(link_type, 'validTo', False): assert to_topic.has_types(set(link_type.validTo)), "to topic '{}' does not have valid types '{}' for link type '{}'. Instead, types are '{}'".format(self.toTopic, ', '.join(link_type.validTo), self.linkType, ', '.join(to_topic.get_types())) From bd0744d128c41ec72ad0c3cea41a75fe0a66ffe2 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Tue, 7 Nov 2023 11:56:30 +0200 Subject: [PATCH 423/756] fix(Search): improve repairCaseVariant to handle multiple words --- static/js/Header.jsx | 2 +- static/js/sefaria/sefaria.js | 32 +++++++++++++------------------- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/static/js/Header.jsx b/static/js/Header.jsx index ca6066ddb4..5f77a7fc69 100644 --- a/static/js/Header.jsx +++ b/static/js/Header.jsx @@ -307,7 +307,7 @@ class SearchBar extends Component { .then(d => { // If the query isn't recognized as a ref, but only for reasons of capitalization. Resubmit with recognizable caps. if (Sefaria.isACaseVariant(query, d)) { - this.submitSearch(Sefaria.titleCaseExceptStopWords(query, d)); + this.submitSearch(Sefaria.repairCaseVariant(query, d)); return; } const repairedQuery = Sefaria.repairGershayimVariant(query, d); diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index 784b8b51ae..360a000681 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -1945,26 +1945,20 @@ _media: {}, }, repairCaseVariant: function(query, data) { // Used when isACaseVariant() is true to prepare the alternative - return data["completions"][0] + query.slice(data["completions"][0].length); - }, - titleCaseExceptStopWords: function(str) { - const stopWords = ["and", "or", "the", "a", "in", "an", "is", "of", "for"]; - let result = []; - if (str[0] === ' ') { - result.push(""); - str = str.trim(); - } - const words = str.split(' '); - for (let i = 0; i < words.length; i++) { - // title case each word except for stop words. - if (stopWords.includes(words[i])) { - result.push(words[i].replace(words[i][0], words[i][0].toLowerCase())); - } else { - result.push(words[i].replace(words[i][0], words[i][0].toUpperCase())); + const completionArray = data["completion_objects"].filter(x => x.type === 'ref').map(x => x.title); + let normalizedQuery = query.toLowerCase(); + let bestMatch = ""; + let bestMatchLength = 0; + + completionArray.forEach((completion) => { + let normalizedCompletion = completion.toLowerCase(); + if (normalizedQuery.includes(normalizedCompletion) && normalizedCompletion.length > bestMatchLength) { + bestMatch = completion; + bestMatchLength = completion.length; } - } - return result.join(' '); - }, + }); + return bestMatch + query.slice(bestMatch.length); + }, repairGershayimVariant: function(query, data) { if (!data["is_ref"] && data.completions && !data.completions.includes(query)) { function normalize_gershayim(string) { From 9cf985e2aed943ba18f06eb9be1f787462874bfe Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Tue, 7 Nov 2023 12:20:21 +0200 Subject: [PATCH 424/756] feat: add Cerberus validations to AbstractMongoRecord. This greatly increases our ability to validate schemas. --- requirements.txt | 1 + sefaria/model/abstract.py | 12 +++ sefaria/model/portal.py | 155 +++++++++++++++++++++++------------ sefaria/system/validators.py | 50 ----------- 4 files changed, 115 insertions(+), 103 deletions(-) diff --git a/requirements.txt b/requirements.txt index 9a63ee1f97..df83dfb6ee 100644 --- a/requirements.txt +++ b/requirements.txt @@ -65,6 +65,7 @@ sentry-sdk==1.26.0 babel python-bidi requests +Cerberus opentelemetry-distro opentelemetry-exporter-otlp diff --git a/sefaria/model/abstract.py b/sefaria/model/abstract.py index a2c1cddb79..2057c0e91a 100644 --- a/sefaria/model/abstract.py +++ b/sefaria/model/abstract.py @@ -3,6 +3,7 @@ """ abstract.py - abstract classes for Sefaria models """ +from cerberus import Validator import collections import structlog import copy @@ -31,6 +32,7 @@ class AbstractMongoRecord(object): criteria_override_field = None # If a record type uses a different primary key (such as 'title' for Index records), and the presence of an override field in a save indicates that the primary attribute is changing ("oldTitle" in Index records) then this class attribute has that override field name used. required_attrs = [] # list of names of required attributes optional_attrs = [] # list of names of optional attributes + attr_schemas = {} # schemas to validate that an attribute is in the right format. Keys are attribute names, values are schemas in Cerberus format. track_pkeys = False pkeys = [] # list of fields that others may depend on history_noun = None # Label for history records @@ -242,6 +244,16 @@ def _validate(self): " not in " + ",".join(self.required_attrs) + " or " + ",".join(self.optional_attrs)) return False """ + for attr, schema in self.attr_schemas.items(): + v = Validator(schema) + try: + value = getattr(self, attr) + if not v.validate(value): + raise InputError(v.errors) + except AttributeError: + # not checking here if value exists, that is done above. + # assumption is if value doesn't exist, it's optional + pass return True def _normalize(self): diff --git a/sefaria/model/portal.py b/sefaria/model/portal.py index 57b83312b0..36984ceaad 100644 --- a/sefaria/model/portal.py +++ b/sefaria/model/portal.py @@ -1,6 +1,7 @@ from . import abstract as abst -from sefaria.system.validators import validate_dictionary, validate_url, validate_http_method +from sefaria.system.validators import validate_url, validate_http_method import structlog + logger = structlog.get_logger(__name__) @@ -18,70 +19,118 @@ class Portal(abst.SluggedAbstractMongoRecord): "newsletter", "organization" ] + attr_schemas = { + "about": { + "title": { + "type": "dict", + "required": True, + "schema": { + "en": {"type": "string", "required": True}, + "he": {"type": "string", "required": True} + } + }, + "title_url": {"type": "string"}, + "image_uri": {"type": "string"}, + "image_caption": { + "type": "dict", + "schema": { + "en": {"type": "string"}, + "he": {"type": "string"} + } + }, + "description": { + "type": "dict", + "schema": { + "en": {"type": "string", "required": True}, + "he": {"type": "string", "required": True} + } + }, + }, + "mobile": { + "title": { + "type": "dict", + "required": True, + "schema": { + "en": {"type": "string", "required": True}, + "he": {"type": "string", "required": True} + } + }, + "description": { + "type": "dict", + "schema": { + "en": {"type": "string"}, + "he": {"type": "string"} + } + }, + "android_link": {"type": "string"}, + "ios_link": {"type": "string"} + }, + "organization": { + "title": { + "type": "dict", + "required": True, + "schema": { + "en": {"type": "string", "required": True}, + "he": {"type": "string", "required": True} + } + }, + "description": { + "type": "dict", + "schema": { + "en": {"type": "string", "required": True}, + "he": {"type": "string", "required": True} + } + }, + }, + "newsletter": { + "title": { + "type": "dict", + "required": True, + "schema": { + "en": {"type": "string", "required": True}, + "he": {"type": "string", "required": True} + } + }, + "description": { + "type": "dict", + "schema": { + "en": {"type": "string", "required": True}, + "he": {"type": "string", "required": True} + } + }, + "title_url": {"type": "string"}, + "api_schema": { + "type": "dict", + "schema": { + "http_method": {"type": "string", "required": True}, + "payload": { + "type": "dict", + "schema": { + "first_name_key": {"type": "string"}, + "last_name_key": {"type": "string"}, + "email_key": {"type": "string"} + } + }, + } + } + } + } def _validate(self): super(Portal, self)._validate() - - about_schema = { - "title": ({"en": (str, "required"), "he": (str, "required")}, "required"), - "title_url": (str, "optional"), - "image_uri": (str, "optional"), - "image_caption": ({"en": (str, "optional"), "he": (str, "optional")}, "optional"), - "description": ({"en": (str, "required"), "he": (str, "required")}, "optional"), - } - - mobile_schema = { - "title": ({"en": (str, "required"), "he": (str, "required")}, "required"), - "description": ({"en": (str, "optional"), "he": (str, "optional")}, "optional"), - "android_link": (str, "optional"), - "ios_link": (str, "optional") - } - - organization_schema = { - "title": ({"en": (str, "required"), "he": (str, "required")}, "required"), - "description": ({"en": (str, "required"), "he": (str, "required")}, "optional"), - } - - newsletter_schema = { - "title": ({"en": (str, "required"), "he": (str, "required")}, "required"), - "description": ({"en": (str, "required"), "he": (str, "required")}, "optional"), - "title_url": (str, "optional"), - "api_schema": ({"http_method": (str, "required"), - "payload": ({"first_name_key": (str, "optional"), "last_name_key": (str, "optional"), "email_key": (str, "optional")}, "optional")} - , "optional") - } - - validate_dictionary(self.name, {"en": (str, "required"), "he": (str, "required")}) - if hasattr(self, "about"): - validate_dictionary(self.about, about_schema) title_url = self.about.get("title_url") - if title_url: - validate_url(title_url) + if title_url: validate_url(title_url) if hasattr(self, "mobile"): - validate_dictionary(self.mobile, mobile_schema) android_link = self.mobile.get("android_link") - if android_link: - validate_url(android_link) + if android_link: validate_url(android_link) ios_link = self.mobile.get("ios_link") - if ios_link: - validate_url(ios_link) - if hasattr(self, "organization"): - validate_dictionary(self.organization, organization_schema) + if ios_link: validate_url(ios_link) if hasattr(self, "newsletter"): - validate_dictionary(self.newsletter, newsletter_schema) http_method = self.newsletter.get("api_schema", {}).get("http_method") - if http_method: - validate_http_method(http_method) + if http_method: validate_http_method(http_method) return True class PortalSet(abst.AbstractMongoSet): recordClass = Portal - - - - - - - diff --git a/sefaria/system/validators.py b/sefaria/system/validators.py index 6c060ab487..f36ba8282f 100644 --- a/sefaria/system/validators.py +++ b/sefaria/system/validators.py @@ -11,56 +11,6 @@ , InvalidHTTPMethodException, InvalidURLException -def validate_dictionary(data, schema): - """ - Validates that a given dictionary complies with the provided schema. - - Args: - data (dict): The dictionary to be validated. - schema (dict): The schema dictionary specifying the expected structure. - - Raises: - SchemaValidationException: If the data does not comply with the schema. - - Returns: - bool: True if the data complies with the schema, False otherwise. - """ - - for key, value_type in schema.items(): - if not (isinstance(value_type, tuple) and len(value_type) == 2 and value_type[1] in ["optional", "required"]): - raise ValueError(f"Invalid schema definition for key '{key}'. Use ('type', 'optional') or ('type', 'required').") - - # Check for keys in data that are not in schema - for key in data.keys(): - if key not in schema: - raise SchemaInvalidKeyException(key) - - for key, value_type in schema.items(): - # Check if the key exists in the data dictionary - if key not in data: - # Check if the field is optional (not required) - if isinstance(value_type, tuple) and len(value_type) == 2 and value_type[1] == "optional": - continue # Field is optional, so skip validation - else: - raise SchemaRequiredFieldException(key) - - # Check if the expected type is a nested dictionary - if isinstance(value_type[0], dict): - nested_data = data[key] - nested_schema = value_type[0] - try: - # Recursively validate the nested dictionary - validate_dictionary(nested_data, nested_schema) - except SchemaValidationException as e: - # If validation fails for the nested dictionary, re-raise the exception with the key - raise SchemaValidationException(f"{key}.{e.key}", e.expected_type) - - # Check the type of the value in the data dictionary - elif not isinstance(data[key], value_type[0]): - raise SchemaValidationException(key, value_type[0]) - return True - - def validate_url(url): try: # Attempt to parse the URL From e1cab153dae0fdeee148418354ba7a54763e1500 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Tue, 7 Nov 2023 12:27:16 +0200 Subject: [PATCH 425/756] refactor: use generic view to subscribe to various organization newsletters --- sefaria/urls.py | 2 +- sefaria/views.py | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/sefaria/urls.py b/sefaria/urls.py index ad7d94349f..757fcef1aa 100644 --- a/sefaria/urls.py +++ b/sefaria/urls.py @@ -390,7 +390,7 @@ # Email Subscribe urlpatterns += [ - url(r'^api/subscribe/steinsaltz/(?P<email>.+)$', sefaria_views.subscribe_steinsaltz_newsletter_view), + url(r'^api/subscribe/(?P<org>.+)/(?P<email>.+)$', sefaria_views.generic_subscribe_to_newsletter_api), url(r'^api/subscribe/(?P<email>.+)$', sefaria_views.subscribe_sefaria_newsletter_view), ] diff --git a/sefaria/views.py b/sefaria/views.py index 5b95d6ba62..68af34159a 100644 --- a/sefaria/views.py +++ b/sefaria/views.py @@ -170,14 +170,21 @@ def accounts(request): }) -def generic_subscribe_to_newsletter_api(request, email, subscribe): +def generic_subscribe_to_newsletter_api(request, org, email): """ Generic view for subscribing a user to a newsletter """ + org_subscribe_fn_map = { + "sefaria": subscribe_sefaria_newsletter, + "steinsaltz": subscribe_steinsaltz, + } body = json.loads(request.body) first_name = body.get("firstName", None) last_name = body.get("lastName", None) try: + subscribe = org_subscribe_fn_map.get(org) + if not subscribe: + return jsonResponse({"error": f"Organization '{org}' not recognized."}) if subscribe(request, email, first_name, last_name): return jsonResponse({"status": "ok"}) else: @@ -189,11 +196,7 @@ def generic_subscribe_to_newsletter_api(request, email, subscribe): def subscribe_sefaria_newsletter_view(request, email): - return generic_subscribe_to_newsletter_api(request, email, subscribe_sefaria_newsletter) - - -def subscribe_steinsaltz_newsletter_view(request, email): - return generic_subscribe_to_newsletter_api(request, email, subscribe_steinsaltz) + return generic_subscribe_to_newsletter_api(request, 'sefaria', email) def subscribe_sefaria_newsletter(request, email, first_name, last_name): From e69470a3cb70c2c1d1e62b8392cfcf191fca2916 Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Tue, 7 Nov 2023 12:52:53 +0200 Subject: [PATCH 426/756] chore(Backend topic images): Add an image-specific exception --- sefaria/system/exceptions.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sefaria/system/exceptions.py b/sefaria/system/exceptions.py index ff64c89336..b52533363f 100644 --- a/sefaria/system/exceptions.py +++ b/sefaria/system/exceptions.py @@ -58,3 +58,7 @@ class ManuscriptError(Exception): class MissingKeyError(Exception): pass + +class ExternalImageError(Exception): + """Thrown when an image is added to the database that is not hosted in our GCP bucket""" + pass From 6e33b7f8374c428aaa1de0bfb8cd9c5b9c8230f8 Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Tue, 7 Nov 2023 12:53:39 +0200 Subject: [PATCH 427/756] feat(Backend topic images): Add image URI validation --- sefaria/model/topic.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sefaria/model/topic.py b/sefaria/model/topic.py index dbc865f83b..d06111ac02 100644 --- a/sefaria/model/topic.py +++ b/sefaria/model/topic.py @@ -3,7 +3,7 @@ from .schema import AbstractTitledObject, TitleGroup from .text import Ref, IndexSet, AbstractTextRecord from .category import Category -from sefaria.system.exceptions import InputError, DuplicateRecordError +from sefaria.system.exceptions import InputError, DuplicateRecordError, ExternalImageError from sefaria.model.timeperiod import TimePeriod from sefaria.system.database import db import structlog, bleach @@ -69,6 +69,11 @@ def _validate(self): super(Topic, self)._validate() if getattr(self, 'subclass', False): assert self.subclass in self.subclass_map, f"Field `subclass` set to {self.subclass} which is not one of the valid subclass keys in `Topic.subclass_map`. Valid keys are {', '.join(self.subclass_map.keys())}" + if getattr(self, 'image', False): + try: + self.image.index("https://storage.googleapis.com/img.sefaria.org/topics/") + except ExternalImageError: + print("The image is not stored properly. Topic should be stored in the image GCP bucket, in the topics subdirectory.") def _normalize(self): super()._normalize() From 5f2d045a445894e0cd1e6139659fa51e78424691 Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Tue, 7 Nov 2023 13:00:59 +0200 Subject: [PATCH 428/756] feat(Backend topic images): Generalize image with caption component, generalize css, rework TopicImage component --- static/css/s2.css | 12 ++++++------ static/js/Misc.jsx | 14 +++++++++++++- static/js/TopicPage.jsx | 6 ++---- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index 128071435d..95f55645ec 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -2289,7 +2289,7 @@ div.interfaceLinks-row a { font-family: "Taamey Frank", "adobe-garamond-pro", "Crimson Text", Georgia, "Times New Roman", serif; margin-bottom: -3px; } -.topicPhoto{ +.imageWithCaptionPhoto{ border: 1px solid #EDEDEC; max-width: 100%; height: auto; @@ -2297,13 +2297,13 @@ div.interfaceLinks-row a { top: 121px; left: 835px; } -.topicImageCaption .int-en { +.imageCaption .int-en { font-family: Roboto; } -.topicImageCaption .int-he { +.imageCaption .int-he { font-family: Roboto; } -.topicImageCaption { +.imageCaption { font-size: 12px; font-weight: 400; line-height: 15px; @@ -2316,7 +2316,7 @@ div.interfaceLinks-row a { padding-right: 44px; } @media (max-width: 600px) { - .topicPhoto{ + .imageWithCaptionPhoto{ height: auto; max-width: calc(66.67vw); max-height: calc(66.67vw); @@ -2331,7 +2331,7 @@ div.interfaceLinks-row a { justify-content: center; align-items: center; } - .topicImageCaption { + .imageCaption { font-size: 12px; font-weight: 400; line-height: 15px; diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 7468ab72f4..e031c8f928 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -3296,6 +3296,17 @@ const Autocompleter = ({getSuggestions, showSuggestionsOnSelect, inputPlaceholde ) } +const ImageWithCaption = ({photoLink, enCaption, heCaption }) => { + + return ( + <div> + <img class="imageWithCaptionPhoto" src={photoLink}/> + <div class="imageCaption"> + <InterfaceText text={{en:enCaption, he:heCaption}} /> + </div> + </div>); +} + export { CategoryHeader, SimpleInterfaceBlock, @@ -3360,5 +3371,6 @@ export { CategoryChooser, TitleVariants, requestWithCallBack, - OnInView + OnInView, + ImageWithCaption }; diff --git a/static/js/TopicPage.jsx b/static/js/TopicPage.jsx index a5bfb8df6f..d9765fa880 100644 --- a/static/js/TopicPage.jsx +++ b/static/js/TopicPage.jsx @@ -23,6 +23,7 @@ import { ToolTipped, SimpleLinkedBlock, CategoryHeader, + ImageWithCaption } from './Misc'; @@ -742,10 +743,7 @@ const TopicImage = ({photoLink, enCaption, heCaption }) => { return ( <div class="topicImage"> - <img class="topicPhoto" src={photoLink}/> - <div class="topicImageCaption"> - <InterfaceText text={{en:enCaption, he:heCaption}} /> - </div> + <ImageWithCaption photoLink={photoLink} enCaption={enCaption} heCaption={heCaption} /> </div>); } From c9c3527aecec0f88d0f86ef79a34d7739164614e Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Tue, 7 Nov 2023 13:06:34 +0200 Subject: [PATCH 429/756] feat(Backend topic images): Add helper function for data migration --- sefaria/helper/topic.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/sefaria/helper/topic.py b/sefaria/helper/topic.py index a18f15cea0..dd44952761 100644 --- a/sefaria/helper/topic.py +++ b/sefaria/helper/topic.py @@ -1266,4 +1266,23 @@ def delete_ref_topic_link(tref, to_topic, link_type, lang): link.delete() return {"status": "ok"} else: - return {"error": f"Cannot delete link between {tref} and {to_topic}."} \ No newline at end of file + return {"error": f"Cannot delete link between {tref} and {to_topic}."} + + +def add_image_to_topic(topic_slug, image_uri, en_caption, he_caption): + """ + A function to add an image to a Topic in the database. Helper for data migration. + This function queries the desired Topic, adds the image data, and then saves. + :param topic_slug String: A valid slug for a Topic + :param image_uri String: The URI of the image stored in the GCP images bucket, in the topics subdirectory. + NOTE: Incorrectly stored, or external images, will not pass validation for save + :param en_caption String: The English caption for a Topic image + :param he_caption String: The Hebrew caption for a Topic image + """ + topic = Topic().load({'slug': topic_slug}) + topic.image = {"image_uri": image_uri, + "image_caption": { + "en": en_caption, + "he": he_caption + }} + topic.save() \ No newline at end of file From 5241a678fc90614cbf5e8221ec19c1e5c7d68469 Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Tue, 7 Nov 2023 13:20:09 +0200 Subject: [PATCH 430/756] fix(Backend topic images): Adjust validate function --- sefaria/model/topic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sefaria/model/topic.py b/sefaria/model/topic.py index d06111ac02..a7e3dfbeec 100644 --- a/sefaria/model/topic.py +++ b/sefaria/model/topic.py @@ -71,7 +71,7 @@ def _validate(self): assert self.subclass in self.subclass_map, f"Field `subclass` set to {self.subclass} which is not one of the valid subclass keys in `Topic.subclass_map`. Valid keys are {', '.join(self.subclass_map.keys())}" if getattr(self, 'image', False): try: - self.image.index("https://storage.googleapis.com/img.sefaria.org/topics/") + self.image["image_uri"].index("https://storage.googleapis.com/img.sefaria.org/topics/") except ExternalImageError: print("The image is not stored properly. Topic should be stored in the image GCP bucket, in the topics subdirectory.") From 0d0936c50013b72b4e65dd61f2b0b1b13e254d89 Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Tue, 7 Nov 2023 13:22:42 +0200 Subject: [PATCH 431/756] chore(Backend topic images): Remove hardcoded map --- static/js/TopicPage.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/TopicPage.jsx b/static/js/TopicPage.jsx index d9765fa880..70b19a9c6f 100644 --- a/static/js/TopicPage.jsx +++ b/static/js/TopicPage.jsx @@ -744,7 +744,7 @@ const TopicImage = ({photoLink, enCaption, heCaption }) => { return ( <div class="topicImage"> <ImageWithCaption photoLink={photoLink} enCaption={enCaption} heCaption={heCaption} /> - </div>); + </div>); } From 83d857a5e042721f2aa3cdaaf77f9142b4a3a572 Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Tue, 7 Nov 2023 13:22:56 +0200 Subject: [PATCH 432/756] chore(Backend topic images): Remove hardcoded map of data --- static/js/TopicPage.jsx | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/static/js/TopicPage.jsx b/static/js/TopicPage.jsx index 70b19a9c6f..00b6e54a15 100644 --- a/static/js/TopicPage.jsx +++ b/static/js/TopicPage.jsx @@ -188,34 +188,6 @@ const sheetRenderWrapper = (toggleSignUpModal) => item => ( ); -const hardcodedTopicImagesMap = { - 'Rosh Hashanah': {'photoLink':'https://museums.cjh.org/web/objects/common/webmedia.php?irn=11469&reftable=ecatalogue&refirn=6640', - 'enCaption':'Rosh Hashanah, Arthur Szyk (1894-1951) Tempera and ink on paper. New Canaan, 1948. Collection of Yeshiva University Museum. Gift of Charles Frost', - 'heCaption': 'ראש השנה, ארתור שיק, ארה״ב 1948. אוסף ישיבה יוניברסיטי'}, - - 'Yom Kippur': {'photoLink':'https://www.bl.uk/IllImages/BLCD/big/K900/K90075-77.jpg', - 'enCaption':'Micrography of Jonah being swallowed by the fish. Germany, 1300-1500, The British Library', - 'heCaption': 'מיקרוגרפיה של יונה בבטן הדג, מתוך ספר יונה ההפטרה של יום כיפור, 1300-1500'}, - - 'The Four Species': {'photoLink':'https://res.cloudinary.com/the-jewish-museum/image/fetch/q_auto,f_auto/v1/https%3A%2F%2Fthejm.netx.net%2Ffile%2Fasset%2F34234%2Fview%2F52568%2Fview_52568%3Ftoken%3D5d5cdc57-6399-40b5-afb0-93139921700e', - 'enCaption':'Etrog container, K B, late 19th century, Germany. The Jewish Museum, Gift of Dr. Harry G. Friedman', - 'heCaption': 'תיבת אתרוג, סוף המאה ה19, גרמניה. המוזיאון היהודי בניו יורק, מתנת דר. הארי ג. פרידמן '}, - - 'Sukkot': {'photoLink':'https://www.bl.uk/IllImages/BLCD/big/d400/d40054-17a.jpg', - 'enCaption':'Detail of a painting of a sukkah. Image taken from f. 316v of Forli Siddur. 1383, Italian rite. The British Library', - 'heCaption': 'פרט ציור של סוכה עם שולחן פרוש ושלוש דמויות. דימוי מתוך סידור פורלי, 1383 איטליה'}, - - 'Simchat Torah': {'photoLink':'https://upload.wikimedia.org/wikipedia/commons/4/4d/Rosh_Hashanah_greeting_card_%287974345646%29.jpg?20150712114334', - 'enCaption':'Rosh Hashanah postcard: Hakafot, Haim Yisroel Goldberg (1888-1943) Publisher: Williamsburg Post Card Co. Germany, ca. 1915 Collection of Yeshiva University Museum', - 'heCaption': 'גלויה לראש השנה: הקפות, חיים גולדברג, גרמניה 1915, אוסף ישיבה יוניברסיטי'}, - - 'Shabbat': {'photoLink':'https://res.cloudinary.com/the-jewish-museum/image/fetch/q_auto,f_auto/v1/https%3A%2F%2Fthejm.netx.net%2Ffile%2Fasset%2F35064%2Fview%2F61838%2Fview_61838%3Ftoken%3D5d5cdc57-6399-40b5-afb0-93139921700e', - 'enCaption':'Friday Evening, Isidor Kaufmann, Austria c. 1920. The Jewish Museum, Gift of Mr. and Mrs. M. R. Schweitzer', - 'heCaption': 'שישי בערב, איזידור קאופמן, וינה 1920. המוזיאון היהודי בניו יורק, מתנת מר וגברת מ.ר. שוויצר'}, - -}; - - /* *** Components */ From af68e4c4f6fdfbe1debe506656598bb56e2fe18c Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Tue, 7 Nov 2023 13:21:58 -0500 Subject: [PATCH 433/756] fix(strapi-cms): Fix allowing banners and modals to be rendered when there isn't a Hebrew localization --- static/js/Misc.jsx | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 7468ab72f4..c0ca0d2c96 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -2220,10 +2220,12 @@ const InterruptingMessage = ({ // Don't show the modal on pages where the button link goes to since you're already there const excludedPaths = ["/donate", "/mobile", "/app", "/ways-to-give"]; if (strapi.modal.buttonURL) { - excludedPaths.push( - new URL(strapi.modal.buttonURL.en).pathname, - new URL(strapi.modal.buttonURL.he).pathname - ); + if (strapi.modal.buttonURL.en) { + excludedPaths.push(new URL(strapi.modal.buttonURL.en).pathname); + } + if (strapi.modal.buttonURL.he) { + excludedPaths.push(new URL(strapi.modal.buttonURL.he).pathname); + } } return excludedPaths.indexOf(window.location.pathname) === -1; }; @@ -2385,10 +2387,12 @@ const Banner = ({ onClose }) => { const excludedPaths = ["/donate", "/mobile", "/app", "/ways-to-give"]; // Don't show the banner on pages where the button link goes to since you're already there if (strapi.banner.buttonURL) { - excludedPaths.push( - new URL(strapi.banner.buttonURL.en).pathname, - new URL(strapi.banner.buttonURL.he).pathname - ); + if (strapi.banner.buttonURL.en) { + excludedPaths.push(new URL(strapi.banner.buttonURL.en).pathname); + } + if (strapi.banner.buttonURL.he) { + excludedPaths.push(new URL(strapi.banner.buttonURL.he).pathname); + } } return excludedPaths.indexOf(window.location.pathname) === -1; }; From fdfc6039dc729827091e5a7f26951d0f3a34c3f1 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Wed, 8 Nov 2023 09:48:28 +0200 Subject: [PATCH 434/756] fix(Title Group): remove 'not' in remove_title --- sefaria/helper/tests/topic_test.py | 1 + sefaria/helper/topic.py | 6 +++--- sefaria/model/schema.py | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/sefaria/helper/tests/topic_test.py b/sefaria/helper/tests/topic_test.py index 526bc88f5d..242539b5dc 100644 --- a/sefaria/helper/tests/topic_test.py +++ b/sefaria/helper/tests/topic_test.py @@ -99,6 +99,7 @@ def test_author_root(author_root, actual_author): update_topic(actual_author["topic"], **new_values) assert Place().load({'key': new_values["birthPlace"]}) assert actual_author["topic"].properties["birthYear"]["value"] == 1300 + Place().load({'key': new_values["birthPlace"]}).delete() def test_change_categories_and_titles(author_root, root_with_self_link): # tests moving both root categories down the tree and back up and asserting that moving down the tree changes the tree diff --git a/sefaria/helper/topic.py b/sefaria/helper/topic.py index 837c34b4a5..30d34725a1 100644 --- a/sefaria/helper/topic.py +++ b/sefaria/helper/topic.py @@ -1042,8 +1042,8 @@ def topic_change_category(topic_obj, new_category, old_category="", rebuild=Fals rebuild_topic_toc(topic_obj, category_changed=True) return topic_obj -def update_topic_titles(topic, **kwargs): - new_primary = {"en": kwargs['title'], "he": kwargs["heTitle"]} +def update_topic_titles(topic, title="", heTitle="", **kwargs): + new_primary = {"en": title, "he": heTitle} for lang in ['en', 'he']: # first add new primary titles then remove old alt titles and add new alt titles for title in topic.get_titles(lang): topic.remove_title(title, lang) @@ -1059,7 +1059,7 @@ def update_authors_place_and_time(topic, dataSource='learning-team-editing-tool' if not hasattr(topic, 'properties'): topic.properties = {} process_topic_place_change(topic, **kwargs) - return update_author_era(topic, **kwargs, dataSource=dataSource) + return update_author_era(topic, dataSource=dataSource, **kwargs) def update_properties(topic_obj, dataSource, k, v): if v == '': diff --git a/sefaria/model/schema.py b/sefaria/model/schema.py index bd107dba02..568f0b89a5 100644 --- a/sefaria/model/schema.py +++ b/sefaria/model/schema.py @@ -125,7 +125,7 @@ def secondary_titles(self, lang=None): return [t for t in self.all_titles(lang) if t != self.primary_title(lang)] def remove_title(self, text, lang): - is_primary = len([t for t in self.titles if not (t["lang"] == lang and t["text"] == text and t.get('primary'))]) + is_primary = len([t for t in self.titles if (t["lang"] == lang and t["text"] == text and t.get('primary'))]) if is_primary: self._primary_title[lang] = None self.titles = [t for t in self.titles if not (t["lang"] == lang and t["text"] == text)] From 9075f4d2109149efa47c3bc1828cd6e1efec3b19 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Wed, 8 Nov 2023 09:52:37 +0200 Subject: [PATCH 435/756] chore: fix comment --- sefaria/helper/topic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sefaria/helper/topic.py b/sefaria/helper/topic.py index 30d34725a1..4f5982728c 100644 --- a/sefaria/helper/topic.py +++ b/sefaria/helper/topic.py @@ -1044,7 +1044,7 @@ def topic_change_category(topic_obj, new_category, old_category="", rebuild=Fals def update_topic_titles(topic, title="", heTitle="", **kwargs): new_primary = {"en": title, "he": heTitle} - for lang in ['en', 'he']: # first add new primary titles then remove old alt titles and add new alt titles + for lang in ['en', 'he']: # first remove all titles and add new primary and then alt titles for title in topic.get_titles(lang): topic.remove_title(title, lang) topic.add_title(new_primary[lang], lang, True, False) From 334bc45cd3559f5ac8d781de49a6640653eaca6d Mon Sep 17 00:00:00 2001 From: Brendan Galloway <brendan@flanksource.com> Date: Wed, 8 Nov 2023 10:38:17 +0200 Subject: [PATCH 436/756] helm(fix): remove misplaced linebreak --- .../templates/configmap/create-mongo-dumps.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/helm-chart/sefaria-project/templates/configmap/create-mongo-dumps.yaml b/helm-chart/sefaria-project/templates/configmap/create-mongo-dumps.yaml index 40f79d01e5..60d126a91b 100644 --- a/helm-chart/sefaria-project/templates/configmap/create-mongo-dumps.yaml +++ b/helm-chart/sefaria-project/templates/configmap/create-mongo-dumps.yaml @@ -67,8 +67,7 @@ data: sleep 2 done - until mongodump --uri="$URI" -v -d $DATABASE --excludeCollection=history --excludeCollection=texts --excludeCollection=sheets --excludeCollection=links - --excludeCollection=django_cache --excludeCollection=user_history -o "${DATADIR}/dump" + until mongodump --uri="$URI" -v -d $DATABASE --excludeCollection=history --excludeCollection=texts --excludeCollection=sheets --excludeCollection=links --excludeCollection=django_cache --excludeCollection=user_history -o "${DATADIR}/dump" do echo "trying to dump other stuff again" sleep 2 From 964bc46510951cbc447f8f4aaad365f1496b977d Mon Sep 17 00:00:00 2001 From: Brendan Galloway <brendan@flanksource.com> Date: Wed, 8 Nov 2023 10:52:34 +0200 Subject: [PATCH 437/756] helm(fix): remove explicit ephemeral storage limits --- .../sefaria-project/templates/cronjob/mongo-backup.yaml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/helm-chart/sefaria-project/templates/cronjob/mongo-backup.yaml b/helm-chart/sefaria-project/templates/cronjob/mongo-backup.yaml index 9c4ec49e9f..ba6f6f37d7 100644 --- a/helm-chart/sefaria-project/templates/cronjob/mongo-backup.yaml +++ b/helm-chart/sefaria-project/templates/cronjob/mongo-backup.yaml @@ -58,8 +58,6 @@ spec: command: ["bash"] args: ["-c", "/scripts/create-dumps.sh"] resources: - requests: - ephemeral-storage: 30Gi limits: memory: "500Mi" containers: @@ -100,8 +98,7 @@ spec: name: upload-dumps-{{ .Values.deployEnv }} defaultMode: 0755 - name: shared-volume - emptyDir: - sizeLimit: 30Gi + emptyDir: {} successfulJobsHistoryLimit: 1 failedJobsHistoryLimit: 2 {{- end }} From 948f5038884bee8d0a841e60b7f23cff390ba140 Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Wed, 8 Nov 2023 12:35:59 +0200 Subject: [PATCH 438/756] chore(Backend topic images): Retrieve data for images from API call --- static/js/TopicPage.jsx | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/static/js/TopicPage.jsx b/static/js/TopicPage.jsx index 00b6e54a15..21f4d1ca66 100644 --- a/static/js/TopicPage.jsx +++ b/static/js/TopicPage.jsx @@ -194,7 +194,6 @@ const sheetRenderWrapper = (toggleSignUpModal) => item => ( - const TopicCategory = ({topic, topicTitle, setTopic, setNavTopic, compare, initialWidth, openDisplaySettings, openSearch}) => { const [topicData, setTopicData] = useState(Sefaria.getTopicFromCache(topic) || {primaryTitle: topicTitle}); @@ -312,12 +311,12 @@ const TopicSponsorship = ({topic_slug}) => { ); } -const TopicHeader = ({ topic, topicData, topicTitle, multiPanel, isCat, setNavTopic, openDisplaySettings, openSearch }) => { +const TopicHeader = ({ topic, topicData, topicTitle, multiPanel, isCat, setNavTopic, openDisplaySettings, openSearch, topicImage }) => { const { en, he } = !!topicData && topicData.primaryTitle ? topicData.primaryTitle : {en: "Loading...", he: "טוען..."}; const isTransliteration = !!topicData ? topicData.primaryTitleIsTransliteration : {en: false, he: false}; const category = !!topicData ? Sefaria.topicTocCategory(topicData.slug) : null; - const topicImageKey = topicTitle.en; - const tpTopImg = topicImageKey in hardcodedTopicImagesMap && !multiPanel ? <TopicImage photoLink={hardcodedTopicImagesMap[topicImageKey].photoLink} enCaption={hardcodedTopicImagesMap[topicImageKey].enCaption} heCaption={hardcodedTopicImagesMap[topicImageKey].heCaption}/> : null; + + const tpTopImg = !multiPanel && topicImage ? <TopicImage photoLink={topicImage.image_uri} enCaption={topicImage.image_caption.en} heCaption={topicImage.image_caption.he}/> : null; return ( <div> @@ -405,6 +404,7 @@ const TopicPage = ({ const [parashaData, setParashaData] = useState(null); const [showFilterHeader, setShowFilterHeader] = useState(false); const tabDisplayData = useTabDisplayData(translationLanguagePreference, versionPref); + const topicImage = topicData.image; const scrollableElement = useRef(); const clearAndSetTopic = (topic, topicTitle) => {setTopic(topic, topicTitle)}; @@ -471,11 +471,12 @@ const TopicPage = ({ onClickFilterIndex = displayTabs.length - 1; } const classStr = classNames({topicPanel: 1, readerNavMenu: 1}); + return <div className={classStr}> <div className="content noOverflowX" ref={scrollableElement}> <div className="columnLayout"> <div className="mainColumn storyFeedInner"> - <TopicHeader topic={topic} topicData={topicData} topicTitle={topicTitle} multiPanel={multiPanel} setNavTopic={setNavTopic} openSearch={openSearch} openDisplaySettings={openDisplaySettings} /> + <TopicHeader topic={topic} topicData={topicData} topicTitle={topicTitle} multiPanel={multiPanel} setNavTopic={setNavTopic} openSearch={openSearch} openDisplaySettings={openDisplaySettings} topicImage={topicImage} /> {(!topicData.isLoading && displayTabs.length) ? <TabView currTabName={tab} @@ -534,6 +535,7 @@ const TopicPage = ({ properties={topicData.properties} topicTitle={topicTitle} multiPanel={multiPanel} + topicImage={topicImage} /> {!topicData.isLoading && <Promotions/>} </> @@ -606,7 +608,7 @@ TopicLink.propTypes = { }; -const TopicSideColumn = ({ slug, links, clearAndSetTopic, parashaData, tref, setNavTopic, timePeriod, properties, topicTitle, multiPanel }) => { +const TopicSideColumn = ({ slug, links, clearAndSetTopic, parashaData, tref, setNavTopic, timePeriod, properties, topicTitle, multiPanel, topicImage }) => { const category = Sefaria.topicTocCategory(slug); const linkTypeArray = links ? Object.values(links).filter(linkType => !!linkType && linkType.shouldDisplay && linkType.links.filter(l => l.shouldDisplay !== false).length > 0) : []; if (linkTypeArray.length === 0) { @@ -626,7 +628,7 @@ const TopicSideColumn = ({ slug, links, clearAndSetTopic, parashaData, tref, set const readingsComponent = hasReadings ? ( <ReadingsComponent parashaData={parashaData} tref={tref} /> ) : null; - const topicMetaData = <TopicMetaData timePeriod={timePeriod} properties={properties} topicTitle={topicTitle} multiPanel={multiPanel}/>; + const topicMetaData = <TopicMetaData timePeriod={timePeriod} properties={properties} topicTitle={topicTitle} multiPanel={multiPanel} topicImage={topicImage}/>; const linksComponent = ( links ? linkTypeArray.sort((a, b) => { @@ -776,7 +778,7 @@ const propKeys = [ ]; -const TopicMetaData = ({ topicTitle, timePeriod, multiPanel, properties={} }) => { +const TopicMetaData = ({ topicTitle, timePeriod, multiPanel, topicImage, properties={} }) => { const tpSection = !!timePeriod ? ( <TopicSideSection title={{en: "Lived", he: "תקופת פעילות"}}> <div className="systemText topicMetaData"><InterfaceText text={timePeriod.name} /></div> @@ -814,8 +816,8 @@ const TopicMetaData = ({ topicTitle, timePeriod, multiPanel, properties={} }) => } </TopicSideSection> ) : null; - const topicImageKey = topicTitle.en; - const tpSidebarImg = topicImageKey in hardcodedTopicImagesMap && multiPanel ? <TopicImage photoLink={hardcodedTopicImagesMap[topicImageKey].photoLink} enCaption={hardcodedTopicImagesMap[topicImageKey].enCaption} heCaption={hardcodedTopicImagesMap[topicImageKey].heCaption}/> : null; + + const tpSidebarImg = multiPanel && topicImage ? <TopicImage photoLink={topicImage.image_uri} enCaption={topicImage.image_caption.en} heCaption={topicImage.image_caption.he}/> : null; return ( <> {tpSidebarImg} From 122776f5d3947252e0d4def658b87e09ea1407e2 Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Wed, 8 Nov 2023 20:47:59 +0200 Subject: [PATCH 439/756] chore(Backend topic images): Add data migration script --- scripts/topic_data_migration.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 scripts/topic_data_migration.py diff --git a/scripts/topic_data_migration.py b/scripts/topic_data_migration.py new file mode 100644 index 0000000000..e69de29bb2 From 23e04d2316a7edf7b6b5453ca6118763fd3215a1 Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Wed, 8 Nov 2023 20:48:36 +0200 Subject: [PATCH 440/756] chore(Backend topic images): Enhanced data migration script --- scripts/topic_data_migration.py | 43 +++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/scripts/topic_data_migration.py b/scripts/topic_data_migration.py index e69de29bb2..fac1c31ce3 100644 --- a/scripts/topic_data_migration.py +++ b/scripts/topic_data_migration.py @@ -0,0 +1,43 @@ +import django + +django.setup() + +from sefaria.model import * + +from sefaria.helper.topic import add_image_to_topic + +## Adding images + +hardcodedTopicImagesMap = { + 'rosh-hashanah': {'image_uri': 'https://storage.googleapis.com/img.sefaria.org/topics/rosh-hashanah.jpeg', + 'enCaption': 'Rosh Hashanah, Arthur Szyk (1894-1951) Tempera and ink on paper. New Canaan, 1948. Collection of Yeshiva University Museum. Gift of Charles Frost', + 'heCaption': 'ראש השנה, ארתור שיק, ארה״ב 1948. אוסף ישיבה יוניברסיטי'}, + + 'yom-kippur': {'image_uri': 'https://storage.googleapis.com/img.sefaria.org/topics/yom-kippur.jpeg', + 'enCaption': 'Micrography of Jonah being swallowed by the fish. Germany, 1300-1500, The British Library', + 'heCaption': 'מיקרוגרפיה של יונה בבטן הדג, מתוך ספר יונה ההפטרה של יום כיפור, 1300-1500'}, + + 'the-four-species': {'image_uri': 'https://storage.googleapis.com/img.sefaria.org/topics/the-four-species.jpg', + 'enCaption': 'Etrog container, K B, late 19th century, Germany. The Jewish Museum, Gift of Dr. Harry G. Friedman', + 'heCaption': 'תיבת אתרוג, סוף המאה ה19, גרמניה. המוזיאון היהודי בניו יורק, מתנת דר. הארי ג. פרידמן '}, + + 'sukkot': {'image_uri': 'https://storage.googleapis.com/img.sefaria.org/topics/sukkot.jpg', + 'enCaption': 'Detail of a painting of a sukkah. Image taken from f. 316v of Forli Siddur. 1383, Italian rite. The British Library', + 'heCaption': 'פרט ציור של סוכה עם שולחן פרוש ושלוש דמויות. דימוי מתוך סידור פורלי, 1383 איטליה'}, + + 'simchat-torah': {'image_uri': 'https://storage.googleapis.com/img.sefaria.org/topics/simchat-torah.jpg', + 'enCaption': 'Rosh Hashanah postcard: Hakafot, Haim Yisroel Goldberg (1888-1943) Publisher: Williamsburg Post Card Co. Germany, ca. 1915 Collection of Yeshiva University Museum', + 'heCaption': 'גלויה לראש השנה: הקפות, חיים גולדברג, גרמניה 1915, אוסף ישיבה יוניברסיטי'}, + + 'shabbat': {'image_uri': 'https://storage.googleapis.com/img.sefaria.org/topics/shabbat.jpg', + 'enCaption': 'Friday Evening, Isidor Kaufmann, Austria c. 1920. The Jewish Museum, Gift of Mr. and Mrs. M. R. Schweitzer', + 'heCaption': 'שישי בערב, איזידור קאופמן, וינה 1920. המוזיאון היהודי בניו יורק, מתנת מר וגברת מ.ר. שוויצר'}, + +} + +for topic in hardcodedTopicImagesMap: + add_image_to_topic(topic, + image_uri=hardcodedTopicImagesMap[topic]["image_uri"], + en_caption=hardcodedTopicImagesMap[topic]["enCaption"], + he_caption=hardcodedTopicImagesMap[topic]["heCaption"]) + From 621a9475273bc6268e60493570b29f6de56bb032 Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Wed, 8 Nov 2023 20:49:04 +0200 Subject: [PATCH 441/756] fix(Backend topic images): Adjust the caption props as per feedback in code review --- static/js/Misc.jsx | 4 ++-- static/js/TopicPage.jsx | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index a2e6dfe310..d095b833fd 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -3300,13 +3300,13 @@ const Autocompleter = ({getSuggestions, showSuggestionsOnSelect, inputPlaceholde ) } -const ImageWithCaption = ({photoLink, enCaption, heCaption }) => { +const ImageWithCaption = ({photoLink, caption }) => { return ( <div> <img class="imageWithCaptionPhoto" src={photoLink}/> <div class="imageCaption"> - <InterfaceText text={{en:enCaption, he:heCaption}} /> + <InterfaceText text={{en:caption.en, he:caption.he}} /> </div> </div>); } diff --git a/static/js/TopicPage.jsx b/static/js/TopicPage.jsx index 21f4d1ca66..dc73f4064e 100644 --- a/static/js/TopicPage.jsx +++ b/static/js/TopicPage.jsx @@ -316,7 +316,7 @@ const TopicHeader = ({ topic, topicData, topicTitle, multiPanel, isCat, setNavTo const isTransliteration = !!topicData ? topicData.primaryTitleIsTransliteration : {en: false, he: false}; const category = !!topicData ? Sefaria.topicTocCategory(topicData.slug) : null; - const tpTopImg = !multiPanel && topicImage ? <TopicImage photoLink={topicImage.image_uri} enCaption={topicImage.image_caption.en} heCaption={topicImage.image_caption.he}/> : null; + const tpTopImg = !multiPanel && topicImage ? <TopicImage photoLink={topicImage.image_uri} caption={topicImage.image_caption}/> : null; return ( <div> @@ -713,11 +713,11 @@ const TopicSideSection = ({ title, children, hasMore }) => { ); } -const TopicImage = ({photoLink, enCaption, heCaption }) => { +const TopicImage = ({photoLink, caption }) => { return ( <div class="topicImage"> - <ImageWithCaption photoLink={photoLink} enCaption={enCaption} heCaption={heCaption} /> + <ImageWithCaption photoLink={photoLink} caption={caption} /> </div>); } @@ -817,7 +817,7 @@ const TopicMetaData = ({ topicTitle, timePeriod, multiPanel, topicImage, propert </TopicSideSection> ) : null; - const tpSidebarImg = multiPanel && topicImage ? <TopicImage photoLink={topicImage.image_uri} enCaption={topicImage.image_caption.en} heCaption={topicImage.image_caption.he}/> : null; + const tpSidebarImg = multiPanel && topicImage ? <TopicImage photoLink={topicImage.image_uri} caption={topicImage.image_caption}/> : null; return ( <> {tpSidebarImg} From ff8b266c73064c4d023b3b5d143b49e2cfd08a8c Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Thu, 9 Nov 2023 11:05:20 +0200 Subject: [PATCH 442/756] feat(api): get texts from new api. --- static/js/sefaria/textGetter.js | 34 +++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 static/js/sefaria/textGetter.js diff --git a/static/js/sefaria/textGetter.js b/static/js/sefaria/textGetter.js new file mode 100644 index 0000000000..572fe3cc48 --- /dev/null +++ b/static/js/sefaria/textGetter.js @@ -0,0 +1,34 @@ +import Sefaria from "./sefaria"; + +function makeParamsString(language, versionTitle) { + if (versionTitle) { + return `${language}|${versionTitle}`; + } else if (language) { + return language; + } +} + +function makeUrl(ref, requiredVersions) { + const host = Sefaria.apiHost; + const endPoint = '/api/v3/texts/' + const versions = Object.entries(requiredVersions).map(([language, versionTitle]) => + makeParamsString(language, versionTitle) + ); + const url = `${host}${endPoint}${ref}?version=${versions.join('&version=')}&fill_in_missing_segments=true`; + return url; +} + +async function getVTextsFromAPI(ref, requiredVersions) { + const url = makeUrl(ref, requiredVersions); + const apiObject = await Sefaria._ApiPromise(url); + Sefaria.saveVersions(ref, apiObject.available_versions); + delete apiObject.available_versions; + return apiObject; +} + +export async function getTexts(ref, requiredVersions) { + // ref is segment ref or bottom level section ref + // requiredVersions is array of objects that can have language and versionTitle + let returnObj = await getVersionsFromAPI(ref, requiredVersions); + return returnObj; +} From 0aeb6e644b80dd7d570ded3205ca041b3bac5ce1 Mon Sep 17 00:00:00 2001 From: yonadavGit <yonadav.le@gmail.com> Date: Thu, 9 Nov 2023 13:37:03 +0200 Subject: [PATCH 443/756] fix(css): polish reader control icons --- static/css/s2.css | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index b5a4d0f3db..3d25a53d0d 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -4511,6 +4511,7 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus } .readerControls.transLangPrefSuggBann { background-color: #EDEDEC; + z-index: 99; } .readerControls .readerControlsInner.transLangPrefSuggBannInner { justify-content: center; @@ -4706,11 +4707,11 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus .readerOptions .int-en { font-size: 25px; line-height: 63px; - margin-right: 8px; + margin-right: 5px; } .readerOptions .int-he { - font-size: 28px; - line-height: 67px; + font-size: 32px; + line-height: 65px; margin-left: 6px; } .rightButtons .readerOptions { From 28e4ff0400e44cd4a2600fe1d8ec040ac95fb879 Mon Sep 17 00:00:00 2001 From: yonadavGit <yonadav.le@gmail.com> Date: Thu, 9 Nov 2023 15:48:53 +0200 Subject: [PATCH 444/756] fix(css): remove margin from English reader icon --- static/css/s2.css | 1 - 1 file changed, 1 deletion(-) diff --git a/static/css/s2.css b/static/css/s2.css index 3d25a53d0d..3586129f39 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -4707,7 +4707,6 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus .readerOptions .int-en { font-size: 25px; line-height: 63px; - margin-right: 5px; } .readerOptions .int-he { font-size: 32px; From da7f921bd85673055aebebd48a64ffcf45b4adc7 Mon Sep 17 00:00:00 2001 From: Ephraim <ephraim@sefaria.org> Date: Thu, 9 Nov 2023 16:36:15 +0200 Subject: [PATCH 445/756] ci: Temporarily disable sandbox uninstall --- .github/workflows/continuous.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/continuous.yaml b/.github/workflows/continuous.yaml index 3fa4611d49..c5056a095f 100644 --- a/.github/workflows/continuous.yaml +++ b/.github/workflows/continuous.yaml @@ -389,5 +389,5 @@ jobs: NAMESPACE: ${{ secrets.DEV_SANDBOX_NAMESPACE }} NAME: sandbox-${{ steps.get-sha.outputs.sha_short }} - name: Uninstall - run: helm delete sandbox-${{ steps.get-sha.outputs.sha_short }} -n ${{ secrets.DEV_SANDBOX_NAMESPACE }} --debug --timeout 10m0s + run: echo "helm delete sandbox-${{ steps.get-sha.outputs.sha_short }} -n ${{ secrets.DEV_SANDBOX_NAMESPACE }} --debug --timeout 10m0s" if: steps.get-helm.outputs.count > 0 From 8e72a9357f7117c43e331fc0b52b49c69bdc1eb5 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Fri, 10 Nov 2023 14:30:21 -0500 Subject: [PATCH 446/756] static(team-page): Update titles for Shanee, Olivia and add Yedida --- static/img/headshots/yedida.png | Bin 0 -> 276814 bytes templates/static/en/team.html | 18 +++++++++++++++--- templates/static/he/team.html | 17 +++++++++++++++-- 3 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 static/img/headshots/yedida.png diff --git a/static/img/headshots/yedida.png b/static/img/headshots/yedida.png new file mode 100644 index 0000000000000000000000000000000000000000..9098f6e9be98dd33ea1e171673ae59a5bd649597 GIT binary patch literal 276814 zcmV)JK)b(*P)<h;3K|Lk000e1NJLTq00Mjf00Mjn1ONa4&g8V+0003ZX+uL$Nkc;* zP;zf(X>4Tx04R~2kiAO8P!NT`tm40wVj&h0Z5M1zWwR;-K_SK!T&=Q678jEkk}S)< zfp4M@vxOk|3ib-to*OMJOyj^XAH&Q!b1$^)BuvZMJxH^vaQ#m2s&{SAzp>0B7CUSQ zVVQS4@07;)eAT4lrERpD=l+bjC@w?QsWJ=mq5|tc?2N12u#)w=u74zc5Zh6jMdBy% zZj?rb&90v&qwq)5T(c5qmlvk5+`{FAGdyhij2Mzo(N@jWImTgM)ukX{LP@Ba5UV%V zOvSCJl#<J#s4LPn4St)opC^~TG=|bL4)>(eG=7->{a*VxuQ#oYX&w|o-I`Ih1_OA1 zgw-CbJ;TyXgV)0Ew>8W-^~^ut_yyjlP+*T>&v5_%00Lr5M??VshmXv^00009a7bBm z000&x000&x0ZCFM@Bjb+2XskIMF-~#2>~h&L|k+k0000IbVXQnLvL+uWo~o;Q)6;v zWo~n5Z*=<+s|o-BAOJ~3K~#9!q}^GQELnCP=I^-2nDcO_x>eOxJpv7gCQ+1RH0XB- z_&fcFD5L3B;u~oa%{YK0NOYsoK+U&q-kW305gvZ@5GNvIaF5KEvaPEsGb6&&hqKS# zYpuO@_&+1T=2rwkL|prAT>IzpUD4+I7vG2UUyFB#)$bl(87kkm{;A9N#`OQi&*R2x ztbOwO_nT)FR^NxM_gFjA$DgeqvFa<XePXtAO}3A{dECvvbN4H(&M@wLQrx-|R@Zs+ z?BdoH3R~}rTkqWd{o@s0-hsGwC)e*#l$px~rE=p*S$x0zUo=bCEC`Bpg=feGxh$Ps zxw@kB)e)OYMS5pICMeAWnL^Z*i|J^kT&_rGl4;7!(Ka*}Wa$_o{q?0x5ab#%4W*fg zA{{5L&Qm(-;#C=Pb2GVob<T^cl&!AW;>wV1-Se<Iepb1v!s>6dy7JY}byJ~8S1hc~ z`f>e)n)RV(O~=|-Y(|CcXGG1~3mZ>dto(V^8rY5tv34C+y)UdqlUVcVjdJTpkC!N{ zU6inOf!2Q>b}nt)SfZOhU%oo4Sfp`nImVqE6F0sKTd&!CaN;Te$I2k`_@1z~6skk< z`c2=xym9rruyNkIw>)eF?oe?ptHSy21GxVyR=zXTKI+w<ym?p4wfnWb)kT?d^_k1F zEtMJ@_aaYszcd$9P+9ywQ>K9TxM2o`OvBt9Iq4{iz?G%fWhwMM1}G`$&1D)W1=;FI zxoD1T6)GMBZK0Jy^77dzH=fMpx#lTA2VoT&=WE+vwspm;x8%6?tS+zc`V+LdQO2ET zZh2*A8tM3PV@2+6lKVoMiglziSx~dF+q-V+o-q}PRoGf>`vPUCJ#u(r%|wmelU={~ zRXfFcZnoUz)))QuJK|Q5TKrMv+DV2dB73<NR33M;xOx=o{^HFHZoYchcK5TeI$GU@ zi))uP?wr?VrWUt0hfsS(sxr{}$0bxgPVtFsvibpxHIH?C;*nk7!h$mWpD0(P%qmYA z*kc}7uC9EUGP)eOECtGPbzK`N8!Kf>bCl^P%X7@dRjyT<$yZrgOaW<eq(UL2jIohE zWf4frwe!!?XJ@Ows-;Xpa`DqLg{MWxT;`<995PGSB42F}i?eB@SA?D1dh<%I*ADC9 zt!go6YtMWsm}`)-zPPcuZr;Ug=kuyljrwOOJag-<4>)mcMOQt|+grxI)$+Wf*RQBX ztW6%{>H)7Vrs76u+K$(8C$LpzPuq*kRuJ30G;z=O>)Bn{3gDYXZ(QxqvEsO)=B8|) zcdRM-V{NGr_6D@-(DL*y{m1<G_Ibpb2P3ZKL~&<J32P<oW44itg2q<PQKqo5%&r&t zO}<f*<?>ZnS{}PJ(@Z(yVu_cjbX;VO?R1q2`Pw!bQt2AaWUJzKs%dU6*DzZv+t=@T zkv^>yQfa;Zm1CZA$$5I`9F0|oeM|_lRnb|lt)jXmTJD6vxKR{uCmN5xTV$|7bCrIC z+M+dX1;9{S<c2-Z!yo$<YgT&Psc+WPrm*!H<z8s$sqAyT;(GjNBUV<_aRX!64rp-; zO^Q3a#^&p)%CB;38O05Ba2HpMTOq03%A%?fflYjH8=ngmk7QisB(V~ii<MYm+=D&r zo=w=<$HKG9fTwoey1g-01oCnZhPnAb#;qd#5zmV0IxWzuMbWnmY2|7$mMPN?iu6gP zOuowS9=EAwPFSY=Y`)55vQ%b<RVEe96@tP<Q&TQC*H{ImsA;yk-wh3OnOu=D!u5Nd zrEoPh)|Q*4kd|pEQZZf#DGY~I*vV5~_?R^=o`ucqD(qR9)m!j3J{)TRqh)cQ=^CtE zNjuBklYuPMX8G#@qul$h{o8J=NLE56)Dt&AkNrqstN^L@t&8RB8dbL;R$hX*fr-R5 zI677wI#%RSWd&-sTV{tnn83b$r`)<+j~B5zxP>R-)6FndZiK8*QRG(>5}}e1sykBE z-XES(Mm#4U+|H#R^MoqsA?&~fVGG8$D!B4g5H7K=<yp^D0o+KTaGuKFMF`8*0?+1| zE)c?b3Ns-EtL69kD$^{{7FIdmB3o-}C{0r!%CuKOF>Zx}MFBe(<mtB~G*{PTDo-J| zT)jF^&svfGnt3|@g7zpk(8!pAVp##HR4vR<xzxjIO@>N@ZJG$4<+&|!(W<}@D}%?= z5X)n}7%R%iSd)i6g*3(u^gC>&TlF>IcGg%O<f{wlunc^$2J4DD)I(hHZ7LGe<%Q4E zz7RG@iSPt(6t<#r+=}S2Vp+z@026kwlCTx(b_04nq7`ZaV_A`A$0rGiI>Mxis|^)6 zP*r;WxFKYF&Lh_1ERR<|tTMRe*L+25gpI-@tOd+G6<u+KvdvTQTLi<HD25=I%VjcA zu`12V)wyP?94w^JxJ2g4l+i6Y+xe;>?FhzZnlgo^%q*SR0*{-^%@yWyQw{U<_cwLT zl%|40!%S|LK4;1HW+_ZBGuipt6`UzGg~TdntIu7QpJA0FZC6ooW9tcPMQB+AlI-5; z&8H|c(lS{=1iMwwdWK)c!)~ht!m~=t>dY_fR44HXnk8;kj$yBop*og?JpfV_|J;Z> z(X-qNHDSf=g&o9dvof#ekHXF+4I2z>sEF}lPZ$Y10W(&T1XWpNc!GaYaTC`c*06O+ zLk(3>EpZ7uJ9(%$uc%&BRL=pIm5)oPd}tnXxW@vmt}Nd?Mod}V62Hq<$Bt{Sc-$=W zw5(^TYz&wsEO2>s=PN>DxuFz^vkfa)WP$nRD<WW?va6PknP9HD;*4Voxxp+2&c(Y5 zom3v?hRC!`<bs((<{$}d7DAhuLL+5Qg;Hy)V#rb%9nFkonyd59))?_J6;|_=%Aoqm zj+OU%H$3gGvO=z5y2^U9wIX>Jk_k1K<MYUixQ7;t&nR>EePUGr+efxMN7_;ssvFj{ zN?{{MiW>mz_Kvj=M%|Q<gc@uu)}kK!c$`=ZVeWFi>w#pm$ca1Is!B{DK5+@3!Z*SW zRK1^~uP^Z9*0qea3|2LoQdPDr7}Ir*a9u1Bwou*WGm&l3S$Qh=B;dbHh3GP?%u<%M zD1`Hr3pO^cN0AEGpqYZr5(ivjc0o&_l?u;U;*95lfw3URM=b|hf>O)aT;H9ZuEJOp zymDxugEJFB(<Ev4Hsvj|lzp{QaGQ%MyDQU~WJ#>EAVr2UO_P-GEx6z1XT?=Ak#7-x zVT;EPJ1c8DXN!BPl97hlD&Gq$kRaC3S@of(8smEo)4Q7(g$<7RDKKUq*|t9htc&pM zQK3A3Vk1`WBirh;-ArsdU&<=v7@MC`dRnWdCi;h3RnfYzVjq~hoAQ=x{NYpkU98O% zpT3P@=Wh*r9;Z;7ojsphtCOpStupqJ9b7K^xEAu(i_k~pH%rT^Ogg%_A@a>qI0|ck zebg{p;cHpK!<NkM<1xcZcv7xJB#m_Jo}QM{-}yrBd#~`df!wW;u}?ghI@9t~KQYpG zuXJtTPRqTYc;}rLUMLMr{YvzSduIxzQsG$FQ4+Ldsj!|UWN~?YW+`-s745N*OrM*Y zJ2hQt6xXx6xDuVjEwE&_j!>DYt@FEOO|~|5kgj%CDOgvNUshKJ`zVEN{x??sKCGUo z_@q2*-xePBBuKk}lv?@!_In$#Qf`4tEm3v0671^OwuR5zy#Uo2$zv(7c@odj2*y2O zA@1aNPZg;914^tU-tE1?VUNCOTe!Mi^n_=G<NY|wt|TI^guJl=AuU(k5W7rs+~E1b zDv;(Y+-|*QD3VBIo{G&<shP04u*!(am0-jwTrM)KzW>LMjtrHie&@mWekQnb;ND0_ zX6Q;=A^O^{ePZg+*qM$WTzjeSgV%m*>R8vEf90S0&(7pdoSJy+zxvS^F8!%oCO1Mf zk!hrWmuYF5nI`fiGu6@1*Gw$)M4pI|kn+S_u_9`=W$<x>ACDEXkM&oUNh)5hB_5C9 z#5`>$vlR%DJ*nx8&wA#ogI4ye3~u)<{#Zp`pa!P;|N9mGkLXER#d@}sRfuLclgY4y z*gbUuVTYs$HL&S6&lT3hEt^Sgs9{mV9%L@=1$}oJ&T45(^?8RqcwgMgY~$YKK0fh~ z?tie#)pE<WTADV|(4|NvTNC&!!`gf$9e$)mVk%gzWo%ZWm&a^xD$`t{bZwLL8H-GC zo)&RKF!i;GAAIF2|AV1B({z*{v>hAcz{H*CrSD{ZsjH>*!oXXfX=&q1=AGXO_{N`U z>-tBRUV7&j{+mB>r{NO~S0;Yy7rH47H&Ve`u85MxDibZtv=!+YX_^Zf`r6v5lOm?V zcb3ZJ_3}2XkxS)DbY1nNhSj}z%&M~uxz+MB8%fH&i_+F3#Mu_NT2*s-0#u1Lpv`V+ z{S@L?CBzApt9KuXQUASNa$|qz#5VU=cQRq^Muyr?h|i4RyMoQ?y(I2E(YO;#_V=-F z%Uadohq2~%#R`pIsKFxQ4sRUy<aKc;bBq;~NT_}9rarNnMGvdI>#>_Pcx>QW7P*h- zQLdo>C0;mR@w0_l%G?$qV<~rusmC@L3mRsERuZ|4wB`Om(+B@r+l{Waf8&*??b=kK z?K{VgWbT!2bW9C&e4`^eHgM$MIFb9p&s-`Tx-m3W`Ul5`hVJy-%a#7tYu`F`=F|^L z*ACojqVIF38(rCBL%F%&P*cN9rn72vTbu<;Rba8w9xesYi;ZNqCN~LNown;$Ot#WD zuDADD;f0&3y4hAgQ&{WX-G-TBg{-VTfbB=P!;@jGd=eM8XZ=2%6V_E=`}pbbFJpaT z8!8je&0=pGunk*c7CZS)+))N?mUzKa{ee~4)%p_7D)XOxyTwx(Z>XgDtD)U0N#$;) zxBgwZ)waHW=PhdxmXbZ9ooy+4W?eKMDm3nKt>a=zOe}Mc#;PH|)D#s7#miP_Q(|V8 z^1HZ_2`?0JUBTSUTq9*ni;QrnH1zlWM#Iq5)U}zROz?@e@4Qnwajm6^maY?pJ41O2 ztOrg7$0j~8^QFw#mpaB~{;gx5IFM<{6eg}kpDDeU%S|+#`rytlyp@?a_f|u0>ekpq zUsH6F2#|B<8d_!=LJEopiQFmEnN3N7%Blp;WSS{p&NS2Yj5Y=P>k^`{l19fBMO1EV zzl-piZK)VS1>h7a6sY?V=jYLS)rD>~)fS%x+&nKhh8_LLZr-@_IsbdyEO_>Xfydor z6J5(T!0jrODQp#T)uet{>*$O-JZPv@@!7hqb-7%q;CP-(7N{&mcJtx7M(V&$_sR1d zF5(|i@`eg>qYBdrmEJn*f$%ZAEZ1Z!%U;??wVH%6%~tJR%jUHO1KN-$v2VHdnvRaP z%y^9*J@Uc7)pPG&6b#%6GKWedcP<4VeAG}H=!j0ebgt`IX=>!gjefE;h+5w2Donj~ zqV(1$ruup~bmWDu(%<+;6DPiP=&kPrm!`h=QzKt$>G?br&F}QgT&Eu4Q=N3S4M#4t zQx-SU(J)D+XQ5-BsEx-ims~DtCdtjB<*vC?<-|(kg5{=_*;HaK{(C*}Z9BBsu`hTI z##ZhGn0+BHcuGMU_h61;F9sN%5dzi306S=7qpqu<UUI1tIjCxHuOcJrlHpLnEo~zb zRi$MeU9gKA?%$!p&i=M7Cyq~qo>=JwQ{9IgcFO;-Q{e9^<F^eZ>aX@L4_~BAA+B+Q zVFy-t6hV|*8`Zi=z_R>`D>3ghwSp}d^&{QESTHg)Q<xi@3UV!nE}W+$mVWCS*9IP( zn3yZvYiej3I&`mZW~@lv5vNMWzR<_Ld!@Exe{SwU&%KF>BS(h*QOnG){K4;i>My+Y z53bF8?<)<*e&Nsk-e2pxQ@WOEx%FO4+XKGyl~3LI($6&<8M-ysGS<-X!punKy;r_W zrR-Ely1lr92WBg?f@~#rDKt$}mfTp&*Vp}UHchdzwcP$Otd*;7n7AQA+s5Lv6=)GR z^1^*vZG2YOP_W$)y8BdC)1>9I3)@|k{dv?#ZAknVF;r$Abt1~DjBY!rj2oe(D&dS3 z<!`7hu2t|!p)#z58Zu@3uMZW-#(D{;lI*XNwLB8{;fZ2l6A-9=+_EP!zWshIyT$#c zQ9!n#po|+RQ@QrwKJv6n)OP7wlBE``b$n@2<}NV4rnX#ZW}-Ah-$-F-s;_jEdM<(q zj{V%g0JnZ`s4&-Yt#GO98xL9z^b8cHn%XYX-+rv?K=9F}w*TVTmvR>}M{YGdn7Z({ z{+*VW+UDNqY5UAS*AX@Rog3fdtv_+DZR9(@^_{6>6JNRXPh5#g$6mPc(XT!DmDfgE zdM1hNIB=%vTJD7-2Xf6swoT>QO2g#hxA@;A75rHWWFwh;3n&S)<o)u9%w`);Nlen} zg(7mQk%C#dGW^=ysu!QR;MC(zH%`4^#j^uUtXvy)EtA{uj6KTKr@*SPH{08-94iCg zHlDQ`tU^T?h!res*vcKMB70U5R-X!do80lP_-4IpV;jZTt^ebOz-C|I+YF4`y)E^- zVyMYV_ZPgOBG-G)9=wgLmZ{6QTywzNZ4qyzU==l1Xwo_jGfT~4j|NPSSwm@(-dD&q z<)$WvrUvE;J!3snLvw|Opi4rjKGpO)GXuBIP4x90n3);M-6|bx=$V=ce(hY#iKcHH z%4BkX?-CQ2+L}7av*psU_lBDO;CnYd@xj~+6X!nnxg)O)blrIGKl+tJ2i`c=@UML< zdS|4m>C~OVg{FxMpSd#fTHy=lM$UaOHrMfq58nCA8^=1D+GcV?^P~kl&`5mnqW!Iz zvf@S9s_LXzVu5ibpbo1JnXp1LL&^i^Iw_}Lnr>|>F08V>xPu>7krd&{GV{4=f=#oF z_)PWdz68S)tLu3#f`oq&cZ<QWXE8k{<Yi4M8Fq@tSkVo*PHco76<er~rc@(&)iM+n zf|XFy{TnL^&+0UuYQuna1Ik!~|J8@R_4AFDWPDwt;O_3c+o~2*(bZVX_Odl8N=&v; zkD$AazM7~^rl4V~6)mKku8<`ST`rpF8yT6KnR;+#YHr{v_1i8wFs3g2)KC1KI}e6F zXzPfEqC@wh%=>g6xkG2#hIr-NsSmn-@6&WHXCB;`iVkrgxO3{2wt*uJ!GF+o<<_x@ zKl8?wj?Y|b8kuVOZ<@}%bnd_p3jddn4)nETCfZ4UbL7^i&Ru!s+=HLEMArv%M~;jY zE*#4f-#bw1YNtZB)LwxOrM8%`zXMYv?G0nMGHpi1YJ({^S~Ig$Uo5<98p(*ONW6Ns zgYs@K`njcrSasN`3ny_;r|v#gUsx-`!?P?=>rwXI8gn1aRbLv1e-XFAd6f+2W{wt~ zJpI6qCDb-nm+u(RR0qCTse{|s#kY$XZnt5DH3#KTBe|)f<3lY*7@q`wLq#wTJFx3> zS+sqP<?BdKxt1@)RiQQCK!LKg3_Wh(nx(n9RuZU$Br#rw#A#ZDO%qe4v7V7aaA$;( z=+;nQq2btBk^#3}n)<<anubo@c;~H=k4`ncbK}TBE_du)$3W?g++5~RQ^z}JdO8l= z>bcRz)Ui^gQ0V$IXRh6Q<y_B4fAG$!=%;1|-kLi0)`1g!bN|Vi7ru~bx%Qy&A04~% zkcgGsfqN$k1ApSsS3Ys(2Y=x+|H;r>ujNW@X9=;33M~gK-Q$A&Ui5s#m7PL!W$v() z0zGOam!|tm@8@EzL|hUp%Rb;Nkx*exClM<vqin4k<!KarU$40*Q;?^$d-2Jqe7p91 zTE$l<YOB%lg=b|L)rD0(brAQmGI0a4Rk8R(MR2Ir@PrES$>wgAS72$I_VoKDE%mo5 zs5a7hPGJzYlzE|s+7GqN*slI7t`)20N?sB-x-#OHiFui_g=`};eDobI6Q1`d;GUYV z@T`$L4O90?qVdrOQw<YCV=ZqrwVk5tz$t#~w?=xRQxA@P@7jfP-)kvkMjC=sxuK}* zXPTyR!MEn3AAM@*jXN_fuYAP!nl9x=<_7+YFa69rKbpJtKWS@vCpXk^E7$d2=Fn8s z@=x5#yptRI+=Zx#uAzVBgPvo5=dBS6SDI#KZgqU8>odP|Yv#hGu7;MLmZnU$GQlra zo`m!C&qmsex+bQn12|u^;)-StQobElEOAI-FQ(5gSNEq(MegPkP~~g)*tt{nELSUB zpJ(~>v@m8rIkKB3g&ief*t0&uGiZ+F75<mMsZMFbp4eXf?@)==g}vnS=I^t$(+ri` zw@`Dmu=Zb}=BQPr=DJjL{j=iM5TWNtUe<4MsagshD%5=2{U~Z7+I@df^`wO|`JQZd zP0ww+C_kQkwl-!*l3Of`;Drfjp0bpN98-OLQ@8r2I%d9iYpUnYk<%m{`I(k`!L?JR zzj5VWFc36M6$%4geG?bPW@(W}M}F$1j!a>0lJs{or#guwd9SbGy?2RWy~4=QgSle| zzSnSV?9i{}1{(S%{<Bfi0|paK1D8JN`mcVkWA1~W8yGorWTG@Q6&<+8sVjwvdly<B ze4%h5x^`=lw1kT**i7gijpR(dw7FX1rcDiVZS7RrJ~FV2O{<lJLopd_%~$Pe&9x4h z^>)N;Yh^s%^{}&9*H=sRE4_NsMR>9f+@GthA2-`s?c1*k&nk1bW5oK=8k->fQ{KLt zE)v!Hu~^HqRF|)H7(&>yw>(8#hbQvPeLGRLCH+&h|8|}n_wJ@y<r$S5v_g1BzPVjy zZ|~{lmP#*Mdu+<J31DH(+OteLvADJkEQ4~EI!~u)$&8E)%#=p@a+&wOF>-J2%&%R_ z^qmSGe68@Q|K7FV`{2r@iReLeVX9%KVQQ)`7ahu-I@d5W6a+1~QqRnlmV3trq662C z-6evBTfsOz+I<hk?p+9a3g7r=&JEn@`oV*lXd>4!)$-A?saM|n!9c0yjc@QL3c<fu zX!+61v3nE8n%+BfsOLvlGPxH<Zj6;?UdjxO<vM90PtlZNYAk3arQ-n7tCn1Cv9iO@ zRu-xAWYv|)O;nGD*eq$+v9)cFomjz`?pgv=xtUjcqtxeg;hDX_Rdd*~y#P=Z{8fdj zS7~8W#uwIx|Ah)<U|XkG-9NYMq!9K*_fW~y+UOx&_dVN=+BP!7&EMObuZEp{Ej*#{ zv%yn8O#tl|280?MCDf$;RJ*msolRpi^p+d0)a&5GcA>jI=WAIA&r^v!CG}S<bBZEC z^*s-I$w=VF$jpuR`g(eP=AZc7fsSJ>r_Oxlml}TW??i=OGR~ZPA>h!pLTRd@sq4sR zUYk2IckkZB+_6HY<w2ovfwyK(U3hStOaqRLm6|%bj&vM&=g<#6N$A?CbN{2yyf$@f zqHwD~D_IY8mF{%BcZCBzpNYQkOHF@f>{g*^pm65MN9Trav>duoc;(WCLNwKurw)~v zdXZyda|?y0PRhrpsfeYimGZ(zEPa{;;OjF-<|)ftcT$|M+?VqmIsNv~g>6yQ?mCRM zy+&23%yXLoLQU4UU9(lMJX<S0+q3qcZc$hZeDxVxe3HIi-&sNh=MtU;PKGTCAXFy9 zVJqR?4AtAAayRhA+P@*}foMbRJhG<%+fWlg>!8`N=86(`8h+Mi+tp>wkB%q1oswY( zOMK)E!xm>>nwu(<D{)A!#EagGiF^Iz2y*AnjVo9B?u?!J6Q3LUsOJwp$`#(}`PMtv z9xzF+!I_b!L(#RlBONm*x?a0T$DHbD8N1dnb12B&XgD#|)YI2??}wC$9?0aHI?kLM z8ylFob)==^L|4OqRl3ym=Q?g(8M+f4m}=<j2?n}auAQ3uM{{G}`oc?@BbPokH`f(K z56Pe9z3*Kb%lzoxmr4@>rzwM*nkDb)nHgz7kmMU3HPePunyjo=mvZMtW<HUper9X) z-Zu01ux36{JxOi1tukyWOrOro>I_FhO~Dx}!(6C=tTq#{DmZIj@xA^T+1~SMJgw5H z9t~1w{g-WB?yxbPqt4@hA7~nCvbMM<RgQc7`|4g@XV+85*T$MzRGY@))!A9yEQG8g zsBQ~gVhvvwtt<VPv&^b>P>(LXaSO_L?BQ5Y8F|8}mP)XN#G<EXqGN90PMO-pnvVTb zuZ<0T^rN}p)X!b}!5epeCo?zIOUTxu!}Uf-=EB5G;aKL>k*HywD3g&w;Z{%SWui7N zG!6AMeVc|;Hl29qr<%TXt}7SZYwF5<=Cy_^-}&d--YI>e?@Kcu%#FO$^ryad<l0M9 zV^f)rF3@-3ubuhauQX*|`d(Y#m2WlNX&NLA<fjJCedTvPcj!Ts>zZk3rqM_=Q%CT! z_j&1IxY%ZzTBb>E@@N;a2y{mZ<CU%d<2rbp?(t|08#nIgZok!|)OnYvj88~GcBzs& zLhfmuU$t`Y>E^V3b=~u_&@FOfH%``<boJ4FA3hfLROw+SZpT#RSCz3<X>q89U~N4B z+lhU6l3EW>ZVcO=C)H9Fdoa*VgR1rGP*;Liwe&suw}mwU>w5cBT<h0Z&!)>&*}EJP z)La=;EoP0)Oby(n8518}nVIQpd*e%A3WmP*w?3Hp)GxgBqnRs1_j<{>Wtu+qUPET8 z>nOFboqDaMX>2AlF*oo*Q*dCQR2rxA$y}SbH!%}*6%L#@Qkr<>*yq|lb?n%uK9T!Q z)5Od>(cgRPIH7Jw-Ww|vMlPLcC=8UEK00u(r*vdssC4GgSaj-Zue|ZOwxK&s7d{FO zv{Cx#gN{REQw>EjYFH2xGY7^7f?Rv$KyB5#+gJ_hv})&z3I{qS=|1JF@E4OKbX=R? z8&vf$1y5V)*2_OaB_y+twAdvYRb+6XwhZ34+CxRx`)>UgHVB^07!n^#-R*3~ezyPs zAOJ~3K~(S*D|p5#zV#_$p<ZJXD)7GTG<Dxa4wVQ(sA*T*eGFpF{`J%@6t;S8cX7jN ztg&udM^(xncNBp8AEezfz1*T`HcR=&s-69jR0*rRE>D)C%><@rX|BY?$jHppMAUb! zFZbGo7cRZg(bUm)qT!8S`pisErXzExp>OCwrZje>tLI4DP$AcL;2`-gx3tXNr$T)Z zT0Us%o1}tppcLI3=_-vK=o;!e_K8={bwo{Pex>hDfstF8d&kbbb0R9-=?LcT%{`d7 z*EZGirSFvPor;FqJ{W5nXlWYym4==MGGBV(!PLyHlO&!VxRGngbsQ*k&64#!Lj%J! z#8UEp&U6y9y)Z1zEJi3zS7NUz4vdqIZ-L>plTUPKqYWUgZEE`kAmK?%IF*5Cd#Gcm zd|j;2G3+NU!`A=mfuL+B8QNYOS&gB{dN|Dgx8GEWEXs;t;@KgnUi)1~PVe51U31f? zcABs^1zx`>aZlIZuA$HFH3%EsAe%Ue$^f|w1y}WQ#7cC-j>u&f$lWdZ%k=n%O5`$2 zTC#j~7FiNDhgJ7Rz9zmX%nXdo%#0MK?p?Wc?uEYhK4PSABA6<@_Cm`KesJfV-|Cr( z4h>9G%YH66)OVf+{4^b!dg;MHOx?d%dL~AuZp}>%6ee!W6ov{zO-GJoy1LqO#||C1 z*Klj-UhY!QjW=5E{oFWpl8hbc2u{+K8k?Bw8=7cnVeCH5VQYwPOw66Sb%&;xN*y=m z-gxVq#BCpXY3{%~Jx6Z5(-xg1h2D~#?r5aBA|uf(kx*^9sa$TFwykE$$wMlUXRBcW zWpZ4OX?t2Wt}Ri)Z5SxqP+M$Exb_#FRqM1m+&w2X@r-Cvm&$}4GGlxFWm}|29kCLS z|J85y8@X(Q-uq9mx~neiQT>Hl-@34K{CZhQu<dWVy^F*rn4VCJ<%Nw$sM>L+isa8K zk+@wRd|$f1y@$p<V6Y8xqg;EicQKgx+R*E41u86wp(VPvC}+neM*0dtsprO>YgdL2 zoVoJRKs48Pu3>B_YCH8`yfgRUL1|`eW^N*LmPEQ89eu~zPTZRrD-%`mtvL!4L#2*8 zbJ197YHDtju&*;;c;nm)eBn}C)I+Xi;1(Qd>X|xpYap2WQ#~{1PE+=J?!-i{RCq8o z@*s1W#MlpJf|1OFv1sVXgWS2ciJoI^zx2Yr_g?!{^qtbwP|w_<uav2X?Ibg*C4A7( z&`Rjup{86X@yiQ`zhc#k(O8>Vyd<rb^wn(j&(2Qh3M*A$wlnT1Jh7_Q<s|ir-Fh#) z`n<0G^_~QS>UiI(s;fF(T48*D^_x(Wlf@cdv?}+CJL&M#hU-;EGu4e;fxX#YsQrtd zs{wq<4kzs8gX=AG)^WFL)Fsr4MyvR<dO<+k61{A8sw`peM=5}{i5riLCEJ()F*7wc z5sj3Z3U@xZac>~=Lf4hAOr7|-j+VI(9$a{>>9@W&_m#<NY-1z2k&GSV&XGe0dRk`Y zJ~}WL4P9uvG7}9A^$fI)lfN*ThPJk2pF4G=?Z}&?jcfbN@7*SA_>PIFD;Et+jQ!GE z*T~#B(>4+mI%dAa8<!sRwFS4D?vg21={v#46cQ&6Qvf;k!jX<sEvGJhC6oD?xgT6& zAPVjk>3DrKF+iS%oKt9OC;YUbo6H{Orb#%y2z3XhYdGyRiI*3njtWh~RalEPICqwE z!mzb5hYBENTP7U#aH#eM!KWJlwgcI2`MMhis!+VF2KuT7qR9X9H=#22?wkD-o(vLo zk-e(bBCpK@sl74FWVwre%kSZ_Y9`BTA+J#{1PIKEVQ!G=l;tzE5SsHB!cjo4;A zZ_U@FEurF!);EM~L;GJQKz(5xTBNSoz8~Bf2oAh(X6&7bhA+KT8ky?))E5qX^zZ!O z#+`PWH~S*J??UNbSJ$F<HKcNn!q9`DrDtHEr>9|TnxOw#Vn3U{^o2v8xOA!IUZGUz zy7$4Uu9k*xU1(~HN*88cIx=wQK{WKz2PZDQ@!&Hbwe|6!<E^24_nM}L8b<mKe4^=v zUmI(g8JlaF`GZ$7eTRYvzjEcb3jD&I!uL*e4LmsY2V)1WoyY`DLq*EcS_Y|1ohDGB zW4s!jo+b8o*?}`(q3+F%`6oe7Cl$Pzx_<C%O(IooM7v%z?v}aLL22KriM1gpRIc$* zD|-o*o$jHs2oJTPdHv@p@YV5{)v$m4JdJwEunG-cu<JDv_U4xRSfqWbqfoJL#4ShW zY)?K~k6hGSGp>iCu+d0Vr)~_DKd(YKsyOA%ImnCbZGGB8)lU0Z-YyL?AFYoTIMB?{ z+{B$L_r@}xICbX-x9*(!r3+mxM_xM6H}Z`S?vt8ss_nr<ks$h>Be%({^jhvtFi<Fr z4U@B9*9b$Sl-Z1x4s@M(<5O?Fa-yZ{!Q82liRit|u~S#xxzur|?}e^o4F?W=r7%dH zyh9fbz1MZF=|Se$p^igC4Zn1)G*pNVG#of~=rfs~14qt$^o=s{!~?w~mLB=c?|d+K z;8VAl>6ns4HH9k=jx-e-a($&{qDV@UWcR>4mAX+wTWO}HyJlrtrn{4AE9L3C&gve9 zic&YO^7-|RFx$d>tBJ=@(^jxgW4ApkC+wv&>P7tZq+qDXRKwoVc6;@11iq>&dB5d) zRq)v-TB&A`>r2nDWmmet`(e8wOl?72Et|H7Ijx@0yE`nr&O0ot<*%xXjeYyrJ{iJ# zP8U~`F}63Jd?lAyzgA`H;w_UA=GcP=eYYNr-5NXcR>N<7r}W0Je32Gq(+A)C-n9>g z?)CMJ<uVf|8ipPe+Hw;QdV<^}^-1&%^yEegw}yg2a(>~!nPb27Q$Ka$3nv<`3<V9x zCNh~jZKa6^O&7@>=7j?v<jxi5j`g&i>3PsG(bx5z*B+8e@kY}tEvN3hckWiH>&OeA zd6h!S#D%V@hL@&3xE6GM^m`r0NjBYdqi5iifsv+b*WS7^cIMjG=B8daNG)}xxtW3a zs%IoOGEUpg!q}ip=xZwl$pz}SkUKXOW~uRQ31q~Lq3@ejz_t}`*a&&m;UiR(nW0jY z^4v)|)oD?+cqLTUky!=66e_UnP}?E45%|_mp>AGlsOfLmF0+E_IUf701FH9eur*q5 zKmYSIMNoz6#hUiK>ahQuQn!k+-Mtmz39kDoALj6c_&coa^I^?GHrwz<%htXx(va`D zc?u<cV`FnnW!`w{!Gi;rj$Jy|b0y0B!4Ga-C2a0SS3~Y04GijQ%9C8VVJ;e(nEU8K zOYT9>T&`!V<=)((*DggrapHyG*ht?G3VlNl`sQ+lffxF?@WOjz4VNzbXr$pVm6X9N z*KP#Sy-&>&!uOT716{X1^<Gok(20*GdghLNZe;FAX{cf9I>~^}oIBI@J9+9h4vyvC zJMhX2LE*LgL{)SJ9V73ZIhGrH@0HRl3A7i=#*s-XYMUCS#u};TBgnMU;K(Bl)0M&4 zBEOrhhC<~v`H&qYTUATwIwKm^K&U!1wNN_)dKcb`HCbAHVXMY=WV2l@^K`>ns3Ale zyZKUeZsQrzyIkW6w>itIF^+Zq&D-4sRbeWtRk1w>jf*wGB5oLXM)l_Q)p%olW?NOz ztGp&pef~a=v#`_3mTh#4JWgO;ZYo83h~_K%qp1O+o{`dAq3JN)*>92%<jBOp-<W87 zqi}6x;)99O)ZElm!=+hjc`H+n7mQ6!jBtd3VY2z`CDBHq;WKYFbX}&=J?{*)<${R| zlhnykYM7gP<(-*xmuCLjrB8gO=fTXEMh-<YFI<Z{f?LOK^}O(-zL|#H_oDYMO;Y%s z$(3>^K2tjPADm+9#DmPCue@~Z-j7<|n7LB;wbvRx_wW6wOz@q*(e@Mf?!9p2&LM(Z zU(3DG)R$|Rn(3OG8kr{McY*CqMKd!qr)iY=$UJHN%9Tacqj71m23{<UVjsgmwpIaz z4a#kOaD3Q<jfFiiVRwXU+#@rqTQBu9x5I|6LZu>N+hTY-t=aqw*Gk=3tCHT#T<VOg zcTY9e&b!_1udOPtqiT!x>d$<Nt_T%2^szF7sv5jk7i6A?Io4%_Sp@-Kg^TX$@#;MJ zHan&t8=81*X9{bk1m&9UZ}Ho)iA>KZwa0g~9hjSV=O#&Jzww{l=?T7Y;A?$XzVH(- z&HSikZf@>Q?wwM{xd&Y%H?GoH$HGuUFjANa=7IxXICtvO*xXd^%3Q--U&q+M)XX%w zEoa6iu4U#5eGgu`FcsbT(oo@hrI}+BrJFQfJUG&G=fcFqd2+&Q`d-Ja(!lo)T*?em zG2M6NrObmPnW0Y%z4ltm*ZRrgAUbv8+5q2p@Y;cQ8gd8vn%=u~p76erxBgLZ;3joF z&y?nZ<77}jH8e3#SYJ~snHdxbGmJ^(vh?0rdj4(aH<^5EWca4}?m8P9whVi#ir+d} zTc}jEhFa^8>QXvZc3s%0TCyz)ChU~$+eA#AIA*QXtzWS5$<lPc3+gtSwygo*24u4e z{a)B3W2r7stHV%yB9w-`lIh(xvU*Y_R`>_*`b=0OR-V?=$0sG9j|NiKtmMOn*WTu} zD3k9;JDFcjl}38zCW4lG_hw3cL(#c`?|n=N-WT5bYqz3?3w?#5PU@TG*ih6=JrF%K z6I+`b%S01X6LSY%_}r=2nr1$jD$TV;-x_NBXr!xUq@kl>t|Qab5eyBTn0qib_ld%l z56--D;&zqwwA`eOuj|YQ6AeG;I`OHmog16!VQl0}FPta@ZD%fw3>+BiYkM$s?xR=k zWv*SD_~=0C+{}Y7b=?^GLd&gzD{l>5>1#O9cc<f{JHe$hGk2P)Yva(=RH4*ND$0eP zkG66(gCnkaHP2V_qb#w&%crWb7BJ#QlCh*f*4JT{3>31pm6vUCyxC4T$toGwuvcna zNB`}Hv9MKo?;>_tMUh;;CboaOMQ&`%AM0YU4O^q9lx=D<zg;($-7GBZRsU5Zal1#W zn&woe^^04E`LR|#7`9Aub~&-C{8lY+8+N$u=gl<T27_ao)DRMaC(Xf~uN3<WRad#P zI$sD_7IQN0jSW+yTA^?1&|F(^C^Pi6k=)o%T{`uDYiK+4iI$<BQqSDTt$Ra3+uYpH z#6v12r-rG>o5+y+nPZ=eGCfi0P)ASKgQ*8E6GFH!XdjC1O?7od!I2B^jd5Y>I|B_D zO4rVu8tZ8~RVaPu#BnO!8&2hVj<pQD({iiypzwp29vtXs`N65oov8=shK_Yiyma8s zM=wp?>&fKF8Sqx2aOA+;oj>>Iez52UK1>rwx&|&BnrpcB&X;n9j%jl7TXgGE8p(|` zbrK&Q63*Au)6`9rN4X-avf56IY-0j;wz~hD;U(MR`F9btuoa9#EyKF`OvXLUrJ>f; zIMkR?{n0~(YNA5w)y*o4&#?Cdp7vO&>qmXssA>QBAQez+T+6aT1@;%!jWmv16X&bV z<e$>}Rdd7DkHW4nyFMx3r;@MJLoM@yY;B(pY3VIGc;_qj?{SFG+|0zlZR(s2=I)HN z(>TVj4E??PRBrw=zYu*Z6OA2u<6D`T5sn2dg|<8IOijE%?#6@Yz)+#3<r7o<nG+e> z3b*Eh8zTqWGEF1BBrjPAeMedjla*CNG*$S_z3)uD)xxcghJgbO=dMhQ41KEYqo}Xx zQ}=GtAk410p`)ZZyzwWRUU=_?=)pvEV(L4``rhf77-;Hv@6fT1()USc7&I|<nOp!1 zr~X&`t@q9xcw^{CrI|a&N<S(zooHw!k@(OoX%D9cqLHx!t)yTam})0?mrNtcmDc4* z+Z9B%#)9s$x$8w_wiW)?pV6$Me||e--2VACosq2t+4^m}s&uVeSNr7rPc=HM1-|{} z(p9EPb;`f^<Vc;m!1uHwZ&Q#GY6DcABC)Czjx`3{aVIkiJ8X3oVi+nmid`-$t7P4( z8QZY8Q#saPnY&r$`qjx&|LDTPGOT8~_FO9+$`u}58SA?j-TGkW-q6gQ7yf5mKls*I z!#Lr2?`3Ar%+gGYqjZ$0XDHwzA&7Ln_7kVNP8<r!YILkjb7w~~rMAANAx@PBqM?~) z3dwT^&YbBRIMb22Gxyr95zZW#I8(|^jRa%S=W>1T<O(fgeP<d@eWzz^sB0kC6b+On zIzAC~{b-)NYQ_RaPPJV)aqUVWYB_YTsdQ)Lg{HCh2L3y3*IEwD6doLyyK&^e6)yZI zQ?DH;<Vl~$#8foW)-YCR$+cGZXlWg>)cj3X8(Oxqrdk3WVZ(np+u}oQz#orKOkR=2 zWm~mP*iyb`J8ILq{H`j1W;?+xtIYI51=d%`>+b)`|ME9suMw3>wx*i4*z8iMw%d$* zOa-gh(<*=A-F&Ye<Ev+zcI6>)Z)1%e==5=C%4*IjZ0wp<)chaUwEeV{C2ZxekBCTP zHE<{-GGS?Ixo}t>yLO{=EC|Lv2wILc1P?|Y3>~}l%0Kns8zWN>8V(H2U2DlT^cAVU z;Tu=xsjq^GhAAdyI?lXuZmf~~Px3THqDVFfBYl~^Hbx0+oXMRz@WGKzTHqa-gVaHL z;lxLUBbR;@y)<)}&Uxlk(~T@Ks3-1yX6{FA(T&{9jh}gGr0d9onKQR;gw(^^HBsmq zqUjqqIyzoB(RS!TX5h-ijWYv7=SEtN6@KMMce;kgj%02e>-%2UnVx$?Q!gDF8=4t- zP$Z}4C{sAlTv_BdlgD2}R5}ukG*idMW@}=e#!i;2Ah&ScX>5eBtkT^)+mOF(Zq&AD zk!p{Ws$w|X>Uw_+aanDvTCAOv6KblcYGK!I9h(2uZ?;8$`&?41vYn`^yRSL~g&L*z zz9M>m<{0-D!_@(9R}vETqJH6tQZwvO>h+_MJYl?}#@?!)pWb!MF4y+p<ty_wOY)dZ z2nrJ+Il%SYxf2{}n7Q)7rQDUVo;$ZnZ9n%HzHsT<twHJqZi>lac&0E@ir#5xYRiq? zn#oh^+0aDk!ilDqL9#u#P15B%Eh7WbskXu_6_dHCW5;HxGc}l-3MPhWTz6YTVdTuE zu|GJl?14RzDSYl))0vJxcx@v1Lg{-=Kln^E)iW@3>_o0_BpS+HD;(%5O%o4&V`A#S z#Eqf3=sfu?zc({+;9ApI)1ln=h7JU|zjfllRBo!5oM9$jDveVPJJmJQPG+Rh+@Yqi zLT5F~ePP(uPEXm)Jjs!|E1Ak-B8QcapHwI>oOT+kK(<SBZ1=oo8x)61R(JEgbyDc6 zabQ(+#d_@%*J#eHCX@VQQih6*@2`Gi4J+8kl&X*<t1Thb=3!y4d6ZgBTwF1f)nVv4 zpS1b6$0q{cF1->en|!J?<ki7zUyounYPX-|-Tm}+{npaPKHsn<S}Og^6#jDb-RX<E zhVI;$E8X~mkNTz_%rzYQUrc=Kx9$y$6poD*NsCqphJvZ7Bc)l=$Q{b{ll?$2^@T4S zC6;(0U!Iz2$~80`iG~{HhABrKpqDb8p(7_|rpP_$V5Dd2wL;54!z*(ioXA{jD_y!X zHFT4#JU`WS?Zi;uCw|m*VeEr9G6O%F8z{~7R-L+s9$d>6X5J)~BV$FfnK}?$d6x{g z<}z(#XAb2a9O=mPJQzDN*LS9LC*ay#)7V`yIw(b#1~R8f?$gmuLn#*%?HVa?jaJ5` z*_HvtGCy0|$*vP#ja8^yGQnX(>9;hl$o5K8Y}f?v;%+MD_v}8|Sxq3VOB(*z)-*e? zTy3cvwz$owW>wmfLsI9iva9#0XGuMQ@Wr+H9xBAp&ux@>4sEgC#}fAhtGcrIxxJ25 zw`gZi1igKwZnm;Ew+e;}d!k2?i!`}xVqhef8~7kNRQLz)oN2f-QMhvI*KS?wxiQmF zreWFlCi<pk37LwbnZkHAn`4kN&S>Vf*V>v&Jr8nI!Q4dC*u*foxI~vSQ$!sn8g3jZ z(kEp`&P1iLd$%TL?nH$sI?*)sUQ06-$W1e!>3WcvdT`)QuIoXeuVv;=+Xyclnz?qM zr>SXXn7SxRV+}(MGZP(|k!UPa8v2E<KbR>LaxF~Fb$oE>-hoTkCN7-lnL0}M<52EO z+aRg+dVb=;&<j&DFEx@`R@Bj24Ihu%g0Z>6C~aSDVMTUi9>)+hw9>Zs=)kubda|{# z0+0D#R>RkZ3b9M|^qa61Qp1*@YF}DVh1=C}#{XZwlDqg{{w7oo^x5?7Dl1Jj?S#m< zBhSrh^S#|nGVaa!Vy!vOegeC`^{h^+Q?2@bDp>CBc-z-B?)e*cCCX3v2iE_*TuGM~ z|1MV}GZ)>t%{82liJ?31ebh5>BKNi5x)M#a749^=^3Q#A?Z&;#2d}g}n46e+P?(#$ zH#IafA-RuCjgqbV%-p$8z0plZS7V)2P&TxT9Vtw;bo7#_W$4Iha%`TOI*{uN^3+6@ znXN`n-s`zHa)`MznQKArz=?s0t~bsUf*<|V#5cxnWrnG&o@&cHxc5=hM8~z@y{;Dy zmF^V!Nybx(MmmCVvN9-r?j7k&lR(y?4;o5yuRXXm@y4}dLk~Jy28NFG4P<7<+K#+3 zaO+sxSkr~RBW-gneGiG<jRwg`c4DTjVPR9c3}fZm@PKl|ykJRNWGiudP&J;bihS6_ zs<TR&$-aMQdrU;Ln%CFWK%vHQbN@<-yPX?hZOF{NK7&V2a1%7FrYf=uM6XWYS7jf# zc_HFfKgIrO8?l0vskQ^~1YuAmyR6e3Rr^P%#4|$Wu%Y$8^r^<XuqO|X8~f^Vo^F}? zK^Mi*V)S)k=+^JuDs;3=eeHW!j-BWkYPs-dey;CYVJ<i{ccf{gZ>lg7L{kHO6JsL- zSsD>EQJR=*CNGpiQ#AL|v2oJ!9hkTh9GDrT0(YV#H<Ah3X#1EJeJ7D1LDN{SAy3Af zJu|0H%mjrC*G3MMx@r2wnHRXxcWmn1t&zUcgBK|qycJ|V>bjR{IaKIr;YOxurXw>< zYCvY@3Wb)YztHptBMqfY;mE{;2T{=Ug_nBXdGA0bIM6Ug%T!-*j2yS#ICZ1tSkO23 zx5<(|8l|z4S+eR{C?dOBX>ie_S!qm`3E65)XuiYPF1xnFnyxSGbZ^z$Xk<IA?^E_0 z;TfO0f2zm+v5|jFqw1C|)%I6b*~GPrjD<=WZJ@S!xT=WGDu|Qk2zTQS-xGHl@H`<} zrf$?qcv70Z-v!oQnUK8#?XZVZ+86xFw2Z>aL_V&<VYaeAT1aMlhQ8L<bmGR$H@-E~ zH*+d;=#5`Hbf@RN8<$QK6FXDrE0Z<*$V6^nnx>+S%##__u~SEmTsSq-OQEf2sP9B{ zqDWHesG%@T|2|13poO`ap<~gd(m+$`O2?6gpsT5?lxd_;d7|%~xrV+8K5C?KmcdWm zdgo7lE10=YSlLi!VrrPA!csRy6BBdOq_kVim}{jOxG@nlgVgo%Q*C$p7PHZ2hVHaQ z$8L1>{lt5@TTw^TkGi_a4q>9F;{t`b*N#m!O?3q4-YLbz9A_pTlDji)9j!E=azR*i zG_f>&UC3kdwK3vZB1x9dN50WGP%UW={Lul;lhj64t3+M5)P5~*)mn`^B<XgGXZ}~e zi4`3On|>a#Ca%`YQsQ1WlKL<ZYtz*Iq|2^rOsMSCsAH+CX0WL&f~#i$?+<2mWxQvE zvbduz+~2nL*ms+)7~YV^)MUw4zDT3IXT~19L)Uxv9!!07YvMcw#y|0efm<Jq-5ICh zJnxOQ^%KH3bEBW~zQx3_xnSzdffFyi^xA>GQtl{8TITK?YH4e@@L+DPsbi+;m9B=N zmWMQ+vFX^<z$kTk95^<WYnzxkOd7j(((C1p4ZQP8!^pk4zEWRitZ?Sm_ZsdDO%*1_ z|1W3vu_j5Hor(E1;aY^u$jlPmT`iyp%riqVv*CvKKw#!_>f*TKf*f)%4;tM-i&7~= zx)x@}g`e<@2siggur`%h72(qC{O6ocS(-~EuH@0N%`LTz%%aTCLNnZwS?ggXQEDs9 zOuUzB`5Pa-wX(7_4-N28$4Xb~*jE-B?i?%JZH?zUF1^s!)5BY1FXiS=tYk7ne>T># z##~}yv>pCiTc_<!WYI?Tkv-7rW)Bj0g~C@XK!lUCXSR5sw~5<FNl9wCd3Ap6`_p9h zn^5hxM$ZLuRi98@wQgIx231w9<@}+2RlT)lJ%Ocqv^Pu+YCN9vP)=$RcprAF4<otl zWz!wn;C*_ueJbMTQRnCExzxmoR|kQoYRk+^qR+ek`@^Mj6`5%xa|<Isx%Nh)Z|>H@ zoy==r_&YuS60RUqO@)EVT4w1wVsX~yy4FTCo%=u2{~N|Xr4Qwhq6m#5<S^tILQ%-M zVGeW1naz;%*+&{FXKl<ev}u_0FsBMRpUp6HKA+Er2w%T^|A+VE^}g=weqPr-fa|#< zjHxnos*MBc684MgKW~b~-u49=n<=Kif}Gw?Yw@`%(3zvKdI$Upj-dQhSZ9xq4C5Wg zxFWo5O;;03q4x$AEf;tqj5Buqjbd}Fj@VZBDtm!pKVieZv1d!PtGl`O-yPllWC-l_ zT)v=<+XW)>$nlee?e|I=rt_-+Llr+;d)ES?k=@%SaZH&aMHXp+E*mmAFSB4LghVbi z6I9wmq6h|6{vcN{?ah7x9b-eAy0P&W)p7XOk0}Ke0Nrefhe=9er~^gCzVoB~|Mq*X z*m{B>e{VuQ8OfAYGt*CP;>`iwCEua3=Z9nYTd0Z&TO_xu^)U9TQQw1%D4jQc44Q<t zE7-<5YD&r*KemlLh|0g82ODrL$`Jvn&v+|b-6D7;CZSx4hQcwrF_wQc<XRUU&22am zxK5fbW-)|!rbZ1z9*#oBsSvY37|zp)^Zw5k$@L7faCDUy5Y7W!cma30#^;uU;rW8r zb<x|eD)~PPu(JF?tHx!eMT_I!)>SD0AbLDuNp7G2#WgE4XCg}e!2IEpFSR~*ESK3g zyO9)7G>pMS%MZ))>Pv<2*DT$Sfo#!^v7;{77lLNX&gLDlUjkb}Ohjy4nFUYS8avx$ z%k5!Crp|68M3+eDp0X*r`wrbu<3zDKX<z`>Ge369#fu~#51MP=rJ$KxZ?`w@5PR+X zdyG2iey`^&bts982PKpqere2b3*hXCJM;?^OeM8gZAgh}LMQsOKamc{sbOM>g+F-z zL*^-k2Z$2xesJeT=yHM$)?80Nq|ZbN-Nti*FsA$9;j5Rs`p@*vGqr86L#IB$Zlzbv zYvY@woZy6g4!hGOQZrW%oB;5WligpDt#Xt)EWR4(zs94BZx|{g55C+KnW~X6^k=1g z4buez5Q^G37_y87h;V6NXW&(lWte-9EkhfBZPYefuXel3=J^QAhGJ=BtEC0Agz%#{ zDUygdETWOn-QQ{Mu+S@EKy5<%-m(y&s84F$haG?QUL9;A#=UGdcTdkqR7%+52earh zAbj3dN2~W6a?%EJ#?70bc=EB+Zrao4Jay^1-xWNU$F=q|U4#C|pv=gtDGV`tB~8fE ztZV;HngS;ZBqynhlcNoEQ~-kj@aQbq*8ctUe|;N`%+%FAS)Qx&)i!vK{|PWUDWI#L zn7^R~WAJ;%t*b&;V=!L5k(ayvY3TlZ1w}F4IQz}6FOT49lzU+V3SCDZ1$CeHJ$qgo z)}nhTjp;Mf2dHKa0p>E)e*DP@S>7Wybi@Hx8_U9ut`=(KZ>97N%L)V@83t;V2Ly85 z84uk`_T3vI6H2S3We;1og0DXu>wL*Cx1G|<NFeBbF%NWG;H1}0X=Ran`22L4Eb5BE z4qj&}Frv_MO0x1Xuxe}LpL>_c$lO^}H#OBfsEpw+Xy>Sle!QvW=9U9@aj{EGQ+${2 zaPP${I|kfK4Qp&EIfnPCkDIH*#^@-W39T?3SQ3&KYou?(J)-NvA>V`}VENd^ES3xF z4La(Ra&+%a?STpk9c=GnfCmj11JN=z_|n^dnWSSDP{sEVQ+xWTG}+s!L_WE)t2GjP z4bgLNwI-oZE)7*Wpq-K$(BV;N<qeEZIlwWQ?#a#yO?FlkjoZnC+`8a@!k*pCFH+7M z(#r0r*UjoN)MmyCZ3+i6H-~1~94PP11^s>A5#ebv{1OtT<vw-rdy3g%z54m+zl=%d zJEq2VnR^)$>jD4d>XCk^Fue`CmJN8hVq;d66#|HFuwP#=-K3E%pwl@!Jumsh{k6QR za{Cugh__&3y70ncWNj$U$zfwK-Yv$`EVpE<)52w=^J!JEB?f22*1jZw&$F3+X(CcO zfU}cwNN{l~?vVkwm|pu$T+-C;iY7!4QbbFaVM<IG1h&Oo?mIvVgP|H()A1o25yg$n z;K)E_^T(E{0VRk;3;osBeirY8j8kHq#gx$srV3S(`E<l52UqB{(H&ideP%Go29ERA z)h%M2chsw__ga#3`-^8#*&8smwaH0xuJy_V4*#N3Pdz$1jFfAW6e>RHu3&P|2rL#W zBqo}HCi*UR4-SI#O(1IAjXmikLX%~J`5@41>q+(DyYI)%uk?MNgKOg_jxrMehE`d7 z{$~PXR-7*lbgAARUi#PJVtYf)IY;3+-=7b+Hzx;&xqr^!vb70LM!~&5S8)vcjaHsK zSNG)+?o$S*88hDn=X?)1c6<+80ydSGHXrU&h5UHrLaYyEoer_grnTM+_^@~QQePOy z+)Cu9@dI+F4#wRfcg_^M(c0P0`_1GF%FRI?)wy9+W+YEW$|rHUxV$YYlm<tRE%wJI z>gczbKO7#qA$dT+#-R{)I3$L|dkER^_fx_k_kg{1iIYr4l>i~#o}SFk`fA^pL?lti z7L$kBXz72Bc_`(*4nw!VnnzoDn7-_OE!a2KYNOC2rI3TqO-!0K+nzgMw%WPmtX6A% zXPe?3QS`<wPYy)?_7c6kO3s92OL1}9N-!7>iD)dtVk2>#8r?cFmEFNP;j9R`d#lbL z)@BrBkw?FB_ud{NU$}@>Ma?#dADw-9<0c>K!o)gkHGDoqp7Xd}Splq!+C2<NZ#a{h zQ^U44hL!)gIAk?=19;5p^ZdEbPu8bn5g(QL1B?`D)8a)A8m}kX>#H1EEVe4MBj;Za zHC|K?3Et8SnJGT49coB#$<JFGk~CHNbQbD=x!&k6Xws|v=matQEoe?Fy3-Kmy%r>g zo%uJZB@X8~BOZen^%1o|2p@EJ$J1-`nOrlLFS}mbs_@Mr{nrk{PKmKw8p!9wdn3+V zPPGCO7o&4uUd_Q3o6qHT>H1Wg(-S)ps?bX;jrZt<;i9==7lT|J6Yvq4o&mnpD;?s! zV$lo%!t4+zRHDw5s%@1nVA54FD$WOE#o2Bl62lKp6GIBnBZ+ZF&;sBHJ!1eKFKJ|& z+b`vVTgqf;3%$;>))eDmp5?sU(XegzTnUEe^8-^@-~rxYxUAyNCMq2Rx;ycuc+nqr z1r`9((Sm>NXMWx7S-k>+@o}i_LsyUUR^Mg92Gd&cq+0!lZ(vxcsZUvUf6_A%JKe+7 z;C1l?W4yxpi?9AZ|6M)a4)4+sFE8iHPJWjB>dgjmrx5V-A&z5D8NQX|q=#A}-RsCo zBysnGYWFS%HENS;J<tAnSmrC^xc^hCGs>t1II+B#es5I&M9kdXu7*{c=j=^$%Mpu| zu4wmt5q#z!B9W)zV1TgY<DgIrGOT73*wnFrXm_ioK(6`t+Fe!gVEGL<6qB_Qfg8tE zrB7!tWsP~ywzOCn>O)=c(d_A%Xz(n7?DqWsV!_)NdIt^y3ZUvI|1E_&7Qr2-vqKi( zsW$kD0(&;jr^f9)GIl@L12BM(YgALmO9~UwU52(D$Hj?GESp0+J=pg-%6Q){s810^ zN`So8^ISajvD4n+o4%C*h}l!}ksyMv>(&mF^j#K=T(}Q=F#x8p+i=99B+{1C1ei1B z^j_EvzZnF8qV$zR{?KGE>O|meOgX}8*(I@%06Uh8;zMY`YJPma%A5EFGC{iIilk}z zQdWT8a%p@-#@SglbJ&xR2Uglpo~jeM`8sY@v%-khf1>E7^#2tI`AQyLdT@4sfPd_b z0MC250!oi+Io*-2as>F#{=m>Z6D8t&fna&n>TL}1)9?CwzM#UI3@==FXFHOZ&3iGU zHzitS_ji!pjcQi=uDB$@|8feZk_JbGuQ?r|tJk)9#|%8;`6+rbm_QW0APW(xrLDGL zWCMRt?fE&nu?nTCJ#I0swLr@xg}Y&sdN2sWkOm~L%a6B~ekJ+NwRCnH+2HV=3n1J~ zrjFxXbTux{Mzco&#ELIHu?Uz^x5@2K(a=|rmPo8&I2Q1ljVMDjg1ApRs4s<l*m=ta zgYp&VS>>$$_?atz2o2y*fkFN?=J%RqP$3%E3`^WBSqn!YtB>!pDFkv#@=ditA0T1> zLPHiuE%-;0Usv)aKSI)(NW{8B0U9%o>%iQQ(f-tQ2&ZxRNPdUarwcnBzYDgY>I-e$ z1ZpJbm(TLA8LWW(bDFO6^YJKd?%^x1O%50<)|0O|5~4;-Ha4PGMRizD31wBUW{F94 zr^>!>rt%YBhuEDzMeG}X3a4q<R4zJx=VzLd4N-pFJj|z`)&|ZSj*$7a=wt%fMB_EO zsG~tsr@t^r#pqYh^hc~u5m*sEgw}|)mc<H3THxGZ3mvTb(}M-KmktQRRb(6=dk(v4 zdfT*&A>!jXqKc%|rMjTm7mbQ;X)i<NAj(wLOdz(47z@J!xWEmSADd+53$^Dr`3m7+ z$Sk-SWScvaiHqd@C=LX|_eCog6zBm69X8oFZCX4rvsEXL%Mw=Pcct>N%A78yrpswu z=&1L6?`&;3v*#1F#razwo{xXKc0naI*>hbPGji^dso$OK3%UDimCGWtimEwfQQ+=! zSHJ}Sv&Upo%lodDhE<Cnb>gJxTS2MIMCmP&uD2p?w<ZqQI(i86X)nTXQh#+1Yr?n0 z5Cwe(I_sO|YxwU;eYt;muy~~@$7id@Ntpu$#fVGQ^0`16W_2`;)y<mjxS56|r?Zf0 zB!|P<D3icu!yDsn|9Ju|1p)>|1PPEqsQ5v7puwsyb4n%KVhuAxtx6hsoZpB0!pU*H z_vqZ&R<t@U{o4xRV!AWauayOnV_he?6I<2v(g8DAQTfr~Ph_G=D||SC<7tw!$U(#l zvSfUsF!CC^xY`t<Gc&7-4}nELY8Q_NbtPgt&6@{7Ik#&ZT^<ccBy*+1)Za|lyA>Ic z<GvKj@IRCvH}#Q~0ccm@D!5o=w4>1&9lAEXvq6~B55F}%VFqh70}VjKO)aX<o1=Lx zlV~wSGJrmik@AHWEs-U@kNqg!jVcYX{QaXujRB<KIV&D)2QbRPns|B{>V`0QsqyXS zJ(v^%1c@77T4@9z@B4CoWYp4R1Dl^4rjmDg%wauDY7E?*zJ8aM#HxDpHRGyatvz#a zDybm%Pz}(LOq)-^$#gh1@|Itmjh{ZOrsNnsQnZ<j(z4yPf2-{)88BSDy{7WMcJV;| z^Gd#Uw_2p}Fdyf>nCOcJInQ0^LNg_<=aunP(D3r-LcnVO(A&K>_1hRx#)Yn$Qn_c1 z!LWs1N;l-ko}hb3i>j0~JJI@>;i7uYz_|!e_q-2rgcY-_lQga^muRCJsT-JpfJ$&c z!{g5C$a}2xQCAivb@3jMm9<Xkzg7%_k^l|$6M<H;FcZ@1zq@c6L!QbWVC6Rj^6d`O zSDYJ<TxqXaOE7=xv9vdqmX`5uYE(|Pn}MMrXC6<u1Gd_qKlED{*bU0hadjhZQ_v)I z4kk`|-GsgT>w@Un7=s#-6d%`6Dl2grr5+5z_{OArp{t_9jYkzL?Hl`d6#22e7XRj# zN$)ffh&C3i@Qd#yTdj3K1g7M+#Bc2M!c3QisT+SV*Z0RrD3oq8`&mv^u!8s2MoAJ4 z=btE0WZxL!jreKhlHT}5a)*_A`z8Ra<nN?UKQ$G0>JgSgz7x0lkDmF)*bhw5Mn+N^ z{FjY7b%^;vh-y0Zs5M&-TfBB~$Uh|z#1HFx?c^)vRj8rlK%h%7VSukIs~-F_2mHd) zGdeL(uRU^JxJ6Bcw4uHkKm%4pE~1!~Yh{tAT0DlVQ)cEwsX_WZ+`E^b+}9VRF4Zwg zDi*)C7adLF?CDQ(JNb-lrLnHj64;YMD4L~$_&L>4ttUBOmuZpwH&T-3-rkX&XA@A+ z^14*Sm=@`8=0_j0%8b4JxL%J~XyDitf7`JNx?w!wesR*J-ZNkht}<LS$j#N|Q+P@! zZl=Xr!54PFb|ezBb%rOA2Dz*wU4|{9A(mnIC5m~MLieE*E}C$u5Tbc9{~&QHLOzzJ zt1HAZk`I5g2t$c(rNjAe20maIh$G#+uu|<rASsdkNxM5EqwM%3_@B2&%Do-Qg{wpJ z36ab_w3NqwMz0t2bdN2x`@dEF@~4;HOlJ`}C^(xpOH@6u#}+7KipGe)Z?by%9l3NQ zH|5g~;FgR``W_jOFr-aNTfRQHu%iN<UX^iH;9H%#`{)T!-r&$TXez&*?8aztz}Qz$ zAwJ@ES1bHawcsnW`#<5&K9(_Q+g%JRTEk3V#V4nu)#d&;@qD8drped)J5LvONLG}S zgl|0pYq91fw6M20evc5hb9F<AZ>wTq*gPkGrkUU&1Ma%EIKBGMcAtN$%jx_|5k|5v zVr`m33-gy0v?iY(JF=6@n(LAv8f{3^uF3^TKgTEgaOF$**PCA;@gTi!c%Z5@+GaHs zqD8o07`)PpL3V03XGW^p7Ob>n?_a8rgBNX7)izRQOQtw5cd7eqURzBN)Mlj>%)?dJ zQ}A|!3d(W7uW1mA4XY=+GU<Ml3x2i=d<*}?n$d~3)Jjm?>t$9aleLi17R5Oni+=*{ z`vB0f?p;b_xWMOrKYo1vv~kz;rRJMjuLkgr*Qtp`)<VNM<jC`Y8n-&eQb?A6a6k3p ze7x_s^U=ofs@Lx%fb(2GL?0U691qt_&g>btp||sV?hSS?`j1YhY5$+_0Kc#r_c`>l zt3vDBjD<?Bc~OOt^Bp&Lk}12~U1#=>(Hd@ULT^$}2Y#%Yr<oEzKi^c%!NcA@+G`q# z%ar$AYhW&(K&OA$+p<vOAaWG;&-gN44wvXo)&8j}vgBaO>&i#1oPnRNH};m%i|iYE zrKEWhqfu_#)LP~5(mFBwGEIzcU7+^{rf(?M5Wb)x?Xi3zv)lud-ru8N7V{gL=G;~D z)G*|1Sh$VSB08kr6Xoq%Z-J?d&DX(WmgL5G&^QE^IWiPJTc^or_JOPWdEZN$SyLJ~ zAcPgo9(cPn>st7uVMh7MpRuC#U&ZYhE}IWHK6*G5<ZKKgaICrrMe$$*`9awoD;J@G zezJk-2cea-MR%Il`~z1gfi;iDxrx-l)UAfI0y@^|=AONi7v8^Y$kZjXDkWfQ9g~UA zEk6I6M+IRrua#=vY`n5qb?<5Z&=hrE+>zz{K!4t;gZopZ;CV5W5PE7F)N!+9)2lJk zz7M8(D4cu=#;;bP;}wYohm}<JQ7ff;MuRo3L-4J%mYZ(Lv`=;iVsTtvh83fqjZuqY z-XYrrEJIsbF<zPq#Z@-NKyzF+d(ANkoX*<woVO#AS5{aCo)`Iiu|ZXGz?^nJHfNa; z;~o-h;Oi05q1)RiNhgZi9TqPpI0C{zO^YPHV4+Vei$uO+bTW3nLtp5><x+DiUsCCH ze-y)g4l|g0)FBqd8cmG0$jvcY{nOMZI$<Uha=ju79T`oyug1PvWYoF{w!#@XPM#pB z8Y#*O1HSMD5(%L#MRdIN){#v?-Ab71B2pPL$AB2bAc+L_7b3MJ-`RLcCE(WmpcyiA z@7a|f{X_ecFUR|vTBxEwF+Dc@m8y_js_nVH7j7%%!K<RjgBmm&&2TTG-NB(he{wL} z-+*@GKg8p))eCm75_RH^e?}4Xecf1!ULwhJI|*>7qF|!XIOv~9ODluj>z?v=00F5( zA?<<FGko9leVIGB9c_0xxFv=w`qtl0whx&cZ>K-5G8k-N)#jWbNnK7q5!IVtQ0nBk zN0LC+u!W|~Dh3rfX<w(+yl4n3kZbA-Cb7)N{<VEAr)P8)3MCvlrt2~DT-b~6NoAGC zHwTu|CMw1cRs6>Jl?es)+R|*LT_y(4J7-H{<oc<(4Bhf_Md13FvB77#WB1z`l~IXW zM<YX#jb$+z(@Z5BTGm+B2@|m=(e`o0E(T!~PfI0nogUpUi#ht|FVx0KR0`sUhl4AB zg{+^*4wBeuYU8{}A`)>aZiKT|r07kALn^tNnpP(vm86&$2%lDDU1Adh%w$^NVC$EG zK~oy`^ViKSKaQ8#6Q`>rKgm2d;lO#CxvqhjaAOhvIl{%6lBFl7uOEgaj3lmm8~si` z*9ZQoY1GxcS)8-FSUvxb*&h#^31IkZKsYhC>f0kaNPDHgr<$7_y4?IVL8|v&AGRB? zoEGIInLqd$v7>+keD!nT@8C{bSc+N7GW)$b2q|bd?~J#j6gD)n=+C4IOJ5&5Jd)&1 zShPMXlq=TtF@Bh7{<^#8tb3}ag<Xt#Y$!3}$$-g<pNwi7rvz&oaz7;xB&|8OTp#yf zziwlQ;U|#Gla@cQJRB}1^~)U+2q0^flR<~K-=&+yY%2C(bvwyA&4Zba*A(?s*R$V3 z%O<ppA-tQ-iH|?4<$St(F=$~p&WMSNrw_rk{CwGbAJ_wrsvRVI%!==;@|4qC!pE5< zfw*pMlVaT>(f7ikc@joX-(jvxdKKHeU5BY?6&>^-ZNex<=z{lwd#b`a$YLMHH4On_ z9M3@pn#-AJtQEY#Ov`ruU-$(UpC>dlJR&_ZEXA;Co6FG;W}z%Hy%Ay<6^ggq2?0Zh z8)H0b53*%H3?HtJ_o2Tld-WffDVa~q83pm=P-xhdg@QYNr6iFyi&x9LlQZje6j2{% z?a#gA)(c*(`g+uLer)D(2URQ=Ej=(M;J*4e;1!&itdDkhZrTUN(Hkhu9BqiD74}2G zFAUn7@-`HI?2#o8J#k1K8{W$}k9dRO@!(Iz3s;*Bd^9eeMH`7~at?ZzA`C3qA7Bvm zmD*lpQB(CWUZ$miNASwpog$YtKAAxl;%n)dwB21}ST|Ko!CEo)EJPx*3L>owq-qsi zuPjf3wO7tl0>|I}mi|ic)c!|2qMdrZ0|IrK*3-~%Tj@IiZ87@XualCK_Jiua^x#<u zz<9ghDy45qdSDAz+JBB(aBsiC?8bH{av3TK#^ohkK7$*Ax^^^k{s>GJ0hg^P<I62; zF5a+x`j%_*M}=w|PEm~&jbjy@RNNVGKZ{Kai|)_0i25&Z5z?1o<ib7qX+c)$C}+Mh z!IdNHh#_J(ap|bFlXqhB$4%)Q9dTpsXO2H86;w)}`=|zUMKb+9>E6Q36wiNLr{p8! z*yV3lS%mS54edPqq5sa-xg&^caAV3_EciTNH+b71q+BD@&&na2S(mWK6}7HWsFX4) zm^>g&i#nyTpP{%<q?*FC)b5soYYB&x5%X3GXzGvE&@~Yxtlp<k+C-@|jdII=@19S} zJ3g5O&|c}5>+1u2&%$lXOk7O0g?x^8CS=Y?ySW9lV6me1vAhL7{3XrQ+;oit@=7BG z%279x?G{)aRQKH~#>>I`$<gY<OO+UX1`!b&xy8Vi(^}6Whp#rgGSb7cGRCP2AddFG z6hz95;e@Qz0O1SFJYc4Yl1Y52)+WqCA`8>tu$^VB$hf#-2u@mJ<Iy47&p-TSTXb0q zLw>P^h&R5wIYQwe!VB<O!m(jspMtsA3_G}}z2RYBNdSz-EU$gsuV?ao%keVLwae-8 zduKk<!@Z62ucM#LwzyEOU?cRichAG<pMm>4WU_|BH}ZUjFK~KYjX&fIaw^uzq;G7d zqit^EM@QclWsBFu)?z%AnaYOax}KbFWM|RwanH65OGP*|*fixxS}P?s596J*`~JPY z0b40tUffjt(etNX;y^>i)YO!NRm>kpK#HGbVwow^bm9%3PFSvqF>^H=PS=Yb72W(= zHqH|^M`uK?FQ1j-lcDQT^GK8zZ0$j%t7!5jL#t{oK(YXd#Nx^#8;OeW<WW(*m^<_g zIzTo5l&(8*MqoY0j1}R`R*wnK%1nh%8ugV;!nG;9xkP=L9>OEqncAe^n?+p35&%pU znpsP{e#Ccilz<z5eBDbVI#Mnav(ycGD3#bQty?7hZnjq=IuRF%46hoD4Q=1-oIU`0 ziyq9=kDweIo8sx^Kn)U|)-F$9^mXe;2A+$nT_ne|T=ZV7#HzT+OQICsn4{?;+*u># zsoZwicShDW9F4yG>`A1A+NVSQ6$N*`w?Q9&nY9>}WL-CY5)yjP-&p&oAnVy@rNI2N zh5c~gu#IM5_7r(jucV+yFYwu+vEpf!WnV*iRjP8|4XrGp;yMRY-FIH42`0>1jt64^ zd(ilI6GfaaR4(3!;iJC8mhurS@8z+H3EQ$MkuX>RO6&S7$lwRAb)*#*oa8b)(OV%q zjJ_3kxKwhLU1`whgThyb<Q1Q(og#oXaG&=Tob`Q;q``My4%>puDyn%42VprMr-x4} zB?#mvGM@~-qJ8e!1C4V8;B61W1m3M<_?#pWDGHJ}21~e8t+@j2N!<QH#=WOL-wJR0 z?#22$X>hvLdlm4@4H$87Lh=gX-}aw4_cz?~Z}4UbUqUn)HB$-Ukc(g9na*@q0-fOn zcSGA$TKxlfQ;t=qT3*UAR-1zi0d1{u-M`AVjL*rWc(Q^g4AD)<L8*?n$Ev&D_Y&9n zOHq^qZ8hvQQQw%dfBDsw9<>t6*Shp~KqiHD!vUD0@xY0rq9s4!B&hm_zNy>cw`OhV zYk_RP48fp}CsEb@!{IC@N<Zcbl|Qq6cMM7)gX9J?vq~v>$(JQ+TTo^Z@v$+cX9&%6 zeEX_0%GxsMyknx~T(rQ~V<$fw-S@~A9JDhZq|nvCIeYFz2EkbEE@m6b6brwmR!ZyY z?u=N~H1#75ab8n(H#*o2y<Acs!5?V@EtgJcC1E&8=_~}>;)y4sxJ8wljYeB(flbFu zeF@2BKAPI2@4f#;U0{)=np|Eg^IbJSlIg3%TMUUb^kf^ssv;*L(ZAPO1`_ka+Dwb+ zq7AKy+u9`=YZ}7spmFUIfQGrI1Kwpu(-At`9-qd^>8RjH3$A7Bm8f7ejLgEsnPAmt z{tKS#Mq;U^kT&a6N)xgLkGLj<Ko)m0QyNpuIvM9%Z9e@#LFMTOU$3;@QX%elnuM0n zlO1k*r^jVsvv^|9=)6YM)5zwHJDEg6r_mJtWgEQ`j1~7wHk0pj!mh4OZ}she3#Z>Y zzR)UoKN(gs^|(s=gMMoZG01S})YjB3dZ2G(S=o<t!1(w$6}(r;Q?#n6Oo&OFe^6hR zo@d5$cAA=cyuzmqyAm1bf0|Hjb?`{SbIPieVo0yzV!QV2*XpOUxp+l+O*NeFtVm&m z>rp=U=<v<;*(*o&jKAkY9$E+iEl%^MU_m_NS&0@VZqT9y47Hmwm?(w8Z509Usz0IN zXIZ9VUIGK2?h!CHVF98kFc+cRLf8l7i)ICucT!%uhB!<Lm@I(R0^*lhG(aw{t>D3X z_Zgz?ytENm6Gt#@9N@LFo<mMoO+^b;GWc_yY5H<xhqGIbkd(Lmvd;J@mx^hPL6xlA z|5<?T%XD4m;3%GI66y9$Pg?cHkV!2`YUIX=H@DFWngEhp*DmzHo6%NQO8Dh2A(=P5 zAH{VyNR=A6%2ZrddXDs7BB%^Sz$&98J14Nf=3o4jxx$gYvKB#<&)UB^wm$i!GnR<r zyKvs<9&``J;ouHzuTH_*F1#ZnWijPO-`}HOJ79A6>3AST!QR@|)|cbM;}F_-k~ zt3&U~r~^(bk2|D(yN1PAm!uSCUH`X$v!;wi{abX1Iq@X)H@A4mxQz3}7KZM~t>Y%F zG;1dLSzfj#)MEh3Oqma7d8cw{-ZrXuKXHG%kJ;tfVdY_m6?^6UFNed_MDOfOnD(>O zG_F6r?o35Z)P^raBovlvb{uOo)Kn(^MSZf+qE=G&i43#b<l;cO9sqGSm;qUJ8A9mJ zwA0nKgk?UUcjBbq7!{E1+3)EVozN0hqbVbSIqNDIGq)NHKYh(I!h%#r{PGt>^3$AO zJ?e7=PR0N7VQn5QCncOxs5))W``+!NiII`m@SMmpzsZ0NahYI=#Pr&vlg{q;#IiUM zfFTXqCRjM&14`~drl_)#_hYbOsR*u2$0c`0vrjHnvAi4=u%K?c(9$*<AD7u~0S&}3 zuAlWjUl>)SSqc=`toMaF%&H5?8LmRFp##6#@ecHFzsOw@6!*T~LGh`l0b9Y%f{DKO zkzK&&?|ux91_!Q{Hki_BNKIT#z(iev49nhId$%Iu*YeG0yn6mkd`-y}|5f>wD~XFP zFGP8i^a=Sxbr)WjBnFtS{pZ2&2k)LX)fGE`TebVRrdZpImFxIrYdDN*hvrLgTYV51 z-*{PWmu5W7VtMnk?_sjsni5Xsp>;gJ`*2>L#5n3Vq711R_T@YEUV1G7w4!DA^#mg> z9XoGz+2fKa8JI$3=rOG{v9TK)zG*k`;u^TRIhjaG!gWhmR~h>~xhmJ)?b97gGxYbn z%OY(wLcmS5s7Lgwy9M393+I-He>2hXaUl;0sK}1zx8>eS(Y6nEHfaDGzOFX<6}vmy zuB)5y4v^l>HsMlwGrk8e3H~^gYpftumwPYBZ3dT0c-R)qfs31(qV7q^6$>)}b<up5 zc2dZ0ilkAg3Wuf+)7fArUfXX+7w=>AbVPD^rwM(8@`P(l5}7)thy-G+ti04dB1gqc z%kJK$X`Roa(Ton%?fcT8=&sMjy!bn~*k13NCQZlv4x{qr>VFe!%YbG$GuLCufa6np zW{-*07q(sp&W^%L(v>1SL<QrZb)9UQhXoW>(~XZq1xl;8_1`v6yb1ce3J&TtwXD>) z9r-3P5q&jK^Iesoixdw2K6|!ySZ@YDw_`+jaBTQ|VdvwCL2}S{*D;r@!n6vQU)WKw zK>I`App)mHw-e|?!*HRJT;kn2I)9x%9l0UXW{j)*jXC6Z{1UZyKnt1LL#09Hwt%wP zbQ{OF%R1KF;B1^Hm(A#xG(|bXYqjIdy;Im*J-P1pYJYQS1DU)SbOSV}*}(MLUDHgH zN6bVLx+J7@o5GIW3Giv|AYR}8>M0nTuwAkrD=LGRp$b_FU$51G<P=I*3?34G?2cH? z`L&)W!+4}ci|V5X{(>DNElj#}tlKm<O(7-=dA>FOF4DG^t-6~EtHo5Ucm(NLaGG^a zXd&b9MLJ)Keel5?qaL$QyEnQv-t^zh8?nQ6EAlL0mk~AiIS^)rAcA4d50y?nZ9?+= zbr<XIv8NYwuD!vtL_}G*6<?n{byTdEB=lcjD-NQ|j+%2N7+=3T?mc-*UDTKLeg#ai zV?QZU^DR^|g<FmV>J&`^BZq&5`zx+)LKKztUKWhBDQ_evnSZMe6)ise`15dM^f2d@ zu`W&<D*ycT#+c{m$id8@wER6Yli{14$mW5B;mP5qkg=c|jtOHiCwfD^>d@U}-g2m* z^Rl|fJTBZ?m~d&ePSk>H-@#{EDGKd)TqK`(rL>GF{y=75y7Eq79A7^c@bh=HDhqZM zNn|y2xKp)u>qn18oljx8q*O)R*9=M1_tB2uxzapKf;T$*qqBTImZW)deg3bk^QWfm zGNL#q&WzE|4r4SPZPzmzb<pG%nm2zBdQT|hPph2p$hp$w_exPwpA3#V6a>2ZvZf!6 zF2fEcCYp`L$nG;a-2NgTA8TqoLcyR6afmIv>fL&d&yYl@pCfdIa5OLhUmj+KGembw z=(KpETddJ7wbPn582;*gunOZ20}ads6n*jQs4@A35X$&l9DmS#P~|4mT@(?8*0Osr z<sHMtS|&5z9h-l;xckD%FRaZ?RcA};Y?TqQ=FjcJYGF)ce%Z8En00JQS=m3_pi6Fh z))=Cb?yZsc?z>U&>fvDD$NX`*v`s<2tzz9mI_vg_7UY{ZFd%pS?Mi#@q)nxRa9yax zddLS<zIX5G@e)+9MF<xVbNWNy@^m`ZZ}S_c<8ASp9LGw=jDQYb3*m{H-CwOC?mJ)O zq04lkX)xX=(N1aY^H&t>6S>UI+oq*W&^4&qxQ4NpY8+HTz<x&8=s&u;)#XBo?TIV$ zRo;t8WODm|ni!O)h@bhdC+h(ZcRqVO?UgpRdTh~py?!6OnW?zdEET{SHYr(29_&}^ z(pK@CCHIL2Gj6!LU<+|h#U#RRL}FISgo}cqbKVB1i%w}o4-*+!j90z&^mFrf%vyVA zyd<L7kWW|I(nYIT_3$!0kEC;Pbo*q{_j7B)c`bddy?$5us-%~8o8wEWBfm_!XRfrW zcvY2}C4-0)SV8?Z)(l#3XC|TMU7nR`M~1VR=_|qN7)9X;&ZN-6=TqsE0#Cf2?Z4Mi zA}GARsdb~k{Jp2o+a(jg15pzhl`{P+$g?GP6(%sW8s3N28JYS18V;Y*==Z<pV;`UP zP=32;Sg&?$4n-__2nrPzUCDEMr;-2y^xpsn9W>wEFZ0=2w~BtF6bS6lyCJ4#|6`{! zow!e?8o5B>IXQy_P>1DHl$jzb$Zn<q;{KjofBnXJ6Q}_GovYe`Gw+Q??%<Ocu8N=1 zyB7H`^v9^r10F=AKY_t-@2{W{h!#ou^+1sl7zgw_V?(9)yxi_R{}D(87F6JlC81V` z{oe3R9YZ(U&4&0*>a}{WyJCL#MY_G~39>0^{^IZ0g$Qc-oS=&V<WU*;@l~<TImaw@ zIU;Cv^igyMq@B@$QvrLd)rZ7om8p+@bo(cJ>Rwv9DW|)VGdOPE`DCE)Z#R-yj19*@ zbkSC~ViwN_+fA^N7n7ZYGK(QDVE$x!M-Nlcx5yc<%vwnasU2+%Pzoi^r(2p&<0*sg z0j~~W*||ehROafy#4QjQ^2*?@+RmBf8`rk^s3XI}d96vMlWgXzg;0*0`fVfMB7c@M zIFd@R-0R1yE_JVgpYfB@xXGJu{XXCi3ly>fH}iuAr3wWrr<Ptq55CPb?@SLT_akd} zjYGhG1>m|jN_&v_yboDo^dQ?xO-$?Nm&wy!0uEFBZ2t9S@suIEBtj~rTG+3Vc@Kvp zHc8*t>wKVOpGZ^nMtgsMpOGnc0_w&Z<mCm^#ziL*Fyq^akYx_84%KmUW!v}2a8^ea z%OwShq&TDWnYoQE4)9_;U8hvn)3q4GcZ9gCz@ugok0xtn21t8S)@$m)Z^m^^6R>gf z#g=tU<Avx!1`!Opv1;qAwH-6aO9EnyxSgSwzjxe1H<}b1iRz?-OX1KKGl*ie2pX-U zox23)3;riXxuA&2Sc^dlmqo-BBgD_9Tb~B|VAFbarRQyC4mx9V2ql(~RgQmuoq&oR z;M2W$hG?mjlVd^g*zPke?Y<y8@xNCcOyl6rie7f}4__2sUC^abW-(ge{hFXZ0pj!z z*u7r#9m-0&X?C<J<ubl;QlJF+c^N#~KF4M42M!x5eCE8)E1&xH%lgxF<q<(}Pz+`` z>xTYm-^Arv1S`+$Y6XWkGmNRJxHF=&&#jL0_mCx76sZ)+QWqK$-!s4VWLnbbPPHRT z_4)7-*k-ShW1Q1`27LsRE^4UU&ckudX@+K+BODgaFoQ^h+P2HfQ^o~KBn!;WPLA?W zpOrJ=<v3FPT7tWUfm;z!w3HP4vAM^Oay=-b(QZjhno-qZn*oy;i9)MlogNMhNP&q7 zIL1fh;`zz+04M188K*{1x9=qKh}^K;4LbA{&XdN$WcA&a%&C*!t>CR7l>%@3$-2)Q z<Pe4N)^VbPt)uI5UiQ9>Otl^fgL>q#WVf_wu@)&ZeR|#V*{+nDorxy{08vcH$(n-U zs3(Ikxz@P;((7NLE{jXO!TE(a%r@D&?%kRmrr+D??Jd(iWEPeX9&LR(-Rf?U_J>f+ zTJ5O(G{p>I^P4)VQ-B9JS|824dF@SoFE)IA_2kS(+lE6-ELWSY7WK^+fO=-fN9e*n zC7t5_mCPDXGf|wdKFt21^hKfUM?MbPMV`xAHu#XB@zf#YTfZIns~~B~J|4IddeSw= zYn6Ql+4!<t9%*Yi{AvEsuCl{v`DK#EyAYqsK6`CGUHgJ%0KLlbueKVC6td&HUtrqv zrgt8@EbhThuDDVOFD|)_GX#l#t`j4ESH1Rwl=VmpKiTx(U;d5)Qc_8QFf48jY$NQ) ze$9;1*zW`M%I#0$c$^9KUqN``IOy3!Hz@=Q!K)=5-rjdmw3M<Yl_v*!Gjt}-PxqQ& zJs<nW7%50>V|e2TEv=`u`iI+g`Q~wFlo34{QR?vko=<}<XJwSyiHN@3084l49AqjU z31ne}o(%0wQZmCMo1z<|5jaFRCLEaqqnSFMR>a}ZagCAei;{wk(uZ1`RBbhF8a0M$ zNi+N3g!`ABnB*2>^&-8A!b#{SeW%62eu{z0y+}(#m7^#LwPQpgA`u1N-$}n>ay;Rx z^Lvx#!ysI&J-_$T7CEwfl~tNBfJq#y67P|azjX(_ZuXd;j=&i;9pCv-tsn{`e%d!h zZhIs^c*z#jHvLWA=#Y5DD@x|4vF_DNpo|*2?NI%5ik%E)cKCgzsk|YfyYZqtzVP^I zkDH%(sxmRAb2I@T;rni-u9O*nXR~|agExM`BoO5cJE_2RKFUCjuxWjMyv~HqDW>)Z zyp5jt7^PDwT7>V;Fs<{lwPBMduGMg0$>0Gl*O*=rr4mbywLe;h3orYc`Wcig)i6&K zXhRno%%;CbBbpMQmBZ+?G86|7fB88oJO4q5hs?p=8&$~4RLIhM_KJMors?K&T-NM* zo(s2uZj#V^;+b^^$<nIPvL{Qi2$yE9S+e`LVh3f@_KByzVKjo2;yaNiFMtY1Py7?n zmrw0W$dEf@u=~TI7@uD}cWMO|m9^@@9JO9>_yZq|XAwLnfA>urs0&|`!4c$immfca zup?H}t2jG9mIj8D2<`El`B_TY0lCu8!c`%L{LB{(j_Y1>kb23YR`NL0<XNl!9imVF z+ug_Mv4YVNK4nEd>8H(7Nr0cT3G=`)8t5Z^(hp_^`$Ox-kX~*7qlGZf$CEO*@Z9^* zVXF@^0UxxKbsPM9SBT1aE^_9N#)M|0(zo;1zfKq~4vlO-z2<4{l(IhNWJK4|_qt~c z>3SpAVY>)h7JT0EqZZ`+?<@N`lV^s11X^~vOpw*ZKpji8srqiSq<Vi`jv-tl?mjH- z-s<a^SmVX;$Bo0K)HJb>mQj)plvAwn$PtWe`B^3c%MazCAL0XemF(kX7;_~u6pmQa z(oAc7=EDx1h>A)yMme*`c!I@HO5A}AKIZz9G0;Q7*%%NIiD-#OosTK<xm%pPU4?c{ zJv)l!W3rYX@qy#)s$ThXIf6rTBo4s^P6gXdq8UzlyjS;HqxrmamHb%vFp&<7NY*wG z!a8uHRoY`v^t(8cs%-)eBsX5VC+iF*Zv30xe7DN^hISg*&D~;pdYAw(p^Q_cNZ9r7 z%k5=+DQRi+N*D1z%00j3Fn%sXV}#p9jvx)l)9VrH`}MD<SF;&4K7Y)g(&2J2&2<=m zAsOIlAxHrAg(w_1vl*Wb`mEB_W|?P`5_2mqcHy<v$%@iwK{|EQAb4ScMs7-A`s&HY z-27Kh<)44ifoeO0)~ZZ~Geq_GuC31O2ZnTotOyS)Hj={9kj(;^j=X`Oh*-C43-0yr z96sc{V$4Gm!gEGuDCCi&Wah-*r3N(&-acL*WbVVd02~U@Wk*)qGTVAxT5jKP@XHPU ztXtja)M$%%)1<Rkvozy@C5)@x=e|&;Idn$TVRO{TcCouGUJIxTgBilx#DM)HJ|YYk zH7Y?s!k2$%>{*uW@7|#{Q6&e5X~mfScJNr7+I_noPfc^yv`DvG&6|SUdt6!1NE5+? zvrA@@GpRk0o78f!a;^iRVQ&|xK}1D6Oe3TXG{Sk_ij2Sgpm8N_O6b1gh_Af^Zud=& z6a2pa$xq~w701bd<Uct~xfZFf9tYdlyq%5>Bl%vg4-?F5n^gi92o{RA|Hv|=@ukPi zAz0fefqLKcs(O{VXsxRnY}2oJyGl}(F~|JlW_A?=T}{;iN7E&r|IvqH2?}+#tKBaT zw{{BpsC9z+Mw>63aK5#r^H=Uz(gpeZ`j}<MKuWH^+53Cix&4qhUMwIRu>DS%A8ev; zR6l&>-r`Q5vUlR-!C26`X_qox)BIa%XS0&^fxU>?V?5qixcdC<s=DJHz*`eu!<uc7 zHo|b?dQ67!glU?PsG6PD$m>*g%cle7NoMWcJrn4FSY4yOLGxA0Pt#&aW2mdOwbbk_ zIXzM9lka(JkEk1*oLEk0^(k4exfXO{A_Lkljg#`Cbwfm^#|X?RhCC^rSW$Q)S58i$ zWxZ`4sF%@!*Xa@M{tJR=>lefD?3;W5Co9bm4v#j;x2cigRmc>Z>!tL2A4-@k(oQcP z*9}c*vWV90sYvbp4)zoKW$Lw7AVzp}-Ln4@I1{SHkZBeQnIvw}rZUr9Niw$_HbcTh z8O1L1t`&c#<?dN@qlF&PBRgUkB{i$p{N$Iyuvl|8EDIM=gbS@LNCAzwk?@T7$I!Ij zL&aBKGm?rjkGcAu5DvPLX@g$ctReZ06^DpuD#=R?z=t6=+Fiz#f%`syI!_zEcaJpm zSBdD@IIQ%`*DyhH=r&!7Cxk&G()Xc7u}0rf$9M8g9n~|k-)Zz|vU-W2M2Umzjf&;9 zPeDi2`8WzM-hWOdm<7nsJR9E`@ov#i5Tj;~Qfn#?QfzFKsjBz0cr4;->hhZ=^VvIl zf9mG|1I;cPxpB3OsHptZW<N|D(0Ant)Ar6kkc~W`h(dpP6LW^qbpp?MLUO0!c{A|$ zSyN3#8AIl<_#_>Vy_liaye#c`11qM=DQ#8EBEa22RE}s&`Fsl1m)aeQ<Fm%!)@o4g zL9E?c@GXtu+J>oI%m0)7fj|21=PJYccW$><D|w55;?jJcKKYJb@rT<k>CA!*+g{r* zRK@b8=i=QS`K{}l$C0S1k;Xs1a6a{2#Y}0uSJ&v{g3BxwTS63kU18rhTF{YxdRKQU zHKs>gNx>ctb5x)Cx8JJ6u$f`3Qwy1@eVW=6M#cGgIhBK-nVh=VOyVNmSH#<{2jnkB z#PO93_gKEVPf{N@D%iqTD|em^5M#p;;trF)L-!P(B5(v&wJ;gNFiD2|Mq@RiR<);- zjLvH6?#1LC|G9Hg`_OP|(zgSFYB9IP^f&fwh-}^;B3DO#;FWlrdryE_Km<wGup91o z^p3&%aoyaA4oe3s`*c*`k5hfKJB|60ah>$TI>V@qQoTObpM_Ak9%x4qbENDP_)|CI z{^KU&OncC6^4aKCI@sHn`Sv%5SJKBeO!lB18lkOg=_&D((9NrrRpb|asex;Dra#%L z;t2a0{<xKh;&4Js>nM>2_$84b%dBhJ_1schyp#s6tL;-b?)A&UdW1Q#X<&qOJsv&| zYHJ--C^E*O(lW7f0_H4z?l#*%7z02XoraGjrb&rFlz>Z~a0yu>znt4A1O$Vw<;CnJ z5RU=yK}OJafapO)cCdTV{$z`ywupw+iK@vA+N4*a8X3sP6n><xy{c(_SuxU|$cm2S zeb(`<`0r#NtuMtF<qMWoAk(*j!ylM-sf9N7Cko+v?y>+}_hS!85*{1Yy7&c2KHauo zsK&DWSc1y9DXi0#+QQ_wL|5{N6FYhB7>aW*aNI;l0rNioJL>YMwMDX*-HSCkTnAF@ z5R0tEsDHftG;tPuv$QS7eixnNt<D+OJ`~kY>}}kjzQTZVjR55BaW-g_D+@+V8oO)K zI&s=})bM;~mBmE)Ck$-7+Z<sI3ZD=aarzVWqp|4GHE?`m6yC+yZMtsiWcEL!T_NH- zvxo9RXPn`flMrEfXB+KJ%PN%%3Z6+8tbF_#L#0v#Zr&2#hl|Q&)Sy=D_8Zoo-;RlR zTy1ia$vOH8rDlnZb$LdqGi~ZCVzL)-lx2fbl(oDGmQlZj4Tna&zrk}>Ufl^tRke`G z3}3<tQ|dnsM70(Z<I^N%SP@R3C1`Jq1IlsL>h#5kxuG`u)Klb#->z=6;*ONK1#Bd} z7aRbG*rA6!-aH!_-UjhWwn=gkBjdq`2Jb=o)|=hzw__T4)~)_nbwy_bMI}t0-VnCw zLW{_Op|va{x6h3#kf~#4rXm(=*P5Vxa*OI}Q=lFl7j}1f^i=wvuY4m&;wRV<(n&T@ zQpQIx66`iFJ9!UAQ9wSuJk#Tz*FPkUOuI*^jSGE7ASFP*@-y|DaC{ugRDXN|^|MMz z-v+T)Z;#KjMT3<~3;5%gGl$W`E(;YGq}3&tUC%!vRqvzHo3>XcRX7EaDM{aQ55Y1y zeiy<b{9O7DI>DR%+yWK+)$=9L0WDB~n0}UY$Nj4lUy#Q%`p5q<-`(5&3Oy_37bCJR zB~5&`6Xs{Dg0eY=df34LS3aC3mM=85caF#oyvVGRC3xgXFwB_>$&fz({eZ2iN+VGr zTq(D2e7X19yK-*NGM!ldgb}wnqWAGmtWGZ9aDP8RQksc<L)CDkSC1P2D=y~q+jw-X z8PQ_eY+wt=yJHszwN{BgoUc7Ez-$ql5I>-GMzv2oXQy2LOjBAgn36k-%C*r32a=tX zpXe9VlJs}c=l7m|*U_FO<V1$k)@J?5tZf=mt}Wk3Cf>;hv0}1{k%{i2fEDv}mwPUn zu?Nz*)f5_2xj63wR{{W1Q&pO*Fg#f1GfO29ob=9YbA;Y_!E-Amyza|;z!@|ey|M1a z+^@YMih>WF`{qhP&EumK`u(5E92mAlgV*&a*<Y^IR{h<IyV68E2~oEc>)*g!XkO); zpj)}-AsAiePcz&Q%fh>%KLo(VvW$<IIXT<UyQ=+OalS?b^T~U-1pGA+qznHVZ^^d6 zS~OSSrRh1nzLRjna8m6lKOMikv1;e;*8%*qe^mTe@9fpg=--r{e!cMDUyk+Tv68ai zAgIf_zVcf;cribrYyjl%w0H!P6Z5uEs5UtHE@7x%&JNH>j34(DO>wS~d;%IJzGEH7 zAbBjz{U44baY9{OQX#3cTa4@XITU<zADS$?IIoOywEix2Dt3eky)hgjml};o)B*nU z^Mq-LEu>Yc%W7Hwd6mocwa^aJG)ndwNuy+O6>$9FVF@Y8keB_OzO4<{2rSFY;D8{y zzs>1rY?4H*+eRA2B@Q$-xD?BGewk0ec;GR>kcGc&ot5Hum9`E@1ip5!Y>8^R(=Dl( zqbCMHO!pHuy?KSnG}FhT1Zt{AG!cBH5iCJ6Zoc%-rXJC*Tm}f5df6o*tcn0cEH+Zb zCH1MN1FkSfLeg`wDG^1IoE1K@MPXZd6`sbNwj@g)=4U?**7m_=cI&Z)wdxxrkOY*| zj8dfwW!D#If-O(DDsFWNdWLsKy#5eR2(CtKWj6wOUM+rMjw2bfBfo*tf}HntM2zKL zFO1Ef>lFq6e60K-QA>F6U9#YhlV)1<D*mMpYu2+tS@7#`$utGn=0u^6h<)l=Q^1de z$+WuN5Y2><9ZQSvg*YyA`ErRp3~b3Qrsxh?Yuv!mXWyRlq4lP-8Du37(@(0BjxJmh znL2L36Hsx{TYuN3KLSmPLu_C+c6p?fS5N!NKP^U{CD8E!xnO-o5|xsl89yVUS*1c? zT!I4;ROe2aq9W<;zXPp_v6(^&%Ft8D<#ER?qoFb>qNuHq6nsMy3C{)Cj;A<2jQjHz zlgE{J98G#syQsw^cfKr=ueaYo@?F<vl3~avJH8Pm%bG8eBYo4Z1a-c@W7xS-*|d?? zuB*nD2^;fwal~x3YK`NNgNewOZE^yWcI%^5LM)LFXb6<q%6Q562krIwX`S>Oqom($ zwRJjOT`vF;ds`qW;pQ-(p@XByhz!%4zsfW<ST_K|<3f==an^*|$`BIx%8f?!&E$Jh z5*@42-HPR%;*!62!$^kc=qSNqwOY!0c4NbZ|Af;7KsNn<G@a)^o9)Ag+iK};R7*=M z#ogLl?V2rWuf`Um_K4V{_N^_d_DD!tR0)E_Oo(07rbMh*wfEkup8Q@sf5Z8?UR>vS ze2=3@g1wN+fqUtun)lfv-ePGjKUDc%n1=r1TZ+raacS~(*cs+7Bl`ZO*Rdz|O1{7$ zC7$iVo9Xn?9~O__m_$cNNFK-K^f~4(gLyX>f$F67rooEAy`OlnxS?V1;|3gqKP%+I z%Zrrg`A;XDaJ#e4#|ofiZgzfs`D<L+t_ZpI(K&ptHKotOM&#~)+8ktuwyx-(<0KCS zN~DwfiIj(5zFY82@R}@}te$SV$)_!_2Y3!!Aw8<*R%31xahT_e7su28r1d~V$Ke6= zCwgqD3GzNphC8~Fi$D|%3})_-@<BOgm&m<188w^Tc`7$DygwCT#1d3co{j*>ihX%} zh%@w6#}L~t$NYp%hs6?yrRp;Op9Q#&%xWj^0U}#tG3@bqAr>rv+d6-ZW-}PB<7F}Z zj!Zt{tU^(_M!iOJ@DwN<3hl$!AV#&q)LANcth%z)+ArF*T#;cLzosJsW$VU!xJWQq zzylZPtX2(6O-%fJK$)q<RO&Z&{tbpe`%bo7{yDN<=~Nl7M&4FxE`2HKd3L;Z{lEWK z=xffP{0|uGx5cNB5^`eOv0U%y<`jqHl)dU&+w~**DL^d0aBmqhNYSH<ot;$DG^Du{ z$Q7oXx_>HldFGg=M)+AMkNy4cR-?S#mYmYp%r_6rx?2}Wd%$^kc2+b^hKD6dJ$}di zmQXgJ2;3gwy`Vaag<-kWNTXU-VDQdEy?EA_Dg2x6PlLAbP;thvvX3t>Eq^eU=QSlD z68zhrNO4&D2&l{sXo$=Q9-j^x)7FOp4Dlz=Xi*_$+kJYIT}X1@s9`s**R0wGRWiAL zUSwoi^?7&4b<wC)g<oDus$zv!Pf$o_i9tYxLI6o<R|i<JihNTotR<%nvaUPTdS~A= zD`(<TDsakfMb)E$56-)5+(^OHLgHV&dM!0a1hEi0$^LI8XB}8%HXIF{)ds6T3=7vO zot8~lfd1^PFU9WlSUatJj0NaQ%EjGhrT0^)@?yA^Zx~fjhnt!T@kC2PAZS63s`nff zboUNqt$lN1Lp5pZ&ENk@TeUGwi2`Po?u|zoS)FEo`B)^z^OUKXQ=jdzQkeWjp*p_K zF)DU3Ie5E?_#AckMt<tC$y@Utt)f@JAZkALT7@ri-PO&weu0QQHC|MUmoey%d~%1; z7IzL?x)L+4{@1bZz|U<av1TB@<(bPi(AJqBo8k)A@bi-obZ|f|OpRjcxNE|bV4Qvs zGHhxrYf=S!{Khh{Q-A_Qeoa697%AeHAn1>se?CrpWBM%4YDUiH)1|%hJMJ-m#`Zr~ zE;W^VKtnqr!|_jDNJxI!leZ)A_9R&?@`=5Ckz9g;uL=kq3KDhDoJ<9EJ`e!w3WJc` z*f|a-47o!4iZj0X1od<nWwNN~oAu=1C<w>hjgR0n&bas~m+?Y5Fmx_(=CMniR8_f@ zG&XV0+m(E6Tc8ccE3<7PV6gzOBu{Y1gTZux!>}z<mzmotv$ouC+EAe(VBmb&GY*ys zvkg_#fc#u1mrqS#Q&RSsa$7sHU8_^ihHnfjDmhp!MogC*POsmua^zg&^4>isl40%- zbB#)-kXr-g3Bzi^a$MVnN9)C2{+hpFO~tp;n@NeBRNA&dqAQ}PhrdJ53wcA04Sjuf z4cWCQ>TM8g9tS-=1&3q*<t;HTFU4pS{`k=-L|+^k-W$@fkXS)owu=`mfGUuSFKdiR zuZy-FO~b@cj+jdq`-cwkjb8-E2FF@b?3ECc=iSICY=XCjh8En&P<xo<Yfl00M3@9b zN0Thh*F8!hSuXa^s^0o85>dGrB5A7S)jV8Tw>Z~nOQQ{ld2nltBg*yr*8pldD*!4n zA<9bdvaGsV_ZHDSFprkwvZ*~l*<{+N7B=2JLcDTu!t4;I5^yJa&!dZ0NFc^CuBO0+ zo{mx7_b-@KX_u)4@GvL>YuVd1W%S_mzk*pgLzm)i$%lVO*+XsL+4&3Dq9*341mrq# zveX<XkNZbXn0VbhP}T<6T&tn_mC9`2072ZD8+K0lv(2LiV)eMV+e))v@W>&YSS2o1 zE$X+WpDnN{AvDeF{*dIR=PuzWtV4Xru)%u>DwGwT;{C?oHqk)!U=ar+{d6gvF#nZS z9`}+vp{ScUAm(+n#44l?+^goKU(VZ?{v>+I1esMC@c;(7gq_klU7t<XHxXh!UCjO{ z5RQNvq!w{6D-bKDcW&hLwCnOOJ>w4u(r=ENilEmLVA)t4DzcomGMVs8r3h)_KYHuE z5`2Nrj36Nm%u416>ZWrAF5}O~QhG~2WCz-BP4!TTi|5J6rLS86opG`QrILP+&b!c! z(t!!FzRwN3U7a4~xH@jj|A@2hw$gq&t7V0tj1;mWJ>k7&NfaX(O9ws=v<nZBn<A}G zjGH}1bU!53rod6I>+MV>%%+=yuWkd%tj;I&mbKLfravh;Fm_!Ci1Yj%6>%C3@TQnf zJF6Vmcx1a*SDg%hGT{o=Iy~A`UT^RdmVQx%J#KARZ$6o7J_<~1*gN_AG1o?imbR~L z%s@P`IvJUbR;D-nI_@{X>gop4l;lcTsE6WVt05>?M~ufZ%^S}8{wx9i%|#zjtmPO! z7eViYxcMX#e!JVt)`iQkxzXJ7L+m&V>_+wOm@rV`R2vX?zhP6Z1(D^y>vo*Jq<=DV z_1A-~wxZZHcwcpFDMGv5lC7P|NTh;IJw%~_*5P_-lE0#RVeZJQVbMNw*|X}NQU^_X z(&FzxA)lmN3P)t&e7Le&kZ9B1!RvxhugoC%ce7Ng3Y@B@-9ZF(-LT*ts$J0+)eVe9 zpJwd=<x7y8MXmQ78Y%Xn7EBF~jLKr9i>m2RG$Va7yX6)<RC8z`UcM(NpKqA93vYT+ z;bog29XLao?P~Y@MSLv+jqjt8lwPL{NKDAmO4*oL<%R0Cq!3LE)j94Tv4X{8@vx~A z>j^kY0F4|1Gm+}TVZltMq}wY#RuK!$<`x@INUD04WFejGhmNwWxv~fECSoI-2R~DE zL_x8w?Y!Z#aW{gt!|sKM?Xh1@m~0c&bY}&$;j?7#=0!(G)OBx!|29B-;WN#w%`Yk` zF*sazigZFLc{7-o;;nJbi0MH>K|vCOkh)pjAklYmYW8$S$I!;f4ef`reV3n$Tp?A! zlNdm*yfn-fh(zLxgtygeQ&O+2zzQIhFjo9M;gQ1;3X9~ZpQ1E-y@me{3+4;ueEkrG zP`fPVx3YeO_`@gTebx@!u6fv@a_@BUHDel?8%PUDjonX-?)$kIzxe9X-sig!Tk031 z5}$3w)1}7r$zlSWO{YGLwjCpepVFNv(t1%(-xl$-D0}7wQGx3;D0E0@ryUV7U?||( zYZFvbMxuAU_;>pRCI%MY=nCKJjhFB{v^<|tx3Awd+cq3bwAav;z|Jp)yT`$kB4<l4 zsw!&Ktli8>vQozb%BD8`7TPu(Ob7OKHpRKFD{!FP{cK$U|22r0KU;+q8U&eWCutel z*==wt3vJ*cv88;DQU!YGMYjN)M}GFp{c9~KOt$hyOCY*B;c{e9+;qk)WFu^I12g>? zFD51~Ep8fp+o9yMamy4@wRm?F8XGxv;wB|bw$i>hPzu`#@w}S-Te^^iiyl)vpD=Eq zi<e9Y4HC$-HvbxP@~leAlmw5t0;396)G)+-sO1_n1)a2yqzWA?GYe*+zXb9}etXzo zFrGHwf`!QiR6OxsWQ3Ndze8&bl>&vuVlk?1TDrN!!MMD!<xz9P+kXV&KB}`s?WCy( zGOv6MlW^I(S54$t{=)M)`vj2R3C%klL2V^_o!w`f^Ns5Hy9P@7B<T1H#{bewae3^9 zD1GO|zA5*`;=qIa1|AB0^IL8Hn>X&&aopq3>QxWhaMtWrIepDikfr}iI1JbaT-tfR zxhb?0=E*kGabi8iIrU8%=C(`?Mq!lIf2%D=IdcnNt`ZWIZCUqTT;%%h^p^JeeV_<+ z46Vm&hLuZYT&Rf?!vIlDO<7S!;_`L<@A5dg>wG}EbSN~Y2}O$?tngRitq^~a1`2T` zQ;Bg!J@#M$I{&YCH7s0R)YKro{`sE;`@?eA)bnIwO8cHZc{yx!GJ_Yn3|Wch4i|-* zeG0DNFzxa8JG|B6Uo7H1Z`RqOts*k7CcoQ}V!SC5!xBzB&SUhyjuBx!>mUmUk7uvm zFpgo6iITKO?v_6w@0Oe)N9BLV95K(8U2~Vm#SPgd6>6}=s{J%Xdl$+Q^{jIg%*^B# z9bBi#i;iDx@`9`oksQ8Cs>fEHGVOESTV0mobW3w4{m`g7Eb}ssI;h2RqR2Ih<7H#m z-+*<k5}9W5+ILG?$2j9Q=T8b7g?lsqq<+jkS6R`gLT8QK+2PZBwRvjP1Ept|OVwhJ zqGq<<2XJy4C%BEg{9~IOv|nUkLQz)CC1y;O)H%Pl_#emn9U~oj&Ot7!JAxxinA)wf zo$v-)o5k?AjWzkYmwz(4v@<)mi*nK1U1Q{%>8?fVmz9KazXTjYM|+*nQDI~X@^E%| z@J!m&DbwsUkmXA)Lm^Js3O*6N1c4w+%GmFpDbRB^_fvwOn7i<D+D7jL*?K^@hldg( z1Lk<k*Fa;zV!FQg=qzLRn~Y)1!A@=-4kVn(0M}@C)b~1%?@o(69M?m$XC<w&UVBIk zc^TwZ=M4zA^>AlC@5H6VwvKiY`?|1(6sFX=_9D7hCG;{U*eK72_7+o05~Xa%=sE_X z<4^3{%r&%9@Rvlj%LF*kp_9K#KZR>H91wI+0&z4~R688`jmg~*bT!fH)TV<J%&_87 z?dO<?<euS`M0%8#Zuz0VQ31)oVum`8^axRG%ttt`6~SLx?a@8sNA!%Ku9x#{p48{8 zzhAbg;}Q1s*A~E^B_im*5?M=;R2zu-HcfMJG2!|zoby6|b|w%kzxlC;N3VS(a;`fs zyW;a6`dquz$4?$hshgdym{I(b7?W(0^9dT$=e*V!dF(rOVpI)O{*@e-GaD*>xi1{D z^vq_HF-Qb^ly<$OKy6=t_s_PA>l)&0RZd#*^beIlnEK3Pe)MI6bv}@YJ^IEnUWcn9 zR34SBd$>P6lV!^#v=dvYj_%q{eHzC<up2Q{D14xwwg!r_f-s&EbaJ=x*~oUe-)-0R zzsttejieqrod=6?bQa)75wbX`b81r(Y1D$+S5lh1h<V;A_0M96;Uv4{Wz|1_-Pc*@ zCeTaah3WQ=b{xit`oz*dQGphbm|**Df`B-xFXw841Wmy9!9}dVR2)t#0+8km{0mX8 zT5dcZho~5<d6@p($9IK+5sn1JCK4>B2`x8J(B_k88iR&b&U%p2k!lt47`KBi7klAS z5NFg$nl$6voK5z5PI5tV)5Q8(_3BM`nEfb|7`taM-n7WYGSm>NHP~OO{1I=QWV+aK z2<ymH<DYbn31cBO%_rs+7ilb>9Th5`(cN;Uy3-$Xy|KyOtk7QE91$2=+NCLA42e04 zh>;2((e+;UdiB;KNV6#k-W1}gNB@bB?1$K1{As$oi}<=<;<)oS63L%<X*e`FU0^)z zs1R`;T7P<Sapy8A{H+E$Yf@8kWCTgVBv{!oGDO9mgde`%2J`40a$=so9P-UyCUvN5 z>yEvU!d$)<@A&t@UTwgSJcGb>V#_P_Cl#`Ca$&Iu+o^M$SQMVz7e}h<>rSWSltQ2> zTUx!kWX0?2tMQkW;`O?Otf;5b8Rp=w%hJMSX9|^B*BCxwaWNcGGyp|BbQbqlaD0b3 zrU!G-T8tsaKRrZlk25DWdT%CR#0}}nbov#1^LF}B8G2bkeAwzL?1*%=#PIRQ3dY#P zoLjqfD_poEt0GN_ctMqSO(GtK^<AyK3URxIiORjIvpZLDfZu&s$q_n;5G~rurOQ!l z<+YhPJczBhhu}Tv65yRSt4=e*@d#q`$yZ2DX!1t)%&K<(h{WQ}R?;0f(i<RHO-Jxo zz}(0Yz0=>-Cm=bG`8qSin;zIgd6HAu7B>Ct*vI@Ij-rk~(}k~$hp0AbW{1Uoc2Y}` zjq!!LyfFhLM@Og#J6@`ow#OLdvJ~l57dnrLSAUwR5(Wu=$K%~~G1oF?MXqB8{+8*c z==e1{P<VF*?%Q0{i-axv;Om%726T0&Rof3|+#`x@H{}eG_$)&}Y}um@6C8si*q%;^ zLL}aio4UF*%nxm32JT+yBP3Jy*Ahp|M+sGldqoB`$~v~TtWyoxorF+E+qe+44ax~^ zU$6~5Ft{a!PLNnywvesk-V3UkS>rE#IAFOk-u0BP)Sec!2Cu8E9vl(JrroL8ZCAVz z;<9Si3Fn~Tv6dZuQBy5;Fw+yb&OJjqBZP#B`%m7tFZ}teZ;^qy>4enGn+0~;I3rMP zxKJXb-t&zZvb>a{kMVKZVu)(G&93rXKAnZHbvMA?aZUq-@|e$qt&lqjBp5**g+`Nk zlTi}N-k%X<Z$)8mPCFDL(Jn?vQ{vlRe~t54H$!wv);u|HkY5eC(0*^Iq7rHGO+NL# zWO`tqTN2N=dA+{(z#!)^UhWIXTb1Um9+2V$>kO0PBV6il`giB6K)B-gDywp2@ukLD zQIR~3g(vBKzcJ5;{ei1v@9bb~g7Dd+3>$Ov{j{D^+(kr&3GC+u5?_1pXxk(3%dd}3 z^|iiRx}3{1=blWFPfpXnP7<n#yM5zbSoFy!$R=srVq50}969_dO3KbtMrz2f)XU?U z_&-6vnp8qC3#uY)n~8`7D_XJ1cSlxksFFJ~KIFj5Xk~I<Ny+lZ3=?V^ZM-uRQ>4^v z53OL^t?L0x%!<V{ji5X6V`xJ{<1Dku>v!7-SGb+<&|XzaE7ik-y<yUcoUgX80Fnm6 z9wt#^SmU!qz+w|HhIDk|_RiE}qXj_woyH-faf^~}w2+QO7^HZiLqPg(Of1UtQd6j| zrY=j@Z$THCP#^$i1!>d7`t$CMYQeL!o5-&Ez6fdR1(KqYgEPkgGN-Y|Pq4<<zJyU$ zD-rUD66Vp$+)zzSqg1o`#w+DLUpEB2zXlM}*7oi7I`eqQ@?~}DQy$qYQayg{7^ysY z2fynr8}Q(Lbn&nXv}-()=kFN(j~E(D^9uL6={;!DXjFIJoCr$Zl)VnYdGfxy`=<KF zG{MW|t0}a~Z|tVo8>shh$mSn2g#3>V#iy}`#%Jy^A4E%ZiS!YRYmI?O&P|sbst*@C zIXNF{MK0r-*WHRic;aY=S=4SIvrIqG3RAK>#D62N69&}fS+?d&(A7OkXnS^1MQ_27 zb%S(mC$u;9svs}fb>c~9Tr*})_FKJOXVnl4{I#R=guXrdOQq-t46&-w5P)$N%s{-9 zG;BvL<<0U53DWt6r58<Px^^UPBKydr2eHi)R<N8Qs1TzgDa0Wbnj{G+(RO!O*HWPI z75bKGfxt`ENEmd{jvQTf4~z--6CJZr>g;tpwN!2|?6Nglfw<12h^;6vK%~JGF>(99 z@OkZ6#wcWpAtRlxI_Ae_zdhp^(A4ZshG@cWk)>&WtP#0+Vdzn8G-g<P_oWX5#}%!R z5C@8AF^px@1&Jpl7o$h|mk`toyhX81^x2c<lvckXJ|0gFm!kbwkB%9;3jZt2LZJQc zCQo_n;{sJJLauBq_Tn?B^q)E+Z~bUk7?9<RybwCB8Qi;CsEH~TjE^a~yl9;(PS02Q zHm5>JTa2AM^oZQ$^;q%9t-qGb&@ZlIG!|X|L=mY)&ykTD@&VBa9$#!JcpgMtofYa$ zGSuA`Kh=^HSoymM<To@OJF?VA@eN6k-<jn%wF<)n9+*@zihO%HlBP{tUL|F6kXE5Q z-`Zy|Y%!M54n>nm+~PN{<`nrWp6K-}uWMkQL^LPu%-{z{_Xl3}D?1h1zjNlR>F6_1 z`E90~o9pSkOeE592;c#rPza`AX?)z0A4`-dHz=wnjL-_%TJ>v9qmUI%1T_^od8)1C zbty@t#m%3yasSy&taM>VByQ=K9}lIF4K>dLT%6a?Fe4k5n$>pRV_!)+X3D=4J<XER zodc##G}B8u>YbynRNBlYP1qb3>rR03Rbu|!rWvO_JuUc*fWHh=8@UR=jS!Koh8M9p zua$x?APrBp`uzNf{etE3a^Y-m%G1A+ia}rm%VSW8N8!pN%waxv3IUgxV9mg4#S$`Z zCCH2j91s0*H+;s-lLIb8lQ0`DtRv7lxHwb=3OjFTF4MkO{9iAR{<lE%?Fpq&km6dK zF8@VCKCsks<jD-(7HYYY3F=s+b^J}9pEK}C#gVH&cU}KT^w-*bG@ie@s5noXKbUZ_ z5E?X?8&Xpx=$U7&80MbDTV#GdbizIE=CZEY-|xuaj8n8A{Ti$h{W9yH!-7k3?ZR%A zSAQmWF=7(M!{YAVL~Oj=kLm-uk)OSWfHXI|{^nQy0~%J8-xJg^3n30!*z{thwG!^W zCXrS%gV-)=!;Idq@h&nkH<_5qj`#>2ky<4h?$c2KP{bBJH{rK#ZWA2l7fwzH@3wvq z&ln^U>#yp8vs)!4H6e4eET?I#N=MwEsy8gM`IWEqE|R{~T6;i+`y`Z`Dv0Gq2XciT zev2LJ)~DL{T}N*+3msuC;T>Oc2UGE>2=#}nzFY6Hi;04?@u3DT+6}uy(o!>DT~Ay4 zI=lGRY7-5Iv8KWf7FtSXll%<_A-ysn^0qeZ-xaQ4sl+g%bh;~^Af2M!=cyo}$$RhQ zrXk<!^BLwH`Tf!M!3)iqH%u}4hd1lX<OhAysc;8Nk%@X-H%csi1f1G^3$0t{rivL{ zj0~-nuiYr-mq=1?-k*6>An&#M-hEw*fBA4fv95lx`_<vT^`^H!m6-|Ypty5<@!#X+ zJtby(V)RJ@gudDL7lm|BxWX0lto^^n^j9NlZu1(QB2rF6cNz_ADvXB%A7X|h?E#Zs zbfx_b(v>6P9Rw=%7ZP+?T?vn}Ctd<qWne{Xazmq|L|`1<w3gK{aJj2})M8e{P~EK{ z3Kxs1um=f5c%c>=?d`9=kG?pFf~El5`UYYv%;P&1R2abk8?C+}!XTa2Kd~5ORwDV< z-^#4q2amyF>LM{f+mDH;f3y@X{`Bi(qM~u_r161qOS|HN!UETl2de7`%Q3<5W-y3V zD8dJ|tnPcw!qn`v`2ye}pT^==1R8(x3H$@%mOH1xR+)lsqBBX%g5#^xgFL{Fj~%k{ z(l8V1FbHUjfsU9JV|1PuXZFfcPW&x`Y&w9~q*9qxqqcj*^Knu@j!qu|8alG0Em|Ak zy0{{#Txl669Vzk3RnzMtHH3ObOsa;SOOQTi|FFe%w<e1>d?R3IPrWkYR0?&uh^%*A zjY-KKVMp)#rc<5R-@AeA2`O#N2UeN-KM0SH<)?3<DfS7ZXKwgCirxQiP$(~B$1{}@ z9PUxqOC=J&Z*AADn018S(S<y6f&uYaP$&^>$}Po>V^F&DR>+ry-Uu_td%Kkff$=7P zF4)S{Ou0nM=sK8AzqmiZ3Lh?`Gzt@bo)(DaHZ*hVb(e&MeR+xij6c?}?9IZbXpQ8% zA%X|`{g9E-HdMvm7xEGr!J#O>g~v!k!>EL8&u@=4H;H~D3kR_C)t6+y2ppP)_7CMb zG5;BKSA@mFM+J__Hkura@*Q3^^t6nM+SWp$^P=GGMkps&T~Qw&d?Z=mim9;7#pKzp zX2JVZ`GyFo9g>ZnTgpne($?KrU{V%Z9R3Qd!}qiG>Ey+Cvy9eHFp{AnFUm&@q({rs zE~J-n!uDTBVcH8%tAQ+MS*@h;3TQ$?K>iZbxk(-Mo36o0%DMo`Tvc~Icj8eghn@>F z>x`b2AxCy(BT%INKz0VKH+)*N)@WxUKfZDxb9-Nr^K=gnyfe2mrezOx7QE>6Ectnp z{-U;;LC9+?Ji<)aY^IpFtre1H^P0+}4WX6fE5mN1pVo7ri9HrUNwkEXr%gW+EQAWf z))zFrJBi0;_`jmcd<iz@Q;GHFUZPqkd~<E`>f>Vb4Q;#4EB;*_ZGgX04^&FFE^#3x zN8A4>G}-Ocx?dlN@9Usap;4d!@vH~Ck;F<Sp_e`FefZWdk2$qtE67<wP@7<APAo^5 zG%(6?n5s;W<8u+@VdCe=srHQ5R($z-q3t{Fj(VF@GQN36NpqJvR@?1#=VTE&tVst` zPTM%4Yw*z&NcYAeIrPKPPF5vA9V7Hm7S@!Z<=+R<vBFnKByX(RKa8y;cS!@H3#2+Q z67%(yZ{sEp1Qb}+*Ktior{)EN5*fc*-)%>!x+0yHD9&-R1+V>{9aJISJe9=3{uV9Y zVjc4-d#Gr(s(p3e^C^g;a9}}mB4fdj`2n|i-9Djx9EHasnoL}H#53y7nZ&><Al-%J zyo}eb-r7zRHrp9pBdraipZ%H;B%$R6%(`D0u5o8h$m5q#%wcehRA|y~wq}##9tzd} zKr16k;a{JwwiILCn}0FDo}PZv2aVi|{lIQV)ie3&WIv@G>GwG9+F&Zvh-oi>fAFnh zP>Tltah5Cc;qu&Rj^cR0cJ6xpd-`b_(}0;^0N(t~)O4yrI_p`S0KiwoQULAlkiDjT z=&;Vxbl8EfgTaZq?^dPNW-`~hvmSp(L)?p>{6Y;Th>gW&gsSACkmx)N^K*D~?#a08 zuc3GIM(g8p>OG%jb7UolQ@*<vF@C)G%YTI>sM-woy+Rc(CuF0?5Q*{!Jia1}0Wzt4 z4$nr%ua7J-=xv#H>DVloeA8^zd&ch2o%U8;+AavAbOG)>dqc~AUT=aRK|N~uqab&g z8K5ifOXDlR?!bg|!bp63k6y}ZPPq_Xlm-J>X*>lTEcA4(N+yCm1Oi$49!rW$of1OI zE`I_1L59D{X>5%A@UXbCsq8^jLSiHun+ve#$l_HklW2e<deI}qK05nM2r2~g#8Wi3 zy&}QlU28TutIl;zPz*zs8L*gxf8{g~8yc5J7e1LJjrnvm`kQ(GUND-elm7DM396Jg zV5zaTYF|%A#YcbXWHP}mC+GLzp7B3YsUPY6C4C1P8xAXdc!i947EGYS`)}W!Oxqbx z$<a<_)J-XcJSAKjfePqZrHVa&tu>bS?*Ej$Z0Z#aY7NesBAgtr7ybU|?YclUw(REQ z{ST9g+5fWuxJ*qyVxvK@=R#gnp8?2;QLenPyD@JEyVK}z=eSO4H{<qS7}xtPUWijm z<Y?V9YD=g<M@Ch^RkfeA#)X+|>#<psHv-+)c*Q^`>M^j5RZ0t67m|{&>wn|gR;*!$ zcpS#?=-odUFS|KxEDY=%y-qU0g^OV02<C3V&*DCBs@gggSBq7>pzHe^UWQM&f2=e4 zYPhuzUXfwq@xiz9w|}qTL$bBs+j5Ppbs5L|Enwl1NLWT9Z79mt-NOUC(L;oa!(mOa z0IVp}Xx{y?d|3}==URz!i5IcZ*KP|@k*3@%nQIplNLZk^=9M*blzoI$rME*aYR6gI zVKmV{KP5r_aE72g_S0>kdTZ0&QUL1F$C+6A@!!Vg4v~KULmjSFl<RnxAe8cLU)*GZ zV{osho~Jay{M(w=^Rlhs$>3l)Q6(a^l2LH_NvKXzS{cm=2jL!Hc!@>Q)wT2W+T>!Q z##s}kh=Rd3TfNnEcXSb^;?1wpETi<sO2T(bNj;M#jPbA3Dg86uqSMRnn{Ms4<#td9 zarEg7XLFCgu&4H!-jj2bzEUI_88~BMapx6^Egza|O05LASWOVPp66l4Sy-=Xm2w7S z{6^l2Q;Sa;t6BxNrAk#BoGOp0jS;cF{*pFty58%nTS;?LUtGpEa#Y*fa|h;ox*`i* zkx1JDG}jOjS5KEZCD2KtT%NgoJJ-+cJ1UQLB}&|PcHDJ?*MBLo)cg5}3Cj}~vs02X zx$9GwSB6^tXg!rPe9AT|aksf`5Kd1m+3A*eY4=DBYU%0(_SjbQu=~?Rc>XXfB*UA9 zV(pc*@Bl#aw`AB-7nx*$MC`rhpbe%G63~uy{p{LNVBIURDJW=#zq_)b=CsoJS!G8( z_wlJSK(dU}4WYPN^wNLpcXTj|RQ6=BCsNqAG&1refW=7+Kq1a*C`UH!^ab7EDhSXc z3UX4iJahAC*AK%auUU}CtG|6c-srd#yN1*koH^L_Zug5XHNF`yT;Xh7m=a+}S}1M) z>7mM)s;o?rB@5~3xrLpscOk}nTKDSKQ>osashn5O>X|AueAda9d1nN9nnpDq>GsFp z|3R1xsgP<xH=Dcr>4)J$s&7JpH8B@kS`V6{!w4Pf5yvxqt;7V^@#fk05b$P)(OA!$ z)q0qTTk!I;8w2ufa*f~b>Z8%LS!U)TkUu>s(Qs5j4U1UWA#I;-d}*I<sn9~{D{j-_ z;o-rgvWPjF*n~(9Upf%09Ec7S>ueGY!~<+fHdfbqI2>^zfo{clLPJMY(g<!ncdm15 zGNPhUyrwRsK&Puh_uKJBE2i(E*48-nw(fzZ=mvw3X01UXb(rr?{4MM9%F1fvDsk~a z*qv-!v<KKOAA>327AH!G`K&Qtj|7GX*e`v|%6mtf9ps2cFE_eQ*-_1vE41RVjO-3` z0{Ch=UCZ8-CbIT-5RU^eTW4PUY)=7zB32sf(Cx8G-6P1ok%X!5%f#3OQ>Mu8F1J5A zim-bMunGtg=eK`bf}Ru+6LCmjo0`fq`m%o@tWj1PMZpMe22It}ovoy!cjC@$9j)6O z4+$<2ZuE=YG4XekGJy2M4{|Kxjae;sM$wx}VIbCQ-@w}?KJ#2u&pwV6<6_PM`n2DZ z3}2+Gx49dy2^ou@mNa&C-}Kay)Wd2e=K=p*zBdrg8BgRsVY+QD^PHejRd<}lo`7~2 z6x#I8l9%>h%G0)2|Lf+8x?8h3I7+I>AwGR_`V&jkKUZ|okU!{o8D0oxy%lXMBd>*S z5fFk1=7CAW<I5GRGhfGRaO;#mPrm}zseaWAiVh7{z}z-gd6(9uEV3t`bOb#gNtUZH zXiI4wk2REoXD4K1@Z#jyTU289n3e*oE=QF3R*T0L%`jUA+^tCchq{%ntEL8KqX1X= zFZ&Mraq!^AxfVwuiBuN=6T@dY%T@$rzQm0Xd^cy$Laqx<tvh$Bo#jPJIi;w7Hw3bJ zIv%|*AqB57H`ma$S?LZt-e=TCVNeBZ!dy+GqPwGb7(%J5-)e1&y5i*4G7g>f_7gpE z<WV-DKIO-P<?jJ}*=DO}C6J|d)F7@tP^GtTO3}w}His!?jVwKkR%<Ee9`(r*6%G&9 zwu#d6Vb5ve@t-vnO5aoH+s=GZk~UFXhL6z)Xd+t#!x&=jcP1Q0UMU8p+9DLoA6{Do zW*9xk6#wGu&U;W!GJ{P~ho@29OG~HrO6MuKK71P_$o{kNo?KEI#V>uwy+#|s-pO~% zi?BLVwK;9U%ZX-06s(vpErk0(Yf?_zkOPn1+p>aYTNeB;RXT=|=+`|Glys;|3>)0j zDG<dTMcIQ<JGu>n#_t~8BR@UBX?#)7Y>Rv5>#K&70gppa587;0=Aeiy7)(c;S4<9{ zs|=D$t^e}gh&n0}xQrI5=1BA@oP!I5W$kjGJj$jD!V`_mqUe!~KPC$-4#V7qQp_xV zVWp*URccl$WU^6G^62xp>um;qjDedgz|B=aQpa7l4;c{P6I+q=cO)mw*^R9;E((UE z2?O|sG0!ET)S2be<i<u!+ea$~CFmZyKVf}4OB1COv^+lAoO%9k1A7vUc_eG1<#_~| z;uUgqDoAk}8yO==26D7@^?=z&vKTHJDxi59A`{VlNM`#WZT%<U4+>n~8y!&?!hoKx z(?ewxQ7k^iQ3k&gl1K=VtN-mWTL^!%Z8ltN;Blh=tox??nqhX?qG6}0<P@^FwSMR0 zL|VxHTxay3#Q}L6q-P1!&5R_e|M(q*ml|!-X83vn@3eGtG|nvOl~K3tKF&LMj57AW zp~-*Arn$7edk}$*^C&AQPr1BKpB_u)3_{GTbiQ#(sVgoi4msSuMU#Bv-g^!4nOn^N zVw^qV)#c}I#yj0Eud3&$D=ajQE!BEpQ7cD3-`jPcP4ZL1QJ<fQ$(Oq)%@Olq5h6Z# z0@d{<8VO`$>rRL`kpJ8vZn)Bf{k*F&a4GBdRY14@l_>cJzXvirXk=*nZn-hH`NTPp zO}^_A9UUDNg-nEs6*xm&|BVawVO4-f0q_I@e?tkNqo);#EvaRVcAC36UMobML|K{X zax7`*11kyjV>LE*{yKs)&lx0(exVhDU9`_3oN+Jb!OT7XDtU^vh$Z}ovN7wmLbT`B ziQZ$@VmtFRmZ6s&SRld8z=*GB-C69qSMNN&uL)?A^_C6JRY5tY%*`k7AZw;gV(DK6 zuKG-)ofOS^r(XKHZlP^@<3G?$kH!6C;gn1KeA*W?4otdw-Aj{1DLh>intU39l7p~9 z0ikQcFcv_xjtvXdx-!zyW!=(=^}$XO>9x(-XIMo_fe*M;U_N+ieE3}Kgg=l42nJNz zK3W~`zg_(xFyzSwp8s*U{@e67PeZ#?c(*c@tVcfGX4Rvhmn&~(62wIaljACr?WkQf zo_n!j^y*pCc_h${&6&fs0owl8;IErcEZ3V2gIl5|uFeRJNn4=4b=LN7O>ezmAmn|i zq^!F~38Q`F)xyr%kb#H!b4N?IuX%scnlk}l^Wykc_=T$VnM)~P0s|P(o=kB16c8vx zD~j#b0D-}scbfe24*y7F8jc=+?rt_-%LJ|UHpDFl+ZXP*{9v~g2gG)=2;ph8b)kty zOFgRUh6VPeOFi<4DY`Tdjua2I;<ke$(W=o2DVw;=5=fuJSJk;(7<q4Q)W#D<G*GeW z?BPA00HcgdZS=L(-QI2S@BR~4R7#nvp<Rtc43tiT%mAoGa&u{P=7}7A;vAG&jNV7P zX3in}3+d(9i3P}G-QMg1U%8uy#F)<9A=onhI*an9S9`ohdE3^0yBB#oaM@xQI2n=R zT5I}#?)TpgBjKUXeRWVIe`_SSY~4)(xeNjM4ie`-IlWog*fBO8BpDa+Y7}%FXkC$) z9RA#LP6;~~UQ7W1i>n2v-xw_wFjz(p*Hp#_H+@ELFK53`<eh~$C7H%)_S+hZq?a_0 z6(g3^5_}if!$a=I7Xv27#>8>}LZcTBNkfO7e`gmL-;1Fj0%^ZRZw4;lH(cIz|D*ak zEB%ZZy1QE=TY7nKZMggGqx19v*zE}63=Q<<A}ElFW%-#ftA+#S>Yc+6;?ayUQ%A1p zd5U+i{hUsldRPrG<K~BmmDfwoY9@cPh-2IcEeadg?mVsY8T!$gUS+0K*XXo7=9V6y z<KxqPWt(CVs9YS$1L87AqEZmpkg-lZWz&b__0ehN;YizNQcJQ$MOU|E=|kau?8Ddj zy;iPf8<bynT7v|Qq<%ojzbKP&nUN456^tW^@|@x7&+lrq5LeT<hhS&8&&J2r1b}li z!ik%kO)v}?oSxaPVw<~QGo_79=yM114IIj^YWrQyj3?Bje5)kh)$ioqDP>JQ({z}B zB-_DXX4&1x8BV(~?)V=crjaJR=VM=zb)ElQ>!7+lghL39ZKBFio)<q*e!%)>siVEm zvW*$`>_v|w;+&h>>Yz(J`-#OH3E*$zz7QUubKT88Plz#8^Oa59tK?;n`RMZRgFk;C zVFSG^^2%iVy|P#expMZ_w%2b|MVxMV^DcV->lN%JFt%|)L`>d$re81Q3iQo?dcE`P zKm`5rhaUFV$c#hf_NrE5K*!Pk<~irIVJ5nb2j~O5Vb!6^MNiac85x|?|7({kL6_mU zt~QSi;jpJcV)S7q1CG(c`A%ZZ|0rB#MblFb2noeE8Y3?7CzvaM(s|iHdR_E`;7sky zU+kii!*5qDUM5lNP2Gs~c&F+4<L~-L`?pfjba=0j@{a}|j2~Ydg~ITZa=eyqtZ{=} zQ1$SVp2iuaB=%W?pPfsStqQ$hUY3VCW>W0A=ot0ZU0aLW47nwa^U5l&kCT!yf`g{0 zgMSwuomZsd2LqoOXP$ioanz`3k#Vh1$l|Dx=_HR~b9u7*{bfQU=ly&3fd$qbAMA3O z$2!c`r>3%i>bX%MdLN|t4))OBN&_2~d&mR!RiWpP8dLMYX4N15>@&YxN9DAAIAsRN zT(3WR>*|iFytk(1bYF%5C+N?#w|TPF&$yPOohc{p?(r~M6bOwBiyu8+CBWfOBuwwW zzuJ!F#Rr+ANZUq1i2%RQk(A>R&9)7jwtiFfD|2HIm!j?i@_|7bb!)dZUUGHF(CX>v zwNP$QUB)yoraj-rBXt8=Ci11m<zB8G73DCom3gfHh)=ap-&+xATdeB(u=r{QxYeZy zi4y$Ez4?=UT1!{q_V-5AjzzCNTOawQd@7GPd?+Rr3<YFRCwZ3~2`QY$<t$QPYc0{( z1h0T>-dGJv`{fy&{-MV_d>}s!b#;4#t%j+hx%rEc1>#}DU0@eAwvFhC%TGB!;+*om z^N0AM?I{_alGpn2&ALaEU)ERGSjMNESo_U}1vy`DglJ*A(5)3>6K)5ocgF=A2MK<$ zxD1PDachrRIy^I2G#b4Zo_`28*_LcL_MmY@)cHCViw*|v0L1ALe@#G+Xyj4{x*~)z zY{e&H@NJJ!+r0lWfq;)~HQP>Om&8u-KZ1$*>M}wgkf>ZLCAZ+g^p_c~!Gkpc!Y4oT zqScYeCZs-7@Tiy&yL@K*XhW-ZER3ihyzMV!bPn?gGy@@AP=4r|7l*zAQ2{y+rDMik z{eJg#v3@kUw&vb@a|XQ}<EIcnhH;{|%WWz4?!;S7_n4Xev%dbWyBcBh?H*#_yhv(R z_{-qm)qf>oxig?Joo~WZQ*u<lppxt5pKKR$u2FK)qlvZ_K?e<GPm&hJLo6b=bSg1y z3Q|X0Aoc^NxcV2y5K@EI8c%aoc{$F6Y~ZolI9*iV5k%94ipCL#VUa-&L3AHJpZsyA zj#3H+FD4Y&Sh%`|tON=l=^BTl(Q`vxgt38-RjK&Tt1Z!eZ*Ci%_=n9Qn+Q?NB!0_~ z<PCGRbBo+O6ybAZpuJqtfWjMnh1c=poB{bDd2_Z^gNh9uETr{`w93a=|EI_;GYT$B zM8)eFg9C(=%w_Hr@|G#)$nVy#!LhN}yL&iGOxcRoumLI$iDIpvZ6%uSO}a~EK~{Xo z3OntxhCS`;MIBRZr_GNgMcHD_>4>jg{etr6MFPfVy_SE{XbjWt1|f=WQ9?E3EnL>Y zW}jTWeL0UBBn}3kDBO;qWJ8K<n+SA<g@1{X`$f!T->B554xp|3yGQTK37&oO>nt0* z2^^B16ov1(DY?`TlqRx>FYk_xU_WoTsY`i2+CS6bxN*ED^}gtHjD4NLmtl%@kb=!l zdc{a=$mxM##(Gf4N`7gL-gxTh`#}0<2C>c26n^tA3b?SXh-%VJHcl)XY5lIaSExLa zrvRq8b=xyyHa&FtyHfVv$WGbGQH-lR1>}wp&Go!KP`g)#8W3zf4@O2I`|u~^T4UJ~ zNlaWJLyRLTT6($ZfLR<aB6+CX^WvC_^_uC@vUp9-DwNUWNdJ7My}h)|(J{)_1uvC5 zALeT)<b2LxlL-Q>%cMn@)i7?yJ8ddfl?y^`P4rc^pubE+C;z6YbCZ@M;=X?<tr?PR zEZ2lcxCH$XmNr7jTkIDE=y1`fcp5QSu`+vuYN|P-W#5i8#S7jaI69QDR2vBqHm$o` zxDSQlqAHrm%Q$>`2=OE@wy)VtX81+g8r+QKe9ZbBjyriBa)zuR3eY$gjCxed2M<;z z^IKA$;0sZCCF$qtdCAwDB>t7~eeopzB-l@lDOJU=C+$tjHk0a%aAmn4(}vEYWb>;D zrZl0ULh6OD(;Nw?9qE*u-kX^nuv>%{ONOBOAVN^sc0fhV%*^$MjO7~(5R04AUY7uU zL9jnXZgIyDh}AV-zuR7qC&1zQ!c$3|=VgF%eh6T6@!uF6rl&;uR7rnhU9pVquJP;- zw#lAii&$)<nOBZ$hdx@FsCaLEkGk7ApzdP*SK*2^e&p2>uqJ#(_}^uSu05TS`0hZS zz7usUoNO>F`xa7um+$qgGH4hNMfFULi~cA8_|O+L9kO4><yfvRfOH`d=~M@ftJv6z zToZ}NvAWmTNiV(g;JuVs6Jk)@xW1Hx{ijAkI;BZ2^&QK)wDHQ9^9M2yhapE9?u=}R z$+zlkVoPeP+b$2@V|1q{XoW3Vy4k@<WGqze-B)4%d4rkqMV6no+reCP|Ai~H5C0FV z?bwd_gXfEFPl(o;g!WA}?u731%$}3rv!_G)Z2poCfAyT!)Y&g>{=V6MOIE}|#3806 zpPF-%(QRx$nv-p*_8w}$U>KbAmlL~Bkl7EBE8sT`-~GN6CQr#ucQ~zfy83Ev=J_ZS zvrHhnB7%;H-Qq6&6dSE?5zOD{H7b>UPRS3gh(yUb&n@%fg!9yhX<z5w7(RLnGk5ow zuIumTwO3)~ubB|{Cphxt_t%pY)C)cjd!;QLX#@#6YwFeBTc0j2Z1!WdJiqUd{B7#@ zx0!yTvB8C=n{fjFi<gn#_4P`f^dkQu4rV6#D|dHakAv<*NK;~e!&v)Y31g2FW<j60 zLJu|@{FY`K@ko5oZh~tNhcO)dY5q$qSX=3oPr?n7_qht*IdGCB#@d{G)<|rbywNCa z*&C0xa=lZsWy!JrL-)I*I<hiZ)|bhv5Dtj7ey=ZEQCoCxX2iiWvwUYsPzBd0;K#25 zYeYT&!b$*fw0cR=S>nZ+-8Woao~*Bf<;K1k^y;btMRqeyNwU@&Sy8r|eaL8ET@-do z%r3--2Bl8XYxdkq{q9MgT%JbE2Hxm2gmG5OVL;;QkEIPcqm~DP!=sQ$?Gua9nf9GM z;l-|`2RzuwhdOyfStXakOiInWGpb`{@kg=WcVVVdPk887W_m4qIIYh{dGN6nNNA*- zuK>hG#&^zh*LqH$LD!y98umbI<cl@uu$b5g;edAt=4t<_@~(Op8l7%3spZY|F<!UT z<B{RS$iSaGS>Tzh_ZK5|*@i=L+<2QiE}v4hCTnk`#-Y%J$$z-9IDjK>R2KG>ceyx^ z5D=JcmgOKK^X$`0p@In|#5#ZdxxMG?h&TT?UKARlf3=Kjo8=k#%UzuWk2wA)X4Z_% zHVu!WV%P0QC;0c5zVNa~&AnEV@wY1L3*A6Bfoj6plDiY0kP#ZJWK;UN-j5x{8)PcZ zFiI@3T1!r<!cl@d?!F|C1?lyN3Cr^<8H38l!Aj<<sWOAbji$w<wC<hfo>lK%jOXr5 zTe9oJsyU@vegtSN*z2nt>I(00Up742NT+K<qDt~Ajqu4|0a{DvLxY~&KsqoaBU(1a zK4UKK-+sSR6-$YV{l}Y&utb`S;P9=V+7T-@8+-|LefiYVGcKpH3=8M40~E*J)=ChP z5#7_2C!)h+G^IrNbL)d72Bi9mR)q5sNkjM`<Rk@Xnz<2dJKuRfufgcd<q4H>uW4>O z0N(S+gG*MppChG)D0-K3U2Q7e&CDVSsh7WtvD+ek=5R@Kt(1`T&ifqmmThLOoxpEB zTS&2E;1V!ZWsL3pVwLY{Wt7Pg69HT}?EkyqNk80vkN(49QSrIuT`!;1K1+QeO~u~R zDxO1shU4(f6a}GTZ=d3fQ10og`)~tR$;QH!;6lo~xO>x_$$93V%_Da8x6#TV8`mJA zlRtT}>b`I!aY}0}<5};xy97Y^+gAi&qplNAzA`47wNY(oSU>=E2X8qld$@(GDGG>w zuCCwtMn=U<JJ7)x=VBBR;Q!j`z8BH2)3-rHp$un&<S{tgyL;h^ud=B!t@zUwSnyq6 zBH2%F533S9uxR0fmH|$BKRn?O6Ej9=`XfDl1i<eM2R%F*wRA-T&D}sj&bA*?qM<MX zu}|x@SHARYMA{MVW-P`*mL$CN*L@CJcA17XzRm22=@(7sM?YLX=TF+xa(b<<XW1q3 zq4E8*lPoM24pzw#ObGMiODpy>w$_@avQ`?FK7RKoDHn!`;<J^S{H_n!mS=GT#az4` zBZ!U;5r$sGxmuaJ^3&Y2K@v#RLm*jxu)TtP)??Xf2Yx7-rPAkEJYItZ`EI6=eleL! zGAi*HaCN_R41CJRX6n-DyKvAuzur<+evwdu`14FM6)MXzwxUUsb8+3RDQV#I;n`~b zXJ=DJqcD?iK5nXBW@kt$-R<H2^M>EkQ%*rs`@YR53D}^`Qx@xb=>r#TNhxeK@uX-U z_*=&=ata;|2Iou(=sc>ERkZt_XX~_;2JM3bkaEb}BnCQDmhEqVUQ1Q4_n<L}c^`~r z2U9i2*yyCs`%aG^J=%d!hwG`Vu~#Q<0!~H8b|Yz!uoH&`fg0rkhaD#kI}0O^sMl+H zzcM{^4}x?khci_mh@tTBP?8upaP`d>4Nz~;Co3t5pZuaHR><5YOhJ$8FwpK99n{_f zS;*(Rlcf}bCR;1P=l?};rJ0?#5;{L`x$-|`qK0D&-rl|n{i5)<hn(t#t5vUFFQ(y@ z$67wpjA0fB!o>EB4GKe=BvSEoYeVEMzQ{7j!iVXwu^&5*6*YQt{<E>9<#qnK7OEm{ zFcXttDUgrgPVJi3RrYHdRDNRG$r0J^_+&dXApzos&Yj}+mw7eITE<n3$OAW1l>RNY zMGvGf-Ezu#LAoe8km5NL>>f%lYa+-E7?-FRkJuu=-A$5L%+@pCyrfrkG*M57E#6Hm z!wo4NKHnLYC_%^PQ5!9e&DgUoTnXTn=Z5;sY#kpxL78Iwbo`74xcJ9N+%p1;OeW`w zMT<?lR4XZRIN}S5Bxp<D6pb?rOjr_08Eod}YW`R?RKH-*i(4|;Ji6<BTJ~4>Zr%y& z^gnYSQB$~@wYRmyReo!liwE*&A>qF&i(-m*g*Q7S9YGa++@B8C!blpnVnO%meeqgt zgRg!hy#8zt1fwhsRZM^5n`~-{0Z7!B+B{^oPFql^SM)h%!FgT#8)(gJT|jVZwN~-~ zsm~8lneDVIV1)-cGsLDu#}Y#~7F|KzLYC3>3)<RB-Ai+uVQ~TiD3IY!|MUi)IJl2} zG9YPJcXmH1GSShY!0~)85c2_g@NQ9?Lp&B>B>SmIQzyj}dpLb3OK!3UI()y}b0)VY zxTC_~M(vl7vcT}x?ESQfwgY5w?uIe8bF9KG|GXrpYGe<|gCLya%4ReIw1xue^tAP! zdL60v+1Pt+*{kUr-Z{zo*;#JP^UTs;#HNm;29vYS#dBh!e|=Tm8|Ea+qj<WrEtpVR z?O<IuCR!<4Axgt(<hJhoY;(YhMYCbhn_N1`$C-Ruy6i(oR;aTIeXRAyr}2gHy^sQ~ zpT6F<bX|_ZDBGAOs+t;l2w?{aL!*NUEo;-1ei;L{zkfD-EkkT_AvSxXv$N+WaORlO z<8peWRqOG=NvL(AL;%Ycoh6tRy<4EQww~EjGrun3_aDIWnj0jYf?#>m0{oFz&F$6# z&1S>saHxh-0Jf+suA%T0bxunDh!%LOTwco_J#DqI?0}$g!OuWA0&K1-b54?F`0sb< zQX@U}VqMd2qt<W2q);!uggDe0xP6qxCz%ERTEuC=gqV4sD{(KzLwz1?>vE^~v-b|j z#Z@ijVl}FB7wleKWBcl$>OTW{U3(fjI(z#-lF3_d9(Q)^#HegWJN8$&lOBHMo}7Gp zYFfXCzbJcYhNQ}IPB@5l>&-CZ^Oj-MW8;KW;j`<x?*?rzP_x6FIaj3alr!|eP=&US z=J9_f7cL*_aw`81y+A_0s@-}Y5?GQG!AZ|IY_&b^c7Km-=VzYz!P|fe^{t#6n8aeC zVWF@<qVR<;tYXx^i3Gnim#B;-ammd*C|#Loc^A2`rLJE1EJt3M$7JuyM-4Be7|P88 z3f0xwn$y9ihwT6WAOJ~3K~yqn0EqJ7a`YlgwcJkm97N8ml{X!u$eeJ9R|m27!Ug)o zc6$P4JuOPI?Ib7Nl3}u)M^qAG>YhB773UGGlWAzh0Y0`2`E)6_F!P6i(qAatD&1RQ z6w=|fcSa^2OwBATywDF(^u4u97v_;(_UAZmTh6TwJq8|PGujxNSSe#wGnA+dRR+%d z%>Qc{ipWCYTIxpI_r@4#DReA-<$I+izOZts?ZH$-!<pQ@r8JKAnHFC9(I)lfB6OFj zLMQZ(GgGNE?I3s$CHlk>T<y@1Hm~z$@ihq|ViAbl4b5g5^5{x)>)uF@I;+pYK|YtV zQPyfrjwgh%+&HdDj*;%k!CWt?VdU)|66p!$T5{qD*=Ms<C){}Khmf4Yh}Maf(<462 z^JK{X(4Kd9j#Y2ut7>IB)pn5~+QVIbU}|ONCx0=q2t-lgMiIB(+}hO2%G^NTjdgfX zGr6@lQXl0W%`Fr<epD#*ebVt(s;4q>rDLS@&e#`bK5CmeGc$3aE7g%$>1xVcg><(F zQ+%$Y5zCFXfkdJi_3G0tQ0!F1`xvt-4x~B`5W8f^=Qb!kAt|zVr9CfI%AKR|S@6<S z#2|jtmT!OuEu`LS>4n~J!)!^y)liuD;Ld|H*ZKxtYihdE@GAIsg^{&VS5q0Oz7rGg z{f+P3hKu-(rM|B{82DDA(lj%WIWq`==UupVmmzEFY&DDb0o&^N&TWW+=URpyee`p8 z-fO$qY8lstepKknVy2c`hv<8)K;h9#U5l7tHhgK~(Rl!?Ho$?Vw%jt3fk`d3W6_bu zjCviSb5X->a`rkZ?<HEAmSI4gtEA>yvEX{D_znYzY`M*+C@u9BpU;q#cF}p&p=Iqj z_;-JXRn?GEKF?>VCk`hE0X<K*LVD1yp7!n?0;!H;A=8sux^(C7?bo7Jjd$?$tnPCY zz3QN$d)|;2k)`%@;Fa4;rd0UJUwr4IxwW;WmX~JM#vUw8qGjp1)(b@V5-piaxqD}! z4Qv>CtF2PFv^F-gmbmmLk|rL#^K%VvUC1%;Nym-cM1rBAuBLTFQJ=fgizDMqKMZmU zO)bkfrqZZq(|GzO+c+lMMNSpf+SB2``)=7*07=MUpG8eipO<c{xx?!uiPyE-?t`<? zp;h4qF1D+X4cc`bc^oa1uwrEjEz9_mwuZH(uD*A23y+rWO*Q3VDH{c*sTHm1CqG%l zcYo)NvDA&Rv4$%>-&t9Eu+-Fw)Y`6rl?m3dq?pC;uptc2bfTVX_{R6~iuOY;w6gRw z-+PGg?=uaBSMI$x-yTIH3q1?XfbgeMk1m{tE4g9r7e+?T-D=|8Qet3@vCK*;F^LE9 zNY6qmYB^g6QYs4(OT&Yirl#?Bg1XYT2p3Kn2WC?1@H$SztxhgNj_<(AVFK^|ci6G4 zozlV_|JD8CG&%9@KMxE@oPuASn(Woh5}z$}X$?*J5H*_CE^yML<~7|D)>bFyoYlch zaQ{t7k6P*^+LLszHQO8&o2lH~PrmoTN0mlEo6gOQOw7##p}9%<%HyVM$n~@omWC=T zpRA-l7;AWRX=&=CnU%^=!$Q-8xf?u44c)u(NyAl04O-ExWEvJ)VgH)R!=By<BxpxF z<~!L=75w~OWCz0XGLEcruUpuarP$VlyQ`z3M!<T^`FWc7ReOiya}fMy{gTI__#~Ct z2#7l|zib3TDUD<ELGC<47ta-8d7ErWF_o5vGfmA{LG`@QGPSny=z|M){!wn>58i3{ zrLhO4``Bdneeic0+99f0>O`#h8gol6XU=p@d}-p7pDZPox_Umu5%Wvmx>s3RYnyq* zg->!-9ItEdW2G?)FjTH<rsvAsOroK!;a*?MGNzxKZT(DFL#`dwS#G|i%4FfYZSHLB zlW2hX6g6SjS}PuuSqRX}9ahWf=C#@@MXD1k$K+&FT-EsG_aFsH%~6+ZH*wWLC8!1f zQ*S0y3xl#J^*C)^No#HY_Q0gO+!lKy^STB&tszeTHLBlfGuk2WpiZ;+{Hi`B&u4Kk zmfLAxWo>QhN8k8p9*0TGFC~7oG`FxeR%v-*U>QjbJ*9>hMtaUYdT%DvcI&~?(AvO9 zOVc13UmI(inET37VdT<BH<ngj$aNx1tFLXPXA<&`j;6D%ptp>iiEKNiNp|3>Pt{#= zbeJt-8kojSlE$mDjyka1k*$<*^(EViVs|@V#h<H{lc%v4O}9T^wM{C1$~JkvTFUt8 z+wqQc1MpcXbuE1|wbC)tGw~>ic+b8x66lI(nLB!}+~`QXH}gu@otA6&7D}o2`uaZT z`(8swTcKkV<AaUHa%LX$xrT1M&r6k+wR6AmwOh1J<r-VL@W1k{k>)l-q@!bGBr(_1 z2!i7>NPvY^yxS|yaPf8`@cpApYweJ5uXNn&$Ah|vbmy)mx{*9gp`qBh;+OGA`QU?g zKuz0n^C0T1bhq>AI`i4SpoRbcgyp(Mc)z=>>SugU!N2>c=cLxhdP*L`aV}a?E9(8c zpt%0;Cwp_bbnlQ!PwaG4r&58_&v-PBE7-aNF|F~C96Mmja6jZbwyh%4HbkB)b06JW zNfdhim98J6UVGFux6<&+I?VZNxt<GcnWjf~`Wl+Pk(xy6>D)|iq3zm@KS_+`1{UtT zlqhwzJw}jZWgg;*r57gJvCZvi#-*FdoJVlaW^|p&L$qM$ak9^*P9uFajaL8p70W_) z{QMZpql(;r_$CLTo=&FVr3b_DU3F=8aE0t=wq$YiWn0VL=JQ*cL9~0Rr>iv659i2C zX($OKSYd6X>q~E3C|rBtT4khd<S$14Y~__7JzDvdOYbD!Sn7Fg9RcixriE$9gL(=b z4Lx0j+z;Nzq<9vy%TXLw9~E(>UCU$^zE{Wsx4)6YH@6zQRaAqUI`d4&xsex2)%G}J z7Ob6?nYKcqV`gQg6&w9bE4rEW_7yCmrY`-Xj?3_P2qt0XKCYZb4Kk`an#uXA^0PV* z{Gre4nKV<${?l)dm2TB%q=27KE;}qes~QKcBgRX8)9bU2*Yw1Ob$4o*oLHhHd!Q>P z59B)ea$0Lnt4_F2s)J;Wrz%7!psENtT*u%!v-Tk>zOVex7QVHXo5z7Mmr0!k#bYy| z|3aqnpzpo4zT6)Ii(VK=BcH703nOo3T9$^|eo*+zy^f#xprNa!<6b8+%5p2M$g}Oq zWf3n^#nXQ?eR|@_<XaWpt_wHWN?b~1faIT$Q=8dSx&=~IJM!aXJBdq9B3#qjXio<- z-}K-XN%u^PX#i65EfygQbk^ojTUdBh+CZ-6ehR^EC+LGiYi*ga%-SFpOOKW&8m?XA z(Sw24e$;X8jh;g8($uYAyK`sl)*IJmau1e9(PWOpFI#9?SUZzw=;--l_+I-ufzyAb zr|-{JDpv-^=9;d}eRQr9S0Ei#2ze@fkMSs)$Kd47h02RSYURdqJ?q%+77AyULEKqr z#UtifdAV`xu3{IwvNrHwsU>k4!BS~lH$|A}o)5mN)~%a-rV&2Q)Y)BNR6BoO9iXq` z;5)v(s{<rVJ&M$4@SlgS*5f%|bx<kQBjQhkQ9thmPEO7i)p#@<NhtVyG}1B3Aw9v1 zsUmD8jg`i8n_C3u;_0Se`m?W%RXYCOy$84MeNvh!EEQ6}(DFe;VrA$;p)z(8!sL68 zDv3vPYv-nBnr<|_wbn8SC&*vixzx6FWu|f#(E3D2t|ifqSfN4KlRCja*`Vjs9i#Zu zx}t~~-g6yXZjnTr&q^g4Qn^OF7S*=V+|}n-M<au$#YVQZuI*O`)!wV0hAHa!-Fo`Z z6U-`!in7{y93|VWWkaH6rEL`l^=4nbaj8tvu+onJ&BWBTrAK!Pr8|6~CG(w;e{G-) zkIhVKq%`zs;#%e0YvjsE)mxiem~9^#E3JrdKR3|vok}w}%8T%T{9E6d#k{qFsZ!sA zmBBW~yB%TYU4=`7AOTh?orovMqb$i4eu}WsGgT}mmXW)=nM;ch^39yI43Tsm_2}A4 z+tfN}-0SfFK4HdkiDp#y$M>Eiu*tD7xjM1ot&WbvT_kC#lKmgQO+GV$J2m?{m8p9Q zCbJ(+Sy#awp6!XcNXK+<S&^K89yq~UPCnx|*-dJzuV&MeaBMq^kYi0+701C-n<Z+C zGC<u=!ETmR&pN4(MTorR?~)~4DfQa^JhF_yU2*R^o2AGWKescGb>{wPD=gEK=9 zKA<wwHuG~6KL(=wEF=a4m7YcT7*e^muA!DZpqz~muC%w0s500ayRAvSJ)WL>h~^eM zT1B8_x_u&N!S~putEII{fUUiJ&#L}RXMTPMb^}JMgR7<7o5^N7`~B=7p|6bX<;JSN z(l*f0R|Kywm76H_&8%I>^{iq8oG8s@-dlL7<&B;ny)%@VnCKf>`qr=g#iOylp>;Hv zb1ieMB5Zu~Xk58aX}Hi)dg;#|wltq}S3Vl*Y4}zdAo<NIp=aeWC@EuILw&2QGkW06 zI5^jvHQ6S)xp1c8QN!A;j?%frNPm04t+%h>=AdlF|4Tw7UP<F&F*eeXyO5Y555DAb zD=0s9L{$engz7U!)3kOrU)9!+&kxq@KmYa=A|S0rH`S@#>Rh{ZUWxQX;I_|V<DC>0 z*JYvAiKr8GnH#4_Gxj!t$F%0;z>Toaiaw1sNVnTdk*4<a@iH2TM_>EiGB_c*Po`$( z3R7Z)J<uW`ldCXW&7JuuaT^!>owlAwT{k)=Mw(_ZJ4^MoeXVP#)Yh=n4wmnxY8ggK z#f6q!!!Y~|`8GPH+Ws!dw#v)42hcuNEQ@Bi+>@qOTbIhyD^c$Fd3T@r>CpqQ+!>{$ zN5P?oEpmEbmrS=Fljj4mxu+p~TGnL2_S?iMZQ_dNx{^o&sUm#oPVU0Q%0#JUrinr> zH}IX0h6Yl<cI7<=F7^GdF3i2~yO2<qZit$0a}00NddomAbd?H2YeQYRncG0|vo@1^ zEpzD))>Z~Gg_X9RrAMiThK9DG#Kc;r5&q(HQ>&=f+FKB8H#XCcM*2!;IwtO%OQlvJ zOiE>X7S_^m%VcqVFSieqsUM{}I+}siZ4*c5hP9m>A?uFo`TY%VQnT2qTehlgp7B)i z9nM4QdB~?YBZr2QL-=S~%SS%7lRMPoBquV~sw2LgBX{tr?2l847}Z{Vcf6xLUGF73 z%Cg;jUwg-AwUe@uY+>u03CYU6w|+7+wNxq$Ow27!B;m2`du3^67}AF`*Y0&J6h;w% zbLr7YN7oB&w-!d$nwr+mH2uN{OHH|4kI;jy^{usJI+o52g1*<5Ys4n*>2P@3c|Hfa zH+I$t#h(B2nLC(;t}oenHIjpeVYN4eepYzpHOhc;yVtF=8a^Lo>CT>iR|oi<9Q}06 zRa-91Gc~wrtuT+Hqv=!B)=TG7nbK=>XR1K2=i1>5W-jwfm630BtY!X<z6;k<|BwIT zCpW$|)^OpYmMmZrNlXTtmZ6<XtuzcYblv%xwb#D4FqdjMw=&W9+PUvNTB@v7hUj?k zDHz&qEoV}<G5_5h?4=7!nQ>scGD~OZ>6!U9Frs5OK}EbVwltNPspK)&TZdSv3c*Wr z%h_J|&cu+&+i2~y!c{Q;_5~}|p0=+#$z@7*yh4W(mZ}z$_k8^2heWI0<fGHr`08Z! zbf}ZA_QuJ_X-KE4G=BwzP}LeycC9PP!CIv{X(e;;LfeWd4xd5E!DrXSW9}jup0nil zG27{uOq|Bpc@YBRWuPcC=ayyx*PMqR_N&;uR?bxt9Umn+-dmY@u!?5q!r04TC4bP; z(3KlnIrGtNn9%yBhE`H5oj4r3R*|OF3;}NzHD(^|`cq}pj^}>5MTFX_^wO<;Xje;@ z?zA%HUNfBR&6J<u1kbPVdPhiK?zF`9BksEO(k|ICIocjp5p14V#-X=aFN|d2W0`Bm z3SedA%FtY45~;ns@WF+acRI#~mbw~NzA^9{E#H{vxHa}o@C;L5sm!cRWCqq|(IB4T zovue~iFMEug;ucVd%2Fk^)9BvQ&W`gTq{E3M`EE;8oKgcUumhdFxVaqZ37b{58lX3 z+*>L_A8=#l+;5EhNl(+-*h)*`la`h|#wHDkzNM*#wO)iviVkJtDgM$o)}b+cR`G2l zHG{pm?k=^b+Vf}~Th)@Cxm}&X;qZjmpB-lZ`M1XhLb7+Fs@;*EM!zISqQR<mQSo`b zpIU~C+GU2C&Fo>LogJy|PWg`2Y7f)eO1ExinI2(IcX3Hi>h>Z^{aj@pE$zMEdl#WL zm(Dy`n;M&1Md;s`&VA#J_O@}Exi+>mF^!gXF4y+P2j5!-ovv$YVB$){Di#_o>+s_? zOt($W=DEBfe00LOnr(w)o=sp`99_$3bJ7}_c@jruwXJQR?Tovup6X!5Rqgc(bqIrM z&tp{WAtR2jxZRk8q(<0Y?L3h3sQ)&8>ejXi&lMUHLy!6~y~;Gaj|a+ni)^K3rjVN} z^elX1>SwN;E9DAzD%bwr8z1~+8iJ61R7o4y*Cr3QvQ%yZ*f*WQLc`3NSMGgOSSdAi zwX~f3@8(D}^;Cw60BNPMT<Yk%cOQ*-$EA_VO2?NTO*OO>aoo>byEgVQ(ve%1rsn9I z2Acj}u7grLsy^EkmCZ(f?vvD+GTPc^1gTbL;q<N!RC~{;f>Vy$<HQYnhiAPaaQ8Zj z8Q<xVeqDg%=Qg>ATiW9v@D#Q9b1i6c;^?bOgsWe_q%|T}HOrOCKEkjrUoF|27VXC& zCkJlj{Tr{`dIpM6;uYp5vBCS~I}>w-3$HPai(?gk@0XE&z(QNsQY%s$X4Yo9GAo%Y z9rsq@qP)@fPD5MU&<9HcBZXeD+Ach3gu3cU$g)|Nv|}>#RO@9i%iEpft+(8~N`0u( zuCMw~jC44*e*TQ?gz$a;qEB}oKIxvpvYrEZxUYYHOqF|*`7(avbwppUBQ0X{z^X#l z)3kDFY2n<4Nmp3t>R866d!(@Nl}Gm`8g3Q7^6%Xk82P_V{3U9vo>b4Jv3|sRkFm10 z44dB*3FTbk7rI(rdLO)<#JQnT-^e><816RlkeisGxpQ3|Yd4nO8JN2WZ|^2V@-cLq z8<*}9O@)^-Ge0^r($H2!DEtONu+WvOoSVd<pKbfRJZy(^pFFtKHwqlrrn)YSmCoaU zt7;Cn>cFXStnRC75d_tV;$>Gmkp1W19yV)FQGaz)J)db)>x9thfokq>D4m?F_0l7P zUtKu&p|s|hv%Gs-oMQK#((TpF29v#Z^>AD5=x0iIKEoELR7C_!X=Y{-2j8tnOAnG* zN_?`gQdnuYcB!YMYbG_&2rAr#?{us@Sb1Y?;Y&kpr3Y&bmBLIr4znLLEuBTnk%vp; zUQ-{OX=!R0I@5~TUSbtD{l0&~R>hTWE$h{R$>^D_V$TO%9J#`iE!?Q?N~&sBX!|$9 zu7YoOKcDW%smq<VyE=L(Jw2L=ZEcwCOl(uj_<IBM?eEjK){Nb1qUGGoz)}^N#-+rg zk+#&rgSM3icOG5)vv+>&Yg2`$xpU)S9adUa!DzV;Qt?LOzR|&*S-Ld$+LyjIi%|B$ zgG+6H4Dw)0WhS>$DBOi`yyeQ;J1eOhEptt4Y3z|N<O*k2N_U2GOE+$V8QF3kQSsj! z1YxC=7;A^jJJD8}#(cQ2NlBjgN!Lr{aa5K;?@J7$V>oPakGsaC#)olg5O!*^w?m++ zmky_Ux3wA{#bIq#?Uj?KRu1-5{mIF??(`nFPGX%NS;S6R(dtujpZ%Y8Hnw97R^3-g zj)*UZNyJr*%d73MzpykhHnX&d5TU7tj+U_pg_XI5hF^Q-rL~2hJj%?KaV#w?RBjbs z`RJX$@dA(DT4@{k<X-8_g@$)lI&O^RmJv$WR%&W(0iJ0%f74La7Yc3dP}rw?664J# zu(fSgo`74)4sE&I!wfv3xw0c>@-v&ctZ7y1=Ro_um?vdfy7M`^6yGO;$l<m8gsyKy z^P0!cP(&QSMr59a!mSx~Pfx?jLaMK)rKuTv*S3z1+?lSPb~rbF^61RB3Ns6D^h~|< zMI_D6Ro;8yt#=l3OBV`hq=Y0fbKK;9-22{xkN)gCm;S-P!cuN&EpZj?Wo~XHwbGX8 zVWw$;v87C<X{u}GM}0!AN$%WR-&;t1ibnf?a~(+ehoHi2(%g6*-{XVAB(CW!D%B$9 zjj7bRnJlKqS%5kgTl{>rGubWopz{tMdG%?ByEDA%Alv1X>@Ka2=6k7nPIA3udA}gt zw<;ZPMo;tZl3E4+sUy$<df+th>XebKeq*in06TUAraPwm!<IMM3MO{BBvs@p*zO|> zb5nCuQ_IMxTB7F*XC8yBm*S0cO)K}2=&;b&HZWHCR?nT(xzftNk^0W>{Sm28N*DUh zv=we~5jX6_nwab@VsWzBGk0_~<F{T%h1!TM?ta2)xi<xV{%zA8NwK5R^OHMySK#t= zIIiRMdA@|pgU6}QcsHx9*rlG(xI5Y|w%;M$zSkR2#8XR~Z*BH@<oWi3B)oEIp{che zj^u`cZ`$Y-8_o?3l&&PMwSD1BOTYKRCl8u_^xynR=|S#n>m$xwNqzFYsmz70m5cDv zZVZhhh01$#SuC@z{4bjNawC1;d*k10Xj>V_d%Bt2-N-zctIQ)JYU!n0kNU=51eBr* z`bzH6+O0pz{n^x)URhb1n`n5@iHw&-*ZX)7XEN*UsAw5%oee^NXl`WbK~F;=wIr4{ z$xhT{vYo}2C*f23<CCgJs$x&Mxqrt$T~FH?pBgJxy?^3!u=m}=vi=*_x24BZG@lWZ zd@e(6w~9!PV#$ve5_NON^u+O-9*AKNO?2s=qO96!Z-o{AIYGX#wlpyb0M)|CZCu_j zC1%D}N(<M1t}qVVXRc-GLepF8XfxisM5S-)t%X!mVWHvDAQXYOejzjUmG6wV$+G8i ziEc1PxY3i#16jUFrpV)WYs9f&PZ3w_k!;dE^U?Dov^*GJCnq$b*@;)Ts*RR8Y<1Hb zdPlWY|D`+id%B}2to9z&&p_2GvQRdM-=+d=Ze1&Lsiw>_AWUs7BdNZ55MDMxN6P?; zUi;FW(z(hf4TYcimH#vL><vvtWVrRWqLP`Fa;vf{RN7V+&fJ(eb7`vYD}`HgOFuW& z@XGIG7D_E^iB3GE?=TF1_1M@z*9UWnnF|A{RMXT%XPcigm;0650JZ+88zGSkL*geN z#f-D58xN|rreZ4=U-}L+4W*VO@UMlnM4`2fiGRLFvy+BVN1NNaQPk(K_1hWpM#ooe zeU<x+J2l~m$$>gAIgw7A*2aP-HR99LX>9f1Uu{C}VZD~tkXVkTC+QLTVBg8R?-{A~ z8sbxk`)o@eskV}iO_lM~NUcIzFgA&XcI?reh0={Hx9(MDX4ZN-K3J%%v?D2^;~NcE z9t@53477~B^PnNIwlLK(z$mg@hZ0Mrfgd8zKJi6JT`Q%&RLd7KrM{su)*5A0KhLsd zdyPfV&5mRT=?+}7s$DxACb1j`3np8~$MZ%x+3}5(dw!kj<Ur(EG5LJ^>?V06d#=ty z(_;}?5lQ5(HDYxjCVta!rxfU!OLaWz#i6*_k#44BzgD_&;gz12zCZY9uf4U1L-wT& zNpa>{p>JYt7W&6^kZ?8?WrqNc-Kq4=OthVw;iU)fV+`@+h0df#D!H{YrNYvUnUzbo zI@W<d7-$<SWkzu%&2i(sFR}8VqaWMbG``-I_c}4%%d9OlBg1%8zfVmJ6q-6NH8f&k zx{`)=Fsbo<RVQgI^>g^-ugKdD7a=<W=6dSz@lloRZF)~J@=h%t98h(W8r)Mo+NrKG ztOF8#4nq2DXG%|Gq9q4dtHZi9-4Vjp`H%NgWS<YsP3^F8I&8!#mARQhX%Vcw2TL8V z%-orKw2Cmo%+grfuQXNetbH`qHL;MG_{t|g`KSsyUKQyZQ!6bj^<_#eD?Ja+6}oyV zD>u4W>*_@kZKfZyEP<YShPB%}!k$-F#oi)gUyxkwO<|8MX!Sz*WJd?MUnwR#gy!eS zqT^_QYDYy`9YJraJ%&eez<zn27ClWP*I~AL!kw;VmI@smrI}pYO2bIQz|7Kxu?It` zwKJ8{y}p^;M+2#UH1Va>*Ae}=)|UE<zNWS-m4<tBrJ=P&P?HM{1A}NPH;?U(Z+yhq zSmi4{->V|vryWXwo{l*#U0e9*(%6FsuZ@%u-q|*BDYvxJGS!lro8y(tnU$55X7CzQ zZ(aLf;Lb{+VX=KB8u1z~V;`OPD3`gg)QCL6wZzC^$9-Mxj5x|8nMzV)AN@T0^H{BE zXEFC#fb;ImYPSxoPMW{#=YYpMx$2-}Pihal>NEU}XO`|WHZt8w;(GpPe+rQ8v}U`1 z`?*!C_6Q;S$kX%)TACcV1Ja|T@Cn$H!~lLHU0zuzRTlA)k&(8at30YKBUND|MeTX* zTH<R<=UP^Q0YB69!nZyd$s)QY*VXjV50)W_E!`Pgh96{RAwyHCAL(fuXh5#16Hz;9 zyjG3KwXh>F*^h)Sx3jV|KDV;Z&3;-GsHv8Bzvc$pQ0>i*tIte%o)dMm7|ria5qE*7 z_Q)*Nw)xHWYK(GkEwVe&ZE8x*O=Z^cw>N;Ng-p``b4_i1XBK*T9;N!$&fI9|`B`{c zay>7-aOOttcd;{HhH3w$pH$9FG@W^=g}$@!bv$jRXA+h7MiNuGpW|HHnaW!?ex-<2 z!rTYBft71R3C3Z9T$#(<`_fD%app=}q3=qea$%v8`Q#4~N&0g=S1KzH9(A;lXpsN_ zAOJ~3K~$vXIzCAi79J$wFe>6b5cq_KDxkG1i}+X`VE@gmuQ>31)dN53{9;KhEUv04 zs}3CfRV_)bZWEduF@<-HUDX~2M(rwDtr+=~yyUP&O>3Iy<lxA8S~{eAf0FJkA?#V0 z>}dB60j0JFTXxW@r3aZF>0V~UVOBsH=~Yz_D-wy-w%VH*$8sYvkht~1qdbt}E3bVK z5c2OVjV<I}E5j0X>j$4a$|5tOg?GL)*OAMtJy^*Mr3yWtoQJF-(GBDf*IHVd+FBaX zQns{|q0`%!2XDA4tN1N9QI6U6oi29{t>h?Wx7@M`cQI)DkHIH6>*n2B@2x@(FYs#n zb$8#_%~4w%On0gsh$+u;hsP#%+ga1IUNO-Rpi&aE+J?TBH0EYqg<BnkL4X~)h8jUJ z97?@#tEZ>$ogdt|+t$mQuTgqyq$lx%M`O7wO&xheyKnZpD{~WT7cniH`e(2GPa0Ay zT@R*SXh|)@iLsQZtPM=el(KLbmpaxiyh3If?e0>`8>L4~Oe9L*7@O<)dvA3NR9YTo z9`ub|1iP=ah)4DYJGKalcp{02)V0!L>&!{F8}#JhVdk@Nyt+glf8`^GAI<mm@X3KH zsyg!6o}yzNM-NrCZrnO~v6GZ72l~Qe?A87(vVM)d-xlxs0;&T+PI6$e-v#EBad1{A zfCSG2_VU2g_5_!In*NoRM%GrQX0bbcBH_OCpljsOOeygn41AD!X(V$kv6OqKrC|_3 zl9_>~<u-tlp^mOQ%=Hzzrc#NasRgmYYe*s*rxiEqhQYTEH)XbsINvu2?bb_aT;z64 zYIU@oeH)pB-QBKkuG-tb?XRTDtzm6nDY|QcOLx{B<(>~9+4Ffk{k+*;8&n>k5}v+V z9(75g6|S3^p3KZdqKh&>p%11CnXbO?BV6j0TcuYzn(>)aslJYX=XV~Q`O#2mqOuO@ z&<k^G9d{udf4ajmnIb&gO%uP7`&OmlFJ73-@VEY(i7ME5o2lShrmvw=%B>{UrYiSJ zxmzun7w)|FPkv?XK||X@Dzork{mLu9@xga~X%)v?(@0_I!dt1CGYee{3yGyfBe>xk zeAbnQsmdyLz*Q_ZQVv+%K6B-qLeE!rqso#4ut9a8=1Z%ZL1LXvtRA_pX33$(F62Nn zfB4nY6GFt(qLlQY-rH||cQ>uOt?+)kTAg@u9M`!|&V_pTWwO)$Jx``~YvEM{(LBwE z7S;+Y3lnRVEOvIOxwV<XTA|@94NH$M{y)OrbVrixOw)VLvDNojG9#DDT1YmldpO+$ zgpnXXXx)P-0h)oXrfx%bq2Iaz&5w~VXo2c(lFeeRnUS&hZo8eMANINVvBi@tpjcIz z5$WM(e(YP`_jx&rRJIu|D|{kC-t!}t1qzQ@o5gy^N=Uw@CB;?b0{+g3Sm6=ziWNRj z94MJFMYn-6CrU{zZ~@ygSWa52GZ{}g$BXGr>1@w<t~>b}E-;|)wKPs^Y@KRxh@-X6 z{`{o)uiLLbUo9gwpzVoex^0#(trzXDn|6lTEPF~(J_cwq=EQ_lq?DxWffZAxOxd#) z0F$ERija&2dki4~zaZl+F*#rG70;}sX0lBA2}{H?3D<mq&$GO$=F`^5-9t(WHtcw0 z#&3DWoExSr35CAm%kfsS5PUuIvow>p2VRg7@DrLRG7LWf5g9l9i8n0qiI`)^$w_$6 zmXCZ%z<rA*Yp(aR<ja*3pQNaz<y8a%()C%B$o#m%kjVqzw)=D2_6q&ldq~l=gWqXK zJlM8f+jhrZAekHeBWY~yaA`NIdRQhdgm~)<+uE^3hPsFz?M8t28(G!)r%UO#ebv7j zZDIAuKH9r5p>i+KNe<C8QcX)(LQDLx81^#R$Vo}$YZD#`@YuECkTWzlGD@yE;BjC_ zz)H0B*X%LO<mq4Y6FcV2iMi&HiWkDm^F+~=Fel=XE9OkG%q3rJ$DAp5q+DZ(SYjnX zB9NV6H@V7_R_}b!yV-bpB$%r2;MTq97&QW<usv*r>fG$E^w_kmYgf*$JIo=~!tKof zb*>!+qH(1Arz+ETQYvae%zLt>Z(JKwc@Rv<m<bSmLP|tNN{o?tYx9t*_{cRc$f)@} z8FSvSB1H2Gz7sig^Znma^Eqogo;ix=+{X}bMT*a7SZY=r`HVGN0`YxZ@fV&X`?z6i zHwwd?Nc<QnNABb&^9eYxAY{j{P+U{-$ltLiXHG7#j5SBDxf1o?8@Y2m;E5ad7_O<w zD7nE>;mcxUOU{Cdg<RLU9Lu?c(fb2;zZ$9hE{j_1K;MVck*QIjK`(5mKPogMQ@2ab zg#I6GFUW#*FAzvym@DcF&8zMlcwVQ*>wXVCiYyrO?u}ti&ofQ!*zGS1s;%Sl)UAd| zQBhC}Y1RlnzR^zQr0l2!8~MN!GXgdeMm;5E!W~D+c|9;?&x%-X%!nBeGJ}Z-S!1a= z;uDY(QF2X&Pau=5#uTL_Ht@)dIhv4rrsRBy53I1<h@CAE@aTzF40IMowv&iqJMXx4 z6mw&mPdk!>Jf!aoG5+U0Zr|hUUBpiI5eL2#g4EyFpGn%b{WteoHu}0!KB~>3QV_|( zTSzKNQxP`TP!W^M(xwo>uZLpJk$2Q2q?EuPcw)*w@{VV|m+0;$YojJ6B;ml$Q6wzL zcqXO9<C>5OA9xF_fRZI1&m>~43#E>WsMrxQ!IDvNM5C$DLfp%kkg($mDy9_d_*+U6 z<``-+6bF9hniX%@v0){%!w^ltHCqX(x?(A_#YV@lWzVhnG(vKGA$>Oj=cv;~*-@d& zc#b2z2k4OEHzYS(x8fTHdXMv$hhKi_8;_~?w6h%11HGO4tG){c|JL2=azU@B{tDh| ztkLbiF5Ol-t{`TpOFSO|XJ11>F82Rgj<gJgAtz-^D!|AmwjBA4f@cacRs;lmMMeW1 zRV*=-%rU$oXNu-~ax(V3VTzLPH78^TB)r73m8LC3Q?eu`q+m$_SW3y!@F{p97}B`K z1NbuE^F-(8ii?MGP)!#Gu38A`Fhd#VdfCpL)ef!#J=leDRwqNp?%2S^>j|ArN1$VS z@*H}~LbvWXS?K~v2eXs}1k^H>Q}TB!!;OXfq_s>Sn=Ip;8qF089`QNy3PZ(#9bfS^ z{|A;g9Dyq;0Y@dwD9Cu02&(2C+yhe{xZ$68fsd6+uHe8GAs@-agAoy;m<j#4Ii^yX z<<6v{-*79L8Cwo~E?esld`6CkVM<0yM9g<w^NN=|U}bWA<XUongVxcJ$r`BH3Rf(d zkmAWftA%OZgdb@cpbQZN{YP0JcA^HyS>HuA2F<hWNTWk2>4Kl_$d}rY-`V{tJ)!|r zBQ)djB#&`Yiym!Gy1;lf;s@?mWJ8~a?J)Z;1G8*zjbaCfy>T20w1fW!D^!ce(GXCO zl5*fEzdt1-=L$`Vr6y!S#T^@RrdUeu*by=3z$+>YhBbyQDYqQB;gN!f8pA<SwPqx& zIAEA?kTJ3^+gC%x6%!QC5>9wUjYlM{&j}XkOLKl6u6HA;RJ)uoj$1<wqFsjXtnG9S z-SB7CgG{Loak&dSP(Aa~X-BG^gGzT^xou7HCozk0IDhA=uWEVIs@+~|nQ;2lg5}Dw zOe9`1kN|kgl!K(wl@x?{lw@e(+3i@c!^aY1`4==lk?<S-mj7M+xl?L3+*0#1GZs7$ zaKj8kA+InY;1-WLFL|J5M*-Y$z+zdm<cVhy`JCJ}M>LtB3r^ig#50Bo4@|hjV?)Rv zDEaSL@&UzrNo3hGCufZz;=fY!TmC)&O3oE~N>bjEGi3{ec;u4p8<P-`OTJ0m*6Ee3 zXbLJSp~&=?9Q_clVK3QTe%TA{aUWyWx<me#Z-yxm<E*=3XKj1?*Z#udvLHo$5*^g_ zbzIUGsu8m8r|5d)6GH}bK|R`(>rtPvb;J?02cKzmZ50`oKom_DpAf^2XP((p$&R?9 zVv8o?$W~YvH#~DKu`UrIDT<0$tf<i(cxHnprU|t+B;-sm959slyb~WsND54O;WU^L z6JC)~u;Pkm-iY&bA{Cy;q;nm-ks(Ll?r(cOZ`EPBT*^=xZsT>wi+O&?>2^!2dJ0qJ z<hhO21TIbH?5R;LOhLD?M}2p7|3Tio0^b3a251VIy5&TIjL-t6QbZJ_)J*ZDde;<8 zu+;c?tk4)9_#0mGe^QD!L`ln9^PV}kD7F}K9$OIDN&nf%W*WNjk9<YQmWY?EsCXoo zgQ&UdVj^+^0+!?)FtRQwB$TvZLBJF60mWCuJO~eFA--jg4L$({&%7aGFGG+m6Eb`r z@u~5d5a3Zt0&jv60evN(kN_iborX}v)Ao~8Ox2N`4XtSX6|L>a)W!rdGUL0HnbFUe z7<Sa3#&goSJ=G^oY<9$?rbpboZquwr9U%S4@qX>ueSO-=Sm@6e?Fc`R5oFT&*z?*C zYLg<GiE7VE1fylkhMmxL&tB0TKJdVSfEk*IkRt~&77~Bhu-FO=hDUNHEU55_c?2?M zGN;*d!!P;3mR$D86LN_NY+&k*iG0a~6$dJQEf|A|$fASx&^n{$yPO%_!voP-3D6k{ zsE+W+4n`s8VDaH3)&-54-u*Pp0Ay4RYI}SboTKlYCg=%*^xXM)4k6pNlg%bVClpW< z!kmjJBmlIb=4AM?x{3tv&_LKrN+vw95^scNNy$GD@XTv&_?i_VDL%JciQ+TBW5O22 zdrqN@zPyeKSnvk|Aj5FOk$YZpMDt8egOv?L1RqjjxM9nYgt-6<QW>XcQh}2N9C$%Y zz)>pOfT<*;HL;Q<DeqYDL3Xh@9*RsrQ<_+S7f`oh=~fW_O27}bpxIQ9TR9ZU^(Vcm z*VYb4AL9>d_kZ61{LL`!jNZ5FL)hKn;c&>WM`(EJuPg}0R}(|nsP4&z^+kw+y%-wZ z#S!R%9Z!!=33V^Y&31+Zx^16yM>%$s=;mhp?qfOdz?L0p3-K%^qu`b!2P!-=z9M4H zktI_m)Wm4EOb9ptIR_NK<Wc5Gdn%UXXtq@Nyy2Q}DOeHX0gqTFRBV}IF-*}!RA>q! zz9e9RCs}B|OvQX>66;o4L(!A&ZQB{FIL6$)!=0SIAf4|M8Qn?WWlCgs;-?)<mLZS^ z9_Rbm7M5ik)tqjx@5$HE03eJUC+8zAqQGN9gilE+@1GVSNyQEypA7+z%sB9hjGCAo zZ}~60=a2j-;*wYVnh768`MG4lJu?hHi3am@^v}e4_>zy@u}4v{<U8@$mV8U$xTq~L z6&V>To~bd+IAS<Rbynj^$Uq}H-g8UL3xOhpOz}C$d$4Ch&YrJ`WriHEVGbO)W=g=E zfC?zc1e0*eW-~+>0bf*7D_=TGhHeZsdKg~LR2x1_Pxqs5N2J4c<aV<?k#fK7RU^2- zzOU|6l43^vy>_q%R3jXQF?Nf(Kmr__C+Z8j@UbS%4ju&B5pk%F$Tk;qS~J(EQ6xMH zfv=L$Z}Z#>ctJu=BDb=~y)aw?3PK7(u9%Z?#|De#EkCdTathJQzvV|le7;8U7YtW~ zD0Wm#i1A1zduvL>4JmVCB4*skvSm&nZJ#$t+UtsXy<R$otETehs5sBh?8ud}gJqfO z&0;S@iEK}}(8bc*!JVkuXv?!k@LVx^lE7=)X{K(Og36IK(;D*|`ES*!4I6n-DdHDP zlbo9}Aw+Y;=ZYtCZa8v>Wy1&Fk&<#v$tx89E&rK<dmeZ|@iqUBKT>eRgoxK9G8XYf zN@FNVMLM%1lyHD0F*9;3G1n|9sPKfF7~&I=un=F1&kjXFEvCDYl#~fG9`IRG@PUv! zZmC%_q2MKR{*{WFkTnS*Kl8U7$v84avt}XsQ=f`JCX&r0vl7qA$p8=vQuVwB)UB#- zJV8gd#fJGS_yKy8WjpHl^?vuw*s53!&RO?)Z7w7jb&*bEc+T<X_!x!HJ!bWVDssFw z9BbCy55Rcnc$d&JR4b}MBd(kf>a(oZ^=&lIJ5N4VhJp<b>;*Ulu?QTN7*b01Y?(2W z03d@;#+;N{y9q0(D1_l=@hRk{c#`JJ@K&tyQxqY4g4TBuiPUx}^4nXM%w+65WiE+p zVP|TpJM&iE9!Y2RT{l8pI}Zdq7&>$nliJZdj`3Rh7!2qWn;C%}Fv`p^7b^wNfzaA^ zkL?LCvyU~fE#UqXPXvaX5D!I2g(Aa~#&;?)j+qSdGl>SbgsiE#rr=0S%nkoHj}(NY z#Ju5We&UJ`_(Z%RK(Xa#o-txx3<*Tjr}&W-&xE`p;f@%Xk)yEuAh6WtwI(b{iQAoi z&Xkf;rq2;q?C_Y&<Siy+$zIHPTQnYXJPIti&{s4*ulW-TG7@19y<j1S9%v#`f#x=~ z_7p`<D1n|0rcHNX5@TMC^WNPXkt$ze^4U+q=>79FgOO<8;l{-N6%Sm0Iig3Ozl>0` zhGsT(@ks9C1jgDhJ6LwuLFo9{QpaAXL{;b4jH4#gV#ziGEQ68ll_&1S9Z&2@$tan~ zP2h3G9K!<@89pzm$tlI^b45Y~9GL?dnl1174-}%FpR!|v!Q%_QC*%qbpP%?886PQt zJ)u;Va}sJMViBJ+#SjA#Ga?BSavuzy)JEOSy6P$Ab=wf0_q=+9m%`vZc9oC)_xU`Q zwmnPXSe%gU?A-f{CwHxO`o4AhXV+2zDG7_L<?u6{B0!B3g?V~S9=6Sv4e-ga1mciU zWEd1P4oo-@5%Wkyg<(TLz?Ohpp3uDI&ln0S{sTv*<Y*EsF@^&#@BxnpLSAskPl63N z{ge?mXhM8aZt!F#XURG8id)J5olsG+z{6(&BxI=eSk7eFlA;MIc_d^_$qOPhHAk*F z@&zAJ9EgbUsCj126HxIR{yp!Rv8Cbzuf-$mQHwkLgtjQ;{ne7x;mNAUHRJWGyslRG zG(oRBCQ18A-JTf@JOl9lr*D+wZnHgIlp1h0`jwj<^+F6G?Bk8&rD5c7?x;TrroT+- z(*r0K7ff)&*QM#_%k_Wu^M~VXusZK{b81Pauc^p5a$rY7O2~>iDIOuWEO_Kw49`rM zFr^@5kH;%&Vrmk6rsR}_d}P8a{=zk}yx$)Qc+Hk;mRK}vD%M=FB_R{`ie)0A(y7?d z?@;(m&`g=(5jfK16Uhzy4$1DkAn-azn(0t<bgPrQ6kx?D=uEYZsOl^)#+=a%dX8aj zX*<pyI&<Hkt>@gFHRWKgRA*}I?&=XycQX%duCB)vsEJ#Kp%w;?M?s8YLxdsVK*EYW z@VNly*C@Va!{763HuxMU1+TEeW6GVlUSUZhs|X83M3fw5^FL+Ikt=3+{LD2TQ=X;u z42h^^*IO{d5Rs6Iwam({*&~)1bV#YOWOx#Xo>5T}V7SF+B7`4>;lK?Oo~ZCR@Fj5D zW@;=j<g)2Sf|?p7!bD7oCu$(QM`bs6&9)q%Kprg&EEBe;%Y^ZuqIN@I2P+FZYU<)b z(K!B%yL%VgsTpi%ZPK5fsS#wTy--<x%KSFm#Ht>(Pmf?q#{f1i4Ng1O^|K__wj4gc zeNt%*WlQ)lLR(HrN$^;TK*b{<W=cs$LN0Y!&RTM9E%(%Dd=5P0b7ab|IkI5NfeOnb zFS!AhglssHkfYhL;WaUlaO6CaHs-o)Z|<0dfaYtdeYEThPsczl;gaVSPIE2JVL_$? zXFV-<Oy}6?-$Ax>OVdl)P#uP!ZV4u?E3^5TbZ4q#E*RZyC*IN}?Raelgx}(<=uIT8 z_`E}**3_+IxnWp0%sY>S8ejIhkpw9=PLC<iz?C$5rw{;mPssnr8(xv3$uRtdn1Ts4 z1qOH|px^^F6){)b@*8GSlUfQA5^9coz+%~>IFOQ{$Vjog71DM<3hc>A#9Oih0tv}% zbf1A_uoUE!cw`iWJoAD-6Hww2vgN>@n22{Qsi?_$<}bjCBYS=+N`;wx3KD9ms;vZd z`eKAM!l$g-I?Xuugb}Yx-MXdEHbc|73+%{W#z}ioo$!Lr3ik3gq#w|$MsM8n3pJw$ zRQI2aV{)RW^zTmuyGNY;1g&9wL2?OLK4z2C1BxD_0Yz$%8fV1F?~6~;w7;uijRmsC z->~L^2T}}EVi0O?B$3^UoUeIJ#WM*tH7oWcvLTGgNI}r?HB&4eGZHfPzza$B3W%6; z<OLNU_*`HHl^nz|zO>jra7`#)UUxZAw?dSYExx3Z1Hy0N0^Vq?c9Qg%fw065oYii% zI1B?-y*B222N*$6m2JL*?eP4pV{dc6zjA;FN(hJ+Ek{?~erMA`V`*uNt$-my(W5no zUnLo_3ZDfYsYpLgCc&o0Ntv)EBO+zW3l?PTF-%GD`8U4cnmNDbzwn>=6aT>9kYG{d zD0VFQnS$@xQ1P12nc`uos3=Ius03M`3z{M074M{*@KD_FhB*^~%Y!1Jq~=N<$U&=R zEQlqOFOcSUPt1GX@+|=q5@vwqnI#@q+;L<}%7z~anD7HL9#A|}im2P5C8nyN!U%c` zLE8s%C6z-$G|pfBuy89lU)uv480YKz8b0F)#(KOzHR95+oiD!3$8sZpjibA_gWGrf zD;b^Y*-@#&7{Pa3ws_gntVSHWV}VM#;|SKhAjvUnlWN@pM*g`J`6$#RY}v8lS?aMs zK5#V)t_jHqh$JGq(OxCY(BzVNQL`aMQ*wg=UIGbM6r@-dL}=~>Jy~)@$!782xaI{j zmPGjYgiM(b;7jv!{z=b9@zZ3_$d0q-97JT=jjwf@d+jVe+;^>ypB>xXyAE76`sl(D zqNtwu$i16ff{u1p8hY>`HFAWVa^p^W>!5A$UC6%Y%%m&%+GeZjiC3oiF13U*HM`4( zU0sn765;a>O~D-j2PQ1=c~45gl!|-au;7pUJ-_8ke$RXUN0wr3tNFk)F*jJIlxS{= z<nB2Io%_T<#SxDkIR#Tn44Pl_1yGZtMV(hk^|!`jLQK-yF)NWOpJu5ER^Vs}ulN^k zNcoW~ET8d_D^^$*{KWUXXHU-O{3Di>J$u&F#BFxnlo=&86A=tG&X<ykN;;6b{mV*o zPEUk%`ed1wuhXyajdKo}5d<Bb9>wDQ?i<xfQc$Buf*tHi>}aHizKD0C*U76<Q2lUw zYC9G&-IJEs3*Tofdv^#??YGzGhFtgF6E&h0tK}d#*NdlkLxm+LLlXfxI}WVbuqPv6 z#R5%%Va6+BYEtg;#2B8jlTGil)C`tT{2w!>JQ6b}CFhR!Bm~TvvnRzz^F&R|gn){Q z9TQ;$*F>yj<nD9L6wOpPER7f<Y>xpeSjkh)M%cEx&S==VntH^?F*XU-gG7x>dQ06g zCn+Zf&D|WkQ#tMM6uovrczVU>SF2@*sAN2DWgdBoMN|V$j~$5Ots{A%g&x$ROe;{- z?QzmztTYE=Y926@e8duvb0p$`=E#&4ihD}tWR!fzZ~1rpfq<MRLhf1NbIU!i`B56% z^ZE&{g|-(;-!YNZN5Kt0@*oxdiVB#Kl94b0QW4+O(tng(QBo1(Q&FO+h<M}$@E!jH z{|_}Q9+{BymXafH$l3A8OWv>`V~Qe^gJ{`C(OV8I&=OSc6H`dl8g!HZV^THuwD9E< z#$b)uAJf?$q@qu50q=L;=nFjIu_teTS!@S5pmCcRgA|JK(exMrW~>Psh8+zvmxj5x zmnG8OMtE$m+)dR$Zgs!?Ae{0vl<WozVe=M}ZF^wvq_9GT#}z9K1#5o9QgC2HOhv*i zg*@MsNDo4MZg~$(iCGg<@`&MzDQilOT=9_uGbXIa$*3iIypm?MNrj!T6a?n2V`v&y zI?ccPZF}p=PQ0Fc#CBAFx<jcMkBjQYtk;8x0n;Hq4~HA}f(KhUjkfLSQP05A^Y8Rp z<C-@*?4dL3J1Zc3;gTrIR=HRU4JAR#ysrt&j6}Fz5^|tMvt`GegpbTwP*9+lk#OWa z-*F8T9QhR<Grl6CAZYi-J`e1A$+MP<lmN>UcT_|~Bp51QQ}B_B0JtR+W#<e?_?ia_ zxkr6jYs9VFqrl=(3Dn|q?%D9)^Uq}b5}%ZWTlTEEB_t-{nm4@1XT}}{6fBrX@8EM_ zCgK(AR9-bRdG}0*mTd=vHr3YL?wx0P8v{nW$o>YRxevVGee<bm&t7DiT-yGQVWGx! zT<Wi43C0xp{jyb!T2hAmKrZO}#+Mpn@!`6iZy~dW#yC{B_H`(+!Y-~vKV>=a#7{hu zaKL9FaX!s{>xzgYdo~=%@JPs56Enx~Ec@U}ytgo6$pM&QNLY}PGa<s`XFMi6k`eHN zilaC=3w%nZ?D2`YMl<0GP0SUVDW?EQL)`L-m856sR^V}_5TBb&(|b&)5sslAos?dF z(4B8uzdfeBp2ZF5q^fi`?7A0J=}A~p_gWg6#v=R~q{_8zUFyORJm;bAwFkSAYu=#K zJvl@h)KV?-rI4DSogD@u?no(^vnC}+v1P$bk}!P!Oo7K8dp3OJo;$AD5%J&fB`Xf> zu>|boYfm5jnu3&=keIbZ8~J!VpqSwEXO1F~KN5<wHsFzv85;~Ua`|beXkL;r6`D{i z^qvZsa!<)So;k82qUOj8JoZcpfbWR;CExNn8p{+-$(*U&=>-unJ7NsA?2ip4lh%w$ z2W2+g+p5k%Hynkik(nT)3eZ--JmlWlo=?M$?rH5sF^TFOK4XL}J3zaS#faFELx>B$ z;-N!hT-MsRzKt2CE}3Rduf3v{d4-j^l9e_`OGbnxBPZj5d!9(7`kIJe_ejA)j<AMr zyWk_KWT{?rU@jBBX7;g=HA#ix6&bML9*>BU@9>$j<C#KX5#Im+AOJ~3K~xvQUh~-z z2=<_6Mv2FQ3eAL=kbszg6)}@`r+GT6y!P1fTeIC+UZNe?Q(rXX=Bl~a0Nr~hwUdPB z1^|pebHB^z4o05V1KjNyybD4m&pD*L&O_JhRrqvLbi1KM+WAS{%sACbFHYaJsh$iW zib5>+jS^0Y8>ivnG^9R-M@mJ3$Bu#-IXNX$j;#|HrhMQt3chDeCEnZ#ANhfbIX;$@ z1#7mH4hX|Xvt-GUnmH4EW=xoH#KYqw&+MrIpM!{^)?Aa5aLt}dWKp5$3l_qqX_9ek zG8Pn6Sl;jhQ+~w{7z`N&ISVFyM@h_U{=kx#tXarIwW-f0vilB+sVGE>AEAU)q}#`l zi<2D_$#sAI)Bdrcs<*F8mWt0c64~DT#gzkgO-hVrrJjxvJ#q++OP|#zxOsNqz8!OV zstycmn7-8A#4@l?*}<FjsS3AhMXhIEZ!J1GuN}Z<>^bnvkz5iMqIR;F5wK)MMU7!g z!Ip2yr2;UNRI-8f<>2vvhzT<cDFKiYVg)na#2RiWnGmw0U_!!zLU52%Dk5Th<^+T+ zcqKtQ9;yWfdF?yX4EhT>)EVeEKSkwH?M8JxX&FbLeEtgU{JP)TlTO)rZW3P?I?&W# zdhmF&0$gw%<bBV$mFcC|**0sZs&qLsXBu@gtty?0w3O=a^qz!NXi34XWNhJ6;87Bg zQc$DW$PyyqE%&S`c*)OHB6-rp?D&jd^PY$e5grkmlJ7(+by`d{!-s~VJmH45^dT!W z8-5awOY_%W0+MDt<MECSAxjcbBs53f0Z(W$6$hrwIq)NvFZh9&uXxWCA1HW|MmNUe znrCi!#diekM66s&1z!RIMVnL{;gd^^sRmXw+u6jrs0i~{Kchpow;h0KE#QCpWK<)H zX#0sti0(BU<NbD*dZ7j<xEe*0kNNY|2wSgP1gi@Z$svegT(j7%dr#R9UBYk;QUiLD zqh=+9Z%s~0hR<9`DNO|z@)<A1D_K(#lFD4ACghP2j}@w=_%+!OkqnP30t)ehB!pBX zRP33uU_nAEx;-tTi@AJ|n`x8B6-z1tJVGJJoo*|yJrt_WL+|vVXS9~4os=nO-Jx1V zqwP`FlmnU?tLsm$S(lcldk<&byN=D>XPotmk&SCN8bLYrWH&KI^*q1UAq;lC1V*m4 zcGQlcWQaAhR3i+WhCw{x04OrvGvy-@Icp+5<KOV`ne!7@Y$OC=#Xl3tLAvAzQohEb zSTo^T=An7}s+&<nf~De$kR$g(WNEJFEw^llxF(kFWKF~~Z@A%}SG;6{2b4feMadFH zN+E2OfQFKM<cS;JvBu{miDY|B*^={x2%IM*#C*jER=|=Y85J?N%-Ca@O7e@5Nb*1? ze2vc4Yd=|@<I^#YXQ&$&I{vxt0;x=|sB=akzx(ELqY2%}K-&wEw4=CqHQ1eY!zk4V zI_eTlVOUG{VF{|EFH{%J%a;&ZmrQr#up`y#iMkNhTC`hAD!-Z|_dK%a$R5i~fInL1 ze{XmVr0mH^DX{#=o`8~oNQfB?N2R1@%MBXEks2+r&Kk|0J&K%?BZ;^&rX(Dguy2tG z9=?PTPMH!hWkDu7vy<NwnMP@6zU2`(6trRWt}n(<DM#+DMiCSJMpgHQ4yN7XX{Ty) z>3Em3S-ReC-Pt_jZ1d0G#iL8&(>>Z>3$Jvbq^`@@r{o|47EerUo@Cdayb~#kn3#f? zl9F3s#|@f~?0LtP987oo3(peCSl|(1fCzvMcZ8&TFBEU2R@DT|xn+wcWPxT$&4H;n zX5SO?hKwTtDF=z5oG@d{YkputDeZ2^jFLC<6GluiM0hBkNx8@8OYX&W+gR5;6bX0u zL`;~FaVNX(9FILUA(ojaT|?QYHwDsZBVOT2{d{_a)q^YE>~n)QhRXI9m+lBoUmR9B zcbU52fjG-JO{<@_P6T7O995$Vz41d%jlAT3Y1{wrj2y#V9ii<BAg%LvSIgbq8+E^) zo<>LwILp@gzNhRj+lEIr!q{e>Qg9?8Wrt<X0@H>zR$TKEL&kwZ;)WWF(4GmtG$xt~ zLn67aP?KTE$b_tGDL9g|MsX04bH$_`O6Ou@kB9_S38^%<IoE`ER9GT89C5hCO7vk? zSS{LVMEaok{y{SozFTKzJSKEkve!Mo(^??z$SA-vOycXu%A6*YUZ*j(?Vskp&VD0Q zclO!cyHPtL=o%bd-}!$f5euneP7PTh4-+Mrss($Lq>!b+Jt=ogh<Gd0yg4s8@_TV- zHz^mVaPEYHAFw3&ge+wN1(m=86e&;mB;?er*i!R`P&^^c?D3j_l#rYULMql2#2ooG zMgV59tb6XcBEyifVMEFWML~hjkL>x99c#YhfaXdN`|sIN@DsOaKJpnxSVGMVyTHhC z4-=x+4tc6q4ORqLN=gIOQ|HwpQ9d7!IAZ3mZO!fFOz&!TocwBZfnWz;j^mo^XP(*t zTZKVY!06WW)9v$^lcX<Tbbm0@F}dIzg6_dM_GWZ-K;4gr9blAn>(KQ?tZc=0-LyMv z9!TU+Td_h*sEi>)5s`?wY{!9GM&%1(d_>GB$l0SgpqcOmGu8qZ+B2tMA#p`HD{^XJ ziX|oDNWzMmiQHnU{dg$mSbWKE^Cf`Ljb?02MYK|Ex3$n09#dSb;;ig5?clxDt*e=~ znYGUSa(ZB?j(A*kTt8!`LMz#~bhLP?Cqh@9s_`^K1s9j<atK}Z;y8IGmc+FNKdLCn zJFhP!XGcy&$ecIKB#!9!9C^zvUs19q<e51O4$RvsNRx47&pjnmpk&V+ErABj=DHN| zQbEpyD@vXy34oGU#QcUkath`w$;3ZfGGoGy44AT{#89&*BLOy8UK5gtNISr1NkBox zJ>T*RGD@DYB-}~>Q$@sQ{0k93@*_3h5;J2;MaEPPsphfNsQa23Wq~3hpg>CtjFbna zwLPAyJ>|$=OsB##oyVrvF{%ySTg|l=^t>Gf2MpI6Zrh><7%4aXNRJkpx<@$~#|(^v z3>eW8_KRHi?`CkM+6x<_VIt@70JfcVx|XMqC)?awreKYte#;{dY&cM{;+lwD_F08E zWi<hYjcE5bq~htY+;Bxk#Y|#=Udh-x0u(Euz4!z?kpPAg#gv4U37_#qLcpeN79zR5 zVghDF_^g;PC6Jr&JRUy@lRamDs~k38KPJ9g`DjNr(v4#}x0bq%c<e{QXeV)32@q9x zwz#GzF(2X%7$oay=dg5n-Ch3nsTtM-Sq>e%DxEE_tNU!+IC-lI^u*?-T1&l0MMg?a z#WM!Tgo*daHFFLWT=NSy@<7-~9a>YPsIU~wSqVhGNx!J1UTOfM88MSK=m60ZrZQ{X zqp>8^L~Mba88b363Jfn<Q;49Y7Pt7dAgE5D3PZ&bc)=4PFDQVN7ibb5B=b3BMaGg1 zZ+XJU$KVqYF_V<JCT^(F`Gsf-0wUoKRr0A~RElyoz1p7q$qul1)^UH2ohk~@TJ%o+ zU~WAEi06W@gHJ%E`fL<6;>7$EieRYAww;F4^sLNAv`y}>^#AJqcShP=(=)`_&a+<2 z6HJS^_|yVb6l9$Of60sn78)^;U0le56>ucSvgUg}NP<<sHGvF-p{8Ui4Nt}^u6awv z_Z;ykg{Klyb7%+aS2EK{MVV!!wQgoEO(KU+z(i;tf!tEfgeU0WrM%vvs@q<6&1tUN z_p}W)y&H0zh+~{AUfWikjpW*N+86hL_2r)R#E+_a(kj&hVR(WxIETV(hq%Xx<<6yO zTZaax+eHtKF|J`7dlDI6U<A@`W$)k2{%)w4;<2Qb$bg6#%OClf-=O(tp81Ne2sjXk zUf^V~JH2NmA)0GqVi|(eBBk;u*-GtKVn{LkgeJ$Z1@5sNP_nA1coOwy#g-4e=2p^o zo1H6ch`AI0aV0+!vR3<<BNeb@!h|Iu1^47^WC~aECtgxhQ*lcrnZk;Uj2cZwC~bHo z*(43Yuxh8LXKr-u43mr;i}fg`;5;^|oX{^b7-z{fR~?j4w<S=W%*(F9oQ_j-+|;E; zjt%>XU_d=GYqTS-P3ue@P5Y*Fr;**cr292yG>6?>j4nJE)O9-msKmFS<YU$Brz%Mr zO-U%o@R$pSSqrhFB4o*ooC9kLQc+p$*@?8_i3E?5J-Jkc9zIKcqF~KOUb93CvY-hx zT#)m~jGC7e7;-9UwVFv`L)Mt0P`u)WT*F#KD|tKNJ6|PVstiwzdA4Kx({kJlA5q$w zrJZ1&y3<bf*&3=Pxmf2<3W)I9&~oDlTU2jW+c!AT@#`uXp`TvSIWAm@0?zMjc*ksX zYE*0$wQwjaF_X<lRAUH5!tA5*iLpdXuxQ@#k!wOCrcA|$Q4mn@Uy1pg_mph-iqDye z90d7uataO{n6L$|c-e}k8UR2pTDgdlJsTEGv3y2BP08nSyjKD!tI;GPTJi8GdFC0P z8FL}!`J_BB<4zuq6$LpyskmL9G5kpqgr{7g$@mimA3%We8~M{bUQ)6{6Smcu5rGv1 z1ahcqq4t^9oT}sqvrel^3##a!27M0Zm~X?qQ@sE68`YzuX~(>+def3_GhjRUV{YOV z9qX66U;<;LHXL_sTmq2O8E2Y{s-EH2STUX*qAtj0PE-(2o;fE6Xx)BQlN74h@iPfq z5{Vc*i8YlBxtCaW(xxU<B<xy^f5XLX1S(eS@GB-HlD2V0%m(Bp`oNNqoyY(J?l_PL zqr+!TCIXg_IS}!ZB_TdbDq^Wz0!jVVvj06H)|E(O=|NakFJw~<Hua3~Z`}LKoxFIh z55x|>r**RPhTa@IXnj>D`_GPA5phU=s)wd^M><|_Gpz=V*ASww@k9^jNt=3_RF-1A z2h9&+PHa|7QxImdV!|6T9@+4Mf{*PK`E<Y?q!nEeuxBlGdh=R+flvgJTDD+8NzIgk zk_CU}zzYl|F)4?(uF4636*-0jl^ooGkQz&t;zv?Ur5c-}$*2jL@B>pkUJ~#qlBzp2 zb1_xErVuq~DUSdnbZ1YHRJDv%0s#=vI&n39gt6_Y{mzO$+Yz<c!Tr(ydkfGVf|-UJ zSluJ8>5<&DE~-dhoTJ&1Iigz?j=`espv|?tYVA}nm;sZ^4mQxXgD<p$&95E(uXQW& zt)<PSZKjn_IhtJCClX2$G!a^&Uu-LIR;27GB#R)G9NZbP96zNn@TT&eCw#<Gb0BAm z&wCUFB^ioqDxRr%!7Vf1qR0jF?MZ4{vqAKMh?tmAB(NuN)=4~4Il8^3((*-DMcWOi zo}qk*b#=F@UH^@BAQRf58mbl|U>%l<k&4=lGomAG>5eAo(DpoMW_N~VopLlx&$!Nr zBujUWLboTP6<erw8>!ldgpqZ_Srbq=4OoK+JMa-rOhv_!2WbcIxFaUwD?TS6;~gtm zU}y{}mV$^mDL!jTUa`P;e8y+@YbkQ}XZBbUG8WXlB9;VOMJexoA*#}XiW!QCP-^{> zT6A?ALZ*~#m}0TKV8=BXiUVKs842)=$LHLVQt=0B_Tm&eiqHq1IZD^#VF*~@lM_*s z;z@c*AV={Dj%MXiqI;jszSh_{!j^MwI8+$Zb2qmYUstNEM-6DIGqiSFW3Q8N+i#NT zNmzz)KwX&&gPW6{k<N~^zg;}J?S$O)C68lq;bS}A^PgSIw8n@kUdc4-eA|^&By8AI zvf)U?f*C#~iEM5&D(8dQaCW!sNXXdnAX+?yPe#g-2?k5Sdn`>{%nw`<2;&V-2yV>| zi{^@yqFr}H9OVf+leuCf$KIlSO!@84;yJTAcfp~1$|%?0U^+@X)vKu7=do^+6-+BO z_F8_QZGoWHN#g4dtcO?byq;h%qZ>MeZTp?nz`uRIn=R)Iy>6}NIft;<V!KX!6Dw&L zm4iRf((Y>ZL^9}DlL!K8Cw14BCw?ZQWJ}Gjc!%QuWy%hp5{*YpNXeWP0X{yj$hjwx z13^2x&!+NAIWiNc#1$103!cci<(Y^bsmw_$AjIG^VJErIM!v%mjYmnsQF32Wj;wfK zNe;{?c*{MOIW>1AJd!fS@QwmYNWy|hYPk%LSSFy2Hz#i+&5QP)X<SdbT_ko@0J;-e zYUPg7BOzd8N!{H#(Bl24Z`^Qh)yC`5YgBa)5u|%ZUw3e=M>s2L)JS$&`ZC<(+TIgi z^{P12DRph<|J7(l`?#QGs3bJ)g{fMqSX24=tv)Sf$DShzYo4gN;RQ1^2^qC;$C~Mt zk0zI#wMTYs`czZNg~S}l<aWOnt6I$!kAwtlQB=$c*;DYuj+g|?4O5zMz&$gRh+IQr znJF!pQxmZyU`{Ag7wbH<aJ;VC$+c2F9b6a2s(Z~O1IU+h78nfFb*d#B_dOsk++H~w zJ=+1Mxm#H~;@qU80#pO%wYzR}zl-~t`?crkL*(PhNM6a3!L$SZKpqSwDIsfS)YPQx zc*%hcaKK|v#FiZoJfQhN!VmldulOTJ3Z|0iX7LCpctOfbHcZHPZkt`}I9^YRfRvoM zh<*d+r2N1&M=F*)wLF@D94J^(lSr^oL?~?VCQ;;PYVNSa)RM)vq$Xs?mw2qXC1Xa+ z2R6(IxEAhr30#r4(AY@a%}J|?KpwscF$dzdqO(0Yl)F|`ojl!c|JS!(=#c`ajg0t@ z-?;1l?oEAmQ0mT6H*9#dqj=XN17UR`*^FLlts3#nc6T!7LSp)usJZ_fa9@vgj7UQ{ zb9d)#+M&3SM$`(!*J~F58C$lLlx$dY<c80ffUGS}jFqTm_BOlA2Og*>2+1iiObG>1 z-mux0_=F-y*b+0NprE8CWyzj3dkohkcwDpPJ-(1_PdQd05i^#on4$26%hFVO(ZC6z zWnp4l`<Lp;lw1}{|B9Y*p?!D7NvIk)T0MGg=TJ~`GAo@cecfxJ`+OkfRCT)7WpsJl zj1YUYqx17x#HekN1FH39`|Y5<KV@nfTrDBnr<F(DPKI*=VbfLeU)BPPIFfQlNys1Z z_<MfjnUa7jZYZ(LII`z+fd?$HJhip(`ExR3#erC^_LmrPZU|8PL`+S=Ew^$PHvI1i zmRQ6%mbvsK9y5`$6etREp#~Lr_)Itwka0yuD1(~=kAy6N6$M7_WGj${ru~g%6;sN) z8_NT)A|w;(@);K~W?Z$7eL!6>$I>Hz4j;$1Irzp`5Ox$7IuyNJ!jO+4sTgSg#)Qpw zM7L*0JOO4@$2Z1{8A1xxXlmTp62&+Qej|qPMl#?DV5+8IM<U4u5A1-Kl4!LhYiCwX zl5HT{DxZSyB;&Luz>tvx3w#zN93{Hikl+Cs6COa=wjK*CM^YdrCE|`XIhr-MXmS*V zjJ!2hEb)n%6EhVFX|p=<+MTCzs4H4HBrZ2acMc4f+&$cdnt^_k){Im;a+&S}rflm^ z_aN&7XidjA+`Wcp5}^B#^GC0;9z~SdPLPxvop~CrD?*{zD1q~92h`R4q=h^ZbCGfw z$w$s)mtJzf5VNCVgNJ6ugp4hpQ{nT-BR*U4>%dD^L{x;7Oi)CWXm+Id;^bEHKyHrY zm^}+(j#R|N_*`?KV9Ex|l-DezDW6H|9u&laLGXkp9m$@$nH)CRzDptuub5y-h<Hgr z#9rdkEem{n-cX>4+6Qnm-=0eq*#N1G*!OCY9cv~ss5u?yUeC$z9%_Me=jn^q2ri=a zd<Ljcdm)@#4ZI!7nK1NY9Mwf{tnQVantr^v;!{Z=XRLVlSIh`Fbr_i0<%76@N;O~% z+E$-OC-2Enw1ncuRCT13TdyFY;3Y4Z31EO$q|tE9YFRJjg5PfTULJ;tuz8nc%us}Q zMC{RQq<OL=tO?L8sffhRUsDqZ)wy6#&1-RoHhhdoYP<<kk#{KZ6(b7?msX4{KF$*_ zdem*})M2Jwc-T(-rq?R;&UpyB1EZ*%c?}M`P7CwIb{=lJv&1+LI!=wUC$#GGispQc zPZ_Yv8GH1F`Nlb-bvv`u?crMq_P`TFK`oDtTCTj3N~{nsM{^v-;;e4sd`i)0)=YWL zo=0ZD8=g_Xf|80gN4{W7iO)*f#2p2S*W9t<jw3ZSa4U@}j(y)F!3cY~<iMPgh&>4v zpYul+Y}k{q;BT1oPwd430xJ$oh%nSd<Z>)2;rulIlZY7}?+MwF^NJlA3BO>^ln1VP z!A9PtBc3edYEd+L)MR3r^dy*}sRJ8YQO$ycIRyckeELeKqdMJ_r?EyP>OwWD-B6<m zYv(jcCv!t}oR=phmb<HE(CCiWcrXSZ@5^F_jj<XjSnYuQtVRVbwo?kb;YY4^&kXjv zrfs?C{2tTpaZgDLt~a!lC;h2et3<>M6$vQ;Z@9uIVNXdxftI|BNWx)eR46`jlqY9X z4@UTe%&5qKJ62q?p=3kAQ4sJ)j`%3{MEJsMsqhITXu6>QA2CEsL=87XW0;6^MRl-G z+DQmjt-xD3nY3=rV;t6=Y9Bx6z#!9p09E@gA{Hd1@<le|TEOh4BW&nTb=^sAA1Bsd zdFl3g*BveAX+2eTJ{FZsvy~H|WQ3dSPPxx>KTqn%GnCA<X}U%PxB)>M=~+oIq$jgK zADFTtAY((0$Amd=c#X$19yb)Eta#!D_uTQNq`x)R{xuu!Su^KI!W9c%O7-uy_C6CL zQub0ohZG#h<RN;GV$FeX$$7y)@>`Y!D2~L;Sjhmyh{(pWB$O&oGhvS9h(|#tGwK5c zM_yv_+4C#8iZL}FucUo$)TB^Ki(AXPlM+*s<H?;}GAAb_U?SSf(@g!O_SCJ^s3swA zAq|X3+lD?ba1-~u|MZO-*@ATO?A>XfI}aSrPlo0*JzyN`0Zn7LkI@%_RF`#qU1UPP z4YC&<A!?8*;a0YFC*!LhL)eUKk213<`N$Ise#MlOkK`g`_6S)Lkdxy}g8h~q0V$7y zgogtemWjj^YEl9sX*wcSJh72Vvk`!%D2{R=m(0X7(x8y$9LWeILNefrm<XS#D78+3 zIJ6e0vn)fmI==Ik_gu-JdAVjlUt!PyyAxH@PW4WkT`S^Mk8T9C?M0QU&bD5+>AS{R z#?`&Wi))1&Qx}-dJ)#`^mTi$!=dJUpy{?_%4y}jdOlv;<goRkw>h?&kM8J_^WJ-JB zk(`pxnG^AETyw>D+)$yI@_+G$KT^r6q)F)AvPQFDN<>b|`;PYQWH|JgvE`bA86igk zdDtC!&nxb!DR@u7oIBoejUh!5u;hs0N<_sG5g|F2B{^2+a)@BY5);X``i7b*ADGEP zXCkcWTge;tNKlkaMbcc#L#{$IlaQu{FIfu9P&s#;OK3g~Qp#2rU@r{KbgRD|gBOGM zAHPvO;3qvdtsbcP#t>Fr<dkkl++BHr?$v$ccz8yo%xch{sDY1^L4m6VL=gr8=KkK; z{pgy`8)l?>v26>d*t5e@NXz?ylq+6idE_X!OJgo;%xjedK{p{S2f@Vq0{d1hh}rWJ z*s|mYwrvxZu#wQfBQd!!Ibt#n%$ea+P_mK3E96L%?6DwZNkqVc5KT<fa#xfiG@(U) z(xH%eZR9iJ`LLZQu$K1FI75rZs^v1(ROk3p&{kGWBU3p`kg;oP+F4)iRDeccp!Eg8 zdKXJ++9Qe)QL-DXWjl|8(_*5jQH`@h^_<PGkrrP|Ki~<L!j~2;qn4RnKn3LFeBj8Q zkF5EcUt!qsnv5TbxMj{0h9g>xVgWfTt_5CS<MV;<I^jE3_P;eHulO4ZBKBOfVv1qU zEq@`z=ZP5!Yo5h;_9gG|m~m*E`<djVrNW0fu$FO(VnQKwpk}->q2LXVXny4HI8tye z_vVt<Wa7e!MV3Qb`FmuP#Bz+LKq;~J5jB<EcSeRW=j&kw)TN~-@djs6R}AM3r0ifD z5b2o1meIP-49Ild&8;0-l+eR=su8h4|DWuLdCWbOZD%HlGuLqsF58K$?CJ#T0ibBi zE_Zt3>vn2%3idmF_)AhMv6~smG@D@Ak;|Yql8;SOnZ>Nolw@pq7Ry*OM2<x{Z&&~& z3lu9-4n$m&kWfm6riduWSu-bPPC+OulTuVtArldeF3`;I(Uk0YAqv7sYE{$oN|;`} z=82fwo${%Xv^&!tKVAnW<U2S>_s=k*-N%7>fe~)<Fda+Fd($W;b*nbDauAyKt7=DR zZ8~8@CztK{_nJ;uV%sE+vs*{CLfVoXEyH@hC!6xypT3gc-CWlkPvVGb0@C)5Jyn)( z`6D?2FZn%Jg#0aQzM$q`*|B8GEl)x&DX}cDXyy{$W5_%5opZ41Eh#BaM1)i<WIwxM z#)5lp@km*a@xTtvE8cO%4VI<kv{a(#OPMlfP0flm2A_n2&xNg=GNr(0&y<W;+!OFU zFWF+zOsL6Ov!vue%%kLxgza)Fr6l5`q`>5|c3E=32=AqNfLHAtS+$sg(D_W8QJLCV z>|_U!4cqAW=y`J)75_1q)wvq2?tj-kFaa=7_UQ}B9BNQ~4%_5D@L(({cZ}>j)Nj+# z_^F;LNjY9p+RRTQ<JQxJ>qsUWKtsWnBTJ_2Ig-j+q=A&KB-cQ(<$;7fkJNaQISL^& z@h*l~JTf9Q2NulPkYmWnfEgiMA~Y#A9swaS6AY<1Iut7c5@5!ZB{L#C=1hr*gznN@ zlTfO@x-*jKD*H6&9E+xvI^QL=BPlr@aK^ESYv*t|`zSrf%W;~q>h@Po>|ERNh@5C7 zs<ZMy9L4APo)$JrH_79?u&PI{x9T0tS}HkTq&-IHcg=y;&=#BdWF)JQ(>__Z56hta z`AjQ-^4fPO$8coBgZPmj`HY00_>o&`{*D!Nsqo$a03ZNKL_t)BdtUQBJ8}X7nTrL4 z1T4vjxy9onY0q8KJQx%a9t8(9ODsng>{w9pK#0c%p9LA3f-Mi^9QmmQY-AFT1XC7R z=~E6YDakNg$%Ee~z~FJsJ=X*no>_7&?P|cDD@w7^HDsDa>uPQqT0_i+BQrt~-bVzK zEU;uEbMfW<)efJ<`-C9<^8ego&HLZJ8M18Y_%hYVU;3|bH+07VsRj$@vGlXCSI|HB z`kFIFk7h<wAs7X3_4l~llnyH&rjvT8YXALYo6b4N=Bfb*t=TarBI6*A&F0phiE(_x zGY585lx)PAp{PaCWLYw0i$_Y#hKebQM+!<Al}}lbl5iufQ^bRO6q?W?Ba(vV@Jgj_ zuv~GCPb@=j+e1s7M-vUt*OLQIxA^)~YhU%U?LB82bAGo?Z-Qn!SFNepYB?Ng8Omo) zKCUY_GLA}4w{5a&vvG~=Q_oXa-)Y57r-5z$Jni7^`}$5FH-)8ZmQ*cu<rKnRw?C2T zV6|+k{Y0`hG>OPYvUaP#!?2}hMa-575w+YKb3$(Ln31wYQ{ao=p`w(dV1~z@t@9I} z^anMHCCJL{6n35xa^#VZe9nO_un^Hm#uXt??73%0B@40{GYaNZLTygu0hr+NlAMGI zAtf=I7<kPSH8pQg%&90jFl8lsaK(|F6<Q)KYhXcvWlB!QExHA7G!>%}M=#ERqpAA+ z4lL}Fzkdj|a(|C@HCmT2@(iYPv>%GIlT!^2xc+8$Y@sn8AT)+Ba`(Yr=TBA6K>n;Y zvppiP8bwrfDbwyw*vPY~l7Fjvs|`!VkqxC(FcEW6@8@I`Exw=`5X`wkb7aeo4Vio_ zjJP?q$RJj{0r6y3R76;wMDL`4IU$fTL6fkjAYl&dSc)K7F+-7aMZumW6XqgCIPFYN ziwm#)V4gSk*2yS4rBjshfj!MyT;o|q-9jU6hqHGI=`jwK#W-sHTHf1Q5=-`ST)CX@ zs>O@d68K%TM_MDSt=bVp&|c|UJT;~Rw>r6#tF~%)`6_){?@6G7L*J=6g)laJ?LxNW zwgX2!A>K||Y=t~9lU8}=qlr;W&>VS%<pT#aA!`&1D*ntCkALEy_=4X^N^edoXp4$1 zSIkgUycTQR>GO1gbxkoGNW{0h2JUIHIebd~z&kwl<Xo{NmW7rgry?Vfqi0Vj?R|3u zzL7h~<B^J6c~Hf2(5AAyfHhMT3l6-bKoe6F{{M`<>5?SbedYJNzQ>+hExmyN2m;cG zoEe#Ej9>K}GD%~aC&4@EDP%_3kQ@Oc4gmtaS5+>thwpy%;U2%?v1C(5Yoe{IGBYy5 z{rZ17=XZo_Efm2Tt0<TgVsz|UrbLoTx8$<8^$V;^?>1L>o7eQ3_6&lnYx95k?P-_` zXC|cANE=+pQi3bEgQKJ~xN?A*t8MOUZL1k*u`jpI=Uba|M*$~WUo)7`*fXZX@%Zh| zexCKWe5a70oI-ymCL<u{6H8)Z{dcS7{fsw+9Awun*^*FEU}-6-<(Ql@Q_x`TpLaPO zg`RiLZvITon1GE8=cx{o2@M+(0vg^>@yHD$Zb(SECs3wuqP<@)4Y1zta5P~Bo;_3P zPbwN}DjFYhX?1}06<45`rQ`0_Z8CduP3PHh-9W|HBgI3Hb~nCV?!2zASJDMCh*@7_ zMe<N1ogITIpB=QIc5rYXu}56(IKqMFI5?>akM&$eOhwM<<XY_4IIT$6J@6|TtYjJ* zFGAUIU_(H~GZiyZek9-<3ZAk2hIhR1E4F;n8fd~8B!M3h6R~4WesK-QWEAq?9;ozN zXUzCBPZS)O@{VUZDke-xnehV+hJt{YjG8$XB$m#ZPzqP&e91FzskH=g(Uf;=)P%-# z%o$-=aZf@DRK$!}iT>?;>Ip5DRB=}U2gN1i7^Q_w)oW-f=NsMNM96e~nQ1RNzu=@g z4FA{Po@ZZ~6M2KVs<ty{pwi$XLi}0-eJXOfgj!w`N}tz>Ty1hMn_bShtma~~J9PdA z7j5`(Ll~UMS^9*ClAK&Q0WQ}Hwyf9?GLl-|RTvZQNNIUyMNUph$dnCRpJrAuVXUEX z$XbHYEgQ{toYp-g;iLQ^F<T;H4$OUo(Uct>H4j8=wJA=Ra?6Yn2@$DgRm0yg9HdR8 z9!wDUVR<7heKI&iDoRR9tRJv<s*Igk>&Vv&wD>^_9!G*X4TtUq?#$y#Di{zS>p|Px zo)8yXf9$p=G>+~J72o>r9h?vbdl0KRTZIKDjdB05usWt2Vnu`-%_V!MFN0a5OTTT1 zD6z!iqAWeRd#9OjLckL{e#wX(0ZS6rY<R-*JH~9u>GO?qBIZ~U&2n>g@_Gk;%N~>Z zbhIr2A>UF-P4H)GI$n4Xg20k<LrzJ}j6IdUu3|>Zn23}E2`xDzN-_#yPQ#3b2ew#7 zI*tt!5r#%gX*Hh?D1@X+*|H$kqbb$kB%#&BbgxKDmmpKCDaNy=@xGi(BraF17U%c> zaFiAPZ@)d|s02QFY8d={y;?I@WGR>O6LXfa8(dZXU0-FGs|4ra^w3@e312hKnXBM~ zbKT#$HO~(Ak^K%AO@|709B2qhN!0RJys)L9(*c4Ux(+?M;!%Nn7DLLGoQ#qZ!%PQK zO2~?ifQ&64h^fhmF-$3msi}x0o#|^BE8svOiNGzDZb`Q=xo6H)uXC&$*ACQkNBXt{ zWAz}<SdGg8D-Uu>#X(hQt0_#Rfw%Jv1T;F>dc%H6NFd8n>$7O<0U<lm?6evv>xZL> zR>yFp3iPxdKT>k=K8tQ3d9j22tv%5t^xo^!y?^IfEB$u}RYi8*Xjd`v`M;-Gg2%SL z(eKq`44@^U)CwrTW$e{*0aZ*CFA$Kjl$W94@Azw;*;D(N#SEhdtI>?DRa6zem5vl! zA5f8ExF;oKLdrdBJ`vJz;1}$uNvJ)+d7>jaB*QXMofXMoSx}QQ5^B^CY0jK8Az;G| z9eZwwXi4apv*d;ahBaeGtaLbren1wfN{_s#YNr8^3rFt+^$$d;=_622=f>9sZl?;9 z;LHdaxQdSx`bKcl;s#gR(#v_CxjNIko~&_>t-qWEa`FnUK`*(I%3-elxARKQo;i50 zJ4`OWOMj!fn<!_+o{E66C}xI^Ei2j7`qzG-W6Fe<B`+NOOw1Coq9vyQX3Th@;ynR- zN=Et<!$ev14+<z>F{J|<MKRaf?RHph*s$k@hA|BlF)a)3xMxa6L_tbQM5vqt<G~A| zClVVCjvHDUw(O|Y)VbTJpz`}|_k1qo<vd`>K#8x7%v6PL?9Ly0sA-M#0BBUR_1oqC zNbAm0+`5zbZx5{ubj%vRckk3zHZ*cf*b^RK>xpmuG1vLoWakgR>SQWpJSi@a`Myv~ zY2YNgwNw00NKK{I(7lp`S}mUY{sA3p_G}~`dEtg<GS<x5Q_+$T^NAII!=L2%O@WX% zJQ6Y^qa&at6@j@|K}KTk%qeI{2!V_nLK<!`d`C$_$&Rns(-9Lhree>QsVev}0hTEh z5sAheAzLCwl9?EF6BUNJbgUB$JKhixF$4Cbyw`NvFk#JDe&S>h<=#(8-B_T|AfeEE z+z>JLqmiRO=TzXvS^0B~6mZ%QhyU@n!B28mu3OH6lO=Na|Eu7<JorwTzOPO1%qx5q zbC$Du9gSdK;dEVouhW^Pl`6}KQPJNzAptv<)a1ZKwVJ#24^+UO1285ery|fIV&lP& zov;Kuw$iUnXi1q8vBAYGS2VQDgtLl?*wa!nr&1L+m6tap<sElk0vbs-7|SCv=Y}aG zGG<JLleD6)9ajwjRwA0jH;^?krE-6Ba&4k(MPim}{91P8vhD5J(y-SQCR7C&QTb_N z=QngMjkcYwuhBa1;|K?Jl$AU7bjI3iBO}!-!9~_qUnL%$-NE3tX-~o<`)S@WpU?Ql zsvFdcu|mSFUtw4;1h*0{^@m~QX?=m-(GE*WA=GO?$A%phOFA+P8)8yE@P?KAs1=s) zdEnX8VXUUPTi&o{LPJ2pid>78{&!CZ*bzx`b6`Wsh!FvQl8?Ay!&l^VjA)tiC@rar zM;+0T>0aot3PM2spOzgFaL0t2IV~vx6B=rA9!MGK;ky7D3Q7_>HtHj+D$+h*qotJ4 z*{ZsW8EXaDNwn2yRTiC0-iP%%XFG!H<&pbz!~gi(;N+G+J@)vCMmM<NuGm)%W~cNH zu22=st8K155gv23Vm!wyT?$x&tDKp0ugU4xcdCZ^(4K&iWC*2Vq8E$_wa4946B4Ob zbI;*!F!p!Qos6122^|M&IWT$>hBJuQ6vQluS%G&l_CIaGm?<GYDit**RoG3eC*+t) zhe5=GdmfnRa&;F}&t&O8>qIh+z|)0mRh1jI#25-yelCl()+O80u~BAMuKA?(6`(^I zSsj}FEUuKXyir)Dq1FOr2s;f1Q8U(~>CIps*lO%;eWX$~P=E$%eyu;W5PP`wRb1nz zwPv9GYX?H54o<LBN9|@M#6#!}Y=Pgcb~HYLH_(d8HRGZ3dus(Xy6W#+3Jl+2*t4Ku z&xlIlI~iM^m;;uB4%9@Vrngl51usn4FlNtg01$R4vppeGBJQy)NSM$uXU{*&IpMh8 zpRp!Y*wl`M1v}M|U`PaF2tg|jhrcg?ls%culobuBfEX<WpJV9w$d~MStL;6+gw)z+ zo59>Jk}y1BjwPbvKuyRLh-qla7!iqxWV|Qn^i;5Ckd))qz)9OHF_JlRECyGd6z0s> zb6Q<>uSWEkS1^E=JKSqYYvwHJ)?QGYPj8f95N2ae;&aSdG$MXx!a&Gj2hNbwHHZ^e zhFB_ISke$PW~2ab7wz5P67N%D8g>KEgR2A@T2c~e|E3f)yrrdNLc)uxfKp1moh(72 z2i=DXd7crmW6p?%l#(TTMv^5L#2kEtkR>5wtY>M=XyAA-C%DH@$6+sj-E&Yywbg!l z&kCz*bgQR-ML|R)YksU2({SV8|5Wq8nt%~cAmX4^wrVILN~eRw1=N)O1`f0^X*{=j z$iVBPlo~G%LdQ!aWPl;{Vx-VFnSG3rYoJ=mKze?1IAApPSB>*wyIpE6w?ZOR7^+B0 zbw8aquo=}Up}vD*#2Ta3L`1=k2Ua|B%QNrz$Y*5iIB?H_mS=W!Y#4LP7Q={=fH^re z8%8{HL&S)X2Wo!wjG=~-oucFCe4~7ol7ttjM*FM3;d>sa2-wLHl1aF;VM3t}!6mw6 zii9jk*%44N<;ABpmvnl7*VH`no{R;Cj*TE%mL&@!_BzT_YJYG=Vp@)vQ!wX60h_7+ z%u{{ZooDrQud4QjyzSHI+~dg+nXC1fy($mvE@U5^Y{Jg{lI9{Z(wrIDe>xb*o`L9J zlOLbLr|qEnvlkoW)1&bG--d6W9oV!gT1KKm>~yP)nUNAG9NY|Q6ek&VB9}y!V<lO^ zmK9q%?nrszd)`X~6VVV8a?6Mp3R3QP=1Iv}V-hOvFtqGhs%b8Wg+6H56EJ2>r8$YK zE>k@#LscX308(x|eI@iOjhu>_Tx_%h1zX8<Bfq9F!bz?PuxxeEhe{U<F)W!fC-<I) z3`@tFlvEg1XXY}RhV|@|&SS3nRIvUPTMY^l9b>WTCF{Kw?t8iYZ|oUziRx1)Eoo19 zen$J@l28KSlvY(vp`$A%reRB=<FZe*h-J8n{Y13S*)uvm1D_#M53V`qOYMlnwj1eT zvL+!XV@FHJl#&U1HW*?>1I1dZm4w8MXvhh9<STL(-15C_ZJ|P}Qf5@d%=v*EwoIuB zytD;zTP*N_84F?z2_*#yTOtY)_N1O)<NU&R9LNduImGN~nRwr>lfk$@D~ZYIm{ZaU zUE1=IcTDsyq9`JSpbx3U4u=O~9)X4tF`Z~bQ+ZyF_JYe|DL9KLxU?#`{~ul3rrIl$ zF?-e;+B4Sa=|OS%f-WHd<`v?~Fgb&h&D`k`dWjzBiE<WBK;_-V^wGY;X6_YZW^jx; zyig?Q)DwFGQYK890EMrKj;}nF>ogHvrKMw}Na-DGN(?1)8V;Ic%qTI${DuQBjC8pq ze5Q?-TbQhP1PNtc#Btg4z#A&xPPEjT>m)3gFp{<^_F{)lm-2Bd7OF<6I8YLj(y*ta zB<IcxQEq{yq`^5Q0TCNwy|xa^TD-c1C6+rnGD-{+Qet9Ol*BY79JHiyHor(WLF;G6 zmYNhp=8gA(npLz{wQ4FYjY!ij*P+wmBGQr5gB3VoJzHfLTPy`u`&PGjS%Xlc9WScr zA}Tf0hL{^F)m}(b*|XyMl~GKs#Y-SlqFY3`Po|XK(Q?B_GREw%Jn@zlBN`8zO{hs| zR3#cF<X92!M!Zq3$^^@;H^}v`p=L`&O2Hdu6x^`&virU_O1#@BXU1Gt$e5iTEg^eC z0ttyjfiw0R<?IRgj6F3ILMGhstgE7Bq=Vg3kx((kGN$I49k+g+VWjM|`m91a3O!b* z#5`#{lwheOm+D>F!G&D;x)ja4GTD!UldaUH1h~6$Oxd%_$Xr!R2Lp(}(BvLm0iF&8 zqQ_&eyK+$3lf#!YP{C={XD`sFr%Yfj;P}TDI2d>c-0kKh9b3sx5@IYhJ9biQ88Tm% zcfO8kHT?@zt+`$BEjMHws9Dkx^M)Iqc}FKhlcS!wd0WAb9f1^7d+Fx`67lvP`M_Hp zY@>l*EhJ-v)w-mI%6HyRVZ8k-P?Mcg(NM7#(0&i>uuK$E*)gSKMMuXJ*bxbORFnIJ zE;j~`u%v8hselP7Z*<H&GUvb}8B<Lis}rC=?WG_wm1-ELRCK|Fl^WR|PhfPUBuee- z7Z$BIwe_i8(ZH?I2q%$C(&B9Mi5_%M(q<wAl3O=57^a%08UhYvSZWeoX|?1f_*F_L z=}siAV2`l~mB;Hu#t9iM5#R7DB6cKH%-9QN(bFI^TJCwI#!xd;%>03l(jS#5KHm9W z#+2;2=a0PQdqUom2;pf45C+!<I~T@5!;6{+#X>rmmh&Dz@WO;O9c#X#WyL$yf{wP= z&`>cKaCF0$6hw3cls;K*OGLs#53*)3kZK0Cenm*i9S2$hD#kjZk!1d;05CrZt$WR} z50M>A&&<UJc6dN^XBruM!9wAcJdEQ^=DZO$SBZM3-~ZawkPFhDIg1v$R_K}mK<JVd zZG3dK@v99yWD;elx#Bxfqvj~E?h<w^j4d@WhK+6*HysHnfhh+HEH^x`pk>N4YktNX zcFeeE%O8}kVA&EgV$UaPGEzb|#MELqH>!&!92hgAmIXf21yM56bTC%MVou1*Zr_;E z(vZqFza?cSuf&a1fMc;>0uqJhJdn{4vXY`Oqh&|Oo=WpCC&0EWNwG`_DA_aSiHx6N zS@S_WxrUNELbb5bAZ4SK(50`}-B`O&0QM0u8+w>1a&d-=`LU|>Ivv6#p>A(f!v{3p z6O&UA5eq`)v}*;=v{+&SrnI~evY^HAk(enVuww*RCUoqW&=5%wZT+Lhd6Fw#oiQCL zTdDD8@?o@uj41dczv6}^hK`hHS|%K*m4$KOZ~4C|h{##8C#S`5izQV+w^JavR~=)B zc_0yD05*J~X=SWCEcEPV%YjU`z=)DPGXPdh2`M$5o)Yo}f8dUf+_C2apA%DJ*~+kH zfLjeI_Pl39%b1puk_^<$wnUP-Sj}b8kjN<+OD*~$b`*>?LO=tTau7diF4!#QWKMs| zbU%)~B6DHmeZ4%iSCRDgBsc5$qq>Xyj?;M@7eR!a9DQ80%Rj|kIOQ-0XEmZdQSuGn zoKpz-Fbksd2VNk##ztt*!1FSkI<3MIk<zhdt*25*rLAR6MM+LbO~#0t84o&?X3R)f z@j$?%zIMSU9Zg$W<(<yhGA7hUuTv#i0=MKOjA-Z>u_GtrKu1X8r+cBs#I3euRxn2g zx$2Z4V)y|A+>ol;bgs&N<>AP<6){ibT7o!yP(aOu2{{dWBF41j97xH?H8&jb87n?A zW6G4Ant(mGY)J)cM^#%OK24yywxyONw-utP9iZl09gU4wC0f7m=wbF-@2|1iKPRj( zSk|P}WK?WP$Q1dw$1vC7X~}7MArVH@l95rf!jLfrDr)wU93_5Z8cVAf5No?{<+!N@ zmvHZKM#=*LAJ`Kqh`YnEBO#N3$TB8l&F4HY1q@?g#fqAr(J&)oA^l({zb-WF8L_7$ zVME6qEfM*N@cU?3Y$=Ge21&@J91JN~bLRsTCP2jv9fkv+v*tj=ijXl85it#G7Sxh8 zcZ_+&l3_^5*h_X(ve3-Zs_w+w1sWzw+ubo`&J#JYyij9J43EO0^RlSpTKxRw$rf?d zxo|D5h?CWTy$E}}#KB*4-d+n6;wo@FxR}G4v-*+?DKfsMGbhK}xrmv8j{g`FJv8if z8nt(-M`|{#2^bNJUxG|FCABnldsZ~Wy5KEqAf%yH=5Ee}duncIiIk`DBQf{vDVdXV zV8Jsr88@t{NLeuDg^HSl2@#Eit33qMO&Sv-8urv=Z0N`&QEACYWi|^Xi#Z1AG=t%M z!IFv*5d{S>)sY#<qUFRV6$f%sGH>9oh>5lSfjLwC)(JHqS&)(`IP*L1flqAcm@wj< z2J|jEC!tY`YyF@g(&{1dP7o^-S*u4w&sGez7O6ejw3CT0*DSG>57kmru_L08(y#MD z12G*5Eh`R+UQhHrQ&M&qDt6rSfdw%eLgqp&6~x@o&@lB*6r*<`rsbeLawG`5bAF83 zGA5=XV8($BQ<l6VrI8NcAhTP_hKm1=-%-oIQ&Td>P_gB$9#PG}>W3bD?m$~Jp=HWO z(Mye=fd}4ag8Brsyx21%R#od<GZQR^gm-+ShsK;IW*8<^<Rnxa7?D$vvg1Kxi<&Vb z3Q`^@RH?Tl>XxE`rV^<i5^~ukVak@AsZ4Edqip=R<~60TJ!^B%Ma>acRuI?Fq}R?@ zdjSpYuHc?tW8&H~^Z*yN-z7oWyix($GZ2+M({-DZviF$p6`cHj!Qcq(E%J@`U_4VY z7iS_-qqNs%%n(ztVoNCBp;M~mB+5Q$I8amY6&Z7_4@w}_VNvsmoiuo^!E=m?dkQ+X zx(Qkp{24JNGoMc2JQfi%o~a3$vk@)V>SONPzuo{AdoM!3mP(b7CDUNpcvoxeZ>km; zky2r}<C#5cS=+b5jt9V<cBpP;&<K>@V)>3+nbSACP%x(8UYo{JQHFsB47kaWIoUu* zTCVgDts@hrJ*i^cwX;*sSw%-lD-B~qF5Zhn$2NjJ7y@Fkhl4>5on=N%&4{_6u5Vdj zS@S~8JrNZNV;T}lO8?GQl8(eW*!%5qOF$%F!km(j89Qc-`M@8!<%wICq~xCHykJYq z0?R*;=&Nh_Uw2G6(C~XIuWLjPIwQMd#VsQW3FRF&BV#|n@AotHf=m(LEgdxz5<S|- z)LNm$bj)b@L_)-rk9<xjX}9|vwwfdUz?WFYbd=oEvX*AF|GYx~&^q>wEgc7K)2-G) zcjW9CVR+;V#;ma?^0sr+njPp*PEVsFKPcj=(%E?Pj(IiY=eV1(1Iy33{KQ_YlCQ~7 zPQTwz`94mMHhU6f$OUHpnj-OZRdQ}kvi>mad_1(JWTlS-B2qO~IhDd^T57gzfrOAk zo3m1`Tth)2C4a+PQVtZF0lmYrr=nvg_Q{AlLbkf8pQVOR7!!K$z@CI#0-mUtN@me8 z<zMt~dp={rn2F+uVm(=nsy8P#j@9Diq~w&`Fr#8l=7W~3PnkO|rb3|?9M*I~&X^e; zE7enhG<0JP{GGxvX97GCvu4VaEjva!*sPF9sf5$U5566?{}etD4N#|@KN6jdF4k}% zrjrb%Ar`y3WUWfl(6XYVB9%L#km|Bk_Hf3YmK$PH5>nOG83|(nUt`{5C^<;J($Q%e zWhHHPkJ3uDWU6;Ol7uFqR6X~Gf1>3_BJOy{vm6fz-%C^X7ks2<!e8=_s-Qx(^3FB$ z6#)Tj$&B4SSW9!-Pc<Wtt=fC%ORw2&2RV7h|7k_aff+F=AzMOX*6LSk8Vm<2a^W@} z{7yS%Ps{<MBYMmSrWgvbl<)b#n2uUkGbgcMM;BMdR=UiFgsEgaF$Hrm{rW7wYvZ@` ze3i@e<>)%y<XUBQdij})#9RAH>c#0x>V6_C?Nan&PTaKT)Kvz+d3!NKyp|>M+H~+- z@QCJKRxRy;EtW)+vp`Ib1Ep35y}KbIl4BzxC0F5Y1$S(?=az`Qbo?E2D)v}z39)Qx zSa46vHze#SNeC#3S!ilzSW#n$wE62tz8yL6wZJ}3&YV-R<7UwQxWfS@VHvN<4|pNs zR)H|i+TIHuD({dOhPk_^YOIBTn?rS2a(1$xcbd2)n&UvrRQ`if)WAftRM%oznfg0r zTeRMNVf^T$KDkL;tY+isDUBcok+#VNA9Pe6XxNE~9W#=O&^b6ZN<!-oj!;Pux7;vR zCUL^VPaQiQAr&yj@<KvG$(Wc%!X)G~Xtd^NS*vz+urcSbo=bf6bN+=Jo><fI1yjM= z@A=4IFycE#?D?D>pY)v%nzxo1629RpZkg&62qpVS<hpHCAvz0K!<>wSidIs0<I!PO zur(NyQ&17}%p0jMcid32W=4QzOv9Ia!w5^lk~<PA#vJs?Lr%(6yX;yre?v^kJ(Vba zfeeiOD>t6Q*n78)Rv#e=J8oFeQZbTH$({vH*o&R+(Aj%h+C|q6f@}Y4UpX?&AW^lu z@}0~T5~{gU4VtqG@I3d-UhtB6WsG^c(73+b=0XU=nfRvjKfmR`C!uzn52BMgzEFFC zul_<AzFe!CQAkte%v*E!sy!<J03ZNKL_t*UJZdu%n6VH}U`xVhyr<-ef;(13yrW=E zNrYw14ojo~^hk5B-X&WQu$H|oAYsm4Ca7CNQpTcg9J4E00n`gDJ0b!~<)-!rq%+#5 zUO?a!gn{g0Mq~P8{WVd3Tdwyc_G09IT6hphtDq)eN<)H`tFw=940QZi%`q9o3mW0T zh6}ac)Yf>-C4}UxSo1`uWZ04|TXIrD3@-#^@-{nnha0q~N|>FpV8nY24H*E`Kr6o! zrm8yvc^I7NWv16NrJ-d*L&Ag|u{W?Kx)xgbPa7Hzge1a}B3APEv{*7qD!%5IbiAYF zmK$n*k7YyxdT1D}7)k=ByyK2F@5s4fslzsq%OsSNw5J)zB(!AQ^FgQpv=bky99l|7 zq|!z@Pi{j>K}{q_dP2(s6_yRZV9$tx2ie%%+M{Jm$$^9yIt&XMpaOF4*hn<foRDp- zM}W9TL*|2>&@Q@>buM8<p-FFWcK@F%JBE1N;qWf}Pro(1{(rA&WKKhYj@4c;kieJ= zLf$1h;B@A9DpkJrcMa#k<<@J_%!Oj_r?xuRe$!wec&&VZVZ%qB2w5;D)dZ&^XDOkl zySerftM-j+NG8EcLnU+9Tj4TYKv5)DLqG10nXu%Amd`bd*|Mf(Ov09$gp`<j#@f8K z<N^lWvt%nfSS@cr&6Ky?@?LV4c)&NY`q=>i1$$C*F<bgNFO*<3)_!n2fMHn@8Z3p@ z5s{iX^kkke@&{}{#)y~^a}vhf5-?@Ky}}D4rOq}aWU`bUQ_O1ZZ3F4v0#EY@eYS@k zu>ah4_dv&iCC{wbvt-ATPkc+I_U|AYek*xTpnozV#?1JPx7;&R{&7s^JB!w*y$0U+ zw)H*?Gk6fh-d5FL<B=aN1eDrc_kQD+icf+t<-Bmim?z%wiT^^wgpLW{bIXC2i5lsM zj*uN&4DX1!WzE5V#%7R>9cuM<%dM1jPapwzd&WaD61Y^vbQ<zF33Eu!LY>a7>iUEc zDZeCP!Yz$bagm%UVyyK<t6VszOSIguC-o5j=){25$6Ixb2Vqa0<kTs9B04fZTQ_HI zZEz(JgDZrm=$v9@u4vrs$(rD@_Oh?GmF7ZYbDf(y+znr5pr2COuBi^q75B#xWn?cn zU9SNZ`gXh>yy_jFXn0^M0F}jZV8>2la1<!s$@JwcQ<<hCr65Nkcaq8ka+z*`10^*n zIV}&AESPGyxTB(GK}5kG!-$TUR0|Q*B8P;``Hn4fCUh)GI53f9IAB7>L>GVLX(XY- zUQ!BjUFQ8nH^E8{(|NPK;aSIyi%iVPCFY5#bqiI#Rcw@|+DdBcY+60vEs{XTO-wUF z8paY(J6sln*vUv+$V#_i>m%NetBrPmk`I8C6$kCXU8R*1a;sn{SFug?(@GC-umsGR zvtY_Zma0~w+)T{F9;%gk;Z3Z@)NvOR_H21ejAcrvnyHb1!^(VE%K$f_Rt`r(!f$w{ z=9vV`Hza(?-}4#2;(HD}@I*k)9UrK9!;S+ZZgp(JhI`)dYYyx^AitMhTR{^-c2vwr zykI*gR?FDmUyYU&F%3CTh{F<-GNxlsMn}N|?|H!xv1LNd4`j*)G^G5<OqkS$lA1XY zH34G^YDR2{N%W{_2mJ2iW}p=8BNNxB_jQzfVy<;mBtf}3%l|tna_yB^$>YHo1!u;z zZqOB9v!z|r_L(cawYlQ0H7EA}%eM3KyId|q%$YFdTAg^U$}<-&xIJsX?Lex1G`p32 z;=mi;s$p~D*A1m41g_bt<Tfm5MUHJH{cSWf@0lq0A#O)WMNUk?jEpBG<0i8I$GoE! z^l7B;abQFz{_#j_ivq)E@=!Rt+Dy~0F%eS%r>vf^b`XwJ4;okNW5(U#IP!pmS`B?c zrTVTv7_IE-xzFOTWb7pqNhG0h)kmdDI#9bg6Dv3(rXbTe-gpW%x-lA`5`yenJ$|Co zV#F$>y3?^+kW;W^&zfAA&XK&tw?N3$Gg)pJ3yONnTQV}g$!!@+sMAsS`BSPHZmjp> zpxV1+L_kf#lJ9xTST(zqj_;^>C^585w7pN+=&E?Z@Rz*fpD9TBfA}?XzU7{fxn{+W z{4GzElzd4+$%YYknp3WsGWSr7W8c$iq2ip~4G}4KYKdF#fH4DhkyYD$Q1)%8#e|y? zy885yDPu|vT?`YR33<S<rR58@-uZnXBNc)*A)};YDw6a>teJjaUwb`YPh^bb|E_3s zlx|2c959SD-90bc>{-M(J~<Gd{46<(xtQSDvs2UzO6cptz~K=UTx^1e%9`M+XoV}@ zS$p9K=0g6=MMm%N$P6xoFy<s9EI4Di_7E!T!AmtuUYPNgO!Y)7^<D*J8V$P^BqHY^ zlvSiqJ|}T@x1AgKCXx!cL|!*ye^19BNJ)7^$qtu4n#!it(UEcBz()VIkn!lCR%K4e zL5*QZ!k&aoj?*4letZXd6TS1IMC3%|7>ty94qa6axEWTKqmY~pEtx`+oZagfYH1~$ zZdB#g8nd^96yIKM(KD<21Tl9oms0TuC!&m2?NC3#vmT&jeF93a0?f5ou-epCB!oI( z6V2&jNmXJ(3>i1v>A$#9h8r0iGh9<Y@$Doia<cEeJ_o0AtH`CObcln6+TTvQ^hCd9 z&76>ikR3I1Ua0t-N1myf^DACh)9@$$h8w=t63Jc3|Bd(jz?xt1o)19Dcg%UO_b-uH zq&u^ALQKGxj<-w{8M*az=+0|dtyf9bgS*r*QTB|fB||pk{F%?xGknH(+;hj*{E45_ zGRH7w$%uqCcO*ip1rqcZ)JzDNC?}<lm@?im-gtOdLqbJGEjf6|f|b&L6DBnNwV%W9 z?Ab}vuSt$4MEIY6`&um9<?&{(P*hH{FfIY6!9|>;d4-*PZ6|z6(>rJ9n~N$lcqJ0) zI*{}-4n5GWuv00$=Ogf*+d*)3O-V&VCZ}daK`C656T_yYQX~bm-q4>AOXXM4h!O0f zzFmypLN`P}%9v0;XHTH0N|+K+()xIriV+j0#N_PRG7`LUPRfW|ZumK4ratp*DExG$ ztb`3EEh!_Wj3l$Q!i&dhRyx`G_q2pq4#Ffnle3k(!0mPq<ShA`y&eum`45$5g`K8$ z6-M5GMpbm6xm!S|*OB-c)$surdD%(jG4HJoJ|pU1>vzg-f|L@GF(o1+0~Rb8F=I@~ zm?<-+@?U2Jq}nT6IdI*OAS2ayz19Rcp(WLlAk@!I^}fBx(wL~tthKDE#Ud&Fk?cYz z8%i?vRN9KBOjz;48yY&cSYird3f}TeN=U~Wwj=}!?-@~&i2D=yJ-(Hw$_-&gdIbIj z-;3HE45s79iCO3|4xIwPt*DI=hKvYHO-4q_UcmV|B~RS&881wTIgkJ)Z@DFrma$XQ z|Dp%ac#x6XZ*@CQv5xgvM3lcoX39>#Pok3G^wkdoZFwaI<H}a|ve_I~T-_@(xuGSS z3wFzON1Z)uu!E~JxARF~a2670f5K?j*K^%ZN^?%(^1-0ZZ6yzV;;jNx0?nGN5K?2A z)oRHk%wWaSZ!>E`#v+ZNX;L6zYpc2?<2Nihu+?{Q+~qYjmYjr+EeQ!VYwm$({gjvq z_w3kfsPFh?TV}+<oKDnqpHgcBZ^m=X-%RbAr6H%0x~|8^hiY>h<!QCN@EskNIqxK> z$pqc%u<Y3js=mdrBPAkYK|~}_Rp`kj4Jm<4`;FHQhVqhFY4-YPq0ncn^}drHAa6Y1 z+4}IHk;hiLXrP(4u{{CMDI!NgJ|#qq7%7-CBUARB`-}=j#>aBvhC0k@t)?PEGJ&u< zHY_N_vJMF)xry|3A%*m7DLDZ%DniC=`76HWk&atF5b)pg13&PFCo*D|THxgTlCMem zGjCb2<d%?vijsuLSBQPBjFva47<&}~Av@mjqd)fSz^`O|Kxd*#J6G4xD)|{|Ek)dV z;yurdnesgeKV!#N%-Qle72k2kEqiViKGZO#pyHNKbR_Iqkg1GF^!bOXgc)pio%E^` zt~SQ<4Jk1PpABPAIzMv;t~FN$&Jfvw$;+G(R4yaiIJ2qQi<uiIC+;;F$u-;AxdzZ) zsIJW!QsA=9HGVxmocRT>I*!k)zQBu?`%Kr0Pvm;r1GR3Ubb3<^r2y(yfC4wPZM{G= zCa0vLq$2W;R8%pQjDTAVKT`0b>Se+XD5yw)83j+=Ghs>2m|MR0tztyP69>k+<>tys z8&T16OUOpa^j0-cZ`=>HU(B?LML+EzR!Snd%0U%ODPiyrJhEfN179#DVvHpf5}^eS z)XIEwa)SdU8}@WOGh;@oD9p(7_Zme@Bk!#fdAQrS^-k%)U*bnMZljjh`n2Olr0Y(L zrYW&fT%80Y^uR86aHeD|G$3yY+0v3S#)3rG!v|=n4dqxzavzG*=ZILQ1T?bb$2|L7 zvq-BAOUK9`;Es1()3TxBYu@rTUy$%8O1|Vf_WTcQcx1$iO!L;5Z}}x5H6woH9e=@! zoCOPN3>(V9{Ieew#Zo`U)J)h(RGcW!EYv|4NJrV{Rd+sEz^EDBkrL7<)7c0nKW9Wu z#vN-689(qDEl)tiEs5^KY%ooB*VCMsC*EkG(g?1>VBTmn({|~){T&#PF=IdA@8H*k z_DayuYgs%Hbr+cy_QJmQT2Sw~8jq{E(V@)5UNy6w8qUnwX>G2~60buPf-4EV=89*8 zi+SNtOVo3I3)Z}0Oot_-q9et!WltthQY(~`p`s%Z``3kVyC%bWVMglN!Y=c4tnlfO z6_z<$N^-SsF_wf8OIFMX`H>e=#(ZQ6;;#kN90;k|GhxCG>ox8bmB=U#2^0=yw#VfL zSe|(&DjrPK5|@g_8MCG(V$YH%ex&0U{1tahh#B)t!3`}HH4{pnFsv8}gr6vFYr+9| zp<zcy$HZIF60IhVWVU{C=R~+}{?k|DfestIy0RYRq;>jU#(O4=A2>LV?}3CwB8^%j zcW0t<Y+=J3!<vAaDMm1Wj*gx1S)qm>A!AZ<zXP|FYUlebi-?eS9C+rvw&<DEnjw1$ z&KrfqCroLW@<`4TQ~rkki;^4uo<H+*e#3udO-?G$Ny-a1Y+1156B%Dn2#J9N?)C(* z8&N7|v=@R;^w^9w$uqvoaRZK6*Ks0HL?@EZfiYvP=&Y~UcalEc^Gp64H8C$t`7;@B z<dCtnWK@*$B^8WW(K6$ST;xKd?R=vMhCWNJ)V%<SYQ7f3guOgE_DX~pS3soZ0xxU` z|Bv6AE47>%(2vh0#iuQ<IjiX|vn{w-D<742=BhV%IFSoZl0NLkhW0XH;}{7TT!~1( zrswM?l-4J<J4O8mR(#2rF{w6W84W8?Ok<_Nexue9J(*(7Eh=S*^|<rEtJ)nkWlq4J zikgOJWpcTx;h2mm8@^#m$|rIjRBd*?C36CV2}T0wh$$lqYT$-j?zrKWxoSS63)bl0 zU7go4#sDL79e57><EnnQeEFUYf1==*{0Dx?garwYe551i_iX6c^EKb{f$xCtD1d~R z9k+yHwamGp;!(3jd}>Rg3L{eE63cr$42q9^&1HOIno;~rEo@k)HH($Fyb-L_Nn8^~ zj2Nlnb#VdClbh(^j;PfRmL$|{DJW?OnaNS&GQ(UYyvHzAQ<liL;~sqYwNAfb94kEA zs<K7exKJ%+q1gC<B`F(f{)&IWuwcmt{>+T2girla%q<miBGv@_!rz@Kf1o%Keg(87 zTE-;mJ7yR@N!o2a(6cxF;RVg<iMB5BgloqXcQv(RDTiz&-TEkl-bnb{TH4Z{5!6W= zIw9homRels)-Ngg5lrWOIjyeVz|ZqSR^BhqSyTomEcx@<{;*7o?3JmP9R#$TIvni9 zCf1%buE9V`_!=G6`gO%=1!o6~knUsybDG6wesUIg8a-mp<SIw^@n!Z$rzX+`0@jo~ zYaQXXQEmyp6nV_DWb3D4p%0l%bm{k@IE`wN5h;ZfXJdJQU%ZvS)`uPfGXZ|~yl}^a zf|7uOgjAG@oUsf`3tGucZpaA8#WS-sEP2n};8EnbbfM3BbKX(+oUNG<V+kna*|1t# z?D@zKyyf4sAf_W^%h%+<8q0!`XLh`Rh`}L`JToDsB;WzVCyzB4^UoyQ4%St!5$ppI zJL$Eo&qoVI0uFpv5a=c>wZrboiJgBi9B-4z$7e=Lgoq@cm}9X@gGhNKre(*NjG6-p zQ>H|WltMe9W1+cH$C^wM<VZHTK*viZh{K~cxGgJEA?rI2mQScjwK~Wps;-Dw@GXXl zKk#e*j$7tbwEW18s&@DLSF9OpBrqf32fkFJA1gPc^->l$`>jYRFpRh(r{YG<tMv&r z=LsxMXdKEd-D5T^TVO&>q%Cj3m?iHqjN~LQuuMsL)Es##L4C?zk02MCT2r&&U%2u2 z-LYzO*}iV25~=p@=hV)cxFDyY)<fr<9d*rnW-i8T(Iq76^f-GRI(mA<*g;yEz0&L+ zUsN!#$PACwfMMWoaE1`*{lVsy<Blr;tZU1U&>!l<HaHaC(`wAxQn6&qo%{?|2COHZ z<ul!|rjSU}YDN|6MsW7<(A&Q{{dwT)+(_`K5W^!G6;l!XQptpCw&I*@Szy>>DHxG+ zV65PmJ6>37PZ?;wSj$0T$e2+u(u&3S7O?e*{?-R6rC4@k<iyM*GHEH<@Q+OScYMYK z!!sdEh2$hWuxF<x_9J)H)Fe8z51zz#LrO<XNy#l=@kGQ-Lu~haFSNyNd}C{UmTone z?)K5sR!Ov0C<5aHnHvAt4!vC4@uv^o{MHe$rlep89C)E3;~fp57FaG~DP+Z(8J~-R zvtq_AB?rbdbc`AELLl#Iss~!3R<_kAKT-|rq}-Wwk#iyD-L^IY-trx{Z1`_Hu;+<? z#~&H#(>9*p-20g;iGHDC&5FOq^1#MV$?$ybKvI{0ltho*jaNU~vu?`>un>y6cdWV( zvY0hO(q`gN&IwrZmWUSu=ENLWGw0|0o;yV_m5gbbvet}lCuwp<$BTNWBLvtz^h!;5 zjS35+$EtgTOgTur9saAd&ewv|BCcZNkEvtlIcMh+z3!ykyo?FI28IvLj?VKI)cV8o zJR8kiwANfLb6<}=!ZT;h30AB2+CHPX#+r(eo~~{(v|>rcf-!5B)Icq<N2nFO@%!!2 zPo6@*HgRbLdo^QD@3yC-V4|o<OF<$5sg)isP&!1xf-MKe+{hT^%tskF65v##uesx7 z%vKzhXyANqB@8G8IxviAI8ZU7^5k0wTK}Hk^OnEiEe#PHzNh64OFl8-k>BYE3Mg6f zfe~|-99RGeRyfi}O77%vfjh>00B*H%F+%$ri8PEv(LDz_@LDs6g0J;JF}|+rVVbQh z_Q?QDH4(kp`6yaa*0elPu;U|BVlut}ZkQ-FIL7eIf^Yaazs3Mt)_jZMJ%*e)0c&8! zo`NYIOBTQjQ?CkeUX>*ghB*gjde}82>~(Brbm{;cHvb0}e9b*0K5)yA<ji>?lZLOi zSoMs^hC-g89aHxFz?W>vG#x!Y;`@=0TUoWLm677poakH`^afU4!H7a5gGg~g4NK-2 z4%$;YDE|{*s&wdB@WO_-e8IQ88#t2PXed-{7EEdQinXT&;iI73-ni2NY<vP==uH5D z?#JZBi*XHGY|q-<<6R%wt9#aNfP1=B@7XI9dwZp=JZ@aBfn)5+*3}L~$HB=fy52n7 zt0()>eG;7Uy37R~aCq!>8sv7KQc<xbW1&b!H}%`GBPL|S8Y_NEt#pfCZ{{xkL_1(B z&}KZy;V8ACD>a1)m@s3j#IdQ&>bZu_j%HB+Ga6z($58V~LBgDtj4>-hZd4uJDPHl0 zNZi^!pfC3G%YcwnP|1c$*$;bWM7*J-Bj%Yu@H_sRUoj;i<s*B3q~vR+eBzOYl1ex3 zge@gImW-&Wh_Q^>G3S;S3<YoaoPeAd%L5;Xm?+@KsrB|$j66KG)`flCHI6(kex$K? z;@!KAr_dew$DveV_{p2Qg$=(`L;9XO)yFMMp4bpG<pX2ZtSES-=7u@9d`82TuldM= z&w0;?fEQ-WFu;U02^}j*nKQ;Tl=31Q;R1|Y*(2S?M#rp6$jg}Xdtwg!oc~P6f+rd# ziu!3aV`~I)3NY;afTiXQ4d3&gmUq19v2F&UtWXZ<Ina>vP7D@vc8}pxPx`gaSi*Lz zL|pft7A*Y!IHzF2jwNrD@K>{7$uk*mX$T|*$~5(KS4X9xPbzy{=hs?+%7yTRxD-gl z9!nQ#)Wn~zHja0nIa|vP=bpZy?oMKRPItPeUvOPo%oVvX-jh09IRsaZtaC#05Fvd! zm<+B^Uj8)<{q^_}dq#-161rOL<XSo^_N)cuY_K5cXhO%5M%w*KLV-xDexwM?)krm3 zeLzAgu0=tj0enkJN5Y63R;mC}GHN;<gKjT6(!wOw<Sk>PTdZZyohlG#S#u1)1@EXx znMw3(2MWtb&+m${LSaG)iZf!~v%&Dh6Ce1Rf6reMlaTW@F@NTXKg#~J(@Mcj%(le5 zCn4iN#FB!XJuy3;xg+Di4|Lqqvf?lJ$Q#yVN@T4FB(84!dl-mboTzCxYStooH?2aI zI#oioW?Pk-;NvpP-K-@c8986`NX!E_lx%n=k$gBH<%O7UfsiFJIiI-UJ)aX%5pvJp z@;(2=l9r4+QVMq5aihf8dqN6oLJnF$K&V<T(ge5D0Uhc03~4FInDGrM6LvI;FLE^R zmWY%C6H;<g4g>^js092sVjphEm{Som^E6{S&^J1jfr}8DGLiS>I9Im=IU8PhA5k)8 zq%>c+=NW_LK*Nd!-*6*bLckrlzz=Q~IVUG(q1v`$tZOf2sZZK%wF@d%j0TN&r`H*J zQXjsy?^i@U1}=KVcwzit3?C$VsaT1wEA-AQz4LbWbk=s9>jf9=lk;%O^E{2<g0dV8 zYQ<CB%xNwzR~ubUzyz1MzTH4(%*m0^coS4h!-|~u0(K%PgO%gaiCb!El&1PFsRSC8 zaGb3sB5sdacr)0VMu^jfF@_O)8piCYWOH)0r<;U4=vnDJuqAVr$`Y%XGGR$UO2|Pr zvzU&t0$UR4xR6?|Z}JNQ?F4(dX9@&NSph4a*zgTM<F5z_`N%(U$A%?4VqVy((kP_@ z9Pt^J&k2EFknoR;iTK1bRwnh73ExwY@{W%Rp!|jfa~<neZ4Ihpy1{6pQBH_k8)e#O z8*Q~K{nJCLBGmzAU_;!~Q1Qqk4ZmSa$A*dndjg)sM$D<0k$a`)RBN4o;tg;3f=A#l z`Jee6|1Vz>lQP2)@_{!%#|u*uN_Na>HEnc_ZRq3LTQW8T+6s5RzbJU%zw#Bory(I_ z#|=+1jwO09J#fQ!ti)gG<o)hAFqH#$#hj^9ijQ|?AQ?+bMox_(;7*^m@%Vmoc5kDh zLP|zSs3@w0gXlIXMnal~k|pn@2uKB<u<SS}D4```r}0O}h>|fGd(B}3!EpmgxH=7I zLQiMz{5jDM!ZQ;^%$q@hX08NL*ehZ^`I!Fq-<~#o*9FAp;!BSn*TDZ13>XOJs!@$A z3O#e7#pB{Du3@swPtyOc*Mi+yLvOwK#YzztD3jq&wEUdR#|RxbuxC#ykmFtuqBo3a zs2Oo1lz@wq?BD*94O=?KBp4!|HLeW^n5xAtiNz=mX_%4Ga-d?$lo5MsVsd|=JE`-6 zmRK3RBRwTc=>u=Hu()Tzgqks(2JS}9Um>r-S_^@YOxnO_wygOl7W`WbmLFKs@Wcx% z{>XurlpAJh+{a8XSpJfG;77jYBR^2`8UG`1*s{dvr(|l}AYx9X&32)9o1XF<sD|sW zCdar*87XBqB_d@^rd{80u2!E&T8*UIMl-i9D`qTYdEc`l=b5#j`;`o3hKO9$k3BmI zS`K{ViISfK8@^=DpLtg0o@o{37=e3DO`Yj3QT>l{)N1dM4n&s@B{y-6-v@bcO1@yJ zg;`6$hz0K%vu7#gVne`)hL((&kQrlACPd6A*q)dH0wJ*ic{rT7`Xg&$xjJuB8@i|> zkAFysNm$d-GFDL#ky0`hXXn6%in07KmX0@!sCiM{?o#CrOxTl?3hPySS&SodBWib_ zmKS<TV6U`@wefeK_Cc_T;Od?KQy~?c%ULdE$#$Uh2`=0f_QXSL2G*BTEuOt9M7FQO z2+Y+A?(%P3pZT4NuCM2Wbc4wrKJv2l=59mB6W{PFG6_^d4s2P`k}#HI(^Uo&QW{Dk zc{e)ISvnm;Auwhx!&${dX|I+{(uz#MM5V|lrN-Z`t$pB0RADDE8SzX&KuW=aj<ICb zdoroiIx<!y%#<8alaOn^*SGX8g|%bKn3^325(2iWOAmZY%ipjjq@-ZWH*9G5sL=DT zxZ!|d%7&D;RKSi$YTodX84GGY5|eYw5(pS!`M`pPH8+gOh$Q~a*lL9ld2OFF_eCUf zxJELvMe_6p!ZxgFw1(*<e89Ijo&HWuO~H3;_*?|yH9J;30Wk-144-jF$308F!tzI6 zi1``M?AcJUWkya(&S(6Lf_wf0|ApW28xl%>#+E4|Q}O6hpjKradJj>jhmH}*zZ0Ug zBa)yc^!wj8-15&9{9As`3opQ)7g7#nB<y8%v|3<ASPEi7=A<MfyyK%sQ(2Gk@0c>8 zBxA&cdmaUDJ*6}TzWQ<<NlnX)97~C1$t`1E*f1qv$CQc8fh8;E%(US6k<Uq)D<vZl z3ZtN-V@bxIL|0nOgq<2@GpO(bU-?@-zO2Mpohsf-_{jNK?tGMXZi<L513S#X@M5m; zQ_g8B!y3<Cb3g~(001BWNkl<Z6(O4eA;At>Q9BUCyta|K2C%pP>b%dK`AtqUHtfK; zdR&McZ8|$XV3;UIGN5L|3pHcLloUQwBU5>9NQkH@sb#@;v5>G~O+?8OC{$@Rs{giX zp)Da><<&p3Al1b@;g*V0S!ywcTuc~b{rV(<KuE6HWJ)IB(n2W_Rw{Sb9`^Pi$55$> z2?e%ymB^kQD_-~o6Iy<t<B=Upc9i5yx#hhQVOJzPP!n=P!+|9`o;c7F6SHQ^o{kYS z5+Su}Wj+La<a2)Dfe+l00kPsb`&n+R6-*#mc%ZdKM4-CDc*C6$lJjV_Y_u14_@b7t z$@r2C%L^@AL0jG4JS5Z1Z-(I;c6_2>!k_sy_v|?E4?JtAZ+PObdBckTiGSel`J7+! z2kv=cg5ewHG;Fw$g}%d55HVLp9jXPknv(XpW*KWj#x%4X81oDMfepXmZ}~s5{F;a# zSYlY{mai3M6?kEolV6qO+|Y5$4?b$siiGG~=8;T{mY?y=qYxUs?8OYS&W?1$ij@wb z*6R{-4$Rn+>e1qY%WIa3J?~iZj+Tx`evW0ro<a?9M9GeOxpyLgr!o&9K!X7*7UL2B z${_x#lPt+xSk<}{vL9FCF!};1vV;2Qbg^Lvo`>tU{-0tTn=91QYjl^NmLmUa1*^I0 z!acX7*^A%m+P1o{0y}b^<$MS*<g9q6BvVLA<;h`*<_#06oC;lN_=?WSJvP*gxn)8@ zL&t=UgCOy(Af8U!ci`vL1RMxhFy=>~V@gHAjF6lJOG-(`iijPdG*!8jYYB<;RXe6s z%tX^QauxI}z#W-4BsuzW!xF;_&-{|l`OoA$F=EArJtYla@|86B0b2^y%R5%unHma4 z(l(C8;%#}$mlS-<O0OelLdx&BXQ>)_A|XqxFeg_lxmwi)%!I1`dOmg~<A0~)-g>lA z=fzSEd9>!4k&y6@>?nD}%21ZFC8XgGe8CMH0v0^-IZIYNv!`aGwmYI@E#2daFL>tP z@W9{E@NX!&0p@ym-zg?3*OJAhePkY8<=|xKaCd975l^hSWlqfhN7$P!Tax8xUccqD z@9|7AWkhCHWlPl(f=0A;!$pH}!#z)c8IRI$)W!>7#swHMqcJt2QHi2dCFK+mC(f`( zpM7z$K407Sa8gxPSz(b8=j^k$`||(reZQ~ao`nCNDgP5OPy8+aEeW@LWJ5~HloF#k zGXmab)ZAz${FW_uJm}c!HK9g8uZV;z3;}mMQ%W$;NVsPFDG=-4sL18KjufrFrecd_ zLrP9bNX(;ph$R&Xd%iFupkPPFGYe(>hSV%E(v3ExbXpG%%awlAKX%@kJ)9<oK%#&` zo}*KMu{{$sAK&%EsS*XFFt%}G?y&E%7n3f`iBs2}Ic)6(ga9w7?~WJEm*Xab7lkb6 z!9nJQKQ}Ir`y)s1@Nu^q<koz}jqvV|Y%$D92q~yk*F}DC91t-jW(&%^HRN2=vl6AY zAr%O-Cgv8y3dkh}X(h#I*hs|Tu-%dJ<zozccHFa2JLRnQg={+m?Zql<bwfzUPyDGi zpTn>^ku=0=0Z<E6<+iA9kA2UYuX)Q)Y+1483p)y8GOqcGsdlIlmTPtr7ew-Yq%4)9 z;VfP`Eg@GVESd3%d;KiWluU?OQWDcsl5nTg+e}aGV04W&lD7=zwU7*}3<@)<-8&Ix zdLIb7qhlh#`<jMlc1(E12d=p1d%1TPOxW^!8Z0YHYG$h1V$D})ykpCXfPbK+Wx*ZS z{3ZXMpZKpq1e%rwU&slm$QAbKHpV@Xm^Y0ou#}#)wv#QdcqHSVulbDSFZf^h0}YQX zS@2u_jEdjzku9B=Bq0qkPxJ~uH*CqV{Kzd8U+ZuWy)HHIkG(4*HsqdD6Z)mX;ZE=L z9YSgE-1fa;34E4_xMRwWsS^3_xus*qj+7|@@JOz((_gV;s}VrRhFdD8G{WBYn(k#( z8e)V;5n_08+YoPs4#ZX!pS^hQyd0Xrn9jn<WNe&C-X9^L%mw4`DLLi5T|EVu+FyWB znTrZN`@#=nE-?IF4n;Z^=!}2A_hkQy9UCURR$KebUbmd{Q?wd|4_~9v_}x8KSJVU& z7_ZsTkt=JVWshaU4JB|*O+zj%Tt>%)4Lu8tRP(8zPzHFW(a-NfoqH12L?krQ)48;) zl#Bwyge^BjUI^@L^sWBs!U6+NgwAxiZ&?Gsq2O=mF_LS$k^hdFpzV=#X%p67xoCyk zcQNTTky_J$IVll2bFMJlQ;6eIi&JRy-qngp8a&ORc7Cq*2ub|fgHgm#sMu+T3qudC zVUBAE$O-sN#}BM!tN)JQFkw%k2hWOvo`S8yKp(_`%jEvpD^cQxFMI+%vt>%pJAT8z z;jj2>e#Ngz<)FTz!!l>5#xfN{#;{j}j@7`w(j%fH<PR+QiSPK4j34>LTV7G|ku8Cm z%AYYzRaXaGaWArRpo*zw%?E*_7Ty(W{5rr{?WP(8RO|(89W-)`Ho$>E3r4Gqh=K_P z3tj&qg*NCFH|(&a7^d{(Jkyht(z9d9l!6CRK61?!Jy-15k+A1RQu@a8xx*1el#$7D zEJ$%eY^#<!IV)Q(JI^j3fRTMc6l{<B=F9nO_9$iTn1yOQv({WV#14Ot7ZrEMb>s0| z?l@}vl3H&J6*{%qoi9eLZvIAeGv}b(^6YIf?&*j3f*M)zJB%Wet#TL&N-UvT;*E~e zoRorVLTW-*suD`s)Sa7Rsi^UiE2WjSBuWQc@F?UGEO}r@LPaEJuDd95A-OYRW-@m> z4s%PZ>;mURuDsPhq>>x6=7x+NVEH{Yj~FaD5dn7!T^zpNoEx4r_i3>5T{w0|K?-aM ziFm`DoQ#B+JN9g-$#`T!!-PAjCvR1i4gpB9$5bVR9$+x2Lx)9MrKy=~YO4`RxT7SL z7VL>V1$*{b629S2c}1bfqeAi3fl?eoI;F}LRKlK5>A2#CfEjZhsp)BHdE$?J$A88D z!S5t;cfq3$ATc8XraoG;ReprqPqsePx|P-Kfjxi2gpV{d{E-P;Uh&VR!MkI}&vaZ7 zk+P#@!7~eLELQ{sT(e>eEJ)dSyr((pYEovjGA35u<6-5R!28SX(TvrKKNAtLWu{{; zWUs0<Vns&DoGAr!dMajQY=kV`kdpF=J1Pp6Iv$6(_>4pVddFNo6i|V7^s&}313tdh zBrrWGe(VMQ(=RPSBVPCy=h9+3>cM}x<vg97nIpgLC93oJRPUvEV1Fj9IVP~4#)#Xq z*L6MxI8feddI~nwRN9VZL=^NuP36^up_s~nQdAoXDld?!aEY`lD%M1#Lh@J)J0e;N z=1gQfnrmYj5;3J>M@rAeJ5nnxKw==Lr{dX1RnNo)iHXQDTodys<kjBWdqPboGrw)F z*$~R8*AX$NC!lAK;g3Ah(r`n|H3dCWZYkLE1J^9|D7oX2Pon$fYC&2uA{~hlF*n?D zr)-d(4|M!Jdp5$QTCT~Nu%~1}L`o+ZeCYGtBGpVobviuEdL3|`9x+Dng-C~SDhJ1o zFO=-r5>oP(Kh-Y0$FP&nVCxCVXk`-W`!-r(U9rTlXTpNt^Aj^}>3AgPhW~{Bi~o~` zmam8iu`E=-Ab$qT$=MO>knSWswnCDw>3C53#5ep0R=i<P!aWTWo@jXBH?%ynWy+i> zTN(n^B;@SH0h~|~a4#D6(8G-9UlFlTXceDSA&0taLa&hv^+4=AfGVJ&CE<yHDTWml zH*!Eb5Z0O-)^gnjteG*xu;D9yCS=8oEfsU_`9j7mKl+_}t_^%F>9ZN-9FFD3M%K^X z!%EsSZ`n(~I#qS3)xC5y9R5&nVKmquxoF4DZ0{?*Q&G%m+^;zrN1o$IIgvHnGntEd zA+^oCI3?t4=y|{ubOIB&D9Ak}TMRQYji=Ga-;1FZ$(`%WX(hETH><G7*pJ-@WVgGO zo<u0iF;wD?IL^e7T2X2qvXMx0!;+Fc5lcEAWK7#JWheN73#~CEz=o0;4J9SlUPU!% z)OuoCVp?+U$ow|8;z!Rt-mxO$E9RsG-1DQNDN}K8d#2>fi1~_@Vkr^a!N}R!su_O6 z6>}Df8jqNBM@YgW8w`&uh{(7VS=VJ=^iq07{)iiz-azitAqT>U<z`0iYnZUouM;9Z zvZAJBN5q1EMlS&Sgor7*5<~(@GK^LxZb9TQ5-|-kUNO-sAz{Oso+-~<@dUi*U-Lin zcLa32XHHDVOzfDB4KY)BN1S%A8W}<(p=>MO@G~VT4ex1qB;i_lXHynDvS!YnoJV%t zh#74ZZ@OhcKt{kV70-J224_dFVSzK96{2*nkMJXQ1gS+WzVQY&tGVKi3C}XIRWfNl z@{R>Rvu4STE4F0xe5T_yPZ-|wAjQIGwW*)D;hpcPUg`L4Wk1A=%dEGbg*x_PEfoX( zY|+KD;T)OA1*R0|6(1L*CH+W3Y|p+}e`YE>c28V-IsFAu%JBx~Qo!W+#RnJcZ>Kh8 zd*(wqwXPk!xh=KYTqhcF`LLmKT4VjGO2hL&vy(^{Zb!;gRbQyxXU?3GDX}8;zUGNq zb1-CD53uaHrqsO4xpF7ski@JN#p9ady<++rV5dpZ5O3}b+_m&tSIp>1JiOg#C+UD$ z6VLj%VveDv;z<Uy71vyI&ozc;wtUSV!v;&o4Uqx{J6hhd<{u^hjYz1-Xh;;cxZoQd zpS9u&*A%?wioc>_K?3Y}OUNzHlHCM;Bp*r5cN`jX7+eevjR8#`Na&H$$`w7O<aa!< zRVem?J7QkxF%V;zam6z`a^_@g2th%f5uql#4LRTNij;<18h%uZch8)XKk`7&JN^}a z&HqD6$6Id2CvkbPO9JLp;>g6lku23Ggv6|vvLT}7fjK{s(Xt}rnkQa!$4^9jR#L_X zo_J)zEpuubYC!?6D44P*6FO@^dG#8C#A<d6t>IeL$i4Q<jW8FTYU{=q7J<*x+f#GR zhLD9=gee^}_PpX=E1oGe3${FxGvS^&GXmDa@6MEZ=b|+&Jzw<6hW?)JMlLPu;lf7L zq+WiV&>xfb<j%GyF&XBC-9q?3{M%EU=P{q23$3F)k`2BTCwZ=&v7@hGM=#Qx)qCcj z%-^_#Fum;PICY=cqiN&7a;vEn+V_cjW_-(3`lyzO1S^PqDF;EUkG_(4A|qulQAfv| z1v_@sbkuw=4g1fmDAne4qTdzd)Y4_;8cMq~hnkcpDl!To!#DJrB6WI>8y^Ag=3jdz z6x^_2&MogqBmj0|5m$A(K<1p<^JF@&f7^1yJ1nnRs6u>0&5i{r5He+|n3xd56}QY~ zH?!h~v=}D5<)8CTsL+BfANj~LYxX>|Afuq8<7>HI@4V;P_(jqIR61lUgno~QD824+ zh4*#`zPU5B`~yGog#`=V^JikXg_QIp7%Ey))@&uLK-PE%I^PH`74m^UWl6`19Y6EL zjMt>}tY~@7XKFg$@HH#dG)QUJNQQkyr9EjX^r)fIP8C`W|1(+|ej?zOPvm@$<&V7Q zo)rlbBK9n~rz0byAY?|ynjLS6sR+r5B$Mi>u_RiE1wIQpP(0Q(k34!$eWGb-tHZ4K za4JheCZ}<y1y)1Go|GHKgw}ju!xhi$2}nJoW=o<OWUA62#&S)?GZPwSTw|2N0T~lE zs_gqCEpQ-1rS<v*OG8OR$CY0koo6;)PT3ikX3?cH(VT5z@sW$hv-l5(U@!3FB^T}C zpc^lmk1M|ZY%^>xh69<iV{goK;h!jRFQt40@_M=!w`9wPJq;0tT6I?@(MKr9My88p znC7+gq$IvkuCxf*uokg-NkYIqmW*mt$#m?<$!LI;5Cb(Cdjf6f+*ft;n^wnaAVfe% zOd>s4CsS9XjF7~8Q6ll5E4e-+dJ;lPk0h<MiA?GEmY$wDEgO1bCe&OjG;pWc*OrQe zDFG=9enm^8tleJN19$m8YH8E>TsWs&?3pR=VyY{r*4Q8v8WkVd6db|K4xfAEXRV2@ zrA)fMkc67O{>CjSrQ(`4igkCjrwfpL&zfg?3KD7W3gx)EiP$^d)AN}f6D)H<ym~Q~ z*Gwr{Vz}mCu;B|Eo>(v?Bj-IShRrArr>0WucBnXmXEOG@<}(r3bo`zPZ}<b>@&gh7 zz%3~cM08~QC67EyOIHx_6&=6hD}G1Bnm0P|N^<_3|CgN}YrRwe839`z7H%om=}K_d zO01*WDy*rObjW4RbpH5knNX;X4A}CSh@9{E13NY(+*0z6nk}U-3qmsXG+g^Z)EDNG zbvJs{#9n%HY?lp3E~`TqSB~(-rx6yH1k2`()D)SEjUX4YiH;o`$H^D{QBJuX{r}}= zvwxu@w?{-~j;Q*_$69|PNiin_%u5^Je)LuQkzm<vb9d~i2w3tUm0RjbDzWEQ97Np- zmS>4KCPZ>&?B%MhmErYBK}^qrPE)-lF%2y(g&JfRhduW*)S4wNITblOdbvGatr&|= z<}TSxrnMYP!V@u5N+uXerXquc`ddS8RpJvj1{GpPOGv7{<})dfvnAv$KeOX23>i}@ zG9FlwGvgyOCQJy(h)B7j#jqDWch3U}IVDrBc;*!!fw=&w4K=?eSIWgi6csBD+kvbR zs$m?q=z$-hxY<ucta`oGG%D3xYevhQx736zrE`2MUQ6qBb`3e-@r4yVhFt5Nlz^oi zu!%maDJ%AD==lp80>0uaa=zt99{Eg1&pW;&<{9Xj@`~50ej8csQffkG3M6oVeFwB! zvms`~ngze&nJsT=_<LIJx#lAoTPFNKz_0mRt&I{aIp2~K(XpT<C8c0N$!8|aG=Mm` zg57oH9(TF)p##{<NNUrOsJcUtfHCUH>LImc&oggmMW+0kFDyt{^FYdk1&^e(eBzyI zzd)_(6FD>Ol{Z{b(@-&y8Wc@-d%wYVOy^kXb#9b1Kt!?&L$u>WaC4R~JT9lAWALYa zaodXTxbaI2dy;i=2y^VuOk-Sd`W`FmURp?;Gym+#RFD^}Y3C!&!?D*(D$r_nw`WCx z;T4e<@rh)|jRYwz5s@~Cp+Gq;mMQtD7_YE`7VRksHL{-3Q?VjsO2it=RPFDYn3#r% za>^2ka63{tcim3yXCjUC2TF5KMazUK&oq({yR_F^YOa{5i3@xNM5v=}%%^d!Z{&x_ zun<EoA*N=jJlcv0B^zceX=Ic8NX<9oWRx^)m~zD@5;E3IeH*q19+aMtkz;tzGj9oq zDVfr%!nQ)4hdu$ABVp}$b=7Hd)ym=)%Cqd^yPdS?PkG>;EgcgjNF?OSuyvn(%AAZ> zKGM7w4W%ceqNioT7p<}0(-HHT-|;Q~hG!mGkn*qT`S*MV5;|^pO-w*c&u3zh5sfD_ z4*^W>Mla}y*pf=5?LLnc2_FghOa6EE?3glV!hfWv;7Ziyj)aaa-|&uq#}y?L_QWi? zWy+ovkylklp1$qgeaD9!{P!;PEGHGdq4Sf|MvvoAd)=6XoJXKyss&v_O34+Elq@JH z*t6qGZ0Ldq-YY=M&GB{wOq5PnO9k7D#GOcN73<K&8%eF-JJ-I<3doP_2bWK+{_M;- z^o-&E_;1g7$>(9*=43Z}J_|G#53oa-YP~+ZzgPp>i)8$BXtX`a@31Fp4Eusy+Fnrh zIMkHe<8Ihfvt-7aPu%boSESxd(^054v=VQ)_7UbxNlEG0Qn8W$bx+BLwGNt@o@xm> zsrYG5z*4f~N>AEYgpWM|6YbGT5(#x1pLCxH#?&jSr=<{+&NZ*^=$UXu$SVaC2A<Mk zeNu%z31SL-g*EI^2js+*n3z<_V>2c|M=qyHtrqSxC683>cx2C38_*R!KeGkZ0vQ<@ zubF`!0K+k4HG(%jeI)dO;*l=>(+HVf8n4ci`P?MRYN|OWU@szdEHl+0u-!9bM@Pm2 zL(V&<OiAgOl2G%^3d@RumZb=U8?L#=@<>KU$k#+<yrWX??Vc4|wmkC4vm(`z1}@Rw zxS=GKXCu&1#M05QA}40gJqdr{GyjU3XQuoEAGoLI0n4BBgA!()Cnlxgfh85URHOtv z@Wu0$dvEU#rHadlnNlch$eA2tCUO-Gz^?<oek#_t6PwILpqy*8a!tpUf)7$()&kPs zQR`aERYMwqcc;|aFE5En*->$$J+k#Q89P#t3|Dn1HjSlai(#s)YI6plyR>mQw>d=P zJ#K$QLNR9xhhz7{xaK<eJI>*N2R7dMS2`x8UUKK2HowQ)VtaN_TFo`=Q6+i|qe8b! zCGdH{Gfx<<$<>ZtbEj=rpr|2N7gpq&qfI1cnF(XCqtZd;(#srD@mVC^fQ(ee^#z%H z4UK9Ow-9kDqK1|^TQXt^TwRT4nX{$RHr7=<6EYUSRPmNEM(tvx!?gEL&rUZ2YI_c1 z0p~e}LprUq(MNJibe>)4l5ek=P$+%khAj=HGKN2BnPW&4${0{EBjJiQxg^JKknPxk zmK;k?HJaub9m9dzn_y&89|M<M4b$pibn@UxkF!9{T+SVqXDlDNV#AzL$kK#8YnBu= zR7Au)Q4rDaM8|{)1&>@|Y56POFePKbjDTA<OeMU1N6!QQ8J!+KPGlP}BjJ|0La;+^ z)hm5IRxa9{QX%C5DO0{@$}M|7((ue*bH$%wnDF;};BQI!8~&UL4>Vj8lCvV>zvQEb zoHn$);uBAjehf58OG-*gNk~p3VbVeQ;HuG#Qs{n{w%2QT(CL$M__Ugio{W;79Scex zc`ugs6g1Nwl62QB{DXBNviK9PG;6NJ9C9VWPRF!8g2)ZVwn3j@6>gq-#=1FjnlZXQ z;4~cfV91TkN$iL{@y^<_Bkc0l)}EA-=FHf3nzCV!UYdPD+jj{df9}6H4khBOrnG0} za4*n6&7My@5OAYbV!>QOPds9~$H<Y9iZkmH#S)2BTUCrkqi=VlIFO^$0j7kcd=^J8 zrJ^7rW2$JPL2KvY4PA3QkxXSv#0{OI3OiC-a&754HE{08xu#>y6_yR}CHn1z*6L~H z5b1pO7$fkh%-eLzFzY?85*gAuUeU4Byl=(7B%mZztm7XkXo+}5hUFb8H9hYrc;sHr zkN4E73u3AA6Y1xo(Zlu-c^G&U(ZR!sSZ#s!zE<ipyn!nb6n{s;KjTL|2s*yfCecL% zt%%s_dg*wgVJDvSlz@z9cI;VV_=cDdWP~iq$hoCtCVz0wLP4B6Lgp+<$%&NVYdw9j z(>2iuF&g+pV5>zH-0^$v$VG<!q(|%5G(7MZ{Ei79=_$$ho<9+EKH|UOf1_p1j1@QB zGGR4(WDF0Dowl;IHyRFE%dOt+oV6lS50o6|!e}XdAV5OHmRD3nRLn_eS@VuhLR56L zthnQmkSz%rXp0{av0_8RUPMOsj=S+qCNWXu6OxB<%mAW_d`fjl>UliJkHYgVk`ZYd z58gFn9iP3p0yY<Zec1_XF6g6!7fr&K6*z|TuyOl>v%{SH^L)^0&c4Fo&vhc_?fJ+@ z0^agUvona;h_RQe*%@38nJ(y_QWr=eOwdXYQin|3X+vy@$e9rlP_QARCnunwAP2U5 zU@c~|YcN|)J}j^iKwu+IM$D9r_Ze6{(>uvG!jZ|mV?w6)aad*y`Si|78;*{ajzh3? zuf+{IOkD+Csfi3E%_s=DVyQHSNJQsXvaof81mx88{FW;WOXkd(vjU+3O8H-0yk>lq z#~gUE#h@6ls$K)H=eGo!qJ<LPI_F2P8K#k^!$ll@lmO$Ih_^~pyP?Ez%_B95_R*Fc zsL6@=j!IT6=hMEyGG!|KYDUYPnw)@$SESl&_tdiQbzWZ6dE9O1Z_r`t8p;~hGv`OX z=6Ag3iHx52w0uv+hJt_z{{@ySmi){EDGwOYgWmEj4JCIZbj<mhFUq?cP^O7)^*J?r zu1A>DNY_n3Kte_<`<l@~1r-xQGD`iO#51pVEZHz2<eoVT;dXb_eC8W@XKGelk?}-8 zM@~VDWyO++3B97R3>_OXCalTya2SSS!%@}wNU7F8HU^_?e}q6WKI-<;nPbMS?&)Oi zGIsPdD##wybDY7S87({P*|B=uc;b^&56Nxzqy@h$ZMl@!aV%dsb@@6iP(x3_jx~?K z2X2^=%W`CSqF_oy!-gkfwPul){+(a&H<~mxiv4Q^B(Odct0o{MXU{zy4Rd;)ftr+B znyj3U<QTPJwO91rQt^?H1zTb&ERi5iof_ayaM7L|L%;;2LCdjB{p*OOhw5~=COQg- zsP1kQIys~w7_D6z?P=XD9Z?Yz(s8TMB*TP|mT$ObPbXP*An?||P+<9vT!P-3fI_c# z*w#j)D{oBOHkxM{KT5G9Y`Rm&6-wK3o8btf<y1w_oSq3W-%`*rB^E(AVMalPrQ(i8 zGLY9I*!@UM#)NN(cx1{oOV*Tpt6gYMDlH=vo+*+^F=;g~9jyMYvJJhOE|deo?N)m= znr_+h19!B1!*9vx`9e#?BQs|FmTxHeJN`SiR4n<K32WZ)%y;Dc8NcW6`869dZq)EY zOr}Gv=aq&ihLmSL96N!+b~5T^l-|S9YXQ;`GSM~QmKJWV9MKB0mGa0vZ<w<pWk*TQ z@A*#Sn=J`B3$}FBO!$alL(Q*7iHk1o+>$Y+*84To@xdtfI#OEU!6U}eT>n&$Y(}ju zC***rAN@;vk!fm9jy-c>I@cd9Lr#yx%hS4Z53W5*emdUD{8Gx=r497)(Q19OG;E4` ztsm~G`HDB>Or<6Z$a(SzoPrfOuvdFlYt3Nj1sp<(_mYa(+vV|*);We9B>`82O`C`A z001BWNkl<Z5~MzhVlt8VWk`sP)jH?es;!vQu#=)b63pK1To=q0|Kcod4R=%o3I%ix zaG=RwtGcMy_ISWNqLs~%vpdu()(eJ>n($Opq=1y2>c~o8C#Lf$A2kIjJrg2wfs~8@ z2PJzZ1h{<H07wW`omf9o$NMxxA8HT{4okoBbq1?Q;#(uwYN%t}Ni!UWFw-jwvAj{P zTA=KQSkU;KIhAH@0ZR!(o|*A0_Dm>QGG$Nh8`Dx`M7Nk|1UU$0fbSIy*$SjExMyQ3 z3CCMigb^VV^&b-g9toL}lJQ&R?uJ~k<2!z2N<o2<7h=U+5#uvHaidD9kht4O59#j6 zsba5|q^u}ZIX7ek)H1y_M3P_!zI6=gRG+!E!kj|<=(lP=3j$i!82$vqni(Itrl2E~ zYNFA#<8tD1zIc0J;2G7CYGuPjZsTDbVn(YLLrhI5cx~WMnb7O}>_`WADrG)CLn1qJ z>)DeG+td93wm?b0s5z05T;lH?AD%BkN5|`q<GL<5sm(4m(&u>k-q)#PJ>dAY^q$UY z1vc8T;(;yi_=;SuVM|TOHD4%X@G7aixj)prAylgjixD-h7C*1oqP|o0lu)x~!h*G& z88ZP$bIndV(M={8_LST(XGQIA%;9!+f2+VUW2RSJkkCtm=c=1MmKhVj$*r|n3+2_d zo*RwlNB5p~+xj@0Kvp-W-*PM%=RcleX-Ejz`l_YVtrl{PCDS9Kk*7G8^mVC9viI{P zv_ZDscWk|LW4zl#`^iAZa-d4lz4}=ExIkQyhzWa)Qs*kE^g5p9RTv<$5KysXPQg@T zgIKWmYo_#Ghvrm<4KbNoYv?I_vff~~a#KgV*VyQL3`u+mfgoB#HR~k4dwtI*8gBUw z|0@v5AXoFu6@S6^%oH&m(C|G|-f~ONf66lzU-Kg&9Uu9c|4%DEmt|Jb5)zWI=0E4} z_)Me9+qr~l_DnTAsPxGUyh*2fm`L4e#3S01&`>iY!g2%b*zuZ@EjcSf9(lu>M<%Sf zqvZ*J4YzEmNeL*3C`n~E40IO_N3PKfx<7JVSV^x_pP_be?Ys=3GAF&E0RoFX%2hvY zD9sDU9=aSjb4T<Sq$B1<P{1iGr8|-q+p~cs7xuWP)n$K1Z84*%0az;5thi^-8$K{) zrpOZ~S9wLmgR(zeHGvdjR?V$ZoR53%RHC{C9)gbI5o0oHYL-|&>Y_=hNm($FEVE)w z!;BjeED^WNSV<TV3A$+|;<OlEF()UbX2(vqX-dWoUvnjl_n^j#{5l}<jJ3oMx1BvM zrjm}|#ZM+89|tn>b^0t~wPgbg)L@wrkT4-rFw#V(vqI~UEd`-m4@*-2VC#I)wH4U? zSQzgfQ?Z)ZUJ!|4t7m*FUZBl$qA6AA!C6Kb6cc7l2nkqGGVzC2C$v$_l1y94Oi3lR z+Hhyjo9mG_Y(P6*16w*^rdqO98gr?|$`EGYmIPNy2XW%5UXNaP&<+~VJtY%9@W?0b z*eV<-prK*GZ~4Eu;a~C}s41~*3F!HQk~ePX`JPwQe9>;$>V2zed;)ENVNXY?316pq zSx2EyB=sw=*h^cWU`8(8YNl3qqfccH;y`bh@+~!2g!Ft;gccP1OopYVpkO2OB!U*i zvP%xZ@C8E_cjRBQ^$8+Y&Y*ad18h(79C<+*a(sS7=T2I4;-@&@lb8#(Fnbc~HWs~@ z6Q9Sp&I?Y`Gso5wdl5x%E+FcUYtG9Ud^}{QVaGGiY`EfEu2cgI^xlM2oZyHJg#xT0 zB_&XVc&ewnOUPPF<rj|D@DVcw8{T60LQ5bqjQg7|0H`HoM^8!14bQqADjF)L-jxvR z_HleOC$QcK#8_zhQUNn8D_Vsbwu1LKU4Ep?X=oy9<$n#+N*B64{2900h@^I`{M8Zq zNlB;A$PK#_*_dh{|Kk7&uhfo)>=h`4FrU4$aSs-^NJn5G8O2oZ&(O|vinAU}Ozu$@ ztBK0kdwGuAhZ_>n5=;4xEzeAdq~Ao={Fu~(11jB8=-;B?MC0S&d-lW%3kOR<B5T-T zYKq6bcS;i}MWBg=0c$lk?gS6mFy#j#9te2NPrPELh|7?PmP}7Pamxceuw{wmfhBt; z)GYZxt=ON4ANas0Rlw*$--z2+(20C!eB736)?>eHaqf`872N_g6TV={2x-ZrE(qDP zkhEn+z%w&G@R1oUAzzruN%P2@DId9}VnvE!PE5s?8JVtC*Uv<%{;U>X<B46bholjL z^;Bna+Ch%bkp8Fx^aSRYt35kPo;}BZo(>l|0T{fL(J^jD!?WXSFmRhQoc&9DEiO1Y z?8xcKD5<xjVaJ*^D^lJumBGLB^pB8?8@})}uXs?BRVychOSK9W?Bi0%YSubFB0qc& z2`I%Nj;Xk!5{qT{iAqwoSVd7fbmvDxVk)1}kkYXe-qcAE=0rN4pkgcMZ9_!QLKzXa zyi%=c#B8aEJlV|62P+*jr4|c~=39qlRIjaIr&_2}L=KveSxIx<=Twl;u^|OXpi9qF zcGj(oM6<3;^HwVdbbr(<9KvsUHPn$upc^{U(QGHsr=0i=cPdjn8WP0vV;D8$w{+~) z7B_xBXh$`2?^|l?E3zT-$f2bsSW4Banes0p#lMF@txCse@zskp*b~T=+$xP@+=TVa z`AE;68&*8<HJ=H%qG3bF9Zx_<rFp8O(fz<JFs0>=pP5i_CqRA9Ow&N?4~<wWA7_0G zeI@Vujz+ur(Cao2;hilTHR>fZpBPfAg%8C(bd+(pX2+I-Czcp0a%%pb?}(@fDHIYK z)3U;FOQ8I;mV{RQN5nV`b{LEdvGcL-HiHZDHv0nN<T(5yvM&Ni?TdiX)2MEHGL5?w zE@Uo<PRt1)+K!ZXmx6%L(>&~1z4sE$1>N=y1smz|zv7OmpY{xGYJ=q!5B#WHhgNoO zck8yqvegHw&GwY4%7#E|XHVMz0oOcYnQM#K%6*#h2H23Y#gLN|@FW?CGi_fB$n7xR zl~1a#*ohbCwy7=yWQQRj<rS&g<5csbQ0WAb_IMG7LKd~wKVb)fz!>&WYu48L(7#Zb z7`4RFGipMKMjB#r;Sq-X)Jk#}_bQw$*4<X6C+}HJ{EUB4^|~!<HLyl?N8*1y)Fp$~ zu$K^JAknx(GxZzOnkl8GgSA?`MvpI}8qNXGhkUYL9_?I}Tdvt-BraVb$4Ws?C2F%f zj3U|L2APWSauz2?FFi-N^Xw4^9KGTT1tlx?{45A=$`5k4M9e4@$=&L@a!U?(z)ktW zPyB`%2@OB+=lr#{$$?kp4To0_!mRchfOJBpPINbfYJoeiflCSKC`n0xjDSa8d#|(u znk|^ou;Pvx5A1owP%|Op3p+mWnVy!<yw#(~t%O{x)F4B0@T_6p>mIYHZ*UyO$b&3S z<#d<v1c!9q@ktWFd6YP3%L{vf)^Z-$XpRzijo)pa;sVWu;Kx&q-X*xq*cTFve)!X` z)Svw<y)I^l8+B<COYWHJ1GPFF8qGcCT=PWCjFb(}^dxF-jNH31mK_z9WG10*Sg2{0 zYLIb7&0g@NfsGad8*W$$L@<?+$2A*P8D%h`Vod_xziX(K-Zp@&j9RgY9KxS@Be`@U zCD{OL@AO&petJ@CHWljKu_G*y)ncI4uVP=34MN;nfjkE~vb(tpac3e;;A-Dq=bDs; z+U5p2R6B2kKkzv^HMEHzbcddN9eCjl-ks~}j@D<J6@)rytm?T;JMN5~ZlGpV>kTd* z*S1G$nH$BO_w2mN&B|>Ms%@=^X{iO~9CpzImNk(3C)R9oLhF(5p^RK56(KcijMSh@ z9!QD!q^vrp5_QQP&Z+Y&o=EiwiNtm+d57heTLM1vR=H=9I=0O7Z-xxpLztEIq&v4Q zjTEzB{Q$}(N^j_B$(8ulPzWH~Yqq;4Wx*%z`Bon7oE>{2COk6ZRtuLx59&Z%z?Poc zN5e;g8Z?Ab&>Bxe>ePvtGe-P*cX)}#ZhQ+I^}ooef2qZ^X9wUhr^TumHU2Q<#7k#i zs2$DOL3lYTxW9;0FegHru{fqvOR?jLJ#*eLS4hx7&f(@X2@7uciO*zA*@?CnX)5A= z!U`y;X;kIl{Slo!njtBMy=s!!%P<oxa~cX@N5X<VFyUFzkc}FxoxH*=mRuGfC!}_% zu{&0Tz@CtrIbTz=;v-*c1RSb$3-p9DqXVwj?H+ojU#k_)kTqwCMUZnKhhD8_>(elL z8U6<I+YnW5R5v5Lztb8zhf1tZDD|QVM#ehhnbPhn#2!mJNQQbZ#&*-l#v@@}b=+w5 z9_umW9>Y`i`t%x+>S8SE2w&byMQJ7OZskqwMoTS-rD}7ZWU1Mp(eyI*OPWv*QKQ=1 zMQX<&DNC)Nr%+=VvDe-^VkO`}P0xaoj)*-wB361tza`^d*wmPfl56jHq9kM{!r_Vu zTcy9nvaN-bl&a)w9nc4fSLAE3;Hb>$#Y34%H09EzU68o@DQ|#`9R*vix#nkn;1xX; z*WAdR(NfTBe;)G64^awoiMj@bUQet$vG-}eAwx*K8TnD{FPJYlDYeXr)gj`HqIYZ@ zGcQcmjy=AY>ozW&z`yjBxj50YFC?v*3-H0iG2V^FpVnWJ$V#7Z#|;^=cT<jC#fh5J zO5C<U6-qeTEVa~9><ulXn=d(*a-?TeCDCu9{b?u^e8fih(11@EUI8sLi9l*Xb|hM+ z1jIaR>SqK3??nNO_@X^y#}ltf_@w;$gAS`zD`vgTf2e;a8s={>%rx3}iop~^p$sr1 zy76$q4C=H9%brMyAj9LM5b!?mq-6JfYaMEhD$c;~<*d)s7?u?Xdm8#nJ{V=@#y;=~ zL6>To>YOkF4IKu#Na2MpjsCPtwM%G}MjVRjJ5k`Gl>=nRAsZfS&OIF{W^)ipmE^=& zDxtlGk%6msoi}1aN2^b-6N1#CzXA;aI#GZI?r|lZXpP~HDbIT6=X~ZLh<Jlx!6RR* zif?((KQiT-6w96|@A;3yt925dFUWZC5Qxry2E(BOJqc48%#7a#TZMrK(!VbBPHw5? zVL4>oWddY<!ZJ~<=xCA^dlpOx*wIk4CZndLVBww2MpAD)Vt#C@93;)b$w2LL0%LG- z$~XKk8jF|BNbPc&zh^Jt;LTCfdC5|Cna_Mm<2!&3`q4Az5OH)Vrt)ww_Gglp{-`$X zHLkwr0hn<`uEBZW1t5pjL`cGvo|1yS_c>V2!!jyrN?LIzT$47@&bMbrN5~y96{)Oa zg&)X<x~$!e_DIi?fQ<ryTtk*qV_7kiIXv+{e=L7+NlD7y1C<*7z%wy#WdBA|9LLKW zW<3YJ9*T)hS;^_pDUkzFPf}muMVfK7pj2TuXb*+&LZTjLkZRS?MxOQ3j&kf`4-q{q z00MuI4G}npTXV2aHD0{Z`oEhfO*hhx(FGP(a^T<_Oy{Yth3L<ziKu8u^l3EOGh4rI z8&IKv@{rwXAXGxf08?^%ezz4(h$U8ul>ioL;2$f8*^Lw$FV}IccqoZ)rs05<kUC(= zp0#wAb6VvH*E}g9ZNXNv*oKgTw`@q|m)Q${+X!4yl4xvD`cE~X(26clTvkNQHG6HN z-NBtm9$b=<up_1*VNO86iXFGQ#*CWMCq1xgN=h;&8audftY-|FUR_1TjFv(X4}~m| z?qKdTI2bTwt*(qAbGi2twM$wNbD;_hytvsq=Y~D0%?@c4#|micj~8>sLpkrT%|&qi zW!Ql|TSA-@l+WAU{=}kYFSf^aG-<SK`9j5vD<!@TzFpK7hJG>`v();cqo7n!bm?!m zUM3{he&zBQwxWZSZ27=z9@#5U#^__qNU-eLkdbmv!(2R+R=4Jqo*gL(E1&e`NFIiq zl7w3Vc68Kyp{HgmjsIs}aie<k0ETV7^rI0e#0rNH5wIdx{byA-AyIHAG<mNzLQkva zxRbP?SA8>Pa$D7cZXVP5?ck7`X1oTp*C#kEVXS|^1wv>Ia^}vz->qb{hrk}=r-gVY zZ|_ZQ=(g@8kC@VUtdpVC`<v^Kwf^U7eSI@De`vzjcxYEd1>P0iNY~lXQ~21Vfqrw? zSO;p3do^{fOp1e4sL_?3QSnjlP2+P~BEKtNknv1U!=u!96)8|MC!tU+=-jn|I-3br zQ#I>%rma4mUR&_gdv^mJ+=2G{kgy_<P0e|ER`l%1Wy1^f`ZK=p2E&SbrZmhjye1&x z3n2wp<l6M-#FBO;gw*6TbY#>cumB!{YW(mb@V*p#@t>zKyYq00=-3u^+=yO+6qpN( z<4ZKG;DrtA%Pp<BU^%lF;wmpo677hxd|dAKUJYJz&x(wkB~xNOKYH&69Bk0;sd7!i zgM^xU7PJ(q5*j%UoZzCT6OKNhlu4(>@JtUpXh80w#{)8U!kl06$VNNTUKC<S=MAaY za7(IIu2YPqqjrScGH1gM!<?FcXPzWeusWQq7layLOAXarpgEp9wsWs<Y)7$(jbz*I zcFjD%p!GlbumtH;!MT*+AvUI06;n%%c!*#>pb^aIsC1j-)|-?f9f;vb;xdR_BLBz< zv@YtsiLIp*^fOe|)%#g;CNXy8kq$W}sYL0WHuasRdBa3?*hi<DVn&iy_saQ7DP^j2 zgN>Y=LPsb5kuY=&fiO(H4sojkqt|03R^^%M*M*M04XNnEZlJOR9tb6&-?5}7<~4s{ zDXU)0Ka#W6p`J6R#Zu@U>__|3P6xC*0H2shnmwek;Ee^T1|N=JIb)`A&xAq;r@KF< z{J^8&R~a1-Gzv~yP%-76o;{hmmnT9dDhMhmJ+0z4(UFUfP#CTlb422^7g?U>Vxy~( zO?M<WIc|CFS=VeXs%6ZHP0hSuQ9G{hg0pGgxs1k~HP@Vht}dl(jM;rwPp_6W4_Kzm zS@`4Vz^3bU!@5Jt%F;iyJ(Z%TjS%!kEzgknX*{ygP3ge0;=RZ!y#}>`ZkL7`jUu}n zUa<jsa>*}Z(Pk}%MB2HIl!Ah{yryGM3S=zVvLk1~M_RdT6R&aVN6hNhTm2hFWe?&I z{9%-1D0SNp?WgrC5BHAd9@p>Mk4#lltwV3{ey1|?*J?S3sh%ATM?y)G6ThR~3FBb= z?Pdg480aka=n6FcKuJb^>`*hv-cJ}KNz;2W@!u-n?&sP)LrF~To6XE$^R=d$4-y_V zBEofAJQ=mti2zO$I`&NUE(cm=Sk<k=kUmnAy4TO)X1_53D?Mti*vYl+pcZ>cifZP3 zB4y4L%SKR(2Mo7bMrHIs&KtH&eVe^!J&I)Nr9CxjxZNsi%N?CQw@9CKAQR?96K}Vj z-%>H-3$Lh|QIoONqq7rnC#9evrB#n(G!0x)k#kK;&r*~>hbL`3b~N-gX&^b1IcjLl zMLN^@roo&k`9_d}@iJgcOtBvMVlJ*1PA7ln7rb&WM*$fwC+wOt65#n3=aj8H-XNP1 z*v6@~zEE(@Tv=iG0KR~h*0*JrmP&PxGuyjJA!qeTRn~VN2<1#$2`$e&DRshV-`bI} zm1-y=(kx_4PQ;Ty+chDNB*et**ozW#C%;A~E^|sm%`Hn7EHo#)5eK9tVviM1E+9|} z+;}?|jO5|QE8lw^5Cau1^e&ch9h}MOVC7cEZ%l`Pa&%M}&(0V=iQp(p#d;~lU^(l( zQ9Z<{J`B7_*{ZR#N55y)ei^Nn4t}FRH`g!}4>cPy5|4EB)Z&t))I?nCF*T>7){0@+ zVrQd6D#tRRW=o`_(vmWhx49z4U{&oE#LU?1@E^L0PSH!fm^Xn>BX%O`gpdhqYGM)s zdR}vn1tx@)%p`-(NaXL1Nm)~Jg`uG0_k2g88rAJX7wjk%l(poU9jQ`U4uMXC(8?Xo z4LfeM1#d_5efQ^5g;ov04Q?4{gc);<cBzo6ni&;)dQ#S!s&=fo<=6b9)^2+;ChU~u z*82xypjr3%u*&>`dG(mr8C={u3|}D{YxOyi8e1PobNN5!1^LUR%(PRE@~JV+p4E}| zMATw0e8=NhaXd}Tc;|26A>|vM2zaGKWK4vyo;`;s$$_8f%_#`z*!b!XrQaHl_l_k3 z-3t*v=*$+ZNY#89O$P_Q#EO)Hl!%nQu<M=Ik_|`!H+Njp(vlJral;!j_QcGTxsme+ zB68hK4o#Y>z8u3JLU}k2;Vt81BT#*N5Vf|xrKmhjt{1cbza@|TG^UZzbXYh<Y9g)I zUL87$Mi+SKt#94YV>1}7rJ~W%#ppYu@%OQ0s`H|jUg>!4NcwcB&Fx5Dl2Ne}S+Mt1 zkYNy#D=t3rsDoG)6ckEM={yuG*XwIY<wghun@{vOHGW87wWHs9NZG{md{Q0NJ8qb> zXU3XqYWa<ybX0cgFIL)k*3?AQa@M%VbRi6XC~MklO-p+}1PKHdbD`bO+_E20u@5!6 zVaD3ul`%0}EiX!-BI64+B?~c%dZx7Oh{@Se(U7ty<BC6F#hzRv5~qWR#p@}Luvoo^ zNsPPFQy#ZH<Hnm4T*@h{GwM%D$WxHOc;Ctyp6Zv*U3-Dta;o_o&*cuTx=Y->OA$mv zF>FS%l`+_=Q-jpevE?&6-fNo_97U4%$`WXOix>LiCsqr#0j9F4)gw}H?>(k_3h`&H zyqK-zsI{oeiN?<vJK?0<bm&PRaKw_BoQR$+Et%xXX!P5$<!k127`D{1uz!zXFFNl+ zp*{nJ4S(4f@4js%Iv!?o1CoANcLd(HG^orNso%$l*#XMX>G(%u@KC=yh)jZ$fOj_- zSLln6F&qxO&<m)oUp)<?7VE_h1AH~|LhY6zCjIch?j^xCo`yV37+WE=diI1Gd|0_E zI^QkydOt&r1X_7md%d!W=*p>I8nmh_jg}Eky;zWI8ro<{V!WT%lCmQpWJbX|0-muf zXq1ub>d^N@Y`CVNXNP5?Xz<*FSDiU9=Oa7+-tD+%rFVCy;L|-B5i9?&3$!f`^eD3I zn6TIWx>1-@B*$(j`Awthy1?+jj2SU0xn$vU8aCwQ>@nW!($lacV9Qs;Y*_J{FD!ix z8fY+Jb*)A|HN>1@P0R)Q(-=#_g<bD>85{K{%Z%~f)|_zp#>A`3@PJFKJo8VGT3)uD zos(S5*`D`+t?!Ta$=1)60(uHo+>@v|j7Rf>PVL9Rh96?sQm~cn$u%CXXb*HLT3;QZ zR;*<&1ie+DjjMi27NqQ@Z6BPjJLPy3`ZG?LVku>#-(nQI7?F~(rYB%S&MlcgU}91} zij4fk3@emoAk0<b-@neU4GuF;t2N4S5CkH!bTYm5(#_YN-fS;UaSv#KG0|sOqJ%m$ zk8AnEa>$JEP1X|~Lw{sjf8iUi;6Dz;id0ud{uKmX#$qKa>5pI-A>*jZnGQ#1IE<u$ z?0p3}eBW3G`9W|IYpvBv4s2y|YZdB=NHy+H4N%X>!`=_jDyy89oPb2~ph(=6DK!-h zAC)Rsu_xw6#>c%<Fd8a0Bz)mJHXb(?N?T~PR_TPuY80x~YyKFhQk(#(9Jx~}5)xW% zl^qac=3!ATFT+hd*9xnzc+XmM><MeN+#!}dGlh4?Ow@5X*lx;-8|4dDq}oEax*|DK za~MDOJD1j3_=Vo|SoINg7gh;&)ZW^&qO$k3ggKe)nHQE2!G-<srAWBTFMYgdxs=Ci zFPQGe)t{wd&ohSCiinAQ!8`~xj7Gd}A6l?s?Vs*=>^j2ByEvVgS}d0k%a$z_wJ!4s zC5EXQ#Z2?DmX3vjiJ&8+lKE+(0dA`GifbvSqN6;s=N0z~|8_Xcj*bO2JxjK<%z%np zuWLU}7!9P-%VhQ+-Z__AYNX;1D3q4fc~ceA{dc3f><~{G>0ud+YQpYJa^Cy>dZ$qH z0{|mBQec`Rs-hV&L=Fd9=tT{oD)&%z?3m;cNwsG@a0_4mg=*c0ub!%Mbvy1>Kzl24 zixqqz_P&xKerGVvyZx_ALUEN-s{L_Gp~+{YMa@BWGu~@Q6snqC0-1wXmAqv~&m#>_ z#4M>PNx34|bS<Ny&~A964R`CMS-n<qp`zyl9~!<RQ20<vJgRY#xO_*>lpV4DRHVbN zWr<-&M9GYrT$t8c@sNS8X$OAYdE7%y%(b)xiCokXbA8oPi>R7h51MxLSr5!ctE$@` z4b;rZ`DHJ3p!S3^WnIO0IZvY>J>?IF*Xb7JILxgd-5AHHt4mBjdtrM#_S%|@q-A>% zTz{xk?daAS_!uQSR-`NyR1*8~Y$fW*FcEaf>%yz&98BKjNaSiaYaJB5grH8Ku%;B9 z*=gA)qy(CM1PTk^k+746-DpT05^*OmWG4zqrCire`5YCYtVy>lNl7SqBIIXQ+^~^T zcO%JgB<PUwpW)!?&4evAYQj40IU50iIz7RMpc?CGc}S)`@NzpZHMOH#xcB<7&ObcO z3IBGGP7DtkCr&<OY2X{<gO==Y?X)7X^g6l%Nu7JYs2a}*6D?3e$ycmyq0S$-fXsK1 z*ZKt>5Nfpe$~+<-ZID}u!+ScW#4>LoE!|M*^%A+H;>`5^(a`y;xYL8KBd4Zg&YFhI z$A2%eGOk4=?8uprDC+|%_N3&bYzQbw*y$5)^`NbZ$w{c#Yb4Ms55rBSQ);nSjHt{p z4LvK~FlVn%d64!%BCJM6K&seP2cMd$MqTMA&GlVeQ(SOG#X=z4R*t4&{1V8+yU_7z z{ro*5)Wd|4&Cz%*r#&-GjDO#}KytDKy*8P>|F88R&~EfQ{SiKBEGe-k8EPESunv=* ze#Ac>EAhs)Uw`y(PrbqB7bbr8XkjvlV|!XE3Z7|1OLn#*E0xkQ5{CbsC;$K;07*na zRH>qtq{aQ$wSSS8Bt!7CB~f}qtNCCn{G?N&rAmEhY1mO{wLfD|PfWy)wLWyX(d;K- zf@Mo7nr|e;drhZ{%n2x5@;vk`NoaXX#F8mXEENk<0#bd2<M*laOO4RS1BFul^^!H9 zh0Xw29U>l$Uw{n%HHzIFn!p2^qLm2v*am1vHZ!z2KRgB_Rc(PEPK=Mga1=UdM^LI` zpHbj5Jgg4aKoiPC8x(tOqV@QKVY=FQB8W2|9`c3<-*2P=ymRYhk_{(pwb+QoH|%9^ zjJ+qi6^(Ha<b<kG3mq%Y*m$LfP(;o`HBLn%x$uN<G=xZmj!&rBv1bYbUUh^#^O_8p zVs+hwgyckieHSUZsv%}VG<whlT1`2}@*_2Kc{I9F%v7O8higYm8Y-UIDtJC4P+rXk zpEF{pnCQ`&k?9?<f;Xh(Lg-scdP)U9b)uRF{&Np}l8xas4cvGauQ^HGwnrVr@H<g= z0uW^+4IF1*1Q+cf_M&IjUW5XkW1#In;S7E$A^rUO9UQsFCxxNyT0_a2oVlpTfxj7> zQ6^X5kx?ZD1v^?265Sq=u$YOI{4P9sV2G6LDJcX4H42l7X}Q*_0k*2cHliyJ=+li< zXEm`<l`Rw5>@)U4c24No(2}y@duFn*<vJ89QQmI#UJr*(a5R~-es<RC|E>1{7+=+g zBiVpCBb^Piyhww9!yeuG!z%Euk3hGPIe7vf&$p~MAu(d_gh%N1LnuM7!?srgYLvP$ z?gfVMX;hbYdab=*r!>As9Tq_2>hjR)x}n4HkaKgqsaoh(G{gaiZzUYDiVzRwT}g!> zXuOLi@JSJ9&KlBDkg4gMO5#)kTV_;bEZLC|5mQL}81N+7Gs@9u=~&Y-r_w=~>0NKU z6E+$tG_95znI@c(td*hS-M7?S5%Azg0md(OT>W{-Z`rcZGGwB-@|IM^Lnqf%f?*<* zM=X_UL(CM*o`!|4%ucJC-sAj-CEDpem$Pl|LArG;V(CxZ9f9@LUMEUe=Tj8=5rp6n z(lbu!IIsNrBW>NO%-9}Pln2E8xbo^pTUuU#;@i<~jiWg7@oNZ_`SqEexu0{O3$pd` z#CThiGfS5gG{Q4hy0HVH&@(2&zBj5W8X77}N_r-eu1rZ(uQ>tkj*V<m1+}220VNw9 zYq7kthLk0*rJZ-%#JwC5kx=*x8b0zxWZjg4if5iAN0|BWqQf3`7(|5Hg{IyWndqt9 z_@&6iqYR7}b(m45XFU}1co#oT#xTOyqgFnYb2spHd#GJ_q=_pdiL+6~8+ws-=qEe_ zFskzieCsjk_|K{L%MsUZW>PB-Tg%p0hlhWL-Uk}!aB!N&$Tz5=5dPK!AS{sv9j=0( z32zYk?QKXZw@XciB_buDQO)PP*)34AC!i(=5*9Rse5T=9hx6R;x}Vso_TNylV@^Y& zg@(%mF0^4CSl2nR7q}P&I|UM~5BLcyMQ7vhu|(k1XKwjGL?dKI=y&3-QbRtanw9d> zuEl^sIJ1_PkczE8X9}%~W(35PDmi)`!3mL|XTfN)*Lufyc(&U;KPBu%(v%rlR0BIY z&Q2?+ew3fl`y;UT%KhFSk=>|OKD5inE{o%Xu0J|L@xOmi6xyRh&T<@!)Q=kO)58|; zQK+fd@T_#vc+>z6bh)AStMnvjG$s#xP-LgNFjXc-uFc*7d(`THjQtaK%T@-s$hT}I zo%B-?2`yU+N+!(Mu%V!4Psf~;9Tfpn4|7j==H811wgSvUNlqmp$(9KVmO9FMwT_)n zeCzb|>^!F~)y@t->K`<C$Fq@x3?%e|aC_p&HogTlLbBqsG4R!3=+`3$iD^4pZkVH% z^&m$X<jsfV6gP#nQcVuLmvEF-i&!V29_W?NjT_$t!N>;H`@`4x@7}5Y!5_+<oFT(z zeMY7TC#yaERLahSS9+LfMikUKguBs3+Xcv%WTG`2U@w1o%8n^hN^0%d=ei=d@{Ls7 z`p0w3oQ4Gf88I<4avfO1-;Vq{5HT6y)B>$-LcN~QyOr+=xnm-wK&E$~*96Z2GhBoM zT$7OUnjTA`iDBnqWLCt)0XFOAl|7Ym1UH)K4c_eJXjEf=q2%I)!_OYs9PFt3`Gw34 zbCO?VM_XPqLQ)z3%s-5w`=cVXKQgSHCgC234h1K>8GAvK%?qcyIhqE!)kMLLkg2B@ zhocB>>&30DZmNco0+cz>=<o=Lh{)LseO`F>#;_~g(-1QwV#k(Fl88tcrOLNDE=6ie z#g>`{J)fBoYpoD5Bch?<N;>*jupgs<%Y=<s!&9F46<0h`($cW!J=ZL}!N`m_W&?iI z>axpZpt7>y4Q95`^Y+I72}h5ha3mi-Rdt#Z^<{9h$ii2HLlk=8)5y$dIdyR49)c!~ z-`x%?jogPx20Hwr5h&sCm0~SVLLWmus5CooH5`vwg3&AKJ^s>qjbHD7D%5cABsm`5 z=ZcbmnpC^Vo(T;T3MIAm66g+rjU@#=Yas**EK?F1zNaK12bQ$#spzRa6S5In|E=n; zy{wAP4gPyxDT6K0C%uyeE%!d%0gx5=4R!AGS6Z#|dI2`Js&z9u3^R6G#cY{tE$7q$ z52Pg2yy2sQNWU-@o&2CIXhxE=Ar`cw(X=q3(W=FINzmXg4n4V%Gt7y(7-@T|3pqTp zgZ@G*d0hMb|BSs^uO-=eruVFv_jINls<MitM6uOwvh9Y^hGF>DH@-LE|I<IsXuuck zi+0<vB}$Yy6l=)J%HixW=2*U1Z+sDZNLmskf_XB}*=O&F72oi_&pTW+xm?t5^IDCo ze|1?so=1Jp8(+>++Wgr{;~Hw<jHYobVB8C)ePmD1h6f`kdo3Y4t2=v3jqj-_X)Nh; zRCk3BJ;iBA-0A?0wZuPBk#I-Kj-6Z=F-wioM;~x3H|V3_PD>mIJ&kKo-7;B`zL4Xf z)a)k0Q;_fz6%8e8JTK|^hL!5aLJ-q1;c^y#GkG8ywRD+|ia|mZOeE?pynB9l_^a@# z$v`%#DaX&-&4ek5%><*fM`ifRcnnWa$zfI_4z?MdWUXGGT5A=*(==^d*i1)fNW=(^ ziiH}eSOD{2uPZbQ^|tqRGf7Qy#_pcH8i_u6M8SSw3>S^hJV_&B49(U?&P=2!Gxk2F zG~Dx`@qT!Ce&8(`1-VT>BYV6IGSxZmDCl^i5*<1bQomK;RIP*JpwNR{;P^qi>Cul8 zwzZkPr`{9`;q%E!Y1q<{u;7=xVns$T<iS9sFr}Kkd_K8d^9M>=GvNhKQH;}rA<PI* zS_wux(^E(=Hc9PsqhwwlaOVNy=cm9exbk$WB-{iFOu&M3Q^=)0hR;@l-fF$;AR>Pu zOP<#W!)G&n&ksE=u=m56z<LmLbjy*HB@ZGfpDV&z0f8dvy?T!9DT!#Pm22w^)l_R> z-CETU93MgIyv9-K`3i*^WhXfLD6S22lp0UYhM(}fVxXeuB`Y0Yy@I%3DkH6+29`oK zMx1!hK*0+-R=nVumUq15kvm(+jD(Y3M@*#4ts$Z#w^l8hEwUz%)x=ntfMflPsd9Ni zxfvE=PG}q(mN2-VF4Wp4ex1SYouNma;pG!GIk8oJkyW5h$tPp#a3kqWI}^^JW5(Ii zX1-WC$7;l9w~yDAF$&tp(CWl$zEKcflgq7<QYzhTYNK08avS-FVrzVzAXL#TV6!$? zM@5Mvr{kU<sVrx<Q}9woPr+*%7ShoTG+K%TbKI}_ozNFvwB$$&g+{1XC(LYY0C}t% zis;CsRt<vum;(ov)a0yqB&E_cH3Vdxc#_vCre@6!&r2%4S8<YyKnR|NV6c^lIx9^F zLw9ti$zfvtjY!m@&*kP`_Ji(5A$JKgh}>ry7j8C_a2Y`E&lEDlMJ4EOPW$}TN@PB( zWA;7$XHizy0-EWk_Oe@-RK#T58*6#IiNl(pl_e)5rIjlFq{(0j3hv3t8BCzuN!Sp? ztEHNVOxxgAh=4?Mp9LL_i8vc2Lj=5)m(VpLlc%wuU`HqV@h1inmYVVfI`Jc(9i>p; z&ph&;J31O3#8Aefc${(goVE0UgVGrpWtdsV=7(5k+t~4em7KXN{6(xNXL8($P8<Uz z0~@RQgH^rJY;PKeL^j!*4&Z*q>znL_gUzT~ORw#%nayXjSHyd(wXvKpK(K~&lyMC9 zO3){a+@kR;Jak;L#OBv%RRiu=QnQ+UZ<J~q={Oqn_()WXr^J-{jz_jUlkmtrjsq(; z#N<@G;R^~ua}z=K9gZ*A;mL^ElS$`SipRI*JH8|%V$XX@oC4?rT{-5SS2pzvEwZ2S zbs`adyk!gaq+;j=C-4V8QLrXqC1-I%%Zfdom&9ziqoU+N0FV+##zGG1Sku3tkyuH2 z5n#8K@(BCckfD>TJkp;J7c;)w#3ruBFq7y75-y&8{=yP;O<M6+N@;giak*LTbxm>c zpN)XyGg4}Q6;|ZWXdiPSw4W{9D@r^uD^{~xr?rZ25HcylQK<rKB_gh5N{jT=Z)r4= zPGrCyskN03n{f;u;V4Kg5;<VOM=|SBiL(tkTbpt%IFj*-jx7snHgr7mBp0gJHa6^u zL!#R&T54VkOJCBmU@c#7KwIV()Y;2xna=95L=ES(fEX?iuX8*7xj)CBN!+LD*r4CP zS8EQ56j>D~8|Zs7O?t-I&Pk``3`fx09y+p0Es}Vn*A3cR{F67$9>%8`RPEF_IuZBW z%y&Ni-l-Y-quFmg^(vzbsMCZqRUANw(H>O!wWQ><YS<dH)h}ttXhbgwL7|oW(OA+& zXq7!H9;L4ADOlmzl5^r9Db*m`S&rkLMnUo=HLvJN`9v&2UcrGk5~Or`3@%u5BxWro zXgMne5{nxeu?3MD;;;sD@(CSz)KRt7Z*#|qnit~7G*sMk&z=Wrc0ACL>2Z<TBPtfz z&<nxVD$Km0BB2pyypwjeGc>X@xW?@;Nq?dG<qA+Romt6cmFLd3c78Tn_`Lhp{{^7@ zWmu&@yUcD8wT3gIg1bp?b~pDr%x=ZbqQ48_fksJo*j~1(&PLH{SUps<x(qW_NMrT0 zP*!XrROPq`X|zsg1sD%v)3CDlQrRVfJB2yej)+_l+|ZG7VnNPFRVxE2TO0!!2O{oC z=qw1Pp<>0FLaFm55qoknGNZ19B-z*$jbr3^&=w{-($U4}Em(8fi2Jjx7*~K-Y{JS+ zRNC2Ix7T;bRS$)Y=V*FsDC9#<&c0)0S4M2GDtA^%I=$M?UR6b+8@M;<YCjvE$Lw(& zY_A;YkR0VJ-Ap;y&t^pKEQ>jiP>^CUosEKm32#MZS4(WwXOO1%l7V!39|E6eR3#1~ zG}pw$<Uq}P7I-#V&b8cgqGhk;SO(k^@yv!h7CdvZsP+|+enP^Z`3?U?(&2EOKXW7^ zp_GUuBcq^|iq-4t>Fqq|^>}G1fRqP*pp&1tk}o8zGa_jK7wmY+6DKlOY=H+FdU9$> zp)+b)HuNOatYmEStQf@atF(MeX1t|NN{U?FoVyu;dpl~(odKzg!0gUK*Dej{?k_lK z-4(NC9xLh2)OFV#x4)U3!A-oWKclalqpJJ`$`l*r*R&Me$#Czb1sdytF^X_(enWfP z;;89JDag3jXt-q|c+ME962v5xGSR#-&BnzFQYyhh0)iziA2|_IlCZRRLNC$tJ094P z@P-g;7-%jFQXUxio<_A}MaP1O*PJ9``GzBFiHeufmANyiNic-RHX@F!GU^Q9G?-A> zSrdF2$?j%GuWr`Rj)z8K6t$Lwnnv&R+}-Wv{7gk?nO{lMg*u8?##TMGJ@BMW47F`y z|2Ee+Jej=%L6-ZYCUzmNGBKKRB&qWlU3hM%bS7r@XMp{2rW{-5SFk0H`Ej)fiO#HY zgB%J+qu51id&A=7K(1YWB+B*IG#prx@{&*NiTFvzuq7E086WXH@`@83?@5W-i|v@v zQjwGJU-&sSh0>bCOf7UUYrfOWG*dRq_$dzBx?-<1rU3bL794q^#k1i=C2C=$72t_G z22L!vqaz__%OgLSLwWRtoVcHj#9~%7bb3Ve>{)0c+>x5LuoVI=w)gFG6(47t`s;kg z@b{BB2ie^~O}VSQB5u@U6J+Xkmc}h|-khX*>-(P{$=7Itc?&jQ#th<Qw7`m`MH!9) zjz|#ENuJQs(I|K??B`MvmMrLarX{0LOv?zy?zFWV%)B!wqA1vyyv^2jcw(vaKGE}J zV$p?$+Yu}NM8t`be+_H_#|vT{H4&coloV9<M1I4ARQ&Hb;5qRng(8VEH4>3I8@z7J z$lBA+ybyD72Nv|xUtlV{v8v}Dj?NNK$AqpiKRCd<dhzN8jR*!39#!omqHlPK86t8^ zT#HPA9h*=Jld}XqPN4mzCbhYYGwl?8xnN+;z<WNZUXC@>bz~HpzP0v~=2}5#4}r`g zj>eRcbe75ysls(~sw8Hw!o0_me<aq5V<4rWleehTG4AO2NKee)u%{-Y<O}|h@5pdU zb1Qktdtk{+a(>j~@J>MpmBhu4iX9*MANe~vRz#d|sxFs2%l3w5jIkkKD-)kNGA*bQ znamsoA9+Pb%?ptLmx?cnsVFG9=M!sk9BT&I+d@LeVA$Q7S_#8pOcb{7W8&e76D!RI z6XDXlp-B@4Sp=`C@Z1a{^EY}`9elUGz}Y<Hyc)ZvvRvY#+y$A13j*I?!pFEXs@~<S zX*xa!+apf0=gz(wLY~(;1jf{WNNsLs-yzm?uV=4-zf`=G5u{VFR09#vJgpsjtuFI~ z6|f^Arcx^${Fx0k_rx6N+3V6z$ykBraiKbDSW&TN&57^GcuU5YI5w>KAUpb)nbmV* z%e_c|JB}o5$Y@z>`c>#+b~7Nslw=l}hHbbo9*(r(iGACdmTDC4`WZ^!Ns=0wHY~6< zj!Kef?f3}K>q4gUQT{#1(C4&bhztxc=x2`Rw8ZGq<Lm9U!A<PA9IOF%YSA)|dcQXm z>?kFZ=oRzR;3#nBb9J^mjLzEoDfY-2=P#y}kK0Q$GFZbs;T&RnjZT3E<8?SUta(Zn zz*Y~^FQ_P}c_E_X3dbEO9Y<<j;%P|ul9%i`l2cLfOP-b25ww7giio#7@k{<h%GMrG z2?Iw`!PP38u0~qeOvb>>BwuS9E#mgds2f<SW2lMAl(`qmf*l)HRHUSQVof8TPS^k+ zNjUN(Wn!rRGfwPSXgVF)q&6J8wb5fjRBETWWptqkxjyJVk4;XdNya(Plr!y+znB8v z4)EXxfBBZ-j0<s#n>}C8zrX*BY8-b{h7(!6w`MVeU=F7J>&zxURtz;;4)oNT)D%3h zpk;?+O{R55qy{>G=Sv&~ONH+pIkF;_U(;z1xM4%jmO<5<qh~|LiiU_vU~$jSD2aH% zkysY1fFob=mW-5pYRiS)lW@mRcq+E6_$^Ol+;b2$rB!7TSpZLWCP|w69;cf*v7vr< z)&cbPNer{0x6^w#9W`TIP+~vpX#R<g6>A&^ZSrE9ZjGDM#46Bu=Id}4h7-wuA&8_Z zt1*KP(;)VGy@Rarsj}66<ONHf*>bRS*inX3lT&LQHBn-Evr$bC@X4`?`G6zY|Dsvs z;gl~D2}LzNWXGU`*d$k8?vO!#Vn@N6??_m%XW+y?k@FQbKZ@D-#NV?d<Bc}_YX&N< zS;DK#M0A0icdYpp|Dce|mF3rQhMaZg+MYh)UIx5~hy|XGC5{aTHpDb6dC&uH55STS zyup*OXW)sP6wiv9rIJk&GNFAB^dvL_Q3YUHsR#Q&tGg(YAZnWThRCcC*2TYo0m?<O zH2ux^fB6q?M&EEYTbZJK<1_bWbVWnB#ZNiUF8>^QfUDKT`DBV4hp{(b$hG8Vw4e{v zlqB3!=ti3F1v|+kdSidoJoAy3o|c4^1sgWR?AWnnBgt$;&xw=JR7c^wI}YsCMxgaZ zZ?WSoB_$1sq{u5#JRCKZj7KlnlCq%#4&1Zjfjt>dEIG30C6&-eg%$-RUsB=tf|QT^ z$j_7mk!hZ^nkCLFVc=qpht03X8Cz?c)ZvV1I0=VS6AJY@7M#TZMN+9nbZpsa&WEas zL`0F7x<Lo4FUI3!<mydkwJ}W5t3{gs`8n1swq=H!@ty~27Ie1R54Ue6(w!HVuoHw9 z(S9SVX`OP^hO<rgprD;Z4+v&PPB(KcP1kIsV>mLAe?M!b4;tqmfkNV^B?+E;dNSTp zlG5-k@1=748E=6VPrT;94^$+4WTS=AftZSf69?7U0}W5)>~X{_C`kFBSDD)@P1Ly* zltNh{QBNf5OhnC+5;)={n9f9xe9MXl?u3w!jPyMx?X5^y+Sc2#q7dYEgx@;dmpcZQ zI><Y9WS!Pg<BYhwq3^vExValRli6}LHa6S@1)3j!^FO<#^m8|AwM!tut^MmQ=bgJC zFEI0oT<Qt0^@mfN){3W+T}Z2^XyWna4Rf?DdmVc*o{SCmq;%{_SZS{pn&(>k)tZQf ztUz8{-4WIlZ0$K62MLBFh4ZEKoYXXg8CcB|9UUz@9!(UIkh4}y;u9SMJGnwjN>ZNr zo>vMs{J@5UlB4*+VZJk%XLX_yM>EX)7$}gaiR@=0q2AP(oeqgX>mslBVlXs8r#V$k zOT{zK9O!uz`L8qBYtC!Ew~=BJ2b(1(b~#6O&<^&gr>x0^15M093Kbo-&`4fRoH5lb zRufu^w|G$F7O^7bNXdy#JNy7;FFEK>Dk0K)OBRXjfe;R^LguSPZf{Ji<n6wh9?_lT zO(QhH8>|W~62g_c5V*?I(()uhZwQC{2(0L7xMNMvhS%It^30xAd+ka)Wk<(RkDi6z zvENbfGYM-tKF~-*7%IiV>h4&=>_OMVpm9p6P=Z1}p@29$8DXX8CmIG`>5&p?n6M(I zl<3DR;bh$Gmz28t26O!<dW4SRmqZJ|!6v;ywmv>QZ!3*2MQ^v50G`O1O)1hvGpDXy z!X(dI+&Q|6D>eKiIqhfQfqCNVEt}k>clR7}fIVCN1<}vjlql?O7c8xz@UzL!Fgq@T zmtn^Tj`~*#3yDP#Z6(vi-KxzUB=yW`m0CZt?_zR`75BCo4Ni|**&huVh4~;_3N|!~ zr3p8FO(D-nt{G5=<4&n<2TE!@zvCM=bW}Xk;@E4pR07MHRAP=|_nNtldb&WG8wJHd zBh~5%3OwjuhsiW77*m4-CbQMSSW~NlO_U*aAT?+6ib9C{y){;YuC>9?RBmQ}903rX zo|;Z!pQ(kFhnqSQ611_`>TL~@ij1A>@1f0R&WbNYqdQSjabQcumXb#8VM|S{G`3oT z5@wlmgT6X<BnNR$WKwVp6yddrpD?YB&HQ3jWW!A46>BC~fexH76F!l#B&A}*8)9D5 zvei$H#p5nD2Rza!nko_?{-0U#Yff5bwDO-LY1dMMk%8LY!;y#=sg6(O$cVTXD|wJ0 ze?iPqrpv){<7#ppuPX`)Q7}C@5sr+8j}*2p>~;Uli<7}ro>R<xZx++&Y8~q@&ZTpJ z$$7Cg5S`O7qO*E0x-jx^QTts3Nc~LNHC$|C{RLEti}rRNHFV8exYX-$CV22OO4}fD zLp)RST>6I52DW9#PVteU&0TZPnuwN)Ob6K@=U}HW<&(zNBe%BK^D-k*71&6`;WfVR zwR?jVYn!RI(tt%Qc;G!Bl~fzCmchIuV@1K1Jq<@G4kHfyk$0MjrR3bR(|1~%Wep2( zai)KFvjcgcmb7}@mDXAi)vEO(=@45J2n||KOqq58U7F|}4;++_v>bpOPfRPnN~(r< zt`mr6c^q>P+E|khMwN~=VXgIG>2>@};p3==kEMvqRre+=6<iaJYR3}=4ULS6;V7%w zQ*)wHa@k0aDYe8J8APb5pXQ*w>XS}Q+XOpnW*S2bqdo|Y<#n)lY1YE)iO47@^d3Kn zmb_=dik2^c7c_jqN{^nN12F{)T`4CH5<5qFcXI~T{E0h$L8BGN1o+I2S(GXwGt4V< z5{+y^(EUKho|J?&nFKgBOEOkG=t0o(!j@+ZJ6d`+^sHGcKGlmF<muT`vy|j%a__{( zNb1fer)~yC8>KCmIMUDIOZ)}s*w39CekK6+SF#p&(*?(8ceMUuFU!p(hr0nF^;h8_ z=eD?8+nVzzoWvTak%c>)?Uj4eiFKUVQK=mb7@wD{NGRD;O1glUBn>5{?B3q&Qlma_ z!OCO{y?*wnUkc4#(Bl_&3ntu=k?;d=80e|lGw_-fsrJJsAY~_sXRZ8E$BBYZ{J=e1 zI{uaeEB3mnmlhA{&P-Yz1BH&6*ly7|_1cLG92Ny5Ry=qW8Z)`gA7j1dGJz9}gt}e{ z6phv_v8o)@Y)>)Z@$6C5Sdh>Z@q^imBITS+Aj)38>0)N9kIyV{xsCWE2`&;vNgh~{ za#HG;$LXL6>wsJ|5YHHw?yVgkea2|V-7Du}&|=A{c1eVB?`8l5Z-SOsu>9V<#lb_8 zaALuVgn~Q1=ZWvAsc3nmqvD<oJu4bK2i|c~fsv{MN##^Yta^@F2zL?jTYklftv>fw z<AY3XX)kyApp=!!6ca(rvf~9?N|y9I@&gsHQ#8_sBRgIRlC=@#FC*oFmMyv58i6i3 zSX2JY8;;hK^<*=MRc|3#gBDzg&VXK(YIh@Jcbd;~;rRC#M`03KaqG{#nC>%JsPj<Y zOIs5+%_RP2+L1ep5Q=VS0NvHg{mU-m!EllB8Afxk8)=aCJqSb^wL61$M+Fb8iRo=h z((3JRX{c<Yi|jP*Z2Az|u9KPDJH4Jx&#ac7RA!=CivR#107*naRQ8>Yggc>yPCSYh zm++OQCmA_g933eKK^;fYM5Wr&<N1LX{4IaYN49(gilXjkRqe=B9wDBC@%j9$W*&FF z{cM{*d376$9Kw=yM{QN4Cx6UBBkE4-uhvq<mNSd~pmrvjfkG$w1lA5lvplwi$!G>s zDE8tUhg)@;{Eg{mPHMnfu9s087{CD=+2bowi!(Y}3Ugtj*<8etOxxku=CqA!_;@C5 zE-Cc>Pw|$mEOyR#J%cINFw+`J^6hjqWLmf#fde%s5?*kkl6Ut`fj>E0Dwc{0Y8g1` zH&66Q24Pcf&Gnie_yym{#nP!xJ_+5G>k(d^r69Maz-$$Nd8fuS)UHRC9Dyaz><#AN z6c!G5l0*$`$aGaUVrF~x{I&2BBdI1fviD$hV340Jbj|d-pOdR1>^u+rc4*n;PD<QG zI-0)-`f+Dxr@tXJ`Ir9BwY$Py1c3}!5b0}H;O+H>KZB_zvq__$MT~nHjuIVZLAczg zK1#V~#XwKPUfF29<voo}R;TZh$=`?)xH1*!t;IUVaxY{sON8xhYF3c)iAUBPSg>Qu zng>!gIQC>jY;`#da+D-e)`zf{5LN!StY~R?f#X^A?P$+)V*0w-F%#=h8y^*^aZDx= zNh{P+qH%cGB%@2$39r@2DpfN`++FMc@6>VzBtoecucw#N(4FDIC*=T*Kytt5IF9Ib zKt&383Z&@Xpkm_;tyMkXwDlX#u8@SDLO-DsTV=%$ikON?SW#JZ5KG0`>3Ya@4TYHR zhJk`c>A8c|s3Y37k;T8#!S0Ot-09YxmT8kC!fCP`9;6F;R&x3{wtV6v150_rU$W<s zgfI9bZ`kuaEjcS{1zKf#G!NzwPvzJ-36PNR9nbuR@61%_aGba!VnHLTW~e}OB9@di z)I3O|P_br<W5G$Tn+1DLJmFbsJh0^@gY1)$LJ=|+RHO=Z3hzyU=a~aPk+Y-FLn^k) z-Rmy%7D?r_7>Z>wh$PTC*Y}>sWBAVypfp^VjNX%aY7PLHV@3UqQ}tGCq`RT)yF8r- zqKl);T}cq5vl-td9P84-8yQJtp3^qzr6!wD5H*A;SFj!}*|5~Hag-=?Vz-6^vejTe zkvn4gy$cFzN_}vVJQ+ESsvj>Ibyz4=MBEee%opTDWNcXRB`J5Zx(B%E105%;jxsZn zLn%w!iC^)+6aU0l{2d<@INhqU%1nnhHm2z4%(Qv)HV5o<!Hv<0gJ`&|4&OotcCTuu z(jYrbMnh%<62*jY?1~eGkeod=Da%>fZFH9CGWX?nb`*IF0BFn&GiYAwR9TGcg4h-$ z&i1flP*6B(GCh9wsz-zSqSHWsTAlQIbd_2UwDP@F+Sa#p6gq5j&0#a4Im6Jz840l$ z4mQ?}Jg#G6%kyYe&j)>5U<AgTIFPc?^}OSWiWNDp_&a{iC*IQXC^tF+XM|bm(Tss; zSvFYn*pTukwc)Xj;Zb{;sl`Xw1Vq?t)hpx-dQhZ9yr86F0i5KbS<_JPo_i6>TQWt= z=PcwE3gNDSGx?G&drsVO<nC;0Kd3LtX<4hD?=^4k&4Mueg+SVCORPKF5JaOB*v+bf zb0b(ZJGL&Jv$s6G{xb*Ot>e%!j{}cpZT3`S_Os2gyXxhy?Q?!6ik(;=(-Z-_`8{53 zZp*+UFIZaqcpzmg#V+f$wHz!&CxkRR)4vsL$jr)?NFSC+KQ@k;k1W`5q+`t^j)o;C z?p1+BJSo>;!3iW33A4ooEe9p9g@;hX0|{UAiDzE$1vSrX_?8DUIzuXrjN)O2;~#6G zM%BkCQR(F<a5yUcZljsLw-t@k_ZpqUq2)wE40IDGz$an~Hau&RW*$i$s?(=En3iu+ z(#6`H_r~rWWE-0}c4Kl{Z0bUcq&>_OYRI1vF8D9`VDaG1%xaggU?68Hd}bsAS-8<c z)OFw)E;-o28OfcS3A9veP8zPFa45B^)l*d}6NzTfu^c2Xcl5Lz$%yzsfhT3blHcJt zlH>S_EhSI<U$oThBryqrqzfuqe&i_sMyi=;OG=94LCU_58W4CJPt@F5mD(z4Bhkby z(h#86@3qkEZl%v38{)(y9C5tmwZH{E2bQdrzB2L}2Q6S8x#yV!k4lLN)6!A*$t(iF zompqSNz1*i7<UCD=7s>yT?C*d(b)pxHl5F(rKHV|#B+=A@LA{W_3?L!HJv7n^TRB< zhylLk5t$&EQ;x&=y1TdEU?Q;%oH%G|x8RPA#-%+qB@t^q@4ON}JCms<LL-GmNsDJm zCcNkvVHix^?M$9fu%=+Zv1X+SM*#V>tXPo}v7z9=GYOw?%9Ib5o<=}Tv?j>-$UOsV z*8Gk~#lAes($=2EOS%iS<6slK5$`lI<03m6V}nA4MMZDcz)@AzXxA6&!i8vqsS*^H z<P^Yrt(sa3-<dRn-pq#mEb{_onkAM5;$>cs%vCy^<+3E2O^w<#&akLc!1^Fhu$L>K zks~;v(((d3<b#|fL?WKZSWwF$+<=6}j)6+<k(P{}MD5ogzcy!e^<a?oWX2G}uBnQM zJ1VvuNLh2_B{{!k&yfPh*OYwAzvElpk+EgNCkk4r@p3%x+3SHd5(7Oc4VmJAJO>&E zR#K6MeBOhsgW(X*<Rlp(tUWC&-CI3N4pf4WhM&D)K}jhv{Xj#>pZPTdDev&SW-SaV z^3bo?k<$>f605P1i^fT9k(ngS8-LH+fWw<Ub>fV>8)Qzd2rl!#mG}#%#Cd~xEi9hT z0sUV*=J*S%4tI5E-rDK9vp<in!o}Ui3dY%O8_n`p{aNcg3J@xe$_OoZ;!Zbri|0tk ziWILlDAH!F(UvZ;>ao|RGNGYVG+@L+)#1oqZX{Yrcvedm>~0HEUP`j+S@BH6j+*zp zu~_OojwL^;Iqft^UvT0Bzu}%|e$RrM-;lHBOP=+2MiW|mmRUDgqDiEmI+_1stAxXr z&}*n%=>5V{t|qce*a<fBg$9s*unI!XQGSwyPPirPa_qzfnKYJ-#`2+RoG((ml=`ur z_T!#?kPg!ulFG?i=yVGW#%>u*I8*S%l7SO3Eg1<X){1%0wfjCOlzpcR*nkgcH23tX z+d9oKYh%ys#Q8?_pK+KoLZe#kbUUFxV`Jb1<!LN1XZZUY;78ullS{~c<OM7KZxRN6 z;J+ndOCg8Kl9p#Ji^e$3Sjl_gT4-=KrEDZ-?^#N<7`V>^OL~rah&au7!ExkHE&54b zoD>9VbGrUh)nZ`^m}@<=W?%*M@{sqG<n)|o1FtZ_t%$kfV9AGx)+nRzJQ9;<kTh!0 z8nd6txBOKex4)6iW9WHz27LE7S$Nm;wdm|P^EWY8?lZ_hceNn#pZS;PV@rQQWbjvE zN0;otQRO$aKRqQ!4jdR*a?cZm_OQJ!o2B$@jnqt`@$8kpfG*(H;uA-<ZiqUp6~W@E zWvNP80Y~mg^;28mi3ZOd8yep6i90+4ujy6v!(O=<i4xupl)#D|NB*9B)*MNANyZ}$ zCryRCtlp6YMq%KkWELakY<roiC3g}ak8{7+gu2O0W!W-_A6JTBIGkNn2Ww(GW5{_K z_zE3+3u+xGPIJ~ohgmr2N?n@c-nXCmf~N&YIF57MlaBCnH=EC*g_W~U|Ggy-2TA%- zH054WqXamxl03%&rBt6|GdPIQ7wI~HgZ!*coRwa+bZj8^z&`1%4)sdx4RF>aIiAQK zpbs?s#Q#Z5A=_O?!7um&9?u;$A1Ek!&7a9wvf-aOlB>T-^+|Uk(Bx_z8)CKnPQ0O> zjhNv%aAaU*!eU3Rejrw+TFpwjL9{Q9<lP7dSwgN|`5hidDK2G*Q%I!$3yp0_Nui-h zViK0v9(kjaq@ID)#s_a}s?gZB5Pl|E8!ixg^PYAhe4p9Qn9bDA9lE2u@G?~JlF@Rh zsJ`}c@EKj7ze3f|ql>N~gXcx?btJjJ$PO88_Z1}-4IL?uJjsOA=@~OfXH_e9XarDs zg-d&rMYP(_jRkoyVQ;DQWNJ<7(BU|+WI;>LMq90vd;W?QN5138S`e#3mu{;`l4HR_ zEhH>h>f_$h(9yBu2TI<M@U1TVku^B-OE7@axVdXJA?auNBV)TcX3MpjSt7F@8j#m! z)Nd$F3>#)wL1LV-7VtFGm22TFLrWYG0l^wp>%Hw~<C2z2Rg#lHx-q*_M@6Lv&ba&t z+w}h8su=P1;T4tgO!p*`2L~$iQrwVQtFBtwLr<$*<S}EVpUvb3Ejz+)w^O1-@TQ(5 z{&2I@wqDJ?(_uc!j9S&J$h;pAp(P1xdOq;KQ}P9{;1#f9#d|&pWxL_Th8oWc3Z7V5 zeuuX~g*Q)hrfR+;B{fD(ix(dS7UUEZlpNLYj=qjjdw9|nuvA?h$=%e8tyuGs6OM)x zI|eO0jvOd71$0zKP7L6UQvR4hSMCUKNHije$<-te76~8ep7d5(y9<Zca5i*1KikIB zEO8fZi0ehfW$ci<DH=RKdafa){z8;|84z;aho7%muocL;Jds%?GGEVlt8)5Tj(RY! z@7Pl+yLKeF1gLz--6*uh?Nn1#@-BE)cTTtLm`N~Bk5VbB3Imfm?PFhX<ivuLZnl<& zpR*z1JtYkb-m<1+L(Ve^r9=QZH93xx1OXwzs^x^|1K;yM(CFzJD%3;-KR=_SxS8Mf ze6rT4aqrE&F<2_^bQPRPRow>CZf)MmP7uzqzN@Vw3)GE9;@5!{JrM&3VjZE4p2|6$ zG;)ogRwE;EsR`ns&1W(r#r3B78@Kg=AhOVBJ=)Y-(NTJ<{X8o^v1CESN$*Og!`ImZ zVvsatt%#h=_$h;g$eFm_u^5Df34w$C@2H(-hbJ?+jZ-B(Wtycj>U#|_MtuBF{EkOa zcwTE^)wAc3pQ!nfU-M^r9`tbT*s3bb^++45_wk@@IL!tael`#)*~l-_iDwgpOlz(4 zA`axFbW|+pMSQHyV-ZBxsnn<?5l0-a$a&Bw>LtAjE5k?+u+Wo)E~OLYD7;S%OH#d` zZibqjFHrllA}N|h+DB#^ygs4)ELHf1)a14XFFLEa&OI6KvjP`ByPWV>IUDB*50`<F zb44~6hoV3GZ}Xqr*;_KI;EtU1{9p0NMvRJ)D^byk)izp-wwzQ4j2Dtuj_#mIMyF@f z<ReU_=NqAk6)9hluqW3n?2%vai5(vS#~*o6c3LTHWTcp$uwgx_J{l2Bk8COVdw#{2 z9C^h{YW_h7cqMP}s5tBGi0Cd9j!S*rslx1y;L>VbK5o#)de}>m7HK$8b7VorCkvGi zslUBgcd>TF5OJgxon{aabTqH`BI`C<XdSr6iKy7i8#2uJI*s{A6XU6bU1X(^e5=Ty zUR&uw!r`D$Tv|#-t*?w2rT|fm@GMx<P%0>~QLCA1Ti==6xF@FOK%wK&+iu^B8#h6o zdaJ@Z=>lRpsUI7@XGP74B|ARyfAA$IQWCykAm^9-6R+6QaUkU%`Jeeczvt&9RNN8q zgJz%JT;R^?=0cCk5F*)`uYDlp#K4IM)v@8RRA@m`Q1d{;o_j5HQcdF!a3VZXGR0xV zICgwRglEeOiJ%-Ug=XQIEi8`QlaX;EQWR!n&@WGB<mFfe$+=0^+j`5*(pE5^jK67@ zof8L=b1vTJk3V<i%y1W20&e;&{QvLb!{6lR`-`6$&A>nYtb84=q_F->isNU9%1%dK z!-<p!?kQ$Ay<QWXM2B1~tW<4+;6VcBMs0C!|2ERid7{z5>9zhSWKT+nc+~M4^EDsY z^DS?9%Yp|^?07+AmYe`DZP{p~-br&9ge5HxG<?JV!xITL|B0GjB$p{AZ5(j>S&)n~ z8p{}!iGY+yaW(>~8ckX{A;8B>+ku2q+sjz`#a_tt*sx$B+o8bm$U#<lPf0J4MXYwQ zm$x{Ud?vh~M+Hj<?0jOCL!|BF$b4=MH4!V!F`D^-Mpa;APJ@V^k2;1LHQM39HO}sO zIts=7EX`Ze>VTV8ER(@6(W5PsjxUv6FPDd9<UR-Fs1rD#(rq3bGnI^75l{RxKhX2v z@&D4ZBIk}z{1xvMD^!t?@hJI7P0oUZ6BQ?;Zg)~JhS_Jrl25Z01~Uv-5QCq1Bq3+T zk|P~QUNNv^g=5VpI!e)#3jqim8;-2Vl|JJ%CEUw>zURP~vRB>%6%Rm4PQ_A#q7x4c zcn;jFc`oG1h?TiJn4hInFTz=e=M950A|j%T^XYS-Do1jiM|q296<v8b{Dqw4@?XsL zc>V$geT$QIxpiUYA#rEt`K<-Xtu}jNrFq@Rp>1s>NxkDq%Yfdzr=2Wem86y@j+Dmm zi%iN9hW{^!spaNyIv|)aWJW>5kywP?1^0MP{F0UvA9&<DN**||Wl2iLK?Q%9$8_2a z-xG17P;0ei;1wxH{xdK5uV{E=pkQZS;Xw^~JS*?}+4eIsDag1`7@JirdT9!v992^x z-85sTBdEj+@Dvnp<AyI|O+=*1s*#RA<wyopdV~xH9dgorC9<riB0slgTgxojWYo+J zMs*qevc1+Mt$+w<64cuK66NI%nu88H%tKvU>ToPLvBKfyOqs5_%pS5q+MU{lyw_J7 zB1(Z38fg|I%WO+D6K1w`4(-3kv7zM)e#f8r$iJcD1OJ|yf6q7kEqhwvM>;BAb70L6 z{5Sk>?D#hf9C<e7=Ew>heZ)>yxj~b<QKBB{mFFDjIr0ULJJx)XuJJWB36HAjdU=$C z+&MgMmK=ECsNSY9dtN~3p0rpQ#~&3B9O>99KewgiHCtL1l1c}*UnktfxY7#sTw`;) zIrV8?_IT6la^^7e7ttEye^2IrG+YRaucIfg<2(Efr~#h|1-~6b?Jj^+d{*S*&Qd!3 zh4G1tH^ZA@Ez(SCkO`-zXUPlhqz@Z&<HtR2CtpOxQ9$}Jz|b4qDW}j(Z=}w(#tLy( zG4__1>y){=$FpF`d$ugu@{X5$B4SCzf_KWj3K<_cFKF3O%WvDTV!;QFN?vt*&wnIm zNk&1gS<;Aok1hDHlXN>aq3=XMNo+Si%=Tl>p6rq4DN`#O<P}?Lb0#FlWA0f}sIDor zT?`Ft5NLEZAM9oL4^7V4);c-`3x;E`S7=Rb!khr^^5#NrZR9-=i2@ih@*<o5Rl03G zxxzO?MU5yt?F?QNw(*U&i>VOvOI5UmQI=D+{NsMR711#_#6pO~P9?z^la5E|R~WtQ z4H1>gwY{U_=lsC;d<``GBVV)R4-|YO*KEwdksV+1H95cK4c`d!`cAuKZ;-Ue*wLd1 zs3cNL9tbB(g^{<c1+s;REel@Lv5;t|mRqUUe6#1ohCK;Sl$xmJQY+Lr3Tn1|$%<!N z{e`JrC5KN$Ejd)Gq>Z2l4K<#dg-CW520z#x?`NVqrzv)-$C<mC$35Xr!WokIvnd%j zWG2^&K7Zw+oi8ukjb`||wsSWeb<=k_pBlQe6^Xz2@9x5&JXn=FYQ#otd94bdA>odD z;US#~Bw9{tX>wM0O7>EfMH<-;YRjA|vc~X|UL}8Ifj6<tNedvA%<~fsKj)>ED;--3 z4m?umyJcE^w3aAU+fC+3v~~%BD|-fF-tr&G_-o@Yhm}jMDrkHcBHi5M`x<NdIpw?h zS>8o&7=!WCiRHa4IH@vBRejaQ?n_u$HI`^T>eR$!ivR9RD_B!W`;}<qUDM+!D1ik% zJ(>3CVRGsvya{-(NDG%vE#e4psBI$M3JTCtYSNW+)Ox6)rQpbpgf$0kb(y(FVj2E} zJrCu%1-W}r{GlCnFYB2Tbp%cE3W=?24MgS*dSP{DBMJDCm(=X}Ac=9o8c6s#F~4KM zcf{;CC{FSv-|#2CW+wz~#f~?;;yWT@O4W3URy>(9NW4ZggIp(r4!%7DNA7__e(ab7 zIWKVZtl7z)7OLl%g?`tVhy^PJ9A)ZBDk@%5u;q~@dl8u<(Z&lL3%0~`RBB|y?s$xq z><r}QEhQw?e|Kk@f6+xI&vjUuzv?(6YxQsBqWPP7xBg5ZGM@@YpV9DfW*j-UmYkc1 z{b$!AT$HZEtdfjo(&p%lz!1$2@Y-Ce!RXX5u;$)&ujAW`Nt9DEQjQ!sNd(|kc#oct z3M6+OQz3c@e8*`-VhOBaNbLAPBZ9_)?*Y%zoS!ZCG;Dd4AhY8WD<VoNNiW8%s`oUc z)I_vWIsGFo{}X?!0d}Vv!kdhBFm!i6!?lhH8Y4BuiI_18v&T@K;AUxbtMqb(7K*Jt zs%{Koj8r0;M6LCR<jiE?>eSR#dW3YGBo>NT@sgL)J*KqUFeW-;3RUinTGKrX^McL| zVk28POxsGn)s}tDz10z8yWF!@a7|7^sd~NVXjb@GjaoxaBe`$z1=aeL!Y;I@BGu=a zh(I{dYVwpDfMWy`gqkpw$*t8oYs*`H#)_5&Z!|Mpu;M46<&XR;fpNDKINtChe?`jQ z@t$AtOvH~o@iRV3UX8`kBgb;gk&cB0F2r&%XA&3%h;1%RLCq&tEP;d#AGB=^c(7bi zL#>#B+nMACYGRIjp$AVO35C6R#adJHr9wDYnvqt@AoeN}ynImON-H%T?r<fzn=3r$ zgaLoCj6E0FCH@K+b(wN;J(Ig69t=08eB8{`y$=8QT!VXy#4@iA=OX6ejPEgdQU{5T zV@3Hzs!nn-mgj-kNcq)?9TqfMjqO8IZ6U{a!)PV}88>*Sr3?u4nv?}K2@8G@Kr~1= zpE;59Q5(u|$U2_cXh!Jd)M%y1tEs42b1(8=O9piG+|zU5_jq>v4NJA7on7|MAp6XS zEXfQCKj9173$T1|NG*(P*Q=^+m7algf2p3`Q;tR|&jvziIz9D+L!rcTAi=SqqNZfW zjxBF=Gln?96A>F_tHy#Nj2MEPqgBPIrWvtE1uehlBS$)xyy1>Vt1?rqUxG_DGUL1> zmB(fTbvg-XI|?e{zK%*R4jbJ@)$}lfusho*m$Kcpnq`LTYM`dz#F9=t<V3j7_uR4O zJ0cq1^O^%29{9-b*|6nErC9okC7zxKJP|+9knx5CpZI|<`I_%3No}%-V5}?q?XW*f zXgRRagR16%BWp_Gydsf>5>fX8V4;@uE5vgiInnZpcT_x(a3Ch7qU2<Poe?q6>1wML z+)$dRJfopzrR7LuM8L%GubC8+Q#4jD=QdUePS6u~C6>G9ea8Rmf4B_wxz(I<#WLk4 zfZ%fe=dKnapUb?wWidHldHBB|x0##?^QqsM84>n=LDRLPW5YcQy~UlXw1I{#AL&@I zB%|gN&sInDa>2$L!3MZ_XuOcjBitxU8}bhVU{%zT%(Rpw{1sagPHd%0-{?OJmLx1V z;@OjGHWv=sAd`K~GmZt0oJNg8u)lBlmXC^73bUjKHZ~x3Ix-?vc4NMQo7Ks^0UyzU zmg*=O=cB#eC8VVfo6wOhoRDf{K3e31<RLaJY_ANj5fR`SSQ2v&EV;)~@{?Qvy%A-4 zX~=>*c0x{h<t6uG(2Ny#LCIVGz%w}!U(1CZ@u0Zx5dK+HDWN35Nt;<pMsGfWg?{gt zTo0%b+u$|IpLB$s8r(=jm5zjxTswR;AqxSPp+b)6`H4NXP1RQXj=$k2dVax<9SJ!< zlJOl2Vmw<OsCnQ$cY?(>{E>*7|DHeb320dHnrGlqVM5{I@QzG}QFt5`_K0d(0TBf` zD|!xmBqf$aC46PzK*T~7S*+tQ_?0SRV(u7NQc%hFlPj`oi)X{Ynv4}cvf)HR$ANoo z^iOnbi0OHtmu7U-Kd$ruiRCQntgVgDnpt;NPDNM!rMq%;%__cVcJN$t?9c1B;U+SG z3&-p{b;{jv(7DeZu$M~_e}mfWujpG-Owu?`Z7t-v*F>w*{BB`h#lfubt#-LN2`zgK zp1pR!P-#{*`YN4<@?Nu~!Lq?n&6a8#*6C8%(eT$=zBg<b>~I<-8!;s(aXc1uv|3m= zVxD-fIu8X41vujaEiHR~%b)m?U$ds9<%Rh|Cn(QY^I`|ogeL7x0TwBRBQtGVZ!^M4 z(chXFA~kza5K<H-)b;v3QaWvJ6V=EtaL1ahRyIAkoGUdur4C2BCTqhJ45EsJX6tB} zsQ_jQ?a+6eNO|IsmW%}n_loPu^+`p_9}Di`BLlVe)`iJy28naSZ`V<3>XnL3m(o)i zPkJ1;lv3#R=Eo>m$Uum$zgWTVp-D?=sL46dk@A+;I9~E2e<b38h$BY^wmh-tiBeFr z2ML-#knxH?^2mXs9?b7p@-;tENrD|K7P8Z0B31L9YxxokaGj`+_X>kw>i`L2>=!ib zw5<+8{ZQL4+3|`KTfSyxsDM}&#h8wrXfHv<m#~!rbWcmH_q*hojjoqO?|x?@?3!9m z-^d`ZT+R7!=jujRZZCIz@C{efyh|sCpZVqdjl|pA)cZ>(2UkAYYfu$NS8!K9CqAnM z=d8e~<M-y)8#h{m08b}isbgz|9*)EIBPA(Ic@RrwY<O+r8Y)r>IZrdyU}3h=P%4=q zD4HVO@JH#;GD^`X5?*rzBJMb<=?S9JI|e#FXapV0hSjOQT1kPn;tRg9NWPw!Ee|AA zG!(q$1Ml%PZ1|0C-9g?3XMB|@O?a3M2V%7losO5lC+=;8oN|&-B3zCK?Sli?ZEY;% zLDgEwckQ{O7tf@ZBxT(Bwm8;wG*tRr1L33Mz{2twycRsU=01bPD#a{VsO65d-1$f& z<X6!DZ^&>wXcHM8K#>mUQf7V3869#bj#|&4NcdEbnu1m_<sG@fMI+VCgZWK6i`)sJ zjD;Q|W8mmGNC-D_DGzhb2MTf=3!c<EbEE(OAOJ~3K~&gM@f%8>Ik98Kjsrg<1ybJg zZ~2~#6GtBSSHQq;wa4~Ed?MmEd~X<sC*YBHvmzrBoiH?xM_!l>?2eaw&$H%bH3c0X zSy2eF;6>z1D1d}J4rFxf*pPAJkx%5DBw*?3dDd<}9KewlJu7Mnpq9Ef1}*C{j=Ju` z_Pf+HvNL2^bRk~3R2ciuVvyZTo|pKml#I)8AUBhP`YYz?tp@hIW^<pFGW*YT;Qj(j zddbee)agtJ!_IsFm_(<yP2!1~ltTW6-sV?9zqh9)QN1^MJH0gF0}Uw!9Y-=2x+UA$ z*CVN=f>5_5r{t(%c&~Rb=T3mr1<yij27F|~iAXjlCz0|XxK*Z1u>%hp6K9%4E@T8s zXoNnGc#kLJB?GN!z=K4{xmFj+j9HAqJ9|5tyxNsmEoC?kTQZ9$a@y)ON(#%wr1rYG z6E(BU;Op3bj@LuTSS`uqgo%}T5eDr&v3BOoOqV#$cQTpE4l<KPM%8<%sKtex3b80F zB@2a_dulS-)q)MM(abXv7(J3;Ig&mz)8sKxbC+<?<0FymHd39`=v^!f;Trb64NF>D z(eq-n&utaEo>1vnYbaQd^PYhdTU`}B5nG9zzTyx3oRataj1pM$TfUMJF4UE8S@0#_ z8Eg2Y>mb!39jn6#Ji<ytfG}xW^OBvw53%ODokT1n04&pEs^<Y%@g2~xWFX^(mQ|7b z?p|6^$1^Kdd{iRek_J!7l6%bn3(ZV(+2IPU>M}!<c^h2K12WtN!<(xp-06&%61kJf z$2MH#<<5`1^PCKS2KM7Kc^J3TZ$IbMxP4Z;3uBzSf*4HkBICX%J=;GIHvJ2x|D|zD z#zfJMmR4MkNY?wxa>hD3?S+^0oYY{p^lBjnslpn~Y6i8t10_pUZEKt|6B^y56^<PZ z6&-gJyq6as<7kJ6v(%3j8Q<gRD5chGh*+uG>evA>Cu!be9^?QGHDM<faW_*jHFEWi zypqABFLOaqZNbn38MOw?OI2whLwKoOuah+?(N7w5(?zOoQW?1hRY}295^>K!!h(91 zo06IEcf{m79SoyPdXO|WQB{^PP%^L}qomN*Hj$G$QqYp8(1d7U$%&SoUU@FgNG~V@ z!fd9REXCqy$HFb7a`8lhlLm<E2}j2rjgIqLv%xT@^^(CvdZ+|*e@w}e0~@7~ZE47P z!@#pLb2}a>c&7u@5mWP?h&9jriNB)e=d>)zW(g@f?)i!Xg=~38GX2hBGMy+MsZ~#r zOAT;G&K)@o8&md%LoN&!mekZVET!=a(whhQw;wo?3uG2tGNnZF<8US>Wve`nNG){` zb`Jt_3<fL>Zl#)){6d{wr4xyXn-&Upp&>rkAq&iQURzD^yTh#Va%bcNfAPQ00;_K^ z{4TRQF4--g3m1=WWbL|(0Lw{S6GFaw`&+{-(HD)VLp@Q56EU9>#+oQ3%2KO@rVq~7 zR#7!E6|v%7YUAj61%fxSdZlu;mWKAMm8P2u(Nl4v<Uot#o?j91Nj}vO)tD)arB|-& z9gc4`JxTC9l92I&oQ5r}8jDafrsO)HYfFGltj!w(i$blKYIb&-CG8H{;C5<vBa>kq z>?a*4CFfakXB*O9Lxx6eW2P;yv&6eZN7rC^#{uz>u_jT>;XEKJ47Fob94NI70VHaU zl0HW)6!VlR^m79&*>Go>u#u|yOjD$ql9-H_tk_Tx>4*$RW~M5`E1jdJnWgTIl<9Ds zMXE^~y=$=oDML8<VCKN1?Z^{#5Fui*A!2Wl51%Mmu;n8=PB>-1Jn)Vsj<4v*`JS)H z`D-Hf{F0wIv6NTDsiKV-c&o&bfE!5JoaIT6_;n}vMDU~(w8Y%8*6}=g&tq0{B$sAp z%!p;`+p!=fW>3RLO4w9UUIBWr(&pAll^GMU*A>zWXt9v2$LWv@kD#Njp-!-QKl8yv zpD{<wPnpPq2%@vHE+XOz1vSiwxKobQbzZK&(Vh+$R<YX|=a)yLyP2`;E}H7-;&^oS z(R9|hO+X@VTfyFv4^pYW63ga@^?f=6T83(Gv^0g7>_AvK8P*Z=YK}qUl`fTpf`JpM z-8>NsmP8a3Z0I=Asy4`3^GL^$h!vg_D_Zs(WXFnFs)pLC^=&EGanPr5V5h3gu>dj_ zO20i)QmIWFt@0CJ+3WclnTXaI+8u3tW6e~8l)4d8eFS(y%8Dh07D$P{Yq*pPaygfC z5u_uV%qHTZWI`EC*wm2NwopA1>F`Hd!%j7JFT7LG(4`ElB}Yz_C0mdyzarOnUXZci z&SbltHljh~)T=^Ir32_Sugjz`>}Ek&C!T5L$*$y}4ztWermZnR94ZPH)Z_{z?@im- z82Ys);R7Ei@ibJN_`nC=Qt}6WBH;}WtoW}v@{UZq*du?#_pCYchJlDr{9FE@3M|r0 z*$Io<X%?1}YkP}%UNY6Nfsit)<vKi1ywVmm1PjCpEcKKe1wz}=DCqrxft)o7o{vOi zw2~hsoRqs4(eaUnf=<h`6MMZ|neNg;2Wl$!MXLKd)nF=?^Vr>pmzd6y$P#KMpdPbK z-pJbI<krbK+4`s5h`+I#xNc}8lO4JXsmomU>~Eq?{bz=i^PhG(K^)AA&rFcuY{L`@ zJTRuGE+k_aQyxN-S8=i|Ha}Ab_VPh^<)x(pG9pD*q$xzB${-iKvQ?eq$$8?V;PxL_ zuobwd=AML{4;*y^!#ywg2QoS;YPEYCdj6yeDcr0rCn{F#sBO@nQgBjRo)D=aj#X8S zsP@PLcbeIOGiZJ?!l5F)tFb8>gQhUlTZJ+`BJMTDPb8!sxiMjrndrSq%~9*M3m$A- zF(wcDS!f_?1ikH=<Fj>|mvXNsX2Fs*(6DC%v`UnV7%bl|q0%cJRM7?EayU3Y;3(J& z0`F&&q)w6X<In1KGe(j#HA-RY*{kIb^THzw9EEoBk%_GR%+)(OdroBhBOj@VdElOe zEj=Ikf*;7qC4PdOPdv~NQ?ce-o;dKp4?I!uo|tz$@R1i%4NfZ8qY&<4KH0I53e)LY z8t|NmBq)z0UGe06#Si*CVg+}1@*rpW*Fgp8Xn2XE<^?~Ju;)O*k%AgX1yv|n>#j&? zIO3={60;}a4Hc!9Fiw-sf>O(+k$>46Ybdg2*jsL(yYh3~3exbi7FY-0a4}7D7v$vO z%n;^o$W8ub5##<U%LBW}ufKBcxx1av;b(qb3^d?pHs<MjkF4a$so{0BwbZJ0TCr+7 z%efyU)CfwxL_m6kDjjI4HSiBc?L_tENpjYfSG=cXLrJZG<xFgnnwXlNJC=MTXURJ@ zv^?syC&YZ>CxM*2ZiYdT(v1z?I|@2VQ=c`86l`Zxs}Yx)(W)vLBaQ-zZj5g%EPn$X z@*Nqi>cElxveaSH7_7?+a+S(ngzDW<Jd?@E8e7;kM$Q}LsX;!9>9g$USh1v%un03Q zMI<~@P)P#Zkg}ALa<G(EEG%)P!L%|(g$Gy8f)A{zg-Gypnu(3MY`q?Uu@Q??O-*}U zO$+%+QVtYK-UveVCwd%5P4g0if(_b|C$uc+8F-810|P&C&sXd@@W3<oGfv4f5eq6J zzNBQuJ63$)U-O?R_=bDl^A!uWx=R+iy2cd2h9yTF3)S(3!BiKVq%2%gl8YC<;)ULY zflN{FXigs9!@!6Pg8MZ!6@|XGW=F#VH4C<KwdWl4YL=p22mS0x)Amf$?<Gfi?zQXo znh-`aC_!WokYN@@<j-ud?xK`)XT!5-VmJ9Sa;Y~z!}RC=O2Xo=VtUVOw&>z`b62hG zdB^Q$U-4I|Nay^$2}k8`$fD=)>8YRh(yGU2&w^gnSEs<qks2Hv923B@)2P<lj4G9s zd9vI)LpY6y&hT`vNO@vQBlbv0ze*Vx_`n_a6m QA;V-vgMgUlGC1!kF+GLrMzFx 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≻Yqzj^B zgr(vCW@2KaXrGv$Xz?;@cfRI@H61G+dEq^O;4k<C2i}nLb3XBoU-CN|dTM!TdI}m! zRYDOX&*2@JiP)1VG^u1qL67C04}8agwOrLE444kodM05{%tp(*nUO}_&NKIXub<Q< z>t*^gxoW|JoR}4R3aK1MVg^#iz!w_AGd2dRqz`pSw5UtMP-=-fo=?sPdj=plX49~c zFP$HQ=7^r>M^iKGg$nQWgrifJujOA46gOvJOs@Xf)4y|8OEHH=?G%1LpR~ebfLx;8 z=Af<9vhMVw`Xsz)lj<m+JX^?}I4C$}WTMnZ51|!X@%Q{&mIG_<c;SIHF?+r#jbdP< zzr)kZaycN49=0KPRb92=;;0h^Sf?81Cu+sq^syIvczHx5eB!tKPeDq%%tf{fEc~>{ zVartkwi=z!;l@2CcI?^8L7ObDvssU{)ranZgqT7$rxkZ}G~82?V0q@Bc+VYcTK2pR z0ZMi;8H*&wUTc?p^x=4T*pBy-R?Ou53%};S;YLu=ku`5PkkZkSF;Y@$vkHaCDuaF! zgdTO7$GsZ*K~ap8rkl=V>Q-E_Vg`=DTN`p{30IO~$KOcE?R>eernIs;x}9 bYNA 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π$X}Gg2(x^F+#l2?wp| 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 JPDHLkV1jOIBk%wK literal 0 HcmV?d00001 diff --git a/templates/static/en/team.html b/templates/static/en/team.html index 7b94e3ca1c..f3b57e7156 100644 --- a/templates/static/en/team.html +++ b/templates/static/en/team.html @@ -110,6 +110,19 @@ <h1> </div> </div> </div> + <div class="teamMember"> + <div class="teamMemberImage"> + <img src="{% static 'img/headshots/yedida.png' %}" alt="Headshot of Yedida Eisenstat"> + </div> + <div class="teamMemberDetails"> + <div class="teamName"> + <span class="int-en">Yedida Eisenstat</span> + </div> + <div class="teamTitle"> + <span class="int-en">Project Manager</span> + </div> + </div> + </div> <div class="teamMember"> <div class="teamMemberImage"> <img src="{% static 'img/headshots/michael-f.png' %}" alt="Headshot of Michael Fankhauser"> @@ -132,7 +145,7 @@ <h1> <span class="int-en">Olivia Gerber</span> </div> <div class="teamTitle"> - <span class="int-en">Marketing Associate</span> + <span class="int-en">Marketing & Communications Project Manager</span> </div> </div> </div> @@ -367,7 +380,7 @@ <h1> <span class="int-en">Shanee Rosen</span> </div> <div class="teamTitle"> - <span class="int-en">Sr. Software Engineer</span> + <span class="int-en">Sr. Software Engineer and Data Analytics Lead</span> </div> </div> </div> @@ -489,7 +502,6 @@ <h1> </div> </div> <div class="teamMember placeholder"></div> - <div class="teamMember placeholder"></div> </section> <header> <h2> diff --git a/templates/static/he/team.html b/templates/static/he/team.html index d9049132fa..3706d85d91 100644 --- a/templates/static/he/team.html +++ b/templates/static/he/team.html @@ -15,6 +15,19 @@ <h1> </header> <div class="row static-text"> <section class="main-text team-members"> + <div class="teamMember"> + <div class="teamMemberImage"> + <img src="{% static 'img/headshots/yedida.png' %}" alt="Headshot of Yedida Eisenstat"> + </div> + <div class="teamMemberDetails"> + <div class="teamName"> + <span class="int-he">ידידה אייזנשטט</span> + </div> + <div class="teamTitle"> + <span class="int-he">מנהל פרוייקט</span> + </div> + </div> + </div> <div class="teamMember"> <div class="teamMemberImage"> <img src="{% static 'img/headshots/rachel.png' %}" alt="Headshot of Rachel Buckman"> @@ -89,7 +102,7 @@ <h1> <span class="int-he">אוליביה גרבר</span> </div> <div class="teamTitle"> - <span class="int-he">מנהלת שיווק</span> + <span class="int-he">מנהל/ת פרוייקטים - שיווק ותקשורת</span> </div> </div> </div> @@ -430,7 +443,7 @@ <h1> <span class="int-he">שני רוזן</span> </div> <div class="teamTitle"> - <span class="int-he">מהנדסת תוכנה בכירה</span> + <span class="int-he">מהנדסת תוכנה בכירה וראש תחום דאטה</span> </div> </div> </div> From fd18bf7f0da00ab1f8974fb98dc6546a93866f08 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Fri, 10 Nov 2023 14:47:30 -0500 Subject: [PATCH 447/756] static(team-page): Correct Hebrew gender for Olivia and Yedida... --- templates/static/he/team.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/static/he/team.html b/templates/static/he/team.html index 3706d85d91..a34e881fa1 100644 --- a/templates/static/he/team.html +++ b/templates/static/he/team.html @@ -24,7 +24,7 @@ <h1> <span class="int-he">ידידה אייזנשטט</span> </div> <div class="teamTitle"> - <span class="int-he">מנהל פרוייקט</span> + <span class="int-he">מנהלת פרוייקט</span> </div> </div> </div> @@ -102,7 +102,7 @@ <h1> <span class="int-he">אוליביה גרבר</span> </div> <div class="teamTitle"> - <span class="int-he">מנהל/ת פרוייקטים - שיווק ותקשורת</span> + <span class="int-he">מנהלת פרוייקטים - שיווק ותקשורת</span> </div> </div> </div> From 88abb8f97acabdd6dc79be4639b94c4be0d0c7d2 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Sun, 12 Nov 2023 09:31:27 +0200 Subject: [PATCH 448/756] refactor(VersionBlock): Extract new component OpenVersion from VersionBlock, relocating functions (onVersionTitleClick, onSelectVersionClick, makeVersionLink) with updated names. Note: The following changes were necessitated by the above refactor and occurred as a result of moving functions to a new component: 1. Handle Hebrew translations in the same way as non-Hebrew. 2. Fix a bug related to single-section books where clicking on versionTitle would reload the page instead of opening the version in the sidebar. 3. Streamline the functions by removing unnecessary conditions based on VersionBlock.props for consistent behavior. --- static/js/OpenVersion.jsx | 75 +++++++++++++++++++++++++++++++++ static/js/VersionBlock.jsx | 86 +++++++++++--------------------------- 2 files changed, 99 insertions(+), 62 deletions(-) create mode 100644 static/js/OpenVersion.jsx diff --git a/static/js/OpenVersion.jsx b/static/js/OpenVersion.jsx new file mode 100644 index 0000000000..87ae522aed --- /dev/null +++ b/static/js/OpenVersion.jsx @@ -0,0 +1,75 @@ +import React from 'react'; +import Sefaria from "./sefaria/sefaria"; +import PropTypes from "prop-types"; + +function OpenVersion({currRef, version, currObjectVersions, targetPanel, string, className, openVersionInSidebar, openVersionInReader, rendermode, firstSectionRef}) { + + const mainPanel = targetPanel === 'main'; + function makeVersionLink() { + // maintain all versions for languages you're not currently selecting + if (version.merged) { + return "#"; // there's no url for a merged version + } + const withParam = mainPanel ? "" : "&with=Translation Open"; + const versionParam = mainPanel ? version.language : 'side'; + const nonSelectedVersionParams = Object.entries(currObjectVersions) + .filter(([vlang, ver]) => !!ver && !!ver?.versionTitle && !version?.merged && (withParam || vlang !== version.language)) // in 'side' case, keep all version params + .map(([vlang, ver]) => `&v${vlang}=${ver.versionTitle.replace(/\s/g,'_')}`) + .join(""); + const versionLink = nonSelectedVersionParams === "" ? null : `/${Sefaria.normRef(currRef)}${nonSelectedVersionParams}&v${versionParam}=${version.versionTitle.replace(/\s/g,'_')}${withParam}`.replace("&","?"); + return versionLink; + } + + function openInSidebar(e) { + e.preventDefault(); + try { + gtag("event", "onClick_version_title", {element_name: `version_title`, + change_to: `${version.versionTitle}`, change_from: `${currObjectVersions[version.language]['versionTitle']}`, + categories: `${Sefaria.refCategories(currRef)}`, book: `${Sefaria.parseRef(currRef).index}` }) + } + catch(err) { + console.log(err); + } + openVersionInSidebar(version.versionTitle, version.language); + } + + function openInMainPanel(e) { + e.preventDefault(); + try { + gtag("event", "onClick_select_version", {element_name: `select_version`, + change_to: `${version.versionTitle}`, change_from: `${currObjectVersions[version.language]['versionTitle']}`, + categories: `${Sefaria.refCategories(currRef)}`, book: `${Sefaria.parseRef(currRef).index}` }) + } + catch(err) { + console.log(err); + } + if (rendermode === 'book-page') { + window.location = `/${firstSectionRef}?v${version.language}=${version.versionTitle.replace(/\s/g,'_')}`; + } else { + openVersionInReader(version.versionTitle, version.language); + } + Sefaria.setVersionPreference(currRef, version.versionTitle, version.language); + } + + return ( + <a className={className} + href={makeVersionLink()} + onClick={mainPanel ? openInMainPanel : openInSidebar}> + {string} + </a> + ); +} +OpenVersion.prototypes = { + version: PropTypes.object.isRequired, + currObjectVersions: PropTypes.object.isRequired, + currRef: PropTypes.string.isRequired, + className: PropTypes.string, + openVersionInSidebar: PropTypes.func, + openVersionInReader: PropTypes.func.isRequired, + targetPanel: PropTypes.string.isRequired, + string: PropTypes.string, + rendermode: PropTypes.string.isRequired, + firstSectionRef: PropTypes.string, +} + +export default OpenVersion; diff --git a/static/js/VersionBlock.jsx b/static/js/VersionBlock.jsx index 40219735a3..4454db3d5f 100644 --- a/static/js/VersionBlock.jsx +++ b/static/js/VersionBlock.jsx @@ -6,6 +6,7 @@ import Util from './sefaria/util'; import $ from './sefaria/sefariaJquery'; import Component from 'react-class'; import {LoadingMessage} from "./Misc"; +import OpenVersion from "./OpenVersion"; @@ -36,40 +37,6 @@ class VersionBlock extends Component { this.updateableVersionAttributes.forEach(attr => s[attr] = props.version[attr]); this.state = s; } - onVersionTitleClick(e) { - e.preventDefault(); - this.index = Sefaria.parseRef(this.props.currentRef).index - try { - gtag("event", "onClick_version_title", {element_name: `version_title`, change_to: `${this.props.version.versionTitle}`, change_from: `${this.props.currObjectVersions[this.props.version.language]['versionTitle']}`, categories: `${Sefaria.refCategories(this.props.currentRef)}`, book: `${Sefaria.parseRef(this.props.currentRef).index}` }) - } - catch(err) { - console.log(err); - } - if (!this.props.openVersionInSidebar && !this.props.openVersionInReader) return; - if (this.props.firstSectionRef) { - window.location = `/${this.props.firstSectionRef}?v${this.props.version.language}=${this.props.version.versionTitle.replace(/\s/g,'_')}`; - } else { - const action = this.props.openVersionInSidebar ? this.props.openVersionInSidebar : this.props.openVersionInReader; - if (action) { - action(this.props.version.versionTitle, this.props.version.language); - } - } - } - onSelectVersionClick(e) { - e.preventDefault(); - try { - gtag("event", "onClick_select_version", {element_name: `select_version`, - change_to: `${this.props.version.versionTitle}`, change_from: `${this.props.currObjectVersions[this.props.version.language]['versionTitle']}`, - categories: `${Sefaria.refCategories(this.props.currentRef)}`, book: `${Sefaria.parseRef(this.props.currentRef).index}` }) - } - catch(err) { - console.log(err); - } - if (this.props.openVersionInReader) { - this.props.openVersionInReader(this.props.version.versionTitle, this.props.version.language); - Sefaria.setVersionPreference(this.props.currentRef, this.props.version.versionTitle, this.props.version.language); - } - } handleInputChange(event) { const target = event.target; const name = target.name; @@ -145,22 +112,6 @@ class VersionBlock extends Component { e.preventDefault(); this.props.viewExtendedNotes(this.props.version.title, this.props.version.language, this.props.version.versionTitle); } - makeVersionLink(versionParam) { - //versionParam - either version language (e.g. 'en') in the case when you're making a link for versions in reader - //otherwise, 'side' for making link for versions in sidebar - - // maintain all versions for languages you're not currently selecting - if (this.props.version.merged) { - return "#"; // there's no url for a merged version - } - const withParam = versionParam === 'side' ? "&with=Translation Open" : ""; - const nonSelectedVersionParams = Object.entries(this.props.currObjectVersions) - .filter(([vlang, version])=>!!version &&!!version?.versionTitle && !version?.merged && (versionParam === 'side' || vlang !== this.props.version.language)) // in 'side' case, keep all version params - .map(([vlang, version])=>`&v${vlang}=${version.versionTitle.replace(/\s/g,'_')}`) - .join(""); - const versionLink = nonSelectedVersionParams == "" ? null : `/${Sefaria.normRef(this.props.currentRef)}${nonSelectedVersionParams}&v${versionParam}=${this.props.version.versionTitle.replace(/\s/g,'_')}${withParam}`.replace("&","?"); - return versionLink; - } makeVersionTitle(){ if(this.props.version.merged){ return {"className": "", "text": Sefaria._("Merged from") + " " + Array.from(new Set(this.props.version.sources)).join(", ")}; @@ -187,7 +138,6 @@ class VersionBlock extends Component { return (this.props.version.license in license_map) ? license_map[this.props.version.license] : "#"; } makeSelectVersionLanguage(){ - if (this.isHeTranslation() && !this.props.isCurrent) { return Sefaria._("View in Sidebar"); } let voc = this.props.version.isBaseText ? 'Version' : "Translation"; return this.props.isCurrent ? Sefaria._("Current " + voc) : Sefaria._("Select "+ voc); } @@ -208,9 +158,6 @@ class VersionBlock extends Component { return !!this.props.version.purchaseInformationImage ? this.props.version.purchaseInformationImage : "data:,"; } - isHeTranslation() { - return this.props.version.actualLanguage === 'he' && !this.props.version.isBaseText && this.props.inTranslationBox; - } render() { if(this.props.version.title == "Sheet") return null //why are we even getting here in such a case??; const v = this.props.version; @@ -286,19 +233,34 @@ class VersionBlock extends Component { <div className="versionBlock"> <div className="versionBlockHeading"> <div className="versionTitle" role="heading"> - <a className={vtitle["className"]} href={this.makeVersionLink('side')} onClick={this.onVersionTitleClick}> - {vtitle["text"]} - </a> + <OpenVersion + version={this.props.version} + currRef={this.props.currentRef} + currObjectVersions={this.props.currObjectVersions} + className={vtitle["className"]} + openVersionInSidebar={this.props.openVersionInSidebar} + openVersionInReader={this.props.openVersionInReader} + targetPanel={this.props.rendermode === 'book-page' ? 'main' : 'side'} + string={vtitle["text"]} + rendermode={this.props.rendermode} + firstSectionRef={this.props.firstSectionRef} + /> </div> <i className={`fa fa-pencil versionEditIcon ${(Sefaria.is_moderator && this.props.rendermode == "book-page") ? "enabled" : ""}`} aria-hidden="true" onClick={this.openEditor}/> <div className="versionLanguage sans-serif">{showLanguagLabel ? Sefaria._(Sefaria.translateISOLanguageCode(v.actualLanguage)) : ""}</div> </div> <div className="versionSelect sans-serif"> - <a className={`selectButton ${this.props.isCurrent ? "currSelectButton": this.isHeTranslation() ? "heTranslation" : ""}`} - href={this.makeVersionLink(v.language)} - onClick={this.isHeTranslation() ? this.onVersionTitleClick : this.onSelectVersionClick}> - {this.makeSelectVersionLanguage()} - </a> + <OpenVersion + version={this.props.version} + currRef={this.props.currentRef} + currObjectVersions={this.props.currObjectVersions} + className={`selectButton ${this.props.isCurrent ? "currSelectButton" : ""}`} + openVersionInSidebar={this.props.openVersionInSidebar} + openVersionInReader={this.props.openVersionInReader} + targetPanel='main' + string={this.makeSelectVersionLanguage()} + rendermode={this.props.rendermode} + /> </div> <div className={classNames(this.makeAttrClassNames({"versionNotes": 1, "sans-serif": (this.props.rendermode == "book-page")}, "versionNotes", true))}> <span className="" dangerouslySetInnerHTML={ {__html: vnotes} } /> From 6027f64d14ccbbb0e697e6eb7019ec0944070307 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Sun, 12 Nov 2023 09:48:09 +0200 Subject: [PATCH 449/756] refactor(VersionBlock): change string to text. --- static/js/OpenVersion.jsx | 6 +++--- static/js/VersionBlock.jsx | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/static/js/OpenVersion.jsx b/static/js/OpenVersion.jsx index 87ae522aed..a716a2d6d8 100644 --- a/static/js/OpenVersion.jsx +++ b/static/js/OpenVersion.jsx @@ -2,7 +2,7 @@ import React from 'react'; import Sefaria from "./sefaria/sefaria"; import PropTypes from "prop-types"; -function OpenVersion({currRef, version, currObjectVersions, targetPanel, string, className, openVersionInSidebar, openVersionInReader, rendermode, firstSectionRef}) { +function OpenVersion({currRef, version, currObjectVersions, targetPanel, text, className, openVersionInSidebar, openVersionInReader, rendermode, firstSectionRef}) { const mainPanel = targetPanel === 'main'; function makeVersionLink() { @@ -55,7 +55,7 @@ function OpenVersion({currRef, version, currObjectVersions, targetPanel, string, <a className={className} href={makeVersionLink()} onClick={mainPanel ? openInMainPanel : openInSidebar}> - {string} + {text} </a> ); } @@ -67,7 +67,7 @@ OpenVersion.prototypes = { openVersionInSidebar: PropTypes.func, openVersionInReader: PropTypes.func.isRequired, targetPanel: PropTypes.string.isRequired, - string: PropTypes.string, + text: PropTypes.string, rendermode: PropTypes.string.isRequired, firstSectionRef: PropTypes.string, } diff --git a/static/js/VersionBlock.jsx b/static/js/VersionBlock.jsx index 4454db3d5f..573b555282 100644 --- a/static/js/VersionBlock.jsx +++ b/static/js/VersionBlock.jsx @@ -241,7 +241,7 @@ class VersionBlock extends Component { openVersionInSidebar={this.props.openVersionInSidebar} openVersionInReader={this.props.openVersionInReader} targetPanel={this.props.rendermode === 'book-page' ? 'main' : 'side'} - string={vtitle["text"]} + text={vtitle["text"]} rendermode={this.props.rendermode} firstSectionRef={this.props.firstSectionRef} /> From c6a3343e07ac6cc1c910d38adf0b34d38f0a27ff Mon Sep 17 00:00:00 2001 From: yonadavGit <yonadav.le@gmail.com> Date: Sun, 12 Nov 2023 14:27:52 +0200 Subject: [PATCH 450/756] chore(DisplaySettingsButton): changed lang icon to be svg not font --- static/css/s2.css | 7 ++----- static/img/lang_icon_english.svg | 3 +++ static/img/lang_icon_hebrew.svg | 3 +++ static/js/Misc.jsx | 4 ++-- 4 files changed, 10 insertions(+), 7 deletions(-) create mode 100644 static/img/lang_icon_english.svg create mode 100644 static/img/lang_icon_hebrew.svg diff --git a/static/css/s2.css b/static/css/s2.css index 3af0a4047b..62251e0a49 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -4716,13 +4716,10 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus cursor: pointer; } .readerOptions .int-en { - font-size: 25px; - line-height: 63px; + margin-right: 4px; } .readerOptions .int-he { - font-size: 32px; - line-height: 65px; - margin-left: 6px; + margin-left: 8px; } .rightButtons .readerOptions { vertical-align: middle; diff --git a/static/img/lang_icon_english.svg b/static/img/lang_icon_english.svg new file mode 100644 index 0000000000..c30df8b811 --- /dev/null +++ b/static/img/lang_icon_english.svg @@ -0,0 +1,3 @@ +<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M11.6162 13.6672C11.8047 14.2327 11.9225 14.6568 11.9225 14.9161C11.9225 15.1517 11.5926 15.2931 11.0978 15.3402L10.4144 15.4109C10.2495 15.5287 10.273 15.9057 10.4615 16C11.1449 15.9764 12.0403 15.9293 13.1714 15.9293C14.2318 15.9293 15.0565 15.9529 16.0227 16C16.1405 15.9057 16.1876 15.5287 16.0227 15.4109L15.3864 15.3402C14.3732 15.2224 14.0668 14.8689 13.3835 12.8424L10.1552 3.36966C9.80175 2.33284 9.42472 1.2489 9.11839 0.212077C9.07126 0.0471289 8.90631 0 8.76493 0C8.50572 0.306333 8.10513 0.730486 7.5396 0.895436C7.61029 1.27246 7.4689 1.72018 7.13901 2.56848L4.73547 8.93078C3.74578 11.57 3.08599 13.1016 2.66183 14.1856C2.33194 15.0339 1.81353 15.2931 1.24799 15.3402L0.470377 15.4109C0.328993 15.5287 0.376121 15.9057 0.517505 16C1.15373 15.9764 2.00204 15.9293 2.77966 15.9293C3.88717 15.9529 4.61765 15.9529 5.23032 16C5.44239 15.9057 5.44239 15.5287 5.27745 15.4109L4.49983 15.3402C3.93429 15.2931 3.81647 15.1517 3.81647 14.8689C3.81647 14.6097 3.98142 13.9735 4.33488 12.9131L5.15963 10.4389C5.32457 9.94404 5.46596 9.87334 6.05506 9.87334H9.61323C10.3202 9.87334 10.4144 9.94404 10.5794 10.4624L11.6162 13.6672ZM7.39821 4.2651C7.65742 3.55817 7.89306 2.99264 7.96375 2.87482H8.01088C8.15226 3.15758 8.36434 3.79381 8.55285 4.35935L9.82531 8.2003C10.0374 8.83652 9.99026 8.93078 9.3069 8.93078H6.40852C5.74873 8.93078 5.72516 8.90722 5.91368 8.36524L7.39821 4.2651Z" fill="#666666"/> +</svg> diff --git a/static/img/lang_icon_hebrew.svg b/static/img/lang_icon_hebrew.svg new file mode 100644 index 0000000000..a5a410036d --- /dev/null +++ b/static/img/lang_icon_hebrew.svg @@ -0,0 +1,3 @@ +<svg width="12" height="16" viewBox="0 0 12 16" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M9.74648 1.35037C10.712 1.70345 11.1963 2.33527 11.1963 3.25203C11.156 4.10664 10.8341 4.92262 10.2825 5.56872H10.0511V5.05459C10.062 4.92522 10.031 4.79578 9.96273 4.68602C9.90486 4.6086 9.77085 4.56833 9.56069 4.56833C9.1681 5.16364 8.83545 5.79763 8.56777 6.4607C8.38488 6.93678 8.2879 7.44248 8.28147 7.95354C8.28424 8.66034 8.5422 9.34149 9.00636 9.8676L10.2673 11.3511C10.9243 12.0361 11.3047 12.9468 11.3333 13.9032C11.3301 14.304 11.2409 14.6992 11.072 15.0613C10.9032 15.4235 10.6587 15.7438 10.3556 16H9.99015C9.9826 15.3339 9.72932 14.695 9.28048 14.2098L3.36863 7.30314C2.35337 7.8214 1.84574 8.48729 1.84574 9.30081C1.85162 9.63687 1.94065 9.96601 2.10463 10.2578C2.28154 10.5844 2.49516 10.8889 2.7412 11.1653L3.49351 12.0108C3.74034 12.2938 3.95303 12.6057 4.12703 12.94C4.29117 13.2374 4.38013 13.5715 4.38592 13.9125C4.38101 14.3735 4.22204 14.8191 3.93514 15.1762C3.83256 15.3419 3.69368 15.4812 3.52936 15.5833C3.36504 15.6854 3.17972 15.7474 2.98791 15.7646H0.368539V15.3744C0.913733 15.2288 1.18481 14.9098 1.18481 14.4173C1.18481 14.1696 0.989877 13.5656 0.593926 12.6086C0.197975 11.6516 0 10.9547 0 10.5304C0 9.08504 0.950282 7.81417 2.85085 6.71777L1.18785 4.78823C0.546117 4.10172 0.176262 3.19842 0.149243 2.25165C0.161747 1.40736 0.494576 0.600638 1.0782 0L1.42847 0C1.41566 0.714047 1.66673 1.407 2.13204 1.94193L7.26722 7.86372C7.49975 6.64711 7.96659 5.48927 8.64087 4.45683C7.28651 4.24003 6.60933 3.39244 6.60933 1.91405C6.5943 1.23208 6.79034 0.562546 7.16976 0L7.52307 0C7.57899 0.261827 7.70779 0.501774 7.8941 0.69123C8.08041 0.880686 8.31638 1.01166 8.57386 1.06853L9.74648 1.35037Z" fill="#666666"/> +</svg> diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 8953fe16b4..5b5f87a287 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -1348,8 +1348,8 @@ class DisplaySettingsButton extends Component { if (Sefaria._siteSettings.TORAH_SPECIFIC) { icon = <InterfaceText> - <EnglishText>A</EnglishText> - <HebrewText>א</HebrewText> + <EnglishText> <img src="/static/img/lang_icon_english.svg" alt="Toggle Reader Menu Display Settings"/></EnglishText> + <HebrewText><img src="/static/img/lang_icon_hebrew.svg" alt="Toggle Reader Menu Display Settings"/></HebrewText> </InterfaceText>; } else { icon = <span className="textIcon">Aa</span>; From dd79d4a4c5931a1ae880cf614a6edc5a130cf40b Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Sun, 12 Nov 2023 14:53:57 +0200 Subject: [PATCH 451/756] chore: added 'picture' to AdminEditor --- static/js/AdminEditor.jsx | 19 ++++++++++++++++++- static/js/Misc.jsx | 15 +++++++++------ static/js/TopicEditor.jsx | 7 ++++--- 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/static/js/AdminEditor.jsx b/static/js/AdminEditor.jsx index 7061b4b7bf..68d0c62903 100644 --- a/static/js/AdminEditor.jsx +++ b/static/js/AdminEditor.jsx @@ -1,9 +1,10 @@ import React, {useRef, useState} from "react"; import Sefaria from "./sefaria/sefaria"; -import {AdminToolHeader, InterfaceText, TitleVariants} from "./Misc"; +import {AdminToolHeader, InterfaceText, ProfilePic, TitleVariants} from "./Misc"; import sanitizeHtml from 'sanitize-html'; import classNames from "classnames"; const options_for_form = { + "Picture": {label: "Picture", field: "picture", placeholder: "Add a picture.", type: "picture"}, "Title": {label: "Title", field: "enTitle", placeholder: "Add a title."}, "Hebrew Title": {label: "Hebrew Title", field: "heTitle", placeholder: "Add a title."}, "English Description": { @@ -125,6 +126,10 @@ const AdminEditor = ({title, data, close, catMenu, updateData, savingStatus, } updateData({...data}); } + const handlePictureChange = (url) => { + data["picture"] = url; + updateData({...data}); + } const handleTitleVariants = (newTitles, field) => { const newData = {...data}; newData[field] = newTitles.map(x => Object.assign({}, x)); @@ -158,9 +163,21 @@ const AdminEditor = ({title, data, close, catMenu, updateData, savingStatus, </select> </div>; } + const getPicture = (label, field) => { + return <ProfilePic + saveCallback={(url) => handlePictureChange(url)} + showButtons={true} + name={""} + url={data[field]} + len={60} + />; + } const item = ({label, field, placeholder, type, dropdown_data}) => { let obj; switch(type) { + case 'picture': + obj = getPicture(label, field); + break; case 'dropdown': obj = getDropdown(field, dropdown_data, placeholder); break; diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 0b1a96cc3d..7d3aacae68 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -262,10 +262,11 @@ class ProfilePic extends Component { if (response.error) { throw new Error(response.error); } else { - this.closePopup({ cb: () => { + const defaultCallback = () => { window.location = "/profile/" + Sefaria.slug; // reload to get update return; - }}); + }; + this.closePopup({ cb: this.props.saveCallback ? this.props.saveCallback(response.urls[0]) : defaultCallback}); } } catch (e) { errored = true; @@ -352,11 +353,12 @@ class ProfilePic extends Component { } } ProfilePic.propTypes = { - url: PropTypes.string, - name: PropTypes.string, - len: PropTypes.number, + url: PropTypes.string, + saveCallback: PropTypes.func, // used by AdminEditor to override default callback upon save + name: PropTypes.string, + len: PropTypes.number, hideOnDefault: PropTypes.bool, // hide profile pic if you have are displaying default pic - showButtons: PropTypes.bool, // show profile pic action buttons + showButtons: PropTypes.bool, // show profile pic action buttons }; @@ -1234,6 +1236,7 @@ const EditorForExistingTopic = ({ toggle, data }) => { origDeathPlace: data?.properties?.deathPlace?.value, origDeathYear: data?.properties?.deathYear?.value, origEra: data?.properties?.era?.value + }; const origWasCat = "displays-above" in data?.links; diff --git a/static/js/TopicEditor.jsx b/static/js/TopicEditor.jsx index 3d7dd86f74..229ba62da2 100644 --- a/static/js/TopicEditor.jsx +++ b/static/js/TopicEditor.jsx @@ -1,5 +1,5 @@ import Sefaria from "./sefaria/sefaria"; -import {InterfaceText, requestWithCallBack, TitleVariants, ToggleSet} from "./Misc"; +import {InterfaceText, requestWithCallBack, ProfilePic} from "./Misc"; import $ from "./sefaria/sefariaJquery"; import {AdminEditor} from "./AdminEditor"; import {Reorder} from "./CategoryEditor"; @@ -18,7 +18,8 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { birthPlace: origData.origBirthPlace || "", heBirthPlace: origData.origHeBirthPlace || "", birthYear: origData.origBirthYear || "", heDeathPlace: origData.origHeDeathPlace || "", deathYear: origData.origDeathYear || "", era: origData.origEra || "", - deathPlace: origData.origDeathPlace || "" + deathPlace: origData.origDeathPlace || "", + picture: origData?.origPicture || "" }); const isNew = !('origSlug' in origData); const [savingStatus, setSavingStatus] = useState(false); @@ -159,7 +160,7 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { const url = `/api/topic/delete/${data.origSlug}`; requestWithCallBack({url, type: "DELETE", redirect: () => window.location.href = "/topics"}); } - let items = ["Title", "Hebrew Title", "English Description", "Hebrew Description", "Category Menu"]; + let items = ["Title", "Hebrew Title", "English Description", "Hebrew Description", "Category Menu", "Picture"]; if (isCategory) { items.push("English Short Description"); items.push("Hebrew Short Description"); From 0dea316340745f97e7bac04a0ebe7e908773ea3e Mon Sep 17 00:00:00 2001 From: Lev Israel <eliezer.israel@gmail.com> Date: Sun, 12 Nov 2023 16:28:45 +0200 Subject: [PATCH 452/756] fix: replace repairGershayimVariant --- static/js/sefaria/sefaria.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index f91265a197..6941c749b5 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -2016,6 +2016,20 @@ _media: {}, }); return bestMatch + query.slice(bestMatch.length); }, + repairGershayimVariant: function(query, data) { + if (!data["is_ref"] && data.completions && !data.completions.includes(query)) { + function normalize_gershayim(string) { + return string.replace('״', '"'); + } + const normalized_query = normalize_gershayim(query); + for (let c of data.completions) { + if (normalize_gershayim(c) === normalized_query) { + return c; + } + } + } + return query; + }, makeSegments: function(data, withContext, sheets=false) { // Returns a flat list of annotated segment objects, // derived from the walking the text in data From 622e30e9914b363dd48627298ed6947450f91914 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Sun, 12 Nov 2023 19:11:53 +0200 Subject: [PATCH 453/756] fix(VersionBlock): remove isRequired from openVersionInReader. --- static/js/OpenVersion.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/OpenVersion.jsx b/static/js/OpenVersion.jsx index a716a2d6d8..d31c061493 100644 --- a/static/js/OpenVersion.jsx +++ b/static/js/OpenVersion.jsx @@ -65,7 +65,7 @@ OpenVersion.prototypes = { currRef: PropTypes.string.isRequired, className: PropTypes.string, openVersionInSidebar: PropTypes.func, - openVersionInReader: PropTypes.func.isRequired, + openVersionInReader: PropTypes.func, targetPanel: PropTypes.string.isRequired, text: PropTypes.string, rendermode: PropTypes.string.isRequired, From f77def9b6dcaebd677d4bdef42e0d37f1d643963 Mon Sep 17 00:00:00 2001 From: Brendan Galloway <brendan@flanksource.com> Date: Mon, 13 Nov 2023 12:39:00 +0200 Subject: [PATCH 454/756] helm(fix): cleanup mongo-destroy configmap --- .../sefaria-project/templates/configmap/mongo-destroy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helm-chart/sefaria-project/templates/configmap/mongo-destroy.yaml b/helm-chart/sefaria-project/templates/configmap/mongo-destroy.yaml index a6c0b51e4d..3108cb1af0 100644 --- a/helm-chart/sefaria-project/templates/configmap/mongo-destroy.yaml +++ b/helm-chart/sefaria-project/templates/configmap/mongo-destroy.yaml @@ -8,7 +8,7 @@ metadata: {{- include "sefaria.labels" . | nindent 4 }} annotations: helm.sh/hook: post-delete - helm.sh/hook-delete-policy: hook-succeeded + helm.sh/hook-delete-policy: hook-succeeded, hook-failed helm.sh/hook-weight: "5" data: destroy-mongo.sh: |- From c6743bbf26a330026ff55111e8b5a727caf3b674 Mon Sep 17 00:00:00 2001 From: Brendan Galloway <brendan@flanksource.com> Date: Mon, 13 Nov 2023 12:40:48 +0200 Subject: [PATCH 455/756] ci: capture chart-only update --- .github/workflows/production-deploy.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/production-deploy.yaml b/.github/workflows/production-deploy.yaml index 11d612b19c..0966286206 100644 --- a/.github/workflows/production-deploy.yaml +++ b/.github/workflows/production-deploy.yaml @@ -220,7 +220,6 @@ jobs: IMAGE_NAME: "${{ secrets.IMAGE_NAME }}" CHART_VERSION: "${{ steps.chart_version.outputs.chart_version }}" - name: Update workflow default chart - if: github.ref == 'refs/heads/master' run: > curl -L -X PATCH From 83aff63b794fe7a57472c34157dbe558b578ad3f Mon Sep 17 00:00:00 2001 From: yonadavGit <yonadav.le@gmail.com> Date: Mon, 13 Nov 2023 16:51:27 +0200 Subject: [PATCH 456/756] chore(Reader Controls): changed color of bookmark icon in svg, enlarged Hebrew Aleph icon --- static/css/s2.css | 1 - static/icons/bookmark-filled.svg | 2 +- static/icons/bookmark.svg | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index 62251e0a49..0d01e8eb44 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -4665,7 +4665,6 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus height: 18px; width: 18px; margin-top: 3px; - filter: grayscale(100%) brightness(80%) sepia(20%); } .rightButtons .saveButton.tooltip-toggle::before { top: 47px; diff --git a/static/icons/bookmark-filled.svg b/static/icons/bookmark-filled.svg index b3c5cabfef..bb38f47693 100644 --- a/static/icons/bookmark-filled.svg +++ b/static/icons/bookmark-filled.svg @@ -1,3 +1,3 @@ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path d="M19 0H5V24L12 19.3896L19 24V0Z" fill="#999999"/> +<path d="M19 0H5V24L12 19.3896L19 24V0Z" fill="#666666"/> </svg> diff --git a/static/icons/bookmark.svg b/static/icons/bookmark.svg index 3638245685..84eaf9cf14 100644 --- a/static/icons/bookmark.svg +++ b/static/icons/bookmark.svg @@ -1,3 +1,3 @@ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path d="M17.0556 1.75V20.1343L12 17.0612L6.94444 20.1343V1.75H17.0556ZM19 0H5V24L12 19.3896L19 24V0Z" fill="#999999"/> +<path d="M17.0556 1.75V20.1343L12 17.0612L6.94444 20.1343V1.75H17.0556ZM19 0H5V24L12 19.3896L19 24V0Z" fill="#666666"/> </svg> From e25aeb5712d6c2ee23c0ddef163c86438785571d Mon Sep 17 00:00:00 2001 From: Lev Israel <eliezer.israel@gmail.com> Date: Mon, 13 Nov 2023 17:50:46 +0200 Subject: [PATCH 457/756] fix: Less sidebar --- static/js/TopicPage.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/TopicPage.jsx b/static/js/TopicPage.jsx index c5a86fef94..a9b3eed59b 100644 --- a/static/js/TopicPage.jsx +++ b/static/js/TopicPage.jsx @@ -539,7 +539,7 @@ const TopicPage = ({ if (topicData.portal_slug) { Sefaria.getPortal(topicData.portal_slug).then(setPortal); if (portal) { - sidebar = <PortalNavSideBar portal={portal} entriesToDisplayList={["about", "mobile", "organization", "newsletter"]}/> + sidebar = <PortalNavSideBar portal={portal} entriesToDisplayList={["about"]}/> // "mobile", "organization", "newsletter"]}/> } } else { sidebar = ( From 72de0feb1a5ea98af9fde03f806decc9d224e30b Mon Sep 17 00:00:00 2001 From: yonadavGit <yonadav.le@gmail.com> Date: Mon, 13 Nov 2023 18:02:21 +0200 Subject: [PATCH 458/756] chore(Reader Controls): enlarge Hebrew Aleph only --- static/css/s2.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/static/css/s2.css b/static/css/s2.css index 0d01e8eb44..9404feac7f 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -4720,6 +4720,9 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus .readerOptions .int-he { margin-left: 8px; } +.readerOptions .int-he img { + height: 18px; +} .rightButtons .readerOptions { vertical-align: middle; } From a5ef1ebc5b2f39e9336e42cfe565947b6a2fd821 Mon Sep 17 00:00:00 2001 From: Brendan Galloway <brendan@flanksource.com> Date: Tue, 14 Nov 2023 13:23:12 +0200 Subject: [PATCH 459/756] ci: remove filters on workflow triggers --- .github/workflows/continuous.yaml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/continuous.yaml b/.github/workflows/continuous.yaml index 3fa4611d49..eeebf4fdc4 100644 --- a/.github/workflows/continuous.yaml +++ b/.github/workflows/continuous.yaml @@ -1,11 +1,7 @@ name: Continuous on: push: - branches: - - "*" pull_request: - branches: - - "*" concurrency: group: ${{ github.ref }} From bf28e49574d160128c8cdc8c0a057d95b45eff79 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Wed, 15 Nov 2023 09:15:17 +0200 Subject: [PATCH 460/756] test(api): tests for one base, source and translation. --- api/tests.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/api/tests.py b/api/tests.py index df63d460ad..684e782146 100644 --- a/api/tests.py +++ b/api/tests.py @@ -33,6 +33,13 @@ def test_api_get_text_source_all(self): self.assertEqual(data["sections"], ["22a"]) self.assertEqual(data["toSections"], ["22a"]) + def test_api_get_text_source(self): + response = c.get('/api/v3/texts/Shabbat.22a?version=source') + self.assertEqual(200, response.status_code) + data = json.loads(response.content) + self.assertEqual(len(data["versions"]), 1) + self.assertEqual(data["versions"][0]['versionTitle'], "William Davidson Edition - Vocalized Aramaic") + def test_api_get_text_translation_all(self): response = c.get('/api/v3/texts/Shabbat.22a?version=translation|all') self.assertEqual(200, response.status_code) @@ -44,6 +51,13 @@ def test_api_get_text_translation_all(self): self.assertEqual(data["sections"], ["22a"]) self.assertEqual(data["toSections"], ["22a"]) + def test_api_get_text_translation(self): + response = c.get('/api/v3/texts/Shabbat.22a?version=translation') + self.assertEqual(200, response.status_code) + data = json.loads(response.content) + self.assertEqual(len(data["versions"]), 1) + self.assertEqual(data["versions"][0]['versionTitle'], "William Davidson Edition - English") + def test_api_get_text_lang_all(self): response = c.get('/api/v3/texts/Rashi_on_Genesis.2.3?version=en|all') self.assertEqual(200, response.status_code) @@ -75,6 +89,13 @@ def test_api_get_text_base_all(self): self.assertTrue(len(data["versions"]) > 3) self.assertTrue(all(v['actualLanguage'] == 'he' for v in data["versions"])) + def test_api_get_text_base(self): + response = c.get('/api/v3/texts/Shabbat.22a?version=base') + self.assertEqual(200, response.status_code) + data = json.loads(response.content) + self.assertEqual(len(data["versions"]), 1) + self.assertEqual(data["versions"][0]['versionTitle'], "William Davidson Edition - Vocalized Aramaic") + def test_api_get_text_two_params(self): response = c.get('/api/v3/texts/Genesis.1?version=he|Tanach with Nikkud&version=en|all') data = json.loads(response.content) From 7b4bd2e7fba9d0de43f024b64506e4dd4cf4db77 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Wed, 15 Nov 2023 09:18:03 +0200 Subject: [PATCH 461/756] doc(api): correct doc string for direction attribute of Version. --- sefaria/model/text.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index 02f4ce26d3..d3e589f0b0 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -1338,7 +1338,7 @@ class Version(AbstractTextRecord, abst.AbstractMongoRecord, AbstractSchemaConten "isBaseText", 'isSource', 'isPrimary', - 'direction', # 1 for rtl, 2 for ltr + 'direction', # 'rtl' or 'ltr' ] def __str__(self): From 1801a0dc82c1f7f05042f69dc5835288f85d5047 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Wed, 15 Nov 2023 09:22:35 +0200 Subject: [PATCH 462/756] feat(api): add direction attr to version when saved. --- sefaria/model/text.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index d3e589f0b0..f065d30756 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -1400,6 +1400,9 @@ def _normalize(self): else: self.actualLanguage = self.language + if not getattr(self, 'direction', None): + self.direction = 'rtl' if self.language == 'he' else 'ltr' + if getattr(self, "priority", None): try: self.priority = float(self.priority) From 7bb7cfb10c9b2350bc5a857d3505eea40bd7b773 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Wed, 15 Nov 2023 10:59:37 +0200 Subject: [PATCH 463/756] fix(Search): josephus doesnt freeze search --- static/js/Header.jsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/static/js/Header.jsx b/static/js/Header.jsx index 5f77a7fc69..d48d90a468 100644 --- a/static/js/Header.jsx +++ b/static/js/Header.jsx @@ -307,8 +307,11 @@ class SearchBar extends Component { .then(d => { // If the query isn't recognized as a ref, but only for reasons of capitalization. Resubmit with recognizable caps. if (Sefaria.isACaseVariant(query, d)) { - this.submitSearch(Sefaria.repairCaseVariant(query, d)); - return; + const repairedQuery = Sefaria.repairCaseVariant(query, d); + if (repairedQuery !== query) { + this.submitSearch(repairedQuery); + return; + } } const repairedQuery = Sefaria.repairGershayimVariant(query, d); if (repairedQuery !== query) { @@ -329,7 +332,8 @@ class SearchBar extends Component { this.props.openTopic(d["topic_slug"]); this.props.onNavigate && this.props.onNavigate(); - } else if (d["type"] === "Person" || d["type"] === "Collection" || d["type"] === "TocCategory") { + } else if (d["type"] === "Person" || d["type"] === "Topic" || d["type"] === "AuthorTopic" + || d["type"] === "Collection" || d["type"] === "TocCategory") { this.redirectToObject(d["type"], d["key"]); } else { From c662738a3d2b362a7fc3cef292fd9120fa29cbf5 Mon Sep 17 00:00:00 2001 From: yonadavGit <yonadav.le@gmail.com> Date: Wed, 15 Nov 2023 11:15:46 +0200 Subject: [PATCH 464/756] chore(Reader Controls): changed verse numbers to #000 --- static/css/s2.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/css/s2.css b/static/css/s2.css index 9404feac7f..d0b3e7d367 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -5431,7 +5431,7 @@ But not to use a display block directive that might break continuous mode for ot } .segment .segmentNumber, .textRagnge .numberLabel { - color: #666; + color: #000; top: 0; } .dark .segment .segmentNumber, From 183c7cc556d619ebdfab57937658ce62169e743e Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Wed, 15 Nov 2023 14:58:14 +0200 Subject: [PATCH 465/756] fix(Search): repairCaseVariant for non-refs as well as refs --- static/js/Header.jsx | 4 +--- static/js/sefaria/sefaria.js | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/static/js/Header.jsx b/static/js/Header.jsx index d48d90a468..003f10b654 100644 --- a/static/js/Header.jsx +++ b/static/js/Header.jsx @@ -332,10 +332,8 @@ class SearchBar extends Component { this.props.openTopic(d["topic_slug"]); this.props.onNavigate && this.props.onNavigate(); - } else if (d["type"] === "Person" || d["type"] === "Topic" || d["type"] === "AuthorTopic" - || d["type"] === "Collection" || d["type"] === "TocCategory") { + } else if (d["type"] === "Person" || d["type"] === "Collection" || d["type"] === "TocCategory") { this.redirectToObject(d["type"], d["key"]); - } else { Sefaria.track.event("Search", "Search Box Search", query); this.closeSearchAutocomplete(); diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index 6941c749b5..db8f4eef29 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -2002,7 +2002,7 @@ _media: {}, }, repairCaseVariant: function(query, data) { // Used when isACaseVariant() is true to prepare the alternative - const completionArray = data["completion_objects"].filter(x => x.type === 'ref').map(x => x.title); + const completionArray = data["completion_objects"].map(x => x.title); let normalizedQuery = query.toLowerCase(); let bestMatch = ""; let bestMatchLength = 0; From 9fecaa4e37059f8ce991c102e7238c2574c6bb05 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Thu, 16 Nov 2023 09:07:12 +0200 Subject: [PATCH 466/756] chore: repairCaseVariant calls isACaseVariant --- static/js/Header.jsx | 10 ++++------ static/js/sefaria/sefaria.js | 30 ++++++++++++++++-------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/static/js/Header.jsx b/static/js/Header.jsx index 003f10b654..7a16a8e3d1 100644 --- a/static/js/Header.jsx +++ b/static/js/Header.jsx @@ -306,12 +306,10 @@ class SearchBar extends Component { Sefaria.getName(query) .then(d => { // If the query isn't recognized as a ref, but only for reasons of capitalization. Resubmit with recognizable caps. - if (Sefaria.isACaseVariant(query, d)) { - const repairedQuery = Sefaria.repairCaseVariant(query, d); - if (repairedQuery !== query) { - this.submitSearch(repairedQuery); - return; - } + const repairedCaseVariant = Sefaria.repairCaseVariant(query, d); + if (repairedCaseVariant !== query) { + this.submitSearch(repairedCaseVariant); + return; } const repairedQuery = Sefaria.repairGershayimVariant(query, d); if (repairedQuery !== query) { diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index db8f4eef29..3da5849570 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -2001,20 +2001,22 @@ _media: {}, data["completions"][0] != query.slice(0, data["completions"][0].length)) }, repairCaseVariant: function(query, data) { - // Used when isACaseVariant() is true to prepare the alternative - const completionArray = data["completion_objects"].map(x => x.title); - let normalizedQuery = query.toLowerCase(); - let bestMatch = ""; - let bestMatchLength = 0; - - completionArray.forEach((completion) => { - let normalizedCompletion = completion.toLowerCase(); - if (normalizedQuery.includes(normalizedCompletion) && normalizedCompletion.length > bestMatchLength) { - bestMatch = completion; - bestMatchLength = completion.length; - } - }); - return bestMatch + query.slice(bestMatch.length); + if (Sefaria.isACaseVariant(query, data)) { + const completionArray = data["completion_objects"].map(x => x.title); + let normalizedQuery = query.toLowerCase(); + let bestMatch = ""; + let bestMatchLength = 0; + + completionArray.forEach((completion) => { + let normalizedCompletion = completion.toLowerCase(); + if (normalizedQuery.includes(normalizedCompletion) && normalizedCompletion.length > bestMatchLength) { + bestMatch = completion; + bestMatchLength = completion.length; + } + }); + return bestMatch + query.slice(bestMatch.length); + } + return query; }, repairGershayimVariant: function(query, data) { if (!data["is_ref"] && data.completions && !data.completions.includes(query)) { From 950eeafdcbf1600d6d9c33247b75c6aa209a7b73 Mon Sep 17 00:00:00 2001 From: yonadavGit <yonadav.le@gmail.com> Date: Thu, 16 Nov 2023 16:10:07 +0200 Subject: [PATCH 467/756] chore(SearchAutocomplete): Ensure magnifying glass appears above search results --- static/js/Header.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/Header.jsx b/static/js/Header.jsx index 5f77a7fc69..de0ca4e2ee 100644 --- a/static/js/Header.jsx +++ b/static/js/Header.jsx @@ -234,7 +234,7 @@ class SearchBar extends Component { }); if (comps.length > 0) { const q = `${this._searchOverridePre}${request.term}${this._searchOverridePost}`; - response(comps.concat([{value: "SEARCH_OVERRIDE", label: q, type: "search"}])); + response([{value: "SEARCH_OVERRIDE", label: q, type: "search"}].concat(comps)); } else { response([]) } From f7936f89b55ad013ad810c6345ed7c40809f5915 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Thu, 16 Nov 2023 16:38:00 +0200 Subject: [PATCH 468/756] feat(api): add return_format param that can be default, text_only (for stripping notes and html), or wrap_all_entities (for adding html for ref links and topic links). --- api/views.py | 3 +- sefaria/model/text_manager.py | 61 ++++++++++++++++++++++++++++++++--- 2 files changed, 59 insertions(+), 5 deletions(-) diff --git a/api/views.py b/api/views.py index 241c95e290..ec2f67077f 100644 --- a/api/views.py +++ b/api/views.py @@ -47,7 +47,8 @@ def get(self, request, *args, **kwargs): versions_params = ['base'] versions_params = [self.split_piped_params(param_str) for param_str in versions_params] fill_in_missing_segments = request.GET.get('fill_in_missing_segments', False) - text_manager = TextManager(self.oref, versions_params, fill_in_missing_segments) + return_format = request.GET.get('return_format', 'default') + text_manager = TextManager(self.oref, versions_params, fill_in_missing_segments, return_format) data = text_manager.get_versions_for_query() data = self._handle_warnings(data) return jsonResponse(data) diff --git a/sefaria/model/text_manager.py b/sefaria/model/text_manager.py index 85640548aa..8a1b4fd004 100644 --- a/sefaria/model/text_manager.py +++ b/sefaria/model/text_manager.py @@ -1,11 +1,13 @@ import copy - +from collections import defaultdict +from functools import partial, reduce +from typing import List import django django.setup() from sefaria.model import * from sefaria.utils.hebrew import hebrew_term -from typing import List - +from sefaria.system.exceptions import InputError +from sefaria.datatype.jagged_array import JaggedTextArray class TextManager: ALL = 'all' @@ -13,10 +15,11 @@ class TextManager: SOURCE = 'source' TRANSLATION = 'translation' - def __init__(self, oref: Ref, versions_params: List[List[str]], fill_in_missing_segments=True): + def __init__(self, oref: Ref, versions_params: List[List[str]], fill_in_missing_segments=True, return_format='default'): self.versions_params = versions_params self.oref = oref self.fill_in_missing_segments = fill_in_missing_segments + self.return_format = return_format self.handled_version_params = [] self.all_versions = self.oref.versionset() @@ -133,10 +136,60 @@ def _add_node_data_to_return_obj(self) -> None: if not inode.is_virtual: self.return_obj['index_offsets_by_depth'] = inode.trim_index_offsets_by_sections(self.oref.sections, self.oref.toSections) + def _format_text(self): + def find_language(version): + return 'he' if version['direction'] == 'rtl' else 'en' # this is because we remove the language attr. do we want to do it later? + + def wrap_links(string, language): + link_wrapping_reg, title_nodes = library.get_regex_and_titles_for_ref_wrapping( + string, lang=language, citing_only=True) + return library.get_wrapped_refs_string(string, lang=language, citing_only=True, + reg=link_wrapping_reg, title_nodes=title_nodes) + + if self.return_format == 'wrap_all_entities': + all_segment_refs = self.oref.all_segment_refs() + query = self.oref.ref_regex_query() + query.update({"inline_citation": True}) + if Link().load(query): + text_modification_funcs = [lambda string, _: wrap_links(string, language)] + + elif self.return_format == 'text_only': + text_modification_funcs = [lambda string, _: text.AbstractTextRecord.strip_itags(string), + lambda string, _: text.AbstractTextRecord.remove_html(string), + lambda string, _: ' '.join(string.split())] + + else: + return + + def make_named_entities_dict(version, language): + named_entities = RefTopicLinkSet({"expandedRefs": {"$in": [r.normal() for r in all_segment_refs]}, + "charLevelData.versionTitle": version['versionTitle'], + "charLevelData.language": language}) + # assumption is that refTopicLinks are all to unranged refs + ne_by_secs = defaultdict(list) + for ne in named_entities: + try: + ne_ref = Ref(ne.ref) + except InputError: + continue + ne_by_secs[ne_ref.sections[-1]-1,] += [ne] + return ne_by_secs + + for version in self.return_obj['versions']: + if self.return_format == 'wrap_all_entities': + language = find_language(version) + ne_by_secs = make_named_entities_dict(version, language) + text_modification_funcs.append(lambda string, sections: library.get_wrapped_named_entities_string(ne_by_secs[(sections[-1],)], string)) + + ja = JaggedTextArray(version['text']) # JaggedTextArray works also with depth 0, i.e. a string + composite_func = lambda string, sections: reduce(lambda s, f: f(s, sections), text_modification_funcs, string) # wrap all functions into one function + version['text'] = ja.modify_by_function(composite_func) + def get_versions_for_query(self) -> dict: for lang, vtitle in self.versions_params: self._append_required_versions(lang, vtitle) self._add_ref_data_to_return_obj() self._add_index_data_to_return_obj() self._add_node_data_to_return_obj() + self._format_text() return self.return_obj From 12bb05c5596c8b5a9dc6e310d6acfe546ac541f6 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Thu, 16 Nov 2023 16:38:17 +0200 Subject: [PATCH 469/756] test(api): test return_format. --- api/tests.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/api/tests.py b/api/tests.py index 684e782146..78549980f8 100644 --- a/api/tests.py +++ b/api/tests.py @@ -196,3 +196,18 @@ def test_without_fill_in_missing_segments(self): data = json.loads(response.content) self.assertEqual(len(data['versions'][0]['text']), 2) self.assertFalse(data['versions'][0].get('sources')) + + def test_wrap_all_entities(self): + vtitle = "The Contemporary Torah, Jewish Publication Society, 2006" + response = c.get(f"/api/v3/texts/Genesis%2010?version=en|{vtitle}&return_format=wrap_all_entities") + self.assertEqual(200, response.status_code) + data = json.loads(response.content) + self.assertTrue('<a class ="refLink"' in data['versions'][0]['text'][3]) + self.assertTrue('<a href="/topics' in data['versions'][0]['text'][8]) + + def text_text_only(self): + response = c.get(f"/api/v3/texts/Shulchan_Arukh%2C_Orach_Chayim.1:1?return_format=text_only") + self.assertEqual(200, response.status_code) + data = json.loads(response.content) + self.assertFalse('<' in data['versions'][0]['text']) + From 07c6d97edfb645863b9760942216670c299f8130 Mon Sep 17 00:00:00 2001 From: yonadavGit <yonadav.le@gmail.com> Date: Thu, 16 Nov 2023 16:51:32 +0200 Subject: [PATCH 470/756] chore(SearchAutocomplete): adjusting magnifying glass border position to appear under it --- static/css/s2.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/css/s2.css b/static/css/s2.css index 510c84d512..a5665c4f28 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -713,7 +713,7 @@ div:has(#bannerMessage) + .readerApp.singlePanel .mobileNavMenu { color: #999; } .ui-autocomplete .ui-menu-item.search-override { - border-top: solid 1px #ccc; + border-bottom: solid 1px #ccc; padding-top: 12px; } .ui-autocomplete .ui-menu-item.hebrew-result a { From 1232196a9bca3221e513fc36174351a7463477c8 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Thu, 16 Nov 2023 17:02:36 +0200 Subject: [PATCH 471/756] refactor(api): remove unnecessary nonlocal params. --- sefaria/model/text_manager.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sefaria/model/text_manager.py b/sefaria/model/text_manager.py index 8a1b4fd004..b1726d2ab8 100644 --- a/sefaria/model/text_manager.py +++ b/sefaria/model/text_manager.py @@ -137,10 +137,10 @@ def _add_node_data_to_return_obj(self) -> None: self.return_obj['index_offsets_by_depth'] = inode.trim_index_offsets_by_sections(self.oref.sections, self.oref.toSections) def _format_text(self): - def find_language(version): + def find_language(): return 'he' if version['direction'] == 'rtl' else 'en' # this is because we remove the language attr. do we want to do it later? - def wrap_links(string, language): + def wrap_links(string): link_wrapping_reg, title_nodes = library.get_regex_and_titles_for_ref_wrapping( string, lang=language, citing_only=True) return library.get_wrapped_refs_string(string, lang=language, citing_only=True, @@ -151,7 +151,7 @@ def wrap_links(string, language): query = self.oref.ref_regex_query() query.update({"inline_citation": True}) if Link().load(query): - text_modification_funcs = [lambda string, _: wrap_links(string, language)] + text_modification_funcs = [lambda string, _: wrap_links(string)] elif self.return_format == 'text_only': text_modification_funcs = [lambda string, _: text.AbstractTextRecord.strip_itags(string), @@ -161,7 +161,7 @@ def wrap_links(string, language): else: return - def make_named_entities_dict(version, language): + def make_named_entities_dict(): named_entities = RefTopicLinkSet({"expandedRefs": {"$in": [r.normal() for r in all_segment_refs]}, "charLevelData.versionTitle": version['versionTitle'], "charLevelData.language": language}) @@ -177,8 +177,8 @@ def make_named_entities_dict(version, language): for version in self.return_obj['versions']: if self.return_format == 'wrap_all_entities': - language = find_language(version) - ne_by_secs = make_named_entities_dict(version, language) + language = find_language() + ne_by_secs = make_named_entities_dict() text_modification_funcs.append(lambda string, sections: library.get_wrapped_named_entities_string(ne_by_secs[(sections[-1],)], string)) ja = JaggedTextArray(version['text']) # JaggedTextArray works also with depth 0, i.e. a string From 1d51f60ee70ce9447fb818fedc65d6874ad2cb53 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Thu, 16 Nov 2023 17:04:53 +0200 Subject: [PATCH 472/756] refactor(api): better order of functions. --- sefaria/model/text_manager.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/sefaria/model/text_manager.py b/sefaria/model/text_manager.py index b1726d2ab8..624018f89f 100644 --- a/sefaria/model/text_manager.py +++ b/sefaria/model/text_manager.py @@ -146,6 +146,20 @@ def wrap_links(string): return library.get_wrapped_refs_string(string, lang=language, citing_only=True, reg=link_wrapping_reg, title_nodes=title_nodes) + def make_named_entities_dict(): + named_entities = RefTopicLinkSet({"expandedRefs": {"$in": [r.normal() for r in all_segment_refs]}, + "charLevelData.versionTitle": version['versionTitle'], + "charLevelData.language": language}) + # assumption is that refTopicLinks are all to unranged refs + ne_by_secs = defaultdict(list) + for ne in named_entities: + try: + ne_ref = Ref(ne.ref) + except InputError: + continue + ne_by_secs[ne_ref.sections[-1]-1,] += [ne] + return ne_by_secs + if self.return_format == 'wrap_all_entities': all_segment_refs = self.oref.all_segment_refs() query = self.oref.ref_regex_query() @@ -161,20 +175,6 @@ def wrap_links(string): else: return - def make_named_entities_dict(): - named_entities = RefTopicLinkSet({"expandedRefs": {"$in": [r.normal() for r in all_segment_refs]}, - "charLevelData.versionTitle": version['versionTitle'], - "charLevelData.language": language}) - # assumption is that refTopicLinks are all to unranged refs - ne_by_secs = defaultdict(list) - for ne in named_entities: - try: - ne_ref = Ref(ne.ref) - except InputError: - continue - ne_by_secs[ne_ref.sections[-1]-1,] += [ne] - return ne_by_secs - for version in self.return_obj['versions']: if self.return_format == 'wrap_all_entities': language = find_language() From f4b57c8e643bd6342a2a9761b4f9c1324cc78684 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Thu, 16 Nov 2023 17:08:52 +0200 Subject: [PATCH 473/756] fix(api): avoid accumulation of functions in iteration. --- sefaria/model/text_manager.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sefaria/model/text_manager.py b/sefaria/model/text_manager.py index 624018f89f..a675c71e11 100644 --- a/sefaria/model/text_manager.py +++ b/sefaria/model/text_manager.py @@ -164,8 +164,9 @@ def make_named_entities_dict(): all_segment_refs = self.oref.all_segment_refs() query = self.oref.ref_regex_query() query.update({"inline_citation": True}) + temp_modification_funcs = [] if Link().load(query): - text_modification_funcs = [lambda string, _: wrap_links(string)] + temp_modification_funcs.append(lambda string, _: wrap_links(string)) elif self.return_format == 'text_only': text_modification_funcs = [lambda string, _: text.AbstractTextRecord.strip_itags(string), @@ -179,7 +180,7 @@ def make_named_entities_dict(): if self.return_format == 'wrap_all_entities': language = find_language() ne_by_secs = make_named_entities_dict() - text_modification_funcs.append(lambda string, sections: library.get_wrapped_named_entities_string(ne_by_secs[(sections[-1],)], string)) + text_modification_funcs = temp_modification_funcs + [lambda string, sections: library.get_wrapped_named_entities_string(ne_by_secs[(sections[-1],)], string)] ja = JaggedTextArray(version['text']) # JaggedTextArray works also with depth 0, i.e. a string composite_func = lambda string, sections: reduce(lambda s, f: f(s, sections), text_modification_funcs, string) # wrap all functions into one function From 492f16ffcaca45749458c5febdddb256d9446979 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Thu, 16 Nov 2023 17:13:18 +0200 Subject: [PATCH 474/756] refactor(api): move creation of function before its unlocal params. --- sefaria/model/text_manager.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sefaria/model/text_manager.py b/sefaria/model/text_manager.py index a675c71e11..8c7f6f9a55 100644 --- a/sefaria/model/text_manager.py +++ b/sefaria/model/text_manager.py @@ -164,9 +164,9 @@ def make_named_entities_dict(): all_segment_refs = self.oref.all_segment_refs() query = self.oref.ref_regex_query() query.update({"inline_citation": True}) - temp_modification_funcs = [] + text_modification_funcs = [lambda string, sections: library.get_wrapped_named_entities_string(ne_by_secs[(sections[-1],)], string)] if Link().load(query): - temp_modification_funcs.append(lambda string, _: wrap_links(string)) + text_modification_funcs.append(lambda string, _: wrap_links(string)) elif self.return_format == 'text_only': text_modification_funcs = [lambda string, _: text.AbstractTextRecord.strip_itags(string), @@ -180,7 +180,6 @@ def make_named_entities_dict(): if self.return_format == 'wrap_all_entities': language = find_language() ne_by_secs = make_named_entities_dict() - text_modification_funcs = temp_modification_funcs + [lambda string, sections: library.get_wrapped_named_entities_string(ne_by_secs[(sections[-1],)], string)] ja = JaggedTextArray(version['text']) # JaggedTextArray works also with depth 0, i.e. a string composite_func = lambda string, sections: reduce(lambda s, f: f(s, sections), text_modification_funcs, string) # wrap all functions into one function From b061e8b2e6b10eea0e9eaaa2e74bcdbcec192709 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Thu, 16 Nov 2023 18:00:11 +0200 Subject: [PATCH 475/756] refactor(api): remove redundant import. --- sefaria/model/text_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sefaria/model/text_manager.py b/sefaria/model/text_manager.py index 8c7f6f9a55..3b84084398 100644 --- a/sefaria/model/text_manager.py +++ b/sefaria/model/text_manager.py @@ -1,6 +1,6 @@ import copy from collections import defaultdict -from functools import partial, reduce +from functools import reduce from typing import List import django django.setup() From 3ea41f49bc8e3e1962d2f67cc2f25632932ebf56 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Thu, 16 Nov 2023 23:11:05 +0200 Subject: [PATCH 476/756] refactor(api): calculate language without function. --- sefaria/model/text_manager.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/sefaria/model/text_manager.py b/sefaria/model/text_manager.py index 3b84084398..c4fb8c4f23 100644 --- a/sefaria/model/text_manager.py +++ b/sefaria/model/text_manager.py @@ -137,9 +137,6 @@ def _add_node_data_to_return_obj(self) -> None: self.return_obj['index_offsets_by_depth'] = inode.trim_index_offsets_by_sections(self.oref.sections, self.oref.toSections) def _format_text(self): - def find_language(): - return 'he' if version['direction'] == 'rtl' else 'en' # this is because we remove the language attr. do we want to do it later? - def wrap_links(string): link_wrapping_reg, title_nodes = library.get_regex_and_titles_for_ref_wrapping( string, lang=language, citing_only=True) @@ -178,7 +175,7 @@ def make_named_entities_dict(): for version in self.return_obj['versions']: if self.return_format == 'wrap_all_entities': - language = find_language() + language = 'he' if version['direction'] == 'rtl' else 'en' ne_by_secs = make_named_entities_dict() ja = JaggedTextArray(version['text']) # JaggedTextArray works also with depth 0, i.e. a string From 97a452f68121d1fd9232bf71160b4d6a23ff6817 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Fri, 17 Nov 2023 12:27:42 -0500 Subject: [PATCH 477/756] static(jobs): Remove Communication Specialist job. Set the page to have no jobs --- templates/static/jobs.html | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/templates/static/jobs.html b/templates/static/jobs.html index 859ee3fa9e..0b341814f7 100644 --- a/templates/static/jobs.html +++ b/templates/static/jobs.html @@ -16,7 +16,7 @@ <h1 class="serif"> </h1> <!-- Comment out when jobs page has no content --> - <h2> + <!-- <h2> <span class="int-en">About Sefaria</span> <span class="int-he">אודות ספריא</span> </h2> @@ -31,7 +31,7 @@ <h2> ספריא היא ארגון ללא מטרות רווח שמטרתו יצירת הדור הבא של לימוד התורה באופן פתוח ומשותף. אנחנו בספריא מרכיבים ספרייה חיה וחופשית של טקסטים יהודיים וכלל הקישורים ביניהם, בשפת המקור ובתרגומים. </span> - </p> + </p> --> <!-- Comment out when jobs page has no content --> </header> @@ -69,15 +69,6 @@ <h2 class="anchorable">HR and Operations</h2> <div class="job"><a class="" target="_blank" href=""></a></div> </section> </section> --> - - <section class="section department englishOnly"> - <header> - <h2 class="anchorable">Marketing and Communications</h2> - </header> - <section class="jobsListForDepartment"> - <div class="job"><a class="joblink" target="_blank" href="https://sefaria.breezy.hr/p/b11c89877ad6-communications-specialist?state=published">Communications Specialist</a></div> - </section> - </section> @@ -100,7 +91,7 @@ <h2 class="anchorable">Israel Team</h2> </header> </section>---> - <!-- + <div class="section nothing"> <p> <span class="int-en"> @@ -115,7 +106,7 @@ <h2 class="anchorable">Israel Team</h2> </span> </p> </div> - --> + <aside class="solicitationNotice"> <p> From be0de328a106e8c9cfb10f3b941f265c86de0c78 Mon Sep 17 00:00:00 2001 From: yonadavGit <yonadav.le@gmail.com> Date: Sun, 19 Nov 2023 12:27:02 +0200 Subject: [PATCH 478/756] refactor(ReaderControls): changed 'var' to 'let' --- static/js/Misc.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 5b5f87a287..24d541c8d7 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -1342,8 +1342,8 @@ class CloseButton extends Component { class DisplaySettingsButton extends Component { render() { - var style = this.props.placeholder ? {visibility: "hidden"} : {}; - var icon; + let style = this.props.placeholder ? {visibility: "hidden"} : {}; + let icon; if (Sefaria._siteSettings.TORAH_SPECIFIC) { icon = From 6efe4f18895c642d1cd9f220fcbddf11b06856b9 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Sun, 19 Nov 2023 15:32:03 -0500 Subject: [PATCH 479/756] Replace placeholders code with CSS to eliminate the need for calculating empty spots and to display correctly in all scenarios --- static/css/static.css | 3 ++- static/js/StaticPages.jsx | 20 -------------------- 2 files changed, 2 insertions(+), 21 deletions(-) diff --git a/static/css/static.css b/static/css/static.css index eec62cdfa3..26b59607ec 100644 --- a/static/css/static.css +++ b/static/css/static.css @@ -845,8 +845,9 @@ p.registration-links a:hover{ display: inline-block; margin-bottom: 20px; } -#teamPage .teamMember.placeholder, #teamPage .teamBoardMember.placeholder { +#teamPage .team-members::after, #teamPage .board-members::after { width: 200px; + content: ""; } #teamPage .teamMember { flex: 0 0 30%; diff --git a/static/js/StaticPages.jsx b/static/js/StaticPages.jsx index 2b603d67e9..b1fc96b5a2 100644 --- a/static/js/StaticPages.jsx +++ b/static/js/StaticPages.jsx @@ -2701,18 +2701,6 @@ const TeamMembers = ({ teamMembers }) => ( </> ); -const Placeholders = ({ teamMembersCount, cls }) => { - const placeholdersCount = - 3 - (teamMembersCount - 3 * Math.floor(teamMembersCount / 3)); - return ( - <> - {Array.from({ length: placeholdersCount }, (_, index) => ( - <div key={index} className={`${cls} placeholder`} /> - ))} - </> - ); -}; - const BoardMember = ({ boardMember }) => ( <div className="teamBoardMember"> <TeamMemberDetails teamMemberDetails={boardMember.teamMemberDetails} /> @@ -2874,10 +2862,6 @@ const TeamMembersPage = memo(() => { <TeamMembers teamMembers={ordinaryTeamMembers.sort(byLastName())} /> - <Placeholders - teamMembersCount={ordinaryTeamMembers.length} - cls="teamMember" - /> </section> <header> <h2> @@ -2887,10 +2871,6 @@ const TeamMembersPage = memo(() => { </header> <section className="main-text board-members"> <BoardMembers boardMembers={teamBoardMembers} /> - <Placeholders - teamMembersCount={teamBoardMembers.length} - cls="teamBoardMember" - /> </section> </> )} From fd6e824c826e507176c2873d25e48b76f11a8359 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Sun, 19 Nov 2023 16:07:42 -0500 Subject: [PATCH 480/756] Move components and functions related to TeamMembersPage inside the component --- static/js/StaticPages.jsx | 168 +++++++++++++++++++------------------- 1 file changed, 85 insertions(+), 83 deletions(-) diff --git a/static/js/StaticPages.jsx b/static/js/StaticPages.jsx index b1fc96b5a2..3d1a835b15 100644 --- a/static/js/StaticPages.jsx +++ b/static/js/StaticPages.jsx @@ -2640,14 +2640,7 @@ const ConditionalLink = ({ link, children }) => * Team Page */ -const byLastName = () => { - const locale = Sefaria.interfaceLang === "hebrew" ? "he" : "en"; - return (a, b) => { - const lastNameA = a.teamMemberDetails.teamName[locale].split(" ").pop(); - const lastNameB = b.teamMemberDetails.teamName[locale].split(" ").pop(); - return lastNameA.localeCompare(lastNameB, locale); - }; -}; + const partition = (arr, prop) => arr.reduce( @@ -2658,88 +2651,97 @@ const partition = (arr, prop) => [[], []] ); -const TeamTitle = ({ teamTitle }) => ( - <div className="teamTitle"> - <InterfaceText text={teamTitle} /> - </div> -); - -const TeamName = ({ teamName }) => ( - <div className="teamName"> - <InterfaceText text={teamName} /> - </div> -); - -const TeamMemberDetails = ({ teamMemberDetails }) => ( - <div className="teamMemberDetails"> - <TeamName teamName={teamMemberDetails.teamName} /> - <TeamTitle teamTitle={teamMemberDetails.teamTitle} /> - </div> -); - -const TeamMemberImage = ({ teamMember }) => ( - <div className="teamMemberImage"> - <img - src={teamMember.teamMemberImage} - alt={`Headshot of ${teamMember.teamMemberDetails.teamName.en}`} - /> - </div> -); - -const TeamMember = ({ teamMember }) => ( - <div className="teamMember"> - <TeamMemberImage teamMember={teamMember} /> - <TeamMemberDetails teamMemberDetails={teamMember.teamMemberDetails} /> - </div> -); - -const TeamMembers = ({ teamMembers }) => ( - <> - {teamMembers.map((teamMember) => ( - <TeamMember key={teamMember.id} teamMember={teamMember} /> - ))} - </> -); - -const BoardMember = ({ boardMember }) => ( - <div className="teamBoardMember"> - <TeamMemberDetails teamMemberDetails={boardMember.teamMemberDetails} /> - </div> -); - -const BoardMembers = ({ boardMembers }) => { - let chairmanBoardMember; - const chairmanIndex = boardMembers.findIndex( - (boardMember) => - boardMember.teamMemberDetails.teamTitle.en.toLowerCase() === - "chairman" +const TeamMembersPage = memo(() => { + const byLastName = () => { + const locale = Sefaria.interfaceLang === "hebrew" ? "he" : "en"; + return (a, b) => { + const lastNameA = a.teamMemberDetails.teamName[locale].split(" ").pop(); + const lastNameB = b.teamMemberDetails.teamName[locale].split(" ").pop(); + return lastNameA.localeCompare(lastNameB, locale); + }; + }; + + const TeamTitle = ({ teamTitle }) => ( + <div className="teamTitle"> + <InterfaceText text={teamTitle} /> + </div> ); - if (chairmanIndex !== -1) { - chairmanBoardMember = boardMembers.splice(chairmanIndex, 1); - } - const [cofounderBoardMembers, regularBoardMembers] = partition( - boardMembers, - (boardMember) => - boardMember.teamMemberDetails.teamTitle.en.toLowerCase() === - "co-founder" + + const TeamName = ({ teamName }) => ( + <div className="teamName"> + <InterfaceText text={teamName} /> + </div> ); - - return ( + + const TeamMemberDetails = ({ teamMemberDetails }) => ( + <div className="teamMemberDetails"> + <TeamName teamName={teamMemberDetails.teamName} /> + <TeamTitle teamTitle={teamMemberDetails.teamTitle} /> + </div> + ); + + const TeamMemberImage = ({ teamMember }) => ( + <div className="teamMemberImage"> + <img + src={teamMember.teamMemberImage} + alt={`Headshot of ${teamMember.teamMemberDetails.teamName.en}`} + /> + </div> + ); + + const TeamMember = ({ teamMember }) => ( + <div className="teamMember"> + <TeamMemberImage teamMember={teamMember} /> + <TeamMemberDetails teamMemberDetails={teamMember.teamMemberDetails} /> + </div> + ); + + const TeamMembers = ({ teamMembers }) => ( <> - {chairmanBoardMember && ( - <BoardMember boardMember={chairmanBoardMember[0]} /> - )} - {cofounderBoardMembers.map((boardMember) => ( - <BoardMember key={boardMember.id} boardMember={boardMember} /> - ))} - {regularBoardMembers.sort(byLastName()).map((boardMember) => ( - <BoardMember key={boardMember.id} boardMember={boardMember} /> + {teamMembers.map((teamMember) => ( + <TeamMember key={teamMember.id} teamMember={teamMember} /> ))} </> ); -}; + + const BoardMember = ({ boardMember }) => ( + <div className="teamBoardMember"> + <TeamMemberDetails teamMemberDetails={boardMember.teamMemberDetails} /> + </div> + ); + + const BoardMembers = ({ boardMembers }) => { + let chairmanBoardMember; + const chairmanIndex = boardMembers.findIndex( + (boardMember) => + boardMember.teamMemberDetails.teamTitle.en.toLowerCase() === + "chairman" + ); + if (chairmanIndex !== -1) { + chairmanBoardMember = boardMembers.splice(chairmanIndex, 1); + } + const [cofounderBoardMembers, regularBoardMembers] = partition( + boardMembers, + (boardMember) => + boardMember.teamMemberDetails.teamTitle.en.toLowerCase() === + "co-founder" + ); + + return ( + <> + {chairmanBoardMember && ( + <BoardMember boardMember={chairmanBoardMember[0]} /> + )} + {cofounderBoardMembers.map((boardMember) => ( + <BoardMember key={boardMember.id} boardMember={boardMember} /> + ))} + {regularBoardMembers.sort(byLastName()).map((boardMember) => ( + <BoardMember key={boardMember.id} boardMember={boardMember} /> + ))} + </> + ); + }; -const TeamMembersPage = memo(() => { const [ordinaryTeamMembers, setOrdinaryTeamMembers] = useState([]); const [teamBoardMembers, setTeamBoardMembers] = useState([]); const [error, setError] = useState(null); From 2cc9808f7398e2041808f84511d683b5cb165a17 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Sun, 19 Nov 2023 16:57:14 -0500 Subject: [PATCH 481/756] feat(jobs): Allow job postings to have a start date and end date for visibility --- static/js/StaticPages.jsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/static/js/StaticPages.jsx b/static/js/StaticPages.jsx index a933720a79..5ca9555b9a 100644 --- a/static/js/StaticPages.jsx +++ b/static/js/StaticPages.jsx @@ -2636,6 +2636,10 @@ const StaticHR = () => const ConditionalLink = ({ link, children }) => link ? <a href={link} target="_blank">{children}</a> : children; +/* +* Jobs Page +*/ + const JobsPageHeader = ({ jobsAreAvailable }) => { return ( <> @@ -2750,10 +2754,17 @@ const JobsPage = memo(() => { const [error, setError] = useState(null); useEffect(() => { + const currentDateTime = new Date().toISOString(); const fetchJobsJSON = async () => { const query = ` query { - jobPostings(pagination: { limit: -1 }) { + jobPostings( + pagination: { limit: -1 } + filters: { + jobPostingStartDate: { lte: \"${currentDateTime}\" } + jobPostingEndDate: { gte: \"${currentDateTime}\" } + } + ) { data { id attributes { From b6ab3225c3df226cdec9b24a230d8541307d3cf4 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Sun, 19 Nov 2023 17:11:22 -0500 Subject: [PATCH 482/756] Move components to be within the JobsPage component and rename component name with comments for clearer intention and less confusion --- static/js/StaticPages.jsx | 305 +++++++++++++++++++------------------- 1 file changed, 156 insertions(+), 149 deletions(-) diff --git a/static/js/StaticPages.jsx b/static/js/StaticPages.jsx index 5ca9555b9a..a72ee6948a 100644 --- a/static/js/StaticPages.jsx +++ b/static/js/StaticPages.jsx @@ -2640,165 +2640,172 @@ const ConditionalLink = ({ link, children }) => * Jobs Page */ -const JobsPageHeader = ({ jobsAreAvailable }) => { - return ( - <> - <header> - <h1 className="serif"> - <span className="int-en">Jobs at Sefaria</span> - <span className="int-he">משרות פנויות בספריא</span> - </h1> - - {jobsAreAvailable ? ( - <> - <h2> - <span className="int-en">About Sefaria</span> - <span className="int-he">אודות ספריא</span> - </h2> - <p> - <span className="int-en"> - Sefaria is a non-profit organization dedicated to creating the - future of Torah in an open and participatory way. We are assembling - a free, living library of Jewish texts and their interconnections, - in Hebrew and in translation. - </span> - <span className="int-he"> - ספריא היא ארגון ללא מטרות רווח שמטרתו יצירת הדור הבא של לימוד התורה - באופן פתוח ומשותף. אנחנו בספריא מרכיבים ספרייה חיה וחופשית של טקסטים - יהודיים וכלל הקישורים ביניהם, בשפת המקור ובתרגומים. - </span> - </p> - </> - ) : null} - </header> - </> - ); -}; - -const Job = ({ job }) => { - return ( - <div className="job"> - <a className="joblink" target="_blank" href={job.jobLink}> - {job.jobDescription} - </a> - </div> - ); -}; - -const JobsListForDepartment = ({ jobsList }) => { - return ( - <section className="jobsListForDepartment"> - {jobsList.map((job) => ( - <Job key={job.id} job={job} /> - ))} - </section> - ); -}; - -const DepartmentJobPostings = ({ department, departmentJobPostings }) => { - return ( - <section className="section department englishOnly"> - <header> - <h2 className="anchorable">{department}</h2> - </header> - <JobsListForDepartment key={department} jobsList={departmentJobPostings} /> - </section> - ); -}; - -const JobPostingsByDepartment = ({ jobPostings }) => { - const groupedJobPostings = jobPostings.reduce((jobPostingsGroupedByDepartment, jobPosting) => { - const category = jobPosting.jobDepartmentCategory; - if (!jobPostingsGroupedByDepartment[category]) { - jobPostingsGroupedByDepartment[category] = []; - } - jobPostingsGroupedByDepartment[category].push(jobPosting); - return jobPostingsGroupedByDepartment; - }, {}); - - return ( - Object.entries(groupedJobPostings).map(([department, departmentJobPostings]) => { - return ( - <DepartmentJobPostings - key={department} - department={department} - departmentJobPostings={departmentJobPostings} - /> - ); - }) - ); -}; - +const JobsPage = memo(() => { -const NoJobsNotice = () => { - return ( - <div className="section nothing"> - <p> - <span className="int-en"> - Sefaria does not currently have any open positions. - Please follow us on <a target="_blank" href="http://www.facebook.com/sefaria.org" >Facebook</a> - to hear about our next openings. - </span> - <span className="int-he"> - ספריא איננה מחפשת כעת עובדים חדשים. - עקבו אחרינו ב<a target="_blank" href="http://www.facebook.com/sefaria.org" >פייסבוק</a>  - כדי להשאר מעודכנים במשרות עתידיות. - </span> - </p> - </div> - ); -}; + // Show a different header with a description of Sefaria for the page in the case that there are jobs available + const JobsPageHeader = ({ jobsAreAvailable }) => { + return ( + <> + <header> + <h1 className="serif"> + <span className="int-en">Jobs at Sefaria</span> + <span className="int-he">משרות פנויות בספריא</span> + </h1> + + {jobsAreAvailable ? ( + <> + <h2> + <span className="int-en">About Sefaria</span> + <span className="int-he">אודות ספריא</span> + </h2> + <p> + <span className="int-en"> + Sefaria is a non-profit organization dedicated to creating the + future of Torah in an open and participatory way. We are assembling + a free, living library of Jewish texts and their interconnections, + in Hebrew and in translation. + </span> + <span className="int-he"> + ספריא היא ארגון ללא מטרות רווח שמטרתו יצירת הדור הבא של לימוד התורה + באופן פתוח ומשותף. אנחנו בספריא מרכיבים ספרייה חיה וחופשית של טקסטים + יהודיים וכלל הקישורים ביניהם, בשפת המקור ובתרגומים. + </span> + </p> + </> + ) : null} + </header> + </> + ); + }; + + const Job = ({ job }) => { + return ( + <div className="job"> + <a className="joblink" target="_blank" href={job.jobLink}> + {job.jobDescription} + </a> + </div> + ); + }; + + // Show the list of job postings within a department category + const JobsListForDepartment = ({ jobsList }) => { + return ( + <section className="jobsListForDepartment"> + {jobsList.map((job) => ( + <Job key={job.id} job={job} /> + ))} + </section> + ); + }; + + // Job postings are grouped by department. This component will show the jobs for a specific department + // Each department has a header for its category before showing a list of the job postings there + const JobPostingsByDepartment = ({ department, departmentJobPostings }) => { + return ( + <section className="section department englishOnly"> + <header> + <h2 className="anchorable">{department}</h2> + </header> + <JobsListForDepartment key={department} jobsList={departmentJobPostings} /> + </section> + ); + }; + + // Show all the job postings, but first group them by department and render each department separately + const JobPostings = ({ jobPostings }) => { + const groupedJobPostings = jobPostings.reduce((jobPostingsGroupedByDepartment, jobPosting) => { + const category = jobPosting.jobDepartmentCategory; + if (!jobPostingsGroupedByDepartment[category]) { + jobPostingsGroupedByDepartment[category] = []; + } + jobPostingsGroupedByDepartment[category].push(jobPosting); + return jobPostingsGroupedByDepartment; + }, {}); + + return ( + Object.entries(groupedJobPostings).map(([department, departmentJobPostings]) => { + return ( + <JobPostingsByDepartment + key={department} + department={department} + departmentJobPostings={departmentJobPostings} + /> + ); + }) + ); + }; + + + const NoJobsNotice = () => { + return ( + <div className="section nothing"> + <p> + <span className="int-en"> + Sefaria does not currently have any open positions. + Please follow us on <a target="_blank" href="http://www.facebook.com/sefaria.org" >Facebook</a> + to hear about our next openings. + </span> + <span className="int-he"> + ספריא איננה מחפשת כעת עובדים חדשים. + עקבו אחרינו ב<a target="_blank" href="http://www.facebook.com/sefaria.org" >פייסבוק</a>  + כדי להשאר מעודכנים במשרות עתידיות. + </span> + </p> + </div> + ); + }; -const JobsPage = memo(() => { const [jobPostings, setJobPostings] = useState([]); const [error, setError] = useState(null); - useEffect(() => { + const fetchJobsJSON = async () => { const currentDateTime = new Date().toISOString(); - const fetchJobsJSON = async () => { - const query = ` - query { - jobPostings( - pagination: { limit: -1 } - filters: { - jobPostingStartDate: { lte: \"${currentDateTime}\" } - jobPostingEndDate: { gte: \"${currentDateTime}\" } - } - ) { - data { - id - attributes { - jobLink - jobDescription - jobDepartmentCategory - } + const query = ` + query { + jobPostings( + pagination: { limit: -1 } + filters: { + jobPostingStartDate: { lte: \"${currentDateTime}\" } + jobPostingEndDate: { gte: \"${currentDateTime}\" } + } + ) { + data { + id + attributes { + jobLink + jobDescription + jobDepartmentCategory } } } - `; - try { - const response = await fetch(STRAPI_INSTANCE + "/graphql", { - method: "POST", - mode: "cors", - cache: "no-cache", - credentials: "omit", - headers: { - "Content-Type": "application/json", - }, - redirect: "follow", - referrerPolicy: "no-referrer", - body: JSON.stringify({ query }), - }); - if (!response.ok) { - throw new Error(`HTTP Error: ${response.statusText}`); - } - const data = await response.json(); - return data; - } catch (error) { - throw error; } - }; + `; + + try { + const response = await fetch(STRAPI_INSTANCE + "/graphql", { + method: "POST", + mode: "cors", + cache: "no-cache", + credentials: "omit", + headers: { + "Content-Type": "application/json", + }, + redirect: "follow", + referrerPolicy: "no-referrer", + body: JSON.stringify({ query }), + }); + if (!response.ok) { + throw new Error(`HTTP Error: ${response.statusText}`); + } + const data = await response.json(); + return data; + } catch (error) { + throw error; + } + }; + useEffect(() => { const loadJobPostings = async () => { if (typeof STRAPI_INSTANCE !== "undefined" && STRAPI_INSTANCE) { try { @@ -2836,7 +2843,7 @@ const JobsPage = memo(() => { <> <JobsPageHeader jobsAreAvailable={jobPostings?.length} /> {jobPostings?.length ? ( - <JobPostingsByDepartment jobPostings={jobPostings} /> + <JobPostings jobPostings={jobPostings} /> ) : ( <NoJobsNotice /> )} From f6b4d08e0d4ba7b797a0e284e38b92acbf25c428 Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Mon, 20 Nov 2023 12:45:57 +0200 Subject: [PATCH 483/756] fix(Backend topic images): Remove custom exception, prefer assert --- sefaria/model/topic.py | 7 ++----- sefaria/system/exceptions.py | 3 --- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/sefaria/model/topic.py b/sefaria/model/topic.py index 2e7f1fc6e7..ffb8c93620 100644 --- a/sefaria/model/topic.py +++ b/sefaria/model/topic.py @@ -3,7 +3,7 @@ from .schema import AbstractTitledObject, TitleGroup from .text import Ref, IndexSet, AbstractTextRecord from .category import Category -from sefaria.system.exceptions import InputError, DuplicateRecordError, ExternalImageError +from sefaria.system.exceptions import InputError, DuplicateRecordError from sefaria.model.timeperiod import TimePeriod, LifePeriod from sefaria.model.portal import Portal from sefaria.system.database import db @@ -73,10 +73,7 @@ def _validate(self): if getattr(self, 'subclass', False): assert self.subclass in self.subclass_map, f"Field `subclass` set to {self.subclass} which is not one of the valid subclass keys in `Topic.subclass_map`. Valid keys are {', '.join(self.subclass_map.keys())}" if getattr(self, 'image', False): - try: - self.image["image_uri"].index("https://storage.googleapis.com/img.sefaria.org/topics/") - except ExternalImageError: - print("The image is not stored properly. Topic should be stored in the image GCP bucket, in the topics subdirectory.") + assert "https://storage.googleapis.com/img.sefaria.org/topics/" in self.image["image_uri"], "The image is not stored properly. Topic should be stored in the image GCP bucket, in the topics subdirectory." if getattr(self, 'portal_slug', None): Portal.validate_slug_exists(self.portal_slug) diff --git a/sefaria/system/exceptions.py b/sefaria/system/exceptions.py index b0e0fd56cf..72d3493f78 100644 --- a/sefaria/system/exceptions.py +++ b/sefaria/system/exceptions.py @@ -60,9 +60,6 @@ class ManuscriptError(Exception): class MissingKeyError(Exception): pass -class ExternalImageError(Exception): - """Thrown when an image is added to the database that is not hosted in our GCP bucket""" - pass class SluggedMongoRecordMissingError(Exception): pass From d6edc688aedf693c2080d3fda35664dda67a09f5 Mon Sep 17 00:00:00 2001 From: yonadavGit <yonadav.le@gmail.com> Date: Mon, 20 Nov 2023 13:19:41 +0200 Subject: [PATCH 484/756] chore(HeaderIcons): aligned header buttons --- static/css/s2.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/static/css/s2.css b/static/css/s2.css index a16596b7c5..d5e6de2f86 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -763,6 +763,7 @@ div:has(#bannerMessage) + .readerApp.singlePanel .mobileNavMenu { } .loggedIn .help img { height: 24px; + margin-bottom: 3px; } .accountLinks.anon .help { margin-top: 6px; @@ -10643,6 +10644,7 @@ cursor: pointer; color: white; font-size: 75px; font-family: "Roboto", "Helvetica Neue", "Helvetica", sans-serif; + margin-bottom: 3px; } .default-profile-img.invisible { visibility: hidden; From 65787d12230a9766bcbfc518414fdd41c307eda8 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Mon, 20 Nov 2023 15:25:51 +0200 Subject: [PATCH 485/756] chore: starting backend for image uploader --- sourcesheets/views.py | 45 +++++++++++++++++--------------- static/js/AdminEditor.jsx | 13 ++-------- static/js/Misc.jsx | 54 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 79 insertions(+), 33 deletions(-) diff --git a/sourcesheets/views.py b/sourcesheets/views.py index d9b0eb80de..7c5fd71444 100644 --- a/sourcesheets/views.py +++ b/sourcesheets/views.py @@ -1134,35 +1134,38 @@ def export_to_drive(request, credential, sheet_id): return jsonResponse(new_file) +def post_img_to_bucket(file, file_name, bucket_name, max_img_size=None): + from PIL import Image + from io import BytesIO + import base64 + import imghdr + img_file_in_mem = BytesIO(base64.b64decode(file)) + + if imghdr.what(img_file_in_mem) == "gif": + img_url = GoogleStorageManager.upload_file(img_file_in_mem, f"{file_name}.gif", bucket_name) + else: + im = Image.open(img_file_in_mem) + img_file = BytesIO() + if max_img_size: + im.thumbnail(max_img_size, Image.ANTIALIAS) + im.save(img_file, format=im.format) + img_file.seek(0) + + img_url = GoogleStorageManager.upload_file(img_file, f"{file_name}.{im.format.lower()}", bucket_name) + return img_url + @catch_error_as_json def upload_sheet_media(request): if not request.user.is_authenticated: return jsonResponse({"error": _("You must be logged in to access this api.")}) if request.method == "POST": - from PIL import Image - from io import BytesIO - import uuid - import base64 - import imghdr - bucket_name = GoogleStorageManager.UGC_SHEET_BUCKET max_img_size = [1024, 1024] - - img_file_in_mem = BytesIO(base64.b64decode(request.POST.get('file'))) - - if imghdr.what(img_file_in_mem) == "gif": - img_url = GoogleStorageManager.upload_file(img_file_in_mem, f"{request.user.id}-{uuid.uuid1()}.gif", bucket_name) - - else: - im = Image.open(img_file_in_mem) - img_file = BytesIO() - im.thumbnail(max_img_size, Image.ANTIALIAS) - im.save(img_file, format=im.format) - img_file.seek(0) - - img_url = GoogleStorageManager.upload_file(img_file, f"{request.user.id}-{uuid.uuid1()}.{im.format.lower()}", bucket_name) - + import uuid + file = request.POST.get('file') + file_name = f'{request.user.id}-{uuid.uuid1()}' + img_url = post_img_to_bucket(file, file_name, bucket_name, max_img_size) return jsonResponse({"url": img_url}) return jsonResponse({"error": "Unsupported HTTP method."}) diff --git a/static/js/AdminEditor.jsx b/static/js/AdminEditor.jsx index 68d0c62903..0f32c72d1f 100644 --- a/static/js/AdminEditor.jsx +++ b/static/js/AdminEditor.jsx @@ -1,6 +1,6 @@ import React, {useRef, useState} from "react"; import Sefaria from "./sefaria/sefaria"; -import {AdminToolHeader, InterfaceText, ProfilePic, TitleVariants} from "./Misc"; +import {AdminToolHeader, InterfaceText, PictureUploader, TitleVariants} from "./Misc"; import sanitizeHtml from 'sanitize-html'; import classNames from "classnames"; const options_for_form = { @@ -163,20 +163,11 @@ const AdminEditor = ({title, data, close, catMenu, updateData, savingStatus, </select> </div>; } - const getPicture = (label, field) => { - return <ProfilePic - saveCallback={(url) => handlePictureChange(url)} - showButtons={true} - name={""} - url={data[field]} - len={60} - />; - } const item = ({label, field, placeholder, type, dropdown_data}) => { let obj; switch(type) { case 'picture': - obj = getPicture(label, field); + obj = <PictureUploader callback={handlePictureChange}/>; break; case 'dropdown': obj = getDropdown(field, dropdown_data, placeholder); diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 7d3aacae68..c0f1f6d30a 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -1570,6 +1570,57 @@ FollowButton.propTypes = { }; +const PictureUploader = (callback) => { + const fileInput = useRef(null); + + var uploadImage = function(imageData) { + const formData = new FormData(); + formData.append('file', imageData.replace(/data:image\/(jpe?g|png|gif);base64,/, "")); + // formData.append('file', imageData); + + $.ajax({ + url: Sefaria.apiHost + "/api/sheets/upload-image", + type: 'POST', + data: formData, + contentType: false, + processData: false, + success: function(data) { + callback(data.url); + // $("#inlineAddMediaInput").val(data.url); + // $("#addmediaDiv").find(".button").first().trigger("click"); + // $("#inlineAddMediaInput").val(""); + }, + error: function(e) { + console.log("photo upload ERROR", e); + } + }); + } + const onFileSelect = (e) => { + const file = fileInput.current.files[0]; + if (file == null) + return; + if (/\.(jpe?g|png|gif)$/i.test(file.name)) { + const reader = new FileReader(); + + reader.addEventListener("load", function() { + uploadImage(reader.result); + }, false); + + reader.addEventListener("onerror", function() { + alert(reader.error); + }, false); + + reader.readAsDataURL(file); + } else { + alert('not an image'); + } + } + return <div> + <div role="button" title={Sefaria._("Add an image")} aria-label="Add an image" className="editorAddInterfaceButton" contentEditable={false} onClick={(e) => e.stopPropagation()} id="addImageButton"> + <label htmlFor="addImageFileSelector" id="addImageFileSelectorLabel"></label> + </div><input id="addImageFileSelector" type="file" style={{ display: "none"}} onChange={onFileSelect} ref={fileInput} /> + </div>; + } const CategoryColorLine = ({category}) => <div className="categoryColorLine" style={{background: Sefaria.palette.categoryColor(category)}}/>; @@ -3243,5 +3294,6 @@ export { CategoryChooser, TitleVariants, requestWithCallBack, - OnInView + OnInView, + PictureUploader }; From 1596f1810a93d6a09b12ab12cab7d366283204db Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Mon, 20 Nov 2023 15:42:19 +0200 Subject: [PATCH 486/756] feat(api-v3): add new attribute 'fullLanguage' to Version, and use it in the api rather than actualLanguage which is just the language ISO code. --- api/api_warnings.py | 4 ++-- api/tests.py | 26 +++++++++++++------------- sefaria/constants/model.py | 20 ++++++++++++++++++++ sefaria/model/text.py | 10 ++++++++-- sefaria/model/text_manager.py | 10 +++++----- 5 files changed, 48 insertions(+), 22 deletions(-) diff --git a/api/api_warnings.py b/api/api_warnings.py index aecf32b601..ac4a4e4788 100644 --- a/api/api_warnings.py +++ b/api/api_warnings.py @@ -38,14 +38,14 @@ class APINoVersion(TextsAPIResponseMessage): def __init__(self, oref: Ref, vtitle: str, lang: str): self.warning_code = APIWarningCode.APINoVersion - self.message = f'We do not have version named {vtitle} with language code {lang} for {oref}' + self.message = f'We do not have version named {vtitle} with language {lang} for {oref}' class APINoLanguageVersion(TextsAPIResponseMessage): def __init__(self, oref: Ref, langs: List[str]): self.warning_code = APIWarningCode.APINoLanguageVersion - self.message = f'We do not have the code language you asked for {oref}. Available codes are {langs}' + self.message = f'We do not have the language you asked for {oref}. Available languages are {langs}' class APINoSourceText(TextsAPIResponseMessage): diff --git a/api/tests.py b/api/tests.py index 78549980f8..9b60202875 100644 --- a/api/tests.py +++ b/api/tests.py @@ -59,7 +59,7 @@ def test_api_get_text_translation(self): self.assertEqual(data["versions"][0]['versionTitle'], "William Davidson Edition - English") def test_api_get_text_lang_all(self): - response = c.get('/api/v3/texts/Rashi_on_Genesis.2.3?version=en|all') + response = c.get('/api/v3/texts/Rashi_on_Genesis.2.3?version=english|all') self.assertEqual(200, response.status_code) data = json.loads(response.content) self.assertTrue(len(data["versions"]) > 1) @@ -71,7 +71,7 @@ def test_api_get_text_lang_all(self): self.assertEqual(data["toSections"], ['2', '3']) def test_api_get_text_specific(self): - response = c.get('/api/v3/texts/Tosafot_on_Sukkah.2a.4.1?version=he|Vilna_Edition') + response = c.get('/api/v3/texts/Tosafot_on_Sukkah.2a.4.1?version=hebrew|Vilna_Edition') self.assertEqual(200, response.status_code) data = json.loads(response.content) self.assertEqual(len(data["versions"]), 1) @@ -97,7 +97,7 @@ def test_api_get_text_base(self): self.assertEqual(data["versions"][0]['versionTitle'], "William Davidson Edition - Vocalized Aramaic") def test_api_get_text_two_params(self): - response = c.get('/api/v3/texts/Genesis.1?version=he|Tanach with Nikkud&version=en|all') + response = c.get('/api/v3/texts/Genesis.1?version=hebrew|Tanach with Nikkud&version=english|all') data = json.loads(response.content) self.assertTrue(len(data["versions"]) > 7) self.assertEqual(data["versions"][0]['actualLanguage'], 'he') @@ -146,7 +146,7 @@ def test_api_get_text_empty_ref(self): self.assertEqual(data["error"], "We have no text for Berakhot 1a.") def test_api_get_text_no_source(self): - response = c.get("/api/v3/texts/The_Book_of_Maccabees_I.1?version=en|Brenton's_Septuagint&version=source") + response = c.get("/api/v3/texts/The_Book_of_Maccabees_I.1?version=english|Brenton's_Septuagint&version=source") self.assertEqual(200, response.status_code) data = json.loads(response.content) self.assertEqual(len(data["versions"]), 1) @@ -162,26 +162,26 @@ def test_api_get_text_no_translation(self): self.assertEqual(data['warnings'][0]['translation']['message'], 'We do not have a translation for Shuvi Shuvi HaShulamit') def test_api_get_text_no_language(self): - response = c.get("/api/v3/texts/The_Book_of_Maccabees_I.1?version=en|Brenton's_Septuagint&version=sgrg|all") + response = c.get("/api/v3/texts/The_Book_of_Maccabees_I.1?version=english|Brenton's_Septuagint&version=sgrg|all") self.assertEqual(200, response.status_code) data = json.loads(response.content) self.assertEqual(len(data["versions"]), 1) self.assertEqual(data['warnings'][0]['sgrg|all']['warning_code'], APIWarningCode.APINoLanguageVersion.value) self.assertEqual(data['warnings'][0]['sgrg|all']['message'], - "We do not have the code language you asked for The Book of Maccabees I 1. Available codes are ['en', 'he']") + "We do not have the language you asked for The Book of Maccabees I 1. Available languages are ['english', 'hebrew']") def test_api_get_text_no_version(self): - response = c.get("/api/v3/texts/The_Book_of_Maccabees_I.1?version=en|Brenton's_Septuagint&version=he|Kishkoosh") + response = c.get("/api/v3/texts/The_Book_of_Maccabees_I.1?version=english|Brenton's_Septuagint&version=hebrew|Kishkoosh") self.assertEqual(200, response.status_code) data = json.loads(response.content) self.assertEqual(len(data["versions"]), 1) - self.assertEqual(data['warnings'][0]['he|Kishkoosh']['warning_code'], APIWarningCode.APINoVersion.value) - self.assertEqual(data['warnings'][0]['he|Kishkoosh']['message'], - 'We do not have version named Kishkoosh with language code he for The Book of Maccabees I 1') + self.assertEqual(data['warnings'][0]['hebrew|Kishkoosh']['warning_code'], APIWarningCode.APINoVersion.value) + self.assertEqual(data['warnings'][0]['hebrew|Kishkoosh']['message'], + 'We do not have version named Kishkoosh with language hebrew for The Book of Maccabees I 1') def test_fill_in_missing_segments(self): vtitle = "Maimonides' Mishneh Torah, edited by Philip Birnbaum, New York, 1967" - response = c.get(f"/api/v3/texts/Mishneh_Torah,_Sabbath_1?version=en|{vtitle}&fill_in_missing_segments=true") + response = c.get(f"/api/v3/texts/Mishneh_Torah,_Sabbath_1?version=english|{vtitle}&fill_in_missing_segments=true") self.assertEqual(200, response.status_code) data = json.loads(response.content) self.assertTrue(len(data['versions'][0]['text']) > 2) @@ -191,7 +191,7 @@ def test_fill_in_missing_segments(self): def test_without_fill_in_missing_segments(self): vtitle = "Maimonides' Mishneh Torah, edited by Philip Birnbaum, New York, 1967" - response = c.get(f"/api/v3/texts/Mishneh_Torah,_Sabbath_1?version=en|{vtitle}") + response = c.get(f"/api/v3/texts/Mishneh_Torah,_Sabbath_1?version=english|{vtitle}") self.assertEqual(200, response.status_code) data = json.loads(response.content) self.assertEqual(len(data['versions'][0]['text']), 2) @@ -199,7 +199,7 @@ def test_without_fill_in_missing_segments(self): def test_wrap_all_entities(self): vtitle = "The Contemporary Torah, Jewish Publication Society, 2006" - response = c.get(f"/api/v3/texts/Genesis%2010?version=en|{vtitle}&return_format=wrap_all_entities") + response = c.get(f"/api/v3/texts/Genesis%2010?version=english|{vtitle}&return_format=wrap_all_entities") self.assertEqual(200, response.status_code) data = json.loads(response.content) self.assertTrue('<a class ="refLink"' in data['versions'][0]['text'][3]) diff --git a/sefaria/constants/model.py b/sefaria/constants/model.py index 107e208a45..8c681d9a02 100644 --- a/sefaria/constants/model.py +++ b/sefaria/constants/model.py @@ -10,3 +10,23 @@ 'img': ['src', 'alt'], 'a': ['dir', 'class', 'href', 'data-ref', "data-ven", "data-vhe", 'data-scroll-link'], } + +LANGUAGE_CODES = { + #maps ISO language codes to their nother language (i.e. jrb to Arabic rather than Judeo-Arabic) + "ar": "arabic", + "de": "german", + "en": "english", + "eo": "esperanto", + "es": "spanish", + "fa": "persian", + "fi": "finnish", + "fr": "french", + "he": "hebrew", + "it": "italian", + "lad": "ladino", + "pl": "polish", + "pt": "portuguese", + "ru": "russian", + "yi": "yiddish", + "jrb": "arabic", +} diff --git a/sefaria/model/text.py b/sefaria/model/text.py index f065d30756..50ce59e93b 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -1335,6 +1335,7 @@ class Version(AbstractTextRecord, abst.AbstractMongoRecord, AbstractSchemaConten "purchaseInformationURL", "hasManuallyWrappedRefs", # true for texts where refs were manually wrapped in a-tags. no need to run linker at run-time. "actualLanguage", + 'fullLanguage', "isBaseText", 'isSource', 'isPrimary', @@ -1356,6 +1357,11 @@ def _validate(self): languageCodeRe = re.search(r"\[([a-z]{2})\]$", getattr(self, "versionTitle", None)) if languageCodeRe and languageCodeRe.group(1) != getattr(self,"actualLanguage",None): self.actualLanguage = languageCodeRe.group(1) + if not getattr(self, 'fullLanguage', None): + try: + self.fullLanguage = constants.LANGUAGE_CODES[self.actualLanguage] + except KeyError: + self.fullLanguage = constants.LANGUAGE_CODES[self.language] if getattr(self,"language", None) not in ["en", "he"]: raise InputError("Version language must be either 'en' or 'he'") index = self.get_index() @@ -1709,7 +1715,7 @@ def __init__(self, oref, lang, vtitle, merge_versions=False): def versions(self): if self._versions == []: condition_query = self.oref.condition_query(self.lang) if self.merge_versions else \ - {'title': self.oref.index.title, 'actualLanguage': self.lang, 'versionTitle': self.vtitle} + {'title': self.oref.index.title, 'fullLanguage': self.lang, 'versionTitle': self.vtitle} self._versions = VersionSet(condition_query, proj=self.oref.part_projection()) return self._versions @@ -1722,7 +1728,7 @@ def _validate_versions(self, versions): if not self.merge_versions and len(versions) > 1: raise InputError("Got many versions instead of one") for version in versions: - condition = version.title == self.oref.index.title and version.actualLanguage == self.lang + condition = version.title == self.oref.index.title and version.fullLanguage == self.lang if not self.merge_versions: condition = condition and version.versionTitle == self.vtitle if not condition: diff --git a/sefaria/model/text_manager.py b/sefaria/model/text_manager.py index c4fb8c4f23..7b9a3bba06 100644 --- a/sefaria/model/text_manager.py +++ b/sefaria/model/text_manager.py @@ -28,7 +28,7 @@ def __init__(self, oref: Ref, versions_params: List[List[str]], fill_in_missing_ self.return_obj = { 'versions': [], 'missings': [], - 'available_langs': sorted({v.actualLanguage for v in self.all_versions}), + 'available_langs': sorted({v.fullLanguage for v in self.all_versions}), 'available_versions': [{f: getattr(v, f, "") for f in fields} for v in self.all_versions] } @@ -38,12 +38,12 @@ def _append_version(self, version): for attr in ['chapter', 'title', 'language']: fields.remove(attr) version_details = {f: getattr(version, f, "") for f in fields} - text_range = TextRange(self.oref, version.actualLanguage, version.versionTitle, self.fill_in_missing_segments) + text_range = TextRange(self.oref, version.fullLanguage, version.versionTitle, self.fill_in_missing_segments) if self.fill_in_missing_segments: # we need a new VersionSet of only the relevant versions for merging. copy should be better than calling for mongo relevant_versions = copy.copy(self.all_versions) - relevant_versions.remove(lambda v: v.actualLanguage != version.actualLanguage) + relevant_versions.remove(lambda v: v.fullLanguage != version.fullLanguage) else: relevant_versions = [version] text_range.versions = relevant_versions @@ -66,7 +66,7 @@ def _append_required_versions(self, lang: str, vtitle: str) -> None: elif lang == self.TRANSLATION: lang_condition = lambda v: not getattr(v, 'isSource', False) elif lang: - lang_condition = lambda v: v.actualLanguage == lang + lang_condition = lambda v: v.fullLanguage == lang else: lang_condition = lambda v: True if vtitle and vtitle != self.ALL: @@ -76,7 +76,7 @@ def _append_required_versions(self, lang: str, vtitle: str) -> None: if vtitle != self.ALL and versions: versions = [max(versions, key=lambda v: getattr(v, 'priority', 0))] for version in versions: - if all(version.actualLanguage != v['actualLanguage'] or version.versionTitle != v['versionTitle'] for v in self.return_obj['versions']): + if all(version.fullLanguage != v['fullLanguage'] or version.versionTitle != v['versionTitle'] for v in self.return_obj['versions']): #do not return the same version even if included in two different version params self._append_version(version) if not versions: From 8115d1f428717c8474c672ee1072a2bf635b6b01 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Mon, 20 Nov 2023 15:51:13 +0200 Subject: [PATCH 487/756] feat(api-v3): return error when return_format is not one of our known formats. --- api/tests.py | 7 ++++++- api/views.py | 4 ++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/api/tests.py b/api/tests.py index 9b60202875..1ea31d542b 100644 --- a/api/tests.py +++ b/api/tests.py @@ -205,9 +205,14 @@ def test_wrap_all_entities(self): self.assertTrue('<a class ="refLink"' in data['versions'][0]['text'][3]) self.assertTrue('<a href="/topics' in data['versions'][0]['text'][8]) - def text_text_only(self): + def test_text_only(self): response = c.get(f"/api/v3/texts/Shulchan_Arukh%2C_Orach_Chayim.1:1?return_format=text_only") self.assertEqual(200, response.status_code) data = json.loads(response.content) self.assertFalse('<' in data['versions'][0]['text']) + def test_error_return_format(self): + response = c.get(f"/api/v3/texts/Shulchan_Arukh%2C_Orach_Chayim.1:1?return_format=not_valid") + self.assertEqual(400, response.status_code) + data = json.loads(response.content) + self.assertEqual(data['error'], "return_format should be one of those formats: ['default', 'wrap_all_entities', 'text_only'].") diff --git a/api/views.py b/api/views.py index ec2f67077f..c6e0beca82 100644 --- a/api/views.py +++ b/api/views.py @@ -7,6 +7,8 @@ class Text(View): + RETURN_FORMATS = ['default', 'wrap_all_entities', 'text_only'] + def dispatch(self, request, *args, **kwargs): try: self.oref = Ref.instantiate_ref_with_legacy_parse_fallback(kwargs['tref']) @@ -48,6 +50,8 @@ def get(self, request, *args, **kwargs): versions_params = [self.split_piped_params(param_str) for param_str in versions_params] fill_in_missing_segments = request.GET.get('fill_in_missing_segments', False) return_format = request.GET.get('return_format', 'default') + if return_format not in self.RETURN_FORMATS: + return jsonResponse({'error': f'return_format should be one of those formats: {self.RETURN_FORMATS}.'}, status=400) text_manager = TextManager(self.oref, versions_params, fill_in_missing_segments, return_format) data = text_manager.get_versions_for_query() data = self._handle_warnings(data) From 778cf492c5ea08db2f1cdfba0d4edf6cac4579f9 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Tue, 21 Nov 2023 09:31:59 +0200 Subject: [PATCH 488/756] chore: start topic_upload_photo --- reader/views.py | 18 +++++++++++++++++ sefaria/urls.py | 1 + sourcesheets/views.py | 46 ++++++++++++++++++++----------------------- 3 files changed, 40 insertions(+), 25 deletions(-) diff --git a/reader/views.py b/reader/views.py index b93cc2bd1e..5a2b550460 100644 --- a/reader/views.py +++ b/reader/views.py @@ -3567,6 +3567,24 @@ def profile_follow_api(request, ftype, slug): return jsonResponse(response) return jsonResponse({"error": "Unsupported HTTP method."}) +@catch_error_as_json +def topic_upload_photo(request): + if not request.user.is_authenticated: + return jsonResponse({"error": _("You must be logged in to update your profile photo.")}) + if request.method == "POST": + now = epoch_time() + + profile = UserProfile(id=request.user.id) + bucket_name = GoogleStorageManager.PROFILES_BUCKET + image = Image.open(request.FILES['file']) + old_big_pic_filename = GoogleStorageManager.get_filename_from_url(profile.profile_pic_url) + big_pic_url = GoogleStorageManager.upload_file(get_resized_file(image, (250, 250)), "{}-{}.png".format(profile.slug, now), bucket_name, old_big_pic_filename) + + profile.update({"profile_pic_url": big_pic_url}) + profile.save() + public_user_data(request.user.id, ignore_cache=True) # reset user data cache + return jsonResponse({"url": big_pic_url}) + return jsonResponse({"error": "Unsupported HTTP method."}) @catch_error_as_json def profile_upload_photo(request): diff --git a/sefaria/urls.py b/sefaria/urls.py index 757fcef1aa..ba5be292da 100644 --- a/sefaria/urls.py +++ b/sefaria/urls.py @@ -103,6 +103,7 @@ url(r'^topics/b/(?P<topic>.+)$', reader_views.topic_page_b), url(r'^topics/(?P<topic>.+)$', reader_views.topic_page), url(r'^api/topic/completion/(?P<topic>.+)', reader_views.topic_completion_api) + url(r'^topics/upload-image$', reader_views.topic_upload_photo), ] # Calendar Redirects diff --git a/sourcesheets/views.py b/sourcesheets/views.py index 7c5fd71444..8f63f65762 100644 --- a/sourcesheets/views.py +++ b/sourcesheets/views.py @@ -1134,38 +1134,34 @@ def export_to_drive(request, credential, sheet_id): return jsonResponse(new_file) -def post_img_to_bucket(file, file_name, bucket_name, max_img_size=None): - from PIL import Image - from io import BytesIO - import base64 - import imghdr - img_file_in_mem = BytesIO(base64.b64decode(file)) - - if imghdr.what(img_file_in_mem) == "gif": - img_url = GoogleStorageManager.upload_file(img_file_in_mem, f"{file_name}.gif", bucket_name) - else: - im = Image.open(img_file_in_mem) - img_file = BytesIO() - if max_img_size: - im.thumbnail(max_img_size, Image.ANTIALIAS) - im.save(img_file, format=im.format) - img_file.seek(0) - - img_url = GoogleStorageManager.upload_file(img_file, f"{file_name}.{im.format.lower()}", bucket_name) - return img_url - - @catch_error_as_json def upload_sheet_media(request): if not request.user.is_authenticated: return jsonResponse({"error": _("You must be logged in to access this api.")}) if request.method == "POST": + from PIL import Image + from io import BytesIO + import uuid + import base64 + import imghdr + bucket_name = GoogleStorageManager.UGC_SHEET_BUCKET max_img_size = [1024, 1024] - import uuid - file = request.POST.get('file') - file_name = f'{request.user.id}-{uuid.uuid1()}' - img_url = post_img_to_bucket(file, file_name, bucket_name, max_img_size) + + img_file_in_mem = BytesIO(base64.b64decode(request.POST.get('file'))) + + if imghdr.what(img_file_in_mem) == "gif": + img_url = GoogleStorageManager.upload_file(img_file_in_mem, f"{request.user.id}-{uuid.uuid1()}.gif", bucket_name) + + else: + im = Image.open(img_file_in_mem) + img_file = BytesIO() + im.thumbnail(max_img_size, Image.ANTIALIAS) + im.save(img_file, format=im.format) + img_file.seek(0) + + img_url = GoogleStorageManager.upload_file(img_file, f"{request.user.id}-{uuid.uuid1()}.{im.format.lower()}", bucket_name) + return jsonResponse({"url": img_url}) return jsonResponse({"error": "Unsupported HTTP method."}) From 80f1a8c36672bf554e29ce232a25250612c1c5e6 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Tue, 21 Nov 2023 14:51:10 +0200 Subject: [PATCH 489/756] feat(Topic Editor): Topic Editor can post to google cloud and return url --- reader/views.py | 35 +++++++++++++++++++------------ sefaria/google_storage_manager.py | 6 ++++-- sefaria/urls.py | 4 ++-- sites/sefaria/site_settings.py | 3 ++- static/js/Misc.jsx | 21 ++++++++++++------- 5 files changed, 44 insertions(+), 25 deletions(-) diff --git a/reader/views.py b/reader/views.py index 5a2b550460..951667996d 100644 --- a/reader/views.py +++ b/reader/views.py @@ -62,7 +62,7 @@ from sefaria.helper.topic import get_topic, get_all_topics, get_topics_for_ref, get_topics_for_book, \ get_bulk_topics, recommend_topics, get_top_topic, get_random_topic, \ get_random_topic_source, edit_topic_source, \ - update_order_of_topic_sources, delete_ref_topic_link, update_authors_place_and_time + update_order_of_topic_sources, delete_ref_topic_link, update_authors_place_and_time, add_image_to_topic from sefaria.helper.community_page import get_community_page_items from sefaria.helper.file import get_resized_file from sefaria.image_generator import make_img_http_response @@ -3572,18 +3572,27 @@ def topic_upload_photo(request): if not request.user.is_authenticated: return jsonResponse({"error": _("You must be logged in to update your profile photo.")}) if request.method == "POST": - now = epoch_time() - - profile = UserProfile(id=request.user.id) - bucket_name = GoogleStorageManager.PROFILES_BUCKET - image = Image.open(request.FILES['file']) - old_big_pic_filename = GoogleStorageManager.get_filename_from_url(profile.profile_pic_url) - big_pic_url = GoogleStorageManager.upload_file(get_resized_file(image, (250, 250)), "{}-{}.png".format(profile.slug, now), bucket_name, old_big_pic_filename) - - profile.update({"profile_pic_url": big_pic_url}) - profile.save() - public_user_data(request.user.id, ignore_cache=True) # reset user data cache - return jsonResponse({"url": big_pic_url}) + from io import BytesIO + import uuid + import base64 + """ + "image" : { + "image_uri" : "https://storage.googleapis.com/img.sefaria.org/topics/shabbat.jpg", + "image_caption" : { + "en" : "Friday Evening, Isidor Kaufmann, Austria c. 1920. The Jewish Museum, Gift of Mr. and Mrs. M. R. Schweitzer", + "he" : "שישי בערב, איזידור קאופמן, וינה 1920. המוזיאון היהודי בניו יורק, מתנת מר וגברת מ.ר. שוויצר" + } + } +Validation that the image_uri url should start with https://storage.googleapis.com/img.sefaria.org/topics/ + """ + bucket_name = GoogleStorageManager.TOPICS_BUCKET + img_file_in_mem = BytesIO(base64.b64decode(request.POST.get('file'))) + img_url = GoogleStorageManager.upload_file(img_file_in_mem, f"topics/{request.user.id}-{uuid.uuid1()}.gif", + bucket_name) + #big_pic_url = GoogleStorageManager.upload_file(get_resized_file(image, (250, 250)), "{}-{}.png".format(profile.slug, now), bucket_name, old_big_pic_filename) + + add_image_to_topic(topic_slug, img_url, en_caption, he_caption) + return jsonResponse({"url": img_url}) return jsonResponse({"error": "Unsupported HTTP method."}) @catch_error_as_json diff --git a/sefaria/google_storage_manager.py b/sefaria/google_storage_manager.py index b186430d6d..4bd2fa6c23 100644 --- a/sefaria/google_storage_manager.py +++ b/sefaria/google_storage_manager.py @@ -18,14 +18,16 @@ class GoogleStorageManager(object): COLLECTIONS_BUCKET = SITE_SETTINGS["COLLECTIONS_BUCKET"] PROFILES_BUCKET = SITE_SETTINGS["PROFILES_BUCKET"] UGC_SHEET_BUCKET = SITE_SETTINGS["UGC_BUCKET"] + TOPICS_BUCKET = SITE_SETTINGS["TOPICS_BUCKET"] BASE_URL = "https://storage.googleapis.com" @classmethod def get_bucket(cls, bucket_name): if getattr(cls, 'client', None) is None: - # for local development, change below line to cls.client = storage.Client(project="production-deployment") - cls.client = storage.Client.from_service_account_json(GOOGLE_APPLICATION_CREDENTIALS_FILEPATH) + # for local development, change below line to + cls.client = storage.Client(project="production-deployment") + #cls.client = storage.Client.from_service_account_json(GOOGLE_APPLICATION_CREDENTIALS_FILEPATH) bucket = cls.client.get_bucket(bucket_name) return bucket diff --git a/sefaria/urls.py b/sefaria/urls.py index ba5be292da..929467238c 100644 --- a/sefaria/urls.py +++ b/sefaria/urls.py @@ -102,8 +102,8 @@ url(r'^topics/?$', reader_views.topics_page), url(r'^topics/b/(?P<topic>.+)$', reader_views.topic_page_b), url(r'^topics/(?P<topic>.+)$', reader_views.topic_page), - url(r'^api/topic/completion/(?P<topic>.+)', reader_views.topic_completion_api) - url(r'^topics/upload-image$', reader_views.topic_upload_photo), + url(r'^api/topic/completion/(?P<topic>.+)', reader_views.topic_completion_api), + url(r'^api/topics/upload-image$', reader_views.topic_upload_photo), ] # Calendar Redirects diff --git a/sites/sefaria/site_settings.py b/sites/sefaria/site_settings.py index 4f03a3d2df..17ec60b3d9 100644 --- a/sites/sefaria/site_settings.py +++ b/sites/sefaria/site_settings.py @@ -13,5 +13,6 @@ "SUPPORTED_TRANSLATION_LANGUAGES": ['en', 'es', 'fr', 'de'], "COLLECTIONS_BUCKET": "sefaria-collection-images", "PROFILES_BUCKET": 'sefaria-profile-pictures', - "UGC_BUCKET": 'sheet-user-uploaded-media' + "UGC_BUCKET": 'sheet-user-uploaded-media', + "TOPICS_BUCKET": 'img.sefaria.org' } \ No newline at end of file diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 5dea36be55..e0a820257b 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -1587,7 +1587,7 @@ const PictureUploader = (callback) => { // formData.append('file', imageData); $.ajax({ - url: Sefaria.apiHost + "/api/sheets/upload-image", + url: Sefaria.apiHost + "/api/topics/upload-image", type: 'POST', data: formData, contentType: false, @@ -1623,11 +1623,18 @@ const PictureUploader = (callback) => { alert('not an image'); } } - return <div> - <div role="button" title={Sefaria._("Add an image")} aria-label="Add an image" className="editorAddInterfaceButton" contentEditable={false} onClick={(e) => e.stopPropagation()} id="addImageButton"> - <label htmlFor="addImageFileSelector" id="addImageFileSelectorLabel"></label> - </div><input id="addImageFileSelector" type="file" style={{ display: "none"}} onChange={onFileSelect} ref={fileInput} /> - </div>; + return <div><div role="button" title={Sefaria._("Add an image")} aria-label="Add an image" className="editorAddInterfaceButton" contentEditable={false} onClick={(e) => e.stopPropagation()} id="addImageButton"> + <label htmlFor="addImageFileSelector" id="addImageFileSelectorLabel"></label> + </div><input id="addImageFileSelector" type="file" onChange={onFileSelect} ref={fileInput} /> + <div className="section"> + <label><InterfaceText>English Caption</InterfaceText></label> + <input type="text" id="enCaption"/> + </div> + <div className="section"> + <label><InterfaceText>Hebrew Caption</InterfaceText></label> + <input type="text" id="heCaption"/> + </div> + </div> } const CategoryColorLine = ({category}) => @@ -3313,6 +3320,6 @@ export { TitleVariants, requestWithCallBack, OnInView, - PictureUploader + PictureUploader, ImageWithCaption }; From fc00285baf92c779a889a933f12fb7d414d7e4b5 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Tue, 21 Nov 2023 15:02:31 +0200 Subject: [PATCH 490/756] fix: use LANCZOS for sheets and collections image uploading --- sefaria/views.py | 2 +- sourcesheets/views.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sefaria/views.py b/sefaria/views.py index 68af34159a..76dd901c98 100644 --- a/sefaria/views.py +++ b/sefaria/views.py @@ -539,7 +539,7 @@ def collections_image_upload(request, resize_image=True): image = Image.open(temp_uploaded_file) resized_image_file = BytesIO() if resize_image: - image.thumbnail(MAX_FILE_DIMENSIONS, Image.ANTIALIAS) + image.thumbnail(MAX_FILE_DIMENSIONS, Image.LANCZOS) image.save(resized_image_file, optimize=True, quality=70, format="PNG") resized_image_file.seek(0) bucket_name = GoogleStorageManager.COLLECTIONS_BUCKET diff --git a/sourcesheets/views.py b/sourcesheets/views.py index d9b0eb80de..f177423023 100644 --- a/sourcesheets/views.py +++ b/sourcesheets/views.py @@ -1157,7 +1157,7 @@ def upload_sheet_media(request): else: im = Image.open(img_file_in_mem) img_file = BytesIO() - im.thumbnail(max_img_size, Image.ANTIALIAS) + im.thumbnail(max_img_size, Image.LANCZOS) im.save(img_file, format=im.format) img_file.seek(0) From 860774587d6627c5fe8be12cc71c377b87f0b3b1 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Tue, 21 Nov 2023 15:40:33 +0200 Subject: [PATCH 491/756] fix(Admin Editor): add confirm message in admineditor --- static/js/AdminEditor.jsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/static/js/AdminEditor.jsx b/static/js/AdminEditor.jsx index 7061b4b7bf..5c51c32ea1 100644 --- a/static/js/AdminEditor.jsx +++ b/static/js/AdminEditor.jsx @@ -183,6 +183,11 @@ const AdminEditor = ({title, data, close, catMenu, updateData, savingStatus, {obj} </div>; } + const confirmDelete = () => { + if (confirm("Are you sure you want to delete?")) { + deleteObj(); + } + } return <div className="editTextInfo"> <div className="static"> @@ -202,7 +207,7 @@ const AdminEditor = ({title, data, close, catMenu, updateData, savingStatus, })} {extras} {!isNew && - <div onClick={deleteObj} id="deleteTopic" className="button small deleteTopic" tabIndex="0" + <div onClick={confirmDelete} id="deleteTopic" className="button small deleteTopic" tabIndex="0" role="button"> <InterfaceText>Delete</InterfaceText> </div>} From 9c2829e16f509786da4c2f17d79e9fcda88ab910 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Tue, 21 Nov 2023 15:43:34 +0200 Subject: [PATCH 492/756] refactor(translations): new js class in VersionBlock.jsx for versions handling. new components VersionBlockHeader and VersionBlockSelectButton taken out from VersionBlock. --- static/js/OpenVersion.jsx | 75 ----------------------- static/js/VersionBlock.jsx | 83 +++++++++++++++++++------- static/js/VersionBlockHeader.jsx | 57 ++++++++++++++++++ static/js/VersionBlockSelectButton.jsx | 20 +++++++ 4 files changed, 138 insertions(+), 97 deletions(-) delete mode 100644 static/js/OpenVersion.jsx create mode 100644 static/js/VersionBlockHeader.jsx create mode 100644 static/js/VersionBlockSelectButton.jsx diff --git a/static/js/OpenVersion.jsx b/static/js/OpenVersion.jsx deleted file mode 100644 index d31c061493..0000000000 --- a/static/js/OpenVersion.jsx +++ /dev/null @@ -1,75 +0,0 @@ -import React from 'react'; -import Sefaria from "./sefaria/sefaria"; -import PropTypes from "prop-types"; - -function OpenVersion({currRef, version, currObjectVersions, targetPanel, text, className, openVersionInSidebar, openVersionInReader, rendermode, firstSectionRef}) { - - const mainPanel = targetPanel === 'main'; - function makeVersionLink() { - // maintain all versions for languages you're not currently selecting - if (version.merged) { - return "#"; // there's no url for a merged version - } - const withParam = mainPanel ? "" : "&with=Translation Open"; - const versionParam = mainPanel ? version.language : 'side'; - const nonSelectedVersionParams = Object.entries(currObjectVersions) - .filter(([vlang, ver]) => !!ver && !!ver?.versionTitle && !version?.merged && (withParam || vlang !== version.language)) // in 'side' case, keep all version params - .map(([vlang, ver]) => `&v${vlang}=${ver.versionTitle.replace(/\s/g,'_')}`) - .join(""); - const versionLink = nonSelectedVersionParams === "" ? null : `/${Sefaria.normRef(currRef)}${nonSelectedVersionParams}&v${versionParam}=${version.versionTitle.replace(/\s/g,'_')}${withParam}`.replace("&","?"); - return versionLink; - } - - function openInSidebar(e) { - e.preventDefault(); - try { - gtag("event", "onClick_version_title", {element_name: `version_title`, - change_to: `${version.versionTitle}`, change_from: `${currObjectVersions[version.language]['versionTitle']}`, - categories: `${Sefaria.refCategories(currRef)}`, book: `${Sefaria.parseRef(currRef).index}` }) - } - catch(err) { - console.log(err); - } - openVersionInSidebar(version.versionTitle, version.language); - } - - function openInMainPanel(e) { - e.preventDefault(); - try { - gtag("event", "onClick_select_version", {element_name: `select_version`, - change_to: `${version.versionTitle}`, change_from: `${currObjectVersions[version.language]['versionTitle']}`, - categories: `${Sefaria.refCategories(currRef)}`, book: `${Sefaria.parseRef(currRef).index}` }) - } - catch(err) { - console.log(err); - } - if (rendermode === 'book-page') { - window.location = `/${firstSectionRef}?v${version.language}=${version.versionTitle.replace(/\s/g,'_')}`; - } else { - openVersionInReader(version.versionTitle, version.language); - } - Sefaria.setVersionPreference(currRef, version.versionTitle, version.language); - } - - return ( - <a className={className} - href={makeVersionLink()} - onClick={mainPanel ? openInMainPanel : openInSidebar}> - {text} - </a> - ); -} -OpenVersion.prototypes = { - version: PropTypes.object.isRequired, - currObjectVersions: PropTypes.object.isRequired, - currRef: PropTypes.string.isRequired, - className: PropTypes.string, - openVersionInSidebar: PropTypes.func, - openVersionInReader: PropTypes.func, - targetPanel: PropTypes.string.isRequired, - text: PropTypes.string, - rendermode: PropTypes.string.isRequired, - firstSectionRef: PropTypes.string, -} - -export default OpenVersion; diff --git a/static/js/VersionBlock.jsx b/static/js/VersionBlock.jsx index 573b555282..cf3a2a397e 100644 --- a/static/js/VersionBlock.jsx +++ b/static/js/VersionBlock.jsx @@ -6,9 +6,53 @@ import Util from './sefaria/util'; import $ from './sefaria/sefariaJquery'; import Component from 'react-class'; import {LoadingMessage} from "./Misc"; -import OpenVersion from "./OpenVersion"; - +import VersionBlockHeader from "./VersionBlockHeader"; +import VersionBlockSelectButton from "./VersionBlockSelectButton"; +class versionTools { + static makeVersionLink(currRef, version, currObjectVersions, mainPanel) { + if (version.merged) { + return "#"; // there's no url for a merged version + } + const withParam = mainPanel ? "" : "&with=Translation Open"; + const versionParam = mainPanel ? version.language : 'side'; + const nonSelectedVersionParams = Object.entries(currObjectVersions) + .filter(([vlang, ver]) => !!ver && !!ver?.versionTitle && !version?.merged && (withParam || vlang !== version.language)) // in 'side' case, keep all version params + .map(([vlang, ver]) => `&v${vlang}=${ver.versionTitle.replace(/\s/g,'_')}`) + .join(""); + const versionLink = nonSelectedVersionParams === "" ? null : `/${Sefaria.normRef(currRef)}${nonSelectedVersionParams}&v${versionParam}=${version.versionTitle.replace(/\s/g,'_')}${withParam}`.replace("&","?"); + return versionLink; + } + static openVersionInSidebar(currRef, version, currObjectVersions, openVersionInSidebar, e) { + e.preventDefault(); + try { + gtag("event", "onClick_version_title", {element_name: `version_title`, + change_to: `${version.versionTitle}`, change_from: `${currObjectVersions[version.language]['versionTitle']}`, + categories: `${Sefaria.refCategories(currRef)}`, book: `${Sefaria.parseRef(currRef).index}` }) + } + catch(err) { + console.log(err); + } + openVersionInSidebar(version.versionTitle, version.language); + } + static openVersionInMoinPanel(currRef, version, currObjectVersions, renderMode, firstSectionRef, openVersionInReader, e) { + e.preventDefault(); + try { + gtag("event", "onClick_select_version", {element_name: `select_version`, + change_to: `${version.versionTitle}`, change_from: `${currObjectVersions[version.language]['versionTitle']}`, + categories: `${Sefaria.refCategories(currRef)}`, book: `${Sefaria.parseRef(currRef).index}` }) + } + catch(err) { + console.log(err); + } + if (renderMode === 'book-page') { + window.location = `/${firstSectionRef}?v${version.language}=${version.versionTitle.replace(/\s/g,'_')}`; + } else { + openVersionInReader(version.versionTitle, version.language); + } + Sefaria.setVersionPreference(currRef, version.versionTitle, version.language); + } +} class VersionBlock extends Component { constructor(props) { @@ -164,6 +208,10 @@ class VersionBlock extends Component { const vtitle = this.makeVersionTitle(); const vnotes = this.makeVersionNotes(); const showLanguagLabel = this.props.rendermode == "book-page"; + const openVersionInSidebar = versionTools.openVersionInSidebar.bind(null, this.props.currentRef, this.props.version, + this.props.currObjectVersions, this.props.openVersionInSidebar); + const openVersionInMoinPanel = versionTools.openVersionInMoinPanel.bind(null, this.props.currentRef, + this.props.version, this.props.currObjectVersions, this.props.renderMode, this.props.firstSectionRef, this.props.openVersionInReader); if (this.state.editing && Sefaria.is_moderator) { // Editing View @@ -233,33 +281,24 @@ class VersionBlock extends Component { <div className="versionBlock"> <div className="versionBlockHeading"> <div className="versionTitle" role="heading"> - <OpenVersion - version={this.props.version} - currRef={this.props.currentRef} - currObjectVersions={this.props.currObjectVersions} - className={vtitle["className"]} - openVersionInSidebar={this.props.openVersionInSidebar} - openVersionInReader={this.props.openVersionInReader} - targetPanel={this.props.rendermode === 'book-page' ? 'main' : 'side'} + <VersionBlockHeader text={vtitle["text"]} - rendermode={this.props.rendermode} - firstSectionRef={this.props.firstSectionRef} + onClick={this.props.rendermode === 'book-page' ? openVersionInMoinPanel : openVersionInSidebar} + renderMode='versionTitle' + link={versionTools.makeVersionLink(this.props.currentRef, this.props.version, + this.props.currObjectVersions, this.props.rendermode === 'book-page')} /> </div> <i className={`fa fa-pencil versionEditIcon ${(Sefaria.is_moderator && this.props.rendermode == "book-page") ? "enabled" : ""}`} aria-hidden="true" onClick={this.openEditor}/> <div className="versionLanguage sans-serif">{showLanguagLabel ? Sefaria._(Sefaria.translateISOLanguageCode(v.actualLanguage)) : ""}</div> </div> <div className="versionSelect sans-serif"> - <OpenVersion - version={this.props.version} - currRef={this.props.currentRef} - currObjectVersions={this.props.currObjectVersions} - className={`selectButton ${this.props.isCurrent ? "currSelectButton" : ""}`} - openVersionInSidebar={this.props.openVersionInSidebar} - openVersionInReader={this.props.openVersionInReader} - targetPanel='main' - string={this.makeSelectVersionLanguage()} - rendermode={this.props.rendermode} + <VersionBlockSelectButton + isSelected={this.props.isCurrent} + openVersionInMoinPanel={openVersionInMoinPanel} + text={this.makeSelectVersionLanguage()} + link={versionTools.makeVersionLink(this.props.currentRef, this.props.version, + this.props.currObjectVersions, true)} /> </div> <div className={classNames(this.makeAttrClassNames({"versionNotes": 1, "sans-serif": (this.props.rendermode == "book-page")}, "versionNotes", true))}> diff --git a/static/js/VersionBlockHeader.jsx b/static/js/VersionBlockHeader.jsx new file mode 100644 index 0000000000..e0bb2f933e --- /dev/null +++ b/static/js/VersionBlockHeader.jsx @@ -0,0 +1,57 @@ +import React from 'react'; +import PropTypes from "prop-types"; + +function VersionBlockHeader({text, link, onClick, renderMode}) { + return renderMode === 'versionTitle' ? + (<VersionBlockHeaderTitle + href={link} + onClick={onClick} + versionTitle={text} + />) : + (<VersionBlockHeaderText + href={link} + onClick={onClick} + versionTitle={text} + />); +} +VersionBlockHeader.prototypes = { + onClick: PropTypes.func.isRequired, + text: PropTypes.string.isRequired, + renderMode: PropTypes.string.isRequired, + link: PropTypes.string.isRequired, +}; + +function VersionBlockHeaderTitle({link, onClick, versionTitle}) { + return ( + <a + href={link} + onClick={onClick} + > + {versionTitle} + </a> + ); +} +VersionBlockHeaderTitle.prototypes = { + onClick: PropTypes.func.isRequired, + versionTitle: PropTypes.string.isRequired, + link: PropTypes.string.isRequired, +}; + +function VersionBlockHeaderText({link, onClick, text}) { + return ( + <a + className='versionPreview' + href={link} + onClick={onClick} + > + {text} + </a> + ); +} +VersionBlockHeaderText.prototypes = { + onClick: PropTypes.func.isRequired, + versionTitle: PropTypes.string.isRequired, + link: PropTypes.string.isRequired, +}; + +export default VersionBlockHeader; diff --git a/static/js/VersionBlockSelectButton.jsx b/static/js/VersionBlockSelectButton.jsx new file mode 100644 index 0000000000..b8f8c55bea --- /dev/null +++ b/static/js/VersionBlockSelectButton.jsx @@ -0,0 +1,20 @@ +import React from 'react'; +import PropTypes from "prop-types"; + +function VersionBlockSelectButton({link, openVersionInMoinPanel, text, isSelected}) { + return ( + <a className={`selectButton ${isSelected ? "currSelectButton" : ""}`} + href={link} + onClick={openVersionInMoinPanel} + > + {text} + </a> + ); +} +VersionBlockSelectButton.prototypes = { + openVersionInMoinPanel: PropTypes.func.isRequired, + text: PropTypes.string.isRequired, + isSelected: PropTypes.bool.isRequired, + link: PropTypes.string.isRequired, +}; +export default VersionBlockSelectButton; From 0ac17b699cc9d7670f41ba5a5b40ede3b00869bd Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Tue, 21 Nov 2023 15:52:16 +0200 Subject: [PATCH 493/756] chore(Backend topic images): Add Cerberus validation --- sefaria/model/topic.py | 196 +++++++++++++++++++++++++++-------------- 1 file changed, 131 insertions(+), 65 deletions(-) diff --git a/sefaria/model/topic.py b/sefaria/model/topic.py index ffb8c93620..81ca0d9663 100644 --- a/sefaria/model/topic.py +++ b/sefaria/model/topic.py @@ -4,6 +4,7 @@ from .text import Ref, IndexSet, AbstractTextRecord from .category import Category from sefaria.system.exceptions import InputError, DuplicateRecordError +from sefaria.system.validators import validate_url from sefaria.model.timeperiod import TimePeriod, LifePeriod from sefaria.model.portal import Portal from sefaria.system.database import db @@ -11,6 +12,7 @@ from sefaria.model.place import Place import regex as re from typing import Type + logger = structlog.get_logger(__name__) @@ -41,16 +43,42 @@ class Topic(abst.SluggedAbstractMongoRecord, AbstractTitledObject): 'numSources', 'shouldDisplay', 'parasha', # name of parsha as it appears in `parshiot` collection - 'ref', # dictionary for topics with refs associated with them (e.g. parashah) containing strings `en`, `he`, and `url`. + 'ref', + # dictionary for topics with refs associated with them (e.g. parashah) containing strings `en`, `he`, and `url`. 'good_to_promote', 'description_published', # bool to keep track of which descriptions we've vetted 'isAmbiguous', # True if topic primary title can refer to multiple other topics - "data_source", #any topic edited manually should display automatically in the TOC and this flag ensures this + "data_source", # any topic edited manually should display automatically in the TOC and this flag ensures this 'image', "portal_slug", # slug to relevant Portal object ] + attr_schemas = { + "image": { + "image_uri": { + "type": "string", + "required": True, + "regex": "^https://storage\.googleapis\.com/img\.sefaria\.org/topics/.*?" + }, + "image_caption": { + "type": "dict", + "required": True, + "schema": { + "en": { + "type": "string", + "required": True + }, + "he": { + "type": "string", + "required": True + } + } + } + } + } + ROOT = "Main Menu" # the root of topic TOC is not a topic, so this is a fake slug. we know it's fake because it's not in normal form - # this constant is helpful in the topic editor tool functions in this file + + # this constant is helpful in the topic editor tool functions in this file def load(self, query, proj=None): if self.__class__ != Topic: @@ -72,11 +100,9 @@ def _validate(self): super(Topic, self)._validate() if getattr(self, 'subclass', False): assert self.subclass in self.subclass_map, f"Field `subclass` set to {self.subclass} which is not one of the valid subclass keys in `Topic.subclass_map`. Valid keys are {', '.join(self.subclass_map.keys())}" - if getattr(self, 'image', False): - assert "https://storage.googleapis.com/img.sefaria.org/topics/" in self.image["image_uri"], "The image is not stored properly. Topic should be stored in the image GCP bucket, in the topics subdirectory." - - if getattr(self, 'portal_slug', None): - Portal.validate_slug_exists(self.portal_slug) + if getattr(self, "image", False): + img_url = self.image.get("image_uri") + if img_url: validate_url(img_url) def _normalize(self): super()._normalize() @@ -136,7 +162,6 @@ def get_types(self, types=None, curr_path=None, search_slug_set=None): new_topic.get_types(types, new_path, search_slug_set) return types - def change_description(self, desc, cat_desc=None): """ Sets description in all cases and sets categoryDescription if this is a top level topic @@ -158,8 +183,9 @@ def change_description(self, desc, cat_desc=None): def topics_by_link_type_recursively(self, **kwargs): topics, _ = self.topics_and_links_by_link_type_recursively(**kwargs) return topics - - def topics_and_links_by_link_type_recursively(self, linkType='is-a', only_leaves=False, reverse=False, max_depth=None, min_sources=None): + + def topics_and_links_by_link_type_recursively(self, linkType='is-a', only_leaves=False, reverse=False, + max_depth=None, min_sources=None): """ Gets all topics linked to `self` by `linkType`. The query is recursive so it's most useful for 'is-a' and 'displays-under' linkTypes :param linkType: str, the linkType to recursively traverse. @@ -168,11 +194,15 @@ def topics_and_links_by_link_type_recursively(self, linkType='is-a', only_leaves :param max_depth: How many levels below this one to traverse. 1 returns only this node's children, 0 returns only this node and None means unlimited. :return: list(Topic) """ - topics, links, below_min_sources = self._topics_and_links_by_link_type_recursively_helper(linkType, only_leaves, reverse, max_depth, min_sources) - links = list(filter(lambda x: x.fromTopic not in below_min_sources and x.toTopic not in below_min_sources, links)) + topics, links, below_min_sources = self._topics_and_links_by_link_type_recursively_helper(linkType, only_leaves, + reverse, max_depth, + min_sources) + links = list( + filter(lambda x: x.fromTopic not in below_min_sources and x.toTopic not in below_min_sources, links)) return topics, links - def _topics_and_links_by_link_type_recursively_helper(self, linkType, only_leaves, reverse, max_depth, min_sources, explored_set=None, below_min_sources_set=None): + def _topics_and_links_by_link_type_recursively_helper(self, linkType, only_leaves, reverse, max_depth, min_sources, + explored_set=None, below_min_sources_set=None): """ Helper function for `topics_and_links_by_link_type_recursively()` to carry out recursive calls :param explored_set: set(str), set of slugs already explored. To be used in recursive calls. @@ -201,7 +231,8 @@ def _topics_and_links_by_link_type_recursively_helper(self, linkType, only_leave continue if max_depth is None or max_depth > 0: next_depth = max_depth if max_depth is None else max_depth - 1 - temp_topics, temp_links, temp_below_min_sources = child_topic._topics_and_links_by_link_type_recursively_helper(linkType, only_leaves, reverse, next_depth, min_sources, explored_set, below_min_sources_set) + temp_topics, temp_links, temp_below_min_sources = child_topic._topics_and_links_by_link_type_recursively_helper( + linkType, only_leaves, reverse, next_depth, min_sources, explored_set, below_min_sources_set) topics += temp_topics links += temp_links below_min_sources_set |= temp_below_min_sources @@ -218,7 +249,9 @@ def has_types(self, search_slug_set) -> bool: return len(search_slug_set.intersection(types)) > 0 def should_display(self) -> bool: - return getattr(self, 'shouldDisplay', True) and (getattr(self, 'numSources', 0) > 0 or self.has_description() or getattr(self, "data_source", "") == "sefaria") + return getattr(self, 'shouldDisplay', True) and ( + getattr(self, 'numSources', 0) > 0 or self.has_description() or getattr(self, "data_source", + "") == "sefaria") def has_description(self) -> bool: """ @@ -247,7 +280,6 @@ def set_slug(self, new_slug) -> None: self.save() # so that topic with this slug exists when saving links to it self.merge(old_slug) - def merge(self, other: Union['Topic', str]) -> None: """ :param other: Topic or old slug to migrate from @@ -263,13 +295,15 @@ def merge(self, other: Union['Topic', str]) -> None: # links for link in TopicLinkSetHelper.find({"$or": [{"toTopic": other_slug}, {"fromTopic": other_slug}]}): - if link.toTopic == getattr(link, 'fromTopic', None): # self-link where fromTopic and toTopic were equal before slug was changed + if link.toTopic == getattr(link, 'fromTopic', + None): # self-link where fromTopic and toTopic were equal before slug was changed link.fromTopic = self.slug link.toTopic = self.slug else: attr = 'toTopic' if link.toTopic == other_slug else 'fromTopic' setattr(link, attr, self.slug) - if getattr(link, 'fromTopic', None) == link.toTopic: # self-link where fromTopic and toTopic are equal AFTER slug was changed + if getattr(link, 'fromTopic', + None) == link.toTopic: # self-link where fromTopic and toTopic are equal AFTER slug was changed link.delete() continue try: @@ -278,16 +312,19 @@ def merge(self, other: Union['Topic', str]) -> None: link.delete() except AssertionError as e: link.delete() - logger.warning('While merging {} into {}, link assertion failed with message "{}"'.format(other_slug, self.slug, str(e))) + logger.warning( + 'While merging {} into {}, link assertion failed with message "{}"'.format(other_slug, self.slug, + str(e))) # source sheets - db.sheets.update_many({'topics.slug': other_slug}, {"$set": {'topics.$[element].slug': self.slug}}, array_filters=[{"element.slug": other_slug}]) + db.sheets.update_many({'topics.slug': other_slug}, {"$set": {'topics.$[element].slug': self.slug}}, + array_filters=[{"element.slug": other_slug}]) # indexes for index in IndexSet({"authors": other_slug}): index.authors = [self.slug if author_slug == other_slug else author_slug for author_slug in index.authors] props = index._saveable_attrs() - db.index.replace_one({"title":index.title}, props) + db.index.replace_one({"title": index.title}, props) if isinstance(other, Topic): # titles @@ -302,7 +339,12 @@ def merge(self, other: Union['Topic', str]) -> None: temp_dict = getattr(self, dict_attr, {}) for k, v in getattr(other, dict_attr, {}).items(): if k in temp_dict: - logger.warning('Key {} with value {} already exists in {} for topic {}. Current value is {}'.format(k, v, dict_attr, self.slug, temp_dict[k])) + logger.warning( + 'Key {} with value {} already exists in {} for topic {}. Current value is {}'.format(k, v, + dict_attr, + self.slug, + temp_dict[ + k])) continue temp_dict[k] = v if len(temp_dict) > 0: @@ -343,7 +385,9 @@ def contents(self, **kwargs): d = {'slug': self.slug} if mini else super(Topic, self).contents(**kwargs) d['primaryTitle'] = {} for lang in ('en', 'he'): - d['primaryTitle'][lang] = self.get_primary_title(lang=lang, with_disambiguation=kwargs.get('with_disambiguation', True)) + d['primaryTitle'][lang] = self.get_primary_title(lang=lang, + with_disambiguation=kwargs.get('with_disambiguation', + True)) if not kwargs.get("with_html"): for k, v in d.get("description", {}).items(): d["description"][k] = re.sub("<[^>]+>", "", v or "") @@ -406,6 +450,7 @@ class PersonTopic(Topic): """ Represents a topic which is a person. Not necessarily an author of a book. """ + @staticmethod def get_person_by_key(key: str): """ @@ -421,7 +466,7 @@ def annotate_place(self, d): if place and heKey not in properties: value, dataSource = place['value'], place['dataSource'] place_obj = Place().load({"key": value}) - if place_obj: + if place_obj: name = place_obj.primary_name('he') d['properties'][heKey] = {'value': name, 'dataSource': dataSource} return d @@ -444,7 +489,7 @@ def contents(self, **kwargs): } } return d - + # A person may have an era, a generation, or a specific birth and death years, which each may be approximate. # They may also have none of these... def _most_accurate_period(self, time_period_class: Type[TimePeriod]) -> Optional[LifePeriod]: @@ -484,6 +529,7 @@ def most_accurate_life_period(self): ''' return self._most_accurate_period(LifePeriod) + class AuthorTopic(PersonTopic): """ Represents a topic which is an author of a book. Can be used on the `authors` field of `Index` @@ -506,7 +552,8 @@ def _authors_indexes_fill_category(self, indexes, path, include_dependant): path_end_set = {tuple(i.categories[len(path):]) for i in indexes} for index_in_path in indexes_in_path: if tuple(index_in_path.categories[len(path):]) in path_end_set: - if index_in_path.title not in temp_index_title_set and self.slug not in set(getattr(index_in_path, 'authors', [])): + if index_in_path.title not in temp_index_title_set and self.slug not in set( + getattr(index_in_path, 'authors', [])): return False return True @@ -522,20 +569,22 @@ def aggregate_authors_indexes_by_category(self): from collections import defaultdict def index_is_commentary(index): - return getattr(index, 'base_text_titles', None) is not None and len(index.base_text_titles) > 0 and getattr(index, 'collective_title', None) is not None + return getattr(index, 'base_text_titles', None) is not None and len(index.base_text_titles) > 0 and getattr( + index, 'collective_title', None) is not None indexes = self.get_authored_indexes() - - index_or_cat_list = [] # [(index_or_cat, collective_title_term, base_category)] - cat_aggregator = defaultdict(lambda: defaultdict(list)) # of shape {(collective_title, top_cat): {(icat, category): [index_object]}} + + index_or_cat_list = [] # [(index_or_cat, collective_title_term, base_category)] + cat_aggregator = defaultdict( + lambda: defaultdict(list)) # of shape {(collective_title, top_cat): {(icat, category): [index_object]}} MAX_ICAT_FROM_END_TO_CONSIDER = 2 for index in indexes: is_comm = index_is_commentary(index) base = library.get_index(index.base_text_titles[0]) if is_comm else index collective_title = index.collective_title if is_comm else None - base_cat_path = tuple(base.categories[:-MAX_ICAT_FROM_END_TO_CONSIDER+1]) + base_cat_path = tuple(base.categories[:-MAX_ICAT_FROM_END_TO_CONSIDER + 1]) for icat in range(len(base.categories) - MAX_ICAT_FROM_END_TO_CONSIDER, len(base.categories)): - cat_aggregator[(collective_title, base_cat_path)][(icat, tuple(base.categories[:icat+1]))] += [index] + cat_aggregator[(collective_title, base_cat_path)][(icat, tuple(base.categories[:icat + 1]))] += [index] for (collective_title, _), cat_choice_dict in cat_aggregator.items(): cat_choices_sorted = sorted(cat_choice_dict.items(), key=lambda x: (len(x[1]), x[0][0]), reverse=True) (_, best_base_cat_path), temp_indexes = cat_choices_sorted[0] @@ -544,7 +593,7 @@ def index_is_commentary(index): continue if best_base_cat_path == ('Talmud', 'Bavli'): best_base_cat_path = ('Talmud',) # hard-coded to get 'Rashi on Talmud' instead of 'Rashi on Bavli' - + base_category = Category().load({"path": list(best_base_cat_path)}) if collective_title is None: index_category = base_category @@ -552,7 +601,9 @@ def index_is_commentary(index): else: index_category = Category.get_shared_category(temp_indexes) collective_title_term = Term().load({"name": collective_title}) - if index_category is None or not self._authors_indexes_fill_category(temp_indexes, index_category.path, collective_title is not None) or (collective_title is None and self._category_matches_author(index_category)): + if index_category is None or not self._authors_indexes_fill_category(temp_indexes, index_category.path, + collective_title is not None) or ( + collective_title is None and self._category_matches_author(index_category)): for temp_index in temp_indexes: index_or_cat_list += [(temp_index, None, None)] continue @@ -574,9 +625,9 @@ def get_aggregated_urls_for_authors_indexes(self) -> list: en_desc = getattr(index_or_cat, 'enShortDesc', None) he_desc = getattr(index_or_cat, 'heShortDesc', None) if isinstance(index_or_cat, Index): - unique_urls.append({"url":f'/{index_or_cat.title.replace(" ", "_")}', - "title": {"en": index_or_cat.get_title('en'), "he": index_or_cat.get_title('he')}, - "description":{"en": en_desc, "he": he_desc}}) + unique_urls.append({"url": f'/{index_or_cat.title.replace(" ", "_")}', + "title": {"en": index_or_cat.get_title('en'), "he": index_or_cat.get_title('he')}, + "description": {"en": en_desc, "he": he_desc}}) else: if collective_title_term is None: cat_term = Term().load({"name": index_or_cat.sharedTitle}) @@ -588,7 +639,7 @@ def get_aggregated_urls_for_authors_indexes(self) -> list: he_text = f'{collective_title_term.get_primary_title("he")} על {base_category_term.get_primary_title("he")}' unique_urls.append({"url": f'/texts/{"/".join(index_or_cat.path)}', "title": {"en": en_text, "he": he_text}, - "description":{"en": en_desc, "he": he_desc}}) + "description": {"en": en_desc, "he": he_desc}}) return unique_urls @staticmethod @@ -604,9 +655,10 @@ def __init__(self, query=None, *args, **kwargs): if self.recordClass != Topic: # include class name of recordClass + any class names of subclasses query = query or {} - subclass_names = [self.recordClass.__name__] + [klass.__name__ for klass in self.recordClass.all_subclasses()] + subclass_names = [self.recordClass.__name__] + [klass.__name__ for klass in + self.recordClass.all_subclasses()] query['subclass'] = {"$in": [self.recordClass.reverse_subclass_map[name] for name in subclass_names]} - + super().__init__(query=query, *args, **kwargs) @staticmethod @@ -646,7 +698,8 @@ class TopicLinkHelper(object): ] optional_attrs = [ 'generatedBy', - 'order', # dict with some data on how to sort this link. can have key 'custom_order' which should trump other data + 'order', + # dict with some data on how to sort this link. can have key 'custom_order' which should trump other data 'isJudgementCall', ] generated_by_sheets = "sheet-topic-aggregator" @@ -698,8 +751,9 @@ def _validate(self): TopicDataSource.validate_slug_exists(self.dataSource) # check for duplicates - duplicate = IntraTopicLink().load({"linkType": self.linkType, "fromTopic": self.fromTopic, "toTopic": self.toTopic, - "class": getattr(self, 'class'), "_id": {"$ne": getattr(self, "_id", None)}}) + duplicate = IntraTopicLink().load( + {"linkType": self.linkType, "fromTopic": self.fromTopic, "toTopic": self.toTopic, + "class": getattr(self, 'class'), "_id": {"$ne": getattr(self, "_id", None)}}) if duplicate is not None: raise DuplicateRecordError( "Duplicate intra topic link for linkType '{}', fromTopic '{}', toTopic '{}'".format( @@ -707,8 +761,9 @@ def _validate(self): link_type = TopicLinkType.init(self.linkType, 0) if link_type.slug == link_type.inverseSlug: - duplicate_inverse = IntraTopicLink().load({"linkType": self.linkType, "toTopic": self.fromTopic, "fromTopic": self.toTopic, - "class": getattr(self, 'class'), "_id": {"$ne": getattr(self, "_id", None)}}) + duplicate_inverse = IntraTopicLink().load( + {"linkType": self.linkType, "toTopic": self.fromTopic, "fromTopic": self.toTopic, + "class": getattr(self, 'class'), "_id": {"$ne": getattr(self, "_id", None)}}) if duplicate_inverse is not None: raise DuplicateRecordError( "Duplicate intra topic link in the inverse direction of the symmetric linkType '{}', fromTopic '{}', toTopic '{}' exists".format( @@ -718,16 +773,21 @@ def _validate(self): from_topic = Topic.init(self.fromTopic) to_topic = Topic.init(self.toTopic) if getattr(link_type, 'validFrom', False): - assert from_topic.has_types(set(link_type.validFrom)), "from topic '{}' does not have valid types '{}' for link type '{}'. Instead, types are '{}'".format(self.fromTopic, ', '.join(link_type.validFrom), self.linkType, ', '.join(from_topic.get_types())) + assert from_topic.has_types( + set(link_type.validFrom)), "from topic '{}' does not have valid types '{}' for link type '{}'. Instead, types are '{}'".format( + self.fromTopic, ', '.join(link_type.validFrom), self.linkType, ', '.join(from_topic.get_types())) if getattr(link_type, 'validTo', False): - assert to_topic.has_types(set(link_type.validTo)), "to topic '{}' does not have valid types '{}' for link type '{}'. Instead, types are '{}'".format(self.toTopic, ', '.join(link_type.validTo), self.linkType, ', '.join(to_topic.get_types())) + assert to_topic.has_types( + set(link_type.validTo)), "to topic '{}' does not have valid types '{}' for link type '{}'. Instead, types are '{}'".format( + self.toTopic, ', '.join(link_type.validTo), self.linkType, ', '.join(to_topic.get_types())) # assert this link doesn't create circular paths (in is_a link type) # should consider this test also for other non-symmetric link types such as child-of if self.linkType == TopicLinkType.isa_type: to_topic = Topic.init(self.toTopic) ancestors = to_topic.get_types() - assert self.fromTopic not in ancestors, "{} is an is-a ancestor of {} creating an illogical circle in the topics graph, here are {} ancestors: {}".format(self.fromTopic, self.toTopic, self.toTopic, ancestors) + assert self.fromTopic not in ancestors, "{} is an is-a ancestor of {} creating an illogical circle in the topics graph, here are {} ancestors: {}".format( + self.fromTopic, self.toTopic, self.toTopic, ancestors) def contents(self, **kwargs): d = super(IntraTopicLink, self).contents(**kwargs) @@ -817,12 +877,15 @@ def _validate(self): to_topic = Topic.init(self.toTopic) link_type = TopicLinkType.init(self.linkType, 0) if getattr(link_type, 'validTo', False): - assert to_topic.has_types(set(link_type.validTo)), "to topic '{}' does not have valid types '{}' for link type '{}'. Instead, types are '{}'".format(self.toTopic, ', '.join(link_type.validTo), self.linkType, ', '.join(to_topic.get_types())) - + assert to_topic.has_types( + set(link_type.validTo)), "to topic '{}' does not have valid types '{}' for link type '{}'. Instead, types are '{}'".format( + self.toTopic, ', '.join(link_type.validTo), self.linkType, ', '.join(to_topic.get_types())) + def _pre_save(self): if getattr(self, "_id", None) is None: # check for duplicates - query = {"linkType": self.linkType, "ref": self.ref, "toTopic": self.toTopic, "dataSource": getattr(self, 'dataSource', {"$exists": False}), "class": getattr(self, 'class')} + query = {"linkType": self.linkType, "ref": self.ref, "toTopic": self.toTopic, + "dataSource": getattr(self, 'dataSource', {"$exists": False}), "class": getattr(self, 'class')} if getattr(self, "charLevelData", None): query["charLevelData.startChar"] = self.charLevelData['startChar'] query["charLevelData.endChar"] = self.charLevelData['endChar'] @@ -831,8 +894,9 @@ def _pre_save(self): duplicate = RefTopicLink().load(query) if duplicate is not None: - raise DuplicateRecordError("Duplicate ref topic link for linkType '{}', ref '{}', toTopic '{}', dataSource '{}'".format( - self.linkType, self.ref, self.toTopic, getattr(self, 'dataSource', 'N/A'))) + raise DuplicateRecordError( + "Duplicate ref topic link for linkType '{}', ref '{}', toTopic '{}', dataSource '{}'".format( + self.linkType, self.ref, self.toTopic, getattr(self, 'dataSource', 'N/A'))) def contents(self, **kwargs): d = super(RefTopicLink, self).contents(**kwargs) @@ -841,6 +905,7 @@ def contents(self, **kwargs): d.pop('toTopic') return d + class TopicLinkSetHelper(object): @staticmethod def init_query(query, link_class): @@ -852,7 +917,8 @@ def init_query(query, link_class): def find(query=None, page=0, limit=0, sort=[("_id", 1)], proj=None, record_kwargs=None): from sefaria.system.database import db record_kwargs = record_kwargs or {} - raw_records = getattr(db, TopicLinkHelper.collection).find(query, proj).sort(sort).skip(page * limit).limit(limit) + raw_records = getattr(db, TopicLinkHelper.collection).find(query, proj).sort(sort).skip(page * limit).limit( + limit) return [TopicLinkHelper.init_by_class(r, **record_kwargs) for r in raw_records] @@ -952,11 +1018,13 @@ def process_index_title_change_in_topic_links(indx, **kwargs): except InputError: logger.warning("Failed to convert ref data from: {} to {}".format(kwargs['old'], kwargs['new'])) + def process_index_delete_in_topic_links(indx, **kwargs): from sefaria.model.text import prepare_index_regex_for_dependency_process pattern = prepare_index_regex_for_dependency_process(indx) RefTopicLinkSet({"ref": {"$regex": pattern}}).delete() + def process_topic_delete(topic): RefTopicLinkSet({"toTopic": topic.slug}).delete() IntraTopicLinkSet({"$or": [{"toTopic": topic.slug}, {"fromTopic": topic.slug}]}).delete() @@ -964,18 +1032,21 @@ def process_topic_delete(topic): sheet["topics"] = [t for t in sheet["topics"] if t["slug"] != topic.slug] db.sheets.save(sheet) + def process_topic_description_change(topic, **kwargs): """ Upon topic description change, get rid of old markdown links and save any new ones """ # need to delete currently existing links but dont want to delete link if its still in the description # so load up a dictionary of relevant data -> link - IntraTopicLinkSet({"toTopic": topic.slug, "linkType": "related-to", "dataSource": "learning-team-editing-tool"}).delete() + IntraTopicLinkSet( + {"toTopic": topic.slug, "linkType": "related-to", "dataSource": "learning-team-editing-tool"}).delete() refLinkType = 'popular-writing-of' if getattr(topic, 'subclass', '') == 'author' else 'about' - RefTopicLinkSet({"toTopic": topic.slug, "linkType": refLinkType, "dataSource": "learning-team-editing-tool"}).delete() + RefTopicLinkSet( + {"toTopic": topic.slug, "linkType": refLinkType, "dataSource": "learning-team-editing-tool"}).delete() markdown_links = set() - for lang, val in kwargs['new'].items(): # put each link in a set so we dont try to create duplicate of same link + for lang, val in kwargs['new'].items(): # put each link in a set so we dont try to create duplicate of same link for m in re.findall('\[.*?\]\((.*?)\)', val): markdown_links.add(m) @@ -995,12 +1066,7 @@ def process_topic_description_change(topic, **kwargs): ref = Ref(markdown_link).normal() except InputError as e: continue - ref_topic_dict = {"toTopic": topic.slug, "dataSource": "learning-team-editing-tool", "linkType": refLinkType, + ref_topic_dict = {"toTopic": topic.slug, "dataSource": "learning-team-editing-tool", + "linkType": refLinkType, 'ref': ref} RefTopicLink(ref_topic_dict).save() - - - - - - From 1dd9c8cbc37a43cfe8d35c027e5ca176ecfe5d40 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Tue, 21 Nov 2023 17:30:42 +0200 Subject: [PATCH 494/756] refactor(translations): new component VersionDetailsInformation extracted from VersionBlock. --- static/js/VersionBlock.jsx | 62 ++++++------------------- static/js/VersionDetailsInformation.jsx | 55 ++++++++++++++++++++++ 2 files changed, 68 insertions(+), 49 deletions(-) create mode 100644 static/js/VersionDetailsInformation.jsx diff --git a/static/js/VersionBlock.jsx b/static/js/VersionBlock.jsx index cf3a2a397e..06407a25ff 100644 --- a/static/js/VersionBlock.jsx +++ b/static/js/VersionBlock.jsx @@ -8,6 +8,7 @@ import Component from 'react-class'; import {LoadingMessage} from "./Misc"; import VersionBlockHeader from "./VersionBlockHeader"; import VersionBlockSelectButton from "./VersionBlockSelectButton"; +import VersionDetailsInformation from "./VersionDetailsInformation"; class versionTools { static makeVersionLink(currRef, version, currObjectVersions, mainPanel) { @@ -23,6 +24,12 @@ class versionTools { const versionLink = nonSelectedVersionParams === "" ? null : `/${Sefaria.normRef(currRef)}${nonSelectedVersionParams}&v${versionParam}=${version.versionTitle.replace(/\s/g,'_')}${withParam}`.replace("&","?"); return versionLink; } + static makeAttrClassNames(version, extraClassNames, attrToExist = null, attrIsMultilingual = false){ + if(attrIsMultilingual && Sefaria.interfaceLang != "english"){ + attrToExist = attrToExist+"In"+Sefaria.interfaceLang.toFirstCapital(); + } + return {...extraClassNames, "n-a": (attrToExist ? !version[attrToExist] : 0)}; + } static openVersionInSidebar(currRef, version, currObjectVersions, openVersionInSidebar, e) { e.preventDefault(); try { @@ -177,10 +184,6 @@ class VersionBlock extends Component { return null; } } - makeLicenseLink(){ - const license_map = Sefaria.getLicenseMap(); - return (this.props.version.license in license_map) ? license_map[this.props.version.license] : "#"; - } makeSelectVersionLanguage(){ let voc = this.props.version.isBaseText ? 'Version' : "Translation"; return this.props.isCurrent ? Sefaria._("Current " + voc) : Sefaria._("Select "+ voc); @@ -189,12 +192,6 @@ class VersionBlock extends Component { hasExtendedNotes(){ return !!(this.props.version.extendedNotes || this.props.version.extendedNotesHebrew); } - makeAttrClassNames(extraClassNames, attrToExist = null, attrIsMultilingual = false){ - if(attrIsMultilingual && Sefaria.interfaceLang != "english"){ - attrToExist = attrToExist+"In"+Sefaria.interfaceLang.toFirstCapital(); - } - return {...extraClassNames, "n-a": (attrToExist ? !this.props.version[attrToExist] : 0)} - } makeImageLink(){ return !!this.props.version.purchaseInformationURL ? this.props.version.purchaseInformationURL : this.props.version.versionSource; } @@ -301,7 +298,7 @@ class VersionBlock extends Component { this.props.currObjectVersions, true)} /> </div> - <div className={classNames(this.makeAttrClassNames({"versionNotes": 1, "sans-serif": (this.props.rendermode == "book-page")}, "versionNotes", true))}> + <div className={classNames(versionTools.makeAttrClassNames(v, {"versionNotes": 1, "sans-serif": (this.props.rendermode == "book-page")}, "versionNotes", true))}> <span className="" dangerouslySetInnerHTML={ {__html: vnotes} } /> <span className={`versionExtendedNotesLinks ${this.hasExtendedNotes() ? "": "n-a"}`}> <a onClick={this.openExtendedNotes} href={`/${this.props.version.title}/${this.props.version.language}/${this.props.version.versionTitle}/notes`}> @@ -311,44 +308,11 @@ class VersionBlock extends Component { </div> { !v.merged ? <div className="versionDetails sans-serif"> - <div className="versionDetailsInformation"> - <div className={classNames(this.makeAttrClassNames({"versionSource": 1, "versionDetailsElement": 1}, "versionSource"))}> - <span className="versionDetailsLabel"> - {`${Sefaria._("Source")}: `} - </span> - <a className="versionDetailsLink" href={v.versionSource} target="_blank"> - { Sefaria.util.parseUrl(v.versionSource).host.replace("www.", "") } - </a> - </div> - <div className={classNames(this.makeAttrClassNames({"versionDigitizedBySefaria": 1, "versionDetailsElement": 1}, "digitizedBySefaria"))}> - <span className="versionDetailsLabel"> - {`${Sefaria._("Digitization")}: `} - < /span> - <a className="versionDetailsLink" href="/digitized-by-sefaria" target="_blank"> - {Sefaria._("Sefaria")} - </a> - </div> - <div className={classNames(this.makeAttrClassNames({"versionLicense": 1, "versionDetailsElement": 1}, "license" ))}> - <span className="versionDetailsLabel"> - {`${Sefaria._("License")}: `} - </span> - <a className="versionDetailsLink" href={this.makeLicenseLink()} target="_blank"> - {Sefaria._(v?.license)} - </a> - </div> - <div className={classNames(this.makeAttrClassNames({"versionHistoryLink": 1, "versionDetailsElement": 1}, null))}> - <a className="versionDetailsLink" href={`/activity/${Sefaria.normRef(this.props.currentRef)}/${v.language}/${v.versionTitle && v.versionTitle.replace(/\s/g,"_")}`} target="_blank"> - {Sefaria._("Revision History")} - </a> - </div> - <div className={classNames(this.makeAttrClassNames({"versionBuyLink": 1, "versionDetailsElement": 1}, "purchaseInformationURL"))}> - <a className="versionDetailsLink" href={v.purchaseInformationURL} target="_blank"> - {Sefaria._("Buy in Print")} - </a> - </div> - </div> + <VersionDetailsInformation + currentRef={this.props.currentRef} version={this.props.version} + /> <div className="versionDetailsImage"> - <div className={classNames(this.makeAttrClassNames({"versionBuyImage": 1, "versionDetailsElement": 1} , "purchaseInformationImage"))}> + <div className={classNames(versionTools.makeAttrClassNames(v, {"versionBuyImage": 1, "versionDetailsElement": 1} , "purchaseInformationImage"))}> <a className="versionDetailsLink versionDetailsImageLink" href={this.makeImageLink()} target="_blank"> <img className="versionImage" src={this.makeImageSrc()} alt={Sefaria._("Buy Now")} /> </a> @@ -494,4 +458,4 @@ VersionsBlocksList.defaultProps = { -export {VersionBlock as default, VersionsBlocksList}; +export {VersionBlock as default, VersionsBlocksList, versionTools}; diff --git a/static/js/VersionDetailsInformation.jsx b/static/js/VersionDetailsInformation.jsx new file mode 100644 index 0000000000..69db372bcb --- /dev/null +++ b/static/js/VersionDetailsInformation.jsx @@ -0,0 +1,55 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import classNames from "classnames"; +import Sefaria from "./sefaria/sefaria"; +import {versionTools} from './VersionBlock'; + +function VersionDetailsInformation({currentRef, version}) { + function makeLicenseLink() { + const license_map = Sefaria.getLicenseMap(); + return (version.license in license_map) ? license_map[version.license] : "#"; + } + return ( + <div className="versionDetailsInformation"> + <div className={classNames(versionTools.makeAttrClassNames(version, {"versionSource": 1, "versionDetailsElement": 1}, "versionSource"))}> + <span className="versionDetailsLabel"> + {`${Sefaria._("Source")}: `} + </span> + <a className="versionDetailsLink" href={version.versionSource} target="_blank"> + { Sefaria.util.parseUrl(version.versionSource).host.replace("www.", "") } + </a> + </div> + <div className={classNames(versionTools.makeAttrClassNames(version, {"versionDigitizedBySefaria": 1, "versionDetailsElement": 1}, "digitizedBySefaria"))}> + <span className="versionDetailsLabel"> + {`${Sefaria._("Digitization")}: `} + < /span> + <a className="versionDetailsLink" href="/digitized-by-sefaria" target="_blank"> + {Sefaria._("Sefaria")} + </a> + </div> + <div className={classNames(versionTools.makeAttrClassNames(version, {"versionLicense": 1, "versionDetailsElement": 1}, "license" ))}> + <span className="versionDetailsLabel"> + {`${Sefaria._("License")}: `} + </span> + <a className="versionDetailsLink" href={makeLicenseLink()} target="_blank"> + {Sefaria._(version?.license)} + </a> + </div> + <div className={classNames(versionTools.makeAttrClassNames(version, {"versionHistoryLink": 1, "versionDetailsElement": 1}, null))}> + <a className="versionDetailsLink" href={`/activity/${Sefaria.normRef(currentRef)}/${version.language}/${version.versionTitle && version.versionTitle.replace(/\s/g,"_")}`} target="_blank"> + {Sefaria._("Revision History")} + </a> + </div> + <div className={classNames(versionTools.makeAttrClassNames(version, {"versionBuyLink": 1, "versionDetailsElement": 1}, "purchaseInformationURL"))}> + <a className="versionDetailsLink" href={version.purchaseInformationURL} target="_blank"> + {Sefaria._("Buy in Print")} + </a> + </div> + </div> + ); +} +VersionDetailsInformation.prototypes = { + currentRef: PropTypes.string.isRequired, + version: PropTypes.object.isRequired, +} +export default VersionDetailsInformation; From 604663f3fab491767ecea54f1625c64740b3c8fb Mon Sep 17 00:00:00 2001 From: Russel Neiss <russel.neiss@gmail.com> Date: Tue, 21 Nov 2023 15:51:39 -0600 Subject: [PATCH 495/756] fix: Fixes random text API so that it doesn't 500 if it's missing the `titles` and `categories` query param --- reader/views.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/reader/views.py b/reader/views.py index b93cc2bd1e..76696f924f 100644 --- a/reader/views.py +++ b/reader/views.py @@ -4022,8 +4022,17 @@ def random_text_api(request): """ Return Texts API data for a random ref. """ - categories = set(request.GET.get('categories', '').split('|')) - titles = set(request.GET.get('titles', '').split('|')) + + if "categories" in request.GET: + categories = set(request.GET.get('categories', '').split('|')) + else: + categories = None + + if "titles" in request.GET: + titles = set(request.GET.get('titles', '').split('|')) + else: + titles = None + response = redirect(iri_to_uri("/api/texts/" + random_ref(categories, titles)) + "?commentary=0&context=0", permanent=False) return response @@ -4575,4 +4584,3 @@ def isNodeJsReachable(): logger.warn("Failed rollout healthcheck. Healthcheck Response: {}".format(resp)) return http.JsonResponse(resp, status=statusCode) - From 5678f8fb2e9a668dca59f08f80fd5533282bcf40 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Wed, 22 Nov 2023 09:01:02 +0200 Subject: [PATCH 496/756] refactor(translation): new component VersionDetailsImage extracted from VersionBlock. --- static/js/VersionBlock.jsx | 19 +++-------------- static/js/VersionDetailsImage.jsx | 27 +++++++++++++++++++++++++ static/js/VersionDetailsInformation.jsx | 2 +- 3 files changed, 31 insertions(+), 17 deletions(-) create mode 100644 static/js/VersionDetailsImage.jsx diff --git a/static/js/VersionBlock.jsx b/static/js/VersionBlock.jsx index 06407a25ff..bd1a1bd9c7 100644 --- a/static/js/VersionBlock.jsx +++ b/static/js/VersionBlock.jsx @@ -9,6 +9,7 @@ import {LoadingMessage} from "./Misc"; import VersionBlockHeader from "./VersionBlockHeader"; import VersionBlockSelectButton from "./VersionBlockSelectButton"; import VersionDetailsInformation from "./VersionDetailsInformation"; +import VersionDetailsImage from "./VersionDetailsImage"; class versionTools { static makeVersionLink(currRef, version, currObjectVersions, mainPanel) { @@ -192,12 +193,6 @@ class VersionBlock extends Component { hasExtendedNotes(){ return !!(this.props.version.extendedNotes || this.props.version.extendedNotesHebrew); } - makeImageLink(){ - return !!this.props.version.purchaseInformationURL ? this.props.version.purchaseInformationURL : this.props.version.versionSource; - } - makeImageSrc(){ - return !!this.props.version.purchaseInformationImage ? this.props.version.purchaseInformationImage : "data:,"; - } render() { if(this.props.version.title == "Sheet") return null //why are we even getting here in such a case??; @@ -308,16 +303,8 @@ class VersionBlock extends Component { </div> { !v.merged ? <div className="versionDetails sans-serif"> - <VersionDetailsInformation - currentRef={this.props.currentRef} version={this.props.version} - /> - <div className="versionDetailsImage"> - <div className={classNames(versionTools.makeAttrClassNames(v, {"versionBuyImage": 1, "versionDetailsElement": 1} , "purchaseInformationImage"))}> - <a className="versionDetailsLink versionDetailsImageLink" href={this.makeImageLink()} target="_blank"> - <img className="versionImage" src={this.makeImageSrc()} alt={Sefaria._("Buy Now")} /> - </a> - </div> - </div> + <VersionDetailsInformation currentRef={this.props.currentRef} version={v}/> + <VersionDetailsImage version={v}/> </div> : null } </div> diff --git a/static/js/VersionDetailsImage.jsx b/static/js/VersionDetailsImage.jsx new file mode 100644 index 0000000000..d68b98e801 --- /dev/null +++ b/static/js/VersionDetailsImage.jsx @@ -0,0 +1,27 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import classNames from "classnames"; +import Sefaria from "./sefaria/sefaria"; +import {versionTools} from "./VersionBlock"; + +function VersionDetailsImage({version}) { + function makeImageLink() { + return !!version.purchaseInformationURL ? version.purchaseInformationURL : version.versionSource; + } + function makeImageSrc(){ + return !!version.purchaseInformationImage ? version.purchaseInformationImage : "data:,"; + } + return ( + <div className="versionDetailsImage"> + <div className={classNames(versionTools.makeAttrClassNames(version, {"versionBuyImage": 1, "versionDetailsElement": 1} , "purchaseInformationImage"))}> + <a className="versionDetailsLink versionDetailsImageLink" href={makeImageLink()} target="_blank"> + <img className="versionImage" src={makeImageSrc()} alt={Sefaria._("Buy Now")} /> + </a> + </div> + </div> + ) +} +VersionDetailsImage.prototypes = { + version: PropTypes.object.isRequired, +}; +export default VersionDetailsImage; diff --git a/static/js/VersionDetailsInformation.jsx b/static/js/VersionDetailsInformation.jsx index 69db372bcb..b2cba54de9 100644 --- a/static/js/VersionDetailsInformation.jsx +++ b/static/js/VersionDetailsInformation.jsx @@ -51,5 +51,5 @@ function VersionDetailsInformation({currentRef, version}) { VersionDetailsInformation.prototypes = { currentRef: PropTypes.string.isRequired, version: PropTypes.object.isRequired, -} +}; export default VersionDetailsInformation; From b5f2c4ddfa8a55ea1573d2450636ddcdc00764be Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Wed, 22 Nov 2023 10:51:04 +0200 Subject: [PATCH 497/756] chore(Backend topic images): Revert bad autoformat --- sefaria/model/topic.py | 55 +++++++++++++----------------------------- 1 file changed, 17 insertions(+), 38 deletions(-) diff --git a/sefaria/model/topic.py b/sefaria/model/topic.py index 81ca0d9663..2fcc080723 100644 --- a/sefaria/model/topic.py +++ b/sefaria/model/topic.py @@ -184,8 +184,7 @@ def topics_by_link_type_recursively(self, **kwargs): topics, _ = self.topics_and_links_by_link_type_recursively(**kwargs) return topics - def topics_and_links_by_link_type_recursively(self, linkType='is-a', only_leaves=False, reverse=False, - max_depth=None, min_sources=None): + def topics_and_links_by_link_type_recursively(self, linkType='is-a', only_leaves=False, reverse=False, max_depth=None, min_sources=None): """ Gets all topics linked to `self` by `linkType`. The query is recursive so it's most useful for 'is-a' and 'displays-under' linkTypes :param linkType: str, the linkType to recursively traverse. @@ -201,8 +200,7 @@ def topics_and_links_by_link_type_recursively(self, linkType='is-a', only_leaves filter(lambda x: x.fromTopic not in below_min_sources and x.toTopic not in below_min_sources, links)) return topics, links - def _topics_and_links_by_link_type_recursively_helper(self, linkType, only_leaves, reverse, max_depth, min_sources, - explored_set=None, below_min_sources_set=None): + def _topics_and_links_by_link_type_recursively_helper(self, linkType, only_leaves, reverse, max_depth, min_sources, explored_set=None, below_min_sources_set=None): """ Helper function for `topics_and_links_by_link_type_recursively()` to carry out recursive calls :param explored_set: set(str), set of slugs already explored. To be used in recursive calls. @@ -295,8 +293,7 @@ def merge(self, other: Union['Topic', str]) -> None: # links for link in TopicLinkSetHelper.find({"$or": [{"toTopic": other_slug}, {"fromTopic": other_slug}]}): - if link.toTopic == getattr(link, 'fromTopic', - None): # self-link where fromTopic and toTopic were equal before slug was changed + if link.toTopic == getattr(link, 'fromTopic', None): # self-link where fromTopic and toTopic were equal before slug was changed link.fromTopic = self.slug link.toTopic = self.slug else: @@ -312,9 +309,7 @@ def merge(self, other: Union['Topic', str]) -> None: link.delete() except AssertionError as e: link.delete() - logger.warning( - 'While merging {} into {}, link assertion failed with message "{}"'.format(other_slug, self.slug, - str(e))) + logger.warning('While merging {} into {}, link assertion failed with message "{}"'.format(other_slug, self.slug, str(e))) # source sheets db.sheets.update_many({'topics.slug': other_slug}, {"$set": {'topics.$[element].slug': self.slug}}, @@ -339,12 +334,7 @@ def merge(self, other: Union['Topic', str]) -> None: temp_dict = getattr(self, dict_attr, {}) for k, v in getattr(other, dict_attr, {}).items(): if k in temp_dict: - logger.warning( - 'Key {} with value {} already exists in {} for topic {}. Current value is {}'.format(k, v, - dict_attr, - self.slug, - temp_dict[ - k])) + logger.warning('Key {} with value {} already exists in {} for topic {}. Current value is {}'.format(k, v, dict_attr, self.slug, temp_dict[k])) continue temp_dict[k] = v if len(temp_dict) > 0: @@ -386,8 +376,7 @@ def contents(self, **kwargs): d['primaryTitle'] = {} for lang in ('en', 'he'): d['primaryTitle'][lang] = self.get_primary_title(lang=lang, - with_disambiguation=kwargs.get('with_disambiguation', - True)) + with_disambiguation=kwargs.get('with_disambiguation', True)) if not kwargs.get("with_html"): for k, v in d.get("description", {}).items(): d["description"][k] = re.sub("<[^>]+>", "", v or "") @@ -525,7 +514,7 @@ def most_accurate_time_period(self): def most_accurate_life_period(self): ''' - :return: most accurate period as LifePeriod. currently the only difference from TimePeriod is the way the time period is formatted as a string. + :return: most accurate period as LifePeriod. Currently, the only difference from TimePeriod is the way the time period is formatted as a string. ''' return self._most_accurate_period(LifePeriod) @@ -552,8 +541,7 @@ def _authors_indexes_fill_category(self, indexes, path, include_dependant): path_end_set = {tuple(i.categories[len(path):]) for i in indexes} for index_in_path in indexes_in_path: if tuple(index_in_path.categories[len(path):]) in path_end_set: - if index_in_path.title not in temp_index_title_set and self.slug not in set( - getattr(index_in_path, 'authors', [])): + if index_in_path.title not in temp_index_title_set and self.slug not in set(getattr(index_in_path, 'authors', [])): return False return True @@ -569,8 +557,7 @@ def aggregate_authors_indexes_by_category(self): from collections import defaultdict def index_is_commentary(index): - return getattr(index, 'base_text_titles', None) is not None and len(index.base_text_titles) > 0 and getattr( - index, 'collective_title', None) is not None + return getattr(index, 'base_text_titles', None) is not None and len(index.base_text_titles) > 0 and getattr(index, 'collective_title', None) is not None indexes = self.get_authored_indexes() @@ -601,9 +588,7 @@ def index_is_commentary(index): else: index_category = Category.get_shared_category(temp_indexes) collective_title_term = Term().load({"name": collective_title}) - if index_category is None or not self._authors_indexes_fill_category(temp_indexes, index_category.path, - collective_title is not None) or ( - collective_title is None and self._category_matches_author(index_category)): + if index_category is None or not self._authors_indexes_fill_category(temp_indexes, index_category.path, collective_title is not None) or (collective_title is None and self._category_matches_author(index_category)): for temp_index in temp_indexes: index_or_cat_list += [(temp_index, None, None)] continue @@ -774,20 +759,17 @@ def _validate(self): to_topic = Topic.init(self.toTopic) if getattr(link_type, 'validFrom', False): assert from_topic.has_types( - set(link_type.validFrom)), "from topic '{}' does not have valid types '{}' for link type '{}'. Instead, types are '{}'".format( - self.fromTopic, ', '.join(link_type.validFrom), self.linkType, ', '.join(from_topic.get_types())) + set(link_type.validFrom)), "from topic '{}' does not have valid types '{}' for link type '{}'. Instead, types are '{}'".format(self.fromTopic, ', '.join(link_type.validFrom), self.linkType, ', '.join(from_topic.get_types())) if getattr(link_type, 'validTo', False): assert to_topic.has_types( - set(link_type.validTo)), "to topic '{}' does not have valid types '{}' for link type '{}'. Instead, types are '{}'".format( - self.toTopic, ', '.join(link_type.validTo), self.linkType, ', '.join(to_topic.get_types())) + set(link_type.validTo)), "to topic '{}' does not have valid types '{}' for link type '{}'. Instead, types are '{}'".format(self.toTopic, ', '.join(link_type.validTo), self.linkType, ', '.join(to_topic.get_types())) # assert this link doesn't create circular paths (in is_a link type) # should consider this test also for other non-symmetric link types such as child-of if self.linkType == TopicLinkType.isa_type: to_topic = Topic.init(self.toTopic) ancestors = to_topic.get_types() - assert self.fromTopic not in ancestors, "{} is an is-a ancestor of {} creating an illogical circle in the topics graph, here are {} ancestors: {}".format( - self.fromTopic, self.toTopic, self.toTopic, ancestors) + assert self.fromTopic not in ancestors, "{} is an is-a ancestor of {} creating an illogical circle in the topics graph, here are {} ancestors: {}".format(self.fromTopic, self.toTopic, self.toTopic, ancestors) def contents(self, **kwargs): d = super(IntraTopicLink, self).contents(**kwargs) @@ -878,8 +860,7 @@ def _validate(self): link_type = TopicLinkType.init(self.linkType, 0) if getattr(link_type, 'validTo', False): assert to_topic.has_types( - set(link_type.validTo)), "to topic '{}' does not have valid types '{}' for link type '{}'. Instead, types are '{}'".format( - self.toTopic, ', '.join(link_type.validTo), self.linkType, ', '.join(to_topic.get_types())) + set(link_type.validTo)), "to topic '{}' does not have valid types '{}' for link type '{}'. Instead, types are '{}'".format(self.toTopic, ', '.join(link_type.validTo), self.linkType, ', '.join(to_topic.get_types())) def _pre_save(self): if getattr(self, "_id", None) is None: @@ -917,8 +898,7 @@ def init_query(query, link_class): def find(query=None, page=0, limit=0, sort=[("_id", 1)], proj=None, record_kwargs=None): from sefaria.system.database import db record_kwargs = record_kwargs or {} - raw_records = getattr(db, TopicLinkHelper.collection).find(query, proj).sort(sort).skip(page * limit).limit( - limit) + raw_records = getattr(db, TopicLinkHelper.collection).find(query, proj).sort(sort).skip(page * limit).limit(limit) return [TopicLinkHelper.init_by_class(r, **record_kwargs) for r in raw_records] @@ -1042,11 +1022,10 @@ def process_topic_description_change(topic, **kwargs): IntraTopicLinkSet( {"toTopic": topic.slug, "linkType": "related-to", "dataSource": "learning-team-editing-tool"}).delete() refLinkType = 'popular-writing-of' if getattr(topic, 'subclass', '') == 'author' else 'about' - RefTopicLinkSet( - {"toTopic": topic.slug, "linkType": refLinkType, "dataSource": "learning-team-editing-tool"}).delete() + RefTopicLinkSet({"toTopic": topic.slug, "linkType": refLinkType, "dataSource": "learning-team-editing-tool"}).delete() markdown_links = set() - for lang, val in kwargs['new'].items(): # put each link in a set so we dont try to create duplicate of same link + for lang, val in kwargs['new'].items(): # put each link in a set, so we don't try to create duplicate of same link for m in re.findall('\[.*?\]\((.*?)\)', val): markdown_links.add(m) From 41d0d778a0d93dfdcea6e03e7009e0f8f8461fe6 Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Wed, 22 Nov 2023 10:52:15 +0200 Subject: [PATCH 498/756] chore(Backend topic images): Remove script for data migration --- scripts/topic_data_migration.py | 43 --------------------------------- 1 file changed, 43 deletions(-) delete mode 100644 scripts/topic_data_migration.py diff --git a/scripts/topic_data_migration.py b/scripts/topic_data_migration.py deleted file mode 100644 index fac1c31ce3..0000000000 --- a/scripts/topic_data_migration.py +++ /dev/null @@ -1,43 +0,0 @@ -import django - -django.setup() - -from sefaria.model import * - -from sefaria.helper.topic import add_image_to_topic - -## Adding images - -hardcodedTopicImagesMap = { - 'rosh-hashanah': {'image_uri': 'https://storage.googleapis.com/img.sefaria.org/topics/rosh-hashanah.jpeg', - 'enCaption': 'Rosh Hashanah, Arthur Szyk (1894-1951) Tempera and ink on paper. New Canaan, 1948. Collection of Yeshiva University Museum. Gift of Charles Frost', - 'heCaption': 'ראש השנה, ארתור שיק, ארה״ב 1948. אוסף ישיבה יוניברסיטי'}, - - 'yom-kippur': {'image_uri': 'https://storage.googleapis.com/img.sefaria.org/topics/yom-kippur.jpeg', - 'enCaption': 'Micrography of Jonah being swallowed by the fish. Germany, 1300-1500, The British Library', - 'heCaption': 'מיקרוגרפיה של יונה בבטן הדג, מתוך ספר יונה ההפטרה של יום כיפור, 1300-1500'}, - - 'the-four-species': {'image_uri': 'https://storage.googleapis.com/img.sefaria.org/topics/the-four-species.jpg', - 'enCaption': 'Etrog container, K B, late 19th century, Germany. The Jewish Museum, Gift of Dr. Harry G. Friedman', - 'heCaption': 'תיבת אתרוג, סוף המאה ה19, גרמניה. המוזיאון היהודי בניו יורק, מתנת דר. הארי ג. פרידמן '}, - - 'sukkot': {'image_uri': 'https://storage.googleapis.com/img.sefaria.org/topics/sukkot.jpg', - 'enCaption': 'Detail of a painting of a sukkah. Image taken from f. 316v of Forli Siddur. 1383, Italian rite. The British Library', - 'heCaption': 'פרט ציור של סוכה עם שולחן פרוש ושלוש דמויות. דימוי מתוך סידור פורלי, 1383 איטליה'}, - - 'simchat-torah': {'image_uri': 'https://storage.googleapis.com/img.sefaria.org/topics/simchat-torah.jpg', - 'enCaption': 'Rosh Hashanah postcard: Hakafot, Haim Yisroel Goldberg (1888-1943) Publisher: Williamsburg Post Card Co. Germany, ca. 1915 Collection of Yeshiva University Museum', - 'heCaption': 'גלויה לראש השנה: הקפות, חיים גולדברג, גרמניה 1915, אוסף ישיבה יוניברסיטי'}, - - 'shabbat': {'image_uri': 'https://storage.googleapis.com/img.sefaria.org/topics/shabbat.jpg', - 'enCaption': 'Friday Evening, Isidor Kaufmann, Austria c. 1920. The Jewish Museum, Gift of Mr. and Mrs. M. R. Schweitzer', - 'heCaption': 'שישי בערב, איזידור קאופמן, וינה 1920. המוזיאון היהודי בניו יורק, מתנת מר וגברת מ.ר. שוויצר'}, - -} - -for topic in hardcodedTopicImagesMap: - add_image_to_topic(topic, - image_uri=hardcodedTopicImagesMap[topic]["image_uri"], - en_caption=hardcodedTopicImagesMap[topic]["enCaption"], - he_caption=hardcodedTopicImagesMap[topic]["heCaption"]) - From c023bbd0d7c74a23515a2e02263e9ce5f28f2c84 Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Wed, 22 Nov 2023 10:53:20 +0200 Subject: [PATCH 499/756] fix(Backend topic images): Prefer use of Topic.init vs load() --- sefaria/helper/topic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sefaria/helper/topic.py b/sefaria/helper/topic.py index 504fe11ade..5e73abcab7 100644 --- a/sefaria/helper/topic.py +++ b/sefaria/helper/topic.py @@ -1274,7 +1274,7 @@ def add_image_to_topic(topic_slug, image_uri, en_caption, he_caption): :param en_caption String: The English caption for a Topic image :param he_caption String: The Hebrew caption for a Topic image """ - topic = Topic().load({'slug': topic_slug}) + topic = Topic.init(topic_slug) topic.image = {"image_uri": image_uri, "image_caption": { "en": en_caption, From 7dbccb3b0138912216a40c68af6e38ec56caa10c Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Wed, 22 Nov 2023 11:02:56 +0200 Subject: [PATCH 500/756] fix(Backend topic images): Restore portal validation to topic validate --- sefaria/model/topic.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sefaria/model/topic.py b/sefaria/model/topic.py index 2fcc080723..56f07b8272 100644 --- a/sefaria/model/topic.py +++ b/sefaria/model/topic.py @@ -103,6 +103,8 @@ def _validate(self): if getattr(self, "image", False): img_url = self.image.get("image_uri") if img_url: validate_url(img_url) + if getattr(self, 'portal_slug', None): + Portal.validate_slug_exists(self.portal_slug) def _normalize(self): super()._normalize() From b8ada142360c37aee413d65395c7170dfa8e3e6f Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Wed, 22 Nov 2023 09:18:17 +0200 Subject: [PATCH 501/756] refactor(translations): move makeVersionTitle to versionTools. --- static/js/VersionBlock.jsx | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/static/js/VersionBlock.jsx b/static/js/VersionBlock.jsx index bd1a1bd9c7..0ad91e1da5 100644 --- a/static/js/VersionBlock.jsx +++ b/static/js/VersionBlock.jsx @@ -12,6 +12,15 @@ import VersionDetailsInformation from "./VersionDetailsInformation"; import VersionDetailsImage from "./VersionDetailsImage"; class versionTools { + static makeVersionTitle(version){ + if (version.merged) { + return {"className" : "", "text": Sefaria._("Merged from") + " " + Array.from(new Set(version.sources)).join(", ")}; + } else if (Sefaria.interfaceLang === "english" || !version.versionTitleInHebrew) { + return {"className" : "", "text" : version.versionTitle}; + } else { + return {"className": "he", "text": version.versionTitleInHebrew}; + } + } static makeVersionLink(currRef, version, currObjectVersions, mainPanel) { if (version.merged) { return "#"; // there's no url for a merged version @@ -164,15 +173,6 @@ class VersionBlock extends Component { e.preventDefault(); this.props.viewExtendedNotes(this.props.version.title, this.props.version.language, this.props.version.versionTitle); } - makeVersionTitle(){ - if(this.props.version.merged){ - return {"className": "", "text": Sefaria._("Merged from") + " " + Array.from(new Set(this.props.version.sources)).join(", ")}; - }else if(Sefaria.interfaceLang=="english" || !this.props.version.versionTitleInHebrew){ - return {"className":"", "text":this.props.version.versionTitle}; - }else{ - return {"className": "he", "text": this.props.version.versionTitleInHebrew}; - } - } makeVersionNotes(){ if (!this.props.showNotes) { return null; @@ -197,7 +197,7 @@ class VersionBlock extends Component { render() { if(this.props.version.title == "Sheet") return null //why are we even getting here in such a case??; const v = this.props.version; - const vtitle = this.makeVersionTitle(); + const vtitle = versionTools.makeVersionTitle(v); const vnotes = this.makeVersionNotes(); const showLanguagLabel = this.props.rendermode == "book-page"; const openVersionInSidebar = versionTools.openVersionInSidebar.bind(null, this.props.currentRef, this.props.version, From 1f53377851b3dac62dd2c33b8b3cb1f14568b49d Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Wed, 22 Nov 2023 11:07:38 +0200 Subject: [PATCH 502/756] refactor(translations): add option for different text in OpenConnectionTabButton. --- static/js/TextList.jsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/static/js/TextList.jsx b/static/js/TextList.jsx index 510d1eb44e..92c42638dc 100644 --- a/static/js/TextList.jsx +++ b/static/js/TextList.jsx @@ -312,12 +312,13 @@ const DeleteConnectionButton = ({delUrl, connectionDeleteCallback}) =>{ } -const OpenConnectionTabButton = ({srefs, openInTabCallback}) =>{ +const OpenConnectionTabButton = ({srefs, openInTabCallback, renderMode}) =>{ /* ConnectionButton composite element. Goes inside a ConnectionButtons Takes a ref(s) for opening as a link and callback for opening in-app */ const sref = Array.isArray(srefs) ? Sefaria.normRefList(srefs) : srefs; + const [en, he] = renderMode === 'versionPreview' ? ['Open Text', 'פתיחת טקסט'] : ['Open', 'פתיחה']; const openLinkInTab = (event) => { if (openInTabCallback) { event.preventDefault(); @@ -330,8 +331,8 @@ const OpenConnectionTabButton = ({srefs, openInTabCallback}) =>{ <SimpleLinkedBlock aclasses={"connection-button panel-open-link"} onClick={openLinkInTab} - en={"Open"} - he={"פתיחה"} + en={en} + he={he} url={`/${sref}`} /> ); From c816a9b658e603f7de106ccf2d6d4662b9abd766 Mon Sep 17 00:00:00 2001 From: Ron Shapiro <shapiro.rd@gmail.com> Date: Wed, 22 Nov 2023 11:40:48 +0200 Subject: [PATCH 503/756] Detect TextFamily with underscores everywhere. Fixes https://github.com/Sefaria/Sefaria-Project/issues/1724 --- reader/views.py | 10 +--------- sefaria/model/tests/chunk_test.py | 11 +++++++++++ sefaria/model/text.py | 5 +++++ 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/reader/views.py b/reader/views.py index b93cc2bd1e..455c9d0ce3 100644 --- a/reader/views.py +++ b/reader/views.py @@ -1215,7 +1215,6 @@ def edit_text(request, ref=None, lang=None, version=None): mode = "Add" else: # Pull a particular section to edit - version = version.replace("_", " ") if version else None #text = get_text(ref, lang=lang, version=version) text = TextFamily(Ref(ref), lang=lang, version=version).contents() text["mode"] = request.path.split("/")[1] @@ -1389,15 +1388,11 @@ def texts_api(request, tref): commentary = bool(int(request.GET.get("commentary", False))) pad = bool(int(request.GET.get("pad", 1))) versionEn = request.GET.get("ven", None) + versionHe = request.GET.get("vhe", None) firstAvailableRef = bool(int(request.GET.get("firstAvailableRef", False))) # use first available ref, which may not be the same as oref if firstAvailableRef: temp_oref = oref.first_available_section_ref() oref = temp_oref or oref # don't overwrite oref if first available section ref fails - if versionEn: - versionEn = versionEn.replace("_", " ") - versionHe = request.GET.get("vhe", None) - if versionHe: - versionHe = versionHe.replace("_", " ") layer_name = request.GET.get("layer", None) alts = bool(int(request.GET.get("alts", True))) wrapLinks = bool(int(request.GET.get("wrapLinks", False))) @@ -1553,9 +1548,6 @@ def social_image_api(request, tref): ref = Ref(tref) ref_str = ref.normal() if lang == "en" else ref.he_normal() - if version: - version = version.replace("_", " ") - tf = TextFamily(ref, stripItags=True, lang=lang, version=version, context=0, commentary=False).contents() he = tf["he"] if type(tf["he"]) is list else [tf["he"]] diff --git a/sefaria/model/tests/chunk_test.py b/sefaria/model/tests/chunk_test.py index 28e99bc099..b1c725c745 100644 --- a/sefaria/model/tests/chunk_test.py +++ b/sefaria/model/tests/chunk_test.py @@ -196,6 +196,17 @@ def test_text_family_alts(): c = tf.contents() assert c.get("alts") +def test_text_family_version_with_underscores(): + with_spaces = TextFamily( + Ref("Amos 1"), lang="he", lang2="en", commentary=False, + version="Miqra according to the Masorah", + version2="Tanakh: The Holy Scriptures, published by JPS") + with_underscores = TextFamily( + Ref("Amos 1"), lang="he", lang2="en", commentary=False, + version="Miqra_according_to_the_Masorah", + version2="Tanakh:_The_Holy_Scriptures,_published_by_JPS") + assert with_spaces.he == with_underscores.he + assert with_spaces.text == with_underscores.text def test_validate(): passing_refs = [ diff --git a/sefaria/model/text.py b/sefaria/model/text.py index 581b7eee93..e1d398a9e5 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -2287,6 +2287,11 @@ def __init__(self, oref, context=1, commentary=True, version=None, lang=None, elif oref.has_default_child(): oref = oref.default_child_ref() + if version: + version = version.replace("_", " ") + if version2: + version2 = version2.replace("_", " ") + self.ref = oref.normal() self.heRef = oref.he_normal() self.isComplex = oref.index.is_complex() From ea5192c8de0400ebda616d37ff633d29ea6402d9 Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Wed, 22 Nov 2023 12:14:10 +0200 Subject: [PATCH 504/756] chore(Backend topic images): revert manual fix of autoformat --- sefaria/model/topic.py | 55 +++++++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/sefaria/model/topic.py b/sefaria/model/topic.py index 56f07b8272..6508def66b 100644 --- a/sefaria/model/topic.py +++ b/sefaria/model/topic.py @@ -186,7 +186,8 @@ def topics_by_link_type_recursively(self, **kwargs): topics, _ = self.topics_and_links_by_link_type_recursively(**kwargs) return topics - def topics_and_links_by_link_type_recursively(self, linkType='is-a', only_leaves=False, reverse=False, max_depth=None, min_sources=None): + def topics_and_links_by_link_type_recursively(self, linkType='is-a', only_leaves=False, reverse=False, + max_depth=None, min_sources=None): """ Gets all topics linked to `self` by `linkType`. The query is recursive so it's most useful for 'is-a' and 'displays-under' linkTypes :param linkType: str, the linkType to recursively traverse. @@ -202,7 +203,8 @@ def topics_and_links_by_link_type_recursively(self, linkType='is-a', only_leaves filter(lambda x: x.fromTopic not in below_min_sources and x.toTopic not in below_min_sources, links)) return topics, links - def _topics_and_links_by_link_type_recursively_helper(self, linkType, only_leaves, reverse, max_depth, min_sources, explored_set=None, below_min_sources_set=None): + def _topics_and_links_by_link_type_recursively_helper(self, linkType, only_leaves, reverse, max_depth, min_sources, + explored_set=None, below_min_sources_set=None): """ Helper function for `topics_and_links_by_link_type_recursively()` to carry out recursive calls :param explored_set: set(str), set of slugs already explored. To be used in recursive calls. @@ -295,7 +297,8 @@ def merge(self, other: Union['Topic', str]) -> None: # links for link in TopicLinkSetHelper.find({"$or": [{"toTopic": other_slug}, {"fromTopic": other_slug}]}): - if link.toTopic == getattr(link, 'fromTopic', None): # self-link where fromTopic and toTopic were equal before slug was changed + if link.toTopic == getattr(link, 'fromTopic', + None): # self-link where fromTopic and toTopic were equal before slug was changed link.fromTopic = self.slug link.toTopic = self.slug else: @@ -311,7 +314,9 @@ def merge(self, other: Union['Topic', str]) -> None: link.delete() except AssertionError as e: link.delete() - logger.warning('While merging {} into {}, link assertion failed with message "{}"'.format(other_slug, self.slug, str(e))) + logger.warning( + 'While merging {} into {}, link assertion failed with message "{}"'.format(other_slug, self.slug, + str(e))) # source sheets db.sheets.update_many({'topics.slug': other_slug}, {"$set": {'topics.$[element].slug': self.slug}}, @@ -336,7 +341,12 @@ def merge(self, other: Union['Topic', str]) -> None: temp_dict = getattr(self, dict_attr, {}) for k, v in getattr(other, dict_attr, {}).items(): if k in temp_dict: - logger.warning('Key {} with value {} already exists in {} for topic {}. Current value is {}'.format(k, v, dict_attr, self.slug, temp_dict[k])) + logger.warning( + 'Key {} with value {} already exists in {} for topic {}. Current value is {}'.format(k, v, + dict_attr, + self.slug, + temp_dict[ + k])) continue temp_dict[k] = v if len(temp_dict) > 0: @@ -378,7 +388,8 @@ def contents(self, **kwargs): d['primaryTitle'] = {} for lang in ('en', 'he'): d['primaryTitle'][lang] = self.get_primary_title(lang=lang, - with_disambiguation=kwargs.get('with_disambiguation', True)) + with_disambiguation=kwargs.get('with_disambiguation', + True)) if not kwargs.get("with_html"): for k, v in d.get("description", {}).items(): d["description"][k] = re.sub("<[^>]+>", "", v or "") @@ -516,7 +527,7 @@ def most_accurate_time_period(self): def most_accurate_life_period(self): ''' - :return: most accurate period as LifePeriod. Currently, the only difference from TimePeriod is the way the time period is formatted as a string. + :return: most accurate period as LifePeriod. currently the only difference from TimePeriod is the way the time period is formatted as a string. ''' return self._most_accurate_period(LifePeriod) @@ -543,7 +554,8 @@ def _authors_indexes_fill_category(self, indexes, path, include_dependant): path_end_set = {tuple(i.categories[len(path):]) for i in indexes} for index_in_path in indexes_in_path: if tuple(index_in_path.categories[len(path):]) in path_end_set: - if index_in_path.title not in temp_index_title_set and self.slug not in set(getattr(index_in_path, 'authors', [])): + if index_in_path.title not in temp_index_title_set and self.slug not in set( + getattr(index_in_path, 'authors', [])): return False return True @@ -559,7 +571,8 @@ def aggregate_authors_indexes_by_category(self): from collections import defaultdict def index_is_commentary(index): - return getattr(index, 'base_text_titles', None) is not None and len(index.base_text_titles) > 0 and getattr(index, 'collective_title', None) is not None + return getattr(index, 'base_text_titles', None) is not None and len(index.base_text_titles) > 0 and getattr( + index, 'collective_title', None) is not None indexes = self.get_authored_indexes() @@ -590,7 +603,9 @@ def index_is_commentary(index): else: index_category = Category.get_shared_category(temp_indexes) collective_title_term = Term().load({"name": collective_title}) - if index_category is None or not self._authors_indexes_fill_category(temp_indexes, index_category.path, collective_title is not None) or (collective_title is None and self._category_matches_author(index_category)): + if index_category is None or not self._authors_indexes_fill_category(temp_indexes, index_category.path, + collective_title is not None) or ( + collective_title is None and self._category_matches_author(index_category)): for temp_index in temp_indexes: index_or_cat_list += [(temp_index, None, None)] continue @@ -761,17 +776,20 @@ def _validate(self): to_topic = Topic.init(self.toTopic) if getattr(link_type, 'validFrom', False): assert from_topic.has_types( - set(link_type.validFrom)), "from topic '{}' does not have valid types '{}' for link type '{}'. Instead, types are '{}'".format(self.fromTopic, ', '.join(link_type.validFrom), self.linkType, ', '.join(from_topic.get_types())) + set(link_type.validFrom)), "from topic '{}' does not have valid types '{}' for link type '{}'. Instead, types are '{}'".format( + self.fromTopic, ', '.join(link_type.validFrom), self.linkType, ', '.join(from_topic.get_types())) if getattr(link_type, 'validTo', False): assert to_topic.has_types( - set(link_type.validTo)), "to topic '{}' does not have valid types '{}' for link type '{}'. Instead, types are '{}'".format(self.toTopic, ', '.join(link_type.validTo), self.linkType, ', '.join(to_topic.get_types())) + set(link_type.validTo)), "to topic '{}' does not have valid types '{}' for link type '{}'. Instead, types are '{}'".format( + self.toTopic, ', '.join(link_type.validTo), self.linkType, ', '.join(to_topic.get_types())) # assert this link doesn't create circular paths (in is_a link type) # should consider this test also for other non-symmetric link types such as child-of if self.linkType == TopicLinkType.isa_type: to_topic = Topic.init(self.toTopic) ancestors = to_topic.get_types() - assert self.fromTopic not in ancestors, "{} is an is-a ancestor of {} creating an illogical circle in the topics graph, here are {} ancestors: {}".format(self.fromTopic, self.toTopic, self.toTopic, ancestors) + assert self.fromTopic not in ancestors, "{} is an is-a ancestor of {} creating an illogical circle in the topics graph, here are {} ancestors: {}".format( + self.fromTopic, self.toTopic, self.toTopic, ancestors) def contents(self, **kwargs): d = super(IntraTopicLink, self).contents(**kwargs) @@ -862,7 +880,8 @@ def _validate(self): link_type = TopicLinkType.init(self.linkType, 0) if getattr(link_type, 'validTo', False): assert to_topic.has_types( - set(link_type.validTo)), "to topic '{}' does not have valid types '{}' for link type '{}'. Instead, types are '{}'".format(self.toTopic, ', '.join(link_type.validTo), self.linkType, ', '.join(to_topic.get_types())) + set(link_type.validTo)), "to topic '{}' does not have valid types '{}' for link type '{}'. Instead, types are '{}'".format( + self.toTopic, ', '.join(link_type.validTo), self.linkType, ', '.join(to_topic.get_types())) def _pre_save(self): if getattr(self, "_id", None) is None: @@ -900,7 +919,8 @@ def init_query(query, link_class): def find(query=None, page=0, limit=0, sort=[("_id", 1)], proj=None, record_kwargs=None): from sefaria.system.database import db record_kwargs = record_kwargs or {} - raw_records = getattr(db, TopicLinkHelper.collection).find(query, proj).sort(sort).skip(page * limit).limit(limit) + raw_records = getattr(db, TopicLinkHelper.collection).find(query, proj).sort(sort).skip(page * limit).limit( + limit) return [TopicLinkHelper.init_by_class(r, **record_kwargs) for r in raw_records] @@ -1024,10 +1044,11 @@ def process_topic_description_change(topic, **kwargs): IntraTopicLinkSet( {"toTopic": topic.slug, "linkType": "related-to", "dataSource": "learning-team-editing-tool"}).delete() refLinkType = 'popular-writing-of' if getattr(topic, 'subclass', '') == 'author' else 'about' - RefTopicLinkSet({"toTopic": topic.slug, "linkType": refLinkType, "dataSource": "learning-team-editing-tool"}).delete() + RefTopicLinkSet( + {"toTopic": topic.slug, "linkType": refLinkType, "dataSource": "learning-team-editing-tool"}).delete() markdown_links = set() - for lang, val in kwargs['new'].items(): # put each link in a set, so we don't try to create duplicate of same link + for lang, val in kwargs['new'].items(): # put each link in a set so we dont try to create duplicate of same link for m in re.findall('\[.*?\]\((.*?)\)', val): markdown_links.add(m) From 2db060db5cf7187974f1e1475fc140a028ade809 Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Wed, 22 Nov 2023 12:16:53 +0200 Subject: [PATCH 505/756] chore(Backend topic images): Undo autoformat --- sefaria/model/topic.py | 191 +++++++++++++---------------------------- 1 file changed, 60 insertions(+), 131 deletions(-) diff --git a/sefaria/model/topic.py b/sefaria/model/topic.py index 6508def66b..0a211f7867 100644 --- a/sefaria/model/topic.py +++ b/sefaria/model/topic.py @@ -4,7 +4,6 @@ from .text import Ref, IndexSet, AbstractTextRecord from .category import Category from sefaria.system.exceptions import InputError, DuplicateRecordError -from sefaria.system.validators import validate_url from sefaria.model.timeperiod import TimePeriod, LifePeriod from sefaria.model.portal import Portal from sefaria.system.database import db @@ -12,7 +11,6 @@ from sefaria.model.place import Place import regex as re from typing import Type - logger = structlog.get_logger(__name__) @@ -43,42 +41,16 @@ class Topic(abst.SluggedAbstractMongoRecord, AbstractTitledObject): 'numSources', 'shouldDisplay', 'parasha', # name of parsha as it appears in `parshiot` collection - 'ref', - # dictionary for topics with refs associated with them (e.g. parashah) containing strings `en`, `he`, and `url`. + 'ref', # dictionary for topics with refs associated with them (e.g. parashah) containing strings `en`, `he`, and `url`. 'good_to_promote', 'description_published', # bool to keep track of which descriptions we've vetted 'isAmbiguous', # True if topic primary title can refer to multiple other topics - "data_source", # any topic edited manually should display automatically in the TOC and this flag ensures this + "data_source", #any topic edited manually should display automatically in the TOC and this flag ensures this 'image', "portal_slug", # slug to relevant Portal object ] - attr_schemas = { - "image": { - "image_uri": { - "type": "string", - "required": True, - "regex": "^https://storage\.googleapis\.com/img\.sefaria\.org/topics/.*?" - }, - "image_caption": { - "type": "dict", - "required": True, - "schema": { - "en": { - "type": "string", - "required": True - }, - "he": { - "type": "string", - "required": True - } - } - } - } - } - ROOT = "Main Menu" # the root of topic TOC is not a topic, so this is a fake slug. we know it's fake because it's not in normal form - - # this constant is helpful in the topic editor tool functions in this file + # this constant is helpful in the topic editor tool functions in this file def load(self, query, proj=None): if self.__class__ != Topic: @@ -100,9 +72,6 @@ def _validate(self): super(Topic, self)._validate() if getattr(self, 'subclass', False): assert self.subclass in self.subclass_map, f"Field `subclass` set to {self.subclass} which is not one of the valid subclass keys in `Topic.subclass_map`. Valid keys are {', '.join(self.subclass_map.keys())}" - if getattr(self, "image", False): - img_url = self.image.get("image_uri") - if img_url: validate_url(img_url) if getattr(self, 'portal_slug', None): Portal.validate_slug_exists(self.portal_slug) @@ -164,6 +133,7 @@ def get_types(self, types=None, curr_path=None, search_slug_set=None): new_topic.get_types(types, new_path, search_slug_set) return types + def change_description(self, desc, cat_desc=None): """ Sets description in all cases and sets categoryDescription if this is a top level topic @@ -185,9 +155,8 @@ def change_description(self, desc, cat_desc=None): def topics_by_link_type_recursively(self, **kwargs): topics, _ = self.topics_and_links_by_link_type_recursively(**kwargs) return topics - - def topics_and_links_by_link_type_recursively(self, linkType='is-a', only_leaves=False, reverse=False, - max_depth=None, min_sources=None): + + def topics_and_links_by_link_type_recursively(self, linkType='is-a', only_leaves=False, reverse=False, max_depth=None, min_sources=None): """ Gets all topics linked to `self` by `linkType`. The query is recursive so it's most useful for 'is-a' and 'displays-under' linkTypes :param linkType: str, the linkType to recursively traverse. @@ -196,15 +165,11 @@ def topics_and_links_by_link_type_recursively(self, linkType='is-a', only_leaves :param max_depth: How many levels below this one to traverse. 1 returns only this node's children, 0 returns only this node and None means unlimited. :return: list(Topic) """ - topics, links, below_min_sources = self._topics_and_links_by_link_type_recursively_helper(linkType, only_leaves, - reverse, max_depth, - min_sources) - links = list( - filter(lambda x: x.fromTopic not in below_min_sources and x.toTopic not in below_min_sources, links)) + topics, links, below_min_sources = self._topics_and_links_by_link_type_recursively_helper(linkType, only_leaves, reverse, max_depth, min_sources) + links = list(filter(lambda x: x.fromTopic not in below_min_sources and x.toTopic not in below_min_sources, links)) return topics, links - def _topics_and_links_by_link_type_recursively_helper(self, linkType, only_leaves, reverse, max_depth, min_sources, - explored_set=None, below_min_sources_set=None): + def _topics_and_links_by_link_type_recursively_helper(self, linkType, only_leaves, reverse, max_depth, min_sources, explored_set=None, below_min_sources_set=None): """ Helper function for `topics_and_links_by_link_type_recursively()` to carry out recursive calls :param explored_set: set(str), set of slugs already explored. To be used in recursive calls. @@ -233,8 +198,7 @@ def _topics_and_links_by_link_type_recursively_helper(self, linkType, only_leave continue if max_depth is None or max_depth > 0: next_depth = max_depth if max_depth is None else max_depth - 1 - temp_topics, temp_links, temp_below_min_sources = child_topic._topics_and_links_by_link_type_recursively_helper( - linkType, only_leaves, reverse, next_depth, min_sources, explored_set, below_min_sources_set) + temp_topics, temp_links, temp_below_min_sources = child_topic._topics_and_links_by_link_type_recursively_helper(linkType, only_leaves, reverse, next_depth, min_sources, explored_set, below_min_sources_set) topics += temp_topics links += temp_links below_min_sources_set |= temp_below_min_sources @@ -251,9 +215,7 @@ def has_types(self, search_slug_set) -> bool: return len(search_slug_set.intersection(types)) > 0 def should_display(self) -> bool: - return getattr(self, 'shouldDisplay', True) and ( - getattr(self, 'numSources', 0) > 0 or self.has_description() or getattr(self, "data_source", - "") == "sefaria") + return getattr(self, 'shouldDisplay', True) and (getattr(self, 'numSources', 0) > 0 or self.has_description() or getattr(self, "data_source", "") == "sefaria") def has_description(self) -> bool: """ @@ -282,6 +244,7 @@ def set_slug(self, new_slug) -> None: self.save() # so that topic with this slug exists when saving links to it self.merge(old_slug) + def merge(self, other: Union['Topic', str]) -> None: """ :param other: Topic or old slug to migrate from @@ -297,15 +260,13 @@ def merge(self, other: Union['Topic', str]) -> None: # links for link in TopicLinkSetHelper.find({"$or": [{"toTopic": other_slug}, {"fromTopic": other_slug}]}): - if link.toTopic == getattr(link, 'fromTopic', - None): # self-link where fromTopic and toTopic were equal before slug was changed + if link.toTopic == getattr(link, 'fromTopic', None): # self-link where fromTopic and toTopic were equal before slug was changed link.fromTopic = self.slug link.toTopic = self.slug else: attr = 'toTopic' if link.toTopic == other_slug else 'fromTopic' setattr(link, attr, self.slug) - if getattr(link, 'fromTopic', - None) == link.toTopic: # self-link where fromTopic and toTopic are equal AFTER slug was changed + if getattr(link, 'fromTopic', None) == link.toTopic: # self-link where fromTopic and toTopic are equal AFTER slug was changed link.delete() continue try: @@ -314,19 +275,16 @@ def merge(self, other: Union['Topic', str]) -> None: link.delete() except AssertionError as e: link.delete() - logger.warning( - 'While merging {} into {}, link assertion failed with message "{}"'.format(other_slug, self.slug, - str(e))) + logger.warning('While merging {} into {}, link assertion failed with message "{}"'.format(other_slug, self.slug, str(e))) # source sheets - db.sheets.update_many({'topics.slug': other_slug}, {"$set": {'topics.$[element].slug': self.slug}}, - array_filters=[{"element.slug": other_slug}]) + db.sheets.update_many({'topics.slug': other_slug}, {"$set": {'topics.$[element].slug': self.slug}}, array_filters=[{"element.slug": other_slug}]) # indexes for index in IndexSet({"authors": other_slug}): index.authors = [self.slug if author_slug == other_slug else author_slug for author_slug in index.authors] props = index._saveable_attrs() - db.index.replace_one({"title": index.title}, props) + db.index.replace_one({"title":index.title}, props) if isinstance(other, Topic): # titles @@ -341,12 +299,7 @@ def merge(self, other: Union['Topic', str]) -> None: temp_dict = getattr(self, dict_attr, {}) for k, v in getattr(other, dict_attr, {}).items(): if k in temp_dict: - logger.warning( - 'Key {} with value {} already exists in {} for topic {}. Current value is {}'.format(k, v, - dict_attr, - self.slug, - temp_dict[ - k])) + logger.warning('Key {} with value {} already exists in {} for topic {}. Current value is {}'.format(k, v, dict_attr, self.slug, temp_dict[k])) continue temp_dict[k] = v if len(temp_dict) > 0: @@ -387,9 +340,7 @@ def contents(self, **kwargs): d = {'slug': self.slug} if mini else super(Topic, self).contents(**kwargs) d['primaryTitle'] = {} for lang in ('en', 'he'): - d['primaryTitle'][lang] = self.get_primary_title(lang=lang, - with_disambiguation=kwargs.get('with_disambiguation', - True)) + d['primaryTitle'][lang] = self.get_primary_title(lang=lang, with_disambiguation=kwargs.get('with_disambiguation', True)) if not kwargs.get("with_html"): for k, v in d.get("description", {}).items(): d["description"][k] = re.sub("<[^>]+>", "", v or "") @@ -452,7 +403,6 @@ class PersonTopic(Topic): """ Represents a topic which is a person. Not necessarily an author of a book. """ - @staticmethod def get_person_by_key(key: str): """ @@ -468,7 +418,7 @@ def annotate_place(self, d): if place and heKey not in properties: value, dataSource = place['value'], place['dataSource'] place_obj = Place().load({"key": value}) - if place_obj: + if place_obj: name = place_obj.primary_name('he') d['properties'][heKey] = {'value': name, 'dataSource': dataSource} return d @@ -491,7 +441,7 @@ def contents(self, **kwargs): } } return d - + # A person may have an era, a generation, or a specific birth and death years, which each may be approximate. # They may also have none of these... def _most_accurate_period(self, time_period_class: Type[TimePeriod]) -> Optional[LifePeriod]: @@ -531,7 +481,6 @@ def most_accurate_life_period(self): ''' return self._most_accurate_period(LifePeriod) - class AuthorTopic(PersonTopic): """ Represents a topic which is an author of a book. Can be used on the `authors` field of `Index` @@ -554,8 +503,7 @@ def _authors_indexes_fill_category(self, indexes, path, include_dependant): path_end_set = {tuple(i.categories[len(path):]) for i in indexes} for index_in_path in indexes_in_path: if tuple(index_in_path.categories[len(path):]) in path_end_set: - if index_in_path.title not in temp_index_title_set and self.slug not in set( - getattr(index_in_path, 'authors', [])): + if index_in_path.title not in temp_index_title_set and self.slug not in set(getattr(index_in_path, 'authors', [])): return False return True @@ -571,22 +519,20 @@ def aggregate_authors_indexes_by_category(self): from collections import defaultdict def index_is_commentary(index): - return getattr(index, 'base_text_titles', None) is not None and len(index.base_text_titles) > 0 and getattr( - index, 'collective_title', None) is not None + return getattr(index, 'base_text_titles', None) is not None and len(index.base_text_titles) > 0 and getattr(index, 'collective_title', None) is not None indexes = self.get_authored_indexes() - - index_or_cat_list = [] # [(index_or_cat, collective_title_term, base_category)] - cat_aggregator = defaultdict( - lambda: defaultdict(list)) # of shape {(collective_title, top_cat): {(icat, category): [index_object]}} + + index_or_cat_list = [] # [(index_or_cat, collective_title_term, base_category)] + cat_aggregator = defaultdict(lambda: defaultdict(list)) # of shape {(collective_title, top_cat): {(icat, category): [index_object]}} MAX_ICAT_FROM_END_TO_CONSIDER = 2 for index in indexes: is_comm = index_is_commentary(index) base = library.get_index(index.base_text_titles[0]) if is_comm else index collective_title = index.collective_title if is_comm else None - base_cat_path = tuple(base.categories[:-MAX_ICAT_FROM_END_TO_CONSIDER + 1]) + base_cat_path = tuple(base.categories[:-MAX_ICAT_FROM_END_TO_CONSIDER+1]) for icat in range(len(base.categories) - MAX_ICAT_FROM_END_TO_CONSIDER, len(base.categories)): - cat_aggregator[(collective_title, base_cat_path)][(icat, tuple(base.categories[:icat + 1]))] += [index] + cat_aggregator[(collective_title, base_cat_path)][(icat, tuple(base.categories[:icat+1]))] += [index] for (collective_title, _), cat_choice_dict in cat_aggregator.items(): cat_choices_sorted = sorted(cat_choice_dict.items(), key=lambda x: (len(x[1]), x[0][0]), reverse=True) (_, best_base_cat_path), temp_indexes = cat_choices_sorted[0] @@ -595,7 +541,7 @@ def index_is_commentary(index): continue if best_base_cat_path == ('Talmud', 'Bavli'): best_base_cat_path = ('Talmud',) # hard-coded to get 'Rashi on Talmud' instead of 'Rashi on Bavli' - + base_category = Category().load({"path": list(best_base_cat_path)}) if collective_title is None: index_category = base_category @@ -603,9 +549,7 @@ def index_is_commentary(index): else: index_category = Category.get_shared_category(temp_indexes) collective_title_term = Term().load({"name": collective_title}) - if index_category is None or not self._authors_indexes_fill_category(temp_indexes, index_category.path, - collective_title is not None) or ( - collective_title is None and self._category_matches_author(index_category)): + if index_category is None or not self._authors_indexes_fill_category(temp_indexes, index_category.path, collective_title is not None) or (collective_title is None and self._category_matches_author(index_category)): for temp_index in temp_indexes: index_or_cat_list += [(temp_index, None, None)] continue @@ -627,9 +571,9 @@ def get_aggregated_urls_for_authors_indexes(self) -> list: en_desc = getattr(index_or_cat, 'enShortDesc', None) he_desc = getattr(index_or_cat, 'heShortDesc', None) if isinstance(index_or_cat, Index): - unique_urls.append({"url": f'/{index_or_cat.title.replace(" ", "_")}', - "title": {"en": index_or_cat.get_title('en'), "he": index_or_cat.get_title('he')}, - "description": {"en": en_desc, "he": he_desc}}) + unique_urls.append({"url":f'/{index_or_cat.title.replace(" ", "_")}', + "title": {"en": index_or_cat.get_title('en'), "he": index_or_cat.get_title('he')}, + "description":{"en": en_desc, "he": he_desc}}) else: if collective_title_term is None: cat_term = Term().load({"name": index_or_cat.sharedTitle}) @@ -641,7 +585,7 @@ def get_aggregated_urls_for_authors_indexes(self) -> list: he_text = f'{collective_title_term.get_primary_title("he")} על {base_category_term.get_primary_title("he")}' unique_urls.append({"url": f'/texts/{"/".join(index_or_cat.path)}', "title": {"en": en_text, "he": he_text}, - "description": {"en": en_desc, "he": he_desc}}) + "description":{"en": en_desc, "he": he_desc}}) return unique_urls @staticmethod @@ -657,10 +601,9 @@ def __init__(self, query=None, *args, **kwargs): if self.recordClass != Topic: # include class name of recordClass + any class names of subclasses query = query or {} - subclass_names = [self.recordClass.__name__] + [klass.__name__ for klass in - self.recordClass.all_subclasses()] + subclass_names = [self.recordClass.__name__] + [klass.__name__ for klass in self.recordClass.all_subclasses()] query['subclass'] = {"$in": [self.recordClass.reverse_subclass_map[name] for name in subclass_names]} - + super().__init__(query=query, *args, **kwargs) @staticmethod @@ -700,8 +643,7 @@ class TopicLinkHelper(object): ] optional_attrs = [ 'generatedBy', - 'order', - # dict with some data on how to sort this link. can have key 'custom_order' which should trump other data + 'order', # dict with some data on how to sort this link. can have key 'custom_order' which should trump other data 'isJudgementCall', ] generated_by_sheets = "sheet-topic-aggregator" @@ -753,9 +695,8 @@ def _validate(self): TopicDataSource.validate_slug_exists(self.dataSource) # check for duplicates - duplicate = IntraTopicLink().load( - {"linkType": self.linkType, "fromTopic": self.fromTopic, "toTopic": self.toTopic, - "class": getattr(self, 'class'), "_id": {"$ne": getattr(self, "_id", None)}}) + duplicate = IntraTopicLink().load({"linkType": self.linkType, "fromTopic": self.fromTopic, "toTopic": self.toTopic, + "class": getattr(self, 'class'), "_id": {"$ne": getattr(self, "_id", None)}}) if duplicate is not None: raise DuplicateRecordError( "Duplicate intra topic link for linkType '{}', fromTopic '{}', toTopic '{}'".format( @@ -763,9 +704,8 @@ def _validate(self): link_type = TopicLinkType.init(self.linkType, 0) if link_type.slug == link_type.inverseSlug: - duplicate_inverse = IntraTopicLink().load( - {"linkType": self.linkType, "toTopic": self.fromTopic, "fromTopic": self.toTopic, - "class": getattr(self, 'class'), "_id": {"$ne": getattr(self, "_id", None)}}) + duplicate_inverse = IntraTopicLink().load({"linkType": self.linkType, "toTopic": self.fromTopic, "fromTopic": self.toTopic, + "class": getattr(self, 'class'), "_id": {"$ne": getattr(self, "_id", None)}}) if duplicate_inverse is not None: raise DuplicateRecordError( "Duplicate intra topic link in the inverse direction of the symmetric linkType '{}', fromTopic '{}', toTopic '{}' exists".format( @@ -775,21 +715,16 @@ def _validate(self): from_topic = Topic.init(self.fromTopic) to_topic = Topic.init(self.toTopic) if getattr(link_type, 'validFrom', False): - assert from_topic.has_types( - set(link_type.validFrom)), "from topic '{}' does not have valid types '{}' for link type '{}'. Instead, types are '{}'".format( - self.fromTopic, ', '.join(link_type.validFrom), self.linkType, ', '.join(from_topic.get_types())) + assert from_topic.has_types(set(link_type.validFrom)), "from topic '{}' does not have valid types '{}' for link type '{}'. Instead, types are '{}'".format(self.fromTopic, ', '.join(link_type.validFrom), self.linkType, ', '.join(from_topic.get_types())) if getattr(link_type, 'validTo', False): - assert to_topic.has_types( - set(link_type.validTo)), "to topic '{}' does not have valid types '{}' for link type '{}'. Instead, types are '{}'".format( - self.toTopic, ', '.join(link_type.validTo), self.linkType, ', '.join(to_topic.get_types())) + assert to_topic.has_types(set(link_type.validTo)), "to topic '{}' does not have valid types '{}' for link type '{}'. Instead, types are '{}'".format(self.toTopic, ', '.join(link_type.validTo), self.linkType, ', '.join(to_topic.get_types())) # assert this link doesn't create circular paths (in is_a link type) # should consider this test also for other non-symmetric link types such as child-of if self.linkType == TopicLinkType.isa_type: to_topic = Topic.init(self.toTopic) ancestors = to_topic.get_types() - assert self.fromTopic not in ancestors, "{} is an is-a ancestor of {} creating an illogical circle in the topics graph, here are {} ancestors: {}".format( - self.fromTopic, self.toTopic, self.toTopic, ancestors) + assert self.fromTopic not in ancestors, "{} is an is-a ancestor of {} creating an illogical circle in the topics graph, here are {} ancestors: {}".format(self.fromTopic, self.toTopic, self.toTopic, ancestors) def contents(self, **kwargs): d = super(IntraTopicLink, self).contents(**kwargs) @@ -879,15 +814,12 @@ def _validate(self): to_topic = Topic.init(self.toTopic) link_type = TopicLinkType.init(self.linkType, 0) if getattr(link_type, 'validTo', False): - assert to_topic.has_types( - set(link_type.validTo)), "to topic '{}' does not have valid types '{}' for link type '{}'. Instead, types are '{}'".format( - self.toTopic, ', '.join(link_type.validTo), self.linkType, ', '.join(to_topic.get_types())) - + assert to_topic.has_types(set(link_type.validTo)), "to topic '{}' does not have valid types '{}' for link type '{}'. Instead, types are '{}'".format(self.toTopic, ', '.join(link_type.validTo), self.linkType, ', '.join(to_topic.get_types())) + def _pre_save(self): if getattr(self, "_id", None) is None: # check for duplicates - query = {"linkType": self.linkType, "ref": self.ref, "toTopic": self.toTopic, - "dataSource": getattr(self, 'dataSource', {"$exists": False}), "class": getattr(self, 'class')} + query = {"linkType": self.linkType, "ref": self.ref, "toTopic": self.toTopic, "dataSource": getattr(self, 'dataSource', {"$exists": False}), "class": getattr(self, 'class')} if getattr(self, "charLevelData", None): query["charLevelData.startChar"] = self.charLevelData['startChar'] query["charLevelData.endChar"] = self.charLevelData['endChar'] @@ -896,9 +828,8 @@ def _pre_save(self): duplicate = RefTopicLink().load(query) if duplicate is not None: - raise DuplicateRecordError( - "Duplicate ref topic link for linkType '{}', ref '{}', toTopic '{}', dataSource '{}'".format( - self.linkType, self.ref, self.toTopic, getattr(self, 'dataSource', 'N/A'))) + raise DuplicateRecordError("Duplicate ref topic link for linkType '{}', ref '{}', toTopic '{}', dataSource '{}'".format( + self.linkType, self.ref, self.toTopic, getattr(self, 'dataSource', 'N/A'))) def contents(self, **kwargs): d = super(RefTopicLink, self).contents(**kwargs) @@ -907,7 +838,6 @@ def contents(self, **kwargs): d.pop('toTopic') return d - class TopicLinkSetHelper(object): @staticmethod def init_query(query, link_class): @@ -919,8 +849,7 @@ def init_query(query, link_class): def find(query=None, page=0, limit=0, sort=[("_id", 1)], proj=None, record_kwargs=None): from sefaria.system.database import db record_kwargs = record_kwargs or {} - raw_records = getattr(db, TopicLinkHelper.collection).find(query, proj).sort(sort).skip(page * limit).limit( - limit) + raw_records = getattr(db, TopicLinkHelper.collection).find(query, proj).sort(sort).skip(page * limit).limit(limit) return [TopicLinkHelper.init_by_class(r, **record_kwargs) for r in raw_records] @@ -1020,13 +949,11 @@ def process_index_title_change_in_topic_links(indx, **kwargs): except InputError: logger.warning("Failed to convert ref data from: {} to {}".format(kwargs['old'], kwargs['new'])) - def process_index_delete_in_topic_links(indx, **kwargs): from sefaria.model.text import prepare_index_regex_for_dependency_process pattern = prepare_index_regex_for_dependency_process(indx) RefTopicLinkSet({"ref": {"$regex": pattern}}).delete() - def process_topic_delete(topic): RefTopicLinkSet({"toTopic": topic.slug}).delete() IntraTopicLinkSet({"$or": [{"toTopic": topic.slug}, {"fromTopic": topic.slug}]}).delete() @@ -1034,21 +961,18 @@ def process_topic_delete(topic): sheet["topics"] = [t for t in sheet["topics"] if t["slug"] != topic.slug] db.sheets.save(sheet) - def process_topic_description_change(topic, **kwargs): """ Upon topic description change, get rid of old markdown links and save any new ones """ # need to delete currently existing links but dont want to delete link if its still in the description # so load up a dictionary of relevant data -> link - IntraTopicLinkSet( - {"toTopic": topic.slug, "linkType": "related-to", "dataSource": "learning-team-editing-tool"}).delete() + IntraTopicLinkSet({"toTopic": topic.slug, "linkType": "related-to", "dataSource": "learning-team-editing-tool"}).delete() refLinkType = 'popular-writing-of' if getattr(topic, 'subclass', '') == 'author' else 'about' - RefTopicLinkSet( - {"toTopic": topic.slug, "linkType": refLinkType, "dataSource": "learning-team-editing-tool"}).delete() + RefTopicLinkSet({"toTopic": topic.slug, "linkType": refLinkType, "dataSource": "learning-team-editing-tool"}).delete() markdown_links = set() - for lang, val in kwargs['new'].items(): # put each link in a set so we dont try to create duplicate of same link + for lang, val in kwargs['new'].items(): # put each link in a set so we dont try to create duplicate of same link for m in re.findall('\[.*?\]\((.*?)\)', val): markdown_links.add(m) @@ -1068,7 +992,12 @@ def process_topic_description_change(topic, **kwargs): ref = Ref(markdown_link).normal() except InputError as e: continue - ref_topic_dict = {"toTopic": topic.slug, "dataSource": "learning-team-editing-tool", - "linkType": refLinkType, + ref_topic_dict = {"toTopic": topic.slug, "dataSource": "learning-team-editing-tool", "linkType": refLinkType, 'ref': ref} RefTopicLink(ref_topic_dict).save() + + + + + + From 90b45085b13b4895e4e24a07fd438dd99e538f4a Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Wed, 22 Nov 2023 12:19:04 +0200 Subject: [PATCH 506/756] chore(Backend topic images): Restore validation code with proper format --- sefaria/model/topic.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/sefaria/model/topic.py b/sefaria/model/topic.py index 0a211f7867..2304a7a5b4 100644 --- a/sefaria/model/topic.py +++ b/sefaria/model/topic.py @@ -5,6 +5,7 @@ from .category import Category from sefaria.system.exceptions import InputError, DuplicateRecordError from sefaria.model.timeperiod import TimePeriod, LifePeriod +from sefaria.system.validators import validate_url from sefaria.model.portal import Portal from sefaria.system.database import db import structlog, bleach @@ -49,6 +50,31 @@ class Topic(abst.SluggedAbstractMongoRecord, AbstractTitledObject): 'image', "portal_slug", # slug to relevant Portal object ] + + attr_schemas = { + "image": { + "image_uri": { + "type": "string", + "required": True, + "regex": "^https://storage\.googleapis\.com/img\.sefaria\.org/topics/.*?" + }, + "image_caption": { + "type": "dict", + "required": True, + "schema": { + "en": { + "type": "string", + "required": True + }, + "he": { + "type": "string", + "required": True + } + } + } + } + } + ROOT = "Main Menu" # the root of topic TOC is not a topic, so this is a fake slug. we know it's fake because it's not in normal form # this constant is helpful in the topic editor tool functions in this file @@ -74,6 +100,9 @@ def _validate(self): assert self.subclass in self.subclass_map, f"Field `subclass` set to {self.subclass} which is not one of the valid subclass keys in `Topic.subclass_map`. Valid keys are {', '.join(self.subclass_map.keys())}" if getattr(self, 'portal_slug', None): Portal.validate_slug_exists(self.portal_slug) + if getattr(self, "image", False): + img_url = self.image.get("image_uri") + if img_url: validate_url(img_url) def _normalize(self): super()._normalize() From 1fdc6ab858d8fd3cb4b5ddd8fd77af88cb2c28af Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Wed, 22 Nov 2023 12:22:52 +0200 Subject: [PATCH 507/756] fix(Backend topic images): Create new migration subdirectory, add script for migration --- scripts/migrations/add_topic_images.py | 40 ++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 scripts/migrations/add_topic_images.py diff --git a/scripts/migrations/add_topic_images.py b/scripts/migrations/add_topic_images.py new file mode 100644 index 0000000000..ed42ba96dd --- /dev/null +++ b/scripts/migrations/add_topic_images.py @@ -0,0 +1,40 @@ +import django + +django.setup() + +from sefaria.helper.topic import add_image_to_topic + +## Adding images + +hardcodedTopicImagesMap = { + 'rosh-hashanah': {'image_uri': 'https://storage.googleapis.com/img.sefaria.org/topics/rosh-hashanah.jpeg', + 'enCaption': 'Rosh Hashanah, Arthur Szyk (1894-1951) Tempera and ink on paper. New Canaan, 1948. Collection of Yeshiva University Museum. Gift of Charles Frost', + 'heCaption': 'ראש השנה, ארתור שיק, ארה״ב 1948. אוסף ישיבה יוניברסיטי'}, + + 'yom-kippur': {'image_uri': 'https://storage.googleapis.com/img.sefaria.org/topics/yom-kippur.jpeg', + 'enCaption': 'Micrography of Jonah being swallowed by the fish. Germany, 1300-1500, The British Library', + 'heCaption': 'מיקרוגרפיה של יונה בבטן הדג, מתוך ספר יונה ההפטרה של יום כיפור, 1300-1500'}, + + 'the-four-species': {'image_uri': 'https://storage.googleapis.com/img.sefaria.org/topics/the-four-species.jpg', + 'enCaption': 'Etrog container, K B, late 19th century, Germany. The Jewish Museum, Gift of Dr. Harry G. Friedman', + 'heCaption': 'תיבת אתרוג, סוף המאה ה19, גרמניה. המוזיאון היהודי בניו יורק, מתנת דר. הארי ג. פרידמן '}, + + 'sukkot': {'image_uri': 'https://storage.googleapis.com/img.sefaria.org/topics/sukkot.jpg', + 'enCaption': 'Detail of a painting of a sukkah. Image taken from f. 316v of Forli Siddur. 1383, Italian rite. The British Library', + 'heCaption': 'פרט ציור של סוכה עם שולחן פרוש ושלוש דמויות. דימוי מתוך סידור פורלי, 1383 איטליה'}, + + 'simchat-torah': {'image_uri': 'https://storage.googleapis.com/img.sefaria.org/topics/simchat-torah.jpg', + 'enCaption': 'Rosh Hashanah postcard: Hakafot, Haim Yisroel Goldberg (1888-1943) Publisher: Williamsburg Post Card Co. Germany, ca. 1915 Collection of Yeshiva University Museum', + 'heCaption': 'גלויה לראש השנה: הקפות, חיים גולדברג, גרמניה 1915, אוסף ישיבה יוניברסיטי'}, + + 'shabbat': {'image_uri': 'https://storage.googleapis.com/img.sefaria.org/topics/shabbat.jpg', + 'enCaption': 'Friday Evening, Isidor Kaufmann, Austria c. 1920. The Jewish Museum, Gift of Mr. and Mrs. M. R. Schweitzer', + 'heCaption': 'שישי בערב, איזידור קאופמן, וינה 1920. המוזיאון היהודי בניו יורק, מתנת מר וגברת מ.ר. שוויצר'}, + +} + +for topic in hardcodedTopicImagesMap: + add_image_to_topic(topic, + image_uri=hardcodedTopicImagesMap[topic]["image_uri"], + en_caption=hardcodedTopicImagesMap[topic]["enCaption"], + he_caption=hardcodedTopicImagesMap[topic]["heCaption"]) \ No newline at end of file From 1bf6cf89906cf77abfdbf077df8a478e82d7f06d Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Wed, 22 Nov 2023 13:15:52 +0200 Subject: [PATCH 508/756] feat(Topic Editor): can add pictures with captions --- reader/views.py | 25 ++++++++----------------- sefaria/helper/topic.py | 3 +++ static/js/AdminEditor.jsx | 13 ++++--------- static/js/Misc.jsx | 37 +++++++++++++++++-------------------- static/js/TopicEditor.jsx | 22 +++++++++++++++++----- 5 files changed, 49 insertions(+), 51 deletions(-) diff --git a/reader/views.py b/reader/views.py index 951667996d..086eed3a7a 100644 --- a/reader/views.py +++ b/reader/views.py @@ -3104,7 +3104,6 @@ def add_new_topic_api(request): isTopLevelDisplay = data["category"] == Topic.ROOT t = Topic({'slug': "", "isTopLevelDisplay": isTopLevelDisplay, "data_source": "sefaria", "numSources": 0}) update_topic_titles(t, data) - if not isTopLevelDisplay: # not Top Level so create an IntraTopicLink to category new_link = IntraTopicLink({"toTopic": data["category"], "fromTopic": t.slug, "linkType": "displays-under", "dataSource": "sefaria"}) new_link.save() @@ -3116,8 +3115,11 @@ def add_new_topic_api(request): t.data_source = "sefaria" # any topic edited manually should display automatically in the TOC and this flag ensures this if "description" in data: t.change_description(data["description"], data.get("categoryDescription", None)) - t.save() + if "image" in data: + t.image = data["image"] + + t.save() library.build_topic_auto_completer() library.get_topic_toc(rebuild=True) library.get_topic_toc_json(rebuild=True) @@ -3570,28 +3572,17 @@ def profile_follow_api(request, ftype, slug): @catch_error_as_json def topic_upload_photo(request): if not request.user.is_authenticated: - return jsonResponse({"error": _("You must be logged in to update your profile photo.")}) + return jsonResponse({"error": _("You must be logged in to update a topic photo.")}) if request.method == "POST": from io import BytesIO import uuid import base64 - """ - "image" : { - "image_uri" : "https://storage.googleapis.com/img.sefaria.org/topics/shabbat.jpg", - "image_caption" : { - "en" : "Friday Evening, Isidor Kaufmann, Austria c. 1920. The Jewish Museum, Gift of Mr. and Mrs. M. R. Schweitzer", - "he" : "שישי בערב, איזידור קאופמן, וינה 1920. המוזיאון היהודי בניו יורק, מתנת מר וגברת מ.ר. שוויצר" - } - } -Validation that the image_uri url should start with https://storage.googleapis.com/img.sefaria.org/topics/ - """ bucket_name = GoogleStorageManager.TOPICS_BUCKET img_file_in_mem = BytesIO(base64.b64decode(request.POST.get('file'))) + old_filename = request.POST.get('old_filename') img_url = GoogleStorageManager.upload_file(img_file_in_mem, f"topics/{request.user.id}-{uuid.uuid1()}.gif", - bucket_name) - #big_pic_url = GoogleStorageManager.upload_file(get_resized_file(image, (250, 250)), "{}-{}.png".format(profile.slug, now), bucket_name, old_big_pic_filename) - - add_image_to_topic(topic_slug, img_url, en_caption, he_caption) + bucket_name, old_filename=old_filename) + #img_url = 'https://storage.googleapis.com/img.sefaria.org/topics/41861-683e06f6-891a-11ee-be47-4a26184f1ad1.gif' return jsonResponse({"url": img_url}) return jsonResponse({"error": "Unsupported HTTP method."}) diff --git a/sefaria/helper/topic.py b/sefaria/helper/topic.py index 504fe11ade..68ce1e0e07 100644 --- a/sefaria/helper/topic.py +++ b/sefaria/helper/topic.py @@ -1107,6 +1107,9 @@ def update_topic(topic_obj, **kwargs): if "description" in kwargs or "categoryDescription" in kwargs: topic_obj.change_description(kwargs.get("description", None), kwargs.get("categoryDescription", None)) + if "image" in kwargs: + topic_obj.image = kwargs["image"] + topic_obj.save() if kwargs.get('rebuild_topic_toc', True): diff --git a/static/js/AdminEditor.jsx b/static/js/AdminEditor.jsx index 0f32c72d1f..566973dacd 100644 --- a/static/js/AdminEditor.jsx +++ b/static/js/AdminEditor.jsx @@ -1,10 +1,12 @@ import React, {useRef, useState} from "react"; import Sefaria from "./sefaria/sefaria"; -import {AdminToolHeader, InterfaceText, PictureUploader, TitleVariants} from "./Misc"; +import {AdminToolHeader, InterfaceText, TitleVariants} from "./Misc"; import sanitizeHtml from 'sanitize-html'; import classNames from "classnames"; const options_for_form = { - "Picture": {label: "Picture", field: "picture", placeholder: "Add a picture.", type: "picture"}, + // "Picture": {label: "Picture", field: "picture", placeholder: "Add a picture.", type: "picture"}, + "English Caption": {label: "English Caption", field: "enImgCaption", placeholder: "Add a caption for topic picture"}, + "Hebrew Caption": {label: "Hebrew Caption", field: "heImgCaption", placeholder: "Add a Hebrew caption for topic picture"}, "Title": {label: "Title", field: "enTitle", placeholder: "Add a title."}, "Hebrew Title": {label: "Hebrew Title", field: "heTitle", placeholder: "Add a title."}, "English Description": { @@ -126,10 +128,6 @@ const AdminEditor = ({title, data, close, catMenu, updateData, savingStatus, } updateData({...data}); } - const handlePictureChange = (url) => { - data["picture"] = url; - updateData({...data}); - } const handleTitleVariants = (newTitles, field) => { const newData = {...data}; newData[field] = newTitles.map(x => Object.assign({}, x)); @@ -166,9 +164,6 @@ const AdminEditor = ({title, data, close, catMenu, updateData, savingStatus, const item = ({label, field, placeholder, type, dropdown_data}) => { let obj; switch(type) { - case 'picture': - obj = <PictureUploader callback={handlePictureChange}/>; - break; case 'dropdown': obj = getDropdown(field, dropdown_data, placeholder); break; diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index e0a820257b..2988f55fa7 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -15,7 +15,7 @@ import {ContentText} from "./ContentText"; import ReactTags from "react-tag-autocomplete"; import {AdminEditorButton, useEditToggle} from "./AdminEditor"; import {CategoryEditor, ReorderEditor} from "./CategoryEditor"; -import {refSort} from "./TopicPage"; +import {refSort, TopicImage} from "./TopicPage"; import {TopicEditor} from "./TopicEditor"; import {generateContentForModal, SignUpModalKind} from './sefaria/signupModalContent'; import {SourceEditor} from "./SourceEditor"; @@ -1235,7 +1235,8 @@ const EditorForExistingTopic = ({ toggle, data }) => { origBirthYear: data?.properties?.birthYear?.value, origDeathPlace: data?.properties?.deathPlace?.value, origDeathYear: data?.properties?.deathYear?.value, - origEra: data?.properties?.era?.value + origEra: data?.properties?.era?.value, + origImage: data?.image, }; @@ -1578,14 +1579,18 @@ FollowButton.propTypes = { }; -const PictureUploader = (callback) => { +const PictureUploader = ({callback, old_filename, caption}) => { + /* + `old_filename` is passed to API so that if it exists, it is deleted + */ const fileInput = useRef(null); var uploadImage = function(imageData) { const formData = new FormData(); formData.append('file', imageData.replace(/data:image\/(jpe?g|png|gif);base64,/, "")); - // formData.append('file', imageData); - + if (old_filename !== "") { + formData.append('old_filename', old_filename); + } $.ajax({ url: Sefaria.apiHost + "/api/topics/upload-image", type: 'POST', @@ -1594,9 +1599,6 @@ const PictureUploader = (callback) => { processData: false, success: function(data) { callback(data.url); - // $("#inlineAddMediaInput").val(data.url); - // $("#addmediaDiv").find(".button").first().trigger("click"); - // $("#inlineAddMediaInput").val(""); }, error: function(e) { console.log("photo upload ERROR", e); @@ -1623,18 +1625,13 @@ const PictureUploader = (callback) => { alert('not an image'); } } - return <div><div role="button" title={Sefaria._("Add an image")} aria-label="Add an image" className="editorAddInterfaceButton" contentEditable={false} onClick={(e) => e.stopPropagation()} id="addImageButton"> - <label htmlFor="addImageFileSelector" id="addImageFileSelectorLabel"></label> - </div><input id="addImageFileSelector" type="file" onChange={onFileSelect} ref={fileInput} /> - <div className="section"> - <label><InterfaceText>English Caption</InterfaceText></label> - <input type="text" id="enCaption"/> - </div> - <div className="section"> - <label><InterfaceText>Hebrew Caption</InterfaceText></label> - <input type="text" id="heCaption"/> - </div> - </div> + return <div className="section"> + <label><InterfaceText>Picture</InterfaceText></label> + <div role="button" title={Sefaria._("Add an image")} aria-label="Add an image" className="editorAddInterfaceButton" contentEditable={false} onClick={(e) => e.stopPropagation()} id="addImageButton"> + <label htmlFor="addImageFileSelector" id="addImageFileSelectorLabel"></label> + </div><input id="addImageFileSelector" type="file" onChange={onFileSelect} ref={fileInput} /> + {old_filename !== "" && <div style={{"max-width": "420px"}}><br/><ImageWithCaption photoLink={old_filename} caption={caption}/></div>} + </div> } const CategoryColorLine = ({category}) => diff --git a/static/js/TopicEditor.jsx b/static/js/TopicEditor.jsx index 229ba62da2..963f0fcac3 100644 --- a/static/js/TopicEditor.jsx +++ b/static/js/TopicEditor.jsx @@ -1,5 +1,5 @@ import Sefaria from "./sefaria/sefaria"; -import {InterfaceText, requestWithCallBack, ProfilePic} from "./Misc"; +import {InterfaceText, requestWithCallBack, ProfilePic, PictureUploader} from "./Misc"; import $ from "./sefaria/sefariaJquery"; import {AdminEditor} from "./AdminEditor"; import {Reorder} from "./CategoryEditor"; @@ -19,7 +19,9 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { birthYear: origData.origBirthYear || "", heDeathPlace: origData.origHeDeathPlace || "", deathYear: origData.origDeathYear || "", era: origData.origEra || "", deathPlace: origData.origDeathPlace || "", - picture: origData?.origPicture || "" + enImgCaption: origData?.origImage?.image_caption?.en || "", + heImgCaption: origData?.origImage?.image_caption?.he || "", + image_uri: origData?.origImage?.image_uri || "" }); const isNew = !('origSlug' in origData); const [savingStatus, setSavingStatus] = useState(false); @@ -68,7 +70,6 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { const updateData = function(newData) { setIsChanged(true); setData(newData); - console.log(newData); } const validate = async function () { if (!isChanged) { @@ -100,6 +101,9 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { postData.altTitles.en = data.enAltTitles.map(x => x.name); // alt titles implemented using TitleVariants which contains list of objects with 'name' property. postData.altTitles.he = data.heAltTitles.map(x => x.name); + if (data.image_uri !== "") { + postData.image = {"image_uri": data.image_uri, "image_caption": {"en": data.enImgCaption, "he": data.heImgCaption}} + } // add descriptions if they changed const origDescription = {en: origData?.origEnDescription || "", he: origData?.origHeDescription || ""}; const origCategoryDescription = {en: origData?.origEnCategoryDescription || "", he: origData?.origHeCategoryDescription || ""}; @@ -155,12 +159,16 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { alert("Unfortunately, there may have been an error saving this topic information: " + errorThrown.toString()); }); } + const handlePictureChange = (url) => { + data["image_uri"] = url; + updateData({...data}); + } const deleteObj = function() { const url = `/api/topic/delete/${data.origSlug}`; requestWithCallBack({url, type: "DELETE", redirect: () => window.location.href = "/topics"}); } - let items = ["Title", "Hebrew Title", "English Description", "Hebrew Description", "Category Menu", "Picture"]; + let items = ["Title", "Hebrew Title", "English Description", "Hebrew Description", "Category Menu"]; if (isCategory) { items.push("English Short Description"); items.push("Hebrew Short Description"); @@ -169,10 +177,14 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { const authorItems = ["English Alternate Titles", "Hebrew Alternate Titles", "Birth Place", "Hebrew Birth Place", "Birth Year", "Place of Death", "Hebrew Place of Death", "Death Year", "Era"]; authorItems.forEach(x => items.push(x)); } + items.push("English Caption"); + items.push("Hebrew Caption"); return <AdminEditor title="Topic Editor" close={close} catMenu={catMenu} data={data} savingStatus={savingStatus} validate={validate} deleteObj={deleteObj} updateData={updateData} isNew={isNew} items={items} extras={ - [isNew ? null : + [<PictureUploader callback={handlePictureChange} old_filename={data.image_uri} + caption={{en: data.enImgCaption, he: data.heImgCaption}}/>, + isNew ? null : <Reorder subcategoriesAndBooks={sortedSubtopics} updateOrder={setSortedSubtopics} displayType="topics"/>, From ab984852d63f8c569ce81e80ef8c8502374a05b3 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Wed, 22 Nov 2023 13:50:13 +0200 Subject: [PATCH 509/756] fix(Topic Editor): when uploading topic photo, delete old one --- reader/views.py | 2 ++ sefaria/google_storage_manager.py | 5 ++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/reader/views.py b/reader/views.py index 086eed3a7a..41320d133b 100644 --- a/reader/views.py +++ b/reader/views.py @@ -3580,6 +3580,8 @@ def topic_upload_photo(request): bucket_name = GoogleStorageManager.TOPICS_BUCKET img_file_in_mem = BytesIO(base64.b64decode(request.POST.get('file'))) old_filename = request.POST.get('old_filename') + if old_filename: + old_filename = f"topics/{old_filename.split('/')[-1]}" img_url = GoogleStorageManager.upload_file(img_file_in_mem, f"topics/{request.user.id}-{uuid.uuid1()}.gif", bucket_name, old_filename=old_filename) #img_url = 'https://storage.googleapis.com/img.sefaria.org/topics/41861-683e06f6-891a-11ee-be47-4a26184f1ad1.gif' diff --git a/sefaria/google_storage_manager.py b/sefaria/google_storage_manager.py index 4bd2fa6c23..756dbc60e4 100644 --- a/sefaria/google_storage_manager.py +++ b/sefaria/google_storage_manager.py @@ -25,9 +25,8 @@ class GoogleStorageManager(object): @classmethod def get_bucket(cls, bucket_name): if getattr(cls, 'client', None) is None: - # for local development, change below line to - cls.client = storage.Client(project="production-deployment") - #cls.client = storage.Client.from_service_account_json(GOOGLE_APPLICATION_CREDENTIALS_FILEPATH) + # for local development, change below line to cls.client = storage.Client(project="production-deployment") + cls.client = storage.Client.from_service_account_json(GOOGLE_APPLICATION_CREDENTIALS_FILEPATH) bucket = cls.client.get_bucket(bucket_name) return bucket From 939d7dea02fbba97855130b5d1ec6fa9c0ee453c Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Wed, 22 Nov 2023 15:29:25 +0200 Subject: [PATCH 510/756] feat(translations): functions for getting texts and particularly translations. Use the new function in TranslationBox. --- static/js/TranslationsBox.jsx | 4 ++-- static/js/sefaria/sefaria.js | 29 ++++++++++++++++++++++++++++ static/js/sefaria/textGetter.js | 34 --------------------------------- 3 files changed, 31 insertions(+), 36 deletions(-) delete mode 100644 static/js/sefaria/textGetter.js diff --git a/static/js/TranslationsBox.jsx b/static/js/TranslationsBox.jsx index 9e0c84382b..f6d0410d9f 100644 --- a/static/js/TranslationsBox.jsx +++ b/static/js/TranslationsBox.jsx @@ -19,12 +19,12 @@ class TranslationsBox extends Component { } componentDidMount() { if(!this.isSheet()) { - Sefaria.getTranslations(this.props.sectionRef).then(this.onVersionsLoad); + Sefaria.getAllTranslationsWithText(this.props.sectionRef).then(this.onVersionsLoad); } } componentDidUpdate(prevProps, prevState) { if (!this.isSheet() && prevProps.sectionRef !== this.props.sectionRef) { - Sefaria.getTranslations(this.props.sectionRef).then(this.onVersionsLoad); + Sefaria.getAllTranslationsWithText(this.props.sectionRef).then(this.onVersionsLoad); } } onVersionsLoad(versions) { diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index 3da5849570..558b4dd938 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -487,6 +487,35 @@ Sefaria = extend(Sefaria, { return Promise.all(promises).then(results => Object.assign({}, ...results)); }, + getTextsFromAPIV3: async function(ref, requiredVersions, mergeText) { + // ref is segment ref or bottom level section ref + // requiredVersions is array of objects that can have language and versionTitle + function makeParamsString(language, versionTitle) { + if (versionTitle) { + return `${language}|${versionTitle}`; + } else if (language) { + return language; + } + } + function makeUrl() { + const host = Sefaria.apiHost; + const endPoint = '/api/v3/texts/' + const versions = requiredVersions.map(obj => + makeParamsString(obj.language, obj.versionTitle) + ); + const url = `${host}${endPoint}${ref}?version=${versions.join('&version=')}&fill_in_missing_segments=${mergeText}`; + return url; + } + const url = makeUrl(ref, requiredVersions); + //here's the place for getting it from cache + const apiObject = await Sefaria._ApiPromise(url); + //here's the place for all changes we want to add, and saving in cache + return apiObject; + }, + getAllTranslationsWithText: async function(ref) { + let returnObj = await Sefaria.getTextsFromAPIV3(ref, [{language: 'translation', versionTitle: 'all'}], false); + return Sefaria._sortVersionsIntoBuckets(returnObj.versions); + }, _bulkSheets: {}, getBulkSheets: function(sheetIds) { if (sheetIds.length === 0) { return Promise.resolve({}); } diff --git a/static/js/sefaria/textGetter.js b/static/js/sefaria/textGetter.js deleted file mode 100644 index 572fe3cc48..0000000000 --- a/static/js/sefaria/textGetter.js +++ /dev/null @@ -1,34 +0,0 @@ -import Sefaria from "./sefaria"; - -function makeParamsString(language, versionTitle) { - if (versionTitle) { - return `${language}|${versionTitle}`; - } else if (language) { - return language; - } -} - -function makeUrl(ref, requiredVersions) { - const host = Sefaria.apiHost; - const endPoint = '/api/v3/texts/' - const versions = Object.entries(requiredVersions).map(([language, versionTitle]) => - makeParamsString(language, versionTitle) - ); - const url = `${host}${endPoint}${ref}?version=${versions.join('&version=')}&fill_in_missing_segments=true`; - return url; -} - -async function getVTextsFromAPI(ref, requiredVersions) { - const url = makeUrl(ref, requiredVersions); - const apiObject = await Sefaria._ApiPromise(url); - Sefaria.saveVersions(ref, apiObject.available_versions); - delete apiObject.available_versions; - return apiObject; -} - -export async function getTexts(ref, requiredVersions) { - // ref is segment ref or bottom level section ref - // requiredVersions is array of objects that can have language and versionTitle - let returnObj = await getVersionsFromAPI(ref, requiredVersions); - return returnObj; -} From c82d71180840f992912f9dcd6ce54edddd87b980 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Wed, 22 Nov 2023 15:39:33 +0200 Subject: [PATCH 511/756] refactor(text api): change fullLanguage to languageFamilyName. --- sefaria/model/text.py | 12 ++++++------ sefaria/model/text_manager.py | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index 50ce59e93b..f0e460b34d 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -1335,7 +1335,7 @@ class Version(AbstractTextRecord, abst.AbstractMongoRecord, AbstractSchemaConten "purchaseInformationURL", "hasManuallyWrappedRefs", # true for texts where refs were manually wrapped in a-tags. no need to run linker at run-time. "actualLanguage", - 'fullLanguage', + 'languageFamilyName', "isBaseText", 'isSource', 'isPrimary', @@ -1357,11 +1357,11 @@ def _validate(self): languageCodeRe = re.search(r"\[([a-z]{2})\]$", getattr(self, "versionTitle", None)) if languageCodeRe and languageCodeRe.group(1) != getattr(self,"actualLanguage",None): self.actualLanguage = languageCodeRe.group(1) - if not getattr(self, 'fullLanguage', None): + if not getattr(self, 'languageFamilyName', None): try: - self.fullLanguage = constants.LANGUAGE_CODES[self.actualLanguage] + self.languageFamilyName = constants.LANGUAGE_CODES[self.actualLanguage] except KeyError: - self.fullLanguage = constants.LANGUAGE_CODES[self.language] + self.languageFamilyName = constants.LANGUAGE_CODES[self.language] if getattr(self,"language", None) not in ["en", "he"]: raise InputError("Version language must be either 'en' or 'he'") index = self.get_index() @@ -1715,7 +1715,7 @@ def __init__(self, oref, lang, vtitle, merge_versions=False): def versions(self): if self._versions == []: condition_query = self.oref.condition_query(self.lang) if self.merge_versions else \ - {'title': self.oref.index.title, 'fullLanguage': self.lang, 'versionTitle': self.vtitle} + {'title': self.oref.index.title, 'languageFamilyName': self.lang, 'versionTitle': self.vtitle} self._versions = VersionSet(condition_query, proj=self.oref.part_projection()) return self._versions @@ -1728,7 +1728,7 @@ def _validate_versions(self, versions): if not self.merge_versions and len(versions) > 1: raise InputError("Got many versions instead of one") for version in versions: - condition = version.title == self.oref.index.title and version.fullLanguage == self.lang + condition = version.title == self.oref.index.title and version.languageFamilyName == self.lang if not self.merge_versions: condition = condition and version.versionTitle == self.vtitle if not condition: diff --git a/sefaria/model/text_manager.py b/sefaria/model/text_manager.py index 7b9a3bba06..2d8b3087a3 100644 --- a/sefaria/model/text_manager.py +++ b/sefaria/model/text_manager.py @@ -28,7 +28,7 @@ def __init__(self, oref: Ref, versions_params: List[List[str]], fill_in_missing_ self.return_obj = { 'versions': [], 'missings': [], - 'available_langs': sorted({v.fullLanguage for v in self.all_versions}), + 'available_langs': sorted({v.languageFamilyName for v in self.all_versions}), 'available_versions': [{f: getattr(v, f, "") for f in fields} for v in self.all_versions] } @@ -38,12 +38,12 @@ def _append_version(self, version): for attr in ['chapter', 'title', 'language']: fields.remove(attr) version_details = {f: getattr(version, f, "") for f in fields} - text_range = TextRange(self.oref, version.fullLanguage, version.versionTitle, self.fill_in_missing_segments) + text_range = TextRange(self.oref, version.languageFamilyName, version.versionTitle, self.fill_in_missing_segments) if self.fill_in_missing_segments: # we need a new VersionSet of only the relevant versions for merging. copy should be better than calling for mongo relevant_versions = copy.copy(self.all_versions) - relevant_versions.remove(lambda v: v.fullLanguage != version.fullLanguage) + relevant_versions.remove(lambda v: v.languageFamilyName != version.languageFamilyName) else: relevant_versions = [version] text_range.versions = relevant_versions @@ -66,7 +66,7 @@ def _append_required_versions(self, lang: str, vtitle: str) -> None: elif lang == self.TRANSLATION: lang_condition = lambda v: not getattr(v, 'isSource', False) elif lang: - lang_condition = lambda v: v.fullLanguage == lang + lang_condition = lambda v: v.languageFamilyName == lang else: lang_condition = lambda v: True if vtitle and vtitle != self.ALL: @@ -76,7 +76,7 @@ def _append_required_versions(self, lang: str, vtitle: str) -> None: if vtitle != self.ALL and versions: versions = [max(versions, key=lambda v: getattr(v, 'priority', 0))] for version in versions: - if all(version.fullLanguage != v['fullLanguage'] or version.versionTitle != v['versionTitle'] for v in self.return_obj['versions']): + if all(version.languageFamilyName != v['languageFamilyName'] or version.versionTitle != v['versionTitle'] for v in self.return_obj['versions']): #do not return the same version even if included in two different version params self._append_version(version) if not versions: From 336e758b4610c67ee60531419a40e097f2c008f3 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Wed, 22 Nov 2023 15:50:17 +0200 Subject: [PATCH 512/756] feat(text api): return the language attribute to api response. --- sefaria/model/text_manager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sefaria/model/text_manager.py b/sefaria/model/text_manager.py index 2d8b3087a3..c39f6c79eb 100644 --- a/sefaria/model/text_manager.py +++ b/sefaria/model/text_manager.py @@ -35,7 +35,7 @@ def __init__(self, oref: Ref, versions_params: List[List[str]], fill_in_missing_ def _append_version(self, version): #TODO part of this function duplicate the functionality of Ref.versionlist(). maybe we should mvoe it to Version fields = Version.optional_attrs + Version.required_attrs - for attr in ['chapter', 'title', 'language']: + for attr in ['chapter', 'title']: fields.remove(attr) version_details = {f: getattr(version, f, "") for f in fields} text_range = TextRange(self.oref, version.languageFamilyName, version.versionTitle, self.fill_in_missing_segments) @@ -175,7 +175,7 @@ def make_named_entities_dict(): for version in self.return_obj['versions']: if self.return_format == 'wrap_all_entities': - language = 'he' if version['direction'] == 'rtl' else 'en' + language = 'he' if version['direction'] == 'rtl' else 'en' #this is neccesary because we want to get rif of the language attribute in future ne_by_secs = make_named_entities_dict() ja = JaggedTextArray(version['text']) # JaggedTextArray works also with depth 0, i.e. a string From 18dad10d8e8fbd07c45f6b2df3aa0b5e98415749 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Wed, 22 Nov 2023 15:58:13 +0200 Subject: [PATCH 513/756] feat(text api): find language also with capital letters. --- sefaria/model/text_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sefaria/model/text_manager.py b/sefaria/model/text_manager.py index c39f6c79eb..5a223d8c2c 100644 --- a/sefaria/model/text_manager.py +++ b/sefaria/model/text_manager.py @@ -66,7 +66,7 @@ def _append_required_versions(self, lang: str, vtitle: str) -> None: elif lang == self.TRANSLATION: lang_condition = lambda v: not getattr(v, 'isSource', False) elif lang: - lang_condition = lambda v: v.languageFamilyName == lang + lang_condition = lambda v: v.languageFamilyName.lower() == lang else: lang_condition = lambda v: True if vtitle and vtitle != self.ALL: From db17da1a8d6be98e9ea2ed0b62feb5597f1609ff Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Wed, 22 Nov 2023 16:03:40 +0200 Subject: [PATCH 514/756] refactor(text api): replace base by primary. --- api/tests.py | 8 ++++---- api/views.py | 2 +- sefaria/model/text_manager.py | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/api/tests.py b/api/tests.py index 1ea31d542b..76709e1612 100644 --- a/api/tests.py +++ b/api/tests.py @@ -83,14 +83,14 @@ def test_api_get_text_specific(self): self.assertEqual(data["sections"], ["2a", '4', '1']) self.assertEqual(data["toSections"], ["2a", '4', '1']) - def test_api_get_text_base_all(self): - response = c.get('/api/v3/texts/Genesis.1?version=base|all') + def test_api_get_text_primary_all(self): + response = c.get('/api/v3/texts/Genesis.1?version=primary|all') data = json.loads(response.content) self.assertTrue(len(data["versions"]) > 3) self.assertTrue(all(v['actualLanguage'] == 'he' for v in data["versions"])) - def test_api_get_text_base(self): - response = c.get('/api/v3/texts/Shabbat.22a?version=base') + def test_api_get_text_primary(self): + response = c.get('/api/v3/texts/Shabbat.22a?version=primary') self.assertEqual(200, response.status_code) data = json.loads(response.content) self.assertEqual(len(data["versions"]), 1) diff --git a/api/views.py b/api/views.py index c6e0beca82..982818902c 100644 --- a/api/views.py +++ b/api/views.py @@ -46,7 +46,7 @@ def get(self, request, *args, **kwargs): return jsonResponse({'error': f'We have no text for {self.oref}.'}, status=400) versions_params = request.GET.getlist('version', []) if not versions_params: - versions_params = ['base'] + versions_params = ['primary'] versions_params = [self.split_piped_params(param_str) for param_str in versions_params] fill_in_missing_segments = request.GET.get('fill_in_missing_segments', False) return_format = request.GET.get('return_format', 'default') diff --git a/sefaria/model/text_manager.py b/sefaria/model/text_manager.py index 5a223d8c2c..f14a54bac8 100644 --- a/sefaria/model/text_manager.py +++ b/sefaria/model/text_manager.py @@ -11,7 +11,7 @@ class TextManager: ALL = 'all' - BASE = 'base' + PRIMARY = 'primary' SOURCE = 'source' TRANSLATION = 'translation' @@ -59,7 +59,7 @@ def _append_version(self, version): self.return_obj['versions'].append(version_details) def _append_required_versions(self, lang: str, vtitle: str) -> None: - if lang == self.BASE: + if lang == self.PRIMARY: lang_condition = lambda v: getattr(v, 'isPrimary', False) elif lang == self.SOURCE: lang_condition = lambda v: getattr(v, 'isSource', False) From fca546381217c50a4b7124424e687aa47323a243 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Thu, 23 Nov 2023 10:11:41 +0200 Subject: [PATCH 515/756] feat(translations): show translations with preview. New components: VersionBlockWithPreview, VersionBlockWithPreviewTitleLine, VersionPreviewMeta. VersionBlockWithPreview is called by VersionBlockList when rendered by TranslationBox. --- static/js/TranslationsBox.jsx | 6 +- static/js/VersionBlock.jsx | 46 ++++++++++----- static/js/VersionBlockHeader.jsx | 13 ++--- static/js/VersionBlockWithPreview.jsx | 58 +++++++++++++++++++ .../js/VersionBlockWithPreviewTitleLine.jsx | 44 ++++++++++++++ static/js/VersionPreviewMeta.jsx | 22 +++++++ 6 files changed, 164 insertions(+), 25 deletions(-) create mode 100644 static/js/VersionBlockWithPreview.jsx create mode 100644 static/js/VersionBlockWithPreviewTitleLine.jsx create mode 100644 static/js/VersionPreviewMeta.jsx diff --git a/static/js/TranslationsBox.jsx b/static/js/TranslationsBox.jsx index f6d0410d9f..07141565cf 100644 --- a/static/js/TranslationsBox.jsx +++ b/static/js/TranslationsBox.jsx @@ -19,12 +19,12 @@ class TranslationsBox extends Component { } componentDidMount() { if(!this.isSheet()) { - Sefaria.getAllTranslationsWithText(this.props.sectionRef).then(this.onVersionsLoad); + Sefaria.getAllTranslationsWithText(this.props.srefs[0]).then(this.onVersionsLoad); } } componentDidUpdate(prevProps, prevState) { if (!this.isSheet() && prevProps.sectionRef !== this.props.sectionRef) { - Sefaria.getAllTranslationsWithText(this.props.sectionRef).then(this.onVersionsLoad); + Sefaria.getAllTranslationsWithText(this.props.srefs[0]).then(this.onVersionsLoad); } } onVersionsLoad(versions) { @@ -90,6 +90,8 @@ class TranslationsBox extends Component { viewExtendedNotes={this.props.viewExtendedNotes} inTranslationBox={true} showNotes={false} + srefs={this.props.srefs} + onRangeClick={this.props.onRangeClick} /> </> ); diff --git a/static/js/VersionBlock.jsx b/static/js/VersionBlock.jsx index 0ad91e1da5..fab50dc515 100644 --- a/static/js/VersionBlock.jsx +++ b/static/js/VersionBlock.jsx @@ -10,6 +10,7 @@ import VersionBlockHeader from "./VersionBlockHeader"; import VersionBlockSelectButton from "./VersionBlockSelectButton"; import VersionDetailsInformation from "./VersionDetailsInformation"; import VersionDetailsImage from "./VersionDetailsImage"; +import VersionBlockWithPreview from "./VersionBlockWithPreview"; class versionTools { static makeVersionTitle(version){ @@ -401,22 +402,33 @@ class VersionsBlocksList extends Component{ } { this.props.versionsByLanguages[lang].map((v) => ( - <VersionBlock - rendermode="versions-box" - sidebarDisplay={true} - version={v} - currObjectVersions={this.props.currObjectVersions} - currentRef={this.props.currentRef} - firstSectionRef={"firstSectionRef" in v ? v.firstSectionRef : null} - key={`${this.isVersionCurrent(v) ? "current" : ""}|${v.versionTitle}|${v.actualLanguage}`} - openVersionInReader={this.props.openVersionInReader} - openVersionInSidebar={this.props.openVersionInSidebar} - viewExtendedNotes={this.props.viewExtendedNotes} - isCurrent={this.isVersionCurrent(v)} - inTranslationBox={this.props.inTranslationBox} - showNotes={this.props.showNotes} - /> - )) + this.props.inTranslationBox ? + <VersionBlockWithPreview + currentRef={this.props.currentRef} + version={v} + currObjectVersions={this.props.currObjectVersions} + openVersionInReader={this.props.openVersionInReader} + openVersionInSidebar={this.props.openVersionInSidebar} + isSelected={this.isVersionCurrent(v)} + srefs={this.props.srefs} + onRangeClick={this.props.onRangeClick} + /> : + <VersionBlock + rendermode="versions-box" + sidebarDisplay={true} + version={v} + currObjectVersions={this.props.currObjectVersions} + currentRef={this.props.currentRef} + firstSectionRef={"firstSectionRef" in v ? v.firstSectionRef : null} + key={`${this.isVersionCurrent(v) ? "current" : ""}|${v.versionTitle}|${v.actualLanguage}`} + openVersionInReader={this.props.openVersionInReader} + openVersionInSidebar={this.props.openVersionInSidebar} + viewExtendedNotes={this.props.viewExtendedNotes} + isCurrent={this.isVersionCurrent(v)} + inTranslationBox={this.props.inTranslationBox} + showNotes={this.props.showNotes} + /> + )) } </div> )) @@ -437,6 +449,8 @@ VersionsBlocksList.propTypes={ showLanguageHeaders: PropTypes.bool, inTranslationBox: PropTypes.bool, showNotes: PropTypes.bool, + srefs: PropTypes.array, + onRangeClick: PropTypes.func, }; VersionsBlocksList.defaultProps = { displayCurrentVersions: true, diff --git a/static/js/VersionBlockHeader.jsx b/static/js/VersionBlockHeader.jsx index e0bb2f933e..a16411b6d9 100644 --- a/static/js/VersionBlockHeader.jsx +++ b/static/js/VersionBlockHeader.jsx @@ -2,16 +2,16 @@ import React from 'react'; import PropTypes from "prop-types"; function VersionBlockHeader({text, link, onClick, renderMode}) { - return renderMode === 'versionTitle' ? + return renderMode === 'versionTitle' ? (<VersionBlockHeaderTitle - href={link} + link={link} onClick={onClick} versionTitle={text} />) : (<VersionBlockHeaderText - href={link} + link={link} onClick={onClick} - versionTitle={text} + text={text} />); } VersionBlockHeader.prototypes = { @@ -43,9 +43,8 @@ function VersionBlockHeaderText({link, onClick, text}) { className='versionPreview' href={link} onClick={onClick} - > - {text} - </a> + dangerouslySetInnerHTML={{__html: text}} + /> ); } VersionBlockHeaderText.prototypes = { diff --git a/static/js/VersionBlockWithPreview.jsx b/static/js/VersionBlockWithPreview.jsx new file mode 100644 index 0000000000..0b517c4029 --- /dev/null +++ b/static/js/VersionBlockWithPreview.jsx @@ -0,0 +1,58 @@ +import React, {useState} from 'react'; +import PropTypes from 'prop-types'; +import VersionBlockHeader from "./VersionBlockHeader"; +import {versionTools} from './VersionBlock'; +import VersionBlockWithPreviewTitleLine from './VersionBlockWithPreviewTitleLine'; +import VersionPreviewMeta from "./VersionPreviewMeta"; +import {OpenConnectionTabButton} from "./TextList"; + +function VersionBlockWithPreview({currentRef, version, currObjectVersions, openVersionInSidebar, openVersionInReader, isSelected, srefs, onRangeClick}) { + const [isInfoOpen, setIsInfoOpen] = useState(false); + const opeInSidebar = versionTools.openVersionInSidebar.bind(null, currentRef, version, currObjectVersions, openVersionInSidebar); + function openInTabCallback(sref) { + onRangeClick(sref, false, {[version.language]: version.versionTitle}); + } + return ( + <div className='version-with-preview'> + <VersionBlockHeader + text={version.text} + onClick={opeInSidebar} + renderMode='contentText' + link={versionTools.makeVersionLink(currentRef, version, currObjectVersions, false)} + /> + <VersionBlockWithPreviewTitleLine + version={version} + currentRef={currentRef} + currObjectVersions={currObjectVersions} + openVersionInReader={openVersionInReader} + isInfoOpen={isInfoOpen} + setIsInfoOpen={setIsInfoOpen} + isSelected={isSelected} + /> + {isInfoOpen ? + <div className='version-block-with-preview-details'> + <VersionPreviewMeta + currentRef={currentRef} + version={version} + /> + <OpenConnectionTabButton + srefs={srefs} + openInTabCallback={openInTabCallback} + renderMode='versionPreview' + /> + </div> + : null} + </div> + ); +} +VersionBlockWithPreview.prototypes = { + version: PropTypes.object.isRequired, + currObjectVersions: PropTypes.object.isRequired, + currentRef: PropTypes.string.isRequired, + openVersionInSidebar: PropTypes.func, + openVersionInReader: PropTypes.func.isRequired, + isSelected: PropTypes.bool.isRequired, + srefs: PropTypes.array.isRequired, + onRangeClick: PropTypes.func.isRequired, +}; +export default VersionBlockWithPreview; diff --git a/static/js/VersionBlockWithPreviewTitleLine.jsx b/static/js/VersionBlockWithPreviewTitleLine.jsx new file mode 100644 index 0000000000..326867b49a --- /dev/null +++ b/static/js/VersionBlockWithPreviewTitleLine.jsx @@ -0,0 +1,44 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import VersionBlockSelectButton from "./VersionBlockSelectButton"; +import {versionTools} from './VersionBlock'; +import Sefaria from "./sefaria/sefaria"; + +function VersionBlockWithPreviewTitleLine({currentRef, version, currObjectVersions, openVersionInReader, isInfoOpen, setIsInfoOpen, isSelected}) { + function makeShortVersionTitle() { + let shortVersionTitle = version.shortVersionTitle || version.versionTitle; + if (Sefaria.interfaceLang === "english") { + shortVersionTitle = version.shortVersionTitleInHebrew || version.versionTitleInHebrew || shortVersionTitle; + } + return shortVersionTitle; + } + const chevronDirection = isInfoOpen ? 'up' : 'down'; + const showOrHide = isInfoOpen ? 'Hide' : 'Show'; + const openVersionInMoinPanel = versionTools.openVersionInMoinPanel.bind(null, currentRef, version, currObjectVersions, 'select-button', + null, openVersionInReader); + const buttonText = isSelected ? 'Currently Selected' : 'Select'; + return ( + <div className='version-with-preview-title-line'> + <a onClick={() => setIsInfoOpen(true)}> + <img src={`/static/icons/little-chevron-${chevronDirection}.svg`} alt={`${showOrHide} details`} /> + <div className='version-with-preview-short-version-title'>{makeShortVersionTitle()}</div> + </a> + <VersionBlockSelectButton + isSelected={isSelected} + openVersionInMoinPanel={openVersionInMoinPanel} + text={buttonText} + link={versionTools.makeVersionLink(currentRef, version, currObjectVersions, true)} + /> + </div> + ); +} +VersionBlockWithPreviewTitleLine.prototypes = { + currObjectVersions: PropTypes.object.isRequired, + version: PropTypes.object.isRequired, + currentRef: PropTypes.string.isRequired, + openVersionInReader: PropTypes.func.isRequired, + isInfoOpen: PropTypes.bool.isRequired, + setIsInfoOpen: PropTypes.func.isRequired, + isSelected: PropTypes.bool.isRequired, +}; +export default VersionBlockWithPreviewTitleLine; diff --git a/static/js/VersionPreviewMeta.jsx b/static/js/VersionPreviewMeta.jsx new file mode 100644 index 0000000000..2c75af4eb2 --- /dev/null +++ b/static/js/VersionPreviewMeta.jsx @@ -0,0 +1,22 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import {versionTools} from "./VersionBlock"; +import VersionDetailsInformation from "./VersionDetailsInformation"; +import VersionDetailsImage from "./VersionDetailsImage"; + +function VersionPreviewMeta({currentRef, version}) { + return ( + <div className='version-preview-meta'> + <div className='version-preview-details'> + <div className='translation-version-title'>{versionTools.makeVersionTitle(version).text}</div> + <VersionDetailsInformation currentRef={currentRef} version={version}/> + </div> + <VersionDetailsImage version={version}/> + </div> + ); +} +VersionPreviewMeta.prototypes = { + currentRef: PropTypes.string.isRequired, + version: PropTypes.object.isRequired, +}; +export default VersionPreviewMeta; From f8194f98610be52098dcd684f0b2917e3ab0b31c Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Thu, 23 Nov 2023 10:26:29 +0200 Subject: [PATCH 516/756] fix: change_parent needs to force update of node full_title --- sefaria/helper/schema.py | 2 +- sefaria/model/schema.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sefaria/helper/schema.py b/sefaria/helper/schema.py index 518d62f01f..c34c1eb9d9 100644 --- a/sefaria/helper/schema.py +++ b/sefaria/helper/schema.py @@ -306,7 +306,7 @@ def change_parent(node, new_parent, place=0): old_parent.children = [n for n in old_parent.children if n.key != node.key] new_parent.children.insert(place, node) node.parent = new_parent - new_normal_form = node.ref().normal() + new_normal_form = node.ref(force_update=True).normal() index.save(override_dependencies=True) library.rebuild() diff --git a/sefaria/model/schema.py b/sefaria/model/schema.py index fe9537c7d3..a72e5c0e7e 100644 --- a/sefaria/model/schema.py +++ b/sefaria/model/schema.py @@ -1371,11 +1371,11 @@ def version_address(self): """ return self.address()[1:] - def ref(self): + def ref(self, force_update=False): from . import text d = { "index": self.index, - "book": self.full_title("en"), + "book": self.full_title("en", force_update=force_update), "primary_category": self.index.get_primary_category(), "index_node": self, "sections": [], From 4f77a137a84c5be8d2aaa3fc2f828778f89abb1a Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Thu, 23 Nov 2023 12:58:27 +0200 Subject: [PATCH 517/756] fix(portal): Pass caption prop properly using the new ImageWithCaption component --- static/js/NavSidebar.jsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/static/js/NavSidebar.jsx b/static/js/NavSidebar.jsx index baeb12a952..69976798fe 100644 --- a/static/js/NavSidebar.jsx +++ b/static/js/NavSidebar.jsx @@ -1,11 +1,10 @@ import React, { useState, useEffect } from 'react'; import classNames from 'classnames'; import Sefaria from './sefaria/sefaria'; -import {AppStoreButton, DonateLink, EnglishText, HebrewText} from './Misc' +import {AppStoreButton, DonateLink, EnglishText, HebrewText, ImageWithCaption} from './Misc' import {NewsletterSignUpForm} from "./NewsletterSignUpForm"; import {InterfaceText, ProfileListing, Dropdown} from './Misc'; import { Promotions } from './Promotions' -import {TopicImage} from './TopicPage' const NavSidebar = ({modules}) => { return <div className="navSidebar sans-serif"> @@ -803,7 +802,7 @@ const PortalAbout = ({title, description, image_uri, image_caption}) => { <Module> <ModuleTitle en={title.en} he={title.he} /> <div className="portalTopicImageWrapper"> - <TopicImage photoLink={image_uri} enCaption={image_caption.en} heCaption={image_caption.he} /> + <ImageWithCaption photoLink={image_uri} caption={image_caption} /> </div> <InterfaceText markdown={{en: description.en, he: description.he}} /> </Module> From 6cd9f89a21b32d5745180bd19255037aa3a300bb Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Thu, 23 Nov 2023 13:13:42 +0200 Subject: [PATCH 518/756] fix(linker): allow possibility of caption not existing for portals --- static/js/Misc.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index acb8c88440..4e591c3ee8 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -3175,7 +3175,7 @@ const ImageWithCaption = ({photoLink, caption }) => { <div> <img class="imageWithCaptionPhoto" src={photoLink}/> <div class="imageCaption"> - <InterfaceText text={{en:caption.en, he:caption.he}} /> + <InterfaceText text={caption} /> </div> </div>); } From 0a41efad9f36b55e0f042b5412de6255b18dce9a Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Thu, 23 Nov 2023 14:00:26 +0200 Subject: [PATCH 519/756] chore: allow for exact_match --- sefaria/helper/schema.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/sefaria/helper/schema.py b/sefaria/helper/schema.py index c34c1eb9d9..c6dadbe762 100644 --- a/sefaria/helper/schema.py +++ b/sefaria/helper/schema.py @@ -278,11 +278,12 @@ def prepare_ja_for_children(ja): v.sub_content(ja.version_address(), value={}) v.save() -def change_parent(node, new_parent, place=0): +def change_parent(node, new_parent, place=0, exact_match=False): """ :param node: :param new_parent: :param place: The index of the child before which to insert, so place=0 inserts at the front of the list, and place=len(parent_node.children) inserts at the end + :param exact_match: if True, if there are two links, "X" and "Y on X", changing "X" will not also change "Y on X" :return: """ assert isinstance(node, SchemaNode) @@ -312,7 +313,10 @@ def change_parent(node, new_parent, place=0): library.rebuild() for link in linkset: - link.refs = [ref.replace(old_normal_form, new_normal_form) for ref in link.refs] + if exact_match: + link.refs = [ref.replace(old_normal_form, new_normal_form) if ref.startswith(old_normal_form) else ref for ref in link.refs] + else: + link.refs = [ref.replace(old_normal_form, new_normal_form) for ref in link.refs] link.save() # todo: commentary linkset @@ -338,7 +342,7 @@ def refresh_version_state(title): VersionState(title, {"flags": flags}) -def change_node_title(snode, old_title, lang, new_title): +def change_node_title(snode, old_title, lang, new_title, exact_match=False): """ Changes the title of snode specified by old_title and lang, to new_title. If the title changing is the primary english title, cascades to all of the impacted objects @@ -346,20 +350,26 @@ def change_node_title(snode, old_title, lang, new_title): :param old_title: :param lang: :param new_title: + :param exact_match: if True, if there are two links, "X" and "Y on X", changing "X" will not also change "Y on X" :return: """ - + old_ref = new_ref = "" def rewriter(string): - return string.replace(old_title, new_title) + return string.replace(old_ref, new_ref) + # return string.replace(old_title, new_title) def needs_rewrite(string, *args): + if exact_match: + return string.find(old_title) >= 0 and string.startswith(snode.index.title) return string.find(old_title) >= 0 and snode.index.title in string if old_title == snode.primary_title(lang=lang): + old_ref = snode.full_title('en') snode.add_title(new_title, lang, replace_primary=True, primary=True) snode.index.save() library.refresh_index_record_in_cache(snode.index) if lang == 'en': + new_ref = snode.full_title('en') cascade(snode.index.title, rewriter=rewriter, needs_rewrite=needs_rewrite) else: snode.add_title(new_title, lang) From 15bdf96af128e3cb3375571981f233e4a084544c Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Thu, 23 Nov 2023 14:28:42 +0200 Subject: [PATCH 520/756] chore: move picture and captions --- sefaria/google_storage_manager.py | 5 +++-- static/js/AdminEditor.jsx | 7 +++++-- static/js/Misc.jsx | 24 ++++++++++++------------ static/js/TopicEditor.jsx | 11 ++++++----- 4 files changed, 26 insertions(+), 21 deletions(-) diff --git a/sefaria/google_storage_manager.py b/sefaria/google_storage_manager.py index 756dbc60e4..4bd2fa6c23 100644 --- a/sefaria/google_storage_manager.py +++ b/sefaria/google_storage_manager.py @@ -25,8 +25,9 @@ class GoogleStorageManager(object): @classmethod def get_bucket(cls, bucket_name): if getattr(cls, 'client', None) is None: - # for local development, change below line to cls.client = storage.Client(project="production-deployment") - cls.client = storage.Client.from_service_account_json(GOOGLE_APPLICATION_CREDENTIALS_FILEPATH) + # for local development, change below line to + cls.client = storage.Client(project="production-deployment") + #cls.client = storage.Client.from_service_account_json(GOOGLE_APPLICATION_CREDENTIALS_FILEPATH) bucket = cls.client.get_bucket(bucket_name) return bucket diff --git a/static/js/AdminEditor.jsx b/static/js/AdminEditor.jsx index 173b7c5754..1d1ecea94a 100644 --- a/static/js/AdminEditor.jsx +++ b/static/js/AdminEditor.jsx @@ -118,7 +118,7 @@ const validateMarkdownLinks = async (input) => { return true; } -const AdminEditor = ({title, data, close, catMenu, updateData, savingStatus, +const AdminEditor = ({title, data, close, catMenu, pictureUploader, updateData, savingStatus, validate, deleteObj, items = [], isNew = true, extras = [], path = []}) => { const [validatingLinks, setValidatingLinks] = useState(false); @@ -204,7 +204,10 @@ const AdminEditor = ({title, data, close, catMenu, updateData, savingStatus, return null; } else if (x === "Category Menu") { return catMenu; - } else { + } else if (x === "Picture Uploader") { + return pictureUploader; + } + else { return item({...options_for_form[x]}); } })} diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 2988f55fa7..87181e2c2c 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -1609,21 +1609,21 @@ const PictureUploader = ({callback, old_filename, caption}) => { const file = fileInput.current.files[0]; if (file == null) return; - if (/\.(jpe?g|png|gif)$/i.test(file.name)) { - const reader = new FileReader(); + if (/\.(jpe?g|png|gif)$/i.test(file.name)) { + const reader = new FileReader(); - reader.addEventListener("load", function() { - uploadImage(reader.result); - }, false); + reader.addEventListener("load", function() { + uploadImage(reader.result); + }, false); - reader.addEventListener("onerror", function() { - alert(reader.error); - }, false); + reader.addEventListener("onerror", function() { + alert(reader.error); + }, false); - reader.readAsDataURL(file); - } else { - alert('not an image'); - } + reader.readAsDataURL(file); + } else { + alert('not an image'); + } } return <div className="section"> <label><InterfaceText>Picture</InterfaceText></label> diff --git a/static/js/TopicEditor.jsx b/static/js/TopicEditor.jsx index 963f0fcac3..7e1faba58a 100644 --- a/static/js/TopicEditor.jsx +++ b/static/js/TopicEditor.jsx @@ -177,14 +177,15 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { const authorItems = ["English Alternate Titles", "Hebrew Alternate Titles", "Birth Place", "Hebrew Birth Place", "Birth Year", "Place of Death", "Hebrew Place of Death", "Death Year", "Era"]; authorItems.forEach(x => items.push(x)); } + items.push("Picture Uploader"); items.push("English Caption"); items.push("Hebrew Caption"); return <AdminEditor title="Topic Editor" close={close} catMenu={catMenu} data={data} savingStatus={savingStatus} - validate={validate} deleteObj={deleteObj} updateData={updateData} isNew={isNew} - items={items} extras={ - [<PictureUploader callback={handlePictureChange} old_filename={data.image_uri} - caption={{en: data.enImgCaption, he: data.heImgCaption}}/>, - isNew ? null : + validate={validate} deleteObj={deleteObj} updateData={updateData} isNew={isNew} items={items} + pictureUploader={<PictureUploader callback={handlePictureChange} old_filename={data.image_uri} + caption={{en: data.enImgCaption, he: data.heImgCaption}}/>} + extras={ + [isNew ? null : <Reorder subcategoriesAndBooks={sortedSubtopics} updateOrder={setSortedSubtopics} displayType="topics"/>, From 8e57dc3875d39973b53962511be6cbffe9460694 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Thu, 23 Nov 2023 15:03:18 +0200 Subject: [PATCH 521/756] chore: undo mistake to get_bucket --- sefaria/google_storage_manager.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sefaria/google_storage_manager.py b/sefaria/google_storage_manager.py index 4bd2fa6c23..756dbc60e4 100644 --- a/sefaria/google_storage_manager.py +++ b/sefaria/google_storage_manager.py @@ -25,9 +25,8 @@ class GoogleStorageManager(object): @classmethod def get_bucket(cls, bucket_name): if getattr(cls, 'client', None) is None: - # for local development, change below line to - cls.client = storage.Client(project="production-deployment") - #cls.client = storage.Client.from_service_account_json(GOOGLE_APPLICATION_CREDENTIALS_FILEPATH) + # for local development, change below line to cls.client = storage.Client(project="production-deployment") + cls.client = storage.Client.from_service_account_json(GOOGLE_APPLICATION_CREDENTIALS_FILEPATH) bucket = cls.client.get_bucket(bucket_name) return bucket From 965c4eed3bf9005143c3b053e32a79ab7285f570 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Thu, 23 Nov 2023 15:48:25 +0200 Subject: [PATCH 522/756] feat(translations): styles for new feature. --- static/css/s2.css | 73 +++++++++++++++++-- static/js/VersionBlockWithPreview.jsx | 2 +- .../js/VersionBlockWithPreviewTitleLine.jsx | 2 +- static/js/VersionPreviewMeta.jsx | 6 +- 4 files changed, 72 insertions(+), 11 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index a16596b7c5..e1e697e90a 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -3515,11 +3515,61 @@ display: none; align-items: flex-start; margin-bottom: 10px; } -.versionBlock .versionTitle { +.versionBlock .versionTitle, +.versionBlock .versionPreview { font-size: 18px; color: #000; /*unicode-bidi: plaintext;*/ } +.versionBlock .versionPreview { + font-family: "Adobe Garamond Pro", "sans-serif"; +} +.versionBlock .versionPreview i.footnote { + display: none; +} +.versionBlock.with-preview { + --english-font: var(--english-sans-serif-font-family); + --hebrew-font: var(--hebrew-sans-serif-font-family); +} +.version-with-preview-title-line { + display: inline-flex; + flex-wrap: wrap; + font-size: 14px; + line-height: 22px; + color: var(--medium-grey); + margin-top: 10px; + margin-bottom: 10px; +} +.version-with-preview-title-line .open-details { + white-space: nowrap; + display: flex; + margin-right: 5px; + font-style: italic; +} +.version-with-preview-title-line img { + margin-right: 5px; +} +.version-with-preview-title-line .selectButton { + white-space: nowrap; + text-decoration: none; +} +.version-with-preview-title-line .selectButton:not(.currSelectButton) { + color: var(--select-blue); +} +.version-with-preview-title-line .selectButton::before { + content: "•"; + margin-right: 5px; + color: var(--medium-grey); +} +.version-block-with-preview-details { + background-color: var(--lighter-grey); + border-radius: 6px; + outline: 10px solid var(--lighter-grey); + margin: 10px; +} +.versionDetails-version-title { + color: black; +} .bookPage .versionBlock .versionTitle{ font-style: normal; font-weight: normal; @@ -6557,7 +6607,8 @@ But not to use a display block directive that might break continuous mode for ot .connection-buttons.access-user .connection-button.delete-link{ display:none; } -.connection-buttons .connection-button{ +.connection-buttons .connection-button, +.version-block-with-preview-details .connection-button{ font-style: normal; font-weight: normal; font-size: 13px; @@ -6570,6 +6621,11 @@ But not to use a display block directive that might break continuous mode for ot flex-flow: row wrap; align-items: center; } +.version-block-with-preview-details .connection-button { + font-size: 14px; + color: var(--dark-grey); + margin-top: 15px; +} .singlePanel .connection-buttons .connection-button{ text-align: start; margin-inline-end: 5px; @@ -6588,7 +6644,8 @@ But not to use a display block directive that might break continuous mode for ot text-align: center; font-size: 18px; } -.connection-buttons .connection-button::before{ +.connection-buttons .connection-button::before, +.version-block-with-preview-details .connection-button::before{ display: block; content: ' '; background-size: 15px 15px; @@ -6597,7 +6654,11 @@ But not to use a display block directive that might break continuous mode for ot width: 15px; margin-inline-end: 5px; } -.connection-buttons .panel-open-link::before{ +.version-block-with-preview-details .connection-button::before { + height: 18px; +} +.connection-buttons .panel-open-link::before, +.version-block-with-preview-details .connection-button::before{ background-image: url("/static/icons/open-panel.svg"); } .connection-buttons .delete-link::before{ @@ -9289,7 +9350,7 @@ body #keyboardInputMaster tbody tr td table tbody tr td.pressed{ .versionsBox .versionLanguage .versionCount { color: #999; } -.versionsBox a.selectButton { +.versionsBox a.selectButton:not(.version-with-preview-title-line .selectButton) { font-style: normal; font-weight: normal; font-size: 13px; @@ -9301,7 +9362,7 @@ body #keyboardInputMaster tbody tr td table tbody tr td.pressed{ line-height: 18px; cursor: pointer; } -.versionsBox a.selectButton.currSelectButton { +.versionsBox a.selectButton.currSelectButton:not(.version-with-preview-title-line .selectButton) { background-color: #212E50; text-decoration: none; cursor: default; diff --git a/static/js/VersionBlockWithPreview.jsx b/static/js/VersionBlockWithPreview.jsx index 0b517c4029..bf8473ff46 100644 --- a/static/js/VersionBlockWithPreview.jsx +++ b/static/js/VersionBlockWithPreview.jsx @@ -13,7 +13,7 @@ function VersionBlockWithPreview({currentRef, version, currObjectVersions, openV onRangeClick(sref, false, {[version.language]: version.versionTitle}); } return ( - <div className='version-with-preview'> + <div className='versionBlock with-preview'> <VersionBlockHeader text={version.text} onClick={opeInSidebar} diff --git a/static/js/VersionBlockWithPreviewTitleLine.jsx b/static/js/VersionBlockWithPreviewTitleLine.jsx index 326867b49a..a2b449e75b 100644 --- a/static/js/VersionBlockWithPreviewTitleLine.jsx +++ b/static/js/VersionBlockWithPreviewTitleLine.jsx @@ -19,7 +19,7 @@ function VersionBlockWithPreviewTitleLine({currentRef, version, currObjectVersio const buttonText = isSelected ? 'Currently Selected' : 'Select'; return ( <div className='version-with-preview-title-line'> - <a onClick={() => setIsInfoOpen(true)}> + <a className='open-details' onClick={() => setIsInfoOpen(true)}> <img src={`/static/icons/little-chevron-${chevronDirection}.svg`} alt={`${showOrHide} details`} /> <div className='version-with-preview-short-version-title'>{makeShortVersionTitle()}</div> </a> diff --git a/static/js/VersionPreviewMeta.jsx b/static/js/VersionPreviewMeta.jsx index 2c75af4eb2..2d52d197d8 100644 --- a/static/js/VersionPreviewMeta.jsx +++ b/static/js/VersionPreviewMeta.jsx @@ -6,9 +6,9 @@ import VersionDetailsImage from "./VersionDetailsImage"; function VersionPreviewMeta({currentRef, version}) { return ( - <div className='version-preview-meta'> - <div className='version-preview-details'> - <div className='translation-version-title'>{versionTools.makeVersionTitle(version).text}</div> + <div className='versionDetails preview'> + <div className='version-preview-informations'> + <div className='versionDetails-version-title'>{versionTools.makeVersionTitle(version).text}</div> <VersionDetailsInformation currentRef={currentRef} version={version}/> </div> <VersionDetailsImage version={version}/> From b2375ba6e035eba769bc63a1d268b871337735ab Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Thu, 23 Nov 2023 16:05:41 +0200 Subject: [PATCH 523/756] fix(translations): version title in Hebrew when interface is Hebrew and not the opposite. --- static/js/VersionBlockWithPreviewTitleLine.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/VersionBlockWithPreviewTitleLine.jsx b/static/js/VersionBlockWithPreviewTitleLine.jsx index a2b449e75b..5fa8b7ed4d 100644 --- a/static/js/VersionBlockWithPreviewTitleLine.jsx +++ b/static/js/VersionBlockWithPreviewTitleLine.jsx @@ -7,7 +7,7 @@ import Sefaria from "./sefaria/sefaria"; function VersionBlockWithPreviewTitleLine({currentRef, version, currObjectVersions, openVersionInReader, isInfoOpen, setIsInfoOpen, isSelected}) { function makeShortVersionTitle() { let shortVersionTitle = version.shortVersionTitle || version.versionTitle; - if (Sefaria.interfaceLang === "english") { + if (Sefaria.interfaceLang === "hebrew") { shortVersionTitle = version.shortVersionTitleInHebrew || version.versionTitleInHebrew || shortVersionTitle; } return shortVersionTitle; From 2e5e4e670eae3200bcb8caa71878b777aef9a988 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Thu, 23 Nov 2023 16:17:38 +0200 Subject: [PATCH 524/756] feat(translations): ass Hebrew strings for Select and Currently Selected. --- static/js/VersionBlockSelectButton.jsx | 2 +- static/js/sefaria/strings.js | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/static/js/VersionBlockSelectButton.jsx b/static/js/VersionBlockSelectButton.jsx index b8f8c55bea..c1ed1bb527 100644 --- a/static/js/VersionBlockSelectButton.jsx +++ b/static/js/VersionBlockSelectButton.jsx @@ -7,7 +7,7 @@ function VersionBlockSelectButton({link, openVersionInMoinPanel, text, isSelecte href={link} onClick={openVersionInMoinPanel} > - {text} + {Sefaria._(text)} </a> ); } diff --git a/static/js/sefaria/strings.js b/static/js/sefaria/strings.js index c1998554e2..9d549c8370 100644 --- a/static/js/sefaria/strings.js +++ b/static/js/sefaria/strings.js @@ -243,6 +243,8 @@ const Strings = { "Select Version": "בחירת מהדורה", "Select Translation": "בחירת תרגום", "View in Sidebar": "פתיחת תרגום", + 'Select': 'בחירה', + 'Currently Selected': 'נוכחי', "Merged from": "נוצר ממיזוג", "Source" : "מקור", "Sources": "מקורות", From 4e5c804528745caf262ec8026570364052d9e7c8 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Thu, 23 Nov 2023 16:55:44 +0200 Subject: [PATCH 525/756] fix(translations): change right-margin to margin-inline-end for working with Hebrew. --- static/css/s2.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index e1e697e90a..6bdbeeb93e 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -3543,11 +3543,11 @@ display: none; .version-with-preview-title-line .open-details { white-space: nowrap; display: flex; - margin-right: 5px; + margin-inline-end: 5px; font-style: italic; } .version-with-preview-title-line img { - margin-right: 5px; + margin-inline-end: 5px; } .version-with-preview-title-line .selectButton { white-space: nowrap; @@ -3558,7 +3558,7 @@ display: none; } .version-with-preview-title-line .selectButton::before { content: "•"; - margin-right: 5px; + margin-inline-end: 5px; color: var(--medium-grey); } .version-block-with-preview-details { From a4bfd2fe8db8dca2bc95ea28850e7cbc4db34bf1 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Thu, 23 Nov 2023 17:02:44 +0200 Subject: [PATCH 526/756] feat(translations): update TranslationBox when moving segments. --- static/js/TranslationsBox.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/TranslationsBox.jsx b/static/js/TranslationsBox.jsx index 07141565cf..31bf7d8b5d 100644 --- a/static/js/TranslationsBox.jsx +++ b/static/js/TranslationsBox.jsx @@ -23,7 +23,7 @@ class TranslationsBox extends Component { } } componentDidUpdate(prevProps, prevState) { - if (!this.isSheet() && prevProps.sectionRef !== this.props.sectionRef) { + if (!this.isSheet() && prevProps.srefs[0] !== this.props.srefs[0]) { Sefaria.getAllTranslationsWithText(this.props.srefs[0]).then(this.onVersionsLoad); } } From 203a744415e2db6437655210bdd81ffa8e40759c Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Thu, 23 Nov 2023 17:31:09 +0200 Subject: [PATCH 527/756] chore: ignore_cascade --- sefaria/helper/schema.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/sefaria/helper/schema.py b/sefaria/helper/schema.py index c6dadbe762..3073c31d41 100644 --- a/sefaria/helper/schema.py +++ b/sefaria/helper/schema.py @@ -342,7 +342,7 @@ def refresh_version_state(title): VersionState(title, {"flags": flags}) -def change_node_title(snode, old_title, lang, new_title, exact_match=False): +def change_node_title(snode, old_title, lang, new_title, ignore_cascade=False): """ Changes the title of snode specified by old_title and lang, to new_title. If the title changing is the primary english title, cascades to all of the impacted objects @@ -350,26 +350,20 @@ def change_node_title(snode, old_title, lang, new_title, exact_match=False): :param old_title: :param lang: :param new_title: - :param exact_match: if True, if there are two links, "X" and "Y on X", changing "X" will not also change "Y on X" + :param ignore_cascade: :return: """ - old_ref = new_ref = "" def rewriter(string): - return string.replace(old_ref, new_ref) - # return string.replace(old_title, new_title) + return string.replace(old_title, new_title) def needs_rewrite(string, *args): - if exact_match: - return string.find(old_title) >= 0 and string.startswith(snode.index.title) return string.find(old_title) >= 0 and snode.index.title in string if old_title == snode.primary_title(lang=lang): - old_ref = snode.full_title('en') snode.add_title(new_title, lang, replace_primary=True, primary=True) snode.index.save() library.refresh_index_record_in_cache(snode.index) - if lang == 'en': - new_ref = snode.full_title('en') + if lang == 'en' and not ignore_cascade: cascade(snode.index.title, rewriter=rewriter, needs_rewrite=needs_rewrite) else: snode.add_title(new_title, lang) From 562de7b9076dbf93f87998b28c2630f55e4f3d9d Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Thu, 23 Nov 2023 18:14:54 +0200 Subject: [PATCH 528/756] feat(translations): rtl for rel translations. --- static/js/VersionBlockHeader.jsx | 8 ++++++-- static/js/VersionBlockWithPreview.jsx | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/static/js/VersionBlockHeader.jsx b/static/js/VersionBlockHeader.jsx index a16411b6d9..5fbc7605da 100644 --- a/static/js/VersionBlockHeader.jsx +++ b/static/js/VersionBlockHeader.jsx @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from "prop-types"; -function VersionBlockHeader({text, link, onClick, renderMode}) { +function VersionBlockHeader({text, link, onClick, renderMode, direction}) { return renderMode === 'versionTitle' ? (<VersionBlockHeaderTitle link={link} @@ -12,6 +12,7 @@ function VersionBlockHeader({text, link, onClick, renderMode}) { link={link} onClick={onClick} text={text} + direction={direction} />); } VersionBlockHeader.prototypes = { @@ -19,6 +20,7 @@ VersionBlockHeader.prototypes = { text: PropTypes.string.isRequired, renderMode: PropTypes.string.isRequired, link: PropTypes.string.isRequired, + direction: PropTypes.string, }; function VersionBlockHeaderTitle({link, onClick, versionTitle}) { @@ -37,13 +39,14 @@ VersionBlockHeaderTitle.prototypes = { link: PropTypes.string.isRequired, }; -function VersionBlockHeaderText({link, onClick, text}) { +function VersionBlockHeaderText({link, onClick, text, direction}) { return ( <a className='versionPreview' href={link} onClick={onClick} dangerouslySetInnerHTML={{__html: text}} + dir={direction} /> ); } @@ -51,6 +54,7 @@ VersionBlockHeaderText.prototypes = { onClick: PropTypes.func.isRequired, versionTitle: PropTypes.string.isRequired, link: PropTypes.string.isRequired, + direction: PropTypes.string.isRequired, }; export default VersionBlockHeader; diff --git a/static/js/VersionBlockWithPreview.jsx b/static/js/VersionBlockWithPreview.jsx index bf8473ff46..caff81b728 100644 --- a/static/js/VersionBlockWithPreview.jsx +++ b/static/js/VersionBlockWithPreview.jsx @@ -19,6 +19,7 @@ function VersionBlockWithPreview({currentRef, version, currObjectVersions, openV onClick={opeInSidebar} renderMode='contentText' link={versionTools.makeVersionLink(currentRef, version, currObjectVersions, false)} + direction={version.direction || 'ltr'} /> <VersionBlockWithPreviewTitleLine version={version} From 6237432c22374f0bb7aaa2373ec0eb3cd66fbebf Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Sun, 26 Nov 2023 09:03:09 +0200 Subject: [PATCH 529/756] fix(translations): click chevron to close. --- static/js/VersionBlockWithPreviewTitleLine.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/VersionBlockWithPreviewTitleLine.jsx b/static/js/VersionBlockWithPreviewTitleLine.jsx index 5fa8b7ed4d..1936152f31 100644 --- a/static/js/VersionBlockWithPreviewTitleLine.jsx +++ b/static/js/VersionBlockWithPreviewTitleLine.jsx @@ -19,7 +19,7 @@ function VersionBlockWithPreviewTitleLine({currentRef, version, currObjectVersio const buttonText = isSelected ? 'Currently Selected' : 'Select'; return ( <div className='version-with-preview-title-line'> - <a className='open-details' onClick={() => setIsInfoOpen(true)}> + <a className='open-details' onClick={() => setIsInfoOpen(!isInfoOpen)}> <img src={`/static/icons/little-chevron-${chevronDirection}.svg`} alt={`${showOrHide} details`} /> <div className='version-with-preview-short-version-title'>{makeShortVersionTitle()}</div> </a> From 565c00acfcbf45ad6a6971b40f29db4bf444d8fb Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Sun, 26 Nov 2023 09:03:59 +0200 Subject: [PATCH 530/756] refactor(translations): remove superfluous css rules. --- static/css/s2.css | 2 -- 1 file changed, 2 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index 6bdbeeb93e..6ed79714d1 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -3541,7 +3541,6 @@ display: none; margin-bottom: 10px; } .version-with-preview-title-line .open-details { - white-space: nowrap; display: flex; margin-inline-end: 5px; font-style: italic; @@ -3550,7 +3549,6 @@ display: none; margin-inline-end: 5px; } .version-with-preview-title-line .selectButton { - white-space: nowrap; text-decoration: none; } .version-with-preview-title-line .selectButton:not(.currSelectButton) { From b1962404e0c766c15fb523fc8e0a3008df39436e Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Sun, 26 Nov 2023 09:06:46 +0200 Subject: [PATCH 531/756] refactor(translations): replace logic of 'if component else null' by && logic. --- static/js/VersionBlockWithPreview.jsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/static/js/VersionBlockWithPreview.jsx b/static/js/VersionBlockWithPreview.jsx index caff81b728..42d61acd2a 100644 --- a/static/js/VersionBlockWithPreview.jsx +++ b/static/js/VersionBlockWithPreview.jsx @@ -30,7 +30,7 @@ function VersionBlockWithPreview({currentRef, version, currObjectVersions, openV setIsInfoOpen={setIsInfoOpen} isSelected={isSelected} /> - {isInfoOpen ? + {isInfoOpen && <div className='version-block-with-preview-details'> <VersionPreviewMeta currentRef={currentRef} @@ -41,8 +41,7 @@ function VersionBlockWithPreview({currentRef, version, currObjectVersions, openV openInTabCallback={openInTabCallback} renderMode='versionPreview' /> - </div> - : null} + </div>} </div> ); } From 9918da99f238da1c1721a838c23926a21fb53cba Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Sun, 26 Nov 2023 10:17:13 +0200 Subject: [PATCH 532/756] chore: undo unnecessary change to ProfilePic --- static/js/Misc.jsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index b6e3a12b60..6d57aeb1d6 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -262,11 +262,10 @@ class ProfilePic extends Component { if (response.error) { throw new Error(response.error); } else { - const defaultCallback = () => { + this.closePopup({ cb: () => { window.location = "/profile/" + Sefaria.slug; // reload to get update return; - }; - this.closePopup({ cb: this.props.saveCallback ? this.props.saveCallback(response.urls[0]) : defaultCallback}); + }}); } } catch (e) { errored = true; @@ -354,7 +353,6 @@ class ProfilePic extends Component { } ProfilePic.propTypes = { url: PropTypes.string, - saveCallback: PropTypes.func, // used by AdminEditor to override default callback upon save name: PropTypes.string, len: PropTypes.number, hideOnDefault: PropTypes.bool, // hide profile pic if you have are displaying default pic From 07d460529592f8a95386ddfbfe0265647e09accf Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Sun, 26 Nov 2023 11:15:05 +0200 Subject: [PATCH 533/756] fix(Topic Editor): resize images as thumbnails 300x300 --- reader/views.py | 5 +++-- static/js/TopicEditor.jsx | 8 ++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/reader/views.py b/reader/views.py index 868e030af2..39bfd36c24 100644 --- a/reader/views.py +++ b/reader/views.py @@ -64,7 +64,7 @@ get_random_topic_source, edit_topic_source, \ update_order_of_topic_sources, delete_ref_topic_link, update_authors_place_and_time, add_image_to_topic from sefaria.helper.community_page import get_community_page_items -from sefaria.helper.file import get_resized_file +from sefaria.helper.file import get_resized_file, thumbnail_image_file from sefaria.image_generator import make_img_http_response import sefaria.tracker as tracker @@ -3571,12 +3571,13 @@ def topic_upload_photo(request): import base64 bucket_name = GoogleStorageManager.TOPICS_BUCKET img_file_in_mem = BytesIO(base64.b64decode(request.POST.get('file'))) + img = Image.open(img_file_in_mem) + img_file_in_mem = thumbnail_image_file(img, (300, 300)) old_filename = request.POST.get('old_filename') if old_filename: old_filename = f"topics/{old_filename.split('/')[-1]}" img_url = GoogleStorageManager.upload_file(img_file_in_mem, f"topics/{request.user.id}-{uuid.uuid1()}.gif", bucket_name, old_filename=old_filename) - #img_url = 'https://storage.googleapis.com/img.sefaria.org/topics/41861-683e06f6-891a-11ee-be47-4a26184f1ad1.gif' return jsonResponse({"url": img_url}) return jsonResponse({"error": "Unsupported HTTP method."}) diff --git a/static/js/TopicEditor.jsx b/static/js/TopicEditor.jsx index 7e1faba58a..7cb58e97e3 100644 --- a/static/js/TopicEditor.jsx +++ b/static/js/TopicEditor.jsx @@ -84,6 +84,14 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { alert(Sefaria._("Title must be provided.")); return false; } + if (data.enImgCaption.length > 200) { + alert("English caption is too long. It should not be more than 200 characters"); + return false; + } + if (data.heImgCaption.length > 200) { + alert("Hebrew caption is too long. It should not be more than 200 characters") + return false; + } if (sortedSubtopics.length > 0 && !isNew) { await saveReorderedSubtopics(); // make sure subtopics reordered before saving topic information below } From 860451249e6a827810b17d8bf9ff654a0ffe3377 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Sun, 26 Nov 2023 13:09:36 +0200 Subject: [PATCH 534/756] feat(translations): truncate translation with expanding ellipsis. --- static/css/s2.css | 15 +++++++++++++-- static/js/VersionBlockHeader.jsx | 23 ++++++++++++++++++++--- static/js/VersionBlockWithPreview.jsx | 2 +- 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index 6ed79714d1..4f30b5c69a 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -3516,14 +3516,19 @@ display: none; margin-bottom: 10px; } .versionBlock .versionTitle, -.versionBlock .versionPreview { +.versionBlock .versionPreviewWithOptionalEllipsis { font-size: 18px; color: #000; /*unicode-bidi: plaintext;*/ } .versionBlock .versionPreview { + --line-height: 22px; + line-height: var(--line-height); font-family: "Adobe Garamond Pro", "sans-serif"; } +.versionBlock .versionPreview big{ + font-size: inherit; +} .versionBlock .versionPreview i.footnote { display: none; } @@ -3531,6 +3536,11 @@ display: none; --english-font: var(--english-sans-serif-font-family); --hebrew-font: var(--hebrew-sans-serif-font-family); } +.versionBlock .versionPreview.shouldAttemptTruncation { + overflow: hidden; + --max-lines: 5; + max-height: calc(var(--line-height) * var(--max-lines)); +} .version-with-preview-title-line { display: inline-flex; flex-wrap: wrap; @@ -5613,7 +5623,8 @@ But not to use a display block directive that might break continuous mode for ot font-family: "Heebo", sans-serif; } /* Footnotes */ -.segment sup { +.segment sup, +.versionPreview sup { margin-left: .2em; margin-right: .2em; text-decoration: none; diff --git a/static/js/VersionBlockHeader.jsx b/static/js/VersionBlockHeader.jsx index 5fbc7605da..d4ff9c3b8c 100644 --- a/static/js/VersionBlockHeader.jsx +++ b/static/js/VersionBlockHeader.jsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {useEffect, useRef, useState} from 'react'; import PropTypes from "prop-types"; function VersionBlockHeader({text, link, onClick, renderMode, direction}) { @@ -40,14 +40,31 @@ VersionBlockHeaderTitle.prototypes = { }; function VersionBlockHeaderText({link, onClick, text, direction}) { + const [shouldAttemptTruncation, setShouldAttemptTruncation] = useState(true); + const [truncationOccurred, setTruncationOccurred] = useState(false); + const textRef = useRef(null); + useEffect(() => { + const element = textRef.current; + const computedStyles = window.getComputedStyle(element); + const maxHeight = parseInt(computedStyles.getPropertyValue('max-height'), 10); + setTruncationOccurred(element.scrollHeight > maxHeight); + }, []); //[] for running in resize seems better than adding a listener + function onEllipsisClick() { + setShouldAttemptTruncation(false); + setTruncationOccurred(false); + } return ( - <a - className='versionPreview' + <div className='versionPreviewWithOptionalEllipsis'> + <div + className={`versionPreview ${shouldAttemptTruncation && 'shouldAttemptTruncation'}`} + ref={textRef} href={link} onClick={onClick} dangerouslySetInnerHTML={{__html: text}} dir={direction} /> + {truncationOccurred && <a className='ellipsis' onClick={onEllipsisClick}>…</a>} + </div> ); } VersionBlockHeaderText.prototypes = { diff --git a/static/js/VersionBlockWithPreview.jsx b/static/js/VersionBlockWithPreview.jsx index 42d61acd2a..953ae4c95e 100644 --- a/static/js/VersionBlockWithPreview.jsx +++ b/static/js/VersionBlockWithPreview.jsx @@ -1,4 +1,4 @@ -import React, {useState} from 'react'; +import React, {useEffect, useRef, useState} from 'react'; import PropTypes from 'prop-types'; import VersionBlockHeader from "./VersionBlockHeader"; import {versionTools} from './VersionBlock'; From a2fa2683d1c496f00b59389d6f898002f3146f6d Mon Sep 17 00:00:00 2001 From: yonadavGit <yonadav.le@gmail.com> Date: Sun, 26 Nov 2023 13:59:13 +0200 Subject: [PATCH 535/756] chore(ProfilePicIcon): aligned default non-default profile pic button --- static/css/s2.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/css/s2.css b/static/css/s2.css index ef7475bd52..490ccb31d2 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -7083,6 +7083,7 @@ But not to use a display block directive that might break continuous mode for ot justify-content: center; align-items: center; flex-direction: column; + margin-bottom: 4px; } .profile-page { background-color: var(--lightest-grey); @@ -10644,7 +10645,6 @@ cursor: pointer; color: white; font-size: 75px; font-family: "Roboto", "Helvetica Neue", "Helvetica", sans-serif; - margin-bottom: 3px; } .default-profile-img.invisible { visibility: hidden; From 437c0664ed7e5408c8d8c23d40881b27d1d34375 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Sun, 26 Nov 2023 15:12:56 +0200 Subject: [PATCH 536/756] chore: add validation for aspect ratio --- reader/views.py | 10 ++++++++-- static/js/Misc.jsx | 11 +++++++---- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/reader/views.py b/reader/views.py index 39bfd36c24..6c9479423e 100644 --- a/reader/views.py +++ b/reader/views.py @@ -3571,9 +3571,15 @@ def topic_upload_photo(request): import base64 bucket_name = GoogleStorageManager.TOPICS_BUCKET img_file_in_mem = BytesIO(base64.b64decode(request.POST.get('file'))) + + # validate img has correct aspect ratio img = Image.open(img_file_in_mem) - img_file_in_mem = thumbnail_image_file(img, (300, 300)) - old_filename = request.POST.get('old_filename') + aspect_ratio = float(img.width)/img.height + if aspect_ratio < 0.65: + return jsonResponse({"error": f"Width-to-height ratio is {aspect_ratio}. The ratio must be at least 0.65."}) + img_file_in_mem.seek(0) + + old_filename = request.POST.get('old_filename') # delete file from google storage if there is one there if old_filename: old_filename = f"topics/{old_filename.split('/')[-1]}" img_url = GoogleStorageManager.upload_file(img_file_in_mem, f"topics/{request.user.id}-{uuid.uuid1()}.gif", diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 6d57aeb1d6..c2881c86a6 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -1596,10 +1596,13 @@ const PictureUploader = ({callback, old_filename, caption}) => { contentType: false, processData: false, success: function(data) { - callback(data.url); - }, + if (data.error) { + alert(data.error); + } else { + callback(data.url); + }}, error: function(e) { - console.log("photo upload ERROR", e); + alert(e); } }); } @@ -1620,7 +1623,7 @@ const PictureUploader = ({callback, old_filename, caption}) => { reader.readAsDataURL(file); } else { - alert('not an image'); + alert('The file is not an image'); } } return <div className="section"> From be94119a92ec756f4b25458d0e84b8b6c25260ef Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Sun, 26 Nov 2023 16:02:30 +0200 Subject: [PATCH 537/756] feat(translations): chevron svg files. --- static/icons/little-chevron-down.svg | 9 +++++++++ static/icons/little-chevron-up.svg | 9 +++++++++ 2 files changed, 18 insertions(+) create mode 100644 static/icons/little-chevron-down.svg create mode 100644 static/icons/little-chevron-up.svg diff --git a/static/icons/little-chevron-down.svg b/static/icons/little-chevron-down.svg new file mode 100644 index 0000000000..5cef2fecad --- /dev/null +++ b/static/icons/little-chevron-down.svg @@ -0,0 +1,9 @@ +<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<rect x="8" width="8" height="8" transform="rotate(90 8 0)" fill="url(#pattern0)" fill-opacity="0.4"/> +<defs> +<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1"> +<use xlink:href="#image0_704_284" transform="scale(0.00416667)"/> +</pattern> +<image id="image0_704_284" width="240" height="240" xlink:href="data:image/png;base64,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"/> +</defs> +</svg> diff --git a/static/icons/little-chevron-up.svg b/static/icons/little-chevron-up.svg new file mode 100644 index 0000000000..b99c21a478 --- /dev/null +++ b/static/icons/little-chevron-up.svg @@ -0,0 +1,9 @@ +<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<rect y="8" width="8" height="8" transform="rotate(-90 0 8)" fill="url(#pattern0)" fill-opacity="0.4"/> +<defs> +<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1"> +<use xlink:href="#image0_704_282" transform="scale(0.00416667)"/> +</pattern> +<image id="image0_704_282" width="240" height="240" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPAAAADwCAYAAAA+VemSAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAI20lEQVR42u3dbY9VVxmH8SvHCSF96dfxIVqRUiSEICIiIkVERKyIiIiVhj7QVkopUkopUkrpc4uIjTGkUtJanz+KMcaYpmmQ4oszRIoMM7P3Oee/117XL1kh4dXNufeVNcycmYH67AJuSw8hqZkLwCWMWCrSBeAqcBFYmB5G0vxcC9iIpQJdH/BV4E2MWCrGjQEbsVSQmwV8dfrvjVjquJkCNmKpALcK+FrEC9JDSnMxSA/QQYuB8xixCmDAN7cEI1YBDHhmS4BzGLE6zIBvbSlGrA4z4NktBc5ixOogA56bZRixOsiA524Z8BowlR5EusaA52c5w5vYiNUJBjx/y/EmVkcYcDMrMGJ1gAE3twJ4BSNWkAG3sxIjVpABt7cSeAkjVoABj8YqjFgBBjw6q4AX8DXVBPmwjdZqhjexr6smwgdt9FbjTawJ8SEbjzUYsSbAB2x81gBn8DXWGPlwjddajFhj5IM1fmuB0/haawx8qCZjHUasMfCBmpx1wCl8zTVCPkyTtR4j1gj5IE3eeuAkvvYaAd+7m7Fh+s+70oOobN4CORsY3sRSYwactREjVgsGnLcROJEeQmUy4G7YhBGrAQPujk3A8fQQKosBd8tm4Fh6CJXDgLtnC0asOTLgbtoCHE0Poe4z4O7aihFrFgbcbVuBI+kh1F0G3H3bMGLNwIDLsA04nB5C3WPA5bgbOJQeQt1iwGXZjhHrOgZcnu3AwfQQ6gYDLtMOjFgYcMl2AAfSQyjLgMu2EyOumgGXbyfwSHoIZRhwP+wCHkoPockz4P7YjRFXx4D7ZTfwYHoITY4B988ejLgaBtxPe4D700No/Ay4v+4B9qWH0HgZcL/txYh7zYD7b+/0UQ8ZcB324U3cSwZcj70M/1+sHvlYeoCAjwN3pocIuR24AryTHkSjUWPAfwE+ABalBwm5HbgM/D49iNqrMWCAd6k74s9N//vfTQ+idmoNGIYP72WGD3ONFmHExas5YBh+GHmF4YeVNVoEvA/8IT2Imqk9YBh+QqfmiD8PvAf8MT2I5s+Ah94BrgKfTQ8SshgjLpIB/8/bGPG/gT+lB9HcGfBH1R7xHRhxUQz4/70NTAGfTg8ScgfwL+DP6UE0OwO+uUvUHfESjLgIBjyzS8AC4FPpQUKWAP9k+M41dZQB39pb1B3xncA/gL+mB9HNGfDs3gIWAp9MDxKyFCPuLAOem4vAbcAn0oOELAX+DvwtPYg+yoDn7nfUHfEXMGL1wAGGXyuu9WxJL0Bqy4ilwh0kH1LybE4vQGrrEPmQjFhqofaIN6UXILV1mHxIybMxvQCpLSOWCneEfEjJsyG9AKmtmiO+ghGrB46SjykZ8fr0AqS2jpGPyYilFmqPeF16AVJbx8nHZMRSC7VHvDa9AKmtE+RjMmKphdojXpNegNTWSfIxGbHUQu0Rr04vQGpjAJwiH5MRSw3VHvFlYFV6CVIbA+A0+ZiMWGrIiGFleglSGwPgDPmYjFhqyIhhRXoJUhsD4AXyMRmx1NAAeIl8TMmIl6eXILVhxEaswg2AV8jHlDofAMvSS5DaMGIjVuGmgNfIx5SMeGl6CVIbRmzEKtwUcJZ8TMmIl6SXILVhxEaswk0B58jHZMRSQ7VH/D6wOL0EqQ0jNmIVbgo4Tz6mZMSL0kuQ2jBiI1bhFgBvkI8pGfHt6SVIbRhxhREP0gNopGre54fTRypO7bfve8Bn0kuQmqg93io/dFY/+Blo41WhjNcvH6lQvgvLeFUo4/UtlCqU8RqvCuX3AhuvCmW8fg+wCuXPwzJeFcp4/WF2KpQ/E9p4VSjj9Qe6q1C1/14k41Wxao/XX2qmYvm7gY1XhRoAZ8hHlIzXX+ytIhmv8apQA+A0+YiS8a5ML0FqwniNV4UaAKfIR5SMd1V6CVITxmu8KthJ8hGlzhVgdXoBUlPGKxXqBPmIkvGuSS9Aasp4pUIdJx9RMt616QVITRmvVKhj5CNKxrsuvQCpKeOVCnWUfETJeNenFyA1dYR8RMYrNVB7vBvSC5CaOkw+ouTZkF6A1FTt8W5ML0Bq6hD5gIxXaqD2eDelFyA1dZB8QMYrNXCAfEDJszm9AKmp2uPdkl6A1NQj5AMyXqmB2uPdml6A1NRD5AMyXqmBB8kHlDzb0guQmjJeqVD3kw8oee5OL0BqqvZ4t6cXIDW1j3xAxis1UHu8O9ILkJraSz4g45UauId8QMmzM70AqSnjlQq1h3xAybMrvQCpqd3kA0qe3ekFSE1tJx+QN6/U0AXyEaXOnvSLr9EapAfQxPwE2J8eQqNlwHX4KcbbSwbcf/cCD6SH0HgYcL/dC9yXHkLjY8D9dR/Gqx6q4bPQe9MvsibDG7h/HsCbtxoG3C/7GX7GWZUw4P7Yz/BrvaqIAffDwxhvlQy4fA8DP04PoQwDLtvPMN6qGXC5HgV+lB5CWQZcpkeBH6aHUJ4Bl+cxjFfTDLgsjwE/SA+h7jDgcjyO8eoGBlyGx4Hvp4dQ9xhw9/0c49UMDLjbngC+lx5C3WXA3fUE8N30EOo2A+6mJzFezYEBd8+TwHfSQ6gMBtwtT2G8mgcD7o6ngG+nh1BZDLgbnsZ41YAB5z0NfCs9hMpkwFm/wHjVggHnPAN8Mz2EymbAGc8A30gPofIZ8OQ9i/FqRAx4sp7FeDVCBjw5zzGM98P0IOoPA56M54C7MF6NmAGP3/MYr8bEgMfreeDrGK/GxIDH50WMV2NmwOPxIvA1jFdjZsCj9zLGqwkx4NF6GfgqxqsJMeDReRXj1YQZ8Gi8CnwF49WEGXB7r+PNqxADbud1hjfvf9KDSLW4AFwdwTkLTKX/MaqbN3AzvwS+jDevNHFtb+BzePOqI7yB5+dXwJfw5pVimt7A5/HmVcd4A8/Nr4Ev4s0rxc33Bn4DWJAeWtLQfAI2Xqlj5hrwbzBeqXPmErDxSh01W8C/xXilzrpVwMYrddxMARuvVICbBXwBWJgeTNLsbgzYeKWCXB/wmxivVJRrARuvVKALwEWMVyrSToxXPfFfiaB4A5ocBusAAAAASUVORK5CYII="/> +</defs> +</svg> From 59fa18f4fbfa18c5fbe519373dd5d93b4e2396e6 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Mon, 27 Nov 2023 09:58:16 +0200 Subject: [PATCH 538/756] refactor(translations): use contentText rather than specific rules. --- static/css/s2.css | 9 +++++---- static/js/VersionBlockHeader.jsx | 3 +-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index 4f30b5c69a..6374137b58 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -3515,18 +3515,19 @@ display: none; align-items: flex-start; margin-bottom: 10px; } -.versionBlock .versionTitle, -.versionBlock .versionPreviewWithOptionalEllipsis { +.versionBlock .versionTitle { font-size: 18px; color: #000; /*unicode-bidi: plaintext;*/ } +.versionBlock.with-preview .versionPreviewWithOptionalEllipsis { + display: block; +} .versionBlock .versionPreview { --line-height: 22px; line-height: var(--line-height); - font-family: "Adobe Garamond Pro", "sans-serif"; } -.versionBlock .versionPreview big{ +.versionBlock .versionPreview big { font-size: inherit; } .versionBlock .versionPreview i.footnote { diff --git a/static/js/VersionBlockHeader.jsx b/static/js/VersionBlockHeader.jsx index d4ff9c3b8c..6222a9ec25 100644 --- a/static/js/VersionBlockHeader.jsx +++ b/static/js/VersionBlockHeader.jsx @@ -54,14 +54,13 @@ function VersionBlockHeaderText({link, onClick, text, direction}) { setTruncationOccurred(false); } return ( - <div className='versionPreviewWithOptionalEllipsis'> + <div className={`versionPreviewWithOptionalEllipsis contentText ${direction==='ltr' ? 'en' : 'he'}`} dir={direction}> <div className={`versionPreview ${shouldAttemptTruncation && 'shouldAttemptTruncation'}`} ref={textRef} href={link} onClick={onClick} dangerouslySetInnerHTML={{__html: text}} - dir={direction} /> {truncationOccurred && <a className='ellipsis' onClick={onEllipsisClick}>…</a>} </div> From 445e64201420d004b2d1e40a0129d187057ff1c2 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Mon, 27 Nov 2023 13:25:50 +0200 Subject: [PATCH 539/756] chore: DELETE image with topic editor started --- reader/views.py | 26 ++++++++++++++++---------- static/js/Misc.jsx | 17 +++++++++++++---- static/js/TopicEditor.jsx | 2 +- 3 files changed, 30 insertions(+), 15 deletions(-) diff --git a/reader/views.py b/reader/views.py index 6c9479423e..ead137e1f4 100644 --- a/reader/views.py +++ b/reader/views.py @@ -3563,15 +3563,24 @@ def profile_follow_api(request, ftype, slug): @catch_error_as_json def topic_upload_photo(request): + from io import BytesIO + import uuid + import base64 if not request.user.is_authenticated: return jsonResponse({"error": _("You must be logged in to update a topic photo.")}) - if request.method == "POST": - from io import BytesIO - import uuid - import base64 - bucket_name = GoogleStorageManager.TOPICS_BUCKET - img_file_in_mem = BytesIO(base64.b64decode(request.POST.get('file'))) - + bucket_name = GoogleStorageManager.TOPICS_BUCKET + file = request.POST.get('file') + old_filename = request.POST.get('old_filename') # delete file from google storage if there is one there + if old_filename: + old_filename = f"topics/{old_filename.split('/')[-1]}" + if request.method == "DELETE": + if file == "": + if old_filename is None: + return jsonResponse({"error": "You cannot remove an image as you haven't selected one yet."}) + GoogleStorageManager.delete_filename(old_filename, bucket_name) + return jsonResponse({"success": "You have successfully removed the image."}) + elif request.method == "POST": + img_file_in_mem = BytesIO(base64.b64decode(file)) # validate img has correct aspect ratio img = Image.open(img_file_in_mem) aspect_ratio = float(img.width)/img.height @@ -3579,9 +3588,6 @@ def topic_upload_photo(request): return jsonResponse({"error": f"Width-to-height ratio is {aspect_ratio}. The ratio must be at least 0.65."}) img_file_in_mem.seek(0) - old_filename = request.POST.get('old_filename') # delete file from google storage if there is one there - if old_filename: - old_filename = f"topics/{old_filename.split('/')[-1]}" img_url = GoogleStorageManager.upload_file(img_file_in_mem, f"topics/{request.user.id}-{uuid.uuid1()}.gif", bucket_name, old_filename=old_filename) return jsonResponse({"url": img_url}) diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index c2881c86a6..75e7cb8b33 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -1583,7 +1583,7 @@ const PictureUploader = ({callback, old_filename, caption}) => { */ const fileInput = useRef(null); - var uploadImage = function(imageData) { + const uploadImage = function(imageData, type="POST") { const formData = new FormData(); formData.append('file', imageData.replace(/data:image\/(jpe?g|png|gif);base64,/, "")); if (old_filename !== "") { @@ -1591,7 +1591,7 @@ const PictureUploader = ({callback, old_filename, caption}) => { } $.ajax({ url: Sefaria.apiHost + "/api/topics/upload-image", - type: 'POST', + type, data: formData, contentType: false, processData: false, @@ -1599,7 +1599,11 @@ const PictureUploader = ({callback, old_filename, caption}) => { if (data.error) { alert(data.error); } else { - callback(data.url); + if (data.url) { + callback(data.url); + } else if (data.success) { + alert(data.success); + } }}, error: function(e) { alert(e); @@ -1631,7 +1635,12 @@ const PictureUploader = ({callback, old_filename, caption}) => { <div role="button" title={Sefaria._("Add an image")} aria-label="Add an image" className="editorAddInterfaceButton" contentEditable={false} onClick={(e) => e.stopPropagation()} id="addImageButton"> <label htmlFor="addImageFileSelector" id="addImageFileSelectorLabel"></label> </div><input id="addImageFileSelector" type="file" onChange={onFileSelect} ref={fileInput} /> - {old_filename !== "" && <div style={{"max-width": "420px"}}><br/><ImageWithCaption photoLink={old_filename} caption={caption}/></div>} + {old_filename !== "" && <div style={{"max-width": "420px"}}> + <div onClick={() => uploadImage("", "DELETE")} id="saveAccountSettings" className="button small blue control-elem" tabIndex="0" role="button"> + <InterfaceText>Remove Picture</InterfaceText> + </div> + <br/><ImageWithCaption photoLink={old_filename} caption={caption}/></div> + } </div> } diff --git a/static/js/TopicEditor.jsx b/static/js/TopicEditor.jsx index 7cb58e97e3..753dfc5244 100644 --- a/static/js/TopicEditor.jsx +++ b/static/js/TopicEditor.jsx @@ -109,7 +109,7 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { postData.altTitles.en = data.enAltTitles.map(x => x.name); // alt titles implemented using TitleVariants which contains list of objects with 'name' property. postData.altTitles.he = data.heAltTitles.map(x => x.name); - if (data.image_uri !== "") { + if (data.image_uri !== "" || data.enImgCaption !== "" || data.heImgCaption !== "") { postData.image = {"image_uri": data.image_uri, "image_caption": {"en": data.enImgCaption, "he": data.heImgCaption}} } // add descriptions if they changed From 1a09b2fc8f63322c201e13c4ed27daaac50c9bbd Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Mon, 27 Nov 2023 15:03:43 +0200 Subject: [PATCH 540/756] fix(translations): all elements of VersionBlockWithPreviewWTitleLine inline. --- static/css/s2.css | 4 ++-- static/js/VersionBlockWithPreviewTitleLine.jsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index 6374137b58..3122e451b2 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -3543,7 +3543,7 @@ display: none; max-height: calc(var(--line-height) * var(--max-lines)); } .version-with-preview-title-line { - display: inline-flex; + display: inline; flex-wrap: wrap; font-size: 14px; line-height: 22px; @@ -3552,7 +3552,7 @@ display: none; margin-bottom: 10px; } .version-with-preview-title-line .open-details { - display: flex; + display: inline; margin-inline-end: 5px; font-style: italic; } diff --git a/static/js/VersionBlockWithPreviewTitleLine.jsx b/static/js/VersionBlockWithPreviewTitleLine.jsx index 1936152f31..f18001d83e 100644 --- a/static/js/VersionBlockWithPreviewTitleLine.jsx +++ b/static/js/VersionBlockWithPreviewTitleLine.jsx @@ -21,7 +21,7 @@ function VersionBlockWithPreviewTitleLine({currentRef, version, currObjectVersio <div className='version-with-preview-title-line'> <a className='open-details' onClick={() => setIsInfoOpen(!isInfoOpen)}> <img src={`/static/icons/little-chevron-${chevronDirection}.svg`} alt={`${showOrHide} details`} /> - <div className='version-with-preview-short-version-title'>{makeShortVersionTitle()}</div> + <span className='version-with-preview-short-version-title'>{makeShortVersionTitle()}</span> </a> <VersionBlockSelectButton isSelected={isSelected} From ea2cf71701a8392986d087b77aec0ad240760e4e Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Mon, 27 Nov 2023 16:30:13 +0200 Subject: [PATCH 541/756] refactoe(translations): chevron image in css rather than jsx. --- static/css/s2.css | 8 +++++++- static/js/VersionBlockWithPreviewTitleLine.jsx | 4 +--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index 3122e451b2..d2fc3dd8c0 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -3556,7 +3556,13 @@ display: none; margin-inline-end: 5px; font-style: italic; } -.version-with-preview-title-line img { +.version-with-preview-title-line .open-details.chevron-up::before { + content: url('/static/icons/little-chevron-up.svg'); +} +.version-with-preview-title-line .open-details.chevron-down::before { + content: url('/static/icons/little-chevron-down.svg'); +} +.version-with-preview-title-line .open-details::before { margin-inline-end: 5px; } .version-with-preview-title-line .selectButton { diff --git a/static/js/VersionBlockWithPreviewTitleLine.jsx b/static/js/VersionBlockWithPreviewTitleLine.jsx index f18001d83e..2f24c2799c 100644 --- a/static/js/VersionBlockWithPreviewTitleLine.jsx +++ b/static/js/VersionBlockWithPreviewTitleLine.jsx @@ -13,14 +13,12 @@ function VersionBlockWithPreviewTitleLine({currentRef, version, currObjectVersio return shortVersionTitle; } const chevronDirection = isInfoOpen ? 'up' : 'down'; - const showOrHide = isInfoOpen ? 'Hide' : 'Show'; const openVersionInMoinPanel = versionTools.openVersionInMoinPanel.bind(null, currentRef, version, currObjectVersions, 'select-button', null, openVersionInReader); const buttonText = isSelected ? 'Currently Selected' : 'Select'; return ( <div className='version-with-preview-title-line'> - <a className='open-details' onClick={() => setIsInfoOpen(!isInfoOpen)}> - <img src={`/static/icons/little-chevron-${chevronDirection}.svg`} alt={`${showOrHide} details`} /> + <a className={`open-details chevron-${chevronDirection}`} onClick={() => setIsInfoOpen(!isInfoOpen)}> <span className='version-with-preview-short-version-title'>{makeShortVersionTitle()}</span> </a> <VersionBlockSelectButton From 5942d78dae71fbe4be48badb937836532c3cf230 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Tue, 28 Nov 2023 11:28:54 +0200 Subject: [PATCH 542/756] refactor(search): change SEARCH_ADMIN to SEARCH_URL which more clearly defines what the variable is --- build/ci/integration-values.yaml | 2 +- build/ci/production-values.yaml | 2 +- build/ci/sandbox-values.yaml | 2 +- .../templates/configmap/local-settings-file.yaml | 2 +- helm-chart/sefaria-project/values.yaml | 4 +--- reader/views.py | 2 +- sefaria/helper/search.py | 4 ++-- sefaria/local_settings_ci.py | 2 +- sefaria/local_settings_example.py | 2 +- sefaria/search_ES6.py | 4 ++-- 10 files changed, 12 insertions(+), 14 deletions(-) diff --git a/build/ci/integration-values.yaml b/build/ci/integration-values.yaml index cbdf7b6fd0..e8619f44c7 100644 --- a/build/ci/integration-values.yaml +++ b/build/ci/integration-values.yaml @@ -57,7 +57,7 @@ localSettings: DEBUG: true DOMAIN_LANGUAGE: {} APSCHEDULER_NAME: "apscheduler-{{ .Values.deployEnv }}" - SEARCH_ADMIN: "http://elasticsearch-data:9200" + SEARCH_URL: "http://elasticsearch-data:9200" TURN_SERVER: '' USE_CLOUDFLARE: false FRONT_END_URL: "http://${NAME}.integration.sefaria.org" diff --git a/build/ci/production-values.yaml b/build/ci/production-values.yaml index b653021544..f2bb659176 100644 --- a/build/ci/production-values.yaml +++ b/build/ci/production-values.yaml @@ -206,7 +206,7 @@ localSettings: } MONGO_HOST: "mongo" APSCHEDULER_NAME: "apscheduler-{{ .Values.deployEnv }}" - SEARCH_ADMIN: "http://elasticsearch-data:9200" + SEARCH_URL: "http://elasticsearch-data:9200" TURN_SERVER: '' USE_CLOUDFLARE: false FRONT_END_URL: "http://www.sefaria.org" diff --git a/build/ci/sandbox-values.yaml b/build/ci/sandbox-values.yaml index 776d232677..12dcce6766 100644 --- a/build/ci/sandbox-values.yaml +++ b/build/ci/sandbox-values.yaml @@ -53,7 +53,7 @@ localSettings: DEBUG: false DOMAIN_LANGUAGE: {} APSCHEDULER_NAME: "apscheduler-{{ .Values.deployEnv }}" - SEARCH_ADMIN: "http://elasticsearch-data:9200" + SEARCH_URL: "http://elasticsearch-data:9200" TURN_SERVER: '' USE_CLOUDFLARE: false FRONT_END_URL: "http://${NAME}.cauldron.sefaria.org" diff --git a/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml b/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml index 2fd02ba86d..860f93d95b 100644 --- a/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml +++ b/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml @@ -137,7 +137,7 @@ data: SERVER_EMAIL = os.getenv("SERVER_EMAIL") auth_str = f'{os.getenv("ELASTIC_USERNAME")}:{os.getenv("ELASTIC_PASSWORD")}@' if os.getenv("ELASTIC_USERNAME") else '' - SEARCH_ADMIN = f'http://{auth_str}' + {os.getenv("SEARCH_HOST")} + ":9200" + SEARCH_URL = f'http://{auth_str}' + {os.getenv("SEARCH_HOST")} + ":9200" SEARCH_INDEX_ON_SAVE = True SEARCH_INDEX_NAME_TEXT = 'text' # name of the ElasticSearch index to use SEARCH_INDEX_NAME_SHEET = 'sheet' diff --git a/helm-chart/sefaria-project/values.yaml b/helm-chart/sefaria-project/values.yaml index 7ba1f6af3f..a157d02918 100644 --- a/helm-chart/sefaria-project/values.yaml +++ b/helm-chart/sefaria-project/values.yaml @@ -329,9 +329,7 @@ secrets: # SEFARIA_DB: # SEFARIA_DB_USER: # SEFARIA_DB_PASSWORD: - # SEARCH_ADMIN_USER: - # SEARCH_ADMIN_PW: - # SEARCH_ADMIN_K8S: + # SEARCH_URL # TURN_SECRET: # TURN_USER: # SEFARIA_BOT_API_KEY: diff --git a/reader/views.py b/reader/views.py index 81c0878869..be9d24fb2a 100644 --- a/reader/views.py +++ b/reader/views.py @@ -48,7 +48,7 @@ from sefaria.utils.util import text_preview, short_to_long_lang_code, epoch_time from sefaria.utils.hebrew import hebrew_term, has_hebrew from sefaria.utils.calendars import get_all_calendar_items, get_todays_calendar_items, get_keyed_calendar_items, get_parasha, get_todays_parasha -from sefaria.settings import STATIC_URL, USE_VARNISH, USE_NODE, NODE_HOST, DOMAIN_LANGUAGES, MULTISERVER_ENABLED, SEARCH_ADMIN, MULTISERVER_REDIS_SERVER, \ +from sefaria.settings import STATIC_URL, USE_VARNISH, USE_NODE, NODE_HOST, DOMAIN_LANGUAGES, MULTISERVER_ENABLED, MULTISERVER_REDIS_SERVER, \ MULTISERVER_REDIS_PORT, MULTISERVER_REDIS_DB, DISABLE_AUTOCOMPLETER, ENABLE_LINKER from sefaria.site.site_settings import SITE_SETTINGS from sefaria.system.multiserver.coordinator import server_coordinator diff --git a/sefaria/helper/search.py b/sefaria/helper/search.py index 9dd542910e..757c931bfa 100644 --- a/sefaria/helper/search.py +++ b/sefaria/helper/search.py @@ -148,5 +148,5 @@ def make_filter(type, agg_type, agg_key): def get_elasticsearch_client(): from elasticsearch import Elasticsearch - from sefaria.settings import SEARCH_ADMIN - return Elasticsearch(SEARCH_ADMIN) + from sefaria.settings import SEARCH_URL + return Elasticsearch(SEARCH_URL) diff --git a/sefaria/local_settings_ci.py b/sefaria/local_settings_ci.py index de5d56d849..d542f0b58a 100644 --- a/sefaria/local_settings_ci.py +++ b/sefaria/local_settings_ci.py @@ -69,7 +69,7 @@ APSCHEDULER_NAME = "apscheduler" # ElasticSearch server -SEARCH_ADMIN = "http://localhost:9200" +SEARCH_URL = "http://localhost:9200" SEARCH_INDEX_ON_SAVE = False # Whether to send texts and source sheet to Search Host for indexing after save SEARCH_INDEX_NAME_TEXT = 'text' # name of the ElasticSearch index to use SEARCH_INDEX_NAME_SHEET = 'sheet' diff --git a/sefaria/local_settings_example.py b/sefaria/local_settings_example.py index 731ed44876..268f30366f 100644 --- a/sefaria/local_settings_example.py +++ b/sefaria/local_settings_example.py @@ -159,7 +159,7 @@ # URL to connect to ES server. # Set this to https://sefaria.org/api/search to connect to production search. # If ElasticSearch server has a password use the following format: http(s)://{username}:{password}@{base_url} -SEARCH_ADMIN = "http://localhost:9200" +SEARCH_URL = "http://localhost:9200" SEARCH_INDEX_ON_SAVE = False # Whether to send texts and source sheet to Search Host for indexing after save SEARCH_INDEX_NAME_TEXT = 'text' # name of the ElasticSearch index to use diff --git a/sefaria/search_ES6.py b/sefaria/search_ES6.py index fb0047e3af..812610eb07 100644 --- a/sefaria/search_ES6.py +++ b/sefaria/search_ES6.py @@ -33,12 +33,12 @@ from sefaria.system.database import db from sefaria.system.exceptions import InputError from sefaria.utils.util import strip_tags -from .settings import SEARCH_ADMIN, SEARCH_INDEX_NAME_TEXT, SEARCH_INDEX_NAME_SHEET, STATICFILES_DIRS +from .settings import SEARCH_URL, SEARCH_INDEX_NAME_TEXT, SEARCH_INDEX_NAME_SHEET, STATICFILES_DIRS from sefaria.site.site_settings import SITE_SETTINGS from sefaria.utils.hebrew import strip_cantillation import sefaria.model.queue as qu -es_client = Elasticsearch(SEARCH_ADMIN) +es_client = Elasticsearch(SEARCH_URL) index_client = IndicesClient(es_client) tracer = structlog.get_logger(__name__) From 8a99c92810a13caaf1104ea70ff140d3e196be90 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Tue, 28 Nov 2023 11:28:54 +0200 Subject: [PATCH 543/756] refactor(search): change SEARCH_ADMIN to SEARCH_URL which more clearly defines what the variable is --- build/ci/integration-values.yaml | 2 +- build/ci/production-values.yaml | 2 +- build/ci/sandbox-values.yaml | 2 +- .../templates/configmap/local-settings-file.yaml | 2 +- helm-chart/sefaria-project/values.yaml | 4 +--- reader/views.py | 2 +- sefaria/helper/search.py | 4 ++-- sefaria/local_settings_ci.py | 2 +- sefaria/local_settings_example.py | 2 +- sefaria/search_ES6.py | 4 ++-- 10 files changed, 12 insertions(+), 14 deletions(-) diff --git a/build/ci/integration-values.yaml b/build/ci/integration-values.yaml index cbdf7b6fd0..e8619f44c7 100644 --- a/build/ci/integration-values.yaml +++ b/build/ci/integration-values.yaml @@ -57,7 +57,7 @@ localSettings: DEBUG: true DOMAIN_LANGUAGE: {} APSCHEDULER_NAME: "apscheduler-{{ .Values.deployEnv }}" - SEARCH_ADMIN: "http://elasticsearch-data:9200" + SEARCH_URL: "http://elasticsearch-data:9200" TURN_SERVER: '' USE_CLOUDFLARE: false FRONT_END_URL: "http://${NAME}.integration.sefaria.org" diff --git a/build/ci/production-values.yaml b/build/ci/production-values.yaml index b653021544..f2bb659176 100644 --- a/build/ci/production-values.yaml +++ b/build/ci/production-values.yaml @@ -206,7 +206,7 @@ localSettings: } MONGO_HOST: "mongo" APSCHEDULER_NAME: "apscheduler-{{ .Values.deployEnv }}" - SEARCH_ADMIN: "http://elasticsearch-data:9200" + SEARCH_URL: "http://elasticsearch-data:9200" TURN_SERVER: '' USE_CLOUDFLARE: false FRONT_END_URL: "http://www.sefaria.org" diff --git a/build/ci/sandbox-values.yaml b/build/ci/sandbox-values.yaml index 776d232677..12dcce6766 100644 --- a/build/ci/sandbox-values.yaml +++ b/build/ci/sandbox-values.yaml @@ -53,7 +53,7 @@ localSettings: DEBUG: false DOMAIN_LANGUAGE: {} APSCHEDULER_NAME: "apscheduler-{{ .Values.deployEnv }}" - SEARCH_ADMIN: "http://elasticsearch-data:9200" + SEARCH_URL: "http://elasticsearch-data:9200" TURN_SERVER: '' USE_CLOUDFLARE: false FRONT_END_URL: "http://${NAME}.cauldron.sefaria.org" diff --git a/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml b/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml index 2fd02ba86d..860f93d95b 100644 --- a/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml +++ b/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml @@ -137,7 +137,7 @@ data: SERVER_EMAIL = os.getenv("SERVER_EMAIL") auth_str = f'{os.getenv("ELASTIC_USERNAME")}:{os.getenv("ELASTIC_PASSWORD")}@' if os.getenv("ELASTIC_USERNAME") else '' - SEARCH_ADMIN = f'http://{auth_str}' + {os.getenv("SEARCH_HOST")} + ":9200" + SEARCH_URL = f'http://{auth_str}' + {os.getenv("SEARCH_HOST")} + ":9200" SEARCH_INDEX_ON_SAVE = True SEARCH_INDEX_NAME_TEXT = 'text' # name of the ElasticSearch index to use SEARCH_INDEX_NAME_SHEET = 'sheet' diff --git a/helm-chart/sefaria-project/values.yaml b/helm-chart/sefaria-project/values.yaml index 7ba1f6af3f..a157d02918 100644 --- a/helm-chart/sefaria-project/values.yaml +++ b/helm-chart/sefaria-project/values.yaml @@ -329,9 +329,7 @@ secrets: # SEFARIA_DB: # SEFARIA_DB_USER: # SEFARIA_DB_PASSWORD: - # SEARCH_ADMIN_USER: - # SEARCH_ADMIN_PW: - # SEARCH_ADMIN_K8S: + # SEARCH_URL # TURN_SECRET: # TURN_USER: # SEFARIA_BOT_API_KEY: diff --git a/reader/views.py b/reader/views.py index 81c0878869..be9d24fb2a 100644 --- a/reader/views.py +++ b/reader/views.py @@ -48,7 +48,7 @@ from sefaria.utils.util import text_preview, short_to_long_lang_code, epoch_time from sefaria.utils.hebrew import hebrew_term, has_hebrew from sefaria.utils.calendars import get_all_calendar_items, get_todays_calendar_items, get_keyed_calendar_items, get_parasha, get_todays_parasha -from sefaria.settings import STATIC_URL, USE_VARNISH, USE_NODE, NODE_HOST, DOMAIN_LANGUAGES, MULTISERVER_ENABLED, SEARCH_ADMIN, MULTISERVER_REDIS_SERVER, \ +from sefaria.settings import STATIC_URL, USE_VARNISH, USE_NODE, NODE_HOST, DOMAIN_LANGUAGES, MULTISERVER_ENABLED, MULTISERVER_REDIS_SERVER, \ MULTISERVER_REDIS_PORT, MULTISERVER_REDIS_DB, DISABLE_AUTOCOMPLETER, ENABLE_LINKER from sefaria.site.site_settings import SITE_SETTINGS from sefaria.system.multiserver.coordinator import server_coordinator diff --git a/sefaria/helper/search.py b/sefaria/helper/search.py index 9dd542910e..757c931bfa 100644 --- a/sefaria/helper/search.py +++ b/sefaria/helper/search.py @@ -148,5 +148,5 @@ def make_filter(type, agg_type, agg_key): def get_elasticsearch_client(): from elasticsearch import Elasticsearch - from sefaria.settings import SEARCH_ADMIN - return Elasticsearch(SEARCH_ADMIN) + from sefaria.settings import SEARCH_URL + return Elasticsearch(SEARCH_URL) diff --git a/sefaria/local_settings_ci.py b/sefaria/local_settings_ci.py index de5d56d849..d542f0b58a 100644 --- a/sefaria/local_settings_ci.py +++ b/sefaria/local_settings_ci.py @@ -69,7 +69,7 @@ APSCHEDULER_NAME = "apscheduler" # ElasticSearch server -SEARCH_ADMIN = "http://localhost:9200" +SEARCH_URL = "http://localhost:9200" SEARCH_INDEX_ON_SAVE = False # Whether to send texts and source sheet to Search Host for indexing after save SEARCH_INDEX_NAME_TEXT = 'text' # name of the ElasticSearch index to use SEARCH_INDEX_NAME_SHEET = 'sheet' diff --git a/sefaria/local_settings_example.py b/sefaria/local_settings_example.py index 731ed44876..268f30366f 100644 --- a/sefaria/local_settings_example.py +++ b/sefaria/local_settings_example.py @@ -159,7 +159,7 @@ # URL to connect to ES server. # Set this to https://sefaria.org/api/search to connect to production search. # If ElasticSearch server has a password use the following format: http(s)://{username}:{password}@{base_url} -SEARCH_ADMIN = "http://localhost:9200" +SEARCH_URL = "http://localhost:9200" SEARCH_INDEX_ON_SAVE = False # Whether to send texts and source sheet to Search Host for indexing after save SEARCH_INDEX_NAME_TEXT = 'text' # name of the ElasticSearch index to use diff --git a/sefaria/search_ES6.py b/sefaria/search_ES6.py index fb0047e3af..812610eb07 100644 --- a/sefaria/search_ES6.py +++ b/sefaria/search_ES6.py @@ -33,12 +33,12 @@ from sefaria.system.database import db from sefaria.system.exceptions import InputError from sefaria.utils.util import strip_tags -from .settings import SEARCH_ADMIN, SEARCH_INDEX_NAME_TEXT, SEARCH_INDEX_NAME_SHEET, STATICFILES_DIRS +from .settings import SEARCH_URL, SEARCH_INDEX_NAME_TEXT, SEARCH_INDEX_NAME_SHEET, STATICFILES_DIRS from sefaria.site.site_settings import SITE_SETTINGS from sefaria.utils.hebrew import strip_cantillation import sefaria.model.queue as qu -es_client = Elasticsearch(SEARCH_ADMIN) +es_client = Elasticsearch(SEARCH_URL) index_client = IndicesClient(es_client) tracer = structlog.get_logger(__name__) From 5605887ab3962919987254047ad85437646badcd Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Tue, 28 Nov 2023 11:35:24 +0200 Subject: [PATCH 544/756] chore(search): update cronjob schedule so that ES 8 will be indexed ASAP --- .../templates/cronjob/reindex-elasticsearch.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml b/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml index 6c5aa13784..92bac4cca2 100644 --- a/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml +++ b/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml @@ -7,7 +7,7 @@ metadata: labels: {{- include "sefaria.labels" . | nindent 4 }} spec: - schedule: "20 13 * * 0" + schedule: "0 0 * * 3" jobTemplate: spec: backoffLimit: 1 From b7af63679085f2a0846c77eded25fc6ace921fda Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Tue, 28 Nov 2023 13:59:09 +0200 Subject: [PATCH 545/756] feat(Topic Editor): remove image button --- reader/views.py | 30 ++++++++++++++++++------------ sefaria/helper/topic.py | 6 +++++- sefaria/urls.py | 2 ++ static/js/Misc.jsx | 26 +++++++++++++++++++------- static/js/TopicEditor.jsx | 7 ++++++- 5 files changed, 50 insertions(+), 21 deletions(-) diff --git a/reader/views.py b/reader/views.py index ead137e1f4..6846950a46 100644 --- a/reader/views.py +++ b/reader/views.py @@ -3561,6 +3561,20 @@ def profile_follow_api(request, ftype, slug): return jsonResponse(response) return jsonResponse({"error": "Unsupported HTTP method."}) + +@catch_error_as_json +def topic_delete_photo(request, file): + if not request.user.is_authenticated: + return jsonResponse({"error": _("You must be logged in to update a topic photo.")}) + bucket_name = GoogleStorageManager.TOPICS_BUCKET + if file: + file = f"topics/{file.split('/')[-1]}" + if request.method == "DELETE": + if file is None: + return jsonResponse({"error": "You cannot remove an image as you haven't selected one yet."}) + GoogleStorageManager.delete_filename(file, bucket_name) + return jsonResponse({"success": "You have successfully removed the image."}) + @catch_error_as_json def topic_upload_photo(request): from io import BytesIO @@ -3574,20 +3588,12 @@ def topic_upload_photo(request): if old_filename: old_filename = f"topics/{old_filename.split('/')[-1]}" if request.method == "DELETE": - if file == "": - if old_filename is None: - return jsonResponse({"error": "You cannot remove an image as you haven't selected one yet."}) - GoogleStorageManager.delete_filename(old_filename, bucket_name) - return jsonResponse({"success": "You have successfully removed the image."}) + if old_filename is None: + return jsonResponse({"error": "You cannot remove an image as you haven't selected one yet."}) + GoogleStorageManager.delete_filename(old_filename, bucket_name) + return jsonResponse({"success": "You have successfully removed the image."}) elif request.method == "POST": img_file_in_mem = BytesIO(base64.b64decode(file)) - # validate img has correct aspect ratio - img = Image.open(img_file_in_mem) - aspect_ratio = float(img.width)/img.height - if aspect_ratio < 0.65: - return jsonResponse({"error": f"Width-to-height ratio is {aspect_ratio}. The ratio must be at least 0.65."}) - img_file_in_mem.seek(0) - img_url = GoogleStorageManager.upload_file(img_file_in_mem, f"topics/{request.user.id}-{uuid.uuid1()}.gif", bucket_name, old_filename=old_filename) return jsonResponse({"url": img_url}) diff --git a/sefaria/helper/topic.py b/sefaria/helper/topic.py index a50b71ce74..d9c3bcd41b 100644 --- a/sefaria/helper/topic.py +++ b/sefaria/helper/topic.py @@ -1106,7 +1106,11 @@ def update_topic(topic, **kwargs): topic.change_description(kwargs.get("description", None), kwargs.get("categoryDescription", None)) if "image" in kwargs: - topic.image = kwargs["image"] + image_dict = kwargs["image"] + if image_dict["image_uri"] != "": + topic.image = kwargs["image"] + elif hasattr(topic, 'image'): + del topic.image topic.save() diff --git a/sefaria/urls.py b/sefaria/urls.py index 929467238c..c1ae9404f9 100644 --- a/sefaria/urls.py +++ b/sefaria/urls.py @@ -104,6 +104,8 @@ url(r'^topics/(?P<topic>.+)$', reader_views.topic_page), url(r'^api/topic/completion/(?P<topic>.+)', reader_views.topic_completion_api), url(r'^api/topics/upload-image$', reader_views.topic_upload_photo), + url(r'^api/topics/delete-image/(?P<file>.+)$', reader_views.topic_delete_photo), + ] # Calendar Redirects diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 75e7cb8b33..da34eeb963 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -1601,8 +1601,6 @@ const PictureUploader = ({callback, old_filename, caption}) => { } else { if (data.url) { callback(data.url); - } else if (data.success) { - alert(data.success); } }}, error: function(e) { @@ -1630,16 +1628,30 @@ const PictureUploader = ({callback, old_filename, caption}) => { alert('The file is not an image'); } } + const deleteImage = () => { + const old_filename_wout_url = old_filename.split("/").slice(-1); + const url = `${Sefaria.apiHost}/api/topics/delete-image/${old_filename_wout_url}`; + requestWithCallBack({url, type: "DELETE", redirect: () => alert("Deleted image.")}); + callback(""); + fileInput.current.value = ""; + } return <div className="section"> <label><InterfaceText>Picture</InterfaceText></label> + <label> + <span className="optional"><InterfaceText>Please use horizontal, square, or only-slightly-vertical images for best results.</InterfaceText></span> + </label> <div role="button" title={Sefaria._("Add an image")} aria-label="Add an image" className="editorAddInterfaceButton" contentEditable={false} onClick={(e) => e.stopPropagation()} id="addImageButton"> - <label htmlFor="addImageFileSelector" id="addImageFileSelectorLabel"></label> - </div><input id="addImageFileSelector" type="file" onChange={onFileSelect} ref={fileInput} /> + <label htmlFor="addImageFileSelector" id="addImageFileSelectorLabel"> + <div className="button small blue control-elem" tabIndex="0" role="button"> + <InterfaceText>Upload Picture</InterfaceText> + </div> + </label> + </div><input style={{visibility: "hidden"}} id="addImageFileSelector" type="file" onChange={onFileSelect} ref={fileInput} /> {old_filename !== "" && <div style={{"max-width": "420px"}}> - <div onClick={() => uploadImage("", "DELETE")} id="saveAccountSettings" className="button small blue control-elem" tabIndex="0" role="button"> + <br/><ImageWithCaption photoLink={old_filename} caption={caption}/> + <br/><div onClick={deleteImage} className="button extraSmall blue control-elem" tabIndex="1" role="button"> <InterfaceText>Remove Picture</InterfaceText> - </div> - <br/><ImageWithCaption photoLink={old_filename} caption={caption}/></div> + </div></div> } </div> } diff --git a/static/js/TopicEditor.jsx b/static/js/TopicEditor.jsx index 753dfc5244..7fed81d4f8 100644 --- a/static/js/TopicEditor.jsx +++ b/static/js/TopicEditor.jsx @@ -109,9 +109,14 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { postData.altTitles.en = data.enAltTitles.map(x => x.name); // alt titles implemented using TitleVariants which contains list of objects with 'name' property. postData.altTitles.he = data.heAltTitles.map(x => x.name); - if (data.image_uri !== "" || data.enImgCaption !== "" || data.heImgCaption !== "") { + // add image if image or caption changed + const origImageURI = origData?.origImage?.image_uri || ""; + const origEnCaption = origData?.origImage?.image_caption?.en || ""; + const origHeCaption = origData?.origImage?.image_caption?.he || ""; + if (data.image_uri !== origImageURI || data.enImgCaption !== origEnCaption || data.heImgCaption !== origHeCaption) { postData.image = {"image_uri": data.image_uri, "image_caption": {"en": data.enImgCaption, "he": data.heImgCaption}} } + // add descriptions if they changed const origDescription = {en: origData?.origEnDescription || "", he: origData?.origHeDescription || ""}; const origCategoryDescription = {en: origData?.origEnCategoryDescription || "", he: origData?.origHeCategoryDescription || ""}; From cd7e6b71a67b1241dcf26a2164812d345f243da4 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Tue, 28 Nov 2023 14:41:14 +0200 Subject: [PATCH 546/756] fix(search): add in missing elasticUser secret so that nginx can proxy /api/search API --- helm-chart/sefaria-project/templates/rollout/nginx.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/helm-chart/sefaria-project/templates/rollout/nginx.yaml b/helm-chart/sefaria-project/templates/rollout/nginx.yaml index e56954eb29..ec9342c8a8 100644 --- a/helm-chart/sefaria-project/templates/rollout/nginx.yaml +++ b/helm-chart/sefaria-project/templates/rollout/nginx.yaml @@ -105,6 +105,8 @@ spec: - secretRef: name: local-settings-nginx-secrets-{{ .Values.deployEnv }} optional: true + - secretRef: + name: {{ template "sefaria.secrets.elasticUser" . }} volumes: - name: nginx-conf configMap: From c8f84a82ab3980d585cca29d276dcc5495cb56e8 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Tue, 28 Nov 2023 14:46:46 +0200 Subject: [PATCH 547/756] chore(search): remove unused elasticAdmin secret from ES6 cronjob --- .../templates/cronjob/reindex-elasticsearch-es6.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch-es6.yaml b/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch-es6.yaml index 2253eba8cc..748e30d1f2 100644 --- a/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch-es6.yaml +++ b/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch-es6.yaml @@ -44,8 +44,6 @@ spec: name: {{ template "sefaria.secrets.slackWebhook" . }} key: slack-webhook envFrom: - - secretRef: - name: {{ template "sefaria.secrets.elasticAdmin" . }} - secretRef: name: {{ .Values.secrets.localSettings.ref }} optional: true From b3bd1fca02432c3cbefb8e628b1187bae46fdf69 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Tue, 28 Nov 2023 14:59:07 +0200 Subject: [PATCH 548/756] chore(search): distinguish where each reindex cronjob connects --- .../templates/cronjob/reindex-elasticsearch-es6.yaml | 2 ++ .../templates/cronjob/reindex-elasticsearch.yaml | 2 ++ helm-chart/sefaria-project/values.yaml | 2 ++ 3 files changed, 6 insertions(+) diff --git a/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch-es6.yaml b/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch-es6.yaml index 748e30d1f2..9d15f9fb38 100644 --- a/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch-es6.yaml +++ b/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch-es6.yaml @@ -32,6 +32,8 @@ spec: requests: memory: 7Gi env: + - name: SEARCH_HOST + value: "{{ .Values.cronjobs.reindexElasticSearch.SEARCH_HOST_ES6 }}" - name: REDIS_HOST value: "redis-{{ .Values.deployEnv }}" - name: NODEJS_HOST diff --git a/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml b/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml index 92bac4cca2..7bc5124fd3 100644 --- a/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml +++ b/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml @@ -32,6 +32,8 @@ spec: requests: memory: 7Gi env: + - name: SEARCH_HOST + value: "{{ .Values.cronjobs.reindexElasticSearch.SEARCH_HOST_ES8 }}" - name: REDIS_HOST value: "redis-{{ .Values.deployEnv }}" - name: NODEJS_HOST diff --git a/helm-chart/sefaria-project/values.yaml b/helm-chart/sefaria-project/values.yaml index a157d02918..232e3d3be8 100644 --- a/helm-chart/sefaria-project/values.yaml +++ b/helm-chart/sefaria-project/values.yaml @@ -399,6 +399,8 @@ cronJobs: enabled: false reindexElasticSearch: enabled: false + SEARCH_HOST_ES6: "elasticsearch-data" + SEARCH_HOST_ES8: "elasticsearch-es-http.elasticsearch.svc" topicsIndexing: enabled: false trello: From 536aaf4aef69ff28d0edff3280861456b640a0df Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Tue, 28 Nov 2023 16:15:41 +0200 Subject: [PATCH 549/756] feat(text helper): function for modifying texts all over texts collection. --- sefaria/helper/text.py | 53 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/sefaria/helper/text.py b/sefaria/helper/text.py index 0c76433662..f8efbd51ac 100644 --- a/sefaria/helper/text.py +++ b/sefaria/helper/text.py @@ -1,6 +1,10 @@ # encoding=utf-8 +import unicodecsv as csv +import io import re +import pymongo + from sefaria.model import * from sefaria.system.database import db from sefaria.datatype.jagged_array import JaggedTextArray @@ -249,6 +253,49 @@ def modify_text_by_function(title, vtitle, lang, rewrite_function, uid, needs_re modify_text(uid, oref, vtitle, lang, modified_text, **kwargs) +def modify_many_texts_and_make_report(rewrite_function, versions_query=None, return_zeros=False): + """ + Uses pymongo because iterating and saving all texts as Version is heavy. + That means - be CAREFUL with that. + + :param rewrite_function(string) -> (string, int of times that string has been replaced) + :param versions_query - query dict for VersionSet, or None for all + :param return_zeros - bool whether you want cases of 0 replaces in report + :returns a csv writer with index title, versionTitle, and number of replacements + """ + def replace_in_text_object(text_obj): + total = 0 + if isinstance(text_obj, dict): + for key in text_obj: + text_obj[key], num = replace_in_text_object(text_obj[key]) + total += num + elif isinstance(text_obj, list): + for i, _ in enumerate(text_obj): + text_obj[i], num = replace_in_text_object(text_obj[i]) + total += num + elif isinstance(text_obj, str): + return rewrite_function(text_obj) + return text_obj, total + texts_collection = db.texts + versions_to_change = texts_collection.find(versions_query) + bulk_operations = [] + output = io.BytesIO() + report = csv.writer(output) + report.writerow(['index', 'versionTitle', 'replaces number']) + for version in versions_to_change: + new_text, replaces = replace_in_text_object(version['chapter']) + if replaces or return_zeros: + report.writerow([version['title'], version['versionTitle'], replaces]) + if replaces: + bulk_operations.append(pymongo.UpdateOne( + {'_id': version['_id']}, + {'$set': {'chapter': new_text}} + )) + if bulk_operations: + texts_collection.bulk_write(bulk_operations) + return output.getvalue() + + def split_text_section(oref, lang, old_version_title, new_version_title): """ Splits the text in `old_version_title` so that the content covered by `oref` now appears in `new_version_title`. @@ -334,8 +381,6 @@ def make_versions_csv(): """ Returns a CSV of all text versions in the DB. """ - import csv - import io output = io.BytesIO() writer = csv.writer(output) fields = [ @@ -359,8 +404,6 @@ def make_versions_csv(): def get_core_link_stats(): - import csv - import io from sefaria.model.link import get_category_category_linkset output = io.BytesIO() writer = csv.writer(output) @@ -439,8 +482,6 @@ def aggregate_stats(toc_node, path): return simple_nodes tree = aggregate_stats(library.get_toc(), []) - import csv - import io from operator import sub output = io.BytesIO() writer = csv.writer(output) From f6f3dba39dcf277f5a8987452507bcbf5dd42e6d Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Tue, 28 Nov 2023 16:45:46 +0200 Subject: [PATCH 550/756] fix(translations): fix font for versionPreviewWithOptionalEllipsis. --- static/css/s2.css | 3 +++ static/js/VersionBlockHeader.jsx | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/static/css/s2.css b/static/css/s2.css index d2fc3dd8c0..a16516ba76 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -3521,6 +3521,9 @@ display: none; /*unicode-bidi: plaintext;*/ } .versionBlock.with-preview .versionPreviewWithOptionalEllipsis { + --english-font: "adobe-garamond-pro", var(--english-serif-font-family); + --hebrew-font: var(--hebrew-serif-font-family); + font-size: 18px; display: block; } .versionBlock .versionPreview { diff --git a/static/js/VersionBlockHeader.jsx b/static/js/VersionBlockHeader.jsx index 6222a9ec25..d61b718bdf 100644 --- a/static/js/VersionBlockHeader.jsx +++ b/static/js/VersionBlockHeader.jsx @@ -54,7 +54,7 @@ function VersionBlockHeaderText({link, onClick, text, direction}) { setTruncationOccurred(false); } return ( - <div className={`versionPreviewWithOptionalEllipsis contentText ${direction==='ltr' ? 'en' : 'he'}`} dir={direction}> + <div className={'versionPreviewWithOptionalEllipsis'} dir={direction}> <div className={`versionPreview ${shouldAttemptTruncation && 'shouldAttemptTruncation'}`} ref={textRef} From f5bab3020b132e1fb155a16eb6ded456b3b4c9b9 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Tue, 28 Nov 2023 17:48:05 +0200 Subject: [PATCH 551/756] fix(translations): fix everything for elements that should have cursor pointer will have it. --- static/css/s2.css | 1 + static/js/VersionBlock.jsx | 2 +- static/js/VersionBlockHeader.jsx | 2 +- static/js/VersionBlockWithPreviewTitleLine.jsx | 4 ++-- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index a16516ba76..dda88b522d 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -3541,6 +3541,7 @@ display: none; --hebrew-font: var(--hebrew-sans-serif-font-family); } .versionBlock .versionPreview.shouldAttemptTruncation { + display: block; overflow: hidden; --max-lines: 5; max-height: calc(var(--line-height) * var(--max-lines)); diff --git a/static/js/VersionBlock.jsx b/static/js/VersionBlock.jsx index fab50dc515..aaba6924f8 100644 --- a/static/js/VersionBlock.jsx +++ b/static/js/VersionBlock.jsx @@ -29,7 +29,7 @@ class versionTools { const withParam = mainPanel ? "" : "&with=Translation Open"; const versionParam = mainPanel ? version.language : 'side'; const nonSelectedVersionParams = Object.entries(currObjectVersions) - .filter(([vlang, ver]) => !!ver && !!ver?.versionTitle && !version?.merged && (withParam || vlang !== version.language)) // in 'side' case, keep all version params + .filter(([vlang, ver]) => !!ver && !!ver?.versionTitle && !version?.merged && (withParam || vlang === version.language)) // in 'side' case, keep all version params .map(([vlang, ver]) => `&v${vlang}=${ver.versionTitle.replace(/\s/g,'_')}`) .join(""); const versionLink = nonSelectedVersionParams === "" ? null : `/${Sefaria.normRef(currRef)}${nonSelectedVersionParams}&v${versionParam}=${version.versionTitle.replace(/\s/g,'_')}${withParam}`.replace("&","?"); diff --git a/static/js/VersionBlockHeader.jsx b/static/js/VersionBlockHeader.jsx index d61b718bdf..6bcc015464 100644 --- a/static/js/VersionBlockHeader.jsx +++ b/static/js/VersionBlockHeader.jsx @@ -55,7 +55,7 @@ function VersionBlockHeaderText({link, onClick, text, direction}) { } return ( <div className={'versionPreviewWithOptionalEllipsis'} dir={direction}> - <div + <a className={`versionPreview ${shouldAttemptTruncation && 'shouldAttemptTruncation'}`} ref={textRef} href={link} diff --git a/static/js/VersionBlockWithPreviewTitleLine.jsx b/static/js/VersionBlockWithPreviewTitleLine.jsx index 2f24c2799c..8201f6b707 100644 --- a/static/js/VersionBlockWithPreviewTitleLine.jsx +++ b/static/js/VersionBlockWithPreviewTitleLine.jsx @@ -18,8 +18,8 @@ function VersionBlockWithPreviewTitleLine({currentRef, version, currObjectVersio const buttonText = isSelected ? 'Currently Selected' : 'Select'; return ( <div className='version-with-preview-title-line'> - <a className={`open-details chevron-${chevronDirection}`} onClick={() => setIsInfoOpen(!isInfoOpen)}> - <span className='version-with-preview-short-version-title'>{makeShortVersionTitle()}</span> + <a className={`open-details chevron-${chevronDirection}`} onClick={() => setIsInfoOpen(!isInfoOpen)} href='#'> + {makeShortVersionTitle()} </a> <VersionBlockSelectButton isSelected={isSelected} From 3dbaa1a48cdf5e3675ec64e7ac88882a67ec651d Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Tue, 28 Nov 2023 19:14:09 +0200 Subject: [PATCH 552/756] fix(translations): typo. --- static/js/VersionBlock.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/VersionBlock.jsx b/static/js/VersionBlock.jsx index aaba6924f8..4ed71caadd 100644 --- a/static/js/VersionBlock.jsx +++ b/static/js/VersionBlock.jsx @@ -204,7 +204,7 @@ class VersionBlock extends Component { const openVersionInSidebar = versionTools.openVersionInSidebar.bind(null, this.props.currentRef, this.props.version, this.props.currObjectVersions, this.props.openVersionInSidebar); const openVersionInMoinPanel = versionTools.openVersionInMoinPanel.bind(null, this.props.currentRef, - this.props.version, this.props.currObjectVersions, this.props.renderMode, this.props.firstSectionRef, this.props.openVersionInReader); + this.props.version, this.props.currObjectVersions, this.props.rendermode, this.props.firstSectionRef, this.props.openVersionInReader); if (this.state.editing && Sefaria.is_moderator) { // Editing View From f74f953e491c593ed81c3cd17b3f3008081131f8 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Tue, 28 Nov 2023 19:18:01 +0200 Subject: [PATCH 553/756] fix(translations): default cursor for 'currently selected'. --- static/css/s2.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/static/css/s2.css b/static/css/s2.css index dda88b522d..571f469146 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -3572,6 +3572,9 @@ display: none; .version-with-preview-title-line .selectButton { text-decoration: none; } +.version-with-preview-title-line .selectButton.currSelectButton { + cursor: default; +} .version-with-preview-title-line .selectButton:not(.currSelectButton) { color: var(--select-blue); } From 1976025f4ed947d4321fe5f15d9e7a6ee21d61ae Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Tue, 18 Jul 2023 14:45:55 +0300 Subject: [PATCH 554/756] fix(search): update to use total.value based on new datastructure returned from ES 8 --- static/js/SearchResultList.jsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/static/js/SearchResultList.jsx b/static/js/SearchResultList.jsx index fcce157f83..6b3f15c70b 100644 --- a/static/js/SearchResultList.jsx +++ b/static/js/SearchResultList.jsx @@ -104,7 +104,7 @@ class SearchResultList extends Component { //console.log("Loaded cached query for") //console.log(args); this.state.hits[t] = this.state.hits[t].concat(cachedQuery.hits.hits); - this.state.totals[t] = cachedQuery.hits.total; + this.state.totals[t] = cachedQuery.hits.total.value; this.state.pagesLoaded[t] += 1; args.start = this.state.pagesLoaded[t] * this.querySize[t]; if (t === "text") { @@ -326,9 +326,9 @@ class SearchResultList extends Component { if (this.state.pagesLoaded[type] === 0) { // Skip if pages have already been loaded from cache, but let aggregation processing below occur let state = { hits: extend(this.state.hits, {[type]: data.hits.hits}), - totals: extend(this.state.totals, {[type]: data.hits.total}), + totals: extend(this.state.totals, {[type]: data.hits.total.value}), pagesLoaded: extend(this.state.pagesLoaded, {[type]: 1}), - moreToLoad: extend(this.state.moreToLoad, {[type]: data.hits.total > this.querySize[type]}) + moreToLoad: extend(this.state.moreToLoad, {[type]: data.hits.total.value > this.querySize[type]}) }; this.setState(state, () => { this.updateTotalResults(); @@ -336,7 +336,7 @@ class SearchResultList extends Component { }); const filter_label = (request_applied && request_applied.length > 0) ? (' - ' + request_applied.join('|')) : ''; const query_label = props.query + filter_label; - Sefaria.track.event("Search", `${this.props.searchInBook? "SidebarSearch ": ""}Query: ${type}`, query_label, data.hits.total); + Sefaria.track.event("Search", `${this.props.searchInBook? "SidebarSearch ": ""}Query: ${type}`, query_label, data.hits.total.value); } if (data.aggregations) { From f266771d8c973f0865a6c659110c6a9586fd390e Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Tue, 18 Jul 2023 14:47:51 +0300 Subject: [PATCH 555/756] chore(search): update elasticsearch to 8 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index df83dfb6ee..853adbd48e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -22,7 +22,7 @@ django==1.11.* djangorestframework @ https://github.com/encode/django-rest-framework/archive/3.11.1.tar.gz djangorestframework_simplejwt==3.3.0 PyJWT==1.7.1 # pinned b/c current version 2.0.0 breaks simplejwt. waiting for 2.0.1 -elasticsearch==7.9.1 +elasticsearch==8.8.2 elasticsearch_dsl==7.3.0 geojson==2.5.0 geopy==2.3.0 From 7fd8a07656bb35712774f0dfb25b063168d32ae9 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Tue, 18 Jul 2023 14:49:35 +0300 Subject: [PATCH 556/756] chore(search): make search.py compatible with ES 7 --- sefaria/search.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/sefaria/search.py b/sefaria/search.py index d42d39e614..29efbacb4e 100644 --- a/sefaria/search.py +++ b/sefaria/search.py @@ -52,7 +52,7 @@ def delete_text(oref, version, lang): curr_index = get_new_and_current_index_names('text')['current'] id = make_text_doc_id(oref.normal(), version, lang) - es_client.delete(index=curr_index, doc_type='text', id=id) + es_client.delete(index=curr_index, id=id) except Exception as e: logger.error("ERROR deleting {} / {} / {} : {}".format(oref.normal(), version, lang, e)) @@ -76,7 +76,7 @@ def delete_version(index, version, lang): def delete_sheet(index_name, id): try: - es_client.delete(index=index_name, doc_type='sheet', id=id) + es_client.delete(index=index_name, id=id) except Exception as e: logger.error("ERROR deleting sheet {}".format(id)) @@ -147,7 +147,7 @@ def index_sheet(index_name, id): "dateModified": sheet.get("dateModified", None), "views": sheet.get("views", 0) } - es_client.create(index=index_name, doc_type='sheet', id=id, body=doc) + es_client.create(index=index_name, id=id, body=doc) global doc_count doc_count += 1 return True @@ -220,7 +220,6 @@ def get_exact_english_analyzer(): "icu_normalizer", ], "filter": [ - "standard", "lowercase", "icu_folding", ], @@ -259,7 +258,7 @@ def create_index(index_name, type): } } print('Creating index {}'.format(index_name)) - index_client.create(index=index_name, body=settings) + index_client.create(index=index_name, settings=settings) if type == 'text': put_text_mapping(index_name) @@ -326,7 +325,7 @@ def put_text_mapping(index_name): } } } - index_client.put_mapping(doc_type='text', body=text_mapping, index=index_name) + index_client.put_mapping(body=text_mapping, index=index_name) def put_sheet_mapping(index_name): @@ -392,7 +391,7 @@ def put_sheet_mapping(index_name): } } } - index_client.put_mapping(doc_type='sheet', body=sheet_mapping, index=index_name) + index_client.put_mapping(body=sheet_mapping, index=index_name) def get_search_categories(oref, categories): toc_tree = library.get_toc_tree() @@ -593,7 +592,6 @@ def _cache_action(cls, segment_str, tref, heTref, version): cls._bulk_actions += [ { "_index": cls.index_name, - "_type": "text", "_id": make_text_doc_id(tref, vtitle, vlang), "_source": doc } From 51ba56e88c69e7368979773995f04fe890fd2cc9 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Tue, 18 Jul 2023 14:50:13 +0300 Subject: [PATCH 557/756] chore(search): make search_wrapper_api compatible with ES 8 --- reader/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reader/views.py b/reader/views.py index dfdceda26d..485848cc34 100644 --- a/reader/views.py +++ b/reader/views.py @@ -4212,7 +4212,7 @@ def search_wrapper_api(request): search_obj = get_query_obj(search_obj=search_obj, **j) response = search_obj.execute() if response.success(): - return jsonResponse(response.to_dict(), callback=request.GET.get("callback", None)) + return jsonResponse(response.to_dict().body, callback=request.GET.get("callback", None)) return jsonResponse({"error": "Error with connection to Elasticsearch. Total shards: {}, Shards successful: {}, Timed out: {}".format(response._shards.total, response._shards.successful, response.timed_out)}, callback=request.GET.get("callback", None)) return jsonResponse({"error": "Unsupported HTTP method."}, callback=request.GET.get("callback", None)) From d641ef60c935fb48bd2da08fe143c071acab5c92 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 24 Aug 2022 20:24:16 +0300 Subject: [PATCH 558/756] feat(search): connect to ES using a password --- reader/views.py | 4 +++- sefaria/helper/search.py | 20 ++++++++++++++++++++ sefaria/local_settings_example.py | 6 +++++- sefaria/search.py | 6 +++--- 4 files changed, 31 insertions(+), 5 deletions(-) diff --git a/reader/views.py b/reader/views.py index 485848cc34..67106f44d3 100644 --- a/reader/views.py +++ b/reader/views.py @@ -4201,13 +4201,15 @@ def dummy_search_api(request): @csrf_exempt def search_wrapper_api(request): + from sefaria.helper.search import get_elasticsearch_client + if request.method == "POST": if "json" in request.POST: j = request.POST.get("json") # using form-urlencoded else: j = request.body # using content-type: application/json j = json.loads(j) - es_client = Elasticsearch(SEARCH_ADMIN) + es_client = get_elasticsearch_client(admin=False) search_obj = Search(using=es_client, index=j.get("type")).params(request_timeout=5) search_obj = get_query_obj(search_obj=search_obj, **j) response = search_obj.execute() diff --git a/sefaria/helper/search.py b/sefaria/helper/search.py index 36aef9eb3e..351af78060 100644 --- a/sefaria/helper/search.py +++ b/sefaria/helper/search.py @@ -144,3 +144,23 @@ def make_filter(type, agg_type, agg_key): return Regexp(path=reg) elif type == "sheet": return Term(**{agg_type: agg_key}) + + +def _get_es_server_url(admin=False): + from sefaria.settings import SEARCH_ADMIN, SEARCH_ADMIN_PW, SEARCH_ADMIN_USER, SEARCH_NON_ADMIN + base_url = SEARCH_ADMIN if admin else SEARCH_NON_ADMIN + if SEARCH_ADMIN_USER: + match = re.search(r'^(https?://)(.*)$', base_url) + if match: + http, base_url = match.group(1), match.group(2) + else: + http, base_url = "http", base_url + es_url = f"{http}{SEARCH_ADMIN_USER}:{SEARCH_ADMIN_PW}@{base_url}" + else: + es_url = base_url + return es_url + + +def get_elasticsearch_client(admin=False): + from elasticsearch import Elasticsearch + return Elasticsearch(_get_es_server_url(admin=admin)) diff --git a/sefaria/local_settings_example.py b/sefaria/local_settings_example.py index b6071153bb..f9e9e7b37a 100644 --- a/sefaria/local_settings_example.py +++ b/sefaria/local_settings_example.py @@ -156,7 +156,11 @@ # ElasticSearch server -SEARCH_ADMIN = "http://localhost:9200" +SEARCH_PASSWORD = "" +SEARCH_ADMIN = "localhost:9200" # URL to connect to internal ES server for admin access. Leave off https:// +SEARCH_NON_ADMIN = "http://localhost:9200/api/search" # URL to connect to ES for searching. Can be /api/search Django endpoint which gets proxied to ES server. +SEARCH_ADMIN_PW = None # Optional password to connect to ES server. If no password, leave as `None` +SEARCH_ADMIN_USER = None # Optional user to connect to ES server. If no user, leave as `None` SEARCH_INDEX_ON_SAVE = False # Whether to send texts and source sheet to Search Host for indexing after save SEARCH_INDEX_NAME_TEXT = 'text' # name of the ElasticSearch index to use SEARCH_INDEX_NAME_SHEET = 'sheet' diff --git a/sefaria/search.py b/sefaria/search.py index 29efbacb4e..f81daedb3b 100644 --- a/sefaria/search.py +++ b/sefaria/search.py @@ -20,7 +20,6 @@ import time as pytime logger = structlog.get_logger(__name__) -from elasticsearch import Elasticsearch from elasticsearch.client import IndicesClient from elasticsearch.helpers import bulk from elasticsearch.exceptions import NotFoundError @@ -31,12 +30,13 @@ from sefaria.system.database import db from sefaria.system.exceptions import InputError from sefaria.utils.util import strip_tags -from .settings import SEARCH_ADMIN, SEARCH_INDEX_NAME_TEXT, SEARCH_INDEX_NAME_SHEET, STATICFILES_DIRS +from .settings import SEARCH_INDEX_NAME_TEXT, SEARCH_INDEX_NAME_SHEET +from sefaria.helper.search import get_elasticsearch_client from sefaria.site.site_settings import SITE_SETTINGS from sefaria.utils.hebrew import strip_cantillation import sefaria.model.queue as qu -es_client = Elasticsearch(SEARCH_ADMIN) +es_client = get_elasticsearch_client(admin=True) index_client = IndicesClient(es_client) tracer = structlog.get_logger(__name__) From b6b5e1eafd84ad0073230c6d4787fb1ed8e2c43d Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Tue, 25 Jul 2023 12:15:24 +0300 Subject: [PATCH 559/756] chore(search): change to a fork of elasticsearch_dsl which adds support for ES 8 while we wait for the ES team to release a new version --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 853adbd48e..0bd6dda6f8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -23,7 +23,7 @@ djangorestframework @ https://github.com/encode/django-rest-framework/archive/3. djangorestframework_simplejwt==3.3.0 PyJWT==1.7.1 # pinned b/c current version 2.0.0 breaks simplejwt. waiting for 2.0.1 elasticsearch==8.8.2 -elasticsearch_dsl==7.3.0 +git+https://github.com/Sefaria/elasticsearch-dsl-py@v8.0.0#egg=elasticsearch-dsl geojson==2.5.0 geopy==2.3.0 gevent==20.12.0; sys_platform != 'darwin' From 7b932ab3e46a95ee7497a8cb35d3dc38368e77e2 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 26 Jul 2023 14:52:47 +0300 Subject: [PATCH 560/756] refactor: reduce number of local settings required to connect to elastic search. Allows possibility of connecting to SEARCH_NON_ADMIN via a password --- sefaria/helper/search.py | 19 +++---------------- sefaria/local_settings_example.py | 12 +++++++----- 2 files changed, 10 insertions(+), 21 deletions(-) diff --git a/sefaria/helper/search.py b/sefaria/helper/search.py index 351af78060..f5a16300e8 100644 --- a/sefaria/helper/search.py +++ b/sefaria/helper/search.py @@ -146,21 +146,8 @@ def make_filter(type, agg_type, agg_key): return Term(**{agg_type: agg_key}) -def _get_es_server_url(admin=False): - from sefaria.settings import SEARCH_ADMIN, SEARCH_ADMIN_PW, SEARCH_ADMIN_USER, SEARCH_NON_ADMIN - base_url = SEARCH_ADMIN if admin else SEARCH_NON_ADMIN - if SEARCH_ADMIN_USER: - match = re.search(r'^(https?://)(.*)$', base_url) - if match: - http, base_url = match.group(1), match.group(2) - else: - http, base_url = "http", base_url - es_url = f"{http}{SEARCH_ADMIN_USER}:{SEARCH_ADMIN_PW}@{base_url}" - else: - es_url = base_url - return es_url - - def get_elasticsearch_client(admin=False): from elasticsearch import Elasticsearch - return Elasticsearch(_get_es_server_url(admin=admin)) + from sefaria.settings import SEARCH_ADMIN, SEARCH_NON_ADMIN + es_url = SEARCH_ADMIN if admin else SEARCH_NON_ADMIN + return Elasticsearch(es_url) diff --git a/sefaria/local_settings_example.py b/sefaria/local_settings_example.py index f9e9e7b37a..a86293f18b 100644 --- a/sefaria/local_settings_example.py +++ b/sefaria/local_settings_example.py @@ -156,11 +156,13 @@ # ElasticSearch server -SEARCH_PASSWORD = "" -SEARCH_ADMIN = "localhost:9200" # URL to connect to internal ES server for admin access. Leave off https:// -SEARCH_NON_ADMIN = "http://localhost:9200/api/search" # URL to connect to ES for searching. Can be /api/search Django endpoint which gets proxied to ES server. -SEARCH_ADMIN_PW = None # Optional password to connect to ES server. If no password, leave as `None` -SEARCH_ADMIN_USER = None # Optional user to connect to ES server. If no user, leave as `None` +# URL to connect to internal ES server for admin access. This URL is used by indexing jobs only +# If ElasticSearch server has a password use the following format: http(s)://{username}:{password}@{base_url} +SEARCH_ADMIN = "http://localhost:9200" +# URL to connect to ES for searching. Can point to /api/search Django endpoint which gets proxied to ES server. +# Set this to https://sefaria.org/api/search to connect to production search. +SEARCH_NON_ADMIN = "http://localhost:8000/api/search" + SEARCH_INDEX_ON_SAVE = False # Whether to send texts and source sheet to Search Host for indexing after save SEARCH_INDEX_NAME_TEXT = 'text' # name of the ElasticSearch index to use SEARCH_INDEX_NAME_SHEET = 'sheet' From 7f66997fd9e80c3ebd28aa8c16075232c75a463d Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 26 Jul 2023 17:47:43 +0300 Subject: [PATCH 561/756] helm(search): update search local settings for elasticsearch 8 --- .../templates/configmap/local-settings-file.yaml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml b/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml index 4a3a9bb6ac..388657a3c7 100644 --- a/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml +++ b/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml @@ -138,14 +138,10 @@ data: SEARCH_HOST = "/api/search" SEARCH_ADMIN = os.getenv("SEARCH_ADMIN") - SEARCH_ADMIN_USER = os.getenv("SEARCH_ADMIN_USER") - SEARCH_ADMIN_PW = os.getenv("SEARCH_ADMIN_PW") - SEARCH_ADMIN_K8S = os.getenv("SEARCH_ADMIN_K8S") + SEARCH_NON_ADMIN = os.getenv("SEARCH_NON_ADMIN") SEARCH_INDEX_ON_SAVE = True - SEARCH_INDEX_NAME = "sefaria" SEARCH_INDEX_NAME_TEXT = 'text' # name of the ElasticSearch index to use SEARCH_INDEX_NAME_SHEET = 'sheet' - SEARCH_INDEX_NAME_MERGED = 'merged' TURN_SERVER = os.getenv("TURN_SERVER") #coturn.cauldron.sefaria.org TURN_SECRET= os.getenv("TURN_SECRET") From 4387975bfa7e81a5a30ee91f253c8809651a4c45 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 2 Aug 2023 13:02:37 +0300 Subject: [PATCH 562/756] helm: remove unused and confusing SEARCH_HOST declarations. --- .../sefaria-project/templates/configmap/local-settings-file.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml b/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml index 388657a3c7..1948fc10d0 100644 --- a/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml +++ b/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml @@ -136,7 +136,6 @@ data: } SERVER_EMAIL = os.getenv("SERVER_EMAIL") - SEARCH_HOST = "/api/search" SEARCH_ADMIN = os.getenv("SEARCH_ADMIN") SEARCH_NON_ADMIN = os.getenv("SEARCH_NON_ADMIN") SEARCH_INDEX_ON_SAVE = True From ed5312191244d75c518213820382df4f28ce2942 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 2 Aug 2023 13:03:00 +0300 Subject: [PATCH 563/756] ci: remove unused and confusing SEARCH_HOST declarations. --- build/ci/production-values.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/build/ci/production-values.yaml b/build/ci/production-values.yaml index f55f4fcc41..b653021544 100644 --- a/build/ci/production-values.yaml +++ b/build/ci/production-values.yaml @@ -216,7 +216,6 @@ localSettings: GLOBAL_WARNING: false GLOBAL_WARNING_MESSAGE: "Sefaria will be in <b>Read-Only</b> mode for scheduled maintenance from 4:45pm-6:45pm Pacific time. Edits will <b>not</b> be saved during that time." SITE_PACKAGE: "sites.sefaria" - SEARCH_HOST: elasticsearch.data DEFAULT_FROM_EMAIL: "Sefaria <hello@sefaria.org>" SERVER_EMAIL: "dev@sefaria.org" MULTISERVER_ENABLED: "True" From d8aed82298e5f7048695756e2d68519528ee0630 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Thu, 3 Aug 2023 14:21:17 +0300 Subject: [PATCH 564/756] fix(search): make code backwards compatible with elasticsearch 6. This commit is meant as a temporary patch to make the deployment of ES 8 smoother --- reader/views.py | 3 ++- static/js/SearchResultList.jsx | 15 +++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/reader/views.py b/reader/views.py index 67106f44d3..c55790ff8c 100644 --- a/reader/views.py +++ b/reader/views.py @@ -4214,7 +4214,8 @@ def search_wrapper_api(request): search_obj = get_query_obj(search_obj=search_obj, **j) response = search_obj.execute() if response.success(): - return jsonResponse(response.to_dict().body, callback=request.GET.get("callback", None)) + response_json = getattr(response.to_dict(), 'body', response.to_dict()) + return jsonResponse(response_json, callback=request.GET.get("callback", None)) return jsonResponse({"error": "Error with connection to Elasticsearch. Total shards: {}, Shards successful: {}, Timed out: {}".format(response._shards.total, response._shards.successful, response.timed_out)}, callback=request.GET.get("callback", None)) return jsonResponse({"error": "Unsupported HTTP method."}, callback=request.GET.get("callback", None)) diff --git a/static/js/SearchResultList.jsx b/static/js/SearchResultList.jsx index 6b3f15c70b..a05afe1f59 100644 --- a/static/js/SearchResultList.jsx +++ b/static/js/SearchResultList.jsx @@ -104,7 +104,7 @@ class SearchResultList extends Component { //console.log("Loaded cached query for") //console.log(args); this.state.hits[t] = this.state.hits[t].concat(cachedQuery.hits.hits); - this.state.totals[t] = cachedQuery.hits.total.value; + this.state.totals[t] = this._get_hits_total(cachedQuery.hits.total); this.state.pagesLoaded[t] += 1; args.start = this.state.pagesLoaded[t] * this.querySize[t]; if (t === "text") { @@ -302,6 +302,13 @@ class SearchResultList extends Component { .zip(aggregation_field_array, aggregation_field_lang_suffix_array) .map(([agg, suffix_map]) => `${agg}${suffix_map ? suffix_map[Sefaria.interfaceLang] : ''}`); // add suffix based on interfaceLang to filter, if present in suffix_map } + _get_hits_total(totalObj) { + /** + * this function ensures backwards compatibility between the way elasticsearch formats the total pre-v8 and post-v8 + */ + if (typeof(totalObj) === 'number') { return totalObj; } + return totalObj.value; + } _executeQuery(props, type) { //This takes a props object, so as to be able to handle being called from componentWillReceiveProps with newProps props = props || this.props; @@ -326,9 +333,9 @@ class SearchResultList extends Component { if (this.state.pagesLoaded[type] === 0) { // Skip if pages have already been loaded from cache, but let aggregation processing below occur let state = { hits: extend(this.state.hits, {[type]: data.hits.hits}), - totals: extend(this.state.totals, {[type]: data.hits.total.value}), + totals: extend(this.state.totals, {[type]: this._get_hits_total(data.hits.total)}), pagesLoaded: extend(this.state.pagesLoaded, {[type]: 1}), - moreToLoad: extend(this.state.moreToLoad, {[type]: data.hits.total.value > this.querySize[type]}) + moreToLoad: extend(this.state.moreToLoad, {[type]: this._get_hits_total(data.hits.total) > this.querySize[type]}) }; this.setState(state, () => { this.updateTotalResults(); @@ -336,7 +343,7 @@ class SearchResultList extends Component { }); const filter_label = (request_applied && request_applied.length > 0) ? (' - ' + request_applied.join('|')) : ''; const query_label = props.query + filter_label; - Sefaria.track.event("Search", `${this.props.searchInBook? "SidebarSearch ": ""}Query: ${type}`, query_label, data.hits.total.value); + Sefaria.track.event("Search", `${this.props.searchInBook? "SidebarSearch ": ""}Query: ${type}`, query_label, this._get_hits_total(data.hits.total)); } if (data.aggregations) { From 1867b7165b91f4a67dc48c697bc771891b4766f7 Mon Sep 17 00:00:00 2001 From: Brendan Galloway <brendan@flanksource.com> Date: Mon, 31 Jul 2023 16:17:56 +0200 Subject: [PATCH 565/756] helm: reorder env priority for reindexing job --- .../templates/cronjob/reindex-elasticsearch.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml b/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml index 9cefe302fc..448dc84c03 100644 --- a/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml +++ b/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml @@ -47,11 +47,11 @@ spec: - secretRef: name: {{ .Values.secrets.localSettings.ref }} optional: true + - configMapRef: + name: local-settings-{{ .Values.deployEnv }} - secretRef: name: local-settings-secrets-{{ .Values.deployEnv }} optional: true - - configMapRef: - name: local-settings-{{ .Values.deployEnv }} volumeMounts: - mountPath: /app/sefaria/local_settings.py name: local-settings From 16606233f1e3185db1c7aeecd0858dd6ec77c416 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Mon, 7 Aug 2023 11:07:31 +0300 Subject: [PATCH 566/756] fix(reader): Temporarily have Django connect to ES using admin creds to ease upgrade. --- reader/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reader/views.py b/reader/views.py index c55790ff8c..320c6259f6 100644 --- a/reader/views.py +++ b/reader/views.py @@ -4209,7 +4209,7 @@ def search_wrapper_api(request): else: j = request.body # using content-type: application/json j = json.loads(j) - es_client = get_elasticsearch_client(admin=False) + es_client = get_elasticsearch_client(admin=True) search_obj = Search(using=es_client, index=j.get("type")).params(request_timeout=5) search_obj = get_query_obj(search_obj=search_obj, **j) response = search_obj.execute() From 498bfd9a04a7ad4cacdf223377ea5ff5067b7996 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Mon, 7 Aug 2023 12:15:10 +0300 Subject: [PATCH 567/756] fix(reader): Temporarily use fallback to SEARCH_ADMIN if SEARCH_NON_ADMIN isn't defined --- reader/views.py | 2 +- sefaria/helper/search.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/reader/views.py b/reader/views.py index 320c6259f6..c55790ff8c 100644 --- a/reader/views.py +++ b/reader/views.py @@ -4209,7 +4209,7 @@ def search_wrapper_api(request): else: j = request.body # using content-type: application/json j = json.loads(j) - es_client = get_elasticsearch_client(admin=True) + es_client = get_elasticsearch_client(admin=False) search_obj = Search(using=es_client, index=j.get("type")).params(request_timeout=5) search_obj = get_query_obj(search_obj=search_obj, **j) response = search_obj.execute() diff --git a/sefaria/helper/search.py b/sefaria/helper/search.py index f5a16300e8..7d71ccce32 100644 --- a/sefaria/helper/search.py +++ b/sefaria/helper/search.py @@ -150,4 +150,7 @@ def get_elasticsearch_client(admin=False): from elasticsearch import Elasticsearch from sefaria.settings import SEARCH_ADMIN, SEARCH_NON_ADMIN es_url = SEARCH_ADMIN if admin else SEARCH_NON_ADMIN + if not es_url: + # try flipping values in case the other is defined + es_url = SEARCH_NON_ADMIN if admin else SEARCH_ADMIN return Elasticsearch(es_url) From fa161954286fef87ee253040cb61c2cb8ea5f629 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Mon, 7 Aug 2023 14:18:10 +0300 Subject: [PATCH 568/756] chore: remove SEARCH_NON_ADMIN which is no longer necessary --- .../templates/configmap/local-settings-file.yaml | 1 - reader/views.py | 2 +- sefaria/helper/search.py | 10 +++------- sefaria/local_settings_example.py | 6 ++---- sefaria/search.py | 2 +- 5 files changed, 7 insertions(+), 14 deletions(-) diff --git a/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml b/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml index 1948fc10d0..85f0774938 100644 --- a/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml +++ b/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml @@ -137,7 +137,6 @@ data: SERVER_EMAIL = os.getenv("SERVER_EMAIL") SEARCH_ADMIN = os.getenv("SEARCH_ADMIN") - SEARCH_NON_ADMIN = os.getenv("SEARCH_NON_ADMIN") SEARCH_INDEX_ON_SAVE = True SEARCH_INDEX_NAME_TEXT = 'text' # name of the ElasticSearch index to use SEARCH_INDEX_NAME_SHEET = 'sheet' diff --git a/reader/views.py b/reader/views.py index c55790ff8c..81c0878869 100644 --- a/reader/views.py +++ b/reader/views.py @@ -4209,7 +4209,7 @@ def search_wrapper_api(request): else: j = request.body # using content-type: application/json j = json.loads(j) - es_client = get_elasticsearch_client(admin=False) + es_client = get_elasticsearch_client() search_obj = Search(using=es_client, index=j.get("type")).params(request_timeout=5) search_obj = get_query_obj(search_obj=search_obj, **j) response = search_obj.execute() diff --git a/sefaria/helper/search.py b/sefaria/helper/search.py index 7d71ccce32..9dd542910e 100644 --- a/sefaria/helper/search.py +++ b/sefaria/helper/search.py @@ -146,11 +146,7 @@ def make_filter(type, agg_type, agg_key): return Term(**{agg_type: agg_key}) -def get_elasticsearch_client(admin=False): +def get_elasticsearch_client(): from elasticsearch import Elasticsearch - from sefaria.settings import SEARCH_ADMIN, SEARCH_NON_ADMIN - es_url = SEARCH_ADMIN if admin else SEARCH_NON_ADMIN - if not es_url: - # try flipping values in case the other is defined - es_url = SEARCH_NON_ADMIN if admin else SEARCH_ADMIN - return Elasticsearch(es_url) + from sefaria.settings import SEARCH_ADMIN + return Elasticsearch(SEARCH_ADMIN) diff --git a/sefaria/local_settings_example.py b/sefaria/local_settings_example.py index a86293f18b..731ed44876 100644 --- a/sefaria/local_settings_example.py +++ b/sefaria/local_settings_example.py @@ -156,12 +156,10 @@ # ElasticSearch server -# URL to connect to internal ES server for admin access. This URL is used by indexing jobs only +# URL to connect to ES server. +# Set this to https://sefaria.org/api/search to connect to production search. # If ElasticSearch server has a password use the following format: http(s)://{username}:{password}@{base_url} SEARCH_ADMIN = "http://localhost:9200" -# URL to connect to ES for searching. Can point to /api/search Django endpoint which gets proxied to ES server. -# Set this to https://sefaria.org/api/search to connect to production search. -SEARCH_NON_ADMIN = "http://localhost:8000/api/search" SEARCH_INDEX_ON_SAVE = False # Whether to send texts and source sheet to Search Host for indexing after save SEARCH_INDEX_NAME_TEXT = 'text' # name of the ElasticSearch index to use diff --git a/sefaria/search.py b/sefaria/search.py index f81daedb3b..3f2c87f714 100644 --- a/sefaria/search.py +++ b/sefaria/search.py @@ -36,7 +36,7 @@ from sefaria.utils.hebrew import strip_cantillation import sefaria.model.queue as qu -es_client = get_elasticsearch_client(admin=True) +es_client = get_elasticsearch_client() index_client = IndicesClient(es_client) tracer = structlog.get_logger(__name__) From 66b6cd8f38535cd502897fdd7e4dfc6a425ee06b Mon Sep 17 00:00:00 2001 From: Brendan Galloway <brendan@flanksource.com> Date: Wed, 2 Aug 2023 14:14:45 +0200 Subject: [PATCH 569/756] helm(feat): add dedicated elastic secrets --- .../sefaria-project/templates/_helpers.tpl | 16 ++++++++++++++++ .../templates/cronjob/reindex-elasticsearch.yaml | 2 ++ .../sefaria-project/templates/rollout/nginx.yaml | 2 ++ .../templates/secret/elastic-admin.yaml | 11 +++++++++++ .../templates/secret/elastic-user.yaml | 11 +++++++++++ helm-chart/sefaria-project/values.yaml | 10 ++++++++++ 6 files changed, 52 insertions(+) create mode 100644 helm-chart/sefaria-project/templates/secret/elastic-admin.yaml create mode 100644 helm-chart/sefaria-project/templates/secret/elastic-user.yaml diff --git a/helm-chart/sefaria-project/templates/_helpers.tpl b/helm-chart/sefaria-project/templates/_helpers.tpl index 0e7c9ecb57..881445d3af 100644 --- a/helm-chart/sefaria-project/templates/_helpers.tpl +++ b/helm-chart/sefaria-project/templates/_helpers.tpl @@ -54,6 +54,22 @@ elastic-certificate-{{ .Values.deployEnv }} {{- end }} {{- end }} +{{- define "sefaria.secrets.elasticUser" }} +{{- if .Values.secrets.elasticUser.ref -}} +{{- .Values.web.secrets.elasticUser.ref }} +{{- else -}} +elastic-user-{{ .Values.deployEnv }} +{{- end }} +{{- end }} + +{{- define "sefaria.secrets.elasticAdmin" }} +{{- if .Values.secrets.elasticAdmin.ref -}} +{{- .Values.web.secrets.elasticAdmin.ref }} +{{- else -}} +elastic-admin-{{ .Values.deployEnv }} +{{- end }} +{{- end }} + {{- define "sefaria.secrets.originTls" }} {{- if .Values.ingress.secrets.originTls.ref -}} diff --git a/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml b/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml index 448dc84c03..6c5aa13784 100644 --- a/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml +++ b/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml @@ -44,6 +44,8 @@ spec: name: {{ template "sefaria.secrets.slackWebhook" . }} key: slack-webhook envFrom: + - secretRef: + name: {{ template "sefaria.secrets.elasticAdmin" . }} - secretRef: name: {{ .Values.secrets.localSettings.ref }} optional: true diff --git a/helm-chart/sefaria-project/templates/rollout/nginx.yaml b/helm-chart/sefaria-project/templates/rollout/nginx.yaml index f1b24d995d..052d8b69ec 100644 --- a/helm-chart/sefaria-project/templates/rollout/nginx.yaml +++ b/helm-chart/sefaria-project/templates/rollout/nginx.yaml @@ -106,6 +106,8 @@ spec: value: "linker-{{ .Values.deployEnv }}-{{ .Release.Revision }}" {{- end }} envFrom: + - secretRef: + name: {{ template "sefaria.secrets.elasticUser" . }} - configMapRef: name: local-settings-nginx-{{ .Values.deployEnv }} optional: true diff --git a/helm-chart/sefaria-project/templates/secret/elastic-admin.yaml b/helm-chart/sefaria-project/templates/secret/elastic-admin.yaml new file mode 100644 index 0000000000..d6a3266af9 --- /dev/null +++ b/helm-chart/sefaria-project/templates/secret/elastic-admin.yaml @@ -0,0 +1,11 @@ +{{- if .Values.secrets.elasticAdmin.data }} +apiVersion: v1 +kind: Secret +metadata: + name: elastic-admin-{{ .Values.deployEnv }} + labels: + deployEnv: "{{ .Values.deployEnv }}" + {{- include "sefaria.labels" . | nindent 4 }} +type: Opaque +stringData: {{ .Values.secrets.elasticAdmin.data | toYaml | nindent 2 }} +{{- end }} diff --git a/helm-chart/sefaria-project/templates/secret/elastic-user.yaml b/helm-chart/sefaria-project/templates/secret/elastic-user.yaml new file mode 100644 index 0000000000..511d271a26 --- /dev/null +++ b/helm-chart/sefaria-project/templates/secret/elastic-user.yaml @@ -0,0 +1,11 @@ +{{- if .Values.secrets.elasticUser.data }} +apiVersion: v1 +kind: Secret +metadata: + name: elastic-user-{{ .Values.deployEnv }} + labels: + deployEnv: "{{ .Values.deployEnv }}" + {{- include "sefaria.labels" . | nindent 4 }} +type: Opaque +stringData: {{ .Values.secrets.elasticUser.data | toYaml | nindent 2 }} +{{- end }} diff --git a/helm-chart/sefaria-project/values.yaml b/helm-chart/sefaria-project/values.yaml index 563a5c6e01..f4efcbb893 100644 --- a/helm-chart/sefaria-project/values.yaml +++ b/helm-chart/sefaria-project/values.yaml @@ -372,6 +372,16 @@ secrets: # should be commented out and vice-versa. ref: trello-secret # data: + elasticUser: + # If you're using a reference to an existing secret then the data: section + # should be commented out and vice-versa. + ref: + # data: + elasticAdmin: + # If you're using a reference to an existing secret then the data: section + # should be commented out and vice-versa. + ref: + # data: # Settings for various cronjobs From f00e384159aeecd60f623a1e434e74c132c2dfb3 Mon Sep 17 00:00:00 2001 From: Brendan Galloway <brendan@flanksource.com> Date: Mon, 7 Aug 2023 11:48:38 +0200 Subject: [PATCH 570/756] helm(feat): add elastic user to web podd --- helm-chart/sefaria-project/templates/rollout/web.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/helm-chart/sefaria-project/templates/rollout/web.yaml b/helm-chart/sefaria-project/templates/rollout/web.yaml index d1862c73dc..eced50467c 100644 --- a/helm-chart/sefaria-project/templates/rollout/web.yaml +++ b/helm-chart/sefaria-project/templates/rollout/web.yaml @@ -118,6 +118,8 @@ spec: value: k8s.container.name=app,k8s.deployment.name={{ .Values.deployEnv }}-web,k8s.namespace.name={{ .Release.Namespace }},k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME) {{- end }} envFrom: + - secretRef: + name: {{ template "sefaria.secrets.elasticUser" . }} - secretRef: name: {{ .Values.secrets.localSettings.ref }} optional: true From 9b6007d274ab119ab2f749954269d682e6d80a0e Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 29 Nov 2023 09:39:31 +0200 Subject: [PATCH 571/756] helm(search): merge --- .../sefaria-project/templates/configmap/nginx.yaml | 10 ++++++++++ .../sefaria-project/templates/rollout/nginx.yaml | 5 ++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/helm-chart/sefaria-project/templates/configmap/nginx.yaml b/helm-chart/sefaria-project/templates/configmap/nginx.yaml index 0b118d9b24..fd1c64565f 100644 --- a/helm-chart/sefaria-project/templates/configmap/nginx.yaml +++ b/helm-chart/sefaria-project/templates/configmap/nginx.yaml @@ -28,6 +28,16 @@ data: } } {{- end }} + entrypoint.sh: | + #!/bin/bash + + set -e + + export ELASTIC_AUTH_HEADER=$(echo -n $ELASTIC_USER:$ELASTIC_PASSWORD | base64) + envsubst '${ENV_NAME},${VARNISH_HOST},${SEARCH_HOST},${RELEASE_TAG},${ELASTIC_AUTH_HEADER},${STRAPI_LOCATION}{{- if .Values.linker.enabled }},${LINKER_HOST}{{- end }}{{- if .Values.instrumentation.enabled }},${NGINX_VERSION}{{- end }}' < /conf/nginx.template.conf > /nginx.conf + + nginx -c /nginx.conf -g 'daemon off;' + nginx.template.conf: |- {{- if .Values.instrumentation.enabled }} load_module /etc/nginx/modules/ngx_http_opentracing_module.so; diff --git a/helm-chart/sefaria-project/templates/rollout/nginx.yaml b/helm-chart/sefaria-project/templates/rollout/nginx.yaml index 052d8b69ec..b8e329a47a 100644 --- a/helm-chart/sefaria-project/templates/rollout/nginx.yaml +++ b/helm-chart/sefaria-project/templates/rollout/nginx.yaml @@ -52,9 +52,8 @@ spec: - name: nginx image: "{{ .Values.nginx.containerImage.imageRegistry }}:{{ .Values.nginx.containerImage.tag }}" imagePullPolicy: Always - command: ["bash", "-c"] - # https://serverfault.com/questions/577370/how-can-i-use-environment-variables-in-nginx-conf - args: [ "envsubst '${ENV_NAME},${VARNISH_HOST},${SEARCH_HOST},${RELEASE_TAG},${STRAPI_LOCATION}{{- if .Values.linker.enabled }},${LINKER_HOST}{{- end }}{{- if .Values.instrumentation.enabled }},${NGINX_VERSION}{{- end }}' < /conf/nginx.template.conf > /nginx.conf && exec nginx -c /nginx.conf -g 'daemon off;'" ] + command: + - /entrypoint.sh ports: - containerPort: 80 - containerPort: 443 From fdee561ca6e1d009e0959661ea374bbc50b49afa Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Wed, 29 Nov 2023 09:55:31 +0200 Subject: [PATCH 572/756] fix(export sheet to google): change hebrew font to Noto Serif Hebrew. --- templates/gdocs_sheet.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/gdocs_sheet.html b/templates/gdocs_sheet.html index a85323fd0f..2894317c27 100644 --- a/templates/gdocs_sheet.html +++ b/templates/gdocs_sheet.html @@ -383,7 +383,7 @@ } #inlineTextPreview .he { - font-family: "Times New Roman", serif; + font-family: "Noto Serif Hebrew", serif; } @@ -664,7 +664,7 @@ #sheet .he { direction: rtl; text-align: right; - font-family: "Times New Roman", serif; + font-family: "Noto Serif Hebrew", serif; font-size: 24px; } From 596b531c0bcd35139bb522569283670301023741 Mon Sep 17 00:00:00 2001 From: Brendan Galloway <brendan@flanksource.com> Date: Wed, 29 Nov 2023 11:35:28 +0200 Subject: [PATCH 573/756] helm(fix): correct secret ref --- helm-chart/sefaria-project/templates/_helpers.tpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/helm-chart/sefaria-project/templates/_helpers.tpl b/helm-chart/sefaria-project/templates/_helpers.tpl index 881445d3af..ce020c7bb7 100644 --- a/helm-chart/sefaria-project/templates/_helpers.tpl +++ b/helm-chart/sefaria-project/templates/_helpers.tpl @@ -56,7 +56,7 @@ elastic-certificate-{{ .Values.deployEnv }} {{- define "sefaria.secrets.elasticUser" }} {{- if .Values.secrets.elasticUser.ref -}} -{{- .Values.web.secrets.elasticUser.ref }} +{{- .Values.secrets.elasticUser.ref }} {{- else -}} elastic-user-{{ .Values.deployEnv }} {{- end }} @@ -64,7 +64,7 @@ elastic-user-{{ .Values.deployEnv }} {{- define "sefaria.secrets.elasticAdmin" }} {{- if .Values.secrets.elasticAdmin.ref -}} -{{- .Values.web.secrets.elasticAdmin.ref }} +{{- .Values.secrets.elasticAdmin.ref }} {{- else -}} elastic-admin-{{ .Values.deployEnv }} {{- end }} From 74e0dfbd3498eb71abfe0081b20d6686ffafdb39 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Wed, 29 Nov 2023 11:53:02 +0200 Subject: [PATCH 574/756] refactor(translations): typo. --- static/js/VersionBlock.jsx | 8 ++++---- static/js/VersionBlockSelectButton.jsx | 4 ++-- static/js/VersionBlockWithPreviewTitleLine.jsx | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/static/js/VersionBlock.jsx b/static/js/VersionBlock.jsx index 4ed71caadd..2e3b5afd0d 100644 --- a/static/js/VersionBlock.jsx +++ b/static/js/VersionBlock.jsx @@ -53,7 +53,7 @@ class versionTools { } openVersionInSidebar(version.versionTitle, version.language); } - static openVersionInMoinPanel(currRef, version, currObjectVersions, renderMode, firstSectionRef, openVersionInReader, e) { + static openVersionInMainPanel(currRef, version, currObjectVersions, renderMode, firstSectionRef, openVersionInReader, e) { e.preventDefault(); try { gtag("event", "onClick_select_version", {element_name: `select_version`, @@ -203,7 +203,7 @@ class VersionBlock extends Component { const showLanguagLabel = this.props.rendermode == "book-page"; const openVersionInSidebar = versionTools.openVersionInSidebar.bind(null, this.props.currentRef, this.props.version, this.props.currObjectVersions, this.props.openVersionInSidebar); - const openVersionInMoinPanel = versionTools.openVersionInMoinPanel.bind(null, this.props.currentRef, + const openVersionInMainPanel = versionTools.openVersionInMainPanel.bind(null, this.props.currentRef, this.props.version, this.props.currObjectVersions, this.props.rendermode, this.props.firstSectionRef, this.props.openVersionInReader); if (this.state.editing && Sefaria.is_moderator) { @@ -276,7 +276,7 @@ class VersionBlock extends Component { <div className="versionTitle" role="heading"> <VersionBlockHeader text={vtitle["text"]} - onClick={this.props.rendermode === 'book-page' ? openVersionInMoinPanel : openVersionInSidebar} + onClick={this.props.rendermode === 'book-page' ? openVersionInMainPanel : openVersionInSidebar} renderMode='versionTitle' link={versionTools.makeVersionLink(this.props.currentRef, this.props.version, this.props.currObjectVersions, this.props.rendermode === 'book-page')} @@ -288,7 +288,7 @@ class VersionBlock extends Component { <div className="versionSelect sans-serif"> <VersionBlockSelectButton isSelected={this.props.isCurrent} - openVersionInMoinPanel={openVersionInMoinPanel} + openVersionInMainPanel={openVersionInMainPanel} text={this.makeSelectVersionLanguage()} link={versionTools.makeVersionLink(this.props.currentRef, this.props.version, this.props.currObjectVersions, true)} diff --git a/static/js/VersionBlockSelectButton.jsx b/static/js/VersionBlockSelectButton.jsx index c1ed1bb527..74cb1ee9f4 100644 --- a/static/js/VersionBlockSelectButton.jsx +++ b/static/js/VersionBlockSelectButton.jsx @@ -1,11 +1,11 @@ import React from 'react'; import PropTypes from "prop-types"; -function VersionBlockSelectButton({link, openVersionInMoinPanel, text, isSelected}) { +function VersionBlockSelectButton({link, openVersionInMainPanel, text, isSelected}) { return ( <a className={`selectButton ${isSelected ? "currSelectButton" : ""}`} href={link} - onClick={openVersionInMoinPanel} + onClick={openVersionInMainPanel} > {Sefaria._(text)} </a> diff --git a/static/js/VersionBlockWithPreviewTitleLine.jsx b/static/js/VersionBlockWithPreviewTitleLine.jsx index 8201f6b707..09e1b56122 100644 --- a/static/js/VersionBlockWithPreviewTitleLine.jsx +++ b/static/js/VersionBlockWithPreviewTitleLine.jsx @@ -13,7 +13,7 @@ function VersionBlockWithPreviewTitleLine({currentRef, version, currObjectVersio return shortVersionTitle; } const chevronDirection = isInfoOpen ? 'up' : 'down'; - const openVersionInMoinPanel = versionTools.openVersionInMoinPanel.bind(null, currentRef, version, currObjectVersions, 'select-button', + const openVersionInMainPanel = versionTools.openVersionInMainPanel.bind(null, currentRef, version, currObjectVersions, 'select-button', null, openVersionInReader); const buttonText = isSelected ? 'Currently Selected' : 'Select'; return ( @@ -23,7 +23,7 @@ function VersionBlockWithPreviewTitleLine({currentRef, version, currObjectVersio </a> <VersionBlockSelectButton isSelected={isSelected} - openVersionInMoinPanel={openVersionInMoinPanel} + openVersionInMainPanel={openVersionInMainPanel} text={buttonText} link={versionTools.makeVersionLink(currentRef, version, currObjectVersions, true)} /> From fcac8744dc80b90f2005d1c03a708cb46bb4d251 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 29 Nov 2023 12:13:39 +0200 Subject: [PATCH 575/756] helm(search): fix formatting of SEARCH_URL --- .../templates/configmap/local-settings-file.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml b/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml index 860f93d95b..3c57402e4e 100644 --- a/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml +++ b/helm-chart/sefaria-project/templates/configmap/local-settings-file.yaml @@ -137,7 +137,7 @@ data: SERVER_EMAIL = os.getenv("SERVER_EMAIL") auth_str = f'{os.getenv("ELASTIC_USERNAME")}:{os.getenv("ELASTIC_PASSWORD")}@' if os.getenv("ELASTIC_USERNAME") else '' - SEARCH_URL = f'http://{auth_str}' + {os.getenv("SEARCH_HOST")} + ":9200" + SEARCH_URL = f'http://{auth_str}{os.getenv("SEARCH_HOST")}:9200' SEARCH_INDEX_ON_SAVE = True SEARCH_INDEX_NAME_TEXT = 'text' # name of the ElasticSearch index to use SEARCH_INDEX_NAME_SHEET = 'sheet' From a2d7305a8e4698c4be0450d251dba96c403cf51d Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Wed, 29 Nov 2023 12:30:40 +0200 Subject: [PATCH 576/756] fix(Admin Editor): Add places that are neighborhoods or villages --- sefaria/model/place.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sefaria/model/place.py b/sefaria/model/place.py index 6f5b2729b5..dd42ff7f47 100644 --- a/sefaria/model/place.py +++ b/sefaria/model/place.py @@ -64,7 +64,7 @@ def create_new_place(cls, en, he=None): def city_to_coordinates(self, city): geolocator = Nominatim(user_agent='hello@sefaria.org') location = geolocator.geocode(city) - if location and location.raw['type'] in ['administrative', 'city', 'town', 'municipality']: + if location and location.raw['type'] in ['administrative', 'city', 'town', 'municipality', 'neighbourhood', 'village']: self.point_location(lon=location.longitude, lat=location.latitude) else: raise InputError(f"{city} is not a real city.") From 29ec9de6cd98ea8cbb368cb4ff94d192223cb005 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 29 Nov 2023 12:39:19 +0200 Subject: [PATCH 577/756] helm(search): fix location of entrypoint.sh --- helm-chart/sefaria-project/templates/rollout/nginx.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/helm-chart/sefaria-project/templates/rollout/nginx.yaml b/helm-chart/sefaria-project/templates/rollout/nginx.yaml index e827bd842d..8016817815 100644 --- a/helm-chart/sefaria-project/templates/rollout/nginx.yaml +++ b/helm-chart/sefaria-project/templates/rollout/nginx.yaml @@ -52,8 +52,7 @@ spec: - name: nginx image: "{{ .Values.nginx.containerImage.imageRegistry }}:{{ .Values.nginx.containerImage.tag }}" imagePullPolicy: Always - command: - - /entrypoint.sh + command: ["bash", "-c", "/usr/src/entrypoint.sh"] ports: - containerPort: 80 - containerPort: 443 From 9add8a1efba430db1a8141b8c1a9f34b4350d8fb Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 29 Nov 2023 13:28:30 +0200 Subject: [PATCH 578/756] helm(search): fix location of entrypoint.sh --- helm-chart/sefaria-project/templates/rollout/nginx.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/helm-chart/sefaria-project/templates/rollout/nginx.yaml b/helm-chart/sefaria-project/templates/rollout/nginx.yaml index 8016817815..e055059ecb 100644 --- a/helm-chart/sefaria-project/templates/rollout/nginx.yaml +++ b/helm-chart/sefaria-project/templates/rollout/nginx.yaml @@ -74,6 +74,9 @@ spec: name: nginx-conf subPath: nginx.template.conf readOnly: true + - mountPath: /usr/src/entrypoint.sh + name: nginx-conf + subPath: entrypoint.sh {{- if .Values.instrumentation.enabled }} - mountPath: /etc/nginx/opentracing.json name: nginx-conf From 6c2b5e193a373ea76fa93978c14511e86b2be235 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 29 Nov 2023 13:51:30 +0200 Subject: [PATCH 579/756] helm(search): fix permissions of entrypoint.sh --- helm-chart/sefaria-project/templates/rollout/nginx.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/helm-chart/sefaria-project/templates/rollout/nginx.yaml b/helm-chart/sefaria-project/templates/rollout/nginx.yaml index e055059ecb..e2e2334473 100644 --- a/helm-chart/sefaria-project/templates/rollout/nginx.yaml +++ b/helm-chart/sefaria-project/templates/rollout/nginx.yaml @@ -119,6 +119,7 @@ spec: - name: nginx-conf configMap: name: nginx-conf-{{ .Values.deployEnv }} + defaultMode: 0755 - name: robots-txt configMap: name: robots-txt-{{ .Values.deployEnv }} From 0b43a860f7f0505cb97d82bd60f2cf2d2ea0104b Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Wed, 29 Nov 2023 14:16:49 +0200 Subject: [PATCH 580/756] feat(translations): border line only after language. --- static/css/s2.css | 5 +++++ static/js/VersionBlock.jsx | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/static/css/s2.css b/static/css/s2.css index 571f469146..25c6657b3c 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -3601,8 +3601,13 @@ display: none; } .versionsBox .versionBlock { padding: 20px 0; +} +.versionsBox .versionBlock:not(.with-preview) { border-top: solid 1px #CCC; } +.language-block .versionLanguage { + border-bottom: solid 1px #CCC; +} .bookPage .versionsBox .versionBlock{ padding-top: 20px; padding-bottom: 34px; diff --git a/static/js/VersionBlock.jsx b/static/js/VersionBlock.jsx index 2e3b5afd0d..1e4164925d 100644 --- a/static/js/VersionBlock.jsx +++ b/static/js/VersionBlock.jsx @@ -392,7 +392,7 @@ class VersionsBlocksList extends Component{ <div className="versionsBox"> { sortedLanguages.map((lang) => ( - <div key={lang}> + <div className="language-block" key={lang}> { this.props.showLanguageHeaders ? <div className="versionLanguage sans-serif"> {Sefaria._(Sefaria.translateISOLanguageCode(lang))}<span className="enInHe connectionsCount">{` (${this.props.versionsByLanguages[lang].length})`}</span> From b5f3bbec35688389d9fe8d7d9c22e767f5aba957 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 29 Nov 2023 14:23:38 +0200 Subject: [PATCH 581/756] helm(search): fix add auth header when proxy passing to ES --- helm-chart/sefaria-project/templates/configmap/nginx.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/helm-chart/sefaria-project/templates/configmap/nginx.yaml b/helm-chart/sefaria-project/templates/configmap/nginx.yaml index 342a5b97c5..845819e361 100644 --- a/helm-chart/sefaria-project/templates/configmap/nginx.yaml +++ b/helm-chart/sefaria-project/templates/configmap/nginx.yaml @@ -118,6 +118,7 @@ data: location /api/search/ { rewrite ^/(?:api/search)/(.*)$ /$1 break; proxy_set_header Content-Type application/json; # es 6.0 requires this header + proxy_set_header Authorization "Basic ${ELASTIC_AUTH_HEADER}"; add_header 'Access-Control-Allow-Origin' ''; proxy_pass http://elasticsearch_upstream/; } From 2d081dbc5bd5d9b54e752372c188b329bbab468c Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Wed, 29 Nov 2023 14:27:14 +0200 Subject: [PATCH 582/756] feat(translations): space between version title and image. --- static/css/s2.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/static/css/s2.css b/static/css/s2.css index 25c6657b3c..1cb5f8d278 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -6654,6 +6654,9 @@ But not to use a display block directive that might break continuous mode for ot color: var(--dark-grey); margin-top: 15px; } +.version-block-with-preview-details img { + padding-inline-start: 15px; +} .singlePanel .connection-buttons .connection-button{ text-align: start; margin-inline-end: 5px; From 7db0ffe52887d0a0ab5c2ebe6744a4e6b85aceb8 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 29 Nov 2023 14:44:11 +0200 Subject: [PATCH 583/756] helm(search): fix typo in $ELASTIC_USERNAME --- helm-chart/sefaria-project/templates/configmap/nginx.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helm-chart/sefaria-project/templates/configmap/nginx.yaml b/helm-chart/sefaria-project/templates/configmap/nginx.yaml index 845819e361..0c2fba7288 100644 --- a/helm-chart/sefaria-project/templates/configmap/nginx.yaml +++ b/helm-chart/sefaria-project/templates/configmap/nginx.yaml @@ -33,7 +33,7 @@ data: set -e - export ELASTIC_AUTH_HEADER=$(echo -n $ELASTIC_USER:$ELASTIC_PASSWORD | base64) + export ELASTIC_AUTH_HEADER=$(echo -n $ELASTIC_USERNAME:$ELASTIC_PASSWORD | base64) envsubst '${ENV_NAME},${VARNISH_HOST},${SEARCH_HOST},${RELEASE_TAG},${STRAPI_LOCATION},${ELASTIC_AUTH_HEADER}{{- if .Values.linker.enabled }},${LINKER_HOST}{{- end }}{{- if .Values.instrumentation.enabled }},${NGINX_VERSION}{{- end }}' < /conf/nginx.template.conf > /nginx.conf nginx -c /nginx.conf -g 'daemon off;' From 51f6c8bcedb2f9c1d3f341cbd87e8375ed7495fd Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Wed, 29 Nov 2023 15:01:47 +0200 Subject: [PATCH 584/756] fix(translations): replace margin and outline by padding. --- static/css/s2.css | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index 1cb5f8d278..8d0b3ee447 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -3586,8 +3586,7 @@ display: none; .version-block-with-preview-details { background-color: var(--lighter-grey); border-radius: 6px; - outline: 10px solid var(--lighter-grey); - margin: 10px; + padding: 10px; } .versionDetails-version-title { color: black; From ed77c69899498205556c40d046dae198750bd4f7 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Wed, 29 Nov 2023 15:32:34 +0200 Subject: [PATCH 585/756] fix(translations): make ReaderPanel.selectVersion work on mobile. --- static/js/ReaderApp.jsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index 3dd75d07eb..41dc82f487 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -1345,10 +1345,12 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { const { dependentPanel, isDependentPanelConnections } = this._getDependentPanel(n); // make sure object reference changes for setState() - dependentPanel.currVersions = {...panel.currVersions}; panel.currVersions = {...panel.currVersions}; + if (this.props.multiPanel) { //there is no dependentPanel in mobile + dependentPanel.currVersions = {...panel.currVersions}; - dependentPanel.settings.language = this._getPanelLangOnVersionChange(dependentPanel, versionLanguage, isDependentPanelConnections); + dependentPanel.settings.language = this._getPanelLangOnVersionChange(dependentPanel, versionLanguage, isDependentPanelConnections); + } this.setState({panels: this.state.panels}); } navigatePanel(n, ref, currVersions={en: null, he: null}) { From 95923ce8a2b20b6e8909578a8680735550692fe9 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Wed, 29 Nov 2023 15:51:17 +0200 Subject: [PATCH 586/756] chore: fix CSS of Upload picture button --- sefaria/google_storage_manager.py | 5 +++-- static/js/Misc.jsx | 8 ++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/sefaria/google_storage_manager.py b/sefaria/google_storage_manager.py index 756dbc60e4..4bd2fa6c23 100644 --- a/sefaria/google_storage_manager.py +++ b/sefaria/google_storage_manager.py @@ -25,8 +25,9 @@ class GoogleStorageManager(object): @classmethod def get_bucket(cls, bucket_name): if getattr(cls, 'client', None) is None: - # for local development, change below line to cls.client = storage.Client(project="production-deployment") - cls.client = storage.Client.from_service_account_json(GOOGLE_APPLICATION_CREDENTIALS_FILEPATH) + # for local development, change below line to + cls.client = storage.Client(project="production-deployment") + #cls.client = storage.Client.from_service_account_json(GOOGLE_APPLICATION_CREDENTIALS_FILEPATH) bucket = cls.client.get_bucket(bucket_name) return bucket diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index da34eeb963..de1c881ba1 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -1640,13 +1640,13 @@ const PictureUploader = ({callback, old_filename, caption}) => { <label> <span className="optional"><InterfaceText>Please use horizontal, square, or only-slightly-vertical images for best results.</InterfaceText></span> </label> - <div role="button" title={Sefaria._("Add an image")} aria-label="Add an image" className="editorAddInterfaceButton" contentEditable={false} onClick={(e) => e.stopPropagation()} id="addImageButton"> - <label htmlFor="addImageFileSelector" id="addImageFileSelectorLabel"> - <div className="button small blue control-elem" tabIndex="0" role="button"> + <div role="button" title={Sefaria._("Add an image")} aria-label="Add an image" contentEditable={false} onClick={(e) => e.stopPropagation()} id="addImageButton"> + <label htmlFor="addImageFileSelector"> + <div className="button extraSmall blue control-elem" tabIndex="0" role="button"> <InterfaceText>Upload Picture</InterfaceText> </div> </label> - </div><input style={{visibility: "hidden"}} id="addImageFileSelector" type="file" onChange={onFileSelect} ref={fileInput} /> + </div><input style={{display: "none"}} id="addImageFileSelector" type="file" onChange={onFileSelect} ref={fileInput} /> {old_filename !== "" && <div style={{"max-width": "420px"}}> <br/><ImageWithCaption photoLink={old_filename} caption={caption}/> <br/><div onClick={deleteImage} className="button extraSmall blue control-elem" tabIndex="1" role="button"> From 6d8152de199a9c1146a49b9a2c47e2858fbb713a Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Thu, 30 Nov 2023 11:40:15 +0200 Subject: [PATCH 587/756] chore: revert get_bucket --- sefaria/google_storage_manager.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sefaria/google_storage_manager.py b/sefaria/google_storage_manager.py index 4bd2fa6c23..756dbc60e4 100644 --- a/sefaria/google_storage_manager.py +++ b/sefaria/google_storage_manager.py @@ -25,9 +25,8 @@ class GoogleStorageManager(object): @classmethod def get_bucket(cls, bucket_name): if getattr(cls, 'client', None) is None: - # for local development, change below line to - cls.client = storage.Client(project="production-deployment") - #cls.client = storage.Client.from_service_account_json(GOOGLE_APPLICATION_CREDENTIALS_FILEPATH) + # for local development, change below line to cls.client = storage.Client(project="production-deployment") + cls.client = storage.Client.from_service_account_json(GOOGLE_APPLICATION_CREDENTIALS_FILEPATH) bucket = cls.client.get_bucket(bucket_name) return bucket From e8b35c3894833b72a834d7570337fd60b98f9a03 Mon Sep 17 00:00:00 2001 From: Ephraim <ephraim@sefaria.org> Date: Thu, 30 Nov 2023 15:44:14 +0200 Subject: [PATCH 588/756] test: Temporarily disable crm tests that were never properly written --- sefaria/helper/crm/tests/crm_connection_manager_test.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sefaria/helper/crm/tests/crm_connection_manager_test.py b/sefaria/helper/crm/tests/crm_connection_manager_test.py index ab8c4182fc..7186bf3ddc 100644 --- a/sefaria/helper/crm/tests/crm_connection_manager_test.py +++ b/sefaria/helper/crm/tests/crm_connection_manager_test.py @@ -3,7 +3,7 @@ from sefaria.helper.crm.nationbuilder import NationbuilderConnectionManager from sefaria.helper.crm.salesforce import SalesforceConnectionManager - +""" class TestConnectionTest(TestCase): def __init__(self): self.nb_connection = NationbuilderConnectionManager() @@ -12,3 +12,4 @@ def __init__(self): def test_subscribes_user(self): for connection in self.connections: +""" \ No newline at end of file From f07d43fb55960d2e0c3323c4c05c00af2d5b422b Mon Sep 17 00:00:00 2001 From: Ephraim <ephraim@sefaria.org> Date: Thu, 30 Nov 2023 19:20:53 +0200 Subject: [PATCH 589/756] fix: Try to connect to mongo replica set using primaryPreferred read preference This is to try and see if this affects sandbox - database connectivity and discoverability --- sefaria/system/database.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sefaria/system/database.py b/sefaria/system/database.py index ff0f07b66e..6d24183dc9 100644 --- a/sefaria/system/database.py +++ b/sefaria/system/database.py @@ -41,9 +41,9 @@ def get_test_db(): # and also escape user/pass username = urllib.parse.quote_plus(SEFARIA_DB_USER) password = urllib.parse.quote_plus(SEFARIA_DB_PASSWORD) - connection_uri = 'mongodb://{}:{}@{}/?ssl=false&readPreference=secondaryPreferred&replicaSet={}'.format(username, password, MONGO_HOST, MONGO_REPLICASET_NAME) + connection_uri = 'mongodb://{}:{}@{}/?ssl=false&readPreference=primaryPreferred&replicaSet={}'.format(username, password, MONGO_HOST, MONGO_REPLICASET_NAME) else: - connection_uri = 'mongodb://{}/?ssl=false&readPreference=secondaryPreferred&replicaSet={}'.format(MONGO_HOST, MONGO_REPLICASET_NAME) + connection_uri = 'mongodb://{}/?ssl=false&readPreference=primaryPreferred&replicaSet={}'.format(MONGO_HOST, MONGO_REPLICASET_NAME) # Now connect to the mongo server client = pymongo.MongoClient(connection_uri) From e14b966f8074bf776cfc3711ba189115faddcd6b Mon Sep 17 00:00:00 2001 From: Brendan Galloway <brendan@flanksource.com> Date: Fri, 1 Dec 2023 13:06:59 +0200 Subject: [PATCH 590/756] ci: rework pytest launcher to read running spec and prevent drift --- .github/workflows/continuous.yaml | 4 +--- build/ci/createJobFromRollout.sh | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) create mode 100755 build/ci/createJobFromRollout.sh diff --git a/.github/workflows/continuous.yaml b/.github/workflows/continuous.yaml index d025215746..e0a71c824a 100644 --- a/.github/workflows/continuous.yaml +++ b/.github/workflows/continuous.yaml @@ -309,12 +309,10 @@ jobs: | sed 's/[^a-z0-9\.\-]//g') >> $GITHUB_OUTPUT - name: Start Job - run: envsubst '${GITHUB_RUN_ID},${DEPLOY_ENV},${WEB_IMAGE_NAME},${WEB_IMAGE_TAG},${TIMESTAMP}' < ./build/ci/pyTestPod.yaml | kubectl apply -f - + run: ./build/ci/createJobFromRollout.sh $GITHUB_RUN_ID $DEPLOY_ENV env: # dependent on GITHUB_RUN_ID, which is implicitly passed in DEPLOY_ENV: sandbox-${{ steps.get-sha.outputs.sha_short }} - WEB_IMAGE_NAME: us-east1-docker.pkg.dev/${{secrets.DEV_PROJECT}}/containers/sefaria-web-${{ steps.branch-name.outputs.current_branch }} - WEB_IMAGE_TAG: sha-${{ steps.get-sha.outputs.sha_short }} - name: Wait For Job To Finish run: ./build/ci/waitForCIJob.bash timeout-minutes: 60 diff --git a/build/ci/createJobFromRollout.sh b/build/ci/createJobFromRollout.sh new file mode 100755 index 0000000000..83095909b0 --- /dev/null +++ b/build/ci/createJobFromRollout.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +GITHUB_RUN_ID=$1 +DEPLOY_ENV=$2 + +cat << EOF > job.yaml +apiVersion: batch/v1 +kind: Job +metadata: + labels: + ci-run: $GITHUB_RUN_ID + test-name: pytest + name: $DEPLOY_ENV-pytest-sandbox-$GITHUB_RUN_ID +spec: + backoffLimit: 2 # in waitForCIJob, we look for 2 fails before declaring failure. This could be made a variable. + template: + metadata: + labels: + ci-run: $GITHUB_RUN_ID + test-name: pytest + spec: +EOF + +kubectl get rollout $DEPLOY_ENV-web -o yaml | yq '.spec.template.spec' > spec.yaml +yq -i '.spec.template.spec += load("spec.yaml")' job.yaml +yq -i '.spec.template.spec.containers[0].args = ["-c", "pip3 install pytest-django; pytest -v -m \"not deep and not failing\" ./sefaria; echo $? > /dev/stdout; exit 0;"]' job.yaml + +kubectl apply -f job.yaml From b38a9224a75700a913f4086afa1a9366e9d47e8c Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Sun, 3 Dec 2023 14:57:03 +0200 Subject: [PATCH 591/756] fix(Topic Editor): dont allow "Cancel" (only allow "Save") if removed/uploaded image --- static/js/TopicEditor.jsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/static/js/TopicEditor.jsx b/static/js/TopicEditor.jsx index 7fed81d4f8..28f49cc3ce 100644 --- a/static/js/TopicEditor.jsx +++ b/static/js/TopicEditor.jsx @@ -33,11 +33,20 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { .filter(x => x.slug !== origData.origSlug) // dont include topics that are self-linked || []); const [isChanged, setIsChanged] = useState(false); + const [changedPicture, setChangedPicture] = useState(false); const toggle = function() { setSavingStatus(savingStatus => !savingStatus); } + const closeTopicEditor = (e) => { + if (changedPicture) { + alert("You changed the topic picture, and therefore, you must save your topic changes."); + return; + } + close(e); + } + const handleCatChange = function(e) { data.catSlug = e.target.value; @@ -174,6 +183,7 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { } const handlePictureChange = (url) => { data["image_uri"] = url; + setChangedPicture(true); updateData({...data}); } @@ -193,7 +203,7 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { items.push("Picture Uploader"); items.push("English Caption"); items.push("Hebrew Caption"); - return <AdminEditor title="Topic Editor" close={close} catMenu={catMenu} data={data} savingStatus={savingStatus} + return <AdminEditor title="Topic Editor" close={closeTopicEditor} catMenu={catMenu} data={data} savingStatus={savingStatus} validate={validate} deleteObj={deleteObj} updateData={updateData} isNew={isNew} items={items} pictureUploader={<PictureUploader callback={handlePictureChange} old_filename={data.image_uri} caption={{en: data.enImgCaption, he: data.heImgCaption}}/>} From 2386a9fd1228195894ed2a158123e59074a1facf Mon Sep 17 00:00:00 2001 From: Ephraim <ephraim@sefaria.org> Date: Sun, 3 Dec 2023 15:05:25 +0200 Subject: [PATCH 592/756] ci: Fix for error parsing job manifest file Fixes: 'error: unable to decode "job.yaml": json: cannot unmarshal number into Go struct field ObjectMeta.metadata.labels of type string' --- build/ci/createJobFromRollout.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/ci/createJobFromRollout.sh b/build/ci/createJobFromRollout.sh index 83095909b0..85b0723dda 100755 --- a/build/ci/createJobFromRollout.sh +++ b/build/ci/createJobFromRollout.sh @@ -8,7 +8,7 @@ apiVersion: batch/v1 kind: Job metadata: labels: - ci-run: $GITHUB_RUN_ID + ci-run: "${GITHUB_RUN_ID}" test-name: pytest name: $DEPLOY_ENV-pytest-sandbox-$GITHUB_RUN_ID spec: From 463ff17009b2f9ee1e2a3b1d6e3b6ba35e570ede Mon Sep 17 00:00:00 2001 From: Ephraim <ephraim@sefaria.org> Date: Sun, 3 Dec 2023 16:41:12 +0200 Subject: [PATCH 593/756] ci: Fix for error parsing job manifest file #2 --- build/ci/createJobFromRollout.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/ci/createJobFromRollout.sh b/build/ci/createJobFromRollout.sh index 85b0723dda..d2812abb5b 100755 --- a/build/ci/createJobFromRollout.sh +++ b/build/ci/createJobFromRollout.sh @@ -16,7 +16,7 @@ spec: template: metadata: labels: - ci-run: $GITHUB_RUN_ID + ci-run: "${GITHUB_RUN_ID}" test-name: pytest spec: EOF From a7f390c23d689ce57293c105681bc7e67d83593a Mon Sep 17 00:00:00 2001 From: Ephraim <ephraim@sefaria.org> Date: Sun, 3 Dec 2023 19:44:23 +0200 Subject: [PATCH 594/756] ci: Try to add restartPolicy to the pytest job's spec. This is to address the error: 'The Job "<sandbox-id>" is invalid: spec.template.spec.restartPolicy: Required value: valid values: "OnFailure", "Never"' --- build/ci/createJobFromRollout.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/build/ci/createJobFromRollout.sh b/build/ci/createJobFromRollout.sh index d2812abb5b..c56baf1d25 100755 --- a/build/ci/createJobFromRollout.sh +++ b/build/ci/createJobFromRollout.sh @@ -23,6 +23,7 @@ EOF kubectl get rollout $DEPLOY_ENV-web -o yaml | yq '.spec.template.spec' > spec.yaml yq -i '.spec.template.spec += load("spec.yaml")' job.yaml +yq -i '.spec.template.spec.restartPolicy = Never' job.yaml yq -i '.spec.template.spec.containers[0].args = ["-c", "pip3 install pytest-django; pytest -v -m \"not deep and not failing\" ./sefaria; echo $? > /dev/stdout; exit 0;"]' job.yaml kubectl apply -f job.yaml From 61b7807cf2033926e8d0e5cc3f2dec714c5343e2 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Mon, 4 Dec 2023 02:24:34 -0500 Subject: [PATCH 595/756] Reorganize code to be within different scope and add some documentation --- static/js/StaticPages.jsx | 278 +++++++++++++++++++------------------- 1 file changed, 140 insertions(+), 138 deletions(-) diff --git a/static/js/StaticPages.jsx b/static/js/StaticPages.jsx index 3d1a835b15..a2154125b0 100644 --- a/static/js/StaticPages.jsx +++ b/static/js/StaticPages.jsx @@ -2640,8 +2640,9 @@ const ConditionalLink = ({ link, children }) => * Team Page */ - - +// Takes an array and boolean proposition function to be evaluated against each element +// Returns two arrays within an array +// The first contains the elements for which the proposition function evaluates to true. The second contains the rest const partition = (arr, prop) => arr.reduce( (accumulator, currentValue) => { @@ -2651,156 +2652,157 @@ const partition = (arr, prop) => [[], []] ); -const TeamMembersPage = memo(() => { - const byLastName = () => { - const locale = Sefaria.interfaceLang === "hebrew" ? "he" : "en"; - return (a, b) => { - const lastNameA = a.teamMemberDetails.teamName[locale].split(" ").pop(); - const lastNameB = b.teamMemberDetails.teamName[locale].split(" ").pop(); - return lastNameA.localeCompare(lastNameB, locale); - }; +// Defines a comparator to be used for sorting team members +const byLastName = () => { + const locale = Sefaria.interfaceLang === "hebrew" ? "he" : "en"; + return (a, b) => { + const lastNameA = a.teamMemberDetails.teamName[locale].split(" ").pop(); + const lastNameB = b.teamMemberDetails.teamName[locale].split(" ").pop(); + return lastNameA.localeCompare(lastNameB, locale); }; - - const TeamTitle = ({ teamTitle }) => ( - <div className="teamTitle"> - <InterfaceText text={teamTitle} /> - </div> - ); - - const TeamName = ({ teamName }) => ( - <div className="teamName"> - <InterfaceText text={teamName} /> - </div> - ); - - const TeamMemberDetails = ({ teamMemberDetails }) => ( - <div className="teamMemberDetails"> - <TeamName teamName={teamMemberDetails.teamName} /> - <TeamTitle teamTitle={teamMemberDetails.teamTitle} /> - </div> - ); - - const TeamMemberImage = ({ teamMember }) => ( - <div className="teamMemberImage"> - <img - src={teamMember.teamMemberImage} - alt={`Headshot of ${teamMember.teamMemberDetails.teamName.en}`} - /> - </div> +}; + +const TeamTitle = ({ teamTitle }) => ( + <div className="teamTitle"> + <InterfaceText text={teamTitle} /> + </div> +); + +const TeamName = ({ teamName }) => ( + <div className="teamName"> + <InterfaceText text={teamName} /> + </div> +); + +const TeamMemberDetails = ({ teamMemberDetails }) => ( + <div className="teamMemberDetails"> + <TeamName teamName={teamMemberDetails.teamName} /> + <TeamTitle teamTitle={teamMemberDetails.teamTitle} /> + </div> +); + +const TeamMemberImage = ({ teamMember }) => ( + <div className="teamMemberImage"> + <img + src={teamMember.teamMemberImage} + alt={`Headshot of ${teamMember.teamMemberDetails.teamName.en}`} + /> + </div> +); + +const TeamMember = ({ teamMember }) => ( + <div className="teamMember"> + <TeamMemberImage teamMember={teamMember} /> + <TeamMemberDetails teamMemberDetails={teamMember.teamMemberDetails} /> + </div> +); + +const TeamMembers = ({ teamMembers }) => ( + <> + {teamMembers.map((teamMember) => ( + <TeamMember key={teamMember.id} teamMember={teamMember} /> + ))} + </> +); + +const BoardMember = ({ boardMember }) => ( + <div className="teamBoardMember"> + <TeamMemberDetails teamMemberDetails={boardMember.teamMemberDetails} /> + </div> +); + +const BoardMembers = ({ boardMembers }) => { + let chairmanBoardMember; + const chairmanIndex = boardMembers.findIndex( + (boardMember) => + boardMember.teamMemberDetails.teamTitle.en.toLowerCase() === + "chairman" ); - - const TeamMember = ({ teamMember }) => ( - <div className="teamMember"> - <TeamMemberImage teamMember={teamMember} /> - <TeamMemberDetails teamMemberDetails={teamMember.teamMemberDetails} /> - </div> + if (chairmanIndex !== -1) { + chairmanBoardMember = boardMembers.splice(chairmanIndex, 1); + } + const [cofounderBoardMembers, regularBoardMembers] = partition( + boardMembers, + (boardMember) => + boardMember.teamMemberDetails.teamTitle.en.toLowerCase() === + "co-founder" ); - - const TeamMembers = ({ teamMembers }) => ( + + return ( <> - {teamMembers.map((teamMember) => ( - <TeamMember key={teamMember.id} teamMember={teamMember} /> + {chairmanBoardMember && ( + <BoardMember boardMember={chairmanBoardMember[0]} /> + )} + {cofounderBoardMembers.map((boardMember) => ( + <BoardMember key={boardMember.id} boardMember={boardMember} /> + ))} + {regularBoardMembers.sort(byLastName()).map((boardMember) => ( + <BoardMember key={boardMember.id} boardMember={boardMember} /> ))} </> ); - - const BoardMember = ({ boardMember }) => ( - <div className="teamBoardMember"> - <TeamMemberDetails teamMemberDetails={boardMember.teamMemberDetails} /> - </div> - ); - - const BoardMembers = ({ boardMembers }) => { - let chairmanBoardMember; - const chairmanIndex = boardMembers.findIndex( - (boardMember) => - boardMember.teamMemberDetails.teamTitle.en.toLowerCase() === - "chairman" - ); - if (chairmanIndex !== -1) { - chairmanBoardMember = boardMembers.splice(chairmanIndex, 1); +}; + +const fetchTeamMembersJSON = async () => { + const query = ` +query { + teamMembers(pagination: { limit: -1 }) { + data { + id + attributes { + teamName + teamTitle + isTeamBoardMember + teamMemberImage { + data { + attributes { + url + } + } } - const [cofounderBoardMembers, regularBoardMembers] = partition( - boardMembers, - (boardMember) => - boardMember.teamMemberDetails.teamTitle.en.toLowerCase() === - "co-founder" - ); - - return ( - <> - {chairmanBoardMember && ( - <BoardMember boardMember={chairmanBoardMember[0]} /> - )} - {cofounderBoardMembers.map((boardMember) => ( - <BoardMember key={boardMember.id} boardMember={boardMember} /> - ))} - {regularBoardMembers.sort(byLastName()).map((boardMember) => ( - <BoardMember key={boardMember.id} boardMember={boardMember} /> - ))} - </> - ); - }; + localizations { + data { + attributes { + locale + teamName + teamTitle + } + } + } + } + } + } +} +`; + try { + const response = await fetch(STRAPI_INSTANCE + "/graphql", { + method: "POST", + mode: "cors", + cache: "no-cache", + credentials: "omit", + headers: { + "Content-Type": "application/json", + }, + redirect: "follow", + referrerPolicy: "no-referrer", + body: JSON.stringify({ query }), + }); + if (!response.ok) { + throw new Error(`HTTP Error: ${response.statusText}`); + } + const data = await response.json(); + return data; + } catch (error) { + throw error; + } +}; +const TeamMembersPage = memo(() => { const [ordinaryTeamMembers, setOrdinaryTeamMembers] = useState([]); const [teamBoardMembers, setTeamBoardMembers] = useState([]); const [error, setError] = useState(null); useEffect(() => { - const fetchTeamMembersJSON = async () => { - const query = ` - query { - teamMembers(pagination: { limit: -1 }) { - data { - id - attributes { - teamName - teamTitle - isTeamBoardMember - teamMemberImage { - data { - attributes { - url - } - } - } - localizations { - data { - attributes { - locale - teamName - teamTitle - } - } - } - } - } - } - } - `; - try { - const response = await fetch(STRAPI_INSTANCE + "/graphql", { - method: "POST", - mode: "cors", - cache: "no-cache", - credentials: "omit", - headers: { - "Content-Type": "application/json", - }, - redirect: "follow", - referrerPolicy: "no-referrer", - body: JSON.stringify({ query }), - }); - if (!response.ok) { - throw new Error(`HTTP Error: ${response.statusText}`); - } - const data = await response.json(); - return data; - } catch (error) { - throw error; - } - }; - const loadTeamMembers = async () => { if (typeof STRAPI_INSTANCE !== "undefined" && STRAPI_INSTANCE) { try { From 3561b7c39c02c4849f7cbad4458432008aac41b9 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Mon, 4 Dec 2023 09:31:44 +0200 Subject: [PATCH 596/756] feat(search): add es6_compat parameter to search wrapper api so that older clients can still work --- reader/views.py | 9 ++++++++- sefaria/urls.py | 4 +++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/reader/views.py b/reader/views.py index be9d24fb2a..8701e0dfad 100644 --- a/reader/views.py +++ b/reader/views.py @@ -4200,7 +4200,12 @@ def dummy_search_api(request): @csrf_exempt -def search_wrapper_api(request): +def search_wrapper_api(request, es6_compat=False): + """ + @param request: + @param es6_compat: True to return API response that's compatible with an Elasticsearch 6 compatible client + @return: + """ from sefaria.helper.search import get_elasticsearch_client if request.method == "POST": @@ -4215,6 +4220,8 @@ def search_wrapper_api(request): response = search_obj.execute() if response.success(): response_json = getattr(response.to_dict(), 'body', response.to_dict()) + if es6_compat and isinstance(response_json['hits']['total'], dict): + response_json['hits']['total'] = response_json['hits']['total']['value'] return jsonResponse(response_json, callback=request.GET.get("callback", None)) return jsonResponse({"error": "Error with connection to Elasticsearch. Total shards: {}, Shards successful: {}, Timed out: {}".format(response._shards.total, response._shards.successful, response.timed_out)}, callback=request.GET.get("callback", None)) return jsonResponse({"error": "Unsupported HTTP method."}, callback=request.GET.get("callback", None)) diff --git a/sefaria/urls.py b/sefaria/urls.py index 757fcef1aa..cc69beab0a 100644 --- a/sefaria/urls.py +++ b/sefaria/urls.py @@ -238,7 +238,9 @@ # Search API urlpatterns += [ url(r'^api/dummy-search$', reader_views.dummy_search_api), - url(r'^api/search-wrapper$', reader_views.search_wrapper_api), + url(r'^api/search-wrapper/es6$', reader_views.search_wrapper_api, {'es6_compat': True}), + url(r'^api/search-wrapper/es8$', reader_views.search_wrapper_api), + url(r'^api/search-wrapper$', reader_views.search_wrapper_api, {'es6_compat': True}), url(r'^api/search-path-filter/(?P<book_title>.+)$', reader_views.search_path_filter), ] From 580dc7dcce54ecc374628c5ac978a35041cdb512 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Mon, 4 Dec 2023 09:34:39 +0200 Subject: [PATCH 597/756] helm(search): update time of reindex cronjob so it runs soon --- .../templates/cronjob/reindex-elasticsearch.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml b/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml index 7bc5124fd3..e0703a2efa 100644 --- a/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml +++ b/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml @@ -7,7 +7,7 @@ metadata: labels: {{- include "sefaria.labels" . | nindent 4 }} spec: - schedule: "0 0 * * 3" + schedule: "0 0 * * 2" jobTemplate: spec: backoffLimit: 1 From 740500f5457de403f52df9cfa479cae58cb25673 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Mon, 4 Dec 2023 09:48:46 +0200 Subject: [PATCH 598/756] helm(search): move SEARCH_HOST values to prod file --- build/ci/production-values.yaml | 2 ++ helm-chart/sefaria-project/values.yaml | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/build/ci/production-values.yaml b/build/ci/production-values.yaml index f2bb659176..bbf9b243d0 100644 --- a/build/ci/production-values.yaml +++ b/build/ci/production-values.yaml @@ -179,6 +179,8 @@ cronJobs: enabled: true reindexElasticSearch: enabled: true + SEARCH_HOST_ES6: "elasticsearch-data" + SEARCH_HOST_ES8: "elasticsearch-es-http.elasticsearch.svc" topicsIndexing: enabled: true trello: diff --git a/helm-chart/sefaria-project/values.yaml b/helm-chart/sefaria-project/values.yaml index 232e3d3be8..72722c98b7 100644 --- a/helm-chart/sefaria-project/values.yaml +++ b/helm-chart/sefaria-project/values.yaml @@ -399,8 +399,8 @@ cronJobs: enabled: false reindexElasticSearch: enabled: false - SEARCH_HOST_ES6: "elasticsearch-data" - SEARCH_HOST_ES8: "elasticsearch-es-http.elasticsearch.svc" + SEARCH_HOST_ES6: "" + SEARCH_HOST_ES8: "" topicsIndexing: enabled: false trello: From 4ff856f47f697121faa7eaaa163cfb6f918506bc Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Mon, 4 Dec 2023 03:32:45 -0500 Subject: [PATCH 599/756] Reorganize code and move grouping of the jobs by department to where the data is retrieved --- static/js/StaticPages.jsx | 324 +++++++++++++++++++------------------- 1 file changed, 163 insertions(+), 161 deletions(-) diff --git a/static/js/StaticPages.jsx b/static/js/StaticPages.jsx index a72ee6948a..e0c6460d43 100644 --- a/static/js/StaticPages.jsx +++ b/static/js/StaticPages.jsx @@ -2640,170 +2640,162 @@ const ConditionalLink = ({ link, children }) => * Jobs Page */ -const JobsPage = memo(() => { +// Show a different header with a description of Sefaria for the page in the case that there are jobs available +const JobsPageHeader = ({ jobsAreAvailable }) => { + return ( + <> + <header> + <h1 className="serif"> + <span className="int-en">Jobs at Sefaria</span> + <span className="int-he">משרות פנויות בספריא</span> + </h1> - // Show a different header with a description of Sefaria for the page in the case that there are jobs available - const JobsPageHeader = ({ jobsAreAvailable }) => { - return ( - <> - <header> - <h1 className="serif"> - <span className="int-en">Jobs at Sefaria</span> - <span className="int-he">משרות פנויות בספריא</span> - </h1> - - {jobsAreAvailable ? ( - <> - <h2> - <span className="int-en">About Sefaria</span> - <span className="int-he">אודות ספריא</span> - </h2> - <p> - <span className="int-en"> - Sefaria is a non-profit organization dedicated to creating the - future of Torah in an open and participatory way. We are assembling - a free, living library of Jewish texts and their interconnections, - in Hebrew and in translation. - </span> - <span className="int-he"> - ספריא היא ארגון ללא מטרות רווח שמטרתו יצירת הדור הבא של לימוד התורה - באופן פתוח ומשותף. אנחנו בספריא מרכיבים ספרייה חיה וחופשית של טקסטים - יהודיים וכלל הקישורים ביניהם, בשפת המקור ובתרגומים. - </span> - </p> - </> - ) : null} - </header> - </> - ); - }; - - const Job = ({ job }) => { - return ( - <div className="job"> - <a className="joblink" target="_blank" href={job.jobLink}> - {job.jobDescription} - </a> - </div> - ); - }; - - // Show the list of job postings within a department category - const JobsListForDepartment = ({ jobsList }) => { - return ( - <section className="jobsListForDepartment"> - {jobsList.map((job) => ( - <Job key={job.id} job={job} /> - ))} - </section> - ); - }; - - // Job postings are grouped by department. This component will show the jobs for a specific department - // Each department has a header for its category before showing a list of the job postings there - const JobPostingsByDepartment = ({ department, departmentJobPostings }) => { - return ( - <section className="section department englishOnly"> - <header> - <h2 className="anchorable">{department}</h2> - </header> - <JobsListForDepartment key={department} jobsList={departmentJobPostings} /> - </section> - ); - }; - - // Show all the job postings, but first group them by department and render each department separately - const JobPostings = ({ jobPostings }) => { - const groupedJobPostings = jobPostings.reduce((jobPostingsGroupedByDepartment, jobPosting) => { - const category = jobPosting.jobDepartmentCategory; - if (!jobPostingsGroupedByDepartment[category]) { - jobPostingsGroupedByDepartment[category] = []; - } - jobPostingsGroupedByDepartment[category].push(jobPosting); - return jobPostingsGroupedByDepartment; - }, {}); - - return ( - Object.entries(groupedJobPostings).map(([department, departmentJobPostings]) => { - return ( - <JobPostingsByDepartment - key={department} - department={department} - departmentJobPostings={departmentJobPostings} - /> - ); - }) - ); - }; - - - const NoJobsNotice = () => { - return ( - <div className="section nothing"> - <p> - <span className="int-en"> - Sefaria does not currently have any open positions. - Please follow us on <a target="_blank" href="http://www.facebook.com/sefaria.org" >Facebook</a> - to hear about our next openings. - </span> - <span className="int-he"> - ספריא איננה מחפשת כעת עובדים חדשים. - עקבו אחרינו ב<a target="_blank" href="http://www.facebook.com/sefaria.org" >פייסבוק</a>  - כדי להשאר מעודכנים במשרות עתידיות. - </span> - </p> - </div> - ); - }; + {jobsAreAvailable ? ( + <> + <h2> + <span className="int-en">About Sefaria</span> + <span className="int-he">אודות ספריא</span> + </h2> + <p> + <span className="int-en"> + Sefaria is a non-profit organization dedicated to creating the + future of Torah in an open and participatory way. We are assembling + a free, living library of Jewish texts and their interconnections, + in Hebrew and in translation. + </span> + <span className="int-he"> + ספריא היא ארגון ללא מטרות רווח שמטרתו יצירת הדור הבא של לימוד התורה + באופן פתוח ומשותף. אנחנו בספריא מרכיבים ספרייה חיה וחופשית של טקסטים + יהודיים וכלל הקישורים ביניהם, בשפת המקור ובתרגומים. + </span> + </p> + </> + ) : null} + </header> + </> + ); +}; - const [jobPostings, setJobPostings] = useState([]); - const [error, setError] = useState(null); +const Job = ({ job }) => { + return ( + <div className="job"> + <a className="joblink" target="_blank" href={job.jobLink}> + {job.jobDescription} + </a> + </div> + ); +}; - const fetchJobsJSON = async () => { - const currentDateTime = new Date().toISOString(); - const query = ` - query { - jobPostings( - pagination: { limit: -1 } - filters: { - jobPostingStartDate: { lte: \"${currentDateTime}\" } - jobPostingEndDate: { gte: \"${currentDateTime}\" } - } - ) { - data { - id - attributes { - jobLink - jobDescription - jobDepartmentCategory - } +// Show the list of job postings within a department category +const JobsListForDepartment = ({ jobsList }) => { + return ( + <section className="jobsListForDepartment"> + {jobsList.map((job) => ( + <Job key={job.id} job={job} /> + ))} + </section> + ); +}; + +// Job postings are grouped by department. This component will show the jobs for a specific department +// Each department has a header for its category before showing a list of the job postings there +const JobPostingsByDepartment = ({ department, departmentJobPostings }) => { + return ( + <section className="section department englishOnly"> + <header> + <h2 className="anchorable">{department}</h2> + </header> + <JobsListForDepartment key={department} jobsList={departmentJobPostings} /> + </section> + ); +}; + +// Show all the job postings grouped by department and render each department separately +const GroupedJobPostings = ({ groupedJobPostings }) => { + + return ( + Object.entries(groupedJobPostings).map(([department, departmentJobPostings]) => { + return ( + <JobPostingsByDepartment + key={department} + department={department} + departmentJobPostings={departmentJobPostings} + /> + ); + }) + ); +}; + + +const NoJobsNotice = () => { + return ( + <div className="section nothing"> + <p> + <span className="int-en"> + Sefaria does not currently have any open positions. + Please follow us on <a target="_blank" href="http://www.facebook.com/sefaria.org" >Facebook</a> + to hear about our next openings. + </span> + <span className="int-he"> + ספריא איננה מחפשת כעת עובדים חדשים. + עקבו אחרינו ב<a target="_blank" href="http://www.facebook.com/sefaria.org" >פייסבוק</a>  + כדי להשאר מעודכנים במשרות עתידיות. + </span> + </p> + </div> + ); +}; + + +const fetchJobsJSON = async () => { + const currentDateTime = new Date().toISOString(); + const query = ` + query { + jobPostings( + pagination: { limit: -1 } + filters: { + jobPostingStartDate: { lte: \"${currentDateTime}\" } + jobPostingEndDate: { gte: \"${currentDateTime}\" } + } + ) { + data { + id + attributes { + jobLink + jobDescription + jobDepartmentCategory } } } - `; - - try { - const response = await fetch(STRAPI_INSTANCE + "/graphql", { - method: "POST", - mode: "cors", - cache: "no-cache", - credentials: "omit", - headers: { - "Content-Type": "application/json", - }, - redirect: "follow", - referrerPolicy: "no-referrer", - body: JSON.stringify({ query }), - }); - if (!response.ok) { - throw new Error(`HTTP Error: ${response.statusText}`); - } - const data = await response.json(); - return data; - } catch (error) { - throw error; } - }; + `; + + try { + const response = await fetch(STRAPI_INSTANCE + "/graphql", { + method: "POST", + mode: "cors", + cache: "no-cache", + credentials: "omit", + headers: { + "Content-Type": "application/json", + }, + redirect: "follow", + referrerPolicy: "no-referrer", + body: JSON.stringify({ query }), + }); + if (!response.ok) { + throw new Error(`HTTP Error: ${response.statusText}`); + } + const data = await response.json(); + return data; + } catch (error) { + throw error; + } +}; + +const JobsPage = memo(() => { + const [groupedJobPostings, setGroupedJobPostings] = useState({}); + const [error, setError] = useState(null); useEffect(() => { const loadJobPostings = async () => { @@ -2822,7 +2814,17 @@ const JobsPage = memo(() => { }; }); - setJobPostings(jobsFromStrapi); + // Group the job postings by department + const groupedJobs = jobsFromStrapi.reduce((jobPostingsGroupedByDepartment, jobPosting) => { + const category = jobPosting.jobDepartmentCategory; + if (!jobPostingsGroupedByDepartment[category]) { + jobPostingsGroupedByDepartment[category] = []; + } + jobPostingsGroupedByDepartment[category].push(jobPosting); + return jobPostingsGroupedByDepartment; + }, {}); + + setGroupedJobPostings(groupedJobs); } catch (error) { console.error("Fetch error:", error); setError("Error: Sefaria's CMS cannot be reached"); @@ -2841,9 +2843,9 @@ const JobsPage = memo(() => { <h1>{error}</h1> ) : ( <> - <JobsPageHeader jobsAreAvailable={jobPostings?.length} /> - {jobPostings?.length ? ( - <JobPostings jobPostings={jobPostings} /> + <JobsPageHeader jobsAreAvailable={Object.keys(groupedJobPostings)?.length} /> + {Object.keys(groupedJobPostings)?.length ? ( + <GroupedJobPostings groupedJobPostings={groupedJobPostings} /> ) : ( <NoJobsNotice /> )} From 4ac841b795fb6d5a58046a8f0373d91716317b50 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Mon, 4 Dec 2023 10:45:44 +0200 Subject: [PATCH 600/756] chore: remove unnecessary imports --- reader/views.py | 4 ++-- static/js/Misc.jsx | 2 +- static/js/TopicEditor.jsx | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/reader/views.py b/reader/views.py index 6846950a46..e2bab5ddf0 100644 --- a/reader/views.py +++ b/reader/views.py @@ -62,9 +62,9 @@ from sefaria.helper.topic import get_topic, get_all_topics, get_topics_for_ref, get_topics_for_book, \ get_bulk_topics, recommend_topics, get_top_topic, get_random_topic, \ get_random_topic_source, edit_topic_source, \ - update_order_of_topic_sources, delete_ref_topic_link, update_authors_place_and_time, add_image_to_topic + update_order_of_topic_sources, delete_ref_topic_link, update_authors_place_and_time from sefaria.helper.community_page import get_community_page_items -from sefaria.helper.file import get_resized_file, thumbnail_image_file +from sefaria.helper.file import get_resized_file from sefaria.image_generator import make_img_http_response import sefaria.tracker as tracker diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index de1c881ba1..4bdedf372c 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -15,7 +15,7 @@ import {ContentText} from "./ContentText"; import ReactTags from "react-tag-autocomplete"; import {AdminEditorButton, useEditToggle} from "./AdminEditor"; import {CategoryEditor, ReorderEditor} from "./CategoryEditor"; -import {refSort, TopicImage} from "./TopicPage"; +import {refSort} from "./TopicPage"; import {TopicEditor} from "./TopicEditor"; import {generateContentForModal, SignUpModalKind} from './sefaria/signupModalContent'; import {SourceEditor} from "./SourceEditor"; diff --git a/static/js/TopicEditor.jsx b/static/js/TopicEditor.jsx index 28f49cc3ce..82d4d4f24a 100644 --- a/static/js/TopicEditor.jsx +++ b/static/js/TopicEditor.jsx @@ -1,5 +1,5 @@ import Sefaria from "./sefaria/sefaria"; -import {InterfaceText, requestWithCallBack, ProfilePic, PictureUploader} from "./Misc"; +import {InterfaceText, requestWithCallBack, PictureUploader} from "./Misc"; import $ from "./sefaria/sefariaJquery"; import {AdminEditor} from "./AdminEditor"; import {Reorder} from "./CategoryEditor"; From 88cdb4999787ed0fc9ff968d7ddca779915dd2c1 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Mon, 4 Dec 2023 10:53:45 +0200 Subject: [PATCH 601/756] helm(search): install py_elasticsearch 8 in reindex cronjob --- .../templates/cronjob/reindex-elasticsearch.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml b/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml index e0703a2efa..23b30e2659 100644 --- a/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml +++ b/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml @@ -64,7 +64,7 @@ spec: command: ["bash"] args: [ "-c", - "mkdir -p /log && touch /log/sefaria_book_errors.log && pip install numpy && /app/run /app/scripts/reindex_elasticsearch_cronjob.py" + "mkdir -p /log && touch /log/sefaria_book_errors.log && pip install numpy elasticsearch==8.8.2 git+https://github.com/Sefaria/elasticsearch-dsl-py@v8.0.0#egg=elasticsearch-dsl && /app/run /app/scripts/reindex_elasticsearch_cronjob.py" ] restartPolicy: Never volumes: From c8bc43398897d847459b1067cfb2d85429903225 Mon Sep 17 00:00:00 2001 From: Brendan Galloway <brendan@flanksource.com> Date: Mon, 4 Dec 2023 10:55:35 +0200 Subject: [PATCH 602/756] fix: yq format --- build/ci/createJobFromRollout.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/ci/createJobFromRollout.sh b/build/ci/createJobFromRollout.sh index c56baf1d25..c78551aae0 100755 --- a/build/ci/createJobFromRollout.sh +++ b/build/ci/createJobFromRollout.sh @@ -23,7 +23,7 @@ EOF kubectl get rollout $DEPLOY_ENV-web -o yaml | yq '.spec.template.spec' > spec.yaml yq -i '.spec.template.spec += load("spec.yaml")' job.yaml -yq -i '.spec.template.spec.restartPolicy = Never' job.yaml +yq -i '.spec.template.spec.restartPolicy = "Never"' job.yaml yq -i '.spec.template.spec.containers[0].args = ["-c", "pip3 install pytest-django; pytest -v -m \"not deep and not failing\" ./sefaria; echo $? > /dev/stdout; exit 0;"]' job.yaml kubectl apply -f job.yaml From 0c17b44f43443249bd8696eccb2702f9e50e30c1 Mon Sep 17 00:00:00 2001 From: Brendan Galloway <brendan@flanksource.com> Date: Mon, 4 Dec 2023 13:16:59 +0200 Subject: [PATCH 603/756] fix: remove healthchecks from job pod --- build/ci/createJobFromRollout.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build/ci/createJobFromRollout.sh b/build/ci/createJobFromRollout.sh index c78551aae0..0bb8fafee8 100755 --- a/build/ci/createJobFromRollout.sh +++ b/build/ci/createJobFromRollout.sh @@ -25,5 +25,8 @@ kubectl get rollout $DEPLOY_ENV-web -o yaml | yq '.spec.template.spec' > spec.ya yq -i '.spec.template.spec += load("spec.yaml")' job.yaml yq -i '.spec.template.spec.restartPolicy = "Never"' job.yaml yq -i '.spec.template.spec.containers[0].args = ["-c", "pip3 install pytest-django; pytest -v -m \"not deep and not failing\" ./sefaria; echo $? > /dev/stdout; exit 0;"]' job.yaml +yq -i 'del(.spec.template.spec.containers[0].startupProbe)' job.yaml +yq -i 'del(.spec.template.spec.containers[0].livenessProbe)' job.yaml +yq -i 'del(.spec.template.spec.containers[0].readinessProbe)' job.yaml kubectl apply -f job.yaml From 78dc3554b6246182e12a644afce8f02930ec1d04 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Mon, 4 Dec 2023 19:21:54 +0200 Subject: [PATCH 604/756] fix(translations): rerender on any change (including resize) for getting ellipsis right. --- static/js/VersionBlockHeader.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/VersionBlockHeader.jsx b/static/js/VersionBlockHeader.jsx index 6bcc015464..2b4207d3ce 100644 --- a/static/js/VersionBlockHeader.jsx +++ b/static/js/VersionBlockHeader.jsx @@ -48,7 +48,7 @@ function VersionBlockHeaderText({link, onClick, text, direction}) { const computedStyles = window.getComputedStyle(element); const maxHeight = parseInt(computedStyles.getPropertyValue('max-height'), 10); setTruncationOccurred(element.scrollHeight > maxHeight); - }, []); //[] for running in resize seems better than adding a listener + }); //no second param for running in resize seems better than adding a listener function onEllipsisClick() { setShouldAttemptTruncation(false); setTruncationOccurred(false); From baee75ffb559741a363f78ff05351b3ee06e671e Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Mon, 4 Dec 2023 19:25:54 +0200 Subject: [PATCH 605/756] feat(translations): no underline when hovering the content of translation. --- static/css/s2.css | 1 + 1 file changed, 1 insertion(+) diff --git a/static/css/s2.css b/static/css/s2.css index 8d0b3ee447..303a959f12 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -3529,6 +3529,7 @@ display: none; .versionBlock .versionPreview { --line-height: 22px; line-height: var(--line-height); + text-decoration: none; } .versionBlock .versionPreview big { font-size: inherit; From 438e7c5703b7f1449747e0d0bb6681419f32fe91 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Mon, 4 Dec 2023 20:40:18 +0200 Subject: [PATCH 606/756] refactor(translations): use html details and summary rather than react useState. --- static/css/s2.css | 15 ++++--- static/js/VersionBlockWithPreview.jsx | 44 +++++++++---------- .../js/VersionBlockWithPreviewTitleLine.jsx | 9 ++-- 3 files changed, 34 insertions(+), 34 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index 303a959f12..c7b98351fd 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -3553,21 +3553,24 @@ display: none; font-size: 14px; line-height: 22px; color: var(--medium-grey); - margin-top: 10px; - margin-bottom: 10px; + margin-inline-start: 5px; } .version-with-preview-title-line .open-details { display: inline; margin-inline-end: 5px; font-style: italic; } -.version-with-preview-title-line .open-details.chevron-up::before { - content: url('/static/icons/little-chevron-up.svg'); +.versionBlock.with-preview summary { + margin-top: 10px; + margin-bottom: 10px; } -.version-with-preview-title-line .open-details.chevron-down::before { +.versionBlock.with-preview summary::marker { content: url('/static/icons/little-chevron-down.svg'); } -.version-with-preview-title-line .open-details::before { +[open] .versionBlock.with-preview summary::marker { + content: url('/static/icons/little-chevron-up.svg'); +} +details .open-details::before { margin-inline-end: 5px; } .version-with-preview-title-line .selectButton { diff --git a/static/js/VersionBlockWithPreview.jsx b/static/js/VersionBlockWithPreview.jsx index 953ae4c95e..bf2d8b2395 100644 --- a/static/js/VersionBlockWithPreview.jsx +++ b/static/js/VersionBlockWithPreview.jsx @@ -7,7 +7,6 @@ import VersionPreviewMeta from "./VersionPreviewMeta"; import {OpenConnectionTabButton} from "./TextList"; function VersionBlockWithPreview({currentRef, version, currObjectVersions, openVersionInSidebar, openVersionInReader, isSelected, srefs, onRangeClick}) { - const [isInfoOpen, setIsInfoOpen] = useState(false); const opeInSidebar = versionTools.openVersionInSidebar.bind(null, currentRef, version, currObjectVersions, openVersionInSidebar); function openInTabCallback(sref) { onRangeClick(sref, false, {[version.language]: version.versionTitle}); @@ -21,27 +20,28 @@ function VersionBlockWithPreview({currentRef, version, currObjectVersions, openV link={versionTools.makeVersionLink(currentRef, version, currObjectVersions, false)} direction={version.direction || 'ltr'} /> - <VersionBlockWithPreviewTitleLine - version={version} - currentRef={currentRef} - currObjectVersions={currObjectVersions} - openVersionInReader={openVersionInReader} - isInfoOpen={isInfoOpen} - setIsInfoOpen={setIsInfoOpen} - isSelected={isSelected} - /> - {isInfoOpen && - <div className='version-block-with-preview-details'> - <VersionPreviewMeta - currentRef={currentRef} - version={version} - /> - <OpenConnectionTabButton - srefs={srefs} - openInTabCallback={openInTabCallback} - renderMode='versionPreview' - /> - </div>} + <details> + <summary> + <VersionBlockWithPreviewTitleLine + version={version} + currentRef={currentRef} + currObjectVersions={currObjectVersions} + openVersionInReader={openVersionInReader} + isSelected={isSelected} + /> + </summary> + <div className='version-block-with-preview-details'> + <VersionPreviewMeta + currentRef={currentRef} + version={version} + /> + <OpenConnectionTabButton + srefs={srefs} + openInTabCallback={openInTabCallback} + renderMode='versionPreview' + /> + </div> + </details> </div> ); } diff --git a/static/js/VersionBlockWithPreviewTitleLine.jsx b/static/js/VersionBlockWithPreviewTitleLine.jsx index 09e1b56122..8c217310f7 100644 --- a/static/js/VersionBlockWithPreviewTitleLine.jsx +++ b/static/js/VersionBlockWithPreviewTitleLine.jsx @@ -4,7 +4,7 @@ import VersionBlockSelectButton from "./VersionBlockSelectButton"; import {versionTools} from './VersionBlock'; import Sefaria from "./sefaria/sefaria"; -function VersionBlockWithPreviewTitleLine({currentRef, version, currObjectVersions, openVersionInReader, isInfoOpen, setIsInfoOpen, isSelected}) { +function VersionBlockWithPreviewTitleLine({currentRef, version, currObjectVersions, openVersionInReader, isSelected}) { function makeShortVersionTitle() { let shortVersionTitle = version.shortVersionTitle || version.versionTitle; if (Sefaria.interfaceLang === "hebrew") { @@ -12,15 +12,14 @@ function VersionBlockWithPreviewTitleLine({currentRef, version, currObjectVersio } return shortVersionTitle; } - const chevronDirection = isInfoOpen ? 'up' : 'down'; const openVersionInMainPanel = versionTools.openVersionInMainPanel.bind(null, currentRef, version, currObjectVersions, 'select-button', null, openVersionInReader); const buttonText = isSelected ? 'Currently Selected' : 'Select'; return ( <div className='version-with-preview-title-line'> - <a className={`open-details chevron-${chevronDirection}`} onClick={() => setIsInfoOpen(!isInfoOpen)} href='#'> + <div className='open-details'> {makeShortVersionTitle()} - </a> + </div> <VersionBlockSelectButton isSelected={isSelected} openVersionInMainPanel={openVersionInMainPanel} @@ -35,8 +34,6 @@ VersionBlockWithPreviewTitleLine.prototypes = { version: PropTypes.object.isRequired, currentRef: PropTypes.string.isRequired, openVersionInReader: PropTypes.func.isRequired, - isInfoOpen: PropTypes.bool.isRequired, - setIsInfoOpen: PropTypes.func.isRequired, isSelected: PropTypes.bool.isRequired, }; export default VersionBlockWithPreviewTitleLine; From 08d78633c0f7b31e49d4d64500d33e872f63fe3a Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Mon, 4 Dec 2023 20:52:20 +0200 Subject: [PATCH 607/756] feat(translations): vertical space between blocks of 30px. --- static/css/s2.css | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index c7b98351fd..9658ec4e3a 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -3562,6 +3562,8 @@ display: none; } .versionBlock.with-preview summary { margin-top: 10px; +} +[open] .versionBlock.with-preview summary { margin-bottom: 10px; } .versionBlock.with-preview summary::marker { @@ -3602,11 +3604,12 @@ details .open-details::before { line-height: 29px; max-inline-size: max-content; } -.versionsBox .versionBlock { - padding: 20px 0; -} .versionsBox .versionBlock:not(.with-preview) { border-top: solid 1px #CCC; + padding: 20px 0; +} +.versionsBox .versionBlock.with-preview { + padding: 15px 0; } .language-block .versionLanguage { border-bottom: solid 1px #CCC; From 9089003f7cfc5eb4f144339830779cbb9262825b Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Tue, 5 Dec 2023 10:36:55 +0200 Subject: [PATCH 608/756] helm(search): change time of cronjob --- .../templates/cronjob/reindex-elasticsearch.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml b/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml index 23b30e2659..97ca61e379 100644 --- a/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml +++ b/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml @@ -7,7 +7,7 @@ metadata: labels: {{- include "sefaria.labels" . | nindent 4 }} spec: - schedule: "0 0 * * 2" + schedule: "20 13 * * 2" jobTemplate: spec: backoffLimit: 1 From 4b042fdb1c0be4770b11c423be5d25c6ef46c8ad Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Tue, 5 Dec 2023 11:12:44 +0200 Subject: [PATCH 609/756] helm(search): fix typo --- .../templates/cronjob/reindex-elasticsearch.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml b/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml index 97ca61e379..640fd4c4ab 100644 --- a/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml +++ b/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml @@ -33,7 +33,7 @@ spec: memory: 7Gi env: - name: SEARCH_HOST - value: "{{ .Values.cronjobs.reindexElasticSearch.SEARCH_HOST_ES8 }}" + value: "{{ .Values.cronJobs.reindexElasticSearch.SEARCH_HOST_ES8 }}" - name: REDIS_HOST value: "redis-{{ .Values.deployEnv }}" - name: NODEJS_HOST From 38dc6c263f14d2c086a671fd175f94db210bbd8b Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Tue, 5 Dec 2023 11:33:05 +0200 Subject: [PATCH 610/756] chore: switch 200 to 150 caption maximum --- static/js/TopicEditor.jsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/static/js/TopicEditor.jsx b/static/js/TopicEditor.jsx index 82d4d4f24a..a42c690d82 100644 --- a/static/js/TopicEditor.jsx +++ b/static/js/TopicEditor.jsx @@ -93,12 +93,12 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { alert(Sefaria._("Title must be provided.")); return false; } - if (data.enImgCaption.length > 200) { - alert("English caption is too long. It should not be more than 200 characters"); + if (data.enImgCaption.length > 150) { + alert("English caption is too long. It should not be more than 150 characters"); return false; } - if (data.heImgCaption.length > 200) { - alert("Hebrew caption is too long. It should not be more than 200 characters") + if (data.heImgCaption.length > 150) { + alert("Hebrew caption is too long. It should not be more than 150 characters") return false; } if (sortedSubtopics.length > 0 && !isNew) { From 75fda5cfc5581155f4e2a81101b6b2fca9ceaa07 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Tue, 5 Dec 2023 11:47:57 +0200 Subject: [PATCH 611/756] helm(search): fix typo --- .../templates/cronjob/reindex-elasticsearch-es6.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch-es6.yaml b/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch-es6.yaml index 9d15f9fb38..9345a644fb 100644 --- a/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch-es6.yaml +++ b/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch-es6.yaml @@ -33,7 +33,7 @@ spec: memory: 7Gi env: - name: SEARCH_HOST - value: "{{ .Values.cronjobs.reindexElasticSearch.SEARCH_HOST_ES6 }}" + value: "{{ .Values.cronJobs.reindexElasticSearch.SEARCH_HOST_ES6 }}" - name: REDIS_HOST value: "redis-{{ .Values.deployEnv }}" - name: NODEJS_HOST From 0df8aaaff549bf49c746faca43aa2247e2d197b0 Mon Sep 17 00:00:00 2001 From: Ephraim <ephraim@sefaria.org> Date: Tue, 5 Dec 2023 13:01:42 +0200 Subject: [PATCH 612/756] test: Remove expected failure on `test_check_first()` This tests appears to be passing and its theorized that recent code changes made it pass, when before we expected failure of this scenario. --- sefaria/model/tests/ref_catching_test.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sefaria/model/tests/ref_catching_test.py b/sefaria/model/tests/ref_catching_test.py index 900d8c7c49..970cd91996 100644 --- a/sefaria/model/tests/ref_catching_test.py +++ b/sefaria/model/tests/ref_catching_test.py @@ -97,6 +97,5 @@ def test_regex_string_he_in_parentheses_3(self): assert In('<p>[שיר השירים א ירושלמי כתובות (דף כח:) בשורות א]')\ .looking_for('שיר השירים').with_parenthesis().finds("Song of Songs 1") - @pytest.mark.xfail(reason="Linker doesn't know that it should look for either Mishnah or Talmud. This is as opposed to Ref instantiation where this ref would parse correctly because it would find the check_first field on the index.") def test_check_first(self): assert In('בבא מציעא פ"ד מ"ו, ועיין לעיל').looking_for('בבא מציעא').finds("Mishnah Bava Metzia 4:6") From 59bf1dd27a41f998aecb4ff28ac0b2594ad2ef07 Mon Sep 17 00:00:00 2001 From: Ephraim <ephraim@sefaria.org> Date: Tue, 5 Dec 2023 13:06:45 +0200 Subject: [PATCH 613/756] test: Refactor tests for mismatched actualLanguage saving The previous code was fixed to cascade the actual language in brackets in the version title to supercede any actualLanguage data set before. --- sefaria/model/tests/text_test.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sefaria/model/tests/text_test.py b/sefaria/model/tests/text_test.py index 6609c17354..8e3fa74243 100644 --- a/sefaria/model/tests/text_test.py +++ b/sefaria/model/tests/text_test.py @@ -768,7 +768,7 @@ def setup_class(cls): ) cls.versionWithoutTranslation.chapter = [['1'], ['2'], ["original text", "2nd"]] cls.versionWithoutTranslation.save() - cls.versionThatWillBreak = model.Version( + cls.versionWithLangCodeMismatch = model.Version( { "chapter": cls.myIndex.nodes.create_skeleton(), "versionTitle": "Version 1 TEST [ar]", @@ -781,7 +781,7 @@ def setup_class(cls): @classmethod def teardown_class(cls): - for c in [cls.myIndex, cls.versionWithTranslation, cls.versionWithoutTranslation, cls.versionThatWillBreak]: + for c in [cls.myIndex, cls.versionWithTranslation, cls.versionWithoutTranslation, cls.versionWithLangCodeMismatch]: try: c.delete() except Exception: @@ -793,6 +793,6 @@ def test_normalizes_actualLanguage_from_brackets(self): def test_normalizes_language_from_language(self): assert self.versionWithoutTranslation.actualLanguage == "he" - def test_fails_validation_when_language_mismatch(self): - with pytest.raises(InputError, match='Version actualLanguage does not match bracketed language'): - self.versionThatWillBreak.save() \ No newline at end of file + def test_save_when_language_mismatch(self): + self.versionWithLangCodeMismatch.save() + assert self.versionWithLangCodeMismatch.actualLanguage == "ar" \ No newline at end of file From 478ea6d9370c8c4169fb3a761a622712f8a47c16 Mon Sep 17 00:00:00 2001 From: Ephraim <ephraim@sefaria.org> Date: Tue, 5 Dec 2023 15:33:36 +0200 Subject: [PATCH 614/756] Revert "ci: Temporarily disable sandbox uninstall" This reverts commit da7f921bd85673055aebebd48a64ffcf45b4adc7. --- .github/workflows/continuous.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/continuous.yaml b/.github/workflows/continuous.yaml index e0a71c824a..4c9dfd788f 100644 --- a/.github/workflows/continuous.yaml +++ b/.github/workflows/continuous.yaml @@ -383,5 +383,5 @@ jobs: NAMESPACE: ${{ secrets.DEV_SANDBOX_NAMESPACE }} NAME: sandbox-${{ steps.get-sha.outputs.sha_short }} - name: Uninstall - run: echo "helm delete sandbox-${{ steps.get-sha.outputs.sha_short }} -n ${{ secrets.DEV_SANDBOX_NAMESPACE }} --debug --timeout 10m0s" + run: helm delete sandbox-${{ steps.get-sha.outputs.sha_short }} -n ${{ secrets.DEV_SANDBOX_NAMESPACE }} --debug --timeout 10m0s if: steps.get-helm.outputs.count > 0 From db1c693cd4406e1e4ebfd554c4e514d58bca9db8 Mon Sep 17 00:00:00 2001 From: Ephraim <ephraim@sefaria.org> Date: Tue, 5 Dec 2023 17:44:28 +0200 Subject: [PATCH 615/756] ci: Remove old and now deprecated pyTestPod.yaml It has been superceded by createJobFromRollout for the purpose of running pytests --- build/ci/pyTestPod.yaml | 103 ---------------------------------------- 1 file changed, 103 deletions(-) delete mode 100644 build/ci/pyTestPod.yaml diff --git a/build/ci/pyTestPod.yaml b/build/ci/pyTestPod.yaml deleted file mode 100644 index d618a265e3..0000000000 --- a/build/ci/pyTestPod.yaml +++ /dev/null @@ -1,103 +0,0 @@ ---- -apiVersion: batch/v1 -kind: Job -metadata: - labels: - ci-run: "${GITHUB_RUN_ID}" - test-name: pytest - name: "${DEPLOY_ENV}-pytest-sandbox-${GITHUB_RUN_ID}" -spec: - backoffLimit: 2 # in waitForCIJob, we look for 2 fails before declaring failure. This could be made a variable. - template: - metadata: - labels: - ci-run: "${GITHUB_RUN_ID}" - test-name: pytest - spec: - restartPolicy: Never - containers: - - name: web - image: "${WEB_IMAGE_NAME}:${WEB_IMAGE_TAG}" - imagePullPolicy: Always - command: ["bash"] - args: ["-c", "pip3 install pytest-django; pytest -v -m \"not deep and not failing\" ./sefaria; echo $? > /dev/stdout; exit 0;"] - env: - # WEB_CONCURRENCY is used for determining the number of server workers - - name: GOOGLE_APPLICATION_CREDENTIALS - value: /app/logging-secret.json - - name: ENV_NAME - value: "${DEPLOY_ENV}" - - name: STACK_COMPONENT - value: web - - name: REDIS_HOST - value: "redis-${DEPLOY_ENV}" - - name: NODEJS_HOST - value: "node-${DEPLOY_ENV}" - - name: VARNISH_HOST - value: "varnish-${DEPLOY_ENV}" - envFrom: - - secretRef: - name: local-settings-secrets - optional: true - - configMapRef: - name: "local-settings-${DEPLOY_ENV}" - ports: - - containerPort: 80 - protocol: TCP - resources: - requests: - memory: "3Gi" - cpu: "500m" - limits: - memory: "3Gi" - cpu: "1000m" - volumeMounts: - - mountPath: /app/sefaria/local_settings.py - name: local-settings - subPath: local_settings.py - readOnly: true - - mountPath: /varnish-secret - name: varnish-secret - readOnly: true - - mountPath: /school-lookup-data - name: school-lookup-data - readOnly: true - - mountPath: /client-secret - name: client-secret - readOnly: true - - mountPath: /google-cloud-secret - name: backup-manager-secret - readOnly: true - - mountPath: /app/logging-secret.json - name: logging-secret - subPath: logging-secret.json - readOnly: true - - name: gunicorn-config - mountPath: /app/gunicorn.conf.py - subPath: gunicorn.conf.py - readOnly: true - volumes: - - name: local-settings - configMap: - name: "local-settings-file-${DEPLOY_ENV}" - items: - - key: local_settings.py - path: local_settings.py - - name: client-secret - secret: - secretName: google-client-secret - - name: backup-manager-secret # used to access google cloud - secret: - secretName: backup-manager - - name: logging-secret - secret: - secretName: logging-secret - - name: varnish-secret - secret: - secretName: varnish-secret-helm - - name: school-lookup-data - secret: - secretName: school-lookup-data - - name: gunicorn-config - configMap: - name: "gunicorn-config-${DEPLOY_ENV}" From 98b802df53f2e50fec7fcdc9cee1f594f71d4cb5 Mon Sep 17 00:00:00 2001 From: Ephraim <ephraim@sefaria.org> Date: Tue, 5 Dec 2023 17:44:37 +0200 Subject: [PATCH 616/756] Revert "chore(tests): remain tests db after done." This reverts commit e5f8a3d1ad69d88afe47045cd0c787fe06abc8c4. --- build/ci/sandbox-values.yaml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/build/ci/sandbox-values.yaml b/build/ci/sandbox-values.yaml index 40466aa962..12dcce6766 100644 --- a/build/ci/sandbox-values.yaml +++ b/build/ci/sandbox-values.yaml @@ -1,8 +1,6 @@ sandbox: "true" contentSandbox: "true" -restore: - cleanup: false -deployEnv: +deployEnv: previousServicesCount: "1" web: containerImage: From 4cddbaac2f823d742cc5a89710dcc6f36105af5c Mon Sep 17 00:00:00 2001 From: Ephraim <ephraim@sefaria.org> Date: Tue, 5 Dec 2023 19:10:13 +0200 Subject: [PATCH 617/756] helm(feat): Add cronjob for Trend calculation --- build/ci/production-values.yaml | 2 + .../templates/cronjob/trends.yaml | 56 +++++++++++++++++++ helm-chart/sefaria-project/values.yaml | 2 + .../recurring-scheduled/recalculate-trends.py | 9 +++ 4 files changed, 69 insertions(+) create mode 100644 helm-chart/sefaria-project/templates/cronjob/trends.yaml create mode 100644 scripts/recurring-scheduled/recalculate-trends.py diff --git a/build/ci/production-values.yaml b/build/ci/production-values.yaml index bbf9b243d0..5f165ac2e0 100644 --- a/build/ci/production-values.yaml +++ b/build/ci/production-values.yaml @@ -185,6 +185,8 @@ cronJobs: enabled: true trello: enabled: true + trends: + enabled: false weeklyEmailNotifications: enabled: true secrets: diff --git a/helm-chart/sefaria-project/templates/cronjob/trends.yaml b/helm-chart/sefaria-project/templates/cronjob/trends.yaml new file mode 100644 index 0000000000..769f6c48e3 --- /dev/null +++ b/helm-chart/sefaria-project/templates/cronjob/trends.yaml @@ -0,0 +1,56 @@ +{{- if .Values.cronJobs.trends.enabled }} +--- +apiVersion: batch/v1 +kind: CronJob +metadata: + name: {{ .Values.deployEnv }}-trends + labels: + {{- include "sefaria.labels" . | nindent 4 }} +spec: + schedule: "0 1 * * 6" + concurrencyPolicy: Forbid + jobTemplate: + spec: + backoffLimit: 1 + template: + spec: + volumes: + - name: local-settings + configMap: + name: local-settings-file-{{ .Values.deployEnv }} + items: + - key: local_settings.py + path: local_settings.py + containers: + - name: trends + image: "{{ .Values.web.containerImage.imageRegistry }}:{{ .Values.web.containerImage.tag }}" + env: + - name: REDIS_HOST + value: "redis-{{ .Values.deployEnv }}" + - name: NODEJS_HOST + value: "node-{{ .Values.deployEnv }}-{{ .Release.Revision }}" + - name: VARNISH_HOST + value: "varnish-{{ .Values.deployEnv }}-{{ .Release.Revision }}" + envFrom: + - secretRef: + name: {{ .Values.secrets.localSettings.ref }} + optional: true + - secretRef: + name: local-settings-secrets-{{ .Values.deployEnv }} + optional: true + - configMapRef: + name: local-settings-{{ .Values.deployEnv }} + volumeMounts: + - mountPath: /app/sefaria/local_settings.py + name: local-settings + subPath: local_settings.py + readOnly: true + command: ["bash"] + args: [ + "-c", + "/app/run /app/scripts/recurring-scheduled/recalculate-trends.py" + ] + restartPolicy: OnFailure + successfulJobsHistoryLimit: 1 + failedJobsHistoryLimit: 2 +{{- end }} diff --git a/helm-chart/sefaria-project/values.yaml b/helm-chart/sefaria-project/values.yaml index 72722c98b7..e67f71eb8f 100644 --- a/helm-chart/sefaria-project/values.yaml +++ b/helm-chart/sefaria-project/values.yaml @@ -405,6 +405,8 @@ cronJobs: enabled: false trello: enabled: false + trends: + enabled: false weeklyEmailNotifications: enabled: false diff --git a/scripts/recurring-scheduled/recalculate-trends.py b/scripts/recurring-scheduled/recalculate-trends.py new file mode 100644 index 0000000000..953840503d --- /dev/null +++ b/scripts/recurring-scheduled/recalculate-trends.py @@ -0,0 +1,9 @@ +import django +django.setup() +from sefaria.model import * + +trend.setAllTrends() + + + + From eda8bbc0adf978d74afd7dddb3c21f77a611e328 Mon Sep 17 00:00:00 2001 From: Ephraim <ephraim@sefaria.org> Date: Tue, 5 Dec 2023 19:15:39 +0200 Subject: [PATCH 618/756] chore: Rename directory and script name to conform to the rest of the codebase --- helm-chart/sefaria-project/templates/cronjob/trends.yaml | 2 +- .../recalculate-trends.py => scheduled/recalculate_trends.py} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename scripts/{recurring-scheduled/recalculate-trends.py => scheduled/recalculate_trends.py} (100%) diff --git a/helm-chart/sefaria-project/templates/cronjob/trends.yaml b/helm-chart/sefaria-project/templates/cronjob/trends.yaml index 769f6c48e3..47fc9c2b91 100644 --- a/helm-chart/sefaria-project/templates/cronjob/trends.yaml +++ b/helm-chart/sefaria-project/templates/cronjob/trends.yaml @@ -48,7 +48,7 @@ spec: command: ["bash"] args: [ "-c", - "/app/run /app/scripts/recurring-scheduled/recalculate-trends.py" + "/app/run /app/scripts/scheduled/recalculate_trends.py" ] restartPolicy: OnFailure successfulJobsHistoryLimit: 1 diff --git a/scripts/recurring-scheduled/recalculate-trends.py b/scripts/scheduled/recalculate_trends.py similarity index 100% rename from scripts/recurring-scheduled/recalculate-trends.py rename to scripts/scheduled/recalculate_trends.py From b44595196193465f9712d96463c287fcd4147a27 Mon Sep 17 00:00:00 2001 From: Ephraim <ephraim@sefaria.org> Date: Tue, 5 Dec 2023 19:32:45 +0200 Subject: [PATCH 619/756] chore: move all cronjob related scripts to their own directory --- .../templates/cronjob/daily-email-notifications.yaml | 2 +- .../sefaria-project/templates/cronjob/index-from-queue.yaml | 2 +- helm-chart/sefaria-project/templates/cronjob/metrics.yaml | 2 +- .../sefaria-project/templates/cronjob/nation-builder-sync.yaml | 2 +- .../templates/cronjob/rambi-webpages-weekly.yaml | 2 +- .../templates/cronjob/regenerate-long-cached-data.yaml | 2 +- .../templates/cronjob/reindex-elasticsearch-es6.yaml | 2 +- .../templates/cronjob/reindex-elasticsearch.yaml | 3 ++- helm-chart/sefaria-project/templates/cronjob/sitemaps.yaml | 2 +- .../sefaria-project/templates/cronjob/topics-indexing.yaml | 2 +- helm-chart/sefaria-project/templates/cronjob/trello.yaml | 2 +- .../templates/cronjob/weekly-email-notifications.yaml | 2 +- scripts/{ => scheduled}/generate_sitemaps.py | 0 scripts/{ => scheduled}/index_from_queue.py | 0 scripts/{ => scheduled}/metrics.py | 0 scripts/{ => scheduled}/nation_builder_sync.py | 0 scripts/{ => scheduled}/parse_rambi_webpages.py | 0 scripts/{ => scheduled}/recalculate_secondary_topic_data.py | 0 scripts/{ => scheduled}/regenerate_long_cached_data.py | 0 scripts/{ => scheduled}/reindex_elasticsearch_cronjob.py | 0 scripts/{ => scheduled}/reindex_elasticsearch_cronjob_ES6.py | 0 scripts/{ => scheduled}/send_email_notifications.py | 0 scripts/{ => scheduled}/webpages_cronjob.py | 0 23 files changed, 13 insertions(+), 12 deletions(-) rename scripts/{ => scheduled}/generate_sitemaps.py (100%) rename scripts/{ => scheduled}/index_from_queue.py (100%) rename scripts/{ => scheduled}/metrics.py (100%) rename scripts/{ => scheduled}/nation_builder_sync.py (100%) rename scripts/{ => scheduled}/parse_rambi_webpages.py (100%) rename scripts/{ => scheduled}/recalculate_secondary_topic_data.py (100%) rename scripts/{ => scheduled}/regenerate_long_cached_data.py (100%) rename scripts/{ => scheduled}/reindex_elasticsearch_cronjob.py (100%) rename scripts/{ => scheduled}/reindex_elasticsearch_cronjob_ES6.py (100%) rename scripts/{ => scheduled}/send_email_notifications.py (100%) rename scripts/{ => scheduled}/webpages_cronjob.py (100%) diff --git a/helm-chart/sefaria-project/templates/cronjob/daily-email-notifications.yaml b/helm-chart/sefaria-project/templates/cronjob/daily-email-notifications.yaml index d7a7016c4f..ed4d06abcb 100644 --- a/helm-chart/sefaria-project/templates/cronjob/daily-email-notifications.yaml +++ b/helm-chart/sefaria-project/templates/cronjob/daily-email-notifications.yaml @@ -40,7 +40,7 @@ spec: command: ["bash"] args: [ "-c", - "/app/run /app/scripts/send_email_notifications.py daily" + "/app/run /app/scripts/scheduled/send_email_notifications.py daily" ] restartPolicy: Never volumes: diff --git a/helm-chart/sefaria-project/templates/cronjob/index-from-queue.yaml b/helm-chart/sefaria-project/templates/cronjob/index-from-queue.yaml index 227bf78520..4d147093fc 100644 --- a/helm-chart/sefaria-project/templates/cronjob/index-from-queue.yaml +++ b/helm-chart/sefaria-project/templates/cronjob/index-from-queue.yaml @@ -51,7 +51,7 @@ spec: command: ["bash"] args: [ "-c", - "/app/run /app/scripts/index_from_queue.py" + "/app/run /app/scripts/scheduled/index_from_queue.py" ] resources: limits: diff --git a/helm-chart/sefaria-project/templates/cronjob/metrics.yaml b/helm-chart/sefaria-project/templates/cronjob/metrics.yaml index 9b30aee20e..10e19f3103 100644 --- a/helm-chart/sefaria-project/templates/cronjob/metrics.yaml +++ b/helm-chart/sefaria-project/templates/cronjob/metrics.yaml @@ -40,7 +40,7 @@ spec: command: ["bash"] args: [ "-c", - "/app/run /app/scripts/metrics.py" + "/app/run /app/scripts/scheduled/metrics.py" ] restartPolicy: OnFailure volumes: diff --git a/helm-chart/sefaria-project/templates/cronjob/nation-builder-sync.yaml b/helm-chart/sefaria-project/templates/cronjob/nation-builder-sync.yaml index a58551879a..c119ab6d36 100644 --- a/helm-chart/sefaria-project/templates/cronjob/nation-builder-sync.yaml +++ b/helm-chart/sefaria-project/templates/cronjob/nation-builder-sync.yaml @@ -47,7 +47,7 @@ spec: command: ["bash"] args: [ "-c", - "/app/run /app/scripts/nation_builder_sync.py --sustainers-only" + "/app/run /app/scripts/scheduled/nation_builder_sync.py --sustainers-only" ] restartPolicy: OnFailure successfulJobsHistoryLimit: 1 diff --git a/helm-chart/sefaria-project/templates/cronjob/rambi-webpages-weekly.yaml b/helm-chart/sefaria-project/templates/cronjob/rambi-webpages-weekly.yaml index 31038a4c07..a3f67ef4cb 100644 --- a/helm-chart/sefaria-project/templates/cronjob/rambi-webpages-weekly.yaml +++ b/helm-chart/sefaria-project/templates/cronjob/rambi-webpages-weekly.yaml @@ -51,7 +51,7 @@ spec: command: ["bash"] args: [ "-c", - "pip install pymarc==4.2.2 && /app/run /app/scripts/parse_rambi_webpages.py" + "pip install pymarc==4.2.2 && /app/run /app/scripts/scheduled/parse_rambi_webpages.py" ] resources: requests: diff --git a/helm-chart/sefaria-project/templates/cronjob/regenerate-long-cached-data.yaml b/helm-chart/sefaria-project/templates/cronjob/regenerate-long-cached-data.yaml index ae935a86bc..262d9a3bfe 100644 --- a/helm-chart/sefaria-project/templates/cronjob/regenerate-long-cached-data.yaml +++ b/helm-chart/sefaria-project/templates/cronjob/regenerate-long-cached-data.yaml @@ -40,7 +40,7 @@ spec: command: ["bash"] args: [ "-c", - "/app/run /app/scripts/regenerate_long_cached_data.py --all" + "/app/run /app/scripts/scheduled/regenerate_long_cached_data.py --all" ] restartPolicy: OnFailure volumes: diff --git a/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch-es6.yaml b/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch-es6.yaml index 9345a644fb..c35cb324cc 100644 --- a/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch-es6.yaml +++ b/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch-es6.yaml @@ -62,7 +62,7 @@ spec: command: ["bash"] args: [ "-c", - "mkdir -p /log && touch /log/sefaria_book_errors.log && pip install numpy && /app/run /app/scripts/reindex_elasticsearch_cronjob_ES6.py" + "mkdir -p /log && touch /log/sefaria_book_errors.log && pip install numpy && /app/run /app/scripts/scheduled/reindex_elasticsearch_cronjob_ES6.py" ] restartPolicy: Never volumes: diff --git a/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml b/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml index 640fd4c4ab..7aab052a47 100644 --- a/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml +++ b/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml @@ -64,7 +64,8 @@ spec: command: ["bash"] args: [ "-c", - "mkdir -p /log && touch /log/sefaria_book_errors.log && pip install numpy elasticsearch==8.8.2 git+https://github.com/Sefaria/elasticsearch-dsl-py@v8.0.0#egg=elasticsearch-dsl && /app/run /app/scripts/reindex_elasticsearch_cronjob.py" + "mkdir -p /log && touch /log/sefaria_book_errors.log && pip install numpy elasticsearch==8.8.2 git+https://github.com/Sefaria/elasticsearch-dsl-py@v8.0 + .0#egg=elasticsearch-dsl && /app/run /app/scripts/scheduled/reindex_elasticsearch_cronjob.py" ] restartPolicy: Never volumes: diff --git a/helm-chart/sefaria-project/templates/cronjob/sitemaps.yaml b/helm-chart/sefaria-project/templates/cronjob/sitemaps.yaml index c279ec87ad..084a5f185d 100644 --- a/helm-chart/sefaria-project/templates/cronjob/sitemaps.yaml +++ b/helm-chart/sefaria-project/templates/cronjob/sitemaps.yaml @@ -50,7 +50,7 @@ spec: subPath: local_settings.py readOnly: true command: ["bash"] - args: ["-c", "/app/run /app/scripts/generate_sitemaps.py -o /storage/"] + args: ["-c", "/app/run /app/scripts/scheduled/generate_sitemaps.py -o /storage/"] containers: - name: file-uploader image: google/cloud-sdk diff --git a/helm-chart/sefaria-project/templates/cronjob/topics-indexing.yaml b/helm-chart/sefaria-project/templates/cronjob/topics-indexing.yaml index b905f8b3e6..049f3aa7b1 100644 --- a/helm-chart/sefaria-project/templates/cronjob/topics-indexing.yaml +++ b/helm-chart/sefaria-project/templates/cronjob/topics-indexing.yaml @@ -18,7 +18,7 @@ spec: containers: - name: topics-indexer image: "{{ .Values.web.containerImage.imageRegistry }}:{{ .Values.web.containerImage.tag }}" - args: [ "yes | pip3 install numpy && touch /log/sefaria_book_errors.log && python3 /app/scripts/recalculate_secondary_topic_data.py" ] + args: [ "yes | pip3 install numpy && touch /log/sefaria_book_errors.log && python3 /app/scripts/scheduled/recalculate_secondary_topic_data.py" ] env: - name: REDIS_HOST value: "redis-{{ .Values.deployEnv }}" diff --git a/helm-chart/sefaria-project/templates/cronjob/trello.yaml b/helm-chart/sefaria-project/templates/cronjob/trello.yaml index 220a4821ce..5b3f6c9ad9 100644 --- a/helm-chart/sefaria-project/templates/cronjob/trello.yaml +++ b/helm-chart/sefaria-project/templates/cronjob/trello.yaml @@ -58,7 +58,7 @@ spec: command: ["bash"] args: [ "-c", - "/app/run /app/scripts/webpages_cronjob.py" + "/app/run /app/scripts/scheduled/webpages_cronjob.py" ] resources: limits: diff --git a/helm-chart/sefaria-project/templates/cronjob/weekly-email-notifications.yaml b/helm-chart/sefaria-project/templates/cronjob/weekly-email-notifications.yaml index 8cc6bcf853..eb407fbd49 100644 --- a/helm-chart/sefaria-project/templates/cronjob/weekly-email-notifications.yaml +++ b/helm-chart/sefaria-project/templates/cronjob/weekly-email-notifications.yaml @@ -40,7 +40,7 @@ spec: command: ["bash"] args: [ "-c", - "/app/run /app/scripts/send_email_notifications.py weekly" + "/app/run /app/scripts/scheduled/send_email_notifications.py weekly" ] restartPolicy: Never volumes: diff --git a/scripts/generate_sitemaps.py b/scripts/scheduled/generate_sitemaps.py similarity index 100% rename from scripts/generate_sitemaps.py rename to scripts/scheduled/generate_sitemaps.py diff --git a/scripts/index_from_queue.py b/scripts/scheduled/index_from_queue.py similarity index 100% rename from scripts/index_from_queue.py rename to scripts/scheduled/index_from_queue.py diff --git a/scripts/metrics.py b/scripts/scheduled/metrics.py similarity index 100% rename from scripts/metrics.py rename to scripts/scheduled/metrics.py diff --git a/scripts/nation_builder_sync.py b/scripts/scheduled/nation_builder_sync.py similarity index 100% rename from scripts/nation_builder_sync.py rename to scripts/scheduled/nation_builder_sync.py diff --git a/scripts/parse_rambi_webpages.py b/scripts/scheduled/parse_rambi_webpages.py similarity index 100% rename from scripts/parse_rambi_webpages.py rename to scripts/scheduled/parse_rambi_webpages.py diff --git a/scripts/recalculate_secondary_topic_data.py b/scripts/scheduled/recalculate_secondary_topic_data.py similarity index 100% rename from scripts/recalculate_secondary_topic_data.py rename to scripts/scheduled/recalculate_secondary_topic_data.py diff --git a/scripts/regenerate_long_cached_data.py b/scripts/scheduled/regenerate_long_cached_data.py similarity index 100% rename from scripts/regenerate_long_cached_data.py rename to scripts/scheduled/regenerate_long_cached_data.py diff --git a/scripts/reindex_elasticsearch_cronjob.py b/scripts/scheduled/reindex_elasticsearch_cronjob.py similarity index 100% rename from scripts/reindex_elasticsearch_cronjob.py rename to scripts/scheduled/reindex_elasticsearch_cronjob.py diff --git a/scripts/reindex_elasticsearch_cronjob_ES6.py b/scripts/scheduled/reindex_elasticsearch_cronjob_ES6.py similarity index 100% rename from scripts/reindex_elasticsearch_cronjob_ES6.py rename to scripts/scheduled/reindex_elasticsearch_cronjob_ES6.py diff --git a/scripts/send_email_notifications.py b/scripts/scheduled/send_email_notifications.py similarity index 100% rename from scripts/send_email_notifications.py rename to scripts/scheduled/send_email_notifications.py diff --git a/scripts/webpages_cronjob.py b/scripts/scheduled/webpages_cronjob.py similarity index 100% rename from scripts/webpages_cronjob.py rename to scripts/scheduled/webpages_cronjob.py From 93fea2a02063ab0476807346b0d43708b1291c61 Mon Sep 17 00:00:00 2001 From: Ephraim <ephraim@sefaria.org> Date: Tue, 5 Dec 2023 20:46:16 +0200 Subject: [PATCH 620/756] fix: Actually enable new cronjob on prod --- build/ci/production-values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/ci/production-values.yaml b/build/ci/production-values.yaml index 5f165ac2e0..386c23eea9 100644 --- a/build/ci/production-values.yaml +++ b/build/ci/production-values.yaml @@ -186,7 +186,7 @@ cronJobs: trello: enabled: true trends: - enabled: false + enabled: true weeklyEmailNotifications: enabled: true secrets: From be2e5eb8b52186819bfb68071b3710d43e8292a2 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 6 Dec 2023 09:15:21 +0200 Subject: [PATCH 621/756] chore(search): upgrade client to ES 8 --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index df83dfb6ee..0bd6dda6f8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -22,8 +22,8 @@ django==1.11.* djangorestframework @ https://github.com/encode/django-rest-framework/archive/3.11.1.tar.gz djangorestframework_simplejwt==3.3.0 PyJWT==1.7.1 # pinned b/c current version 2.0.0 breaks simplejwt. waiting for 2.0.1 -elasticsearch==7.9.1 -elasticsearch_dsl==7.3.0 +elasticsearch==8.8.2 +git+https://github.com/Sefaria/elasticsearch-dsl-py@v8.0.0#egg=elasticsearch-dsl geojson==2.5.0 geopy==2.3.0 gevent==20.12.0; sys_platform != 'darwin' From ebd8ce6e82cb18c6dcaf9feabb8c89e0d6962647 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 6 Dec 2023 09:16:13 +0200 Subject: [PATCH 622/756] chore(search): remove pip install from cronjob since it's now in requirements.txt --- .../templates/cronjob/reindex-elasticsearch.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml b/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml index 640fd4c4ab..58a83e2126 100644 --- a/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml +++ b/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml @@ -64,7 +64,7 @@ spec: command: ["bash"] args: [ "-c", - "mkdir -p /log && touch /log/sefaria_book_errors.log && pip install numpy elasticsearch==8.8.2 git+https://github.com/Sefaria/elasticsearch-dsl-py@v8.0.0#egg=elasticsearch-dsl && /app/run /app/scripts/reindex_elasticsearch_cronjob.py" + "mkdir -p /log && touch /log/sefaria_book_errors.log && pip install numpy && /app/run /app/scripts/reindex_elasticsearch_cronjob.py" ] restartPolicy: Never volumes: From dffd1c2864bfbdab1d60cc0e04216cd9f4ae9479 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 6 Dec 2023 09:17:13 +0200 Subject: [PATCH 623/756] chore(search): client should explicitly query ES 8 --- static/js/sefaria/search.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/sefaria/search.js b/static/js/sefaria/search.js index 15716e67d9..6506574bed 100644 --- a/static/js/sefaria/search.js +++ b/static/js/sefaria/search.js @@ -39,7 +39,7 @@ class Search { } wrapper.addQuery($.ajax({ - url: `${Sefaria.apiHost}/api/search-wrapper`, + url: `${Sefaria.apiHost}/api/search-wrapper/es8`, type: 'POST', data: jsonData, contentType: "application/json; charset=utf-8", From 4ae2c10c52b7072f28237fb58523bedd921b422d Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 6 Dec 2023 09:19:09 +0200 Subject: [PATCH 624/756] chore(search): remove compatibility code in JS --- static/js/SearchResultList.jsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/static/js/SearchResultList.jsx b/static/js/SearchResultList.jsx index b09e91b170..51ed5bf099 100644 --- a/static/js/SearchResultList.jsx +++ b/static/js/SearchResultList.jsx @@ -100,11 +100,7 @@ class SearchTotal { function createSearchTotal(total) { - /** - * this function ensures backwards compatibility between the way elasticsearch formats the total pre-v8 and post-v8 - */ - const totalObj = typeof(total) === 'number' ? {value: total} : {value: total.value, relation: total.relation}; - return new SearchTotal(totalObj) + return new SearchTotal(total); } From c05388c451c4dfbef78c8b817d420879c2fa9cde Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 6 Dec 2023 09:21:06 +0200 Subject: [PATCH 625/756] fix(search): remove compatibility code --- reader/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reader/views.py b/reader/views.py index 8701e0dfad..83b51fb92c 100644 --- a/reader/views.py +++ b/reader/views.py @@ -4219,7 +4219,7 @@ def search_wrapper_api(request, es6_compat=False): search_obj = get_query_obj(search_obj=search_obj, **j) response = search_obj.execute() if response.success(): - response_json = getattr(response.to_dict(), 'body', response.to_dict()) + response_json = response.to_dict().body if es6_compat and isinstance(response_json['hits']['total'], dict): response_json['hits']['total'] = response_json['hits']['total']['value'] return jsonResponse(response_json, callback=request.GET.get("callback", None)) From 387d1e68013d50bd09a01b4fcd0cc92bca1b3b4e Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 6 Dec 2023 09:51:58 +0200 Subject: [PATCH 626/756] helm(search): switch to ES 8 SEARCH_HOST --- build/ci/production-values.yaml | 4 +- .../cronjob/reindex-elasticsearch-es6.yaml | 77 -- .../cronjob/reindex-elasticsearch.yaml | 2 +- scripts/reindex_elasticsearch_cronjob_ES6.py | 49 - sefaria/search_ES6.py | 844 ------------------ 5 files changed, 2 insertions(+), 974 deletions(-) delete mode 100644 helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch-es6.yaml delete mode 100644 scripts/reindex_elasticsearch_cronjob_ES6.py delete mode 100644 sefaria/search_ES6.py diff --git a/build/ci/production-values.yaml b/build/ci/production-values.yaml index bbf9b243d0..dbeda4badf 100644 --- a/build/ci/production-values.yaml +++ b/build/ci/production-values.yaml @@ -141,7 +141,7 @@ nginx: containerImage: imageRegistry: tag: - SEARCH_HOST: elasticsearch-data + SEARCH_HOST: elasticsearch-es-http.elasticsearch.svc disableScraping: false replicaCount: 2 resources: @@ -179,8 +179,6 @@ cronJobs: enabled: true reindexElasticSearch: enabled: true - SEARCH_HOST_ES6: "elasticsearch-data" - SEARCH_HOST_ES8: "elasticsearch-es-http.elasticsearch.svc" topicsIndexing: enabled: true trello: diff --git a/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch-es6.yaml b/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch-es6.yaml deleted file mode 100644 index 9345a644fb..0000000000 --- a/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch-es6.yaml +++ /dev/null @@ -1,77 +0,0 @@ -{{- if .Values.cronJobs.reindexElasticSearch.enabled }} ---- -apiVersion: batch/v1 -kind: CronJob -metadata: - name: {{ .Values.deployEnv }}-reindex-elastic-search-es6 - labels: - {{- include "sefaria.labels" . | nindent 4 }} -spec: - schedule: "20 13 * * 0" - jobTemplate: - spec: - backoffLimit: 1 - template: - spec: - affinity: - podAntiAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - - labelSelector: - matchExpressions: - - key: app - operator: In - values: - - mongo - topologyKey: kubernetes.io.hostname - containers: - - name: reindex-elastic-search-es6 - image: "{{ .Values.web.containerImage.imageRegistry }}:{{ .Values.web.containerImage.tag }}" - resources: - limits: - memory: 9Gi - requests: - memory: 7Gi - env: - - name: SEARCH_HOST - value: "{{ .Values.cronJobs.reindexElasticSearch.SEARCH_HOST_ES6 }}" - - name: REDIS_HOST - value: "redis-{{ .Values.deployEnv }}" - - name: NODEJS_HOST - value: "node-{{ .Values.deployEnv }}-{{ .Release.Revision }}" - - name: VARNISH_HOST - value: "varnish-{{ .Values.deployEnv }}-{{ .Release.Revision }}" - - name: SLACK_URL - valueFrom: - secretKeyRef: - name: {{ template "sefaria.secrets.slackWebhook" . }} - key: slack-webhook - envFrom: - - secretRef: - name: {{ .Values.secrets.localSettings.ref }} - optional: true - - configMapRef: - name: local-settings-{{ .Values.deployEnv }} - - secretRef: - name: local-settings-secrets-{{ .Values.deployEnv }} - optional: true - volumeMounts: - - mountPath: /app/sefaria/local_settings.py - name: local-settings - subPath: local_settings.py - readOnly: true - command: ["bash"] - args: [ - "-c", - "mkdir -p /log && touch /log/sefaria_book_errors.log && pip install numpy && /app/run /app/scripts/reindex_elasticsearch_cronjob_ES6.py" - ] - restartPolicy: Never - volumes: - - name: local-settings - configMap: - name: local-settings-file-{{ .Values.deployEnv }} - items: - - key: local_settings.py - path: local_settings.py - successfulJobsHistoryLimit: 1 - failedJobsHistoryLimit: 2 -{{- end }} diff --git a/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml b/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml index 58a83e2126..07d74ae80a 100644 --- a/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml +++ b/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml @@ -33,7 +33,7 @@ spec: memory: 7Gi env: - name: SEARCH_HOST - value: "{{ .Values.cronJobs.reindexElasticSearch.SEARCH_HOST_ES8 }}" + value: "{{ .Values.nginx.SEARCH_HOST }}" - name: REDIS_HOST value: "redis-{{ .Values.deployEnv }}" - name: NODEJS_HOST diff --git a/scripts/reindex_elasticsearch_cronjob_ES6.py b/scripts/reindex_elasticsearch_cronjob_ES6.py deleted file mode 100644 index 1a3f181eb2..0000000000 --- a/scripts/reindex_elasticsearch_cronjob_ES6.py +++ /dev/null @@ -1,49 +0,0 @@ -""" -This file is meant to be temporary while we are migrating to elasticsearch 8 -""" -from datetime import datetime -import requests -import traceback -import os -import django -django.setup() -from sefaria.model import * -from sefaria.search_ES6 import index_all -from sefaria.local_settings import SEFARIA_BOT_API_KEY -from sefaria.pagesheetrank import update_pagesheetrank - -""" -Source sheets added after last_sheet_timestamp will be missing from the index process. We want to manually index all -source sheets created after this. Depending on the database being used to index the timestamp will be different. If -running against a production database, last_sheet_timestamp will be the time this script began running. Otherwise, this -value will need to be set to the time at which the last mongo dump was created (assuming the database is using the most -up-to-date mongo dump). -""" -# last_sheet_timestamp = datetime.fromtimestamp(os.path.getmtime("/var/data/sefaria_public/dump/sefaria")).isoformat() -try: - last_sheet_timestamp = datetime.now().isoformat() - update_pagesheetrank() - index_all() - r = requests.post("https://www.sefaria.org/admin/index-sheets-by-timestamp", data={"timestamp": last_sheet_timestamp, "apikey": SEFARIA_BOT_API_KEY}) - if "error" in r.text: - raise Exception("Error when calling admin/index-sheets-by-timestamp API: " + r.text) - else: - print("SUCCESS!", r.text) -except Exception as e: - tb_str = traceback.format_exc() - print("Caught exception") - post_object = { - "icon_emoji": ":facepalm:", - "username": "Reindex ElasticSearch", - "channel": "#engineering-discuss", - "attachments": [ - { - "fallback": tb_str, - "color": "#a30200", - "pretext": "Cronjob Error", - "text": tb_str - } - ] - } - requests.post(os.environ['SLACK_URL'], json=post_object) - raise e diff --git a/sefaria/search_ES6.py b/sefaria/search_ES6.py deleted file mode 100644 index 812610eb07..0000000000 --- a/sefaria/search_ES6.py +++ /dev/null @@ -1,844 +0,0 @@ -# -*- coding: utf-8 -*- -""" -This file is meant to be temporary while we are migrating to elasticsearch 8 - -search.py - full-text search for Sefaria using ElasticSearch - -Writes to MongoDB Collection: index_queue -""" -import os -from datetime import datetime, timedelta -import re -import bleach -import pymongo - -# To allow these files to be run directly from command line (w/o Django shell) -os.environ['DJANGO_SETTINGS_MODULE'] = "settings" - -import structlog -import logging -from logging import NullHandler -from collections import defaultdict -import time as pytime -logger = structlog.get_logger(__name__) - -from elasticsearch import Elasticsearch -from elasticsearch.client import IndicesClient -from elasticsearch.helpers import bulk -from elasticsearch.exceptions import NotFoundError -from sefaria.model import * -from sefaria.model.text import AbstractIndex, AbstractTextRecord -from sefaria.model.user_profile import user_link, public_user_data -from sefaria.model.collection import CollectionSet -from sefaria.system.database import db -from sefaria.system.exceptions import InputError -from sefaria.utils.util import strip_tags -from .settings import SEARCH_URL, SEARCH_INDEX_NAME_TEXT, SEARCH_INDEX_NAME_SHEET, STATICFILES_DIRS -from sefaria.site.site_settings import SITE_SETTINGS -from sefaria.utils.hebrew import strip_cantillation -import sefaria.model.queue as qu - -es_client = Elasticsearch(SEARCH_URL) -index_client = IndicesClient(es_client) - -tracer = structlog.get_logger(__name__) -tracer.setLevel(logging.CRITICAL) -#tracer.addHandler(logging.FileHandler('/tmp/es_trace.log')) -tracer.addHandler(NullHandler()) - -doc_count = 0 - - -def delete_text(oref, version, lang): - try: - curr_index = get_new_and_current_index_names('text')['current'] - - id = make_text_doc_id(oref.normal(), version, lang) - es_client.delete(index=curr_index, doc_type='text', id=id) - except Exception as e: - logger.error("ERROR deleting {} / {} / {} : {}".format(oref.normal(), version, lang, e)) - - -def delete_version(index, version, lang): - assert isinstance(index, AbstractIndex) - - refs = [] - - if SITE_SETTINGS["TORAH_SPECIFIC"]: - all_gemara_indexes = library.get_indexes_in_category("Bavli") - davidson_indexes = all_gemara_indexes[:all_gemara_indexes.index("Horayot") + 1] - if Ref(index.title).is_bavli() and index.title not in davidson_indexes: - refs += index.all_section_refs() - - refs += index.all_segment_refs() - - for ref in refs: - delete_text(ref, version, lang) - - -def delete_sheet(index_name, id): - try: - es_client.delete(index=index_name, doc_type='sheet', id=id) - except Exception as e: - logger.error("ERROR deleting sheet {}".format(id)) - - -def make_text_doc_id(ref, version, lang): - """ - Returns a doc id string for indexing based on ref, versiona and lang. - - [HACK] Since Elasticsearch chokes on non-ascii ids, hebrew titles are converted - into a number using unicode_number. This mapping should be unique, but actually isn't. - (any tips welcome) - """ - if not version.isascii(): - version = str(unicode_number(version)) - - id = "%s (%s [%s])" % (ref, version, lang) - return id - - -def unicode_number(u): - """ - Returns a number corresponding to the sum value - of each unicode character in u - """ - n = 0 - for i in range(len(u)): - n += ord(u[i]) - return n - - -def index_sheet(index_name, id): - """ - Index source sheet with 'id'. - """ - - sheet = db.sheets.find_one({"id": id}) - if not sheet: return False - - pud = public_user_data(sheet["owner"]) - tag_terms_simple = make_sheet_tags(sheet) - tags = [t["en"] for t in tag_terms_simple] - topics = [] - for t in sheet.get('topics', []): - topic_obj = Topic.init(t['slug']) - if not topic_obj: - continue - topics += [topic_obj] - collections = CollectionSet({"sheets": id, "listed": True}) - collection_names = [c.name for c in collections] - try: - doc = { - "title": strip_tags(sheet["title"]), - "content": make_sheet_text(sheet, pud), - "owner_id": sheet["owner"], - "owner_name": pud["name"], - "owner_image": pud["imageUrl"], - "profile_url": pud["profileUrl"], - "version": "Source Sheet by " + user_link(sheet["owner"]), - "tags": tags, - "topic_slugs": [topic_obj.slug for topic_obj in topics], - "topics_en": [topic_obj.get_primary_title('en') for topic_obj in topics], - "topics_he": [topic_obj.get_primary_title('he') for topic_obj in topics], - "sheetId": id, - "summary": sheet.get("summary", None), - "collections": collection_names, - "datePublished": sheet.get("datePublished", None), - "dateCreated": sheet.get("dateCreated", None), - "dateModified": sheet.get("dateModified", None), - "views": sheet.get("views", 0) - } - es_client.create(index=index_name, doc_type='sheet', id=id, body=doc) - global doc_count - doc_count += 1 - return True - except Exception as e: - print("Error indexing sheet %d" % id) - print(e) - return False - - -def make_sheet_tags(sheet): - def get_primary_title(lang, titles): - return [t for t in titles if t.get("primary") and t.get("lang", "") == lang][0]["text"] - - tags = sheet.get('tags', []) - tag_terms = [(Term().load({'name': t}) or Term().load_by_title(t)) for t in tags] - tag_terms_simple = [ - { - 'en': tags[iterm], # save as en even if it's Hebrew - 'he': '' - } if term is None else - { - 'en': get_primary_title('en', term.titles), - 'he': get_primary_title('he', term.titles) - } for iterm, term in enumerate(tag_terms) - ] - #tags_en, tags_he = zip(*tag_terms_simple.values()) - return tag_terms_simple - -def make_sheet_text(sheet, pud): - """ - Returns a plain text representation of the content of sheet. - :param sheet: The sheet record - :param pud: Public User Database record for the author - """ - text = sheet["title"] + "\n{}".format(sheet.get("summary", '')) - if pud.get("name"): - text += "\nBy: " + pud["name"] - text += "\n" - if sheet.get("tags"): - text += " [" + ", ".join(sheet["tags"]) + "]\n" - for s in sheet["sources"]: - text += source_text(s) + " " - - text = bleach.clean(text, strip=True, tags=()) - - return text - - -def source_text(source): - """ - Recursive function to translate a source dictionary into text. - """ - str_fields = ["customTitle", "ref", "comment", "outsideText"] - dict_fields = ["text", "outsideBiText"] - content = [source.get(field, "") for field in str_fields] - content += [val for field in dict_fields for val in source.get(field, {}).values()] - text = " ".join([strip_tags(c) for c in content]) - - if "subsources" in source: - for s in source["subsources"]: - text += source_text(s) - - return text - - -def get_exact_english_analyzer(): - return { - "tokenizer": "standard", - "char_filter": [ - "icu_normalizer", - ], - "filter": [ - "standard", - "lowercase", - "icu_folding", - ], - } - - -def get_stemmed_english_analyzer(): - stemmed_english_analyzer = get_exact_english_analyzer() - stemmed_english_analyzer['filter'] += ["my_snow"] - return stemmed_english_analyzer - - -def create_index(index_name, type): - """ - Clears the indexes and creates it fresh with the below settings. - """ - clear_index(index_name) - - settings = { - "index": { - "blocks": { - "read_only_allow_delete": False - }, - "analysis": { - "analyzer": { - "stemmed_english": get_stemmed_english_analyzer(), - "exact_english": get_exact_english_analyzer(), - }, - "filter": { - "my_snow": { - "type": "snowball", - "language": "English" - } - } - } - } - } - print('Creating index {}'.format(index_name)) - index_client.create(index=index_name, body=settings) - - if type == 'text': - put_text_mapping(index_name) - elif type == 'sheet': - put_sheet_mapping(index_name) - - -def put_text_mapping(index_name): - """ - Settings mapping for the text document type. - """ - text_mapping = { - 'properties' : { - 'categories': { - 'type': 'keyword', - }, - "category": { - 'type': 'keyword', - }, - "he_category": { - 'type': 'keyword', - }, - "index_title": { - 'type': 'keyword', - }, - "path": { - 'type': 'keyword', - }, - "he_index_title": { - 'type': 'keyword', - }, - "he_path": { - 'type': 'keyword', - }, - "order": { - 'type': 'keyword', - }, - "pagesheetrank": { - 'type': 'double', - 'index': False - }, - "comp_date": { - 'type': 'integer', - 'index': False - }, - "version_priority": { - 'type': 'integer', - 'index': False - }, - "exact": { - 'type': 'text', - 'analyzer': 'exact_english' - }, - "naive_lemmatizer": { - 'type': 'text', - 'analyzer': 'sefaria-naive-lemmatizer', - 'search_analyzer': 'sefaria-naive-lemmatizer-less-prefixes', - 'fields': { - 'exact': { - 'type': 'text', - 'analyzer': 'exact_english' - } - } - } - } - } - index_client.put_mapping(doc_type='text', body=text_mapping, index=index_name) - - -def put_sheet_mapping(index_name): - """ - Sets mapping for the sheets document type. - """ - sheet_mapping = { - 'properties': { - 'owner_name': { - 'type': 'keyword' - }, - 'tags': { - 'type': 'keyword' - }, - "topics_en": { - "type": "keyword" - }, - "topics_he": { - "type": "keyword" - }, - "topic_slugs": { - "type": "keyword" - }, - 'owner_image': { - 'type': 'keyword' - }, - 'datePublished': { - 'type': 'date' - }, - 'dateCreated': { - 'type': 'date' - }, - 'dateModified': { - 'type': 'date' - }, - 'sheetId': { - 'type': 'integer' - }, - 'collections': { - 'type': 'keyword' - }, - 'title': { - 'type': 'keyword' - }, - 'views': { - 'type': 'integer' - }, - 'summary': { - 'type': 'keyword' - }, - 'content': { - 'type': 'text', - 'analyzer': 'stemmed_english' - }, - 'version': { - 'type': 'keyword' - }, - 'profile_url': { - 'type': 'keyword' - }, - 'owner_id': { - 'type': 'integer' - } - } - } - index_client.put_mapping(doc_type='sheet', body=sheet_mapping, index=index_name) - -def get_search_categories(oref, categories): - toc_tree = library.get_toc_tree() - cats = oref.index.categories - - indexed_categories = categories # the default - - # get the full path of every cat along the way. - # starting w/ the longest, - # check if they're root swapped. - paths = [cats[:i] for i in range(len(cats), 0, -1)] - for path in paths: - cnode = toc_tree.lookup(path) - if getattr(cnode, "searchRoot", None) is not None: - # Use the specified searchRoot, with the rest of the category path appended. - indexed_categories = [cnode.searchRoot] + cats[len(path) - 1:] - break - return indexed_categories - - -class TextIndexer(object): - - @classmethod - def clear_cache(cls): - cls.terms_dict = None - cls.version_priority_map = None - cls._bulk_actions = None - cls.best_time_period = None - - - @classmethod - def create_terms_dict(cls): - cls.terms_dict = {} - ts = TermSet() - for t in ts: - cls.terms_dict[t.name] = t.contents() - - @classmethod - def create_version_priority_map(cls): - toc = library.get_toc() - cls.version_priority_map = {} - - def traverse(mini_toc): - if type(mini_toc) == list: - for t in mini_toc: - traverse(t) - elif "contents" in mini_toc: - for t in mini_toc["contents"]: - traverse(t) - elif "title" in mini_toc and not mini_toc.get("isCollection", False): - title = mini_toc["title"] - try: - r = Ref(title) - except InputError: - print("Failed to parse ref, {}".format(title)) - return - vlist = cls.get_ref_version_list(r) - vpriorities = defaultdict(lambda: 0) - for i, v in enumerate(vlist): - lang = v.language - cls.version_priority_map[(title, v.versionTitle, lang)] = (vpriorities[lang], mini_toc["categories"]) - vpriorities[lang] += 1 - - traverse(toc) - - @staticmethod - def get_ref_version_list(oref, tries=0): - try: - return oref.index.versionSet().array() - except InputError as e: - print(f"InputError: {oref.normal()}") - return [] - except pymongo.errors.AutoReconnect as e: - if tries < 200: - pytime.sleep(5) - return TextIndexer.get_ref_version_list(oref, tries+1) - else: - print("get_ref_version_list -- Tried: {} times. Failed :(".format(tries)) - raise e - - @classmethod - def get_all_versions(cls, tries=0, versions=None, page=0): - versions = versions or [] - try: - version_limit = 10 - temp_versions = [] - first_run = True - while first_run or len(temp_versions) > 0: - temp_versions = VersionSet(limit=version_limit, page=page).array() - versions += temp_versions - page += 1 - first_run = False - return versions - except pymongo.errors.AutoReconnect as e: - if tries < 200: - pytime.sleep(5) - return cls.get_all_versions(tries+1, versions, page) - else: - print("Tried: {} times. Got {} versions".format(tries, len(versions))) - raise e - - @classmethod - def index_all(cls, index_name, debug=False, for_es=True, action=None): - cls.index_name = index_name - cls.create_version_priority_map() - cls.create_terms_dict() - Ref.clear_cache() # try to clear Ref cache to save RAM - - versions = sorted([x for x in cls.get_all_versions() if (x.title, x.versionTitle, x.language) in cls.version_priority_map], key=lambda x: cls.version_priority_map[(x.title, x.versionTitle, x.language)][0]) - versions_by_index = {} - # organizing by index for the merged case. There is no longer a merged case but keeping this logic b/c it seems fine - for v in versions: - key = (v.title, v.language) - if key in versions_by_index: - versions_by_index[key] += [v] - else: - versions_by_index[key] = [v] - print("Beginning index of {} versions.".format(len(versions))) - vcount = 0 - total_versions = len(versions) - versions = None # release RAM - for title, vlist in list(versions_by_index.items()): - cls.curr_index = vlist[0].get_index() if len(vlist) > 0 else None - if for_es: - cls._bulk_actions = [] - try: - cls.best_time_period = cls.curr_index.best_time_period() - except ValueError: - cls.best_time_period = None - for v in vlist: - if v.versionTitle == "Yehoyesh's Yiddish Tanakh Translation [yi]": - print("skipping yiddish. we don't like yiddish") - continue - - cls.index_version(v, action=action) - print("Indexed Version {}/{}".format(vcount, total_versions)) - vcount += 1 - if for_es: - bulk(es_client, cls._bulk_actions, stats_only=True, raise_on_error=False) - - @classmethod - def index_version(cls, version, tries=0, action=None): - if not action: - action = cls._cache_action - try: - version.walk_thru_contents(action, heTref=cls.curr_index.get_title('he'), schema=cls.curr_index.schema, terms_dict=cls.terms_dict) - except pymongo.errors.AutoReconnect as e: - # Adding this because there is a mongo call for dictionary words in walk_thru_contents() - if tries < 200: - pytime.sleep(5) - print("Retrying {}. Try {}".format(version.title, tries)) - cls.index_version(version, tries+1) - else: - print("Tried {} times to get {}. I have failed you...".format(tries, version.title)) - raise e - except StopIteration: - print("Could not find dictionary node in {}".format(version.title)) - - @classmethod - def index_ref(cls, index_name, oref, version_title, lang): - # slower than `cls.index_version` but useful when you don't want the overhead of loading all versions into cache - cls.index_name = index_name - cls.curr_index = oref.index - try: - cls.best_time_period = cls.curr_index.best_time_period() - except ValueError: - cls.best_time_period = None - version_priority = 0 - hebrew_version_title = None - for priority, v in enumerate(cls.get_ref_version_list(oref)): - if v.versionTitle == version_title: - version_priority = priority - hebrew_version_title = getattr(v, 'versionTitleInHebrew', None) - content = TextChunk(oref, lang, vtitle=version_title).ja().flatten_to_string() - categories = cls.curr_index.categories - tref = oref.normal() - doc = cls.make_text_index_document(tref, oref.he_normal(), version_title, lang, version_priority, content, categories, hebrew_version_title) - id = make_text_doc_id(tref, version_title, lang) - es_client.index(index_name, doc, id=id) - - @classmethod - def _cache_action(cls, segment_str, tref, heTref, version): - # Index this document as a whole - vtitle = version.versionTitle - vlang = version.language - hebrew_version_title = getattr(version, 'versionTitleInHebrew', None) - try: - version_priority, categories = cls.version_priority_map[(version.title, vtitle, vlang)] - #TODO include sgement_str in this func - doc = cls.make_text_index_document(tref, heTref, vtitle, vlang, version_priority, segment_str, categories, hebrew_version_title) - # print doc - except Exception as e: - logger.error("Error making index document {} / {} / {} : {}".format(tref, vtitle, vlang, str(e))) - return - - if doc: - try: - cls._bulk_actions += [ - { - "_index": cls.index_name, - "_type": "text", - "_id": make_text_doc_id(tref, vtitle, vlang), - "_source": doc - } - ] - except Exception as e: - logger.error("ERROR indexing {} / {} / {} : {}".format(tref, vtitle, vlang, e)) - - @classmethod - def remove_footnotes(cls, content): - ftnotes = AbstractTextRecord.find_all_itags(content, only_footnotes=True)[1] - if len(ftnotes) == 0: - return content - else: - for sup_tag in ftnotes: - i_tag = sup_tag.next_sibling - content += f" {sup_tag.text} {i_tag.text}" - content = AbstractTextRecord.strip_itags(content) - return content - - @classmethod - def modify_text_in_doc(cls, content): - content = AbstractTextRecord.strip_imgs(content) - content = cls.remove_footnotes(content) - content = strip_cantillation(content, strip_vowels=False).strip() - content = re.sub(r'<[^>]+>', ' ', content) # replace HTML tags with space so that words dont get smushed together - content = re.sub(r'\([^)]+\)', ' ', content) # remove all parens - while " " in content: # make sure there are not many spaces in a row - content = content.replace(" ", " ") - return content - - @classmethod - def make_text_index_document(cls, tref, heTref, version, lang, version_priority, content, categories, hebrew_version_title): - """ - Create a document for indexing from the text specified by ref/version/lang - """ - # Don't bother indexing if there's no content - if not content: - return False - content = cls.modify_text_in_doc(content) - if len(content) == 0: - return False - - oref = Ref(tref) - - indexed_categories = get_search_categories(oref, categories) - - tp = cls.best_time_period - if tp is not None: - comp_start_date = int(tp.start) - else: - comp_start_date = 3000 # far in the future - - ref_data = RefData().load({"ref": tref}) - pagesheetrank = ref_data.pagesheetrank if ref_data is not None else RefData.DEFAULT_PAGESHEETRANK - - return { - "ref": tref, - "heRef": heTref, - "version": version, - "lang": lang, - "version_priority": version_priority if version_priority is not None else 1000, - "titleVariants": oref.index_node.all_tree_titles("en"), - "categories": indexed_categories, - "order": oref.order_id(), - "path": "/".join(indexed_categories + [cls.curr_index.title]), - "pagesheetrank": pagesheetrank, - "comp_date": comp_start_date, - #"hebmorph_semi_exact": content, - "exact": content, - "naive_lemmatizer": content, - 'hebrew_version_title': hebrew_version_title, - } - - -def index_sheets_by_timestamp(timestamp): - """ - :param timestamp str: index all sheets modified after `timestamp` (in isoformat) - """ - - name_dict = get_new_and_current_index_names('sheet', debug=False) - curr_index_name = name_dict['current'] - try: - ids = db.sheets.find({"status": "public", "dateModified": {"$gt": timestamp}}).distinct("id") - except Exception as e: - print(e) - return str(e) - - succeeded = [] - failed = [] - - for id in ids: - did_succeed = index_sheet(curr_index_name, id) - if did_succeed: - succeeded += [id] - else: - failed += [id] - - return {"succeeded": {"num": len(succeeded), "ids": succeeded}, "failed": {"num": len(failed), "ids": failed}} - - -def index_public_sheets(index_name): - """ - Index all source sheets that are publicly listed. - """ - ids = db.sheets.find({"status": "public"}).distinct("id") - for id in ids: - index_sheet(index_name, id) - - -def index_public_notes(): - """ - Index all public notes. - - TODO - """ - pass - - -def clear_index(index_name): - """ - Delete the search index. - """ - try: - index_client.delete(index=index_name) - except Exception as e: - print("Error deleting Elasticsearch Index named %s" % index_name) - print(e) - - -def add_ref_to_index_queue(ref, version, lang): - """ - Adds a text to index queue to be indexed later. - """ - qu.IndexQueue({ - "ref": ref, - "lang": lang, - "version": version, - "type": "ref", - }).save() - - return True - - -def index_from_queue(): - """ - Index every ref/version/lang found in the index queue. - Delete queue records on success. - """ - index_name = get_new_and_current_index_names('text')['current'] - queue = db.index_queue.find() - for item in queue: - try: - TextIndexer.index_ref(index_name, Ref(item["ref"]), item["version"], item["lang"], False) - db.index_queue.remove(item) - except Exception as e: - logging.error("Error indexing from queue ({} / {} / {}) : {}".format(item["ref"], item["version"], item["lang"], e)) - - -def add_recent_to_queue(ndays): - """ - Look through the last ndays of the activitiy log, - add to the index queue any refs that had their text altered. - """ - cutoff = datetime.now() - timedelta(days=ndays) - query = { - "date": {"$gt": cutoff}, - "rev_type": {"$in": ["add text", "edit text"]} - } - activity = db.history.find(query) - refs = set() - for a in activity: - refs.add((a["ref"], a["version"], a["language"])) - for ref in list(refs): - add_ref_to_index_queue(ref[0], ref[1], ref[2]) - - -def get_new_and_current_index_names(type, debug=False): - base_index_name_dict = { - 'text': SEARCH_INDEX_NAME_TEXT, - 'sheet': SEARCH_INDEX_NAME_SHEET, - } - index_name_a = "{}-a{}".format(base_index_name_dict[type], '-debug' if debug else '') - index_name_b = "{}-b{}".format(base_index_name_dict[type], '-debug' if debug else '') - alias_name = "{}{}".format(base_index_name_dict[type], '-debug' if debug else '') - aliases = index_client.get_alias() - try: - a_alias = aliases[index_name_a]['aliases'] - choose_a = alias_name not in a_alias - except KeyError: - choose_a = True - - if choose_a: - new_index_name = index_name_a - old_index_name = index_name_b - else: - new_index_name = index_name_b - old_index_name = index_name_a - return {"new": new_index_name, "current": old_index_name, "alias": alias_name} - - -def index_all(skip=0, debug=False): - """ - Fully create the search index from scratch. - """ - start = datetime.now() - index_all_of_type('text', skip=skip, debug=debug) - index_all_of_type('sheet', skip=skip, debug=debug) - end = datetime.now() - db.index_queue.delete_many({}) # index queue is now stale - print("Elapsed time: %s" % str(end-start)) - - -def index_all_of_type(type, skip=0, debug=False): - index_names_dict = get_new_and_current_index_names(type=type, debug=debug) - print('CREATING / DELETING {}'.format(index_names_dict['new'])) - print('CURRENT {}'.format(index_names_dict['current'])) - for i in range(10): - print('STARTING IN T-MINUS {}'.format(10 - i)) - pytime.sleep(1) - - index_all_of_type_by_index_name(type, index_names_dict['new'], skip, debug) - - try: - #index_client.put_settings(index=index_names_dict['current'], body={"index": { "blocks": { "read_only_allow_delete": False }}}) - index_client.delete_alias(index=index_names_dict['current'], name=index_names_dict['alias']) - print("Successfully deleted alias {} for index {}".format(index_names_dict['alias'], index_names_dict['current'])) - except NotFoundError: - print("Failed to delete alias {} for index {}".format(index_names_dict['alias'], index_names_dict['current'])) - - clear_index(index_names_dict['alias']) # make sure there are no indexes with the alias_name - - #index_client.put_settings(index=index_names_dict['new'], body={"index": { "blocks": { "read_only_allow_delete": False }}}) - index_client.put_alias(index=index_names_dict['new'], name=index_names_dict['alias']) - - if index_names_dict['new'] != index_names_dict['current']: - clear_index(index_names_dict['current']) - - -def index_all_of_type_by_index_name(type, index_name, skip=0, debug=False): - if skip == 0: - create_index(index_name, type) - if type == 'text': - TextIndexer.clear_cache() - TextIndexer.index_all(index_name, debug=debug) - elif type == 'sheet': - index_public_sheets(index_name) \ No newline at end of file From f386c210a5bee409fe379065f4632d592a1dd8de Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Wed, 6 Dec 2023 13:43:26 -0500 Subject: [PATCH 627/756] Reorganize functions into proper scope --- static/js/StaticPages.jsx | 196 +++++++++++++++++++------------------- 1 file changed, 98 insertions(+), 98 deletions(-) diff --git a/static/js/StaticPages.jsx b/static/js/StaticPages.jsx index adf5e016ee..09afb6f0c9 100644 --- a/static/js/StaticPages.jsx +++ b/static/js/StaticPages.jsx @@ -2743,116 +2743,116 @@ const BoardMembers = ({ boardMembers }) => { ); }; -const fetchTeamMembersJSON = async () => { - const query = ` -query { - teamMembers(pagination: { limit: -1 }) { - data { - id - attributes { - teamName - teamTitle - isTeamBoardMember - teamMemberImage { - data { - attributes { - url - } - } - } - localizations { - data { - attributes { - locale - teamName - teamTitle - } - } - } - } - } - } -} -`; - try { - const response = await fetch(STRAPI_INSTANCE + "/graphql", { - method: "POST", - mode: "cors", - cache: "no-cache", - credentials: "omit", - headers: { - "Content-Type": "application/json", - }, - redirect: "follow", - referrerPolicy: "no-referrer", - body: JSON.stringify({ query }), - }); - if (!response.ok) { - throw new Error(`HTTP Error: ${response.statusText}`); - } - const data = await response.json(); - return data; - } catch (error) { - throw error; - } -}; - const TeamMembersPage = memo(() => { const [ordinaryTeamMembers, setOrdinaryTeamMembers] = useState([]); const [teamBoardMembers, setTeamBoardMembers] = useState([]); const [error, setError] = useState(null); - useEffect(() => { - const loadTeamMembers = async () => { - if (typeof STRAPI_INSTANCE !== "undefined" && STRAPI_INSTANCE) { - try { - const teamMembersData = await fetchTeamMembersJSON(); - - const teamMembersFromStrapi = - teamMembersData.data.teamMembers.data.map( - (teamMember) => { - const heLocalization = - teamMember.attributes.localizations.data[0]; - - return { - id: teamMember.id, - isTeamBoardMember: - teamMember.attributes.isTeamBoardMember, - teamMemberImage: - teamMember.attributes.teamMemberImage - ?.data?.attributes?.url, - teamMemberDetails: { - teamName: { - en: teamMember.attributes.teamName, - he: heLocalization.attributes - .teamName, - }, - teamTitle: { - en: teamMember.attributes.teamTitle, - he: heLocalization.attributes - .teamTitle, - }, - }, - }; + const fetchTeamMembersJSON = async () => { + const query = ` + query { + teamMembers(pagination: { limit: -1 }) { + data { + id + attributes { + teamName + teamTitle + isTeamBoardMember + teamMemberImage { + data { + attributes { + url + } + } } - ); + localizations { + data { + attributes { + locale + teamName + teamTitle + } + } + } + } + } + } + } + `; + try { + const response = await fetch(STRAPI_INSTANCE + "/graphql", { + method: "POST", + mode: "cors", + cache: "no-cache", + credentials: "omit", + headers: { + "Content-Type": "application/json", + }, + redirect: "follow", + referrerPolicy: "no-referrer", + body: JSON.stringify({ query }), + }); + if (!response.ok) { + throw new Error(`HTTP Error: ${response.statusText}`); + } + const data = await response.json(); + return data; + } catch (error) { + throw error; + } + }; - const [ordinaryMembers, boardMembers] = partition( - teamMembersFromStrapi, - (teamMember) => !teamMember.isTeamBoardMember + const loadTeamMembers = async () => { + if (typeof STRAPI_INSTANCE !== "undefined" && STRAPI_INSTANCE) { + try { + const teamMembersData = await fetchTeamMembersJSON(); + + const teamMembersFromStrapi = + teamMembersData.data.teamMembers.data.map( + (teamMember) => { + const heLocalization = + teamMember.attributes.localizations.data[0]; + + return { + id: teamMember.id, + isTeamBoardMember: + teamMember.attributes.isTeamBoardMember, + teamMemberImage: + teamMember.attributes.teamMemberImage + ?.data?.attributes?.url, + teamMemberDetails: { + teamName: { + en: teamMember.attributes.teamName, + he: heLocalization.attributes + .teamName, + }, + teamTitle: { + en: teamMember.attributes.teamTitle, + he: heLocalization.attributes + .teamTitle, + }, + }, + }; + } ); - setOrdinaryTeamMembers(ordinaryMembers); - setTeamBoardMembers(boardMembers); - } catch (error) { - console.error("Fetch error:", error); - setError("Error: Sefaria's CMS cannot be reached"); - } - } else { + const [ordinaryMembers, boardMembers] = partition( + teamMembersFromStrapi, + (teamMember) => !teamMember.isTeamBoardMember + ); + + setOrdinaryTeamMembers(ordinaryMembers); + setTeamBoardMembers(boardMembers); + } catch (error) { + console.error("Fetch error:", error); setError("Error: Sefaria's CMS cannot be reached"); } - }; + } else { + setError("Error: Sefaria's CMS cannot be reached"); + } + }; + useEffect(() => { loadTeamMembers(); }, []); From d37b5a107cd74e588c38bd70df2717fa9c2a4182 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Wed, 6 Dec 2023 14:13:48 -0500 Subject: [PATCH 628/756] Reorganize functions into proper scope --- static/js/StaticPages.jsx | 155 +++++++++++++++++++------------------- 1 file changed, 78 insertions(+), 77 deletions(-) diff --git a/static/js/StaticPages.jsx b/static/js/StaticPages.jsx index 7ce3a00a69..8ae588a57e 100644 --- a/static/js/StaticPages.jsx +++ b/static/js/StaticPages.jsx @@ -2747,93 +2747,94 @@ const NoJobsNotice = () => { }; -const fetchJobsJSON = async () => { - const currentDateTime = new Date().toISOString(); - const query = ` - query { - jobPostings( - pagination: { limit: -1 } - filters: { - jobPostingStartDate: { lte: \"${currentDateTime}\" } - jobPostingEndDate: { gte: \"${currentDateTime}\" } - } - ) { - data { - id - attributes { - jobLink - jobDescription - jobDepartmentCategory - } - } - } - } - `; - - try { - const response = await fetch(STRAPI_INSTANCE + "/graphql", { - method: "POST", - mode: "cors", - cache: "no-cache", - credentials: "omit", - headers: { - "Content-Type": "application/json", - }, - redirect: "follow", - referrerPolicy: "no-referrer", - body: JSON.stringify({ query }), - }); - if (!response.ok) { - throw new Error(`HTTP Error: ${response.statusText}`); - } - const data = await response.json(); - return data; - } catch (error) { - throw error; - } -}; const JobsPage = memo(() => { const [groupedJobPostings, setGroupedJobPostings] = useState({}); const [error, setError] = useState(null); - useEffect(() => { - const loadJobPostings = async () => { - if (typeof STRAPI_INSTANCE !== "undefined" && STRAPI_INSTANCE) { - try { - const jobsData = await fetchJobsJSON(); - - const jobsFromStrapi = jobsData.data?.jobPostings?.data?.map((jobPosting) => { - return { - id: jobPosting.id, - jobLink: jobPosting.attributes.jobLink, - jobDescription: jobPosting.attributes.jobDescription, - jobDepartmentCategory: jobPosting.attributes.jobDepartmentCategory - .split("_") - .join(" "), - }; - }); - - // Group the job postings by department - const groupedJobs = jobsFromStrapi.reduce((jobPostingsGroupedByDepartment, jobPosting) => { - const category = jobPosting.jobDepartmentCategory; - if (!jobPostingsGroupedByDepartment[category]) { - jobPostingsGroupedByDepartment[category] = []; + const fetchJobsJSON = async () => { + const currentDateTime = new Date().toISOString(); + const query = ` + query { + jobPostings( + pagination: { limit: -1 } + filters: { + jobPostingStartDate: { lte: \"${currentDateTime}\" } + jobPostingEndDate: { gte: \"${currentDateTime}\" } + } + ) { + data { + id + attributes { + jobLink + jobDescription + jobDepartmentCategory } - jobPostingsGroupedByDepartment[category].push(jobPosting); - return jobPostingsGroupedByDepartment; - }, {}); - - setGroupedJobPostings(groupedJobs); - } catch (error) { - console.error("Fetch error:", error); - setError("Error: Sefaria's CMS cannot be reached"); + } } - } else { + } + `; + + try { + const response = await fetch(STRAPI_INSTANCE + "/graphql", { + method: "POST", + mode: "cors", + cache: "no-cache", + credentials: "omit", + headers: { + "Content-Type": "application/json", + }, + redirect: "follow", + referrerPolicy: "no-referrer", + body: JSON.stringify({ query }), + }); + if (!response.ok) { + throw new Error(`HTTP Error: ${response.statusText}`); + } + const data = await response.json(); + return data; + } catch (error) { + throw error; + } + }; + + const loadJobPostings = async () => { + if (typeof STRAPI_INSTANCE !== "undefined" && STRAPI_INSTANCE) { + try { + const jobsData = await fetchJobsJSON(); + + const jobsFromStrapi = jobsData.data?.jobPostings?.data?.map((jobPosting) => { + return { + id: jobPosting.id, + jobLink: jobPosting.attributes.jobLink, + jobDescription: jobPosting.attributes.jobDescription, + jobDepartmentCategory: jobPosting.attributes.jobDepartmentCategory + .split("_") + .join(" "), + }; + }); + + // Group the job postings by department + const groupedJobs = jobsFromStrapi.reduce((jobPostingsGroupedByDepartment, jobPosting) => { + const category = jobPosting.jobDepartmentCategory; + if (!jobPostingsGroupedByDepartment[category]) { + jobPostingsGroupedByDepartment[category] = []; + } + jobPostingsGroupedByDepartment[category].push(jobPosting); + return jobPostingsGroupedByDepartment; + }, {}); + + setGroupedJobPostings(groupedJobs); + } catch (error) { + console.error("Fetch error:", error); setError("Error: Sefaria's CMS cannot be reached"); } - }; + } else { + setError("Error: Sefaria's CMS cannot be reached"); + } + }; + useEffect(() => { loadJobPostings(); }, []); From 2df57a35d153fe9e37504cd9168e538eccbf1855 Mon Sep 17 00:00:00 2001 From: Brendan Galloway <brendan@flanksource.com> Date: Thu, 7 Dec 2023 11:19:03 +0200 Subject: [PATCH 629/756] ci: use monorepo semantic extention to filter for commits affecting helm chart --- .github/workflows/helm.yaml | 2 ++ helm-chart/release-rules.sh | 20 ++------------------ 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/.github/workflows/helm.yaml b/.github/workflows/helm.yaml index 0d7e037e85..4869f61bf4 100644 --- a/.github/workflows/helm.yaml +++ b/.github/workflows/helm.yaml @@ -54,6 +54,8 @@ jobs: extra_plugins: | conventional-changelog-conventionalcommits@6.1.0 @semantic-release/commit-analyzer@10.0.1 + extends: | + semantic-release-monorepo@7.0.5 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Setup diff --git a/helm-chart/release-rules.sh b/helm-chart/release-rules.sh index 8e8cf05ac0..fe1ae4db0f 100755 --- a/helm-chart/release-rules.sh +++ b/helm-chart/release-rules.sh @@ -1,6 +1,8 @@ #!/bin/bash cat << EOF > helm-chart/.releaserc +extends: + - semantic-release-monorepo tagFormat: helm-chart-\${version} plugins: - - "@semantic-release/commit-analyzer" @@ -8,15 +10,6 @@ plugins: releaseRules: - {"type": "helm", "release": "minor" } - {"type": "helm", "scope": "fix", "release": "patch" } - - {"type": "feat", "release": false} - - {"type": "fix", "release": false} - - {"type": "chore", "release": false} - - {"type": "docs", "release": false} - - {"type": "style", "release": false} - - {"type": "refactor", "release": false} - - {"type": "perf", "release": false} - - {"type": "test", "release": false} - - {"type": "static", "release": false} parserOpts: noteKeywords: - MAJOR RELEASE @@ -25,15 +18,6 @@ plugins: presetConfig: "types": - {"type": "helm", "section": "Helm Chart Changes"} - - {"type": "feat", "hidden": true} - - {"type": "fix", "hidden": true} - - {"type": "chore", "hidden": true} - - {"type": "docs", "hidden": true} - - {"type": "style", "hidden": true} - - {"type": "refactor", "hidden": true} - - {"type": "perf", "hidden": true} - - {"type": "test", "hidden": true} - - {"type": "static", "hidden": true} - - "@semantic-release/github" - "successComment": false EOF From cae792bfae7990a0602d19743bcd7d83a156cc0b Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Thu, 7 Dec 2023 14:31:58 +0200 Subject: [PATCH 630/756] fix(Topic Editor): save topic upon upload/remove image --- reader/views.py | 45 +++++++++++++++++---------------------- sefaria/urls.py | 3 +-- static/js/Misc.jsx | 8 +++---- static/js/TopicEditor.jsx | 6 +++--- 4 files changed, 28 insertions(+), 34 deletions(-) diff --git a/reader/views.py b/reader/views.py index e2bab5ddf0..6481684eee 100644 --- a/reader/views.py +++ b/reader/views.py @@ -3561,41 +3561,36 @@ def profile_follow_api(request, ftype, slug): return jsonResponse(response) return jsonResponse({"error": "Unsupported HTTP method."}) - -@catch_error_as_json -def topic_delete_photo(request, file): - if not request.user.is_authenticated: - return jsonResponse({"error": _("You must be logged in to update a topic photo.")}) - bucket_name = GoogleStorageManager.TOPICS_BUCKET - if file: - file = f"topics/{file.split('/')[-1]}" - if request.method == "DELETE": - if file is None: - return jsonResponse({"error": "You cannot remove an image as you haven't selected one yet."}) - GoogleStorageManager.delete_filename(file, bucket_name) - return jsonResponse({"success": "You have successfully removed the image."}) - -@catch_error_as_json -def topic_upload_photo(request): +@staff_member_required +def topic_upload_photo(request, topic): from io import BytesIO import uuid import base64 - if not request.user.is_authenticated: - return jsonResponse({"error": _("You must be logged in to update a topic photo.")}) - bucket_name = GoogleStorageManager.TOPICS_BUCKET - file = request.POST.get('file') - old_filename = request.POST.get('old_filename') # delete file from google storage if there is one there - if old_filename: - old_filename = f"topics/{old_filename.split('/')[-1]}" if request.method == "DELETE": + old_filename = request.GET.get("old_filename") if old_filename is None: return jsonResponse({"error": "You cannot remove an image as you haven't selected one yet."}) - GoogleStorageManager.delete_filename(old_filename, bucket_name) + old_filename = f"topics/{old_filename.split('/')[-1]}" + GoogleStorageManager.delete_filename(old_filename, GoogleStorageManager.TOPICS_BUCKET) + topic = Topic.init(topic) + if hasattr(topic, "image"): + del topic.image + topic.save() return jsonResponse({"success": "You have successfully removed the image."}) elif request.method == "POST": + file = request.POST.get('file') + old_filename = request.POST.get('old_filename') # delete file from google storage if there is one there + if old_filename: + old_filename = f"topics/{old_filename.split('/')[-1]}" img_file_in_mem = BytesIO(base64.b64decode(file)) img_url = GoogleStorageManager.upload_file(img_file_in_mem, f"topics/{request.user.id}-{uuid.uuid1()}.gif", - bucket_name, old_filename=old_filename) + GoogleStorageManager.TOPICS_BUCKET, old_filename=old_filename) + topic = Topic.init(topic) + if not hasattr(topic, "image"): + topic.image = {"image_uri": img_url, "image_caption": {"en": "", "he": ""}} + else: + topic.image["image_uri"] = img_url + topic.save() return jsonResponse({"url": img_url}) return jsonResponse({"error": "Unsupported HTTP method."}) diff --git a/sefaria/urls.py b/sefaria/urls.py index c1ae9404f9..8e7fbd0f94 100644 --- a/sefaria/urls.py +++ b/sefaria/urls.py @@ -103,8 +103,7 @@ url(r'^topics/b/(?P<topic>.+)$', reader_views.topic_page_b), url(r'^topics/(?P<topic>.+)$', reader_views.topic_page), url(r'^api/topic/completion/(?P<topic>.+)', reader_views.topic_completion_api), - url(r'^api/topics/upload-image$', reader_views.topic_upload_photo), - url(r'^api/topics/delete-image/(?P<file>.+)$', reader_views.topic_delete_photo), + url(r'^api/topics/images/(?P<topic>.+)$', reader_views.topic_upload_photo) ] diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 4bdedf372c..168caf1740 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -1577,7 +1577,7 @@ FollowButton.propTypes = { }; -const PictureUploader = ({callback, old_filename, caption}) => { +const TopicPictureUploader = ({slug, callback, old_filename, caption}) => { /* `old_filename` is passed to API so that if it exists, it is deleted */ @@ -1590,7 +1590,7 @@ const PictureUploader = ({callback, old_filename, caption}) => { formData.append('old_filename', old_filename); } $.ajax({ - url: Sefaria.apiHost + "/api/topics/upload-image", + url: `${Sefaria.apiHost}/api/topics/images/${slug}`, type, data: formData, contentType: false, @@ -1630,7 +1630,7 @@ const PictureUploader = ({callback, old_filename, caption}) => { } const deleteImage = () => { const old_filename_wout_url = old_filename.split("/").slice(-1); - const url = `${Sefaria.apiHost}/api/topics/delete-image/${old_filename_wout_url}`; + const url = `${Sefaria.apiHost}/api/topics/images/${slug}?old_filename=${old_filename_wout_url}`; requestWithCallBack({url, type: "DELETE", redirect: () => alert("Deleted image.")}); callback(""); fileInput.current.value = ""; @@ -3339,6 +3339,6 @@ export { TitleVariants, requestWithCallBack, OnInView, - PictureUploader, + TopicPictureUploader, ImageWithCaption }; diff --git a/static/js/TopicEditor.jsx b/static/js/TopicEditor.jsx index a42c690d82..0c36f2489c 100644 --- a/static/js/TopicEditor.jsx +++ b/static/js/TopicEditor.jsx @@ -1,5 +1,5 @@ import Sefaria from "./sefaria/sefaria"; -import {InterfaceText, requestWithCallBack, PictureUploader} from "./Misc"; +import {InterfaceText, requestWithCallBack, TopicPictureUploader} from "./Misc"; import $ from "./sefaria/sefariaJquery"; import {AdminEditor} from "./AdminEditor"; import {Reorder} from "./CategoryEditor"; @@ -205,8 +205,8 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { items.push("Hebrew Caption"); return <AdminEditor title="Topic Editor" close={closeTopicEditor} catMenu={catMenu} data={data} savingStatus={savingStatus} validate={validate} deleteObj={deleteObj} updateData={updateData} isNew={isNew} items={items} - pictureUploader={<PictureUploader callback={handlePictureChange} old_filename={data.image_uri} - caption={{en: data.enImgCaption, he: data.heImgCaption}}/>} + pictureUploader={<TopicPictureUploader slug={data.origSlug} callback={handlePictureChange} old_filename={data.image_uri} + caption={{en: data.enImgCaption, he: data.heImgCaption}}/>} extras={ [isNew ? null : <Reorder subcategoriesAndBooks={sortedSubtopics} From fa4c5f30a67f3670435b9d09d8cf21e5a8ade84d Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Wed, 13 Dec 2023 11:05:43 +0200 Subject: [PATCH 631/756] chore: ajax to JS --- sefaria/helper/topic.py | 1 + static/js/Misc.jsx | 41 ++++++++++++++++++++++------------------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/sefaria/helper/topic.py b/sefaria/helper/topic.py index d9c3bcd41b..d45a925030 100644 --- a/sefaria/helper/topic.py +++ b/sefaria/helper/topic.py @@ -1110,6 +1110,7 @@ def update_topic(topic, **kwargs): if image_dict["image_uri"] != "": topic.image = kwargs["image"] elif hasattr(topic, 'image'): + # we don't want captions without image_uris, so if the image_uri is blank, get rid of the caption too del topic.image topic.save() diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 168caf1740..4d7205fc74 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -1589,25 +1589,28 @@ const TopicPictureUploader = ({slug, callback, old_filename, caption}) => { if (old_filename !== "") { formData.append('old_filename', old_filename); } - $.ajax({ - url: `${Sefaria.apiHost}/api/topics/images/${slug}`, - type, - data: formData, - contentType: false, - processData: false, - success: function(data) { - if (data.error) { - alert(data.error); - } else { - if (data.url) { - callback(data.url); - } - }}, - error: function(e) { - alert(e); - } - }); - } + const request = new Request( + `${Sefaria.apiHost}/api/topics/images/${slug}`, + {headers: {'X-CSRFToken': Cookies.get('csrftoken')}} + ); + fetch(request, { + method: 'POST', + mode: 'same-origin', + credentials: 'same-origin', + body: formData + }).then(response => { + if (!response.ok) { + response.text().then(resp_text=> { + alert(resp_text); + }) + }else{ + response.json().then(resp_json=>{ + callback(resp_json.url); + }); + } + }).catch(error => { + alert(error); + })}; const onFileSelect = (e) => { const file = fileInput.current.files[0]; if (file == null) From 2929c046d188e54b9aaca42a44a73eccc214096c Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Wed, 13 Dec 2023 12:08:55 +0200 Subject: [PATCH 632/756] chore: add topics bucket to contextus site settings --- sites/s4d/site_settings.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sites/s4d/site_settings.py b/sites/s4d/site_settings.py index 481a278993..96df0b4bd6 100644 --- a/sites/s4d/site_settings.py +++ b/sites/s4d/site_settings.py @@ -14,5 +14,6 @@ "LIBRARY_MESSAGE": "This site is a proof of concept for bringing <a href='https://www.sefaria.org' target='_blank'>Sefaria</a>'s approach to interlinking Jewish texts to the United States Constitution and related writings. It contains an initial set of example texts, for which we are seeking a partner organization to build into a complete library. This library is not interconnected with Sefaria's library of Jewish texts. <a target='_blank' href='https://docs.google.com/document/d/e/2PACX-1vREoqWlYLC68jScm2yBO2cZY1SbLOjj67Tu59VdVVYf1lbnNIxiKG07GSNKlg6p77kIpauiqPRZsSsk/pub'>Learn More ›</a>", "COLLECTIONS_BUCKET": "jmc-collection-images", "PROFILES_BUCKET": 'jmc-profile-pictures', - "UGC_BUCKET": 'jmc-sheet-user-uploaded-media' + "UGC_BUCKET": 'jmc-sheet-user-uploaded-media', + "TOPICS_BUCKET": 'topicimages' } \ No newline at end of file From 6c1a224e98f6026b9bcff26bfe82fd7506627f75 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Wed, 13 Dec 2023 18:24:46 -0800 Subject: [PATCH 633/756] Updates Ways To Give pages for both languages to point to the most recent 990 form --- static/files/Sefaria_2022_990_Public.pdf | Bin 0 -> 539028 bytes static/js/StaticPages.jsx | 4 ++-- templates/static/he/ways-to-give.html | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 static/files/Sefaria_2022_990_Public.pdf diff --git a/static/files/Sefaria_2022_990_Public.pdf b/static/files/Sefaria_2022_990_Public.pdf new file mode 100644 index 0000000000000000000000000000000000000000..9c62f136b151aed0c7f8cd8c1466501f99e28656 GIT binary patch literal 539028 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 Zr&K|X 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�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%0A=|h5=J7 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%0D=`;1Wnor~u6 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 N-doz(3~(Q4`Crk%Aaei! literal 0 HcmV?d00001 diff --git a/static/js/StaticPages.jsx b/static/js/StaticPages.jsx index 09afb6f0c9..414dc2c947 100644 --- a/static/js/StaticPages.jsx +++ b/static/js/StaticPages.jsx @@ -1557,7 +1557,7 @@ const DonatePage = () => ( enTitle="Where does my gift go? How does Sefaria use the donations it receives?" heTitle="" enText="<p>Generally, gifts made to Sefaria are considered “unrestricted,” meaning that our staff allocates funds where they’re needed most. This includes everything from the text and learning you see on your screen to the technology support that keeps us online to the time and energy of the Sefaria team.</p> - <p><a href='https://www.guidestar.org/profile/46-4406454'>Sefaria has a Platinum rating on GuideStar</a> and we’re devoted to making sure we’re transparent and open with our donors. For a closer look at our financials, <a target='_blank' href='/static/files/Sefaria_2021_990_Public.pdf'>download the most recent Sefaria 990</a>.</p>" + <p><a href='https://www.guidestar.org/profile/46-4406454'>Sefaria has a Platinum rating on GuideStar</a> and we’re devoted to making sure we’re transparent and open with our donors. For a closer look at our financials, <a target='_blank' href='/static/files/Sefaria_2022_990_Public.pdf'>download the most recent Sefaria 990</a>.</p>" heText="" colorBar="#B8D4D3" /> @@ -1727,7 +1727,7 @@ const DonatePage = () => ( rounded={true} tall={false} newTab={true} - href="/static/files/Sefaria_2021_990_Public.pdf" + href="/static/files/Sefaria_2022_990_Public.pdf" he_href="" he="" en="See Here" diff --git a/templates/static/he/ways-to-give.html b/templates/static/he/ways-to-give.html index a9a0ab6c3a..1101c7639c 100644 --- a/templates/static/he/ways-to-give.html +++ b/templates/static/he/ways-to-give.html @@ -150,7 +150,7 @@ <h2> </header> <section> <span class="int-he"> - <a target="_blank" href="{% static "files/Sefaria_2020_990_Public.pdf" %}">דוח 990</a> + <a target="_blank" href="{% static "files/Sefaria_2022_990_Public.pdf" %}">דוח 990</a> </span> </section> <section> From c716d15ed0f4c6a30def8c07420b27e18b1686bc Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Wed, 13 Dec 2023 20:25:24 -0800 Subject: [PATCH 634/756] Change "non-profit" to "nonprofit" in all instances --- locale/en/LC_MESSAGES/django.po | 4 ++-- locale/he/LC_MESSAGES/django.po | 4 ++-- static/js/NavSidebar.jsx | 8 ++++---- static/js/StaticPages.jsx | 2 +- static/js/sefaria/strings.js | 2 +- templates/static/app.html | 2 +- templates/static/aramaic-translation-contest.html | 2 +- templates/static/dicta-thanks.html | 2 +- templates/static/en/about.html | 6 +++--- templates/static/he/about.html | 2 +- templates/static/home.html | 2 +- templates/static/jobs.html | 4 ++-- templates/static/mobile.html | 2 +- 13 files changed, 21 insertions(+), 21 deletions(-) diff --git a/locale/en/LC_MESSAGES/django.po b/locale/en/LC_MESSAGES/django.po index cc65939c5d..d215208623 100644 --- a/locale/en/LC_MESSAGES/django.po +++ b/locale/en/LC_MESSAGES/django.po @@ -1057,7 +1057,7 @@ msgstr "" #: templates/static/en/about.html:6 templates/static/he/about.html:6 msgid "" -"Sefaria is a non-profit organization dedicated to building the future of " +"Sefaria is a nonprofit organization dedicated to building the future of " "Jewish learning in an open and participatory way." msgstr "" @@ -1091,7 +1091,7 @@ msgstr "" #: templates/static/jobs.html:6 msgid "" -"Job openings at Sefaria, a non-profit technology organization dedicated to " +"Job openings at Sefaria, a nonprofit technology organization dedicated to " "building the Jewish future." msgstr "" diff --git a/locale/he/LC_MESSAGES/django.po b/locale/he/LC_MESSAGES/django.po index 1219a7332a..3228ed80a0 100644 --- a/locale/he/LC_MESSAGES/django.po +++ b/locale/he/LC_MESSAGES/django.po @@ -1132,7 +1132,7 @@ msgstr "אודות ספריא" #: templates/static/en/about.html:6 templates/static/he/about.html:6 msgid "" -"Sefaria is a non-profit organization dedicated to building the future of " +"Sefaria is a nonprofit organization dedicated to building the future of " "Jewish learning in an open and participatory way." msgstr "" "ספריא היא עמותה ללא מטרות רווה השואפת לעיצוב עתיד הלימוד היהודי כך שיהא פתוח " @@ -1168,7 +1168,7 @@ msgstr "משרות בספריא" #: templates/static/jobs.html:6 msgid "" -"Job openings at Sefaria, a non-profit technology organization dedicated to " +"Job openings at Sefaria, a nonprofit technology organization dedicated to " "building the Jewish future." msgstr "" "משרות פנויות בספריא, עמותה ללא מטרות רווח שמטרתה הבטחת העתיד הטכנולוגי של " diff --git a/static/js/NavSidebar.jsx b/static/js/NavSidebar.jsx index 69976798fe..6ea1d45507 100644 --- a/static/js/NavSidebar.jsx +++ b/static/js/NavSidebar.jsx @@ -97,7 +97,7 @@ const AboutSefaria = ({hideTitle}) => ( <ModuleTitle h1={true}>A Living Library of Torah</ModuleTitle> : null } <InterfaceText> <EnglishText> - Sefaria is home to 3,000 years of Jewish texts. We are a non-profit organization offering free access to texts, translations, + Sefaria is home to 3,000 years of Jewish texts. We are a nonprofit organization offering free access to texts, translations, and commentaries so that everyone can participate in the ongoing process of studying, interpreting, and creating Torah. </EnglishText> <HebrewText> @@ -135,7 +135,7 @@ const AboutTranslatedText = ({translationsSlug}) => { "fi": {title: "Tooran elävä kirjasto", body: "Sefaria on koti 3000 vuoden juutalaisille teksteille. Olemme voittoa tavoittelematon organisaatio, joka tarjoaa ilmaisen pääsyn teksteihin, käännöksiin ja kommentteihin, jotta kaikki voivat osallistua jatkuvaan Tooran opiskelu-, tulkkaus- ja luomisprosessiin."}, "fr": {title: "Une bibliothèque vivante de la Torah", body: "Une bibliothèque de Torah vivante. Sefaria abrite 3 000 ans de textes juifs. Nous sommes une organisation à but non lucratif offrant un accès gratuit aux textes de la Torah, aux commentaires et aux traductions, afin que chacun puisse participer au processus infini de l'étude, de l'interprétation et de la création de la Torah."}, "it": {title: "Una biblioteca vivente della Torah", body: "Sefaria ospita 3.000 anni di testi ebraici. Siamo un'organizzazione senza scopo di lucro che offre libero accesso a testi, traduzioni e commenti in modo che tutti possano partecipare al processo in corso di studio, interpretazione e creazione della Torah."}, - "pl": {title: "Żywa Biblioteka Tory", body: "Sefaria jest domem dla 3000 lat żydowskich tekstów. Jesteśmy organizacją non-profit oferującą bezpłatny dostęp do tekstów, tłumaczeń i komentarzy, dzięki czemu każdy może uczestniczyć w bieżącym procesie studiowania, tłumaczenia i tworzenia Tory."}, + "pl": {title: "Żywa Biblioteka Tory", body: "Sefaria jest domem dla 3000 lat żydowskich tekstów. Jesteśmy organizacją nonprofit oferującą bezpłatny dostęp do tekstów, tłumaczeń i komentarzy, dzięki czemu każdy może uczestniczyć w bieżącym procesie studiowania, tłumaczenia i tworzenia Tory."}, "pt": {title: "Uma Biblioteca Viva da Torá", body: "Sefaria é o lar de 3.000 anos de textos judaicos. Somos uma organização sem fins lucrativos que oferece acesso gratuito a textos, traduções e comentários para que todos possam participar do processo contínuo de estudo, interpretação e criação da Torá."}, "ru": {title: "Живая библиотека Торы", body: "Сефария является домом для еврейских текстов 3000-летней давности. Мы — некоммерческая организация, предлагающая бесплатный доступ к текстам, переводам и комментариям, чтобы каждый мог участвовать в продолжающемся процессе изучения, толкования и создания Торы."}, "yi": {title: "א לעבעדיקע ביבליאטעק פון תורה", body: "אין ספֿריאַ איז אַ היים פֿון 3,000 יאָר ייִדישע טעקסטן. מיר זענען אַ נאַן-נוץ אָרגאַניזאַציע וואָס אָפפערס פריי אַקסעס צו טעקסטן, איבערזעצונגען און קאָמענטאַרן אַזוי אַז אַלעמען קענען אָנטייל נעמען אין די אָנגאָינג פּראָצעס פון לערנען, ינטערפּריטיישאַן און שאפן תורה."} @@ -148,7 +148,7 @@ const AboutTranslatedText = ({translationsSlug}) => { translationLookup[translationsSlug]["body"] : <InterfaceText> <EnglishText> - Sefaria is home to 3,000 years of Jewish texts. We are a non-profit organization offering free access to texts, translations, + Sefaria is home to 3,000 years of Jewish texts. We are a nonprofit organization offering free access to texts, translations, and commentaries so that everyone can participate in the ongoing process of studying, interpreting, and creating Torah. </EnglishText> <HebrewText> @@ -191,7 +191,7 @@ const TheJewishLibrary = ({hideTitle}) => ( const SupportSefaria = ({blue}) => ( <Module blue={blue}> <ModuleTitle>Support Sefaria</ModuleTitle> - <InterfaceText>Sefaria is an open source, non-profit project. Support us by making a tax-deductible donation.</InterfaceText> + <InterfaceText>Sefaria is an open source, nonprofit project. Support us by making a tax-deductible donation.</InterfaceText> <br /> <DonateLink classes={"button small" + (blue ? " white" : "")} source={"NavSidebar-SupportSefaria"}> <img src="/static/img/heart.png" alt="donation icon" /> diff --git a/static/js/StaticPages.jsx b/static/js/StaticPages.jsx index 09afb6f0c9..1c38f1ae1b 100644 --- a/static/js/StaticPages.jsx +++ b/static/js/StaticPages.jsx @@ -1973,7 +1973,7 @@ const PoweredByPage = () => ( /> <Feature enTitle="Dicta" - enText="Dicta is a non-profit research organization based in Israel that applies cutting-edge machine learning and natural language processing (the ability of a computer program to understand human language as it is spoken and written) to the analysis of Hebrew texts. Sefaria and Dicta often collaborate, sharing texts and splitting the costs of shared projects. Dicta offers a broad range of tools for free use by anyone, including the ability to add nikud (vocalization) to text as you type, intuitive Talmud and Bible search, and more." + enText="Dicta is a nonprofit research organization based in Israel that applies cutting-edge machine learning and natural language processing (the ability of a computer program to understand human language as it is spoken and written) to the analysis of Hebrew texts. Sefaria and Dicta often collaborate, sharing texts and splitting the costs of shared projects. Dicta offers a broad range of tools for free use by anyone, including the ability to add nikud (vocalization) to text as you type, intuitive Talmud and Bible search, and more." enImg="/static/img/powered-by-landing-page/talmudsearch.dicta.org.il_.png" enImgAlt="Screenshot of Dicta" borderColor={palette.colors.lightblue} diff --git a/static/js/sefaria/strings.js b/static/js/sefaria/strings.js index c1998554e2..462214f6b9 100644 --- a/static/js/sefaria/strings.js +++ b/static/js/sefaria/strings.js @@ -35,7 +35,7 @@ const Strings = { "Stay Connected": "הישארו מעודכנים", "Get updates on new texts, learning resources, features, and more.": "קבלו עדכונים על מקורות חדשים, כלי למידה חדשים ועוד.", "Support Sefaria": "תמכו בספריא", - "Sefaria is an open source, non-profit project. Support us by making a tax-deductible donation.": "ספריא היא מאגר פתוח וחינמי. תמכו בנו בעזרת תרומה.", + "Sefaria is an open source, nonprofit project. Support us by making a tax-deductible donation.": "ספריא היא מאגר פתוח וחינמי. תמכו בנו בעזרת תרומה.", "Make a Donation": "לתרומה", "Join the Conversation": "קחו חלק בשיח" , "Explore the Community": "לעמוד הקהילה", diff --git a/templates/static/app.html b/templates/static/app.html index 4c55712ceb..ef2f6884d7 100644 --- a/templates/static/app.html +++ b/templates/static/app.html @@ -334,7 +334,7 @@ <h3 class="mobileNewsletterHeader"> </div> <div id="mobilePageNonProfit"> <p class="int-en"> - Sefaria is a 501(c)(3) non-profit organization that creates open source software and publishes digital texts with open licenses. + Sefaria is a 501(c)(3) nonprofit organization that creates open source software and publishes digital texts with open licenses. </p> <p class="int-he"> ספריא הוא אירגון ללא מטרת רווח לפי חוקי ארצות הברית אשר יוצר תוכנות חיפוש למקורות פתוחים לשימוש ומפרסם טקסטים דיגיטליים שאין עליהם זכויות יוצרים. diff --git a/templates/static/aramaic-translation-contest.html b/templates/static/aramaic-translation-contest.html index 515bef5f99..1511878c80 100644 --- a/templates/static/aramaic-translation-contest.html +++ b/templates/static/aramaic-translation-contest.html @@ -154,7 +154,7 @@ <h2> <span class="int-he">אנו זקוקים לעזרתכם</span> </h2> <div class="description"> - <span class="int-en">Sefaria is an open source, non-profit project. Support us by making a tax-deductible donation.</span> + <span class="int-en">Sefaria is an open source, nonprofit project. Support us by making a tax-deductible donation.</span> <span class="int-he">פרויקט ספריא פתוח לקהל הרחב (open source) ללא מטרות רווח. תמכו בנו באמצעות תרומה פטורה ממס.</span> </div> <a href="/donate"> diff --git a/templates/static/dicta-thanks.html b/templates/static/dicta-thanks.html index a7cd48cbd9..df5ca09815 100644 --- a/templates/static/dicta-thanks.html +++ b/templates/static/dicta-thanks.html @@ -30,7 +30,7 @@ <h1> </span> </p> <p> - <span class="int-en">Dicta is a non-profit organization that provides its products at no charge for the benefit of the public.</span> + <span class="int-en">Dicta is a nonprofit organization that provides its products at no charge for the benefit of the public.</span> <span class="int-he">דיקטה היא עמותה ללא מטרת רווח המספקת את מוצריה ללא תשלום לטובת הכלל.</span> </p> <p> diff --git a/templates/static/en/about.html b/templates/static/en/about.html index 105bb9b8b5..4672781660 100644 --- a/templates/static/en/about.html +++ b/templates/static/en/about.html @@ -3,7 +3,7 @@ {% block title %}{% trans "About Sefaria" %}{% endblock %} -{% block description %}{% trans "Sefaria is a non-profit organization dedicated to building the future of Jewish learning in an open and participatory way." %}{% endblock %} +{% block description %}{% trans "Sefaria is a nonprofit organization dedicated to building the future of Jewish learning in an open and participatory way." %}{% endblock %} {% block css %} #top #languageToggle { @@ -47,7 +47,7 @@ <h1 id="aboutTitle"> </span> </p> <p> - <span class="int-en">Sefaria is a non-profit organization dedicated to building the future of Jewish learning in an open and participatory way. + <span class="int-en">Sefaria is a nonprofit organization dedicated to building the future of Jewish learning in an open and participatory way. We are assembling a free living library of Jewish texts and their interconnections, in Hebrew and in translation. With these digital texts, we can create new, interactive interfaces for Web, tablet and mobile, @@ -131,7 +131,7 @@ <h1> </span> </section> <section class="historyItem"> - <span class="int-en">Sefaria incorporates as a non-profit and hires its first employee – who remains at the company to this day!</span> + <span class="int-en">Sefaria incorporates as a nonprofit and hires its first employee – who remains at the company to this day!</span> </section> <section class="historyItem"> <span class="int-en">The Sefaria library grows to 8.5 million words.</span> diff --git a/templates/static/he/about.html b/templates/static/he/about.html index a936311229..16d3ce6e49 100644 --- a/templates/static/he/about.html +++ b/templates/static/he/about.html @@ -3,7 +3,7 @@ {% block title %}{% trans "About Sefaria" %}{% endblock %} -{% block description %}{% trans "Sefaria is a non-profit organization dedicated to building the future of Jewish learning in an open and participatory way." %}{% endblock %} +{% block description %}{% trans "Sefaria is a nonprofit organization dedicated to building the future of Jewish learning in an open and participatory way." %}{% endblock %} {% block css %} #top #languageToggle { diff --git a/templates/static/home.html b/templates/static/home.html index 0116a5a18f..e795bb1840 100644 --- a/templates/static/home.html +++ b/templates/static/home.html @@ -336,7 +336,7 @@ <h2> <span class="int-he">אנו זקוקים לעזרתכם</span> </h2> <div class="description systemText"> - <span class="int-en">Sefaria is an open source, non-profit project. Support us by making a tax-deductible donation.</span> + <span class="int-en">Sefaria is an open source, nonprofit project. Support us by making a tax-deductible donation.</span> <span class="int-he">פרויקט ספריא פתוח לקהל הרחב (open source) ללא מטרות רווח. תמכו בנו באמצעות תרומה פטורה ממס.</span> </div> <a href="/donate"> diff --git a/templates/static/jobs.html b/templates/static/jobs.html index 0b341814f7..b47f4d244b 100644 --- a/templates/static/jobs.html +++ b/templates/static/jobs.html @@ -3,7 +3,7 @@ {% block title %}{% trans "Jobs at Sefaria" %}{% endblock %} -{% block description %}{% trans "Job openings at Sefaria, a non-profit technology organization dedicated to building the Jewish future." %}{% endblock %} +{% block description %}{% trans "Job openings at Sefaria, a nonprofit technology organization dedicated to building the Jewish future." %}{% endblock %} {% block content %} @@ -22,7 +22,7 @@ <h1 class="serif"> </h2> <p> <span class="int-en"> - Sefaria is a non-profit organization dedicated to creating the future of Torah in an open and + Sefaria is a nonprofit organization dedicated to creating the future of Torah in an open and participatory way. We are assembling a free, living library of Jewish texts and their interconnections, in Hebrew and in translation. diff --git a/templates/static/mobile.html b/templates/static/mobile.html index e7943735fe..1137d6c52c 100644 --- a/templates/static/mobile.html +++ b/templates/static/mobile.html @@ -339,7 +339,7 @@ <h3 class="mobileNewsletterHeader"> </div> <div id="mobilePageNonProfit"> <p class="int-en"> - Sefaria is a 501(c)(3) non-profit organization that creates open source software and publishes digital texts with open licenses. + Sefaria is a 501(c)(3) nonprofit organization that creates open source software and publishes digital texts with open licenses. </p> <p class="int-he"> ספריא הוא אירגון ללא מטרת רווח לפי חוקי ארצות הברית אשר יוצר תוכנות חיפוש למקורות פתוחים לשימוש ומפרסם טקסטים דיגיטליים שאין עליהם זכויות יוצרים. From 1e9cb5bd46fd9762c72dc05eafa4c36ca9b226ed Mon Sep 17 00:00:00 2001 From: Skyler C <skyler@sefaria.org> Date: Thu, 14 Dec 2023 13:47:18 -0500 Subject: [PATCH 635/756] Fix merge issue and change last remaining "non-profit" string --- static/js/StaticPages.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/StaticPages.jsx b/static/js/StaticPages.jsx index 3d0bf92187..005337d866 100644 --- a/static/js/StaticPages.jsx +++ b/static/js/StaticPages.jsx @@ -2904,7 +2904,7 @@ const JobsPageHeader = ({ jobsAreAvailable }) => { </h2> <p> <span className="int-en"> - Sefaria is a non-profit organization dedicated to creating the + Sefaria is a nonprofit organization dedicated to creating the future of Torah in an open and participatory way. We are assembling a free, living library of Jewish texts and their interconnections, in Hebrew and in translation. From 88f0c7daaa8060a01deb2d05e69e457b5f89e2b7 Mon Sep 17 00:00:00 2001 From: Ephraim <ephraim@sefaria.org> Date: Sun, 17 Dec 2023 15:15:39 +0200 Subject: [PATCH 636/756] chore(helm): Add Varnish cfg for v3 text api endpoint --- .../sefaria-project/templates/configmap/varnish-config.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/helm-chart/sefaria-project/templates/configmap/varnish-config.yaml b/helm-chart/sefaria-project/templates/configmap/varnish-config.yaml index d2c5670f13..c293c3963e 100644 --- a/helm-chart/sefaria-project/templates/configmap/varnish-config.yaml +++ b/helm-chart/sefaria-project/templates/configmap/varnish-config.yaml @@ -44,6 +44,7 @@ data: if ((req.method == "GET") && ( (req.url ~"^/api/texts" && req.url !~"^/api/texts/versions/" && req.url !~"^/api/texts/parashat_hashavua" && req.url !~"^/api/texts/random" && req.url !~"layer=") + || req.url ~"^/api/v3/texts" || req.url ~"^/api/texts/version-status/tree/" || req.url ~"^/api/links/bare" || req.url ~"^/api/links" From 5cb18881922142b53cf17660d1cc97ec9ded5987 Mon Sep 17 00:00:00 2001 From: Ephraim <ephraim@sefaria.org> Date: Mon, 18 Dec 2023 15:47:11 +0200 Subject: [PATCH 637/756] chore(otel): Disable otel (temporarily?) --- build/web/Dockerfile | 2 +- requirements.txt | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build/web/Dockerfile b/build/web/Dockerfile index 7065e16cee..df5a02be51 100644 --- a/build/web/Dockerfile +++ b/build/web/Dockerfile @@ -4,7 +4,7 @@ ARG TYPE=build-prod WORKDIR /app/ # Copied separately to allow for caching of the `pip install` build step COPY requirements.txt /app/requirements.txt -RUN pip3 install --no-cache-dir -r /app/requirements.txt && opentelemetry-bootstrap -a install +RUN pip3 install --no-cache-dir -r /app/requirements.txt COPY package*.json /app/ RUN npm install --unsafe-perm diff --git a/requirements.txt b/requirements.txt index df83dfb6ee..bb35b78e5a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -67,7 +67,7 @@ python-bidi requests Cerberus -opentelemetry-distro -opentelemetry-exporter-otlp -opentelemetry-propagator-b3 -opentelemetry-propagator-jaeger +#opentelemetry-distro +#opentelemetry-exporter-otlp +#opentelemetry-propagator-b3 +#opentelemetry-propagator-jaeger From b85856e5a47513a6409ffd1348f9d93d90a3d64b Mon Sep 17 00:00:00 2001 From: Ephraim <ephraim@sefaria.org> Date: Tue, 19 Dec 2023 10:10:06 +0200 Subject: [PATCH 638/756] test: Fix broken test on Baal Haturim the index record was renamed to Ba'al Haturim --- sefaria/tests/texts_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sefaria/tests/texts_test.py b/sefaria/tests/texts_test.py index 4273bc465e..ce1b2eee90 100644 --- a/sefaria/tests/texts_test.py +++ b/sefaria/tests/texts_test.py @@ -26,7 +26,7 @@ def test_rename_category(): def test_get_commentary_texts_list(): l = tm.library.get_dependant_indices() - assert 'Baal HaTurim on Genesis' in l + assert "Ba'al HaTurim on Genesis" in l assert 'Bartenura on Mishnah Eduyot' in l assert 'Tosafot on Pesachim' in l From d7c83b8440ff1613b93972986a7c1d5e3205d178 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Tue, 19 Dec 2023 13:48:39 +0200 Subject: [PATCH 639/756] fix(translations): add 1 to maxHeight for cases where for some reason scrollHeight is bigger by 1. --- static/js/VersionBlockHeader.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/VersionBlockHeader.jsx b/static/js/VersionBlockHeader.jsx index 2b4207d3ce..31ce43baa7 100644 --- a/static/js/VersionBlockHeader.jsx +++ b/static/js/VersionBlockHeader.jsx @@ -47,7 +47,7 @@ function VersionBlockHeaderText({link, onClick, text, direction}) { const element = textRef.current; const computedStyles = window.getComputedStyle(element); const maxHeight = parseInt(computedStyles.getPropertyValue('max-height'), 10); - setTruncationOccurred(element.scrollHeight > maxHeight); + setTruncationOccurred(element.scrollHeight > maxHeight+1); //added +1 because for some reason when the view is too big the height has 1 more }); //no second param for running in resize seems better than adding a listener function onEllipsisClick() { setShouldAttemptTruncation(false); From 37eef42373dda78b5533519f2cdb12ede428f468 Mon Sep 17 00:00:00 2001 From: yonadavGit <yonadav.le@gmail.com> Date: Tue, 19 Dec 2023 16:49:38 +0200 Subject: [PATCH 640/756] fix(copyFromReader): first approach for fixing - remove spans from text segment. recognize spans to remove through their ancestors --- static/js/ReaderApp.jsx | 83 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index e372ced53e..20e1c0731f 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -1897,6 +1897,86 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { poetryElsToCollapse.forEach(poetryEl => { poetryEl.outerHTML = poetryEl.innerHTML; }); + // function removeClassesFromSpans(element) { + // // Remove classes from the current element + // if (element.tagName === 'SPAN') { + // element.className = ''; + // } + // // Recursively process child elements + // for (let i = 0; i < element.children.length; i++) { + // removeClassesFromSpans(element.children[i]); + // } + // } + // function removeSpansKeepText(element, classListToRemove) { + // // Iterate over the child nodes in reverse order to handle removal + // for (let i = element.children.length - 1; i >= 0; i--) { + // const child = element.children[i]; + // + // // Check if the child is a span + // if (child.tagName === 'SPAN' && classListToRemove.includes(child.className)) { + // // Replace the span with its text content + // const textNode = document.createTextNode(child.textContent); + // element.replaceChild(textNode, child); + // } else { + // // Recursively process non-span child elements + // removeSpansKeepText(child, classListToRemove); + // } + // } + // } + // removeSpansKeepText(container, ["mam-kq-trivial"]); + function removeSpansWithAncestorP(element) { + // Base case: If the current element is a text node, return its text content + if (element.nodeType === Node.TEXT_NODE) { + return element.textContent; + } + + // Check if the current element is a <span> and has a <p> ancestor + if (element.nodeName === 'SPAN' && hasAncestorP(element)) { + // Replace the <span> with its text content + return element.textContent; + } + + // Recursively process child nodes + for (let i = 0; i < element.childNodes.length; i++) { + const childResult = removeSpansWithAncestorP(element.childNodes[i]); + + // Replace the child node with its processed content + if (childResult) { + if (element.childNodes[i].nodeType === Node.TEXT_NODE) { + element.replaceChild(document.createTextNode(childResult), element.childNodes[i]); + } else { + element.replaceChild(createFragmentFromHTML(childResult), element.childNodes[i]); + } + } + } + + // Return the processed content of the current element + return element.outerHTML; + } + + function hasAncestorP(element) { + let ancestor = element.parentNode; + while (ancestor) { + if (ancestor.nodeName === 'P') { + return true; + } + ancestor = ancestor.parentNode; + } + return false; + } + + function createFragmentFromHTML(htmlString) { + const fragment = document.createDocumentFragment(); + const tempDiv = document.createElement('div'); + tempDiv.innerHTML = htmlString; + + while (tempDiv.firstChild) { + fragment.appendChild(tempDiv.firstChild); + } + + return fragment; + } + removeSpansWithAncestorP(container) // Remove extra breaks for continuous mode if (closestReaderPanel && closestReaderPanel.classList.contains('continuous')) { @@ -1940,6 +2020,9 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { html = container.outerHTML; textOnly = Sefaria.util.htmlToText(html); selectedEls = container; + console.log(container) + console.log(html) + console.log(textOnly) } From b34f86dcd5cf040379013aed063cbb125ce48331 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 20 Dec 2023 09:06:28 +0200 Subject: [PATCH 641/756] helm: remove superfluous double spaces! --- .../templates/configmap/varnish-config.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/helm-chart/sefaria-project/templates/configmap/varnish-config.yaml b/helm-chart/sefaria-project/templates/configmap/varnish-config.yaml index c293c3963e..1959feefb2 100644 --- a/helm-chart/sefaria-project/templates/configmap/varnish-config.yaml +++ b/helm-chart/sefaria-project/templates/configmap/varnish-config.yaml @@ -30,16 +30,16 @@ data: return (pass); } if (req.http.User-Agent ~ "MegaIndex") { - return(synth(403, "Your spider is not honoring our robots.txt crawl-delay. Banning.")); + return(synth(403, "Your spider is not honoring our robots.txt crawl-delay. Banning.")); } if (req.http.User-Agent ~ "^Sogou") { - return(synth(403, "Your spider has put too much load on our server. Banning.")); + return(synth(403, "Your spider has put too much load on our server. Banning.")); } if (req.http.User-Agent ~ "SemrushBot") { - return(synth(403, "Your spider has put too much load on our server. Banning.")); + return(synth(403, "Your spider has put too much load on our server. Banning.")); } if (req.http.User-Agent ~ "YandexBot") { - return(synth(403, "Your spider has put too much load on our server. Banning.")); + return(synth(403, "Your spider has put too much load on our server. Banning.")); } if ((req.method == "GET") && ( From 9bf885e268936800d6db38955ee4355dbebbf12c Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Wed, 20 Dec 2023 11:18:32 +0200 Subject: [PATCH 642/756] refactor(translation): move functions to the root of sefaria.js --- static/js/sefaria/sefaria.js | 38 ++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index 558b4dd938..16d4e456dd 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -487,29 +487,29 @@ Sefaria = extend(Sefaria, { return Promise.all(promises).then(results => Object.assign({}, ...results)); }, - getTextsFromAPIV3: async function(ref, requiredVersions, mergeText) { - // ref is segment ref or bottom level section ref - // requiredVersions is array of objects that can have language and versionTitle - function makeParamsString(language, versionTitle) { - if (versionTitle) { + makeParamsStringForAPIV3: function(language, versionTitle) { + if (versionTitle) { return `${language}|${versionTitle}`; - } else if (language) { + } else if (language) { return language; - } - } - function makeUrl() { - const host = Sefaria.apiHost; - const endPoint = '/api/v3/texts/' - const versions = requiredVersions.map(obj => - makeParamsString(obj.language, obj.versionTitle) - ); - const url = `${host}${endPoint}${ref}?version=${versions.join('&version=')}&fill_in_missing_segments=${mergeText}`; - return url; } - const url = makeUrl(ref, requiredVersions); - //here's the place for getting it from cache + }, + makeUrlForAPIV3Text: function(ref, requiredVersions, mergeText) { + const host = Sefaria.apiHost; + const endPoint = '/api/v3/texts/'; + const versions = requiredVersions.map(obj => + Sefaria.makeParamsStringForAPIV3(obj.language, obj.versionTitle) + ); + const url = `${host}${endPoint}${ref}?version=${versions.join('&version=')}&fill_in_missing_segments=${mergeText}`; + return url; + }, + getTextsFromAPIV3: async function(ref, requiredVersions, mergeText) { + // ref is segment ref or bottom level section ref + // requiredVersions is array of objects that can have language and versionTitle + const url = Sefaria.makeUrlForAPIV3Text(ref, requiredVersions, mergeText); + //TODO here's the place for getting it from cache const apiObject = await Sefaria._ApiPromise(url); - //here's the place for all changes we want to add, and saving in cache + //TODO here's the place for all changes we want to add, and saving in cache return apiObject; }, getAllTranslationsWithText: async function(ref) { From 873aeef40d2f916f17bf710f9c8a88197cb89683 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Wed, 20 Dec 2023 11:36:32 +0200 Subject: [PATCH 643/756] refactor(translation): rename versionTools to VersionBlockUtils --- static/js/VersionBlock.jsx | 16 ++++++++-------- static/js/VersionBlockWithPreview.jsx | 6 +++--- static/js/VersionBlockWithPreviewTitleLine.jsx | 6 +++--- static/js/VersionDetailsImage.jsx | 4 ++-- static/js/VersionDetailsInformation.jsx | 12 ++++++------ static/js/VersionPreviewMeta.jsx | 4 ++-- 6 files changed, 24 insertions(+), 24 deletions(-) diff --git a/static/js/VersionBlock.jsx b/static/js/VersionBlock.jsx index 1e4164925d..969c6cd2c3 100644 --- a/static/js/VersionBlock.jsx +++ b/static/js/VersionBlock.jsx @@ -12,7 +12,7 @@ import VersionDetailsInformation from "./VersionDetailsInformation"; import VersionDetailsImage from "./VersionDetailsImage"; import VersionBlockWithPreview from "./VersionBlockWithPreview"; -class versionTools { +class VersionBlockUtils { static makeVersionTitle(version){ if (version.merged) { return {"className" : "", "text": Sefaria._("Merged from") + " " + Array.from(new Set(version.sources)).join(", ")}; @@ -198,12 +198,12 @@ class VersionBlock extends Component { render() { if(this.props.version.title == "Sheet") return null //why are we even getting here in such a case??; const v = this.props.version; - const vtitle = versionTools.makeVersionTitle(v); + const vtitle = VersionBlockUtils.makeVersionTitle(v); const vnotes = this.makeVersionNotes(); const showLanguagLabel = this.props.rendermode == "book-page"; - const openVersionInSidebar = versionTools.openVersionInSidebar.bind(null, this.props.currentRef, this.props.version, + const openVersionInSidebar = VersionBlockUtils.openVersionInSidebar.bind(null, this.props.currentRef, this.props.version, this.props.currObjectVersions, this.props.openVersionInSidebar); - const openVersionInMainPanel = versionTools.openVersionInMainPanel.bind(null, this.props.currentRef, + const openVersionInMainPanel = VersionBlockUtils.openVersionInMainPanel.bind(null, this.props.currentRef, this.props.version, this.props.currObjectVersions, this.props.rendermode, this.props.firstSectionRef, this.props.openVersionInReader); if (this.state.editing && Sefaria.is_moderator) { @@ -278,7 +278,7 @@ class VersionBlock extends Component { text={vtitle["text"]} onClick={this.props.rendermode === 'book-page' ? openVersionInMainPanel : openVersionInSidebar} renderMode='versionTitle' - link={versionTools.makeVersionLink(this.props.currentRef, this.props.version, + link={VersionBlockUtils.makeVersionLink(this.props.currentRef, this.props.version, this.props.currObjectVersions, this.props.rendermode === 'book-page')} /> </div> @@ -290,11 +290,11 @@ class VersionBlock extends Component { isSelected={this.props.isCurrent} openVersionInMainPanel={openVersionInMainPanel} text={this.makeSelectVersionLanguage()} - link={versionTools.makeVersionLink(this.props.currentRef, this.props.version, + link={VersionBlockUtils.makeVersionLink(this.props.currentRef, this.props.version, this.props.currObjectVersions, true)} /> </div> - <div className={classNames(versionTools.makeAttrClassNames(v, {"versionNotes": 1, "sans-serif": (this.props.rendermode == "book-page")}, "versionNotes", true))}> + <div className={classNames(VersionBlockUtils.makeAttrClassNames(v, {"versionNotes": 1, "sans-serif": (this.props.rendermode == "book-page")}, "versionNotes", true))}> <span className="" dangerouslySetInnerHTML={ {__html: vnotes} } /> <span className={`versionExtendedNotesLinks ${this.hasExtendedNotes() ? "": "n-a"}`}> <a onClick={this.openExtendedNotes} href={`/${this.props.version.title}/${this.props.version.language}/${this.props.version.versionTitle}/notes`}> @@ -459,4 +459,4 @@ VersionsBlocksList.defaultProps = { -export {VersionBlock as default, VersionsBlocksList, versionTools}; +export {VersionBlock as default, VersionsBlocksList, VersionBlockUtils}; diff --git a/static/js/VersionBlockWithPreview.jsx b/static/js/VersionBlockWithPreview.jsx index bf2d8b2395..7af8326bb7 100644 --- a/static/js/VersionBlockWithPreview.jsx +++ b/static/js/VersionBlockWithPreview.jsx @@ -1,13 +1,13 @@ import React, {useEffect, useRef, useState} from 'react'; import PropTypes from 'prop-types'; import VersionBlockHeader from "./VersionBlockHeader"; -import {versionTools} from './VersionBlock'; +import {VersionBlockUtils} from './VersionBlock'; import VersionBlockWithPreviewTitleLine from './VersionBlockWithPreviewTitleLine'; import VersionPreviewMeta from "./VersionPreviewMeta"; import {OpenConnectionTabButton} from "./TextList"; function VersionBlockWithPreview({currentRef, version, currObjectVersions, openVersionInSidebar, openVersionInReader, isSelected, srefs, onRangeClick}) { - const opeInSidebar = versionTools.openVersionInSidebar.bind(null, currentRef, version, currObjectVersions, openVersionInSidebar); + const opeInSidebar = VersionBlockUtils.openVersionInSidebar.bind(null, currentRef, version, currObjectVersions, openVersionInSidebar); function openInTabCallback(sref) { onRangeClick(sref, false, {[version.language]: version.versionTitle}); } @@ -17,7 +17,7 @@ function VersionBlockWithPreview({currentRef, version, currObjectVersions, openV text={version.text} onClick={opeInSidebar} renderMode='contentText' - link={versionTools.makeVersionLink(currentRef, version, currObjectVersions, false)} + link={VersionBlockUtils.makeVersionLink(currentRef, version, currObjectVersions, false)} direction={version.direction || 'ltr'} /> <details> diff --git a/static/js/VersionBlockWithPreviewTitleLine.jsx b/static/js/VersionBlockWithPreviewTitleLine.jsx index 8c217310f7..9d39ee7935 100644 --- a/static/js/VersionBlockWithPreviewTitleLine.jsx +++ b/static/js/VersionBlockWithPreviewTitleLine.jsx @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import VersionBlockSelectButton from "./VersionBlockSelectButton"; -import {versionTools} from './VersionBlock'; +import {VersionBlockUtils} from './VersionBlock'; import Sefaria from "./sefaria/sefaria"; function VersionBlockWithPreviewTitleLine({currentRef, version, currObjectVersions, openVersionInReader, isSelected}) { @@ -12,7 +12,7 @@ function VersionBlockWithPreviewTitleLine({currentRef, version, currObjectVersio } return shortVersionTitle; } - const openVersionInMainPanel = versionTools.openVersionInMainPanel.bind(null, currentRef, version, currObjectVersions, 'select-button', + const openVersionInMainPanel = VersionBlockUtils.openVersionInMainPanel.bind(null, currentRef, version, currObjectVersions, 'select-button', null, openVersionInReader); const buttonText = isSelected ? 'Currently Selected' : 'Select'; return ( @@ -24,7 +24,7 @@ function VersionBlockWithPreviewTitleLine({currentRef, version, currObjectVersio isSelected={isSelected} openVersionInMainPanel={openVersionInMainPanel} text={buttonText} - link={versionTools.makeVersionLink(currentRef, version, currObjectVersions, true)} + link={VersionBlockUtils.makeVersionLink(currentRef, version, currObjectVersions, true)} /> </div> ); diff --git a/static/js/VersionDetailsImage.jsx b/static/js/VersionDetailsImage.jsx index d68b98e801..0e06692609 100644 --- a/static/js/VersionDetailsImage.jsx +++ b/static/js/VersionDetailsImage.jsx @@ -2,7 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import classNames from "classnames"; import Sefaria from "./sefaria/sefaria"; -import {versionTools} from "./VersionBlock"; +import {VersionBlockUtils} from "./VersionBlock"; function VersionDetailsImage({version}) { function makeImageLink() { @@ -13,7 +13,7 @@ function VersionDetailsImage({version}) { } return ( <div className="versionDetailsImage"> - <div className={classNames(versionTools.makeAttrClassNames(version, {"versionBuyImage": 1, "versionDetailsElement": 1} , "purchaseInformationImage"))}> + <div className={classNames(VersionBlockUtils.makeAttrClassNames(version, {"versionBuyImage": 1, "versionDetailsElement": 1} , "purchaseInformationImage"))}> <a className="versionDetailsLink versionDetailsImageLink" href={makeImageLink()} target="_blank"> <img className="versionImage" src={makeImageSrc()} alt={Sefaria._("Buy Now")} /> </a> diff --git a/static/js/VersionDetailsInformation.jsx b/static/js/VersionDetailsInformation.jsx index b2cba54de9..7814979727 100644 --- a/static/js/VersionDetailsInformation.jsx +++ b/static/js/VersionDetailsInformation.jsx @@ -2,7 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import classNames from "classnames"; import Sefaria from "./sefaria/sefaria"; -import {versionTools} from './VersionBlock'; +import {VersionBlockUtils} from './VersionBlock'; function VersionDetailsInformation({currentRef, version}) { function makeLicenseLink() { @@ -11,7 +11,7 @@ function VersionDetailsInformation({currentRef, version}) { } return ( <div className="versionDetailsInformation"> - <div className={classNames(versionTools.makeAttrClassNames(version, {"versionSource": 1, "versionDetailsElement": 1}, "versionSource"))}> + <div className={classNames(VersionBlockUtils.makeAttrClassNames(version, {"versionSource": 1, "versionDetailsElement": 1}, "versionSource"))}> <span className="versionDetailsLabel"> {`${Sefaria._("Source")}: `} </span> @@ -19,7 +19,7 @@ function VersionDetailsInformation({currentRef, version}) { { Sefaria.util.parseUrl(version.versionSource).host.replace("www.", "") } </a> </div> - <div className={classNames(versionTools.makeAttrClassNames(version, {"versionDigitizedBySefaria": 1, "versionDetailsElement": 1}, "digitizedBySefaria"))}> + <div className={classNames(VersionBlockUtils.makeAttrClassNames(version, {"versionDigitizedBySefaria": 1, "versionDetailsElement": 1}, "digitizedBySefaria"))}> <span className="versionDetailsLabel"> {`${Sefaria._("Digitization")}: `} < /span> @@ -27,7 +27,7 @@ function VersionDetailsInformation({currentRef, version}) { {Sefaria._("Sefaria")} </a> </div> - <div className={classNames(versionTools.makeAttrClassNames(version, {"versionLicense": 1, "versionDetailsElement": 1}, "license" ))}> + <div className={classNames(VersionBlockUtils.makeAttrClassNames(version, {"versionLicense": 1, "versionDetailsElement": 1}, "license" ))}> <span className="versionDetailsLabel"> {`${Sefaria._("License")}: `} </span> @@ -35,12 +35,12 @@ function VersionDetailsInformation({currentRef, version}) { {Sefaria._(version?.license)} </a> </div> - <div className={classNames(versionTools.makeAttrClassNames(version, {"versionHistoryLink": 1, "versionDetailsElement": 1}, null))}> + <div className={classNames(VersionBlockUtils.makeAttrClassNames(version, {"versionHistoryLink": 1, "versionDetailsElement": 1}, null))}> <a className="versionDetailsLink" href={`/activity/${Sefaria.normRef(currentRef)}/${version.language}/${version.versionTitle && version.versionTitle.replace(/\s/g,"_")}`} target="_blank"> {Sefaria._("Revision History")} </a> </div> - <div className={classNames(versionTools.makeAttrClassNames(version, {"versionBuyLink": 1, "versionDetailsElement": 1}, "purchaseInformationURL"))}> + <div className={classNames(VersionBlockUtils.makeAttrClassNames(version, {"versionBuyLink": 1, "versionDetailsElement": 1}, "purchaseInformationURL"))}> <a className="versionDetailsLink" href={version.purchaseInformationURL} target="_blank"> {Sefaria._("Buy in Print")} </a> diff --git a/static/js/VersionPreviewMeta.jsx b/static/js/VersionPreviewMeta.jsx index 2d52d197d8..76e0c1d2e1 100644 --- a/static/js/VersionPreviewMeta.jsx +++ b/static/js/VersionPreviewMeta.jsx @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import {versionTools} from "./VersionBlock"; +import {VersionBlockUtils} from "./VersionBlock"; import VersionDetailsInformation from "./VersionDetailsInformation"; import VersionDetailsImage from "./VersionDetailsImage"; @@ -8,7 +8,7 @@ function VersionPreviewMeta({currentRef, version}) { return ( <div className='versionDetails preview'> <div className='version-preview-informations'> - <div className='versionDetails-version-title'>{versionTools.makeVersionTitle(version).text}</div> + <div className='versionDetails-version-title'>{VersionBlockUtils.makeVersionTitle(version).text}</div> <VersionDetailsInformation currentRef={currentRef} version={version}/> </div> <VersionDetailsImage version={version}/> From 9f9da158348200ea99cd50ba6396c57bc2f900d3 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Wed, 20 Dec 2023 11:42:45 +0200 Subject: [PATCH 644/756] refactor(translation): use openStrings in OpenConnectionTabButton rather than renderMode --- static/js/TextList.jsx | 4 ++-- static/js/VersionBlockWithPreview.jsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/static/js/TextList.jsx b/static/js/TextList.jsx index 92c42638dc..9055fafc9d 100644 --- a/static/js/TextList.jsx +++ b/static/js/TextList.jsx @@ -312,13 +312,13 @@ const DeleteConnectionButton = ({delUrl, connectionDeleteCallback}) =>{ } -const OpenConnectionTabButton = ({srefs, openInTabCallback, renderMode}) =>{ +const OpenConnectionTabButton = ({srefs, openInTabCallback, openStrings}) =>{ /* ConnectionButton composite element. Goes inside a ConnectionButtons Takes a ref(s) for opening as a link and callback for opening in-app */ const sref = Array.isArray(srefs) ? Sefaria.normRefList(srefs) : srefs; - const [en, he] = renderMode === 'versionPreview' ? ['Open Text', 'פתיחת טקסט'] : ['Open', 'פתיחה']; + const [en, he] = openStrings || ['Open', 'פתיחה']; const openLinkInTab = (event) => { if (openInTabCallback) { event.preventDefault(); diff --git a/static/js/VersionBlockWithPreview.jsx b/static/js/VersionBlockWithPreview.jsx index 7af8326bb7..49f744bfa1 100644 --- a/static/js/VersionBlockWithPreview.jsx +++ b/static/js/VersionBlockWithPreview.jsx @@ -38,7 +38,7 @@ function VersionBlockWithPreview({currentRef, version, currObjectVersions, openV <OpenConnectionTabButton srefs={srefs} openInTabCallback={openInTabCallback} - renderMode='versionPreview' + openStrings={['Open Text', 'פתיחת טקסט']} /> </div> </details> From 59733e080bf2bff69a75b01a33205bf7e812e97c Mon Sep 17 00:00:00 2001 From: yonadavGit <yonadav.le@gmail.com> Date: Wed, 20 Dec 2023 13:02:39 +0200 Subject: [PATCH 645/756] fix(copyFromReader): second and cleaner approach - remove all spans that are not "rangeSpan" from copied selection --- static/js/ReaderApp.jsx | 83 ++--------------------------------------- 1 file changed, 3 insertions(+), 80 deletions(-) diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index 20e1c0731f..8f55710f34 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -1897,86 +1897,6 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { poetryElsToCollapse.forEach(poetryEl => { poetryEl.outerHTML = poetryEl.innerHTML; }); - // function removeClassesFromSpans(element) { - // // Remove classes from the current element - // if (element.tagName === 'SPAN') { - // element.className = ''; - // } - // // Recursively process child elements - // for (let i = 0; i < element.children.length; i++) { - // removeClassesFromSpans(element.children[i]); - // } - // } - // function removeSpansKeepText(element, classListToRemove) { - // // Iterate over the child nodes in reverse order to handle removal - // for (let i = element.children.length - 1; i >= 0; i--) { - // const child = element.children[i]; - // - // // Check if the child is a span - // if (child.tagName === 'SPAN' && classListToRemove.includes(child.className)) { - // // Replace the span with its text content - // const textNode = document.createTextNode(child.textContent); - // element.replaceChild(textNode, child); - // } else { - // // Recursively process non-span child elements - // removeSpansKeepText(child, classListToRemove); - // } - // } - // } - // removeSpansKeepText(container, ["mam-kq-trivial"]); - function removeSpansWithAncestorP(element) { - // Base case: If the current element is a text node, return its text content - if (element.nodeType === Node.TEXT_NODE) { - return element.textContent; - } - - // Check if the current element is a <span> and has a <p> ancestor - if (element.nodeName === 'SPAN' && hasAncestorP(element)) { - // Replace the <span> with its text content - return element.textContent; - } - - // Recursively process child nodes - for (let i = 0; i < element.childNodes.length; i++) { - const childResult = removeSpansWithAncestorP(element.childNodes[i]); - - // Replace the child node with its processed content - if (childResult) { - if (element.childNodes[i].nodeType === Node.TEXT_NODE) { - element.replaceChild(document.createTextNode(childResult), element.childNodes[i]); - } else { - element.replaceChild(createFragmentFromHTML(childResult), element.childNodes[i]); - } - } - } - - // Return the processed content of the current element - return element.outerHTML; - } - - function hasAncestorP(element) { - let ancestor = element.parentNode; - while (ancestor) { - if (ancestor.nodeName === 'P') { - return true; - } - ancestor = ancestor.parentNode; - } - return false; - } - - function createFragmentFromHTML(htmlString) { - const fragment = document.createDocumentFragment(); - const tempDiv = document.createElement('div'); - tempDiv.innerHTML = htmlString; - - while (tempDiv.firstChild) { - fragment.appendChild(tempDiv.firstChild); - } - - return fragment; - } - removeSpansWithAncestorP(container) // Remove extra breaks for continuous mode if (closestReaderPanel && closestReaderPanel.classList.contains('continuous')) { @@ -2017,6 +1937,9 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { let elsToStrip = container.querySelectorAll(linksToStrip); elsToStrip.forEach(el => el.outerHTML = el.innerText); + + let SourceTextSpans = container.querySelectorAll('span:not(.rangeSpan)'); + SourceTextSpans.forEach(el => el.outerHTML = el.innerText); html = container.outerHTML; textOnly = Sefaria.util.htmlToText(html); selectedEls = container; From 92bc445d2490951ef1d660f903d1fbc65590ce0d Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Wed, 20 Dec 2023 14:18:18 +0200 Subject: [PATCH 646/756] refactor(translation): new manes for components --- static/js/VersionBlock.jsx | 8 ++++---- static/js/VersionBlockWithPreview.jsx | 8 ++++---- .../{VersionDetailsImage.jsx => VersionImage.jsx} | 6 +++--- ...tailsInformation.jsx => VersionInformation.jsx} | 6 +++--- ...{VersionPreviewMeta.jsx => VersionMetadata.jsx} | 14 +++++++------- ...ewTitleLine.jsx => VersionTitleAndSelector.jsx} | 6 +++--- 6 files changed, 24 insertions(+), 24 deletions(-) rename static/js/{VersionDetailsImage.jsx => VersionImage.jsx} (89%) rename static/js/{VersionDetailsInformation.jsx => VersionInformation.jsx} (95%) rename static/js/{VersionPreviewMeta.jsx => VersionMetadata.jsx} (55%) rename static/js/{VersionBlockWithPreviewTitleLine.jsx => VersionTitleAndSelector.jsx} (87%) diff --git a/static/js/VersionBlock.jsx b/static/js/VersionBlock.jsx index 969c6cd2c3..e2f1ab7c06 100644 --- a/static/js/VersionBlock.jsx +++ b/static/js/VersionBlock.jsx @@ -8,8 +8,8 @@ import Component from 'react-class'; import {LoadingMessage} from "./Misc"; import VersionBlockHeader from "./VersionBlockHeader"; import VersionBlockSelectButton from "./VersionBlockSelectButton"; -import VersionDetailsInformation from "./VersionDetailsInformation"; -import VersionDetailsImage from "./VersionDetailsImage"; +import VersionInformation from "./VersionInformation"; +import VersionImage from "./VersionImage"; import VersionBlockWithPreview from "./VersionBlockWithPreview"; class VersionBlockUtils { @@ -304,8 +304,8 @@ class VersionBlock extends Component { </div> { !v.merged ? <div className="versionDetails sans-serif"> - <VersionDetailsInformation currentRef={this.props.currentRef} version={v}/> - <VersionDetailsImage version={v}/> + <VersionInformation currentRef={this.props.currentRef} version={v}/> + <VersionImage version={v}/> </div> : null } </div> diff --git a/static/js/VersionBlockWithPreview.jsx b/static/js/VersionBlockWithPreview.jsx index 49f744bfa1..b88319e52b 100644 --- a/static/js/VersionBlockWithPreview.jsx +++ b/static/js/VersionBlockWithPreview.jsx @@ -2,8 +2,8 @@ import React, {useEffect, useRef, useState} from 'react'; import PropTypes from 'prop-types'; import VersionBlockHeader from "./VersionBlockHeader"; import {VersionBlockUtils} from './VersionBlock'; -import VersionBlockWithPreviewTitleLine from './VersionBlockWithPreviewTitleLine'; -import VersionPreviewMeta from "./VersionPreviewMeta"; +import VersionTitleAndSelector from './VersionTitleAndSelector'; +import VersionMetadata from "./VersionMetadata"; import {OpenConnectionTabButton} from "./TextList"; function VersionBlockWithPreview({currentRef, version, currObjectVersions, openVersionInSidebar, openVersionInReader, isSelected, srefs, onRangeClick}) { @@ -22,7 +22,7 @@ function VersionBlockWithPreview({currentRef, version, currObjectVersions, openV /> <details> <summary> - <VersionBlockWithPreviewTitleLine + <VersionTitleAndSelector version={version} currentRef={currentRef} currObjectVersions={currObjectVersions} @@ -31,7 +31,7 @@ function VersionBlockWithPreview({currentRef, version, currObjectVersions, openV /> </summary> <div className='version-block-with-preview-details'> - <VersionPreviewMeta + <VersionMetadata currentRef={currentRef} version={version} /> diff --git a/static/js/VersionDetailsImage.jsx b/static/js/VersionImage.jsx similarity index 89% rename from static/js/VersionDetailsImage.jsx rename to static/js/VersionImage.jsx index 0e06692609..1eade3b86e 100644 --- a/static/js/VersionDetailsImage.jsx +++ b/static/js/VersionImage.jsx @@ -4,7 +4,7 @@ import classNames from "classnames"; import Sefaria from "./sefaria/sefaria"; import {VersionBlockUtils} from "./VersionBlock"; -function VersionDetailsImage({version}) { +function VersionImage({version}) { function makeImageLink() { return !!version.purchaseInformationURL ? version.purchaseInformationURL : version.versionSource; } @@ -21,7 +21,7 @@ function VersionDetailsImage({version}) { </div> ) } -VersionDetailsImage.prototypes = { +VersionImage.prototypes = { version: PropTypes.object.isRequired, }; -export default VersionDetailsImage; +export default VersionImage; diff --git a/static/js/VersionDetailsInformation.jsx b/static/js/VersionInformation.jsx similarity index 95% rename from static/js/VersionDetailsInformation.jsx rename to static/js/VersionInformation.jsx index 7814979727..7222fbf8de 100644 --- a/static/js/VersionDetailsInformation.jsx +++ b/static/js/VersionInformation.jsx @@ -4,7 +4,7 @@ import classNames from "classnames"; import Sefaria from "./sefaria/sefaria"; import {VersionBlockUtils} from './VersionBlock'; -function VersionDetailsInformation({currentRef, version}) { +function VersionInformation({currentRef, version}) { function makeLicenseLink() { const license_map = Sefaria.getLicenseMap(); return (version.license in license_map) ? license_map[version.license] : "#"; @@ -48,8 +48,8 @@ function VersionDetailsInformation({currentRef, version}) { </div> ); } -VersionDetailsInformation.prototypes = { +VersionInformation.prototypes = { currentRef: PropTypes.string.isRequired, version: PropTypes.object.isRequired, }; -export default VersionDetailsInformation; +export default VersionInformation; diff --git a/static/js/VersionPreviewMeta.jsx b/static/js/VersionMetadata.jsx similarity index 55% rename from static/js/VersionPreviewMeta.jsx rename to static/js/VersionMetadata.jsx index 76e0c1d2e1..2226e4307f 100644 --- a/static/js/VersionPreviewMeta.jsx +++ b/static/js/VersionMetadata.jsx @@ -1,22 +1,22 @@ import React from 'react'; import PropTypes from 'prop-types'; import {VersionBlockUtils} from "./VersionBlock"; -import VersionDetailsInformation from "./VersionDetailsInformation"; -import VersionDetailsImage from "./VersionDetailsImage"; +import VersionInformation from "./VersionInformation"; +import VersionImage from "./VersionImage"; -function VersionPreviewMeta({currentRef, version}) { +function VersionMetadata({currentRef, version}) { return ( <div className='versionDetails preview'> <div className='version-preview-informations'> <div className='versionDetails-version-title'>{VersionBlockUtils.makeVersionTitle(version).text}</div> - <VersionDetailsInformation currentRef={currentRef} version={version}/> + <VersionInformation currentRef={currentRef} version={version}/> </div> - <VersionDetailsImage version={version}/> + <VersionImage version={version}/> </div> ); } -VersionPreviewMeta.prototypes = { +VersionMetadata.prototypes = { currentRef: PropTypes.string.isRequired, version: PropTypes.object.isRequired, }; -export default VersionPreviewMeta; +export default VersionMetadata; diff --git a/static/js/VersionBlockWithPreviewTitleLine.jsx b/static/js/VersionTitleAndSelector.jsx similarity index 87% rename from static/js/VersionBlockWithPreviewTitleLine.jsx rename to static/js/VersionTitleAndSelector.jsx index 9d39ee7935..907888c5ca 100644 --- a/static/js/VersionBlockWithPreviewTitleLine.jsx +++ b/static/js/VersionTitleAndSelector.jsx @@ -4,7 +4,7 @@ import VersionBlockSelectButton from "./VersionBlockSelectButton"; import {VersionBlockUtils} from './VersionBlock'; import Sefaria from "./sefaria/sefaria"; -function VersionBlockWithPreviewTitleLine({currentRef, version, currObjectVersions, openVersionInReader, isSelected}) { +function VersionTitleAndSelector({currentRef, version, currObjectVersions, openVersionInReader, isSelected}) { function makeShortVersionTitle() { let shortVersionTitle = version.shortVersionTitle || version.versionTitle; if (Sefaria.interfaceLang === "hebrew") { @@ -29,11 +29,11 @@ function VersionBlockWithPreviewTitleLine({currentRef, version, currObjectVersio </div> ); } -VersionBlockWithPreviewTitleLine.prototypes = { +VersionTitleAndSelector.prototypes = { currObjectVersions: PropTypes.object.isRequired, version: PropTypes.object.isRequired, currentRef: PropTypes.string.isRequired, openVersionInReader: PropTypes.func.isRequired, isSelected: PropTypes.bool.isRequired, }; -export default VersionBlockWithPreviewTitleLine; +export default VersionTitleAndSelector; From 79140ad8da8505611752c1323460cb626053c5b4 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Wed, 20 Dec 2023 14:23:39 +0200 Subject: [PATCH 647/756] refactor(translation): move all VersionBlock related components to a new folder --- static/js/AboutBox.jsx | 2 +- static/js/BookPage.jsx | 2 +- static/js/TranslationsBox.jsx | 2 +- static/js/{ => VersionBlock}/VersionBlock.jsx | 8 ++++---- static/js/{ => VersionBlock}/VersionBlockHeader.jsx | 0 static/js/{ => VersionBlock}/VersionBlockSelectButton.jsx | 0 static/js/{ => VersionBlock}/VersionBlockWithPreview.jsx | 2 +- static/js/{ => VersionBlock}/VersionImage.jsx | 2 +- static/js/{ => VersionBlock}/VersionInformation.jsx | 2 +- static/js/{ => VersionBlock}/VersionMetadata.jsx | 0 static/js/{ => VersionBlock}/VersionTitleAndSelector.jsx | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) rename static/js/{ => VersionBlock}/VersionBlock.jsx (99%) rename static/js/{ => VersionBlock}/VersionBlockHeader.jsx (100%) rename static/js/{ => VersionBlock}/VersionBlockSelectButton.jsx (100%) rename static/js/{ => VersionBlock}/VersionBlockWithPreview.jsx (97%) rename static/js/{ => VersionBlock}/VersionImage.jsx (96%) rename static/js/{ => VersionBlock}/VersionInformation.jsx (98%) rename static/js/{ => VersionBlock}/VersionMetadata.jsx (100%) rename static/js/{ => VersionBlock}/VersionTitleAndSelector.jsx (97%) diff --git a/static/js/AboutBox.jsx b/static/js/AboutBox.jsx index 2d7435a170..292ce5896c 100644 --- a/static/js/AboutBox.jsx +++ b/static/js/AboutBox.jsx @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import Sefaria from './sefaria/sefaria'; -import VersionBlock, {VersionsBlocksList} from './VersionBlock'; +import VersionBlock, {VersionsBlocksList} from './VersionBlock/VersionBlock'; import Component from 'react-class'; import {InterfaceText} from "./Misc"; import {ContentText} from "./ContentText"; diff --git a/static/js/BookPage.jsx b/static/js/BookPage.jsx index 6f21dc9187..5edd222026 100644 --- a/static/js/BookPage.jsx +++ b/static/js/BookPage.jsx @@ -21,7 +21,7 @@ import $ from './sefaria/sefariaJquery'; import Sefaria from './sefaria/sefaria'; import { NavSidebar, Modules } from './NavSidebar'; import DictionarySearch from './DictionarySearch'; -import VersionBlock from './VersionBlock'; +import VersionBlock from './VersionBlock/VersionBlock'; import ExtendedNotes from './ExtendedNotes'; import Footer from './Footer'; import classNames from 'classnames'; diff --git a/static/js/TranslationsBox.jsx b/static/js/TranslationsBox.jsx index 31bf7d8b5d..117145f759 100644 --- a/static/js/TranslationsBox.jsx +++ b/static/js/TranslationsBox.jsx @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import Sefaria from './sefaria/sefaria'; -import {VersionsBlocksList} from './VersionBlock'; +import {VersionsBlocksList} from './VersionBlock/VersionBlock'; import Component from 'react-class'; import {EnglishText, HebrewText, InterfaceText, LoadingMessage} from "./Misc"; import {RecentFilterSet} from "./ConnectionFilters"; diff --git a/static/js/VersionBlock.jsx b/static/js/VersionBlock/VersionBlock.jsx similarity index 99% rename from static/js/VersionBlock.jsx rename to static/js/VersionBlock/VersionBlock.jsx index e2f1ab7c06..4cf6828bba 100644 --- a/static/js/VersionBlock.jsx +++ b/static/js/VersionBlock/VersionBlock.jsx @@ -1,11 +1,11 @@ import React from 'react'; import PropTypes from 'prop-types'; import classNames from 'classnames'; -import Sefaria from './sefaria/sefaria'; -import Util from './sefaria/util'; -import $ from './sefaria/sefariaJquery'; +import Sefaria from '../sefaria/sefaria'; +import Util from '../sefaria/util'; +import $ from '../sefaria/sefariaJquery'; import Component from 'react-class'; -import {LoadingMessage} from "./Misc"; +import {LoadingMessage} from "../Misc"; import VersionBlockHeader from "./VersionBlockHeader"; import VersionBlockSelectButton from "./VersionBlockSelectButton"; import VersionInformation from "./VersionInformation"; diff --git a/static/js/VersionBlockHeader.jsx b/static/js/VersionBlock/VersionBlockHeader.jsx similarity index 100% rename from static/js/VersionBlockHeader.jsx rename to static/js/VersionBlock/VersionBlockHeader.jsx diff --git a/static/js/VersionBlockSelectButton.jsx b/static/js/VersionBlock/VersionBlockSelectButton.jsx similarity index 100% rename from static/js/VersionBlockSelectButton.jsx rename to static/js/VersionBlock/VersionBlockSelectButton.jsx diff --git a/static/js/VersionBlockWithPreview.jsx b/static/js/VersionBlock/VersionBlockWithPreview.jsx similarity index 97% rename from static/js/VersionBlockWithPreview.jsx rename to static/js/VersionBlock/VersionBlockWithPreview.jsx index b88319e52b..800880a737 100644 --- a/static/js/VersionBlockWithPreview.jsx +++ b/static/js/VersionBlock/VersionBlockWithPreview.jsx @@ -4,7 +4,7 @@ import VersionBlockHeader from "./VersionBlockHeader"; import {VersionBlockUtils} from './VersionBlock'; import VersionTitleAndSelector from './VersionTitleAndSelector'; import VersionMetadata from "./VersionMetadata"; -import {OpenConnectionTabButton} from "./TextList"; +import {OpenConnectionTabButton} from "../TextList"; function VersionBlockWithPreview({currentRef, version, currObjectVersions, openVersionInSidebar, openVersionInReader, isSelected, srefs, onRangeClick}) { const opeInSidebar = VersionBlockUtils.openVersionInSidebar.bind(null, currentRef, version, currObjectVersions, openVersionInSidebar); diff --git a/static/js/VersionImage.jsx b/static/js/VersionBlock/VersionImage.jsx similarity index 96% rename from static/js/VersionImage.jsx rename to static/js/VersionBlock/VersionImage.jsx index 1eade3b86e..33169df196 100644 --- a/static/js/VersionImage.jsx +++ b/static/js/VersionBlock/VersionImage.jsx @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import classNames from "classnames"; -import Sefaria from "./sefaria/sefaria"; +import Sefaria from "../sefaria/sefaria"; import {VersionBlockUtils} from "./VersionBlock"; function VersionImage({version}) { diff --git a/static/js/VersionInformation.jsx b/static/js/VersionBlock/VersionInformation.jsx similarity index 98% rename from static/js/VersionInformation.jsx rename to static/js/VersionBlock/VersionInformation.jsx index 7222fbf8de..6a48ed8ccf 100644 --- a/static/js/VersionInformation.jsx +++ b/static/js/VersionBlock/VersionInformation.jsx @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import classNames from "classnames"; -import Sefaria from "./sefaria/sefaria"; +import Sefaria from "../sefaria/sefaria"; import {VersionBlockUtils} from './VersionBlock'; function VersionInformation({currentRef, version}) { diff --git a/static/js/VersionMetadata.jsx b/static/js/VersionBlock/VersionMetadata.jsx similarity index 100% rename from static/js/VersionMetadata.jsx rename to static/js/VersionBlock/VersionMetadata.jsx diff --git a/static/js/VersionTitleAndSelector.jsx b/static/js/VersionBlock/VersionTitleAndSelector.jsx similarity index 97% rename from static/js/VersionTitleAndSelector.jsx rename to static/js/VersionBlock/VersionTitleAndSelector.jsx index 907888c5ca..2bcfb66028 100644 --- a/static/js/VersionTitleAndSelector.jsx +++ b/static/js/VersionBlock/VersionTitleAndSelector.jsx @@ -2,7 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import VersionBlockSelectButton from "./VersionBlockSelectButton"; import {VersionBlockUtils} from './VersionBlock'; -import Sefaria from "./sefaria/sefaria"; +import Sefaria from "../sefaria/sefaria"; function VersionTitleAndSelector({currentRef, version, currObjectVersions, openVersionInReader, isSelected}) { function makeShortVersionTitle() { From 8be051bc03f315931422e7b5011a5bbc3ce09cdc Mon Sep 17 00:00:00 2001 From: yonadavGit <yonadav.le@gmail.com> Date: Wed, 20 Dec 2023 15:31:23 +0200 Subject: [PATCH 648/756] fix(copyFromReader): clean code and remove debug prints --- static/js/ReaderApp.jsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index 8f55710f34..abd9c3f6fc 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -1938,14 +1938,13 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { elsToStrip.forEach(el => el.outerHTML = el.innerText); + // Collapse all spans that are not rangeSpan. This is also needed for specifically pasting into Google Docs in Chrome to work. let SourceTextSpans = container.querySelectorAll('span:not(.rangeSpan)'); SourceTextSpans.forEach(el => el.outerHTML = el.innerText); + html = container.outerHTML; textOnly = Sefaria.util.htmlToText(html); selectedEls = container; - console.log(container) - console.log(html) - console.log(textOnly) } From c5854993514318c7e955e53620fc82c86fd04078 Mon Sep 17 00:00:00 2001 From: yonadavGit <yonadav.le@gmail.com> Date: Thu, 21 Dec 2023 12:39:03 +0200 Subject: [PATCH 649/756] feat(AboutNavBar): add hebrew getting started video button --- static/js/NavSidebar.jsx | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/static/js/NavSidebar.jsx b/static/js/NavSidebar.jsx index 69976798fe..d3f9ce52cf 100644 --- a/static/js/NavSidebar.jsx +++ b/static/js/NavSidebar.jsx @@ -112,13 +112,24 @@ const AboutSefaria = ({hideTitle}) => ( <HebrewText>לקריאה נוספת ›</HebrewText> </InterfaceText> </a> - { Sefaria.interfaceLang === 'english' && !hideTitle && - <a className="button get-start" href="/sheets/210670"> - <img src="/static/icons/vector.svg"/> - <div className="get-start"> - Getting Started (2 min) - </div> - </a> + {!hideTitle && <InterfaceText> + <EnglishText> + <a className="button get-start" href="/sheets/210670"> + <img src="/static/icons/vector.svg"/> + <div className="get-start"> + Getting Started (2 min) + </div> + </a> + </EnglishText> + <HebrewText> + <a className="button get-start" href="https://youtu.be/eo5e8Z54h1Y"> + <img src="/static/icons/vector.svg"/> + <div className="get-start"> + הכירו את ספריא + </div> + </a> + </HebrewText> + </InterfaceText> } </Module> ); From 96b2f03d6bc784f1e4da4dfe1a9e7873a724de87 Mon Sep 17 00:00:00 2001 From: yonadavGit <yonadav.le@gmail.com> Date: Thu, 21 Dec 2023 12:55:05 +0200 Subject: [PATCH 650/756] feat(AboutNavBar): rotate vector img --- static/icons/vector-turning-left.svg | 5 +++++ static/js/NavSidebar.jsx | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 static/icons/vector-turning-left.svg diff --git a/static/icons/vector-turning-left.svg b/static/icons/vector-turning-left.svg new file mode 100644 index 0000000000..136c4325a1 --- /dev/null +++ b/static/icons/vector-turning-left.svg @@ -0,0 +1,5 @@ +<svg width="18" height="19" viewBox="0 0 18 19" fill="none" xmlns="http://www.w3.org/2000/svg"> + <g transform="rotate(-180 9 9.5)"> + <path d="M9 2C13.1355 2 16.5 5.3645 16.5 9.5C16.5 13.6355 13.1355 17 9 17C4.8645 17 1.5 13.6355 1.5 9.5C1.5 5.3645 4.8645 2 9 2ZM9 0.5C4.02975 0.5 0 4.52975 0 9.5C0 14.4703 4.02975 18.5 9 18.5C13.9703 18.5 18 14.4703 18 9.5C18 4.52975 13.9703 0.5 9 0.5ZM6.75 13.25V5.75L13.5 9.6095L6.75 13.25Z" fill="white"/> + </g> +</svg> diff --git a/static/js/NavSidebar.jsx b/static/js/NavSidebar.jsx index d3f9ce52cf..61a1f5c3d1 100644 --- a/static/js/NavSidebar.jsx +++ b/static/js/NavSidebar.jsx @@ -123,7 +123,7 @@ const AboutSefaria = ({hideTitle}) => ( </EnglishText> <HebrewText> <a className="button get-start" href="https://youtu.be/eo5e8Z54h1Y"> - <img src="/static/icons/vector.svg"/> + <img src="/static/icons/vector-turning-left.svg"/> <div className="get-start"> הכירו את ספריא </div> From abdd9f4a8f22c82ba4a7bddfcc8c56fc9cf78974 Mon Sep 17 00:00:00 2001 From: yonadavGit <yonadav.le@gmail.com> Date: Thu, 21 Dec 2023 13:27:49 +0200 Subject: [PATCH 651/756] feat(AboutNavBar): add video length to button text --- static/js/NavSidebar.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/NavSidebar.jsx b/static/js/NavSidebar.jsx index 61a1f5c3d1..cfbf20b8a0 100644 --- a/static/js/NavSidebar.jsx +++ b/static/js/NavSidebar.jsx @@ -125,7 +125,7 @@ const AboutSefaria = ({hideTitle}) => ( <a className="button get-start" href="https://youtu.be/eo5e8Z54h1Y"> <img src="/static/icons/vector-turning-left.svg"/> <div className="get-start"> - הכירו את ספריא + הכירו את ספריא (2 דק') </div> </a> </HebrewText> From fe4bb0b49933e5ceb11d9ce6cf1b538a8122886b Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Sun, 24 Dec 2023 11:45:18 +0200 Subject: [PATCH 652/756] doc(api-v3): doc strings for Version attributes. --- sefaria/model/text.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index 22c2e19199..26e486144b 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -1334,12 +1334,12 @@ class Version(AbstractTextRecord, abst.AbstractMongoRecord, AbstractSchemaConten "purchaseInformationImage", "purchaseInformationURL", "hasManuallyWrappedRefs", # true for texts where refs were manually wrapped in a-tags. no need to run linker at run-time. - "actualLanguage", - 'languageFamilyName', - "isBaseText", - 'isSource', - 'isPrimary', - 'direction', # 'rtl' or 'ltr' + "actualLanguage", # ISO language code + 'languageFamilyName', # full name of the language, but without specificity (for Judeo Arabic actualLanguage=jrb, languageFamilyName=arabic + "isBaseText", # should be deprecated (needs some changes on client side) + 'isSource', # bool, True if this version is not a translation + 'isPrimary', # bool, True if we see it as a primary version (usually equals to isSource, but Hebrew Kuzarif or example is primary but not source) + 'direction', # 'rtl' or 'ltr' ] def __str__(self): From 4e482c0708818c516d21d7526a4eda1f3210df74 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Sun, 24 Dec 2023 11:51:00 +0200 Subject: [PATCH 653/756] fix(api-v3): change 400 to 404 when ref is invalid or empty. --- api/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/views.py b/api/views.py index 982818902c..05e3b6c1d8 100644 --- a/api/views.py +++ b/api/views.py @@ -13,7 +13,7 @@ def dispatch(self, request, *args, **kwargs): try: self.oref = Ref.instantiate_ref_with_legacy_parse_fallback(kwargs['tref']) except Exception as e: - return jsonResponse({'error': getattr(e, 'message', str(e))}, status=400) + return jsonResponse({'error': getattr(e, 'message', str(e))}, status=404) return super().dispatch(request, *args, **kwargs) @staticmethod @@ -43,7 +43,7 @@ def _handle_warnings(self, data): def get(self, request, *args, **kwargs): if self.oref.is_empty() and not self.oref.index_node.is_virtual: - return jsonResponse({'error': f'We have no text for {self.oref}.'}, status=400) + return jsonResponse({'error': f'We have no text for {self.oref}.'}, status=404) versions_params = request.GET.getlist('version', []) if not versions_params: versions_params = ['primary'] From 201d8af85cb53e9e3aebee30373b530d4bd3af41 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Sun, 24 Dec 2023 11:53:50 +0200 Subject: [PATCH 654/756] refactor(api-v3): change TextManger to TextRequestAdapter. --- api/views.py | 4 ++-- sefaria/model/{text_manager.py => text_reuqest_adapter.py} | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename sefaria/model/{text_manager.py => text_reuqest_adapter.py} (99%) diff --git a/api/views.py b/api/views.py index 05e3b6c1d8..0e714da047 100644 --- a/api/views.py +++ b/api/views.py @@ -1,5 +1,5 @@ from sefaria.model import * -from sefaria.model.text_manager import TextManager +from sefaria.model.text_reuqest_adapter import TextRequestAdapter from sefaria.client.util import jsonResponse from django.views import View from .api_warnings import * @@ -52,7 +52,7 @@ def get(self, request, *args, **kwargs): return_format = request.GET.get('return_format', 'default') if return_format not in self.RETURN_FORMATS: return jsonResponse({'error': f'return_format should be one of those formats: {self.RETURN_FORMATS}.'}, status=400) - text_manager = TextManager(self.oref, versions_params, fill_in_missing_segments, return_format) + text_manager = TextRequestAdapter(self.oref, versions_params, fill_in_missing_segments, return_format) data = text_manager.get_versions_for_query() data = self._handle_warnings(data) return jsonResponse(data) diff --git a/sefaria/model/text_manager.py b/sefaria/model/text_reuqest_adapter.py similarity index 99% rename from sefaria/model/text_manager.py rename to sefaria/model/text_reuqest_adapter.py index f14a54bac8..e6963cf348 100644 --- a/sefaria/model/text_manager.py +++ b/sefaria/model/text_reuqest_adapter.py @@ -9,7 +9,7 @@ from sefaria.system.exceptions import InputError from sefaria.datatype.jagged_array import JaggedTextArray -class TextManager: +class TextRequestAdapter: ALL = 'all' PRIMARY = 'primary' SOURCE = 'source' From 2943990b858b5eb57776418684d07cbaa2fd481e Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Sun, 24 Dec 2023 11:58:02 +0200 Subject: [PATCH 655/756] doc(api-v3): doc string for TextRequestAdapter. --- sefaria/model/text_reuqest_adapter.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sefaria/model/text_reuqest_adapter.py b/sefaria/model/text_reuqest_adapter.py index e6963cf348..1bae459909 100644 --- a/sefaria/model/text_reuqest_adapter.py +++ b/sefaria/model/text_reuqest_adapter.py @@ -10,6 +10,11 @@ from sefaria.datatype.jagged_array import JaggedTextArray class TextRequestAdapter: + """ + This class is used for getting texts for client side (API or SSR) + It takes the same params as the api/v3/text (ref, version_params that are language and versionTitle, fill_in_missing_segments, and return_format + It returns a JSON-like object for an HTTP response. + """ ALL = 'all' PRIMARY = 'primary' SOURCE = 'source' From de5e59debbb065a6c5462275f684795e378708c9 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Sun, 24 Dec 2023 12:05:42 +0200 Subject: [PATCH 656/756] doc(api-v3): doc string for TextRange. --- sefaria/model/text.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index 26e486144b..56d596000c 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -1696,6 +1696,11 @@ def __call__(cls, *args, **kwargs): class TextRange: + """ + This class is planned to replace TextChunk, using real language rather than he/en + For now it's used by v3 texts api + It can be used for getting text, but not yet for saving + """ def __init__(self, oref, lang, vtitle, merge_versions=False): if isinstance(oref.index_node, JaggedArrayNode) or isinstance(oref.index_node, DictionaryEntryNode): #text cannot be SchemaNode From 01eb7d1067e4e58c58f4f5a5b30ea18fe2804d81 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Sun, 24 Dec 2023 15:20:33 +0200 Subject: [PATCH 657/756] refactor(api-v3): change versions to be a private param with a public setter. --- sefaria/model/text.py | 25 +++++++++++-------------- sefaria/model/text_reuqest_adapter.py | 4 ++-- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index 56d596000c..e72d3728da 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -1700,9 +1700,10 @@ class TextRange: This class is planned to replace TextChunk, using real language rather than he/en For now it's used by v3 texts api It can be used for getting text, but not yet for saving + The versions param is for better performance when the version(s) were already loaded from mongo """ - def __init__(self, oref, lang, vtitle, merge_versions=False): + def __init__(self, oref, lang, vtitle, merge_versions=False, versions=None): if isinstance(oref.index_node, JaggedArrayNode) or isinstance(oref.index_node, DictionaryEntryNode): #text cannot be SchemaNode self.oref = oref elif oref.has_default_child(): #use default child: @@ -1712,22 +1713,18 @@ def __init__(self, oref, lang, vtitle, merge_versions=False): self.lang = lang self.vtitle = vtitle self.merge_versions = merge_versions - self._versions = [] self._text = None self.sources = None + self._set_versions(versions) - @property - def versions(self): - if self._versions == []: + def _set_versions(self, versions): + if versions: + self._validate_versions(versions) + self._versions = versions + else: condition_query = self.oref.condition_query(self.lang) if self.merge_versions else \ {'title': self.oref.index.title, 'languageFamilyName': self.lang, 'versionTitle': self.vtitle} self._versions = VersionSet(condition_query, proj=self.oref.part_projection()) - return self._versions - - @versions.setter - def versions(self, versions): - self._validate_versions(versions) - self._versions = versions def _validate_versions(self, versions): if not self.merge_versions and len(versions) > 1: @@ -1758,15 +1755,15 @@ def _trim_text(self, text): @property def text(self): if self._text is None: - if self.merge_versions and len(self.versions) > 1: - merged_text, sources = self.versions.merge(self.oref.index_node, prioritized_vtitle=self.vtitle) + if self.merge_versions and len(self._versions) > 1: + merged_text, sources = self._versions.merge(self.oref.index_node, prioritized_vtitle=self.vtitle) self._text = self._trim_text(merged_text) if len(sources) > 1: self.sources = sources elif self.oref.index_node.is_virtual: self._text = self.oref.index_node.get_text() else: - self._text = self._trim_text(self.versions[0].content_node(self.oref.index_node)) #todo if there is no version it will fail + self._text = self._trim_text(self._versions[0].content_node(self.oref.index_node)) #todo if there is no version it will fail return self._text diff --git a/sefaria/model/text_reuqest_adapter.py b/sefaria/model/text_reuqest_adapter.py index 1bae459909..e47e5fd047 100644 --- a/sefaria/model/text_reuqest_adapter.py +++ b/sefaria/model/text_reuqest_adapter.py @@ -43,7 +43,6 @@ def _append_version(self, version): for attr in ['chapter', 'title']: fields.remove(attr) version_details = {f: getattr(version, f, "") for f in fields} - text_range = TextRange(self.oref, version.languageFamilyName, version.versionTitle, self.fill_in_missing_segments) if self.fill_in_missing_segments: # we need a new VersionSet of only the relevant versions for merging. copy should be better than calling for mongo @@ -51,7 +50,8 @@ def _append_version(self, version): relevant_versions.remove(lambda v: v.languageFamilyName != version.languageFamilyName) else: relevant_versions = [version] - text_range.versions = relevant_versions + text_range = TextRange(self.oref, version.languageFamilyName, version.versionTitle, + self.fill_in_missing_segments, relevant_versions) version_details['text'] = text_range.text sources = getattr(text_range, 'sources', None) From fc8ff4c974566baa604fb70962fc9eca7055c066 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Sun, 24 Dec 2023 15:21:11 +0200 Subject: [PATCH 658/756] test(api-v3): fix tests to fit the 400-404 change. --- api/tests.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/tests.py b/api/tests.py index 76709e1612..c3b0ffefba 100644 --- a/api/tests.py +++ b/api/tests.py @@ -119,7 +119,7 @@ def text_api_virtual_node(self): def test_api_get_text_bad_text(self): response = c.get('/api/v3/texts/Life_of_Pi.13.13') - self.assertEqual(400, response.status_code) + self.assertEqual(404, response.status_code) data = json.loads(response.content) self.assertEqual(data["error"], "Could not find title in reference: Life of Pi.13.13") @@ -135,13 +135,13 @@ def test_api_get_text_too_many_hyphens(self): def test_api_get_text_bad_sections(self): response = c.get('/api/v3/texts/Job.6-X') - self.assertEqual(400, response.status_code) + self.assertEqual(404, response.status_code) data = json.loads(response.content) self.assertEqual(data["error"], "Couldn't understand text sections: 'Job.6-X'.") def test_api_get_text_empty_ref(self): response = c.get("/api/v3/texts/Berakhot.1a") - self.assertEqual(400, response.status_code) + self.assertEqual(404, response.status_code) data = json.loads(response.content) self.assertEqual(data["error"], "We have no text for Berakhot 1a.") From 5f3fa2402e64d28fd5e91e0d810569f02c46d737 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Sun, 24 Dec 2023 16:11:30 +0200 Subject: [PATCH 659/756] refactor(api-v3): change isBaseText on client side to isSource (next step is deprecating it). --- static/js/VersionBlock/VersionBlock.jsx | 2 +- static/js/sefaria/sefaria.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/static/js/VersionBlock/VersionBlock.jsx b/static/js/VersionBlock/VersionBlock.jsx index 4cf6828bba..36a33099f2 100644 --- a/static/js/VersionBlock/VersionBlock.jsx +++ b/static/js/VersionBlock/VersionBlock.jsx @@ -187,7 +187,7 @@ class VersionBlock extends Component { } } makeSelectVersionLanguage(){ - let voc = this.props.version.isBaseText ? 'Version' : "Translation"; + let voc = this.props.version.isSource ? 'Version' : "Translation"; return this.props.isCurrent ? Sefaria._("Current " + voc) : Sefaria._("Select "+ voc); } diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index 16d4e456dd..a39d3b2566 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -692,13 +692,13 @@ Sefaria = extend(Sefaria, { }, getTranslations: async function(ref) { /** - * Gets all versions except Hebrew versions that have isBaseText true + * Gets all versions except Hebrew versions that have isSource true * @ref {string} ref * @returns {string: [versions]} Versions by language */ return Sefaria.getVersions(ref).then(result => { let versions = Sefaria.filterVersionsObjByLangs(result, ['he'], false); - let heVersions = Sefaria.filterVersionsArrayByAttr(result?.he || [], {isBaseText: false}); + let heVersions = Sefaria.filterVersionsArrayByAttr(result?.he || [], {isSource: false}); if (heVersions.length) { versions.he = heVersions; } From d6552834e7e905b1407ca025796d842824261688 Mon Sep 17 00:00:00 2001 From: yonadavGit <yonadav.le@gmail.com> Date: Mon, 25 Dec 2023 16:01:32 +0200 Subject: [PATCH 660/756] fix(SearchFilters): changed 'path' regex to ignore books whose title is the prefix of the current book --- sefaria/helper/search.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sefaria/helper/search.py b/sefaria/helper/search.py index 757c931bfa..833c023b03 100644 --- a/sefaria/helper/search.py +++ b/sefaria/helper/search.py @@ -140,7 +140,9 @@ def get_filter_obj(type, filters, filter_fields): def make_filter(type, agg_type, agg_key): if type == "text": # filters with '/' might be leading to books. also, very unlikely they'll match an false positives - reg = re.escape(agg_key) + (".*" if "/" in agg_key else "/.*") + agg_key = agg_key.rstrip('/') + agg_key = re.escape(agg_key) + reg = f"{agg_key}|{agg_key}/.*" return Regexp(path=reg) elif type == "sheet": return Term(**{agg_type: agg_key}) From d6240795ff9a1c025eb1daf656bcc70197ffca3b Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Tue, 26 Dec 2023 09:02:10 +0200 Subject: [PATCH 661/756] fix(translations): hide pseudo-element marker (for safari doesn't support content for it) and use instead the before pseudo-element. --- static/css/s2.css | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/static/css/s2.css b/static/css/s2.css index d5dfb23712..c097236932 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -3567,12 +3567,20 @@ display: none; [open] .versionBlock.with-preview summary { margin-bottom: 10px; } -.versionBlock.with-preview summary::marker { +.versionBlock.with-preview details summary::before { content: url('/static/icons/little-chevron-down.svg'); } -[open] .versionBlock.with-preview summary::marker { +.versionBlock.with-preview details[open] summary::before { content: url('/static/icons/little-chevron-up.svg'); } +.versionBlock.with-preview summary::-webkit-details-marker { + /*hide marker for safari*/ + display: none; +} +.versionBlock.with-preview summary { + /*hide marker for chrome*/ + list-style: none; +} details .open-details::before { margin-inline-end: 5px; } From ef0644a97e3a3ccf2f1b03df86b3a81bb02606cd Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Tue, 26 Dec 2023 09:03:18 +0200 Subject: [PATCH 662/756] fix(translations): pointer cursor for all summary element. --- static/css/s2.css | 1 + 1 file changed, 1 insertion(+) diff --git a/static/css/s2.css b/static/css/s2.css index c097236932..a53431c3f8 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -3580,6 +3580,7 @@ display: none; .versionBlock.with-preview summary { /*hide marker for chrome*/ list-style: none; + cursor: pointer; } details .open-details::before { margin-inline-end: 5px; From 0f6a3b3de4213e8d64c4de8fd45d2cc5e91b06eb Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Tue, 26 Dec 2023 13:12:39 +0200 Subject: [PATCH 663/756] fix(Topic Editor): all topics can have alt titles --- static/js/TopicEditor.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/static/js/TopicEditor.jsx b/static/js/TopicEditor.jsx index 0c36f2489c..a916a71685 100644 --- a/static/js/TopicEditor.jsx +++ b/static/js/TopicEditor.jsx @@ -191,13 +191,13 @@ const TopicEditor = ({origData, onCreateSuccess, close, origWasCat}) => { const url = `/api/topic/delete/${data.origSlug}`; requestWithCallBack({url, type: "DELETE", redirect: () => window.location.href = "/topics"}); } - let items = ["Title", "Hebrew Title", "English Description", "Hebrew Description", "Category Menu"]; + let items = ["Title", "Hebrew Title", "English Description", "Hebrew Description", "Category Menu", "English Alternate Titles", "Hebrew Alternate Titles",]; if (isCategory) { items.push("English Short Description"); items.push("Hebrew Short Description"); } if (isAuthor) { - const authorItems = ["English Alternate Titles", "Hebrew Alternate Titles", "Birth Place", "Hebrew Birth Place", "Birth Year", "Place of Death", "Hebrew Place of Death", "Death Year", "Era"]; + const authorItems = ["Birth Place", "Hebrew Birth Place", "Birth Year", "Place of Death", "Hebrew Place of Death", "Death Year", "Era"]; authorItems.forEach(x => items.push(x)); } items.push("Picture Uploader"); From 43647489c1a72c8feb92aaf8c9a163a8e5de2008 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Tue, 26 Dec 2023 14:06:22 +0200 Subject: [PATCH 664/756] fix(Topics): display without links in the TOC but with links on topic pages --- static/js/Misc.jsx | 4 ++-- static/js/TopicPage.jsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 4d7205fc74..d5fab55bbc 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -60,7 +60,7 @@ const __filterChildrenByLanguage = (children, language) => { return newChildren; }; -const InterfaceText = ({text, html, markdown, children, context}) => { +const InterfaceText = ({text, html, markdown, children, context, disallowedMarkdownElements=[]}) => { /** * Renders a single span for interface string with either class `int-en`` or `int-he` depending on Sefaria.interfaceLang. * If passed explicit text or html objects as props with "en" and/or "he", will only use those to determine correct text or fallback text to display. @@ -92,7 +92,7 @@ const InterfaceText = ({text, html, markdown, children, context}) => { return ( html ? <span className={elemclasses} dangerouslySetInnerHTML={{__html: textResponse}}/> - : markdown ? <span className={elemclasses}><ReactMarkdown className={'reactMarkdown'} unwrapDisallowed={true} disallowedElements={['p']}>{textResponse}</ReactMarkdown></span> + : markdown ? <span className={elemclasses}><ReactMarkdown className={'reactMarkdown'} unwrapDisallowed={true} disallowedElements={['p', ...disallowedMarkdownElements]}>{textResponse}</ReactMarkdown></span> : <span className={elemclasses}>{textResponse}</span> ); }; diff --git a/static/js/TopicPage.jsx b/static/js/TopicPage.jsx index 5d767d83a2..70fa05f305 100644 --- a/static/js/TopicPage.jsx +++ b/static/js/TopicPage.jsx @@ -231,7 +231,7 @@ const TopicCategory = ({topic, topicTitle, setTopic, setNavTopic, compare, initi </a> {description ? <div className="navBlockDescription clamped"> - <InterfaceText markdown={{en: description.en, he: description.he}} /> + <InterfaceText markdown={{en: description.en, he: description.he}} disallowedMarkdownElements={['a']}/> </div> : null } </div> From 1316f7e16337a1e9d28a95863a8b6822ca8df6f7 Mon Sep 17 00:00:00 2001 From: yonadavGit <yonadav.le@gmail.com> Date: Tue, 26 Dec 2023 15:34:44 +0200 Subject: [PATCH 665/756] chore(AboutNavSideBar): update link to video --- static/js/NavSidebar.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/NavSidebar.jsx b/static/js/NavSidebar.jsx index cfbf20b8a0..dfbaedae0e 100644 --- a/static/js/NavSidebar.jsx +++ b/static/js/NavSidebar.jsx @@ -122,7 +122,7 @@ const AboutSefaria = ({hideTitle}) => ( </a> </EnglishText> <HebrewText> - <a className="button get-start" href="https://youtu.be/eo5e8Z54h1Y"> + <a className="button get-start" href="https://youtu.be/rCADxtqPqnw"> <img src="/static/icons/vector-turning-left.svg"/> <div className="get-start"> הכירו את ספריא (2 דק') From 5c62082694e5c72e057ebb1ab67b3a656afd8da8 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Thu, 28 Dec 2023 11:31:05 +0200 Subject: [PATCH 666/756] fix(Topics): topics shown in sidebar with markdown did not display markdown properly --- static/js/ContentText.jsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/static/js/ContentText.jsx b/static/js/ContentText.jsx index ed0ab120b7..234979da76 100644 --- a/static/js/ContentText.jsx +++ b/static/js/ContentText.jsx @@ -1,6 +1,7 @@ import React from "react"; import {useContentLang} from './Hooks'; import Sefaria from './sefaria/sefaria'; +import ReactMarkdown from "react-markdown"; const ContentText = (props) => { /* Renders content language throughout the site (content that comes from the database and is not interface language). @@ -14,7 +15,7 @@ const ContentText = (props) => { * order to return the bilingual langauage elements in (as opposed to the unguaranteed order by default). */ const langAndContentItems = _filterContentTextByLang(props); - return langAndContentItems.map(item => <ContentSpan lang={item[0]} content={item[1]} isHTML={!!props.html}/>); + return langAndContentItems.map(item => <ContentSpan lang={item[0]} content={item[1]} isHTML={!!props.html} markdown={props.markdown}/>); }; const VersionContent = (props) => { @@ -76,11 +77,13 @@ const _filterContentTextByLang = ({text, html, markdown, overrideLanguage, defau return langAndContentItems; } -const ContentSpan = ({lang, content, isHTML}) => { +const ContentSpan = ({lang, content, isHTML, markdown}) => { return isHTML ? <span className={`contentSpan ${lang}`} lang={lang} key={lang} dangerouslySetInnerHTML={{__html: content}}/> - : - <span className={`contentSpan ${lang}`} lang={lang} key={lang}>{content}</span>; + : markdown ? <span className={`contentSpan ${lang}`} lang={lang} key={lang}> + <ReactMarkdown className={'reactMarkdown'} unwrapDisallowed={true} disallowedElements={['p']}>{content}</ReactMarkdown> + </span> + : <span className={`contentSpan ${lang}`} lang={lang} key={lang}>{content}</span>; } export {ContentText, VersionContent}; From 63f665fee2598f7b3c1bd989dc7d8d1a1416a5a3 Mon Sep 17 00:00:00 2001 From: yonadavGit <yonadav.le@gmail.com> Date: Tue, 2 Jan 2024 16:32:13 +0200 Subject: [PATCH 667/756] chore(AboutNavSideBar): make 'play button' vector face right --- static/icons/vector-turning-left.svg | 5 ----- static/js/NavSidebar.jsx | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) delete mode 100644 static/icons/vector-turning-left.svg diff --git a/static/icons/vector-turning-left.svg b/static/icons/vector-turning-left.svg deleted file mode 100644 index 136c4325a1..0000000000 --- a/static/icons/vector-turning-left.svg +++ /dev/null @@ -1,5 +0,0 @@ -<svg width="18" height="19" viewBox="0 0 18 19" fill="none" xmlns="http://www.w3.org/2000/svg"> - <g transform="rotate(-180 9 9.5)"> - <path d="M9 2C13.1355 2 16.5 5.3645 16.5 9.5C16.5 13.6355 13.1355 17 9 17C4.8645 17 1.5 13.6355 1.5 9.5C1.5 5.3645 4.8645 2 9 2ZM9 0.5C4.02975 0.5 0 4.52975 0 9.5C0 14.4703 4.02975 18.5 9 18.5C13.9703 18.5 18 14.4703 18 9.5C18 4.52975 13.9703 0.5 9 0.5ZM6.75 13.25V5.75L13.5 9.6095L6.75 13.25Z" fill="white"/> - </g> -</svg> diff --git a/static/js/NavSidebar.jsx b/static/js/NavSidebar.jsx index dfbaedae0e..139b11254a 100644 --- a/static/js/NavSidebar.jsx +++ b/static/js/NavSidebar.jsx @@ -123,7 +123,7 @@ const AboutSefaria = ({hideTitle}) => ( </EnglishText> <HebrewText> <a className="button get-start" href="https://youtu.be/rCADxtqPqnw"> - <img src="/static/icons/vector-turning-left.svg"/> + <img src="/static/icons/vector.svg"/> <div className="get-start"> הכירו את ספריא (2 דק') </div> From cd53180d553884cb94addafefa86c18e790df896 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Tue, 2 Jan 2024 23:45:56 -0500 Subject: [PATCH 668/756] chore(urls): Add a new redirect for tracking donations from @EliezerIsrael's Munich promotional presentation --- sites/sefaria/urls.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sites/sefaria/urls.py b/sites/sefaria/urls.py index 45c2ed2a86..8ba076b345 100644 --- a/sites/sefaria/urls.py +++ b/sites/sefaria/urls.py @@ -85,6 +85,7 @@ url(r'^faq/?$', lambda x: HttpResponseRedirect('/collections/sefaria-faqs' if x.interfaceLang == 'english' else '/collections/%D7%A9%D7%90%D7%9C%D7%95%D7%AA-%D7%A0%D7%A4%D7%95%D7%A6%D7%95%D7%AA-%D7%91%D7%A1%D7%A4%D7%A8%D7%99%D7%90')), url(r'^help/?$', lambda x: HttpResponseRedirect('/collections/sefaria-faqs' if x.interfaceLang == 'english' else '/collections/%D7%A9%D7%90%D7%9C%D7%95%D7%AA-%D7%A0%D7%A4%D7%95%D7%A6%D7%95%D7%AA-%D7%91%D7%A1%D7%A4%D7%A8%D7%99%D7%90')), url(r'^gala/?$', lambda x: HttpResponseRedirect('https://donate.sefaria.org/event/sefarias-10-year-anniversary-gala/e486954')), + url(r'^give/?$', lambda x: HttpResponseRedirect('https://donate.sefaria.org/give/550774/#!/donation/checkout?c_src=mu')), url(r'^jfn?$', lambda x: HttpResponseRedirect('https://www.sefaria.org/sheets/60494')), url(r'^[nN]echama/?', lambda x: HttpResponseRedirect("/collections/גיליונות-נחמה")), url(r'^contest?', lambda x: HttpResponseRedirect("/powered-by-sefaria-contest-2020")), From 91ca4d4856680abb90f7a0b85759af0ffcbd1e57 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler.cohen@gmail.com> Date: Wed, 3 Jan 2024 18:04:13 -0500 Subject: [PATCH 669/756] chore(urls): Add generic method of redirecting to donation campaign pages based on campaign ID. Also provide a default in case no ID was given --- sites/sefaria/urls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sites/sefaria/urls.py b/sites/sefaria/urls.py index 8ba076b345..4e4dcc19c9 100644 --- a/sites/sefaria/urls.py +++ b/sites/sefaria/urls.py @@ -85,7 +85,7 @@ url(r'^faq/?$', lambda x: HttpResponseRedirect('/collections/sefaria-faqs' if x.interfaceLang == 'english' else '/collections/%D7%A9%D7%90%D7%9C%D7%95%D7%AA-%D7%A0%D7%A4%D7%95%D7%A6%D7%95%D7%AA-%D7%91%D7%A1%D7%A4%D7%A8%D7%99%D7%90')), url(r'^help/?$', lambda x: HttpResponseRedirect('/collections/sefaria-faqs' if x.interfaceLang == 'english' else '/collections/%D7%A9%D7%90%D7%9C%D7%95%D7%AA-%D7%A0%D7%A4%D7%95%D7%A6%D7%95%D7%AA-%D7%91%D7%A1%D7%A4%D7%A8%D7%99%D7%90')), url(r'^gala/?$', lambda x: HttpResponseRedirect('https://donate.sefaria.org/event/sefarias-10-year-anniversary-gala/e486954')), - url(r'^give/?$', lambda x: HttpResponseRedirect('https://donate.sefaria.org/give/550774/#!/donation/checkout?c_src=mu')), + url(r'^give/(?P<campaign_id>[a-zA-Z0-9]+)/?$', lambda x, campaign_id: HttpResponseRedirect(f'https://donate.sefaria.org/give/550774/#!/donation/checkout?c_src={campaign_id}')), url(r'^jfn?$', lambda x: HttpResponseRedirect('https://www.sefaria.org/sheets/60494')), url(r'^[nN]echama/?', lambda x: HttpResponseRedirect("/collections/גיליונות-נחמה")), url(r'^contest?', lambda x: HttpResponseRedirect("/powered-by-sefaria-contest-2020")), From 5def4e3ed7c87aecd82b9612922a007413eedb42 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler.cohen@gmail.com> Date: Wed, 3 Jan 2024 18:07:31 -0500 Subject: [PATCH 670/756] chore(urls): Provide a default in case no ID was given --- sites/sefaria/urls.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sites/sefaria/urls.py b/sites/sefaria/urls.py index 4e4dcc19c9..164ca37fde 100644 --- a/sites/sefaria/urls.py +++ b/sites/sefaria/urls.py @@ -85,7 +85,8 @@ url(r'^faq/?$', lambda x: HttpResponseRedirect('/collections/sefaria-faqs' if x.interfaceLang == 'english' else '/collections/%D7%A9%D7%90%D7%9C%D7%95%D7%AA-%D7%A0%D7%A4%D7%95%D7%A6%D7%95%D7%AA-%D7%91%D7%A1%D7%A4%D7%A8%D7%99%D7%90')), url(r'^help/?$', lambda x: HttpResponseRedirect('/collections/sefaria-faqs' if x.interfaceLang == 'english' else '/collections/%D7%A9%D7%90%D7%9C%D7%95%D7%AA-%D7%A0%D7%A4%D7%95%D7%A6%D7%95%D7%AA-%D7%91%D7%A1%D7%A4%D7%A8%D7%99%D7%90')), url(r'^gala/?$', lambda x: HttpResponseRedirect('https://donate.sefaria.org/event/sefarias-10-year-anniversary-gala/e486954')), - url(r'^give/(?P<campaign_id>[a-zA-Z0-9]+)/?$', lambda x, campaign_id: HttpResponseRedirect(f'https://donate.sefaria.org/give/550774/#!/donation/checkout?c_src={campaign_id}')), + url(r'^give/(?P<campaign_id>[a-zA-Z0-9]+)/?$', lambda x, campaign_id: HttpResponseRedirect(f'https://donate.sefaria.org/give/550774/#!/donation/checkout?c_src={campaign_id}')), + url(r'^give/?$', lambda x: HttpResponseRedirect(f'https://donate.sefaria.org/give/550774/#!/donation/checkout?c_src=mu')), url(r'^jfn?$', lambda x: HttpResponseRedirect('https://www.sefaria.org/sheets/60494')), url(r'^[nN]echama/?', lambda x: HttpResponseRedirect("/collections/גיליונות-נחמה")), url(r'^contest?', lambda x: HttpResponseRedirect("/powered-by-sefaria-contest-2020")), From 3c9a42c38be3490b59b113f61687a2787401a207 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Sun, 7 Jan 2024 11:37:40 +0200 Subject: [PATCH 671/756] fix(Source Editor): Prompts should be textareas not input elements --- static/js/AdminEditor.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/AdminEditor.jsx b/static/js/AdminEditor.jsx index 1d1ecea94a..d761f200b8 100644 --- a/static/js/AdminEditor.jsx +++ b/static/js/AdminEditor.jsx @@ -21,7 +21,7 @@ const options_for_form = { placeholder: "Add a description.", type: 'textarea' }, - "Prompt": {label: "Prompt", field: "prompt", placeholder: "Add a prompt.", textarea: true}, + "Prompt": {label: "Prompt", field: "prompt", placeholder: "Add a prompt.", type: 'textarea'}, "English Short Description": { label: "English Short Description for Table of Contents", field: "enCategoryDescription", placeholder: "Add a short description.", type: 'input' From d3e31c5045cd3316097fa6d1eb21c2a519326ec2 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Sun, 7 Jan 2024 12:28:37 +0200 Subject: [PATCH 672/756] fix(Markdown): markdown in admin editors was not being validated --- static/js/AdminEditor.jsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/static/js/AdminEditor.jsx b/static/js/AdminEditor.jsx index 1d1ecea94a..d324d6b687 100644 --- a/static/js/AdminEditor.jsx +++ b/static/js/AdminEditor.jsx @@ -13,13 +13,15 @@ const options_for_form = { label: "English Description", field: "enDescription", placeholder: "Add a description.", - type: 'textarea' + type: 'textarea', + markdown: true, }, "Hebrew Description": { label: "Hebrew Description", field: "heDescription", placeholder: "Add a description.", - type: 'textarea' + type: 'textarea', + markdown: true }, "Prompt": {label: "Prompt", field: "prompt", placeholder: "Add a prompt.", textarea: true}, "English Short Description": { @@ -136,7 +138,7 @@ const AdminEditor = ({title, data, close, catMenu, pictureUploader, updateData, const preprocess = async () => { setValidatingLinks(true); for (const x of items) { - if (options_for_form[x]?.is_textarea) { + if (options_for_form[x]?.markdown) { const field = options_for_form[x].field; const valid_tags = await validateMarkdownLinks(data[field]); if (!valid_tags) { From 2ccf84d954fa856dfbc436a3c408d06e28d9a4bf Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Mon, 8 Jan 2024 09:20:53 +0200 Subject: [PATCH 673/756] test: change ref tests that used mekhilta (after refoactoring of mekhilta) --- sefaria/model/tests/ref_test.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/sefaria/model/tests/ref_test.py b/sefaria/model/tests/ref_test.py index b769f5dd87..e5b5c231bc 100644 --- a/sefaria/model/tests/ref_test.py +++ b/sefaria/model/tests/ref_test.py @@ -177,9 +177,8 @@ def test_next_ref(self): assert Ref("Shabbat 4b").next_section_ref().normal() == "Shabbat 5a" assert Ref("Shabbat 5a").next_section_ref().normal() == "Shabbat 5b" assert Ref("Rashi on Genesis 5:32:2").next_section_ref().normal() == "Rashi on Genesis 6:2" - assert Ref("Mekhilta_DeRabbi_Yishmael.35.3").next_section_ref() is None - # This will start to fail when we fill in this text - assert Ref("Mekhilta_DeRabbi_Yishmael.23.19").next_section_ref().normal() == "Mekhilta DeRabbi Yishmael 31:12" + assert Ref("Berakhot 64a").next_section_ref() is None + assert Ref("Rif Chullin 43a").next_section_ref().normal() == "Rif Chullin 44b" def test_complex_next_ref(self): #at time of test we only had complex commentaries stable to test with assert Ref('Pesach Haggadah, Kadesh').next_section_ref().normal() == 'Pesach Haggadah, Urchatz' @@ -197,9 +196,8 @@ def test_prev_ref(self): assert Ref("Shabbat 4b").prev_section_ref().normal() == "Shabbat 4a" assert Ref("Shabbat 5a").prev_section_ref().normal() == "Shabbat 4b" assert Ref("Rashi on Genesis 6:2:1").prev_section_ref().normal() == "Rashi on Genesis 5:32" - assert Ref("Mekhilta 12:1").prev_section_ref() is None - # This will start to fail when we fill in this text - assert Ref("Mekhilta_DeRabbi_Yishmael.31.12").prev_section_ref().normal() == "Mekhilta DeRabbi Yishmael 23:19" + assert Ref("Berakhot 2a").prev_section_ref() is None + assert Ref("Rif Chullin 44b").prev_section_ref().normal() == "Rif Chullin 43a" def test_complex_prev_ref(self): assert Ref('Pesach Haggadah, Urchatz').prev_section_ref().normal() == 'Pesach Haggadah, Kadesh' From 7686dd2134f7fe3668fe52746a1d46cfbfb0ffd4 Mon Sep 17 00:00:00 2001 From: Brendan Galloway <brendan@flanksource.com> Date: Tue, 9 Jan 2024 09:57:23 +0200 Subject: [PATCH 674/756] helm(feat): allocate more resources to mongo restore pod --- helm-chart/sefaria-project/templates/jobs/mongo-restore.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/helm-chart/sefaria-project/templates/jobs/mongo-restore.yaml b/helm-chart/sefaria-project/templates/jobs/mongo-restore.yaml index edd5d85b86..0969c121d0 100644 --- a/helm-chart/sefaria-project/templates/jobs/mongo-restore.yaml +++ b/helm-chart/sefaria-project/templates/jobs/mongo-restore.yaml @@ -40,6 +40,11 @@ spec: containers: - name: mongo-backup-restorer-{{ .Values.deployEnv }} image: mongo:4.4 + resources: + requests: + cpu: 500m + limits: + cpu: 750m volumeMounts: - name: shared-volume mountPath: /storage From 7844d8984eba5af09789013fb8303a8a5ec1ac19 Mon Sep 17 00:00:00 2001 From: Brendan Galloway <brendan@flanksource.com> Date: Tue, 9 Jan 2024 12:57:16 +0200 Subject: [PATCH 675/756] ci: update monorepo release rules --- helm-chart/release-rules.sh | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/helm-chart/release-rules.sh b/helm-chart/release-rules.sh index 8e8cf05ac0..649e51b604 100755 --- a/helm-chart/release-rules.sh +++ b/helm-chart/release-rules.sh @@ -8,15 +8,15 @@ plugins: releaseRules: - {"type": "helm", "release": "minor" } - {"type": "helm", "scope": "fix", "release": "patch" } - - {"type": "feat", "release": false} - - {"type": "fix", "release": false} - - {"type": "chore", "release": false} - - {"type": "docs", "release": false} - - {"type": "style", "release": false} - - {"type": "refactor", "release": false} - - {"type": "perf", "release": false} - - {"type": "test", "release": false} - - {"type": "static", "release": false} + - {"type": "feat", "release": "minor"} + - {"type": "fix", "release": "patch"} + - {"type": "chore", "release": "patch"} + - {"type": "docs", "release": "patch"} + - {"type": "style", "release": "patch"} + - {"type": "refactor", "release": "patch"} + - {"type": "perf", "release": "patch"} + - {"type": "test", "release": "patch"} + - {"type": "static", "release": "patch"} parserOpts: noteKeywords: - MAJOR RELEASE From 5217da9b5e22bb6e529f9f5bfa01e1b76b3bf0bb Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 10 Jan 2024 09:54:14 +0200 Subject: [PATCH 676/756] fix: fully convert tref to URL form before looking it up in the legacy ref mapping --- sefaria/helper/legacy_ref.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sefaria/helper/legacy_ref.py b/sefaria/helper/legacy_ref.py index 92a303ad14..2c384a15ac 100644 --- a/sefaria/helper/legacy_ref.py +++ b/sefaria/helper/legacy_ref.py @@ -33,6 +33,7 @@ class LegacyRefParsingData(AbstractMongoRecord): } ``` To be used with LegacyRefParser classes in this module. + Current assumption is all trefs in the mapping (both old and mapped) are in URL form and are segment level. """ collection = 'legacy_ref_data' criteria_field = 'title' @@ -109,8 +110,9 @@ def __range_list(ranged_tref: str) -> List[str]: return range_list def __get_mapped_tref(self, legacy_tref: str) -> str: - # replace last space before sections with a period to conform with normal form + # replace last space before sections with a period to conform with url form legacy_tref = re.sub(r" (?=[\d.:ab]+$)", ".", legacy_tref) + legacy_tref = legacy_tref.replace(' ', '_') try: return self._mapping[legacy_tref] except KeyError as err: From 85d208ff623448a46fae9d64f067b25f7861e9b0 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 10 Jan 2024 10:15:31 +0200 Subject: [PATCH 677/756] test(legacy ref): modify legacy ref tests to use a title with a space in it that is more general. --- sefaria/helper/tests/legacy_ref_test.py | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/sefaria/helper/tests/legacy_ref_test.py b/sefaria/helper/tests/legacy_ref_test.py index 934b205666..4b41b1baaa 100644 --- a/sefaria/helper/tests/legacy_ref_test.py +++ b/sefaria/helper/tests/legacy_ref_test.py @@ -46,14 +46,14 @@ def test_zohar_index(test_index_title): @pytest.fixture(scope="module", autouse=True) -def test_zohar_mapping_data(test_index_title): +def test_zohar_mapping_data(test_index_title, url_test_index_title): lrpd = LegacyRefParsingData({ "index_title": test_index_title, "data": { "mapping": { - f"{test_index_title}.1.15a.1": f"{test_index_title}.1.42", - f"{test_index_title}.1.15a.2": f"{test_index_title}.1.42", - f"{test_index_title}.1.15a.3": f"{test_index_title}.1.43", + f"{url_test_index_title}.1.15a.1": f"{url_test_index_title}.1.42", + f"{url_test_index_title}.1.15a.2": f"{url_test_index_title}.1.42", + f"{url_test_index_title}.1.15a.3": f"{url_test_index_title}.1.43", }, }, }) @@ -66,15 +66,20 @@ def test_zohar_mapping_data(test_index_title): @pytest.fixture(scope="module") def test_index_title(): - return "TestZohar" + return "Test Zohar" @pytest.fixture(scope="module") -def old_and_new_trefs(request, test_index_title): +def url_test_index_title(test_index_title): + return test_index_title.replace(" ", "_") + + +@pytest.fixture(scope="module") +def old_and_new_trefs(request, url_test_index_title): old_ref, new_ref = request.param # if new_ref is None, means mapping doesn't exist - new_ref = new_ref and f"{test_index_title}.{new_ref}" - return f"{test_index_title}.{old_ref}", new_ref + new_ref = new_ref and f"{url_test_index_title}.{new_ref}" + return f"{url_test_index_title}.{old_ref}", new_ref def get_book(tref): @@ -143,12 +148,12 @@ def test_old_zohar_partial_ref_legacy_parsing(self, old_and_new_trefs): assert converted_ref.legacy_tref == old_ref assert converted_ref.normal() == Ref(new_ref).normal() - def test_instantiate_ref_with_legacy_parse_fallback(self, test_index_title, old_and_new_trefs): + def test_instantiate_ref_with_legacy_parse_fallback(self, url_test_index_title, old_and_new_trefs): old_tref, new_tref = old_and_new_trefs oref = Ref.instantiate_ref_with_legacy_parse_fallback(old_tref) if new_tref is None: - assert oref.url() == test_index_title + assert oref.url() == url_test_index_title assert getattr(oref, 'legacy_tref', None) is None else: assert oref.url() == new_tref From b8caab3dc3874102b532472ae8a9fbab6f5d54c4 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 10 Jan 2024 10:15:59 +0200 Subject: [PATCH 678/756] fix(legacy ref): make sure legacy tref is in URL form --- sefaria/helper/legacy_ref.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sefaria/helper/legacy_ref.py b/sefaria/helper/legacy_ref.py index 2c384a15ac..288006446e 100644 --- a/sefaria/helper/legacy_ref.py +++ b/sefaria/helper/legacy_ref.py @@ -121,14 +121,14 @@ def __get_mapped_tref(self, legacy_tref: str) -> str: def __parse_segment_ref(self, legacy_tref: str) -> Ref: converted_tref = self.__get_mapped_tref(legacy_tref) converted_ref = Ref(converted_tref) - converted_ref.legacy_tref = legacy_tref + converted_ref.legacy_tref = legacy_tref.replace(" ", "_") return converted_ref def __parse_ranged_ref(self, legacy_tref: str) -> Ref: parsed_range_list = [self.__parse_segment_ref(temp_tref) for temp_tref in self.__range_list(legacy_tref)] parsed_range_list.sort(key=lambda x: x.order_id()) # not assuming mapping is in order ranged_oref = parsed_range_list[0].to(parsed_range_list[-1]) - ranged_oref.legacy_tref = legacy_tref + ranged_oref.legacy_tref = legacy_tref.replace(" ", "_") return ranged_oref From 96be1df10207b44286cc31663da88366a4a133a8 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 10 Jan 2024 10:51:52 +0200 Subject: [PATCH 679/756] refactor(legacy ref): move url form logic to function --- sefaria/helper/legacy_ref.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/sefaria/helper/legacy_ref.py b/sefaria/helper/legacy_ref.py index 288006446e..1042004e0c 100644 --- a/sefaria/helper/legacy_ref.py +++ b/sefaria/helper/legacy_ref.py @@ -86,10 +86,22 @@ def parse(self, legacy_tref: str) -> Ref: @param legacy_tref: @return: """ + legacy_tref = self.__to_url_form(legacy_tref) if self.__is_ranged_ref(legacy_tref): return self.__parse_ranged_ref(legacy_tref) return self.__parse_segment_ref(legacy_tref) + @staticmethod + def __to_url_form(tref: str): + """ + replace last space before sections with a period + AND + then replace remaining spaces with underscore + @param tref: + @return: + """ + return re.sub(r" (?=[\d.:ab]+$)", ".", tref).replace(" ", "_") + @staticmethod def __is_ranged_ref(tref: str) -> bool: return "-" in tref @@ -110,9 +122,6 @@ def __range_list(ranged_tref: str) -> List[str]: return range_list def __get_mapped_tref(self, legacy_tref: str) -> str: - # replace last space before sections with a period to conform with url form - legacy_tref = re.sub(r" (?=[\d.:ab]+$)", ".", legacy_tref) - legacy_tref = legacy_tref.replace(' ', '_') try: return self._mapping[legacy_tref] except KeyError as err: @@ -121,14 +130,14 @@ def __get_mapped_tref(self, legacy_tref: str) -> str: def __parse_segment_ref(self, legacy_tref: str) -> Ref: converted_tref = self.__get_mapped_tref(legacy_tref) converted_ref = Ref(converted_tref) - converted_ref.legacy_tref = legacy_tref.replace(" ", "_") + converted_ref.legacy_tref = legacy_tref return converted_ref def __parse_ranged_ref(self, legacy_tref: str) -> Ref: parsed_range_list = [self.__parse_segment_ref(temp_tref) for temp_tref in self.__range_list(legacy_tref)] parsed_range_list.sort(key=lambda x: x.order_id()) # not assuming mapping is in order ranged_oref = parsed_range_list[0].to(parsed_range_list[-1]) - ranged_oref.legacy_tref = legacy_tref.replace(" ", "_") + ranged_oref.legacy_tref = legacy_tref return ranged_oref From 6d2f96710878b8bf5f1d8a365c729e76e0bde6a3 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 10 Jan 2024 12:39:46 +0200 Subject: [PATCH 680/756] test(search): fix failing search filter test --- sefaria/helper/tests/search_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sefaria/helper/tests/search_test.py b/sefaria/helper/tests/search_test.py index 08ce2dac08..fdc62ed06c 100644 --- a/sefaria/helper/tests/search_test.py +++ b/sefaria/helper/tests/search_test.py @@ -22,7 +22,7 @@ def test_query_obj(): assert ordered(t) == ordered(s.to_dict()) # text query sorted by pagerank and with multiple applied filters s = get_query_obj("moshe", "text", "naive_lemmatizer", False, 10, 0, 10, ["Tanakh/Targum/Targum Jonathan", "Mishnah/Seder Zeraim/Mishnah Peah", "Talmud/Bavli/Seder Moed/Pesachim"], ["path", "path", "path"], [], "score", ['pagesheetrank'], sort_score_missing=0.04) - t = json.loads("""{"_source": false, "from":0,"size":10,"highlight":{"fields":{"naive_lemmatizer":{"fragment_size":200,"pre_tags":["<b>"],"post_tags":["</b>"]}}},"query":{"function_score":{"functions":[{"field_value_factor":{"field":"pagesheetrank","missing":0.04}}],"query":{"bool":{"must":[{"match_phrase":{"naive_lemmatizer":{"query":"moshe","slop":10}}}],"filter":[{"bool":{"must":[{"bool":{"should":[{"regexp":{"path":"Tanakh/Targum/Targum\\\\ Jonathan.*"}},{"regexp":{"path":"Mishnah/Seder\\\\ Zeraim/Mishnah\\\\ Peah.*"}},{"regexp":{"path":"Talmud/Bavli/Seder\\\\ Moed/Pesachim.*"}}]}}]}}]}}}}}""") + t = json.loads("""{"_source": false, "from":0,"size":10,"highlight":{"fields":{"naive_lemmatizer":{"fragment_size":200,"pre_tags":["<b>"],"post_tags":["</b>"]}}},"query":{"function_score":{"functions":[{"field_value_factor":{"field":"pagesheetrank","missing":0.04}}],"query":{"bool":{"must":[{"match_phrase":{"naive_lemmatizer":{"query":"moshe","slop":10}}}],"filter":[{"bool":{"must":[{"bool":{"should":[{"regexp":{"path":"Tanakh/Targum/Targum\\\\ Jonathan|Tanakh/Targum/Targum\\\\ Jonathan/.*"}},{"regexp":{"path":"Mishnah/Seder\\\\ Zeraim/Mishnah\\\\ Peah|Mishnah/Seder\\\\ Zeraim/Mishnah\\\\ Peah/.*"}},{"regexp":{"path":"Talmud/Bavli/Seder\\\\ Moed/Pesachim|Talmud/Bavli/Seder\\\\ Moed/Pesachim/.*"}}]}}]}}]}}}}}""") assert ordered(t) == ordered(s.to_dict()) # sheet query sorted by relevance, with a collections agg and collections/tag filters s = get_query_obj("moshe", "sheet", "content", False, 10, 0, 10, ["", "Moses", "Passover"], ["collections", "tags", "tags"], ['collections'], "score", []) From e0e66b1885624f2ba6c89d60cc49b7f94d5f6f75 Mon Sep 17 00:00:00 2001 From: Brendan Galloway <brendan@flanksource.com> Date: Wed, 10 Jan 2024 13:03:28 +0200 Subject: [PATCH 681/756] ci: restrict pytest concurrency to test possible resource constraints --- .github/workflows/continuous.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/continuous.yaml b/.github/workflows/continuous.yaml index 4c9dfd788f..587dcd0963 100644 --- a/.github/workflows/continuous.yaml +++ b/.github/workflows/continuous.yaml @@ -196,6 +196,9 @@ jobs: run: cat /home/runner/jestResults.json; STATUS=`jq ".numFailedTestSuites" /home/runner/jestResults.json`; exit $STATUS if: ${{ always() }} sandbox-deploy: + concurrency: + group: dev-mongo + cancel-in-progress: false if: ${{ github.event_name == 'pull_request' }} runs-on: ubuntu-latest needs: build-derived @@ -265,6 +268,9 @@ jobs: WAIT_DURATION: "3000" GIT_COMMIT: "${{ steps.get-sha.outputs.sha_short }}" pytest-job: + concurrency: + group: dev-mongo + cancel-in-progress: false if: ${{ github.event_name == 'pull_request' }} name: "PyTest" # This name is referenced when slacking status needs: From 6d3bfd71a2702d32b9212788962626bfda393b56 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 10 Jan 2024 14:52:15 +0200 Subject: [PATCH 682/756] fix(legacy): replace input title with normalized title in case input was an alt title --- sefaria/model/text.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index 22c2e19199..0e0aadfd20 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -4873,6 +4873,8 @@ def instantiate_ref_with_legacy_parse_fallback(tref: str) -> 'Ref': except PartialRefInputError as e: matched_ref = Ref(e.matched_part) try: + # replace input title with normalized title in case input was an alt title + tref = tref.replace(e.matched_part, matched_ref.normal()) tref = Ref.__clean_tref(tref, matched_ref._lang) legacy_ref_parser = legacy_ref_parser_handler[matched_ref.index.title] return legacy_ref_parser.parse(tref) From 3fb1b5eb332d6c3d4b1c15fc5615707aa4c2c849 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 10 Jan 2024 15:30:58 +0200 Subject: [PATCH 683/756] test(legacy ref): add alt title legacy ref test --- sefaria/helper/tests/legacy_ref_test.py | 45 ++++++++++++++++++------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/sefaria/helper/tests/legacy_ref_test.py b/sefaria/helper/tests/legacy_ref_test.py index 4b41b1baaa..9c9e0bd071 100644 --- a/sefaria/helper/tests/legacy_ref_test.py +++ b/sefaria/helper/tests/legacy_ref_test.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import pytest +import re from sefaria.model import * from sefaria.helper.legacy_ref import legacy_ref_parser_handler, MappingLegacyRefParser, NoLegacyRefParserError, LegacyRefParsingData, LegacyRefParserMappingKeyError from sefaria.system.exceptions import PartialRefInputError @@ -21,6 +22,10 @@ def test_zohar_index(test_index_title): "text": en_title, "primary": True }, + { + "lang": "en", + "text": "Alt Title Yo yo" + }, { "lang": "he", "text": 'זהר לא אמיתי', @@ -150,19 +155,33 @@ def test_old_zohar_partial_ref_legacy_parsing(self, old_and_new_trefs): def test_instantiate_ref_with_legacy_parse_fallback(self, url_test_index_title, old_and_new_trefs): old_tref, new_tref = old_and_new_trefs - - oref = Ref.instantiate_ref_with_legacy_parse_fallback(old_tref) - if new_tref is None: - assert oref.url() == url_test_index_title - assert getattr(oref, 'legacy_tref', None) is None - else: - assert oref.url() == new_tref - assert oref.legacy_tref == old_tref.replace(':', '.') - - if new_tref is not None: - oref = Ref.instantiate_ref_with_legacy_parse_fallback(new_tref) - assert oref.url() == new_tref - assert getattr(oref, 'legacy_tref', None) is None + instantiate_legacy_refs_tester(url_test_index_title, old_tref, new_tref) + + +@pytest.mark.parametrize(('url_index_title', 'input_title', 'input_sections', 'output_tref'), [ + ["Test_Zohar", "Alt Title Yo yo", "1:15a:1", "Test_Zohar.1.42"], +]) +def test_instantiate_legacy_refs_parametrized(url_index_title, input_title, input_sections, output_tref): + old_tref = f"{input_title}.{input_sections}" + instantiate_legacy_refs_tester(url_index_title, old_tref, output_tref, old_title=input_title) + + +def instantiate_legacy_refs_tester(url_index_title, old_tref, new_tref, old_title=None): + oref = Ref.instantiate_ref_with_legacy_parse_fallback(old_tref) + if new_tref is None: + assert oref.url() == url_index_title + assert getattr(oref, 'legacy_tref', None) is None + else: + assert oref.url() == new_tref + expected_legacy_tref = old_tref.replace(':', '.') + if old_title: + expected_legacy_tref = expected_legacy_tref.replace(old_title, url_index_title) + assert oref.legacy_tref == expected_legacy_tref + + if new_tref is not None: + oref = Ref.instantiate_ref_with_legacy_parse_fallback(new_tref) + assert oref.url() == new_tref + assert getattr(oref, 'legacy_tref', None) is None class TestLegacyRefsRandomIndex: From a8aeaeeb006806b7786f48ab4aa9879a5535794e Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Wed, 10 Jan 2024 21:54:55 +0200 Subject: [PATCH 684/756] fix(legacy ref): clean tref of underscores before replacing alt title so alt title will match exactly --- sefaria/helper/tests/legacy_ref_test.py | 1 + sefaria/model/text.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/sefaria/helper/tests/legacy_ref_test.py b/sefaria/helper/tests/legacy_ref_test.py index 9c9e0bd071..8880d14500 100644 --- a/sefaria/helper/tests/legacy_ref_test.py +++ b/sefaria/helper/tests/legacy_ref_test.py @@ -160,6 +160,7 @@ def test_instantiate_ref_with_legacy_parse_fallback(self, url_test_index_title, @pytest.mark.parametrize(('url_index_title', 'input_title', 'input_sections', 'output_tref'), [ ["Test_Zohar", "Alt Title Yo yo", "1:15a:1", "Test_Zohar.1.42"], + ["Test_Zohar", "Alt_Title_Yo_yo", "1:15a:1", "Test_Zohar.1.42"], ]) def test_instantiate_legacy_refs_parametrized(url_index_title, input_title, input_sections, output_tref): old_tref = f"{input_title}.{input_sections}" diff --git a/sefaria/model/text.py b/sefaria/model/text.py index 0e0aadfd20..6e614c61b3 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -4873,9 +4873,9 @@ def instantiate_ref_with_legacy_parse_fallback(tref: str) -> 'Ref': except PartialRefInputError as e: matched_ref = Ref(e.matched_part) try: + tref = Ref.__clean_tref(tref, matched_ref._lang) # replace input title with normalized title in case input was an alt title tref = tref.replace(e.matched_part, matched_ref.normal()) - tref = Ref.__clean_tref(tref, matched_ref._lang) legacy_ref_parser = legacy_ref_parser_handler[matched_ref.index.title] return legacy_ref_parser.parse(tref) except LegacyRefParserError: From 9c5d207644e5a81e31eb9b7193ab3a811ec226e8 Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Thu, 11 Jan 2024 17:52:53 +0200 Subject: [PATCH 685/756] chore(docstrings): Fix examples in Term class docstring to working examples --- sefaria/model/schema.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sefaria/model/schema.py b/sefaria/model/schema.py index 379c1a1a7a..82e89fe656 100644 --- a/sefaria/model/schema.py +++ b/sefaria/model/schema.py @@ -243,7 +243,7 @@ def remove_shared_term(self, term): class Term(abst.AbstractMongoRecord, AbstractTitledObject): """ A Term is a shared title node. It can be referenced and used by many different Index nodes. - Examples: Noah, Perek HaChovel, Even HaEzer + Examples: Noah, HaChovel Terms that use the same TermScheme can be ordered. """ collection = 'term' From 4bd1847cdfbf17ac9576795938765fa480f0b8f8 Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Thu, 11 Jan 2024 17:53:53 +0200 Subject: [PATCH 686/756] chore(docstrings): Fix typo --- sefaria/model/text.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index fce43fe69f..04b4a1da6c 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -997,7 +997,7 @@ def sub_content_with_ref(self, ref=None, value=None): def sub_content(self, key_list=None, indx_list=None, value=None): """ - Get's or sets values deep within the content of this version. + Gets or sets values deep within the content of this version. This returns the result by reference, NOT by value. http://stackoverflow.com/questions/27339165/slice-nested-list-at-variable-depth :param key_list: The node keys to traverse to get to the content node From 3e02e078330566f4a4576011b3fba4898329aef6 Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Thu, 11 Jan 2024 17:54:43 +0200 Subject: [PATCH 687/756] chore(docstrings): Place return after param --- sefaria/model/text.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index 04b4a1da6c..5d5e6decb3 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -3945,8 +3945,8 @@ def all_context_refs(self, include_self = True, include_book = False): def context_ref(self, level=1): """ - :return: :class:`Ref` that is more general than this :class:`Ref`. :param level: how many levels to 'zoom out' from the most specific possible :class:`Ref` + :return: :class:`Ref` that is more general than this :class:`Ref`. :: From ef06f44d3395e3f2a720db85fac306931b03f98c Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Thu, 11 Jan 2024 17:58:31 +0200 Subject: [PATCH 688/756] chore(docstrings): Formatting clean up --- sefaria/model/text.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index 5d5e6decb3..aa1919a549 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -5033,7 +5033,7 @@ def rebuild_toc(self, skip_toc_tree=False): While building these ToC data structures, this function also builds the equivalent JSON structures as an API optimization. - @param: skip_toc_tree boolean + :param skip_toc_tree: Boolean """ if not skip_toc_tree: self._toc_tree = self.get_toc_tree(rebuild=True) @@ -5127,7 +5127,7 @@ def get_topic_toc(self, rebuild=False): def get_topic_toc_json(self, rebuild=False): """ Returns JSON representation of Topics ToC. - @param: rebuild boolean + :param rebuild: Boolean """ if rebuild or not self._topic_toc_json: if not rebuild: @@ -5141,9 +5141,9 @@ def get_topic_toc_json(self, rebuild=False): def get_topic_toc_json_recursive(self, topic=None, explored=None, with_descriptions=False): """ Returns JSON representation of Topics ToC - @param: topic Topic - @param: explored Set - @param: with_descriptions boolean + :param topic: Topic + :param explored: Set + :param with_descriptions: Boolean """ from .topic import Topic, TopicSet, IntraTopicLinkSet explored = explored or set() @@ -5210,7 +5210,7 @@ def build_topic_toc_category_mapping(self) -> dict: def get_topic_toc_category_mapping(self, rebuild=False) -> dict: """ Returns the category mapping as a dictionary for the topics ToC. Loads on Library startup. - @param: rebuild boolean + :param rebuild: Boolean """ if rebuild or not self._topic_toc_category_mapping: if not rebuild: @@ -5263,7 +5263,7 @@ def root_title_sorter(t): def get_topic_link_type(self, link_type): """ Returns a TopicLinkType with a slug of link_type (parameter) if not already present - @param: link_type String + :param link_type: String """ from .topic import TopicLinkTypeSet if not self._topic_link_types: @@ -5276,7 +5276,7 @@ def get_topic_link_type(self, link_type): def get_topic_data_source(self, data_source): """ Returns a TopicDataSource with the data_source (parameter) slug if not already present - @param: data_source String + :param data_source: String """ from .topic import TopicDataSourceSet if not self._topic_data_sources: @@ -5380,7 +5380,7 @@ def lexicon_auto_completer(self, lexicon): is not present, it assumes the need to rebuild the lexicon_auto_completer and calls the build function with appropriate logger warnings before returning the desired result - @param: lexicon String + :param lexicon: String """ try: return self._lexicon_auto_completer[lexicon] @@ -5774,9 +5774,9 @@ def citing_title_list(self, lang="en"): def full_title_list(self, lang="en", with_terms=False): """ - :return: list of strings of all possible titles :param lang: "he" or "en" :param with_terms: if True, includes shared titles ('terms') + :return: list of strings of all possible titles """ key = lang key += "_terms" if with_terms else "" From 82da43a42248b2372b8099e5637dcbbc89b8fc00 Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Thu, 11 Jan 2024 18:04:10 +0200 Subject: [PATCH 689/756] chore(docstrings): Some added docstrings to text.py functions --- sefaria/model/text.py | 58 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index aa1919a549..eb0cc55daa 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -5640,6 +5640,12 @@ def get_term_dict(self, lang="en"): return term_dict def build_term_mappings(self): + """ + Build simple and full term mappings + A full term mapping has the term name as the key, and the term as the value. + A simple term mapping has the term name as the key, and a dictionary containing the English and Hebrew + primary titles for the terms as the value. + """ self._simple_term_mapping = {} self._full_term_mapping = {} for term in TermSet(): @@ -5675,6 +5681,11 @@ def get_simple_term_mapping_json(self, rebuild=False): return self._simple_term_mapping_json def get_term(self, term_name): + """ + Returns the full term, if mapping not present, builds the full term mapping. + :param term_name: String + :returns: full Term (Mongo Record) + """ if not self._full_term_mapping: self.build_term_mappings() return self._full_term_mapping.get(term_name) if term_name in self._full_term_mapping else Term().load({"name": term_name}) @@ -5682,15 +5693,27 @@ def get_term(self, term_name): def get_topic(self, slug): + """ + Returns the corresponding topic for a given slug + :param slug: String + :returns: topic map for the given slug Dictionary + """ return self._topic_mapping[slug] def get_topic_mapping(self, rebuild=False): + """ + Returns the topic mapping if it exists, if not rebuilds it and returns + :param rebuild: Boolean (optional, default set to False) + """ tm = self._topic_mapping if not tm or rebuild: tm = self._build_topic_mapping() return tm def _build_topic_mapping(self): + """ + Builds the topic mapping + """ from .topic import Topic, TopicSet self._topic_mapping = {t.slug: {"en": t.get_primary_title("en"), "he": t.get_primary_title("he")} for t in TopicSet()} return self._topic_mapping @@ -5734,6 +5757,9 @@ def get_index_forest(self): return root_nodes def all_index_records(self): + """ + Returns an array of all index records + """ return [self._index_map[k] for k in list(self._index_title_maps["en"].keys())] def get_title_node_dict(self, lang="en"): @@ -5800,6 +5826,11 @@ def build_text_titles_json(self, lang="en"): return title_list def get_text_titles_json(self, lang="en", rebuild=False): + """ + Returns the json text title list + :param lang: String (optional, default set to 'en') + :param rebuild: Boolean (optional, default set to False) + """ if rebuild or not self._full_title_list_jsons.get(lang): if not rebuild: self._full_title_list_jsons[lang] = scache.get_shared_cache_elem('books_'+lang+'_json') @@ -5813,6 +5844,9 @@ def get_text_titles_json(self, lang="en", rebuild=False): return self._full_title_list_jsons[lang] def reset_text_titles_cache(self): + """ + Resets the text titles for all languages + """ for lang in self.langs: scache.delete_shared_cache_elem('books_' + lang) scache.delete_shared_cache_elem('books_' + lang + '_json') @@ -6058,6 +6092,19 @@ def get_multi_title_regex_string(self, titles, lang, for_js=False, anchored=Fals # do we want to move this to the schema node? We'd still have to pass the title... def get_regex_string(self, title, lang, for_js=False, anchored=False, capture_title=False, parentheses=False): + """ + Given a string with a Ref not in Sefaria format (i.e. "See Genesis 2 3" as opposed to "Genesis 2:3", + it returns a regex for the Ref. + If the language is 'en', it calls the full_regex() function which returns the regex, whereas for 'he' we + limit the search to content inside parenthesis to limit false positives (i.e. שבת לא תעשה could be caught by + mistake as Shabbat 31) + :param title: String + :param lang: 'en' or 'he' + :param for_js: Boolean (default set to False, optional) + :param anchored: Boolean (default set to False, optional) + :param capture_title: Boolean (default set to False, optional) + :param parentheses: Boolean (default set to False, optional) + """ node = self.get_schema_node(title, lang) assert isinstance(node, JaggedArrayNode) # Assumes that node is a JaggedArrayNode @@ -6210,6 +6257,11 @@ def repl(match): return re.sub(fr"{dummy_char}+", repl, dummy_text) def category_id_dict(self, toc=None, cat_head="", code_head=""): + """Returns a dict of unique category ids based on the ToC + :param toc: ToC object (optional, default is None) + :param cat_head: String, (optional, default is "" - an empty string) + :param code_head: String, (optional, default is "" - an empty string) + """ if toc is None: if not self._category_id_dict: self._category_id_dict = self.category_id_dict(self.get_toc()) @@ -6233,6 +6285,12 @@ def category_id_dict(self, toc=None, cat_head="", code_head=""): return d def simplify_toc(self, lang=None, toc_node=None, path=None): + """ + Simplifies the table of contents (ToC) + :param lang: 'en' or 'he', default is None (optional) + :param toc_node: ToC Node, default is None (optional) + :param path: Node Path, default is None (optional) + """ is_root = toc_node is None and path is None toc_node = toc_node if toc_node else self.get_toc() path = path if path else [] From 7a1aa2a3e8e5ab86a98088150a3c76a041c1c439 Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Thu, 11 Jan 2024 18:05:11 +0200 Subject: [PATCH 690/756] chore(docstrings): Improve modify bulk text docstring --- sefaria/tracker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sefaria/tracker.py b/sefaria/tracker.py index 0caf7ba5b4..7e68968ea7 100644 --- a/sefaria/tracker.py +++ b/sefaria/tracker.py @@ -46,7 +46,7 @@ def modify_bulk_text(user: int, version: model.Version, text_map: dict, vsource= """ user: user ID of user making modification version: version object of text being modified - text_map: dict with segment ref keys and text values. Each key/value pair represents a segment that should be modified. Segments that don't have changes will be ignored. + text_map: dict with segment ref keys and text values. Each key/value pair represents a segment that should be modified. Segments that don't have changes will be ignored. The key should be the tref, and the value the text, ex: {'Mishnah Berakhot 1:1': 'Text of the Mishnah goes here'} vsource: optional parameter to set the version source of the version. not sure why this is here. I copied it from modify_text. """ def populate_change_map(old_text, en_tref, he_tref, _): From 38704d33665292719493a3c1ba8c862baa459071 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Mon, 15 Jan 2024 09:49:31 +0200 Subject: [PATCH 691/756] fix(Search Result): markdown should be properly rendered --- static/js/SearchResultList.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/SearchResultList.jsx b/static/js/SearchResultList.jsx index b09e91b170..6fc8173789 100644 --- a/static/js/SearchResultList.jsx +++ b/static/js/SearchResultList.jsx @@ -69,7 +69,7 @@ const SearchTopic = (props) => { {topicCategory} {"enDesc" in props.topic ? <div className="topicDescSearchResult systemText"> - <InterfaceText text={{en:props.topic.enDesc, he:props.topic.heDesc}}/> + <InterfaceText markdown={{en:props.topic.enDesc, he:props.topic.heDesc}}/> </div> : null} {sourcesSheetsDiv} </div> From ea2a6cca4811c1de5bea6320dacccc8989f03529 Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Mon, 15 Jan 2024 12:44:41 +0200 Subject: [PATCH 692/756] chore(docstrings): Addressing some of the comments --- sefaria/model/text.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index eb0cc55daa..c735796fc7 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -5694,7 +5694,9 @@ def get_term(self, term_name): def get_topic(self, slug): """ - Returns the corresponding topic for a given slug + Returns a dictionary containing with keys "en" and "he". + The "en" field has a value of the topic's English primary title, and the "he" field has a + value of the topic's Hebrew primary title. :param slug: String :returns: topic map for the given slug Dictionary """ @@ -5712,7 +5714,11 @@ def get_topic_mapping(self, rebuild=False): def _build_topic_mapping(self): """ - Builds the topic mapping + Builds the topic mapping. The topic mapping is a dictionary with one key, the slug of the topic. + That key contains the value of another dictionary, containing with keys "en" and "he". + The "en" field has a value of the topic's English primary title, and the "he" field has a + value of the topic's Hebrew primary title. + :returns: topic map for the given slug Dictionary """ from .topic import Topic, TopicSet self._topic_mapping = {t.slug: {"en": t.get_primary_title("en"), "he": t.get_primary_title("he")} for t in TopicSet()} @@ -5845,7 +5851,7 @@ def get_text_titles_json(self, lang="en", rebuild=False): def reset_text_titles_cache(self): """ - Resets the text titles for all languages + Resets the text titles for all languages by clearing the existing titles from the cache. """ for lang in self.langs: scache.delete_shared_cache_elem('books_' + lang) @@ -6093,10 +6099,11 @@ def get_multi_title_regex_string(self, titles, lang, for_js=False, anchored=Fals # do we want to move this to the schema node? We'd still have to pass the title... def get_regex_string(self, title, lang, for_js=False, anchored=False, capture_title=False, parentheses=False): """ - Given a string with a Ref not in Sefaria format (i.e. "See Genesis 2 3" as opposed to "Genesis 2:3", - it returns a regex for the Ref. + Given a string with a Ref, it returns a regex for the Ref. + This works for references not in Sefaria format (i.e. "See Genesis 2 3" as opposed to "Genesis 2:3", + as well as for references in Sefaria format. If the language is 'en', it calls the full_regex() function which returns the regex, whereas for 'he' we - limit the search to content inside parenthesis to limit false positives (i.e. שבת לא תעשה could be caught by + limit the regex creation to content inside parenthesis to limit false positives (i.e. שבת לא תעשה could be caught by mistake as Shabbat 31) :param title: String :param lang: 'en' or 'he' From e9116736c385aabeee90e0ca6b513d32306db78b Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Mon, 15 Jan 2024 17:47:24 +0200 Subject: [PATCH 693/756] chore(docstrings): finish addressing comments --- sefaria/model/text.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index c735796fc7..44da0a0c4b 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -6103,8 +6103,8 @@ def get_regex_string(self, title, lang, for_js=False, anchored=False, capture_ti This works for references not in Sefaria format (i.e. "See Genesis 2 3" as opposed to "Genesis 2:3", as well as for references in Sefaria format. If the language is 'en', it calls the full_regex() function which returns the regex, whereas for 'he' we - limit the regex creation to content inside parenthesis to limit false positives (i.e. שבת לא תעשה could be caught by - mistake as Shabbat 31) + limit the regex creation to content inside parenthesis to limit false positives (i.e. the phrase שבת לא תעשה + could be caught by mistake as Shabbat 31) :param title: String :param lang: 'en' or 'he' :param for_js: Boolean (default set to False, optional) @@ -6264,10 +6264,11 @@ def repl(match): return re.sub(fr"{dummy_char}+", repl, dummy_text) def category_id_dict(self, toc=None, cat_head="", code_head=""): - """Returns a dict of unique category ids based on the ToC - :param toc: ToC object (optional, default is None) - :param cat_head: String, (optional, default is "" - an empty string) - :param code_head: String, (optional, default is "" - an empty string) + """Returns a dict of unique category ids based on the ToC, with the + values being the category IDs. + :param toc: ToC object (optional, default is None) + :param cat_head: String, (optional, default is "" - an empty string) + :param code_head: String, (optional, default is "" - an empty string) """ if toc is None: if not self._category_id_dict: From 677387d9f44dbb20b2bd3ac7f0f44ea4679689b6 Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Tue, 16 Jan 2024 09:35:40 +0200 Subject: [PATCH 694/756] chore(docstrings): Fix typos --- sefaria/model/text.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index 44da0a0c4b..4b7e61f38a 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -5694,7 +5694,7 @@ def get_term(self, term_name): def get_topic(self, slug): """ - Returns a dictionary containing with keys "en" and "he". + Returns a dictionary containing the keys "en" and "he". The "en" field has a value of the topic's English primary title, and the "he" field has a value of the topic's Hebrew primary title. :param slug: String @@ -5715,7 +5715,7 @@ def get_topic_mapping(self, rebuild=False): def _build_topic_mapping(self): """ Builds the topic mapping. The topic mapping is a dictionary with one key, the slug of the topic. - That key contains the value of another dictionary, containing with keys "en" and "he". + That key contains the value of another dictionary, containing the keys "en" and "he". The "en" field has a value of the topic's English primary title, and the "he" field has a value of the topic's Hebrew primary title. :returns: topic map for the given slug Dictionary From c180b5e99849dda6e52b9735df09e4fdeb5500b2 Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Tue, 16 Jan 2024 09:36:58 +0200 Subject: [PATCH 695/756] chore(docstrings): Improve topic mapping docstring --- sefaria/model/text.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index 4b7e61f38a..008d5c7cb4 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -5714,8 +5714,9 @@ def get_topic_mapping(self, rebuild=False): def _build_topic_mapping(self): """ - Builds the topic mapping. The topic mapping is a dictionary with one key, the slug of the topic. - That key contains the value of another dictionary, containing the keys "en" and "he". + Builds the topic mapping. The topic mapping is a dictionary with keys, where each key + is a slug of a topic. + That key contains the value of another dictionary, with the keys "en" and "he". The "en" field has a value of the topic's English primary title, and the "he" field has a value of the topic's Hebrew primary title. :returns: topic map for the given slug Dictionary From bf4a08209e0d50d5cfd5a0e42621749201c715c1 Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Tue, 16 Jan 2024 09:38:56 +0200 Subject: [PATCH 696/756] chore(docstrings): Refine get_regex_string() docstring --- sefaria/model/text.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sefaria/model/text.py b/sefaria/model/text.py index 008d5c7cb4..79e398fcb1 100644 --- a/sefaria/model/text.py +++ b/sefaria/model/text.py @@ -6100,7 +6100,7 @@ def get_multi_title_regex_string(self, titles, lang, for_js=False, anchored=Fals # do we want to move this to the schema node? We'd still have to pass the title... def get_regex_string(self, title, lang, for_js=False, anchored=False, capture_title=False, parentheses=False): """ - Given a string with a Ref, it returns a regex for the Ref. + Given a book title, this function returns a regex for a Ref. This works for references not in Sefaria format (i.e. "See Genesis 2 3" as opposed to "Genesis 2:3", as well as for references in Sefaria format. If the language is 'en', it calls the full_regex() function which returns the regex, whereas for 'he' we From 2181ef338af0401978374a40d0a86fb607bf77d4 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Tue, 16 Jan 2024 11:04:47 +0200 Subject: [PATCH 697/756] fix(search): remove newline that was auto-added --- .../templates/cronjob/reindex-elasticsearch.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml b/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml index 7aab052a47..06bae04bdb 100644 --- a/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml +++ b/helm-chart/sefaria-project/templates/cronjob/reindex-elasticsearch.yaml @@ -64,8 +64,7 @@ spec: command: ["bash"] args: [ "-c", - "mkdir -p /log && touch /log/sefaria_book_errors.log && pip install numpy elasticsearch==8.8.2 git+https://github.com/Sefaria/elasticsearch-dsl-py@v8.0 - .0#egg=elasticsearch-dsl && /app/run /app/scripts/scheduled/reindex_elasticsearch_cronjob.py" + "mkdir -p /log && touch /log/sefaria_book_errors.log && pip install numpy elasticsearch==8.8.2 git+https://github.com/Sefaria/elasticsearch-dsl-py@v8.0.0#egg=elasticsearch-dsl && /app/run /app/scripts/scheduled/reindex_elasticsearch_cronjob.py" ] restartPolicy: Never volumes: From e4e2748f0209a5b38dda4984850a8dde05eb135d Mon Sep 17 00:00:00 2001 From: Lev Israel <eliezer.israel@gmail.com> Date: Thu, 18 Jan 2024 17:31:04 +0200 Subject: [PATCH 698/756] fix: Fix recursion call in `TreeNode.traverse_tree`. Add some docs to `Topic.merge` --- sefaria/model/schema.py | 2 +- sefaria/model/topic.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/sefaria/model/schema.py b/sefaria/model/schema.py index 82e89fe656..0efb7bbbe2 100644 --- a/sefaria/model/schema.py +++ b/sefaria/model/schema.py @@ -588,7 +588,7 @@ def traverse_tree(self, callback, **kwargs): """ callback(self, **kwargs) for child in self.children: - child.traverse_to_string(callback, **kwargs) + child.traverse_tree(callback, **kwargs) def traverse_to_string(self, callback, depth=0, **kwargs): st = callback(self, depth, **kwargs) diff --git a/sefaria/model/topic.py b/sefaria/model/topic.py index 2304a7a5b4..a916c78c16 100644 --- a/sefaria/model/topic.py +++ b/sefaria/model/topic.py @@ -276,6 +276,9 @@ def set_slug(self, new_slug) -> None: def merge(self, other: Union['Topic', str]) -> None: """ + Merge `other` into `self`. This means that all data from `other` will be merged into self. + Data from self takes precedence in the event of conflict. + Links to `other` will be changed to point to `self` and `other` will be deleted. :param other: Topic or old slug to migrate from :return: None """ From 95b32bcb49ff91809f6d8e92f1785f6b45992339 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Mon, 22 Jan 2024 10:29:53 +0200 Subject: [PATCH 699/756] chore: make base_text_titles and base_text_mapping available in Sefaria.toc --- sefaria/model/category.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sefaria/model/category.py b/sefaria/model/category.py index 9339521c07..6c091ede63 100644 --- a/sefaria/model/category.py +++ b/sefaria/model/category.py @@ -294,8 +294,8 @@ def _explicit_order_and_title(node): else: cat.children.sort(key=_explicit_order_and_title) - def _make_index_node(self, index, old_title=None, mobile=False): - d = index.toc_contents(include_first_section=False, include_flags=False) + def _make_index_node(self, index, old_title=None, mobile=False, include_first_section=False): + d = index.toc_contents(include_first_section=include_first_section, include_flags=False, include_base_texts=True) title = old_title or d["title"] @@ -303,7 +303,8 @@ def _make_index_node(self, index, old_title=None, mobile=False): vs = self._vs_lookup.get(title, {}) d["firstSection"] = vs.get("first_section_ref", None) - if "base_text_titles" in d and len(d["base_text_titles"]) > 0: + if "base_text_titles" in d and len(d["base_text_titles"]) > 0 and include_first_section: + # `d["firstSection"]` assumes `include_first_section` is True d["refs_to_base_texts"] = {btitle: self._first_comment_lookup.get(frozenset([btitle, title]), d["firstSection"]) for btitle in d["base_text_titles"] From a70e5320e6b472edf1d74e1c00d295309b9bbbb7 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Mon, 22 Jan 2024 10:56:55 +0200 Subject: [PATCH 700/756] chore: refactor showBaseText and openPanelAt to use getCurrentlyVisibleAndHighlightedRefs --- static/js/ReaderApp.jsx | 13 +------------ static/js/ReaderPanel.jsx | 13 ++----------- static/js/sefaria/sefaria.js | 15 +++++++++++++++ 3 files changed, 18 insertions(+), 23 deletions(-) diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index 5c30ecf5fc..24dc7bc2c9 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -1441,18 +1441,7 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { ...options }); } else { // Text - let refs, currentlyVisibleRef, highlightedRefs; - if (ref.constructor === Array) { - // When called with an array, set highlight for the whole spanning range of the array - refs = ref; - currentlyVisibleRef = Sefaria.normRef(ref); - const splitArray = refs.map(ref => Sefaria.splitRangingRef(ref)); - highlightedRefs = [].concat.apply([], splitArray); - } else { - refs = [ref]; - currentlyVisibleRef = ref; - highlightedRefs = []; - } + let {refs, currentlyVisibleRef, highlightedRefs} = Sefaria.getCurrentlyVisibleAndHighlightedRefs(ref); //console.log("Higlighted refs:", highlightedRefs) panel = this.makePanelState({ refs, diff --git a/static/js/ReaderPanel.jsx b/static/js/ReaderPanel.jsx index 5bca1cebd9..d17b124a3d 100644 --- a/static/js/ReaderPanel.jsx +++ b/static/js/ReaderPanel.jsx @@ -247,22 +247,13 @@ class ReaderPanel extends Component { // allow it to return to bilingual. this.state.settings.language = "bilingual"; } - let refs, currentlyVisibleRef, highlightedRefs; - if (ref.constructor === Array) { - // When called with an array, set highlight for the whole spanning range - refs = ref; - currentlyVisibleRef = Sefaria.humanRef(ref); - let splitArray = refs.map(ref => Sefaria.splitRangingRef(ref)); - highlightedRefs = [].concat.apply([], splitArray); - } else { + let {refs, currentlyVisibleRef, highlightedRefs} = Sefaria.getCurrentlyVisibleAndHighlightedRefs(ref); + if (ref.constructor !== Array) { const oRef = Sefaria.parseRef(ref); if (oRef.book === "Sheet") { this.openSheet(ref); return; } - refs = [ref]; - currentlyVisibleRef = ref; - highlightedRefs = []; } if (this.replaceHistory) { diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index a39d3b2566..2b9d660b61 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -2183,6 +2183,21 @@ _media: {}, return result; }, + getCurrentlyVisibleAndHighlightedRefs(ref) { + let refs, currentlyVisibleRef, highlightedRefs; + if (ref.constructor === Array) { + // When called with an array, set highlight for the whole spanning range of the array + refs = ref; + currentlyVisibleRef = Sefaria.normRef(ref); + const splitArray = refs.map(ref => Sefaria.splitRangingRef(ref)); + highlightedRefs = [].concat.apply([], splitArray); + } else { + refs = [ref]; + currentlyVisibleRef = ref; + highlightedRefs = []; + } + return {refs, currentlyVisibleRef, highlightedRefs}; + }, commentaryList: function(title, toc) { var title = arguments.length == 0 || arguments[0] === undefined ? null : arguments[0]; /** Returns the list of commentaries for 'title' which are found in Sefaria.toc **/ From 481d467502fd8e9e33902a30b5a9427269e14cfa Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Mon, 22 Jan 2024 12:56:29 +0200 Subject: [PATCH 701/756] feat(Commentary): opens Genesis instead of Rashi on Genesis, but without sidebar --- static/js/ReaderApp.jsx | 2 +- static/js/ReaderPanel.jsx | 6 +++--- static/js/sefaria/sefaria.js | 35 ++++++++++++++++++++++++++++++++++- 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index 24dc7bc2c9..84aec9f533 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -1441,7 +1441,7 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { ...options }); } else { // Text - let {refs, currentlyVisibleRef, highlightedRefs} = Sefaria.getCurrentlyVisibleAndHighlightedRefs(ref); + let {refs, currentlyVisibleRef, highlightedRefs} = Sefaria.getCurrentlyVisibleAndHighlightedRefs(ref, true); //console.log("Higlighted refs:", highlightedRefs) panel = this.makePanelState({ refs, diff --git a/static/js/ReaderPanel.jsx b/static/js/ReaderPanel.jsx index d17b124a3d..9891b0fd20 100644 --- a/static/js/ReaderPanel.jsx +++ b/static/js/ReaderPanel.jsx @@ -171,7 +171,7 @@ class ReaderPanel extends Component { } } handleTextListClick(ref, replaceHistory, currVersions) { - this.showBaseText(ref, replaceHistory, currVersions); + this.showBaseText(ref, replaceHistory, currVersions, [], false); } updateCurrVersionsToMatchAPIResult(enVTitle, heVTitle) { const newVersions = { @@ -236,7 +236,7 @@ class ReaderPanel extends Component { highlightedRefsInSheet }); } - showBaseText(ref, replaceHistory, currVersions={en: null, he: null}, filter=[]) { + showBaseText(ref, replaceHistory, currVersions={en: null, he: null}, filter=[], convertCommToBase=true) { // Set the current primary text `ref`, which may be either a string or an array of strings. // `replaceHistory` - bool whether to replace browser history rather than push for this change if (!ref) { return; } @@ -247,7 +247,7 @@ class ReaderPanel extends Component { // allow it to return to bilingual. this.state.settings.language = "bilingual"; } - let {refs, currentlyVisibleRef, highlightedRefs} = Sefaria.getCurrentlyVisibleAndHighlightedRefs(ref); + let {refs, currentlyVisibleRef, highlightedRefs} = Sefaria.getCurrentlyVisibleAndHighlightedRefs(ref, convertCommToBase); if (ref.constructor !== Array) { const oRef = Sefaria.parseRef(ref); if (oRef.book === "Sheet") { diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index 2b9d660b61..caa3089314 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -2183,7 +2183,40 @@ _media: {}, return result; }, - getCurrentlyVisibleAndHighlightedRefs(ref) { + convertCommToBase(ref) { + const parsedRef = Sefaria.parseRef(ref); + const book = Sefaria.index(parsedRef.index); + if (!book) { + return ref; + } + if (book?.dependence === "Commentary" && !!book?.base_text_titles && !!book?.base_text_mapping && book?.base_text_titles.length === 1) { + const base_text = book.base_text_titles[0]; + const many_to_one = book.base_text_mapping.startsWith("many_to_one"); // four options, two start with many_to_one and two start with one_to_one + if (parsedRef.sections.length <= 2 || !many_to_one) { + // Rashi on Genesis 1:2 => Genesis 1:2 and Rashi on Genesis => Genesis. + return ref.replace(book.title, base_text); + } + else if (many_to_one) { + // Rashi on Genesis 1:2:1 => Genesis 1:2 + parsedRef.sections = parsedRef.sections.slice(0, parsedRef.sections.length - 1); + parsedRef.toSections = parsedRef.toSections.slice(0, parsedRef.toSections.length - 1); + parsedRef.book = parsedRef.index = parsedRef.index.replace(book.title, base_text); + parsedRef.ref = parsedRef.ref.replace(book.title, base_text); + parsedRef.ref = parsedRef.ref.split(' ').slice(0, -1).join(' '); + return Sefaria.makeRef(parsedRef); + } + } + return ref; + }, + getCurrentlyVisibleAndHighlightedRefs(ref, convertCommToBase) { + if (convertCommToBase) { + if (ref.constructor === Array) { + ref = ref.map(x => Sefaria.convertCommToBase(x)); + } + else { + ref = Sefaria.convertCommToBase(ref); + } + } let refs, currentlyVisibleRef, highlightedRefs; if (ref.constructor === Array) { // When called with an array, set highlight for the whole spanning range of the array From 18f5dffa6f666329322e31ae28697435bb5ce259 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Wed, 24 Jan 2024 15:56:23 +0200 Subject: [PATCH 702/756] chore: refactor showBaseText to use openPanelAt --- static/js/ReaderApp.jsx | 39 +++++++++++++++++++++++++++++++-------- static/js/ReaderPanel.jsx | 36 ++++++++++++++++-------------------- 2 files changed, 47 insertions(+), 28 deletions(-) diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index 84aec9f533..76c2caa3de 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -1422,13 +1422,13 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { this.state.panels = []; // temporarily clear panels directly in state, set properly with setState in openPanelAt this.openPanelAt(0, ref, currVersions, options); } - openPanelAt(n, ref, currVersions, options, replace) { + openPanelAt(n, ref, currVersions, options, replace, convertCommToBase=true, replaceHistory=true) { // Open a new panel after `n` with the new ref // If `replace`, replace existing panel at `n`, otherwise insert new panel at `n` // If book level, Open book toc const parsedRef = Sefaria.parseRef(ref); const index = Sefaria.index(ref); // Do we have to worry about normalization, as in Header.subimtSearch()? - let panel; + let panel, connectionPanel; if (index) { panel = this.makePanelState({"menuOpen": "book toc", "bookRef": index.title}); } else if (parsedRef.book === "Sheet") { @@ -1441,7 +1441,7 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { ...options }); } else { // Text - let {refs, currentlyVisibleRef, highlightedRefs} = Sefaria.getCurrentlyVisibleAndHighlightedRefs(ref, true); + let {refs, currentlyVisibleRef, highlightedRefs} = Sefaria.getCurrentlyVisibleAndHighlightedRefs(ref, convertCommToBase); //console.log("Higlighted refs:", highlightedRefs) panel = this.makePanelState({ refs, @@ -1450,12 +1450,34 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { currentlyVisibleRef, mode: "Text", ...options }); + // connectionPanel = this.makePanelState({ + // refs, + // currVersions, + // highlightedRefs, + // currentlyVisibleRef, mode: "Connections", + // filter: ["Rashi"], + // recentFilters: ["Rashi"], + // connectionsMode: "TextList", + // ...options + // }); + } + if (!connectionPanel) { + const newPanels = this.state.panels.slice(); + newPanels.splice(replace ? n : n + 1, replace ? 1 : 0, panel); + this.setState({panels: newPanels}); + if (replaceHistory) { + this.saveLastPlace(panel, n + 1); + } + } + else { + const newPanels = this.state.panels.slice(); + newPanels.splice(replace ? n : n+1, replace ? 1 : 0, panel); + newPanels.push(connectionPanel); + this.setState({panels: newPanels}); + if (replaceHistory) { + this.saveLastPlace(panel, n + 1); + } } - - const newPanels = this.state.panels.slice(); - newPanels.splice(replace ? n : n+1, replace ? 1 : 0, panel); - this.setState({panels: newPanels}); - this.saveLastPlace(panel, n+1); } openPanelAtEnd(ref, currVersions) { this.openPanelAt(this.state.panels.length+1, ref, currVersions); @@ -2096,6 +2118,7 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { var classes = classNames({readerPanelBox: 1, sidebar: panel.mode == "Connections"}); panels.push(<div className={classes} style={style} key={key}> <ReaderPanel + openPanelAt={this.openPanelAt} panelPosition={i} initialState={panel} interfaceLang={this.props.interfaceLang} diff --git a/static/js/ReaderPanel.jsx b/static/js/ReaderPanel.jsx index 9891b0fd20..c22b7ec1a1 100644 --- a/static/js/ReaderPanel.jsx +++ b/static/js/ReaderPanel.jsx @@ -167,7 +167,7 @@ class ReaderPanel extends Component { if (this.props.multiPanel) { this.props.onCitationClick(citationRef, textRef, replace, currVersions); } else { - this.showBaseText(citationRef, replace, currVersions); + this.showBaseText(citationRef, replace, currVersions, [], false); } } handleTextListClick(ref, replaceHistory, currVersions) { @@ -247,7 +247,6 @@ class ReaderPanel extends Component { // allow it to return to bilingual. this.state.settings.language = "bilingual"; } - let {refs, currentlyVisibleRef, highlightedRefs} = Sefaria.getCurrentlyVisibleAndHighlightedRefs(ref, convertCommToBase); if (ref.constructor !== Array) { const oRef = Sefaria.parseRef(ref); if (oRef.book === "Sheet") { @@ -255,24 +254,21 @@ class ReaderPanel extends Component { return; } } - - if (this.replaceHistory) { - this.props.saveLastPlace({ mode: "Text", refs, currVersions, settings: this.state.settings }, this.props.panelPosition); - } - this.conditionalSetState({ - mode: "Text", - refs, - filter, - currentlyVisibleRef, - currVersions, - highlightedRefs, - recentFilters: [], - menuOpen: null, - compare: false, - sheetID: null, - connectionsMode: "Resources", - settings: this.state.settings - }); + this.props.openPanelAt(this.props.panelPosition, ref, currVersions, {settings: this.state.settings}, true, convertCommToBase, this.replaceHistory); + // this.conditionalSetState({ + // mode: "Text", + // refs, + // filter, + // currentlyVisibleRef, + // currVersions, + // highlightedRefs, + // recentFilters: [], + // menuOpen: null, + // compare: false, + // sheetID: null, + // connectionsMode: "Resources", + // settings: this.state.settings + // }); } openSheet(sheetRef, replaceHistory) { this.replaceHistory = Boolean(replaceHistory); From 556baf6ec1b96c0210e7a8221a1bb7d56ed83260 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Thu, 25 Jan 2024 09:47:39 +0200 Subject: [PATCH 703/756] fix(TranslationBox): avoid sorting translations when there are none. --- static/js/TranslationsBox.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/TranslationsBox.jsx b/static/js/TranslationsBox.jsx index 117145f759..8a1d7e9723 100644 --- a/static/js/TranslationsBox.jsx +++ b/static/js/TranslationsBox.jsx @@ -34,7 +34,7 @@ class TranslationsBox extends Component { let currentVersionsByActualLangs = Sefaria.transformVersionObjectsToByActualLanguageKeys(this.props.currObjectVersions); for(let [lang,ver] of Object.entries(currentVersionsByActualLangs)){ if (!this._excludedLangs.includes(lang)) { - versionsByLang[lang].sort((a, b) => { + versionsByLang[lang]?.sort((a, b) => { return a.versionTitle === ver.versionTitle ? -1 : b.versionTitle === ver.versionTitle ? 1 : 0; }); } From acc4102d712386de2449dc19aa852cc0806ce6aa Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Thu, 25 Jan 2024 11:07:43 +0200 Subject: [PATCH 704/756] helm(search): dont set Content-Type header. ES no longer requires this header to be set and it's problematic to set it to application/json because ES has a strange requirement that if Accept is set to "application/vnd.elasticsearch+json; compatible-with=8" (which is sent from our Python ES client) than Content-Type needs to be set to the same thing. --- helm-chart/sefaria-project/templates/configmap/nginx.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/helm-chart/sefaria-project/templates/configmap/nginx.yaml b/helm-chart/sefaria-project/templates/configmap/nginx.yaml index 0c2fba7288..9841b891db 100644 --- a/helm-chart/sefaria-project/templates/configmap/nginx.yaml +++ b/helm-chart/sefaria-project/templates/configmap/nginx.yaml @@ -117,7 +117,6 @@ data: # allow urls which aren't caught by regex above location /api/search/ { rewrite ^/(?:api/search)/(.*)$ /$1 break; - proxy_set_header Content-Type application/json; # es 6.0 requires this header proxy_set_header Authorization "Basic ${ELASTIC_AUTH_HEADER}"; add_header 'Access-Control-Allow-Origin' ''; proxy_pass http://elasticsearch_upstream/; From e6be9244434acb54fd90284bfb73294029efa35f Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Thu, 25 Jan 2024 11:12:27 +0200 Subject: [PATCH 705/756] chore: refactor using Sefaria.getBaseRefAndFilter --- static/js/ReaderApp.jsx | 61 +++++++++++++++---------- static/js/sefaria/sefaria.js | 88 ++++++++++++++++++------------------ 2 files changed, 80 insertions(+), 69 deletions(-) diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index 76c2caa3de..ba665323ad 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -1441,42 +1441,53 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { ...options }); } else { // Text - let {refs, currentlyVisibleRef, highlightedRefs} = Sefaria.getCurrentlyVisibleAndHighlightedRefs(ref, convertCommToBase); + let filter; + ({ref, filter} = Sefaria.getBaseRefAndFilter(ref, convertCommToBase)); + let refs, currentlyVisibleRef, highlightedRefs; + if (ref.constructor === Array) { + // When called with an array, set highlight for the whole spanning range of the array + refs = ref; + currentlyVisibleRef = Sefaria.normRef(ref); + const splitArray = refs.map(ref => Sefaria.splitRangingRef(ref)); + highlightedRefs = [].concat.apply([], splitArray); + } else { + refs = [ref]; + currentlyVisibleRef = ref; + highlightedRefs = []; + } //console.log("Higlighted refs:", highlightedRefs) panel = this.makePanelState({ refs, currVersions, highlightedRefs, + filter: filter, + recentFilters: filter, currentlyVisibleRef, mode: "Text", ...options }); - // connectionPanel = this.makePanelState({ - // refs, - // currVersions, - // highlightedRefs, - // currentlyVisibleRef, mode: "Connections", - // filter: ["Rashi"], - // recentFilters: ["Rashi"], - // connectionsMode: "TextList", - // ...options - // }); - } - if (!connectionPanel) { - const newPanels = this.state.panels.slice(); - newPanels.splice(replace ? n : n + 1, replace ? 1 : 0, panel); - this.setState({panels: newPanels}); - if (replaceHistory) { - this.saveLastPlace(panel, n + 1); + if (filter.length > 0) { + connectionPanel = this.makePanelState({ + refs, + currVersions, + highlightedRefs, + currentlyVisibleRef, mode: "Connections", + filter: filter, + recentFilters: filter, + connectionsMode: "TextList", + ...options + }); } + } - else { - const newPanels = this.state.panels.slice(); - newPanels.splice(replace ? n : n+1, replace ? 1 : 0, panel); + + const newPanels = this.state.panels.slice(); + newPanels.splice(replace ? n : n+1, replace ? 1 : 0, panel); + if (connectionPanel) { newPanels.push(connectionPanel); - this.setState({panels: newPanels}); - if (replaceHistory) { - this.saveLastPlace(panel, n + 1); - } + } + this.setState({panels: newPanels}); + if (replaceHistory) { + this.saveLastPlace(panel, n + 1); } } openPanelAtEnd(ref, currVersions) { diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index caa3089314..69174fc962 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -2183,53 +2183,53 @@ _media: {}, return result; }, - convertCommToBase(ref) { - const parsedRef = Sefaria.parseRef(ref); - const book = Sefaria.index(parsedRef.index); - if (!book) { - return ref; - } - if (book?.dependence === "Commentary" && !!book?.base_text_titles && !!book?.base_text_mapping && book?.base_text_titles.length === 1) { - const base_text = book.base_text_titles[0]; - const many_to_one = book.base_text_mapping.startsWith("many_to_one"); // four options, two start with many_to_one and two start with one_to_one - if (parsedRef.sections.length <= 2 || !many_to_one) { - // Rashi on Genesis 1:2 => Genesis 1:2 and Rashi on Genesis => Genesis. - return ref.replace(book.title, base_text); - } - else if (many_to_one) { - // Rashi on Genesis 1:2:1 => Genesis 1:2 - parsedRef.sections = parsedRef.sections.slice(0, parsedRef.sections.length - 1); - parsedRef.toSections = parsedRef.toSections.slice(0, parsedRef.toSections.length - 1); - parsedRef.book = parsedRef.index = parsedRef.index.replace(book.title, base_text); - parsedRef.ref = parsedRef.ref.replace(book.title, base_text); - parsedRef.ref = parsedRef.ref.split(' ').slice(0, -1).join(' '); - return Sefaria.makeRef(parsedRef); - } + isCommentaryWithBaseText(book) { + return book?.dependence === "Commentary" && !!book?.base_text_titles && !!book?.base_text_mapping && book?.base_text_titles.length === 1; + }, + convertCommToBase(parsedRef, book) { + if (!book || !this.isCommentaryWithBaseText(book)) { + return parsedRef.ref; + } + const base_text = book.base_text_titles[0]; + const many_to_one = book.base_text_mapping.startsWith("many_to_one"); // four options, two start with many_to_one and two start with one_to_one + if (parsedRef.sections.length <= 2 || !many_to_one) { + // Rashi on Genesis 1:2 => Genesis 1:2 and Rashi on Genesis => Genesis. + parsedRef.ref = parsedRef.ref.replace(book.title, base_text); + return Sefaria.humanRef(parsedRef.ref); + } + else if (many_to_one) { + // Rashi on Genesis 1:2:1 => Genesis 1:2 + parsedRef.sections = parsedRef.sections.slice(0, parsedRef.sections.length - 1); + parsedRef.toSections = parsedRef.toSections.slice(0, parsedRef.toSections.length - 1); + parsedRef.book = parsedRef.index = parsedRef.index.replace(book.title, base_text); + parsedRef.ref = parsedRef.ref.replace(book.title, base_text); + parsedRef.ref = parsedRef.ref.split(' ').slice(0, -1).join(' '); + return Sefaria.humanRef(Sefaria.makeRef(parsedRef)); + } + return parsedRef.ref; + }, + getBaseRefAndFilter(ref, convertCommToBase) { + if (!convertCommToBase) { + return {ref: ref, filter: []}; + } + let parsedRefs = []; + let firstParsedRef, book; + if (ref.constructor === Array) { + parsedRefs = ref.map(x => Sefaria.parseRef(x)); + firstParsedRef = parsedRefs[0]; + book = Sefaria.index(firstParsedRef.index); + ref = parsedRefs.map(x => Sefaria.convertCommToBase(x, book)); } - return ref; - }, - getCurrentlyVisibleAndHighlightedRefs(ref, convertCommToBase) { - if (convertCommToBase) { - if (ref.constructor === Array) { - ref = ref.map(x => Sefaria.convertCommToBase(x)); - } - else { - ref = Sefaria.convertCommToBase(ref); - } + else { + firstParsedRef = Sefaria.parseRef(ref); + book = Sefaria.index(firstParsedRef.index); + ref = Sefaria.convertCommToBase(firstParsedRef, book); } - let refs, currentlyVisibleRef, highlightedRefs; - if (ref.constructor === Array) { - // When called with an array, set highlight for the whole spanning range of the array - refs = ref; - currentlyVisibleRef = Sefaria.normRef(ref); - const splitArray = refs.map(ref => Sefaria.splitRangingRef(ref)); - highlightedRefs = [].concat.apply([], splitArray); - } else { - refs = [ref]; - currentlyVisibleRef = ref; - highlightedRefs = []; + let filter = []; + if (this.isCommentaryWithBaseText(book)) { + filter = [book.collectiveTitle]; } - return {refs, currentlyVisibleRef, highlightedRefs}; + return {ref: ref, filter: filter}; }, commentaryList: function(title, toc) { var title = arguments.length == 0 || arguments[0] === undefined ? null : arguments[0]; From beda64935c1e272a6d77220cd6673dca1d075ddc Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Thu, 25 Jan 2024 12:36:31 +0200 Subject: [PATCH 706/756] fix(Commentary Redirects): should always return humanRef --- static/js/sefaria/sefaria.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index 69174fc962..7dfe3be903 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -2188,7 +2188,7 @@ _media: {}, }, convertCommToBase(parsedRef, book) { if (!book || !this.isCommentaryWithBaseText(book)) { - return parsedRef.ref; + return Sefaria.humanRef(parsedRef.ref); } const base_text = book.base_text_titles[0]; const many_to_one = book.base_text_mapping.startsWith("many_to_one"); // four options, two start with many_to_one and two start with one_to_one @@ -2206,7 +2206,7 @@ _media: {}, parsedRef.ref = parsedRef.ref.split(' ').slice(0, -1).join(' '); return Sefaria.humanRef(Sefaria.makeRef(parsedRef)); } - return parsedRef.ref; + return Sefaria.humanRef(parsedRef.ref); }, getBaseRefAndFilter(ref, convertCommToBase) { if (!convertCommToBase) { From 9f0bf2348a88dc63f8745d89c64edb6b5f86cb8f Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Thu, 25 Jan 2024 12:46:35 +0200 Subject: [PATCH 707/756] fix(search): move encapsulation of SearchTotal to search.js This ensures that both Dicta and Sefaria queries are using the same format of SearchTotal. --- static/js/SearchResultList.jsx | 36 ++++---------------------------- static/js/sefaria/search.js | 20 +++++++++++------- static/js/sefaria/searchTotal.js | 23 ++++++++++++++++++++ 3 files changed, 39 insertions(+), 40 deletions(-) create mode 100644 static/js/sefaria/searchTotal.js diff --git a/static/js/SearchResultList.jsx b/static/js/SearchResultList.jsx index 9c37f9a520..d2b6f65aaa 100644 --- a/static/js/SearchResultList.jsx +++ b/static/js/SearchResultList.jsx @@ -6,7 +6,7 @@ import extend from 'extend'; import classNames from 'classnames'; import $ from './sefaria/sefariaJquery'; import Sefaria from './sefaria/sefaria'; -import { FilterNode } from './sefaria/search'; +import { SearchTotal } from "./sefaria/searchTotal"; import SearchTextResult from './SearchTextResult'; import SearchSheetResult from './SearchSheetResult'; import SearchFilters from './SearchFilters'; @@ -76,34 +76,6 @@ const SearchTopic = (props) => { } -class SearchTotal { - constructor({value=0, relation="eq"} = {}) { - this._value = value; - this._relation = relation; - } - getValue = () => this._value; - add = (num) => this._value += num; - asString = () => `${this._value.addCommas()}${this._getRelationString()}`; - _getRelationString = () => this._relation === 'gte' ? '+' : ''; - combine = (other) => { - if (!(other instanceof SearchTotal)) { - throw new TypeError('Parameter must be an instance of SearchTotal.'); - } - const newValue = this.getValue() + other.getValue(); - let newRelation = this._relation; - if (other._relation === 'gte' || this._relation === 'gte') { - newRelation = 'gte'; - } - return new SearchTotal({value: newValue, relation: newRelation}); - }; -} - - -function createSearchTotal(total) { - return new SearchTotal(total); -} - - class SearchResultList extends Component { constructor(props) { super(props); @@ -130,7 +102,7 @@ class SearchResultList extends Component { //console.log("Loaded cached query for") //console.log(args); this.state.hits[t] = this.state.hits[t].concat(cachedQuery.hits.hits); - this.state.totals[t] = createSearchTotal(cachedQuery.hits.total); + this.state.totals[t] = cachedQuery.hits.total; this.state.pagesLoaded[t] += 1; args.start = this.state.pagesLoaded[t] * this.querySize[t]; if (t === "text") { @@ -350,7 +322,7 @@ class SearchResultList extends Component { args.success = data => { this.updateRunningQuery(type, null); if (this.state.pagesLoaded[type] === 0) { // Skip if pages have already been loaded from cache, but let aggregation processing below occur - const currTotal = createSearchTotal(data.hits.total); + const currTotal = data.hits.total; let state = { hits: extend(this.state.hits, {[type]: data.hits.hits}), totals: extend(this.state.totals, {[type]: currTotal}), @@ -363,7 +335,7 @@ class SearchResultList extends Component { }); const filter_label = (request_applied && request_applied.length > 0) ? (' - ' + request_applied.join('|')) : ''; const query_label = props.query + filter_label; - Sefaria.track.event("Search", `${this.props.searchInBook? "SidebarSearch ": ""}Query: ${type}`, query_label, createSearchTotal(data.hits.total).getValue()); + Sefaria.track.event("Search", `${this.props.searchInBook? "SidebarSearch ": ""}Query: ${type}`, query_label, data.hits.total.getValue()); } if (data.aggregations) { diff --git a/static/js/sefaria/search.js b/static/js/sefaria/search.js index 6506574bed..1dda4f42f7 100644 --- a/static/js/sefaria/search.js +++ b/static/js/sefaria/search.js @@ -2,6 +2,7 @@ import $ from './sefariaJquery'; import extend from 'extend'; import FilterNode from './FilterNode'; import SearchState from './searchState'; +import { SearchTotal } from "./searchTotal"; class Search { @@ -9,8 +10,8 @@ class Search { this.searchIndexText = searchIndexText; this.searchIndexSheet = searchIndexSheet; this._cache = {}; - this.sefariaQueryQueue = {hits: {hits:[], total: 0, max_score: 0.0}, lastSeen: -1}; - this.dictaQueryQueue = {lastSeen: -1, hits: {total: 0, hits:[]}}; + this.sefariaQueryQueue = {hits: {hits:[], total: new SearchTotal(), max_score: 0.0}, lastSeen: -1}; + this.dictaQueryQueue = {lastSeen: -1, hits: {total: new SearchTotal(), hits:[]}}; this.queryDictaFlag = true; this.dictaCounts = null; this.sefariaSheetsResult = null; @@ -47,6 +48,7 @@ class Search { processData: false, dataType: 'json', success: data => { + data.hits.total = new SearchTotal(data.hits.total); this.cache(cacheKey, data); resolve(data) }, @@ -103,7 +105,7 @@ class Search { return new Promise((resolve, reject) => { if (this.queryDictaFlag && args.type === "text") { - if (this.dictaQueryQueue.lastSeen + 1 >= this.dictaQueryQueue.hits.total && ('start' in args && args['start'] > 0)) { + if (this.dictaQueryQueue.lastSeen + 1 >= this.dictaQueryQueue.hits.total.getValue() && ('start' in args && args['start'] > 0)) { /* don't make new queries if results are exhausted. * 'start' is omitted on first query (defaults to 0). On a first query, we'll always want to query. */ @@ -125,6 +127,7 @@ class Search { contentType: 'application/json; charset=UTF-8', data: jsonData, success: data => { + data.total = new SearchTotal({value: data.total}); this.cache(cacheKey, data); resolve(data); }, @@ -134,7 +137,7 @@ class Search { } else { - resolve({total: 0, hits: []}); + resolve({total: new SearchTotal(), hits: []}); } }).then(x => { if (args.type === "sheet") { @@ -244,15 +247,16 @@ class Search { } if (!!filters.length) { const expression = new RegExp(`^(${filters.join('|')})(\/.*|$)`); - result.hits.total = this.buckets.reduce((total, currentBook) => { + const accumulatedTotal = this.buckets.reduce((total, currentBook) => { if (expression.test(currentBook.key)) { total += currentBook.doc_count; } return total }, 0); + result.hits.total = new SearchTotal({value: accumulatedTotal}); } else { - result.hits.total = this.sefariaQueryQueue.hits.total + this.dictaQueryQueue.hits.total; + result.hits.total = this.sefariaQueryQueue.hits.total.combine(this.dictaQueryQueue.hits.total); } let sefariaHits = (this.queryDictaFlag) @@ -327,8 +331,8 @@ class Search { if (args.type === 'text') { this.dictaCounts = null; this.queryDictaFlag = this.isDictaQuery(args); - this.sefariaQueryQueue = {hits: {hits: [], total: 0, max_score: 0.0}, lastSeen: -1}; - this.dictaQueryQueue = {lastSeen: -1, hits: {total: 0, hits: []}}; + this.sefariaQueryQueue = {hits: {hits: [], total: new SearchTotal(), max_score: 0.0}, lastSeen: -1}; + this.dictaQueryQueue = {lastSeen: -1, hits: {total: new SearchTotal(), hits: []}}; this.queryAborter.abort(); } } diff --git a/static/js/sefaria/searchTotal.js b/static/js/sefaria/searchTotal.js new file mode 100644 index 0000000000..e07f5772ad --- /dev/null +++ b/static/js/sefaria/searchTotal.js @@ -0,0 +1,23 @@ +export class SearchTotal { + constructor({value=0, relation="eq"} = {}) { + this._value = value; + this._relation = relation; + } + getValue = () => this._value; + add = (num) => this._value += num; + asString = () => `${this._value.addCommas()}${this._getRelationString()}`; + _getRelationString = () => this._relation === 'gte' ? '+' : ''; + combine = (other) => { + if (!(other instanceof SearchTotal)) { + throw new TypeError('Parameter must be an instance of SearchTotal.'); + } + const newValue = this.getValue() + other.getValue(); + let newRelation = this._relation; + if (other._relation === 'gte' || this._relation === 'gte') { + newRelation = 'gte'; + } + return new SearchTotal({value: newValue, relation: newRelation}); + }; +} + + From 6882972aafb8d2e867724111b2a2c93f0e543015 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Thu, 25 Jan 2024 13:04:16 +0200 Subject: [PATCH 708/756] chore(search): split up long line --- static/js/sefaria/search.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/static/js/sefaria/search.js b/static/js/sefaria/search.js index 1dda4f42f7..4000acea6d 100644 --- a/static/js/sefaria/search.js +++ b/static/js/sefaria/search.js @@ -105,7 +105,8 @@ class Search { return new Promise((resolve, reject) => { if (this.queryDictaFlag && args.type === "text") { - if (this.dictaQueryQueue.lastSeen + 1 >= this.dictaQueryQueue.hits.total.getValue() && ('start' in args && args['start'] > 0)) { + if (this.dictaQueryQueue.lastSeen + 1 >= this.dictaQueryQueue.hits.total.getValue() && + ('start' in args && args['start'] > 0)) { /* don't make new queries if results are exhausted. * 'start' is omitted on first query (defaults to 0). On a first query, we'll always want to query. */ From 7f2f81a2b4dd17802d9a5d4d0a9e79db0a2f3fa5 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Thu, 25 Jan 2024 18:52:05 +0200 Subject: [PATCH 709/756] fix(Commentary Redirects): do not edit a parsed ref directly --- static/js/sefaria/sefaria.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index 7dfe3be903..b7c415a8a1 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -2194,17 +2194,19 @@ _media: {}, const many_to_one = book.base_text_mapping.startsWith("many_to_one"); // four options, two start with many_to_one and two start with one_to_one if (parsedRef.sections.length <= 2 || !many_to_one) { // Rashi on Genesis 1:2 => Genesis 1:2 and Rashi on Genesis => Genesis. - parsedRef.ref = parsedRef.ref.replace(book.title, base_text); - return Sefaria.humanRef(parsedRef.ref); + const parsedRefCopy = Object.create(parsedRef); + parsedRefCopy.ref = parsedRef.ref.replace(book.title, base_text); + return Sefaria.humanRef(parsedRefCopy.ref); } else if (many_to_one) { // Rashi on Genesis 1:2:1 => Genesis 1:2 - parsedRef.sections = parsedRef.sections.slice(0, parsedRef.sections.length - 1); - parsedRef.toSections = parsedRef.toSections.slice(0, parsedRef.toSections.length - 1); - parsedRef.book = parsedRef.index = parsedRef.index.replace(book.title, base_text); - parsedRef.ref = parsedRef.ref.replace(book.title, base_text); - parsedRef.ref = parsedRef.ref.split(' ').slice(0, -1).join(' '); - return Sefaria.humanRef(Sefaria.makeRef(parsedRef)); + const parsedRefCopy = Object.create(parsedRef); + parsedRefCopy.sections = parsedRefCopy.sections.slice(0, parsedRef.sections.length - 1); + parsedRefCopy.toSections = parsedRefCopy.toSections.slice(0, parsedRef.toSections.length - 1); + parsedRefCopy.book = parsedRefCopy.index = parsedRefCopy.index.replace(book.title, base_text); + parsedRefCopy.ref = parsedRefCopy.ref.replace(book.title, base_text); + parsedRefCopy.ref = parsedRefCopy.ref.split(' ').slice(0, -1).join(' '); + return Sefaria.humanRef(Sefaria.makeRef(parsedRefCopy)); } return Sefaria.humanRef(parsedRef.ref); }, From e1cb275696ff5bcfe471ba859fd13ef880629a3f Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Fri, 26 Jan 2024 11:05:16 +0200 Subject: [PATCH 710/756] feat(Commentary Redirects): showHighlight when there is a filter as otherwise its unclear --- static/js/ReaderApp.jsx | 5 ++++- static/js/TextRange.jsx | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index ba665323ad..940104d29b 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -1453,7 +1453,10 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { } else { refs = [ref]; currentlyVisibleRef = ref; - highlightedRefs = []; + highlightedRefs = filter === [] ? [] : [ref]; + if (filter) { + options.showHighlight = true; + } } //console.log("Higlighted refs:", highlightedRefs) panel = this.makePanelState({ diff --git a/static/js/TextRange.jsx b/static/js/TextRange.jsx index 98a8840686..c933112d6f 100644 --- a/static/js/TextRange.jsx +++ b/static/js/TextRange.jsx @@ -125,7 +125,7 @@ class TextRange extends Component { if (this.props.basetext && this.props.sref !== data.ref) { // Replace ReaderPanel contents ref with the normalized form of the ref, if they differ. // Pass parameter to showBaseText to replaceHistory - normalization should't add a step to history - this.props.showBaseText(data.ref, true, this.props.currVersions); + this.props.showBaseText(data.ref, true, this.props.currVersions, [], false); return; } else if (this.props.basetext && data.spanning) { // Replace ReaderPanel contents with split refs if ref is spanning @@ -139,7 +139,7 @@ class TextRange extends Component { // If this is a ref to a super-section, rewrite it to first available section if (this.props.basetext && data.textDepth - data.sections.length > 1 && data.firstAvailableSectionRef) { - this.props.showBaseText(data.firstAvailableSectionRef, true, this.props.currVersions); + this.props.showBaseText(data.firstAvailableSectionRef, true, this.props.currVersions, [], false); return; } From ab37239d611505767e52def465346b19bf986747 Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Sat, 27 Jan 2024 22:39:54 +0200 Subject: [PATCH 711/756] fix(linker): fix order of parameters --- sefaria/helper/linker.py | 2 +- sefaria/helper/tests/linker_test.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sefaria/helper/linker.py b/sefaria/helper/linker.py index 998f346a05..ceb5fc15a0 100644 --- a/sefaria/helper/linker.py +++ b/sefaria/helper/linker.py @@ -52,8 +52,8 @@ class _FindRefsTextOptions: @attr version_preferences_by_corpus: dict of dicts of the form { <corpus>: { <lang>: <vtitle> }} """ - debug: bool = False with_text: bool = False + debug: bool = False max_segments: int = 0 version_preferences_by_corpus: dict = None diff --git a/sefaria/helper/tests/linker_test.py b/sefaria/helper/tests/linker_test.py index 4a0b96c41a..94835d2a09 100644 --- a/sefaria/helper/tests/linker_test.py +++ b/sefaria/helper/tests/linker_test.py @@ -131,8 +131,8 @@ def test_find_refs_text(self, mock_is_hebrew: Mock): assert find_refs_text.lang == 'en' def test_find_refs_text_options(self): - find_refs_text_options = linker._FindRefsTextOptions(True, True, 10, {}) - assert find_refs_text_options.debug + find_refs_text_options = linker._FindRefsTextOptions(True, False, 10, {}) + assert not find_refs_text_options.debug assert find_refs_text_options.with_text assert find_refs_text_options.max_segments == 10 assert find_refs_text_options.version_preferences_by_corpus == {} From 24a7c5262aae43838fd7bafeb4f70550e1aa3cbe Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Sun, 28 Jan 2024 15:02:01 +0200 Subject: [PATCH 712/756] feat(Commentary Redirects): refactor with openPanelWithSidebar and works with mobile --- static/js/ReaderApp.jsx | 64 +++++++++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 25 deletions(-) diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index 940104d29b..2031256315 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -970,7 +970,7 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { if (textRef) { this.setTextListHighlight(n, textRef); } - this.openPanelAt(n, citationRef, currVersions, {scrollToHighlighted: !!replace}); + this.openPanelAt(n, citationRef, currVersions, {scrollToHighlighted: !!replace}, false); } openNamedEntityInNewPanel(n, textRef, namedEntityState) { //this.setTextListHighlight(n, [textRef]); @@ -1453,34 +1453,12 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { } else { refs = [ref]; currentlyVisibleRef = ref; - highlightedRefs = filter === [] ? [] : [ref]; + highlightedRefs = filter.length === 0 ? [] : [ref]; if (filter) { options.showHighlight = true; } } - //console.log("Higlighted refs:", highlightedRefs) - panel = this.makePanelState({ - refs, - currVersions, - highlightedRefs, - filter: filter, - recentFilters: filter, - currentlyVisibleRef, mode: "Text", - ...options - }); - if (filter.length > 0) { - connectionPanel = this.makePanelState({ - refs, - currVersions, - highlightedRefs, - currentlyVisibleRef, mode: "Connections", - filter: filter, - recentFilters: filter, - connectionsMode: "TextList", - ...options - }); - } - + [panel, connectionPanel] = this.openPanelWithSidebar(refs, currentlyVisibleRef, highlightedRefs, currVersions, filter, options); } const newPanels = this.state.panels.slice(); @@ -1493,6 +1471,42 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { this.saveLastPlace(panel, n + 1); } } + openPanelWithSidebar(refs, currentlyVisibleRef, highlightedRefs, currVersions, filter, options) { + let panel, connectionPanel; + let panelProps = { + refs, + currVersions, + highlightedRefs, + filter: filter, + recentFilters: filter, + currentlyVisibleRef, mode: "Text", + ...options + }; + if (filter.length === 0) { + panel = this.makePanelState(panelProps) + } + else { + if (this.props.multiPanel) { + connectionPanel = this.makePanelState({ + refs, + currVersions, + highlightedRefs, + currentlyVisibleRef, mode: "Connections", + filter: filter, + recentFilters: filter, + connectionsMode: "TextList", + ...options + }); + panel = this.makePanelState(panelProps); + } else { + panelProps.mode = "TextAndConnections"; + panelProps.connectionsMode = "TextList"; + panel = this.makePanelState(panelProps); + } + } + return [panel, connectionPanel]; + } + openPanelAtEnd(ref, currVersions) { this.openPanelAt(this.state.panels.length+1, ref, currVersions); } From d100a04dc0a2c71a284976aebba707957927b328 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Mon, 29 Jan 2024 10:46:53 +0200 Subject: [PATCH 713/756] chore: manually return sectionRef in ConnectionsPanel --- static/js/ConnectionsPanel.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/ConnectionsPanel.jsx b/static/js/ConnectionsPanel.jsx index 47bee2a508..ec2ece7a4f 100644 --- a/static/js/ConnectionsPanel.jsx +++ b/static/js/ConnectionsPanel.jsx @@ -184,7 +184,7 @@ class ConnectionsPanel extends Component { return !prevRefs.compare(nextRefs); } sectionRef() { - return Sefaria.sectionRef(Sefaria.humanRef(this.props.srefs)) || this.props.srefs; + return Sefaria.sectionRef(Sefaria.humanRef(this.props.srefs)) || Sefaria.humanRef(this.props.srefs).split(":")[0]; } loadData() { let ref = this.sectionRef(); From 9b0b65959b256eb3dcfdbc09b8d24e21595afc8b Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Mon, 29 Jan 2024 11:01:47 +0200 Subject: [PATCH 714/756] fix(Commentary Redirects): mobile should show commentator name not "Resources" in the connections panel --- static/js/ReaderApp.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index 2031256315..c670984eb6 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -1495,12 +1495,14 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { filter: filter, recentFilters: filter, connectionsMode: "TextList", + connectionsCategory: "Commentary", ...options }); panel = this.makePanelState(panelProps); } else { panelProps.mode = "TextAndConnections"; panelProps.connectionsMode = "TextList"; + panelProps.connectionsCategory = "Commentary"; panel = this.makePanelState(panelProps); } } From 62b2dfa9aefc234160dd951172c728f0f4cbfed6 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Mon, 29 Jan 2024 12:57:50 +0200 Subject: [PATCH 715/756] chore: starting history for commentary redirects --- static/js/ReaderApp.jsx | 9 +++++---- static/js/ReaderPanel.jsx | 16 +--------------- 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index c670984eb6..4ca63171b6 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -1467,9 +1467,9 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { newPanels.push(connectionPanel); } this.setState({panels: newPanels}); - if (replaceHistory) { - this.saveLastPlace(panel, n + 1); - } + const panelNumToSave = !replaceHistory ? n+1 : n; + this.saveLastPlace(panel, panelNumToSave, !!connectionPanel); + } openPanelWithSidebar(refs, currentlyVisibleRef, highlightedRefs, currVersions, filter, options) { let panel, connectionPanel; @@ -1483,7 +1483,7 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { ...options }; if (filter.length === 0) { - panel = this.makePanelState(panelProps) + panel = this.makePanelState(panelProps); } else { if (this.props.multiPanel) { @@ -1506,6 +1506,7 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { panel = this.makePanelState(panelProps); } } + panel.currentlyVisibleRef = currentlyVisibleRef; return [panel, connectionPanel]; } diff --git a/static/js/ReaderPanel.jsx b/static/js/ReaderPanel.jsx index c22b7ec1a1..c2e00262af 100644 --- a/static/js/ReaderPanel.jsx +++ b/static/js/ReaderPanel.jsx @@ -167,7 +167,7 @@ class ReaderPanel extends Component { if (this.props.multiPanel) { this.props.onCitationClick(citationRef, textRef, replace, currVersions); } else { - this.showBaseText(citationRef, replace, currVersions, [], false); + this.showBaseText(citationRef, replace, currVersions, [], true); } } handleTextListClick(ref, replaceHistory, currVersions) { @@ -255,20 +255,6 @@ class ReaderPanel extends Component { } } this.props.openPanelAt(this.props.panelPosition, ref, currVersions, {settings: this.state.settings}, true, convertCommToBase, this.replaceHistory); - // this.conditionalSetState({ - // mode: "Text", - // refs, - // filter, - // currentlyVisibleRef, - // currVersions, - // highlightedRefs, - // recentFilters: [], - // menuOpen: null, - // compare: false, - // sheetID: null, - // connectionsMode: "Resources", - // settings: this.state.settings - // }); } openSheet(sheetRef, replaceHistory) { this.replaceHistory = Boolean(replaceHistory); From 73d3edf4ab89bcdbbc2c9c161466ecb419e7f989 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Mon, 29 Jan 2024 19:50:27 +0200 Subject: [PATCH 716/756] fix(Commentary Redirects): make currentlyVisibleRef humanRef --- static/js/ReaderApp.jsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index 4ca63171b6..76c1ddeb14 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -1458,7 +1458,7 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { options.showHighlight = true; } } - [panel, connectionPanel] = this.openPanelWithSidebar(refs, currentlyVisibleRef, highlightedRefs, currVersions, filter, options); + [panel, connectionPanel] = this.openPanelWithConnections(refs, currentlyVisibleRef, highlightedRefs, currVersions, filter, options); } const newPanels = this.state.panels.slice(); @@ -1471,7 +1471,7 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { this.saveLastPlace(panel, panelNumToSave, !!connectionPanel); } - openPanelWithSidebar(refs, currentlyVisibleRef, highlightedRefs, currVersions, filter, options) { + openPanelWithConnections(refs, currentlyVisibleRef, highlightedRefs, currVersions, filter, options) { let panel, connectionPanel; let panelProps = { refs, @@ -1506,7 +1506,7 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { panel = this.makePanelState(panelProps); } } - panel.currentlyVisibleRef = currentlyVisibleRef; + panel.currentlyVisibleRef = Sefaria.humanRef(currentlyVisibleRef); return [panel, connectionPanel]; } From 9feef7d54406f4ab45c2e017e283da923e1c6edd Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Tue, 30 Jan 2024 09:53:34 +0200 Subject: [PATCH 717/756] fix(Commentary Redirects): set replaceHistory in ReaderApp --- static/js/ReaderApp.jsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index 76c1ddeb14..7f6b5722e5 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -1422,10 +1422,11 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { this.state.panels = []; // temporarily clear panels directly in state, set properly with setState in openPanelAt this.openPanelAt(0, ref, currVersions, options); } - openPanelAt(n, ref, currVersions, options, replace, convertCommToBase=true, replaceHistory=true) { + openPanelAt(n, ref, currVersions, options, replace, convertCommToBase=true, replaceHistory=false) { // Open a new panel after `n` with the new ref // If `replace`, replace existing panel at `n`, otherwise insert new panel at `n` // If book level, Open book toc + this.replaceHistory = Boolean(replaceHistory); const parsedRef = Sefaria.parseRef(ref); const index = Sefaria.index(ref); // Do we have to worry about normalization, as in Header.subimtSearch()? let panel, connectionPanel; @@ -1453,8 +1454,9 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { } else { refs = [ref]; currentlyVisibleRef = ref; - highlightedRefs = filter.length === 0 ? [] : [ref]; - if (filter) { + highlightedRefs = []; + if (filter.length > 0) { + options.highlightedRefs = [ref]; options.showHighlight = true; } } From df1125ab6f021e7c613d426ee9427264f0bd6f1d Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Tue, 30 Jan 2024 10:35:01 +0200 Subject: [PATCH 718/756] feat(Commentary Redirects): compare panel doesnt redirect --- static/js/ReaderApp.jsx | 4 ++-- static/js/ReaderPanel.jsx | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index 7f6b5722e5..0eb1040a42 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -1422,7 +1422,7 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { this.state.panels = []; // temporarily clear panels directly in state, set properly with setState in openPanelAt this.openPanelAt(0, ref, currVersions, options); } - openPanelAt(n, ref, currVersions, options, replace, convertCommToBase=true, replaceHistory=false) { + openPanelAt(n, ref, currVersions, options, replace, attemptConvertCommToBase=true, replaceHistory=false) { // Open a new panel after `n` with the new ref // If `replace`, replace existing panel at `n`, otherwise insert new panel at `n` // If book level, Open book toc @@ -1443,7 +1443,7 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { }); } else { // Text let filter; - ({ref, filter} = Sefaria.getBaseRefAndFilter(ref, convertCommToBase)); + ({ref, filter} = Sefaria.getBaseRefAndFilter(ref, attemptConvertCommToBase)); let refs, currentlyVisibleRef, highlightedRefs; if (ref.constructor === Array) { // When called with an array, set highlight for the whole spanning range of the array diff --git a/static/js/ReaderPanel.jsx b/static/js/ReaderPanel.jsx index c2e00262af..c899784616 100644 --- a/static/js/ReaderPanel.jsx +++ b/static/js/ReaderPanel.jsx @@ -236,11 +236,12 @@ class ReaderPanel extends Component { highlightedRefsInSheet }); } - showBaseText(ref, replaceHistory, currVersions={en: null, he: null}, filter=[], convertCommToBase=true) { + showBaseText(ref, replaceHistory, currVersions={en: null, he: null}, filter=[], attemptConvertCommToBase=true) { // Set the current primary text `ref`, which may be either a string or an array of strings. // `replaceHistory` - bool whether to replace browser history rather than push for this change if (!ref) { return; } this.replaceHistory = Boolean(replaceHistory); + attemptConvertCommToBase = this.state.compare ? false : attemptConvertCommToBase; // console.log("showBaseText", ref, replaceHistory); if (this.state.mode === "Connections" && this.props.masterPanelLanguage === "bilingual") { // Connections panels are forced to be mono-lingual. When opening a text from a connections panel, @@ -254,7 +255,7 @@ class ReaderPanel extends Component { return; } } - this.props.openPanelAt(this.props.panelPosition, ref, currVersions, {settings: this.state.settings}, true, convertCommToBase, this.replaceHistory); + this.props.openPanelAt(this.props.panelPosition, ref, currVersions, {settings: this.state.settings}, true, attemptConvertCommToBase, this.replaceHistory); } openSheet(sheetRef, replaceHistory) { this.replaceHistory = Boolean(replaceHistory); From 253dd0b03a331650f03631e7a755eb9ba016e987 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Tue, 30 Jan 2024 11:12:40 +0200 Subject: [PATCH 719/756] chore: refactor openPanelWithConnections --- static/js/ReaderApp.jsx | 59 +++++++++++++++-------------------------- 1 file changed, 21 insertions(+), 38 deletions(-) diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index 0eb1040a42..8425acef46 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -1455,12 +1455,20 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { refs = [ref]; currentlyVisibleRef = ref; highlightedRefs = []; - if (filter.length > 0) { - options.highlightedRefs = [ref]; - options.showHighlight = true; - } } - [panel, connectionPanel] = this.openPanelWithConnections(refs, currentlyVisibleRef, highlightedRefs, currVersions, filter, options); + let panelProps = { + refs, + currVersions, + highlightedRefs, + filter: filter, + recentFilters: filter, + currentlyVisibleRef, mode: "Text", + ...options + }; + panel = this.makePanelState(panelProps); + if (filter.length > 0) { + [panel, connectionPanel] = this.openPanelWithConnections(panel, panelProps); + } } const newPanels = this.state.panels.slice(); @@ -1473,42 +1481,17 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { this.saveLastPlace(panel, panelNumToSave, !!connectionPanel); } - openPanelWithConnections(refs, currentlyVisibleRef, highlightedRefs, currVersions, filter, options) { - let panel, connectionPanel; - let panelProps = { - refs, - currVersions, - highlightedRefs, - filter: filter, - recentFilters: filter, - currentlyVisibleRef, mode: "Text", - ...options - }; - if (filter.length === 0) { - panel = this.makePanelState(panelProps); - } - else { - if (this.props.multiPanel) { - connectionPanel = this.makePanelState({ - refs, - currVersions, - highlightedRefs, - currentlyVisibleRef, mode: "Connections", - filter: filter, - recentFilters: filter, - connectionsMode: "TextList", - connectionsCategory: "Commentary", - ...options - }); + openPanelWithConnections(panel, panelProps) { + let connectionPanel; + if (this.props.multiPanel) { + const connectionPanelProps = {...panelProps, showHighlight: true, highlightedRefs: panelProps.refs, mode: "Connections", connectionsMode: "TextList", connectionsCategory: "Commentary"}; + connectionPanel = this.makePanelState(connectionPanelProps); panel = this.makePanelState(panelProps); - } else { - panelProps.mode = "TextAndConnections"; - panelProps.connectionsMode = "TextList"; - panelProps.connectionsCategory = "Commentary"; + } else { + panelProps = {...panelProps, mode: "TextAndConnections", connectionsMode: "TextList", connectionsCategory: "Commentary", highlightedRefs: panelProps.refs}; panel = this.makePanelState(panelProps); - } } - panel.currentlyVisibleRef = Sefaria.humanRef(currentlyVisibleRef); + panel.currentlyVisibleRef = Sefaria.humanRef(panelProps.currentlyVisibleRef); return [panel, connectionPanel]; } From 6601056bfd19a3e499a43ba57ded396e76019b29 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Tue, 30 Jan 2024 11:21:04 +0200 Subject: [PATCH 720/756] chore: showHighlight for mobile --- static/js/ReaderApp.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index 8425acef46..b1aae2814b 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -1488,7 +1488,7 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { connectionPanel = this.makePanelState(connectionPanelProps); panel = this.makePanelState(panelProps); } else { - panelProps = {...panelProps, mode: "TextAndConnections", connectionsMode: "TextList", connectionsCategory: "Commentary", highlightedRefs: panelProps.refs}; + panelProps = {...panelProps, mode: "TextAndConnections", connectionsMode: "TextList", connectionsCategory: "Commentary", showHighlight: true, highlightedRefs: panelProps.refs}; panel = this.makePanelState(panelProps); } panel.currentlyVisibleRef = Sefaria.humanRef(panelProps.currentlyVisibleRef); From c63d4315a80c2cba4b70c346c59a0f9520322271 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Tue, 30 Jan 2024 11:41:02 +0200 Subject: [PATCH 721/756] fix(Commentary Redirects): restore showHighlight --- static/js/ReaderApp.jsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index b1aae2814b..6f4b215712 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -1460,7 +1460,7 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { refs, currVersions, highlightedRefs, - filter: filter, + filter, recentFilters: filter, currentlyVisibleRef, mode: "Text", ...options @@ -1484,13 +1484,13 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { openPanelWithConnections(panel, panelProps) { let connectionPanel; if (this.props.multiPanel) { - const connectionPanelProps = {...panelProps, showHighlight: true, highlightedRefs: panelProps.refs, mode: "Connections", connectionsMode: "TextList", connectionsCategory: "Commentary"}; + const connectionPanelProps = {...panelProps, mode: "Connections", connectionsMode: "TextList", connectionsCategory: "Commentary"}; connectionPanel = this.makePanelState(connectionPanelProps); - panel = this.makePanelState(panelProps); } else { - panelProps = {...panelProps, mode: "TextAndConnections", connectionsMode: "TextList", connectionsCategory: "Commentary", showHighlight: true, highlightedRefs: panelProps.refs}; - panel = this.makePanelState(panelProps); + panelProps = {...panelProps, mode: "TextAndConnections", connectionsMode: "TextList", connectionsCategory: "Commentary", highlightedRefs: panelProps.refs}; } + panel = this.makePanelState(panelProps); + panel.showHighlight = true; panel.currentlyVisibleRef = Sefaria.humanRef(panelProps.currentlyVisibleRef); return [panel, connectionPanel]; } From ed4eda1e0ba00bb5d407871e970a374aa1c740f0 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Tue, 30 Jan 2024 11:51:36 +0200 Subject: [PATCH 722/756] fix(Commentary Redirects): fix currentlyvisibleref in ranges --- static/js/ReaderApp.jsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index 6f4b215712..9d110ac07d 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -1465,10 +1465,13 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { currentlyVisibleRef, mode: "Text", ...options }; - panel = this.makePanelState(panelProps); if (filter.length > 0) { - [panel, connectionPanel] = this.openPanelWithConnections(panel, panelProps); + [panel, connectionPanel] = this.openPanelWithConnections(panelProps); } + else { + panel = this.makePanelState(panelProps); + } + panel.currentlyVisibleRef = Sefaria.humanRef(panelProps.currentlyVisibleRef); } const newPanels = this.state.panels.slice(); @@ -1481,7 +1484,7 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { this.saveLastPlace(panel, panelNumToSave, !!connectionPanel); } - openPanelWithConnections(panel, panelProps) { + openPanelWithConnections(panelProps) { let connectionPanel; if (this.props.multiPanel) { const connectionPanelProps = {...panelProps, mode: "Connections", connectionsMode: "TextList", connectionsCategory: "Commentary"}; @@ -1489,9 +1492,8 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { } else { panelProps = {...panelProps, mode: "TextAndConnections", connectionsMode: "TextList", connectionsCategory: "Commentary", highlightedRefs: panelProps.refs}; } - panel = this.makePanelState(panelProps); + const panel = this.makePanelState(panelProps); panel.showHighlight = true; - panel.currentlyVisibleRef = Sefaria.humanRef(panelProps.currentlyVisibleRef); return [panel, connectionPanel]; } From 600dc62d3390a606f6bb2aa52e76afa4da63c91c Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Tue, 30 Jan 2024 12:58:23 +0200 Subject: [PATCH 723/756] chore(alt tags): add alt tags for globe, bookmarks and notifications icons. --- static/js/Header.jsx | 23 +++++++++++++++++++---- static/js/Misc.jsx | 4 +++- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/static/js/Header.jsx b/static/js/Header.jsx index f252297076..42f76a8f53 100644 --- a/static/js/Header.jsx +++ b/static/js/Header.jsx @@ -16,6 +16,21 @@ import { } from './Misc'; +function getAltText(name) { + const alts = { + 'notifications': { + 'english': 'Notifications', + 'hebrew': 'עדכונים' + }, + 'bookmarks': { + 'english': 'Bookmarks', + 'hebrew': 'מועדפים' + }, + }; + return alts[name]?.[Sefaria.interfaceLang]; +} + + class Header extends Component { constructor(props) { super(props) @@ -439,10 +454,10 @@ const LoggedInButtons = ({headerMode}) => { return ( <div className="loggedIn accountLinks"> <a href="/texts/saved" aria-label="See My Saved Texts"> - <img src="/static/icons/bookmarks.svg" /> + <img src="/static/icons/bookmarks.svg" alt={getAltText('bookmarks')}/> </a> <a href="/notifications" aria-label="See New Notifications" key={`notificationCount-C-${unread}`} className={notificationsClasses}> - <img src="/static/icons/notification.svg" /> + <img src="/static/icons/notification.svg" alt={getAltText('notifications')} /> </a> { Sefaria._siteSettings.TORAH_SPECIFIC ? <HelpButton /> : null} <ProfilePicMenu len={24} url={Sefaria.profile_pic_url} name={Sefaria.full_name} key={`profile-${isClient}-${Sefaria.full_name}`}/> @@ -502,11 +517,11 @@ const MobileNavMenu = ({onRefClick, showSearch, openTopic, openURL, close, visib <InterfaceText>Profile</InterfaceText> </a> <a href="/texts/saved" onClick={close}> - <img src="/static/icons/bookmarks.svg" /> + <img src="/static/icons/bookmarks.svg" alt={getAltText('bookmarks')} /> <InterfaceText>Saved & History</InterfaceText> </a> <a href="/notifications" onClick={close}> - <img src="/static/icons/notification.svg" /> + <img src="/static/icons/notification.svg" alt={getAltText('notifications')} /> <InterfaceText>Notifications</InterfaceText> </a> </> : null } diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index d5fab55bbc..11d6dc37ec 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -1413,9 +1413,11 @@ function InterfaceLanguageMenu({currentLang, translationLanguagePreference, setT }; }, []); + const globeAlt = (Sefaria.interfaceLang === 'english') ? 'Toggle Interface Language Menu' : 'פתח תפריט שפת ממשק'; + return ( <div className="interfaceLinks" ref={wrapperRef}> - <a className="interfaceLinks-button" onClick={handleClick}><img src="/static/icons/globe-wire.svg"/></a> + <a className="interfaceLinks-button" onClick={handleClick}><img src="/static/icons/globe-wire.svg" alt={globeAlt}/></a> <div className={`interfaceLinks-menu ${ isOpen ? "open" : "closed"}`}> <div className="interfaceLinks-header"> <InterfaceText>Site Language</InterfaceText> From d61a408f3d21c5760f431b5aae3118f57679080e Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Tue, 30 Jan 2024 15:47:58 +0200 Subject: [PATCH 724/756] refactor(alt tags): move strings to strings.js. --- static/js/Header.jsx | 23 ++++------------------- static/js/Misc.jsx | 4 +--- static/js/sefaria/strings.js | 5 +++++ 3 files changed, 10 insertions(+), 22 deletions(-) diff --git a/static/js/Header.jsx b/static/js/Header.jsx index 42f76a8f53..c793f66ea1 100644 --- a/static/js/Header.jsx +++ b/static/js/Header.jsx @@ -16,21 +16,6 @@ import { } from './Misc'; -function getAltText(name) { - const alts = { - 'notifications': { - 'english': 'Notifications', - 'hebrew': 'עדכונים' - }, - 'bookmarks': { - 'english': 'Bookmarks', - 'hebrew': 'מועדפים' - }, - }; - return alts[name]?.[Sefaria.interfaceLang]; -} - - class Header extends Component { constructor(props) { super(props) @@ -454,10 +439,10 @@ const LoggedInButtons = ({headerMode}) => { return ( <div className="loggedIn accountLinks"> <a href="/texts/saved" aria-label="See My Saved Texts"> - <img src="/static/icons/bookmarks.svg" alt={getAltText('bookmarks')}/> + <img src="/static/icons/bookmarks.svg" alt={Sefaria._('Bookmarks')}/> </a> <a href="/notifications" aria-label="See New Notifications" key={`notificationCount-C-${unread}`} className={notificationsClasses}> - <img src="/static/icons/notification.svg" alt={getAltText('notifications')} /> + <img src="/static/icons/notification.svg" alt={Sefaria._('Notifications')} /> </a> { Sefaria._siteSettings.TORAH_SPECIFIC ? <HelpButton /> : null} <ProfilePicMenu len={24} url={Sefaria.profile_pic_url} name={Sefaria.full_name} key={`profile-${isClient}-${Sefaria.full_name}`}/> @@ -517,11 +502,11 @@ const MobileNavMenu = ({onRefClick, showSearch, openTopic, openURL, close, visib <InterfaceText>Profile</InterfaceText> </a> <a href="/texts/saved" onClick={close}> - <img src="/static/icons/bookmarks.svg" alt={getAltText('bookmarks')} /> + <img src="/static/icons/bookmarks.svg" alt={Sefaria._('Bookmarks')} /> <InterfaceText>Saved & History</InterfaceText> </a> <a href="/notifications" onClick={close}> - <img src="/static/icons/notification.svg" alt={getAltText('notifications')} /> + <img src="/static/icons/notification.svg" alt={Sefaria._('Notifications')} /> <InterfaceText>Notifications</InterfaceText> </a> </> : null } diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 11d6dc37ec..4abc2a61b3 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -1413,11 +1413,9 @@ function InterfaceLanguageMenu({currentLang, translationLanguagePreference, setT }; }, []); - const globeAlt = (Sefaria.interfaceLang === 'english') ? 'Toggle Interface Language Menu' : 'פתח תפריט שפת ממשק'; - return ( <div className="interfaceLinks" ref={wrapperRef}> - <a className="interfaceLinks-button" onClick={handleClick}><img src="/static/icons/globe-wire.svg" alt={globeAlt}/></a> + <a className="interfaceLinks-button" onClick={handleClick}><img src="/static/icons/globe-wire.svg" alt={Sefaria._('Toggle Interface Language Menu')}/></a> <div className={`interfaceLinks-menu ${ isOpen ? "open" : "closed"}`}> <div className="interfaceLinks-header"> <InterfaceText>Site Language</InterfaceText> diff --git a/static/js/sefaria/strings.js b/static/js/sefaria/strings.js index e04a9e1ee5..e37212bf9d 100644 --- a/static/js/sefaria/strings.js +++ b/static/js/sefaria/strings.js @@ -536,6 +536,11 @@ const Strings = { 'Citing': 'מצטט', 'Sites that are listed here use the': 'אתרים המפורטים כאן משתמשים', 'Sefaria Linker': 'במרשתת ההפניות', + + //alt tags + 'Notifications': 'עדכונים', + 'Bookmarks': 'שמורים', + 'Toggle Interface Language Menu' : 'פתח תפריט שפת ממשק', }, _i18nInterfaceStringsWithContext: { From 9f6b71eb840e7fdbdc2bc50368e0639e9004831f Mon Sep 17 00:00:00 2001 From: Russel Neiss <russel.neiss@gmail.com> Date: Tue, 30 Jan 2024 14:14:54 -0600 Subject: [PATCH 725/756] fix(api): patches a bug where setting a url param to false or 0 would return true at certain API endpoints. --- api/views.py | 2 +- reader/views.py | 22 +++++++++++----------- sourcesheets/views.py | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/api/views.py b/api/views.py index 0e714da047..0b5a3a5202 100644 --- a/api/views.py +++ b/api/views.py @@ -48,7 +48,7 @@ def get(self, request, *args, **kwargs): if not versions_params: versions_params = ['primary'] versions_params = [self.split_piped_params(param_str) for param_str in versions_params] - fill_in_missing_segments = request.GET.get('fill_in_missing_segments', False) + fill_in_missing_segments = bool(int(request.GET.get('fill_in_missing_segments', False))) return_format = request.GET.get('return_format', 'default') if return_format not in self.RETURN_FORMATS: return jsonResponse({'error': f'return_format should be one of those formats: {self.RETURN_FORMATS}.'}, status=400) diff --git a/reader/views.py b/reader/views.py index 59d30236db..1f8cf7386a 100644 --- a/reader/views.py +++ b/reader/views.py @@ -1645,10 +1645,10 @@ def index_api(request, title, raw=False): API for manipulating text index records (aka "Text Info") """ if request.method == "GET": - with_content_counts = bool(request.GET.get("with_content_counts", False)) + with_content_counts = bool(int(request.GET.get("with_content_counts", False))) i = library.get_index(title).contents(raw=raw, with_content_counts=with_content_counts) - if request.GET.get("with_related_topics", False): + if bool(int(request.GET.get("with_related_topics", False))): i["relatedTopics"] = get_topics_for_book(title, annotate=True) return jsonResponse(i, callback=request.GET.get("callback", None)) @@ -1862,7 +1862,7 @@ def _collapse_book_leaf_shapes(leaf_shapes): else: cat_list = title.split("/") depth = request.GET.get("depth", 2) - include_dependents = request.GET.get("dependents", False) + include_dependents = bool(int(request.GET.get("dependents", False))) indexes = [] if len(cat_list) == 1: # try as corpus @@ -2067,7 +2067,7 @@ def notes_api(request, note_id_or_ref): raise Http404 oref = Ref(note_id_or_ref) cb = request.GET.get("callback", None) - private = request.GET.get("private", False) + private = bool(int(request.GET.get("private", False))) res = get_notes(oref, uid=creds["user_id"], public=(not private)) return jsonResponse(res, cb) @@ -2141,7 +2141,7 @@ def protected_note_post(req): @catch_error_as_json def all_notes_api(request): - private = request.GET.get("private", False) + private = bool(int(request.GET.get("private", False))) if private: if not request.user.is_authenticated: res = {"error": "You must be logged in to access you notes."} @@ -2157,17 +2157,17 @@ def related_api(request, tref): """ Single API to bundle available content related to `tref`. """ - if request.GET.get("private", False) and request.user.is_authenticated: + if bool(int(request.GET.get("private", False))) and request.user.is_authenticated: oref = Ref(tref) response = { "sheets": get_sheets_for_ref(tref, uid=request.user.id), "notes": get_notes(oref, uid=request.user.id, public=False) } - elif request.GET.get("private", False) and not request.user.is_authenticated: + elif bool(int(request.GET.get("private", False))) and not request.user.is_authenticated: response = {"error": "You must be logged in to access private content."} else: response = { - "links": get_links(tref, with_text=False, with_sheet_links=request.GET.get("with_sheet_links", False)), + "links": get_links(tref, with_text=False, with_sheet_links=bool(int(request.GET.get("with_sheet_links", False)))), "sheets": get_sheets_for_ref(tref), "notes": [], # get_notes(oref, public=True) # Hiding public notes for now "webpages": get_webpages_for_ref(tref), @@ -2660,7 +2660,7 @@ def name_api(request, name): name = name[1:] if topic_override else name # Number of results to return. 0 indicates no limit LIMIT = int(request.GET.get("limit", 10)) - ref_only = request.GET.get("ref_only", False) + ref_only = bool(int(request.GET.get("ref_only", False))) completions_dict = get_name_completions(name, LIMIT, ref_only, topic_override) ref = completions_dict["ref"] topic = completions_dict["topic"] @@ -2764,7 +2764,7 @@ def user_stats_api(request, uid): assert request.method == "GET", "Unsupported Method" u = request.user assert (u.is_active and u.is_staff) or (int(uid) == u.id) - quick = bool(request.GET.get("quick", False)) + quick = bool(int(request.GET.get("quick", False))) if quick: return jsonResponse(public_user_data(uid)) return jsonResponse(user_stats_data(uid)) @@ -4599,7 +4599,7 @@ def isNodeJsReachable(): except Exception as e: logger.warn(f"Failed node healthcheck. Error: {e}") return False - + def is_database_reachable(): try: from sefaria.system.database import db diff --git a/sourcesheets/views.py b/sourcesheets/views.py index ea5d874d72..1340dc9111 100644 --- a/sourcesheets/views.py +++ b/sourcesheets/views.py @@ -960,7 +960,7 @@ def all_sheets_api(request, limiter, offset=0): limiter = int(limiter) offset = int(offset) lang = request.GET.get("lang") - filtered = request.GET.get("filtered", False) + filtered = bool(int(request.GET.get("filtered", False))) response = public_sheets(limit=limiter, skip=offset, lang=lang, filtered=filtered) response = jsonResponse(response, callback=request.GET.get("callback", None)) return response From a875632bceb62adae88b775203d3c3f2535e7951 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Wed, 31 Jan 2024 14:44:32 +0200 Subject: [PATCH 726/756] chore: add comments to Commentary Redirects --- sefaria/model/category.py | 1 + static/js/ConnectionsPanel.jsx | 14 +++++++++++++- static/js/ReaderApp.jsx | 7 +++++-- static/js/ReaderPanel.jsx | 2 +- static/js/sefaria/sefaria.js | 22 ++++++++++++++-------- 5 files changed, 34 insertions(+), 12 deletions(-) diff --git a/sefaria/model/category.py b/sefaria/model/category.py index 6c091ede63..1cea152560 100644 --- a/sefaria/model/category.py +++ b/sefaria/model/category.py @@ -305,6 +305,7 @@ def _make_index_node(self, index, old_title=None, mobile=False, include_first_se if "base_text_titles" in d and len(d["base_text_titles"]) > 0 and include_first_section: # `d["firstSection"]` assumes `include_first_section` is True + # this code seems to never actually get run d["refs_to_base_texts"] = {btitle: self._first_comment_lookup.get(frozenset([btitle, title]), d["firstSection"]) for btitle in d["base_text_titles"] diff --git a/static/js/ConnectionsPanel.jsx b/static/js/ConnectionsPanel.jsx index ec2ece7a4f..889d9d6560 100644 --- a/static/js/ConnectionsPanel.jsx +++ b/static/js/ConnectionsPanel.jsx @@ -184,7 +184,19 @@ class ConnectionsPanel extends Component { return !prevRefs.compare(nextRefs); } sectionRef() { - return Sefaria.sectionRef(Sefaria.humanRef(this.props.srefs)) || Sefaria.humanRef(this.props.srefs).split(":")[0]; + const humanRefForm = Sefaria.humanRef(this.props.srefs); + let sectionRef = Sefaria.sectionRef(humanRefForm); + if (!sectionRef) { + //Sefaria.sectionRef checks Sefaria ref cache, if a segment ref isn't there, derive section ref from the segment ref instead, + //for example, given this.props.srefs value of ["Genesis 3:3"] return "Genesis 3" + if (!!humanRefForm && typeof humanRefForm === "string") { + sectionRef = humanRefForm.split(":")[0]; + } + else { + sectionRef = this.props.srefs; + } + } + return sectionRef; } loadData() { let ref = this.sectionRef(); diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index 9d110ac07d..a7829f9709 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -1426,6 +1426,8 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { // Open a new panel after `n` with the new ref // If `replace`, replace existing panel at `n`, otherwise insert new panel at `n` // If book level, Open book toc + // `attemptConvertCommToBase` if true and ref is commentary ref (Rashi on Genesis 3:3:1), open Genesis 3:3 with Rashi's comments in the sidebar + // `replaceHistory`: can be true when openPanelAt is called from showBaseText in cases of ref normalizing in TextRange when we want to replace history with normalized ref this.replaceHistory = Boolean(replaceHistory); const parsedRef = Sefaria.parseRef(ref); const index = Sefaria.index(ref); // Do we have to worry about normalization, as in Header.subimtSearch()? @@ -1465,7 +1467,7 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { currentlyVisibleRef, mode: "Text", ...options }; - if (filter.length > 0) { + if (filter.length > 0) { // there will be a filter such as ["Rashi"] if attemptConvertCommToBase is true [panel, connectionPanel] = this.openPanelWithConnections(panelProps); } else { @@ -1485,7 +1487,8 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { } openPanelWithConnections(panelProps) { - let connectionPanel; + // in the case of multipanel, create two panels based on panelProps + let connectionPanel; // in mobile, connectionPanel will remain undefined if (this.props.multiPanel) { const connectionPanelProps = {...panelProps, mode: "Connections", connectionsMode: "TextList", connectionsCategory: "Commentary"}; connectionPanel = this.makePanelState(connectionPanelProps); diff --git a/static/js/ReaderPanel.jsx b/static/js/ReaderPanel.jsx index c899784616..98ff9a5741 100644 --- a/static/js/ReaderPanel.jsx +++ b/static/js/ReaderPanel.jsx @@ -171,7 +171,7 @@ class ReaderPanel extends Component { } } handleTextListClick(ref, replaceHistory, currVersions) { - this.showBaseText(ref, replaceHistory, currVersions, [], false); + this.showBaseText(ref, replaceHistory, currVersions, [], false); // don't attempt to convert commentary to base ref when opening from connections panel } updateCurrVersionsToMatchAPIResult(enVTitle, heVTitle) { const newVersions = { diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index b7c415a8a1..323a21e545 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -2184,23 +2184,26 @@ _media: {}, return result; }, isCommentaryWithBaseText(book) { + // only returns true for commentaries with a base_text_mapping to one and only one base_text_title return book?.dependence === "Commentary" && !!book?.base_text_titles && !!book?.base_text_mapping && book?.base_text_titles.length === 1; }, convertCommToBase(parsedRef, book) { - if (!book || !this.isCommentaryWithBaseText(book)) { + // converts commentary refs to base refs: + // example input and output: Rashi on Genesis 1:2 => Genesis 1:2, + // Rashi on Exodus 2:3:1 => Exodus 2:3 + if (!book || !this.isCommentaryWithBaseText(book)) { // if book is not isCommentaryWithBaseText just return the ref return Sefaria.humanRef(parsedRef.ref); } const base_text = book.base_text_titles[0]; const many_to_one = book.base_text_mapping.startsWith("many_to_one"); // four options, two start with many_to_one and two start with one_to_one + const parsedRefCopy = Object.create(parsedRef); // need to create a copy so that the Sefaria._parseRef cache isn't changed if (parsedRef.sections.length <= 2 || !many_to_one) { - // Rashi on Genesis 1:2 => Genesis 1:2 and Rashi on Genesis => Genesis. - const parsedRefCopy = Object.create(parsedRef); + // Rashi on Genesis 1:2 => Genesis 1:2 and Rashi on Genesis => Genesis. in this case, sections stay the same so just change the book title parsedRefCopy.ref = parsedRef.ref.replace(book.title, base_text); return Sefaria.humanRef(parsedRefCopy.ref); } else if (many_to_one) { - // Rashi on Genesis 1:2:1 => Genesis 1:2 - const parsedRefCopy = Object.create(parsedRef); + // Rashi on Genesis 1:2:4 => Genesis 1:2; sections and book title need to change parsedRefCopy.sections = parsedRefCopy.sections.slice(0, parsedRef.sections.length - 1); parsedRefCopy.toSections = parsedRefCopy.toSections.slice(0, parsedRef.toSections.length - 1); parsedRefCopy.book = parsedRefCopy.index = parsedRefCopy.index.replace(book.title, base_text); @@ -2210,12 +2213,15 @@ _media: {}, } return Sefaria.humanRef(parsedRef.ref); }, - getBaseRefAndFilter(ref, convertCommToBase) { - if (!convertCommToBase) { + getBaseRefAndFilter(ref, attemptConvertCommToBase) { + // if `attemptConvertCommToBase`, this function converts a commentary ref (Rashi on Genesis 3:3:1) to a base ref (Genesis 3:3) + // and return the filter ["Rashi"] + // `ref` can be an array or a string + if (!attemptConvertCommToBase) { return {ref: ref, filter: []}; } let parsedRefs = []; - let firstParsedRef, book; + let firstParsedRef, book; // get a parsed ref version of `ref` in order to access book's collective title, base_text_titles, and base_text_mapping if (ref.constructor === Array) { parsedRefs = ref.map(x => Sefaria.parseRef(x)); firstParsedRef = parsedRefs[0]; From 9f8a2f6a3a63fd61b20d3d212d1e8b01afeaffb1 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Thu, 1 Feb 2024 13:21:21 +0200 Subject: [PATCH 727/756] fix(Commentary Redirects): this.props.versionLanguage not a prop of TextRange, so removing it Moreover, showBaseText originally did not accept a fourth argument. --- static/js/TextRange.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/TextRange.jsx b/static/js/TextRange.jsx index c933112d6f..d577ef4a1d 100644 --- a/static/js/TextRange.jsx +++ b/static/js/TextRange.jsx @@ -131,7 +131,7 @@ class TextRange extends Component { // Replace ReaderPanel contents with split refs if ref is spanning // Pass parameter to showBaseText to replaceHistory - normalization should't add a step to history // console.log("Re-rewriting spanning ref") - this.props.showBaseText(data.spanningRefs, true, this.props.currVersions, this.props.versionLanguage); + this.props.showBaseText(data.spanningRefs, true, this.props.currVersions, [], false); return; } From 7fed9871d654bf6b56850c9bc0100d0c2b148828 Mon Sep 17 00:00:00 2001 From: Brendan Galloway <brendan@flanksource.com> Date: Wed, 14 Feb 2024 09:37:58 +0200 Subject: [PATCH 728/756] chore: clean up unused helm inputs --- build/ci/integration-values.yaml | 1 - build/ci/production-values.yaml | 1 - build/ci/sandbox-values.yaml | 1 - 3 files changed, 3 deletions(-) diff --git a/build/ci/integration-values.yaml b/build/ci/integration-values.yaml index e8619f44c7..007a28c360 100644 --- a/build/ci/integration-values.yaml +++ b/build/ci/integration-values.yaml @@ -57,7 +57,6 @@ localSettings: DEBUG: true DOMAIN_LANGUAGE: {} APSCHEDULER_NAME: "apscheduler-{{ .Values.deployEnv }}" - SEARCH_URL: "http://elasticsearch-data:9200" TURN_SERVER: '' USE_CLOUDFLARE: false FRONT_END_URL: "http://${NAME}.integration.sefaria.org" diff --git a/build/ci/production-values.yaml b/build/ci/production-values.yaml index 845b84e649..1206d5ab81 100644 --- a/build/ci/production-values.yaml +++ b/build/ci/production-values.yaml @@ -208,7 +208,6 @@ localSettings: } MONGO_HOST: "mongo" APSCHEDULER_NAME: "apscheduler-{{ .Values.deployEnv }}" - SEARCH_URL: "http://elasticsearch-data:9200" TURN_SERVER: '' USE_CLOUDFLARE: false FRONT_END_URL: "http://www.sefaria.org" diff --git a/build/ci/sandbox-values.yaml b/build/ci/sandbox-values.yaml index 12dcce6766..f729cd82f5 100644 --- a/build/ci/sandbox-values.yaml +++ b/build/ci/sandbox-values.yaml @@ -53,7 +53,6 @@ localSettings: DEBUG: false DOMAIN_LANGUAGE: {} APSCHEDULER_NAME: "apscheduler-{{ .Values.deployEnv }}" - SEARCH_URL: "http://elasticsearch-data:9200" TURN_SERVER: '' USE_CLOUDFLARE: false FRONT_END_URL: "http://${NAME}.cauldron.sefaria.org" From 7b7ca2db1bfc7eb5f517a9dfe0fb2be3fecc46b0 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Thu, 15 Feb 2024 14:31:47 +0200 Subject: [PATCH 729/756] fix(Category Editor): fix typo origEnTitle => origEn --- static/js/Misc.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/Misc.jsx b/static/js/Misc.jsx index 4abc2a61b3..22c451afd4 100644 --- a/static/js/Misc.jsx +++ b/static/js/Misc.jsx @@ -1288,7 +1288,7 @@ const CategoryEditorWrapper = ({toggle, data, type}) => { } const CategoryAdderWrapper = ({toggle, data, type}) => { - const origData = {origEnTitle: ""}; + const origData = {origEn: ""}; switch (type) { case "cats": return <CategoryEditor origData={origData} close={toggle} origPath={data}/>; From 4163f22c023559b6b6b374749397f3f8765dd527 Mon Sep 17 00:00:00 2001 From: Brendan Galloway <brendan@flanksource.com> Date: Thu, 15 Feb 2024 16:24:32 +0200 Subject: [PATCH 730/756] fix: add package for monorepo semantic --- helm-chart/package.json | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 helm-chart/package.json diff --git a/helm-chart/package.json b/helm-chart/package.json new file mode 100644 index 0000000000..658b778f02 --- /dev/null +++ b/helm-chart/package.json @@ -0,0 +1,3 @@ +{ + "name": "helm-chart" +} From 5f3b54fd7f92a6394211da0b534f7e4168754c2e Mon Sep 17 00:00:00 2001 From: Brendan Galloway <brendan@flanksource.com> Date: Thu, 15 Feb 2024 16:38:57 +0200 Subject: [PATCH 731/756] fix: rework semantic module install --- .github/workflows/helm.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/helm.yaml b/.github/workflows/helm.yaml index 4869f61bf4..5c929d1995 100644 --- a/.github/workflows/helm.yaml +++ b/.github/workflows/helm.yaml @@ -51,11 +51,13 @@ jobs: id: semantic with: working_directory: ./helm-chart + semantic_version: 18.0.1 extra_plugins: | conventional-changelog-conventionalcommits@6.1.0 + semantic-release-monorepo@7.0.5 @semantic-release/commit-analyzer@10.0.1 extends: | - semantic-release-monorepo@7.0.5 + semantic-release-monorepo env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Setup From a579bdefbef2801af0810fdf89740ea2a79e902a Mon Sep 17 00:00:00 2001 From: Brendan Galloway <brendan@flanksource.com> Date: Thu, 15 Feb 2024 16:46:46 +0200 Subject: [PATCH 732/756] fix: commit-analyser version --- .github/workflows/helm.yaml | 2 +- helm-chart/package.json | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/helm.yaml b/.github/workflows/helm.yaml index 5c929d1995..32d00dc62a 100644 --- a/.github/workflows/helm.yaml +++ b/.github/workflows/helm.yaml @@ -55,7 +55,7 @@ jobs: extra_plugins: | conventional-changelog-conventionalcommits@6.1.0 semantic-release-monorepo@7.0.5 - @semantic-release/commit-analyzer@10.0.1 + @semantic-release/commit-analyzer@9.0.2 extends: | semantic-release-monorepo env: diff --git a/helm-chart/package.json b/helm-chart/package.json index 658b778f02..ba12b4c7b5 100644 --- a/helm-chart/package.json +++ b/helm-chart/package.json @@ -1,3 +1,8 @@ { - "name": "helm-chart" + "name": "helm-chart", + "dependencies": { + "@semantic-release/commit-analyzer": "^10.0.1", + "conventional-changelog-conventionalcommits": "^6.1.0", + "semantic-release-monorepo": "^7.0.5" + } } From 54f0c1fd24cf46905c736bb97d081255866b2e9f Mon Sep 17 00:00:00 2001 From: nsantacruz <noahssantacruz@gmail.com> Date: Thu, 15 Feb 2024 20:33:20 +0200 Subject: [PATCH 733/756] fix(translations): make sure fill_in_missing_segments is either 1 or 0 --- static/js/sefaria/sefaria.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index a39d3b2566..af8acc321e 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -500,7 +500,8 @@ Sefaria = extend(Sefaria, { const versions = requiredVersions.map(obj => Sefaria.makeParamsStringForAPIV3(obj.language, obj.versionTitle) ); - const url = `${host}${endPoint}${ref}?version=${versions.join('&version=')}&fill_in_missing_segments=${mergeText}`; + const mergeTextInt = mergeText ? 1 : 0; + const url = `${host}${endPoint}${ref}?version=${versions.join('&version=')}&fill_in_missing_segments=${mergeTextInt}`; return url; }, getTextsFromAPIV3: async function(ref, requiredVersions, mergeText) { From 8b227b01592afc788c7ed499583c210e71387c17 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler.cohen@gmail.com> Date: Fri, 16 Feb 2024 03:04:51 -0500 Subject: [PATCH 734/756] Add new redirect to donation page for the path '/giving' --- sites/sefaria/urls.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sites/sefaria/urls.py b/sites/sefaria/urls.py index 164ca37fde..57fb7819bc 100644 --- a/sites/sefaria/urls.py +++ b/sites/sefaria/urls.py @@ -87,6 +87,7 @@ url(r'^gala/?$', lambda x: HttpResponseRedirect('https://donate.sefaria.org/event/sefarias-10-year-anniversary-gala/e486954')), url(r'^give/(?P<campaign_id>[a-zA-Z0-9]+)/?$', lambda x, campaign_id: HttpResponseRedirect(f'https://donate.sefaria.org/give/550774/#!/donation/checkout?c_src={campaign_id}')), url(r'^give/?$', lambda x: HttpResponseRedirect(f'https://donate.sefaria.org/give/550774/#!/donation/checkout?c_src=mu')), + url(r'^giving/?$', lambda x: HttpResponseRedirect('https://donate.sefaria.org/give/524771/#!/donation/checkout')), url(r'^jfn?$', lambda x: HttpResponseRedirect('https://www.sefaria.org/sheets/60494')), url(r'^[nN]echama/?', lambda x: HttpResponseRedirect("/collections/גיליונות-נחמה")), url(r'^contest?', lambda x: HttpResponseRedirect("/powered-by-sefaria-contest-2020")), From 8f3d468aff3e428627e2f9726bf2042a7856a399 Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Sun, 18 Feb 2024 11:49:48 +0200 Subject: [PATCH 735/756] chore(Footer): Update footer links --- static/js/Footer.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/static/js/Footer.jsx b/static/js/Footer.jsx index ad341725f3..a8c082bf19 100644 --- a/static/js/Footer.jsx +++ b/static/js/Footer.jsx @@ -67,8 +67,8 @@ class Footer extends Component { </Section> <Section en="Developers" he="מפתחים"> - <Link href="/developers" en="Get Involved" he="הצטרפו אלינו" blank={true} /> - <Link href="/developers#api" en="API Docs" he="מסמכי API" blank={true} /> + <Link href="https://developers.sefaria.org" en="Get Involved" he="הצטרפו אלינו" blank={true} /> + <Link href="https://developers.sefaria.org/reference" en="API Docs" he="מסמכי API" blank={true} /> <Link href="https://github.com/Sefaria/Sefaria-Project" en="Fork us on GitHub" he="Github" blank={true} /> <Link href="https://github.com/Sefaria/Sefaria-Export" en="Download our Data" he="בסיס נתונים" blank={true} /> </Section> From 7ac2b42e4743ea15d8a0de276298460df5e89b71 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Tue, 20 Feb 2024 12:30:08 +0200 Subject: [PATCH 736/756] chore: refactor getBaseRefAndFilter --- static/js/ReaderApp.jsx | 18 +++++---- static/js/ReaderPanel.jsx | 7 ++-- static/js/sefaria/sefaria.js | 76 ++++++++++++++++++------------------ 3 files changed, 53 insertions(+), 48 deletions(-) diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index a7829f9709..9aa74b66e4 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -1422,11 +1422,11 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { this.state.panels = []; // temporarily clear panels directly in state, set properly with setState in openPanelAt this.openPanelAt(0, ref, currVersions, options); } - openPanelAt(n, ref, currVersions, options, replace, attemptConvertCommToBase=true, replaceHistory=false) { + openPanelAt(n, ref, currVersions, options, replace, attemptConvertCommentaryRefToBaseRef=true, replaceHistory=false) { // Open a new panel after `n` with the new ref // If `replace`, replace existing panel at `n`, otherwise insert new panel at `n` // If book level, Open book toc - // `attemptConvertCommToBase` if true and ref is commentary ref (Rashi on Genesis 3:3:1), open Genesis 3:3 with Rashi's comments in the sidebar + // `attemptConvertCommentaryRefToBaseRef` if true and ref is commentary ref (Rashi on Genesis 3:3:1), open Genesis 3:3 with Rashi's comments in the sidebar // `replaceHistory`: can be true when openPanelAt is called from showBaseText in cases of ref normalizing in TextRange when we want to replace history with normalized ref this.replaceHistory = Boolean(replaceHistory); const parsedRef = Sefaria.parseRef(ref); @@ -1444,10 +1444,12 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { ...options }); } else { // Text - let filter; - ({ref, filter} = Sefaria.getBaseRefAndFilter(ref, attemptConvertCommToBase)); + let filter = []; + if (attemptConvertCommentaryRefToBaseRef && Sefaria.isCommentaryRefWithBaseText(ref)) { + ({ref, filter} = Sefaria.getBaseRefAndFilter(ref)); + } let refs, currentlyVisibleRef, highlightedRefs; - if (ref.constructor === Array) { + if (Array.isArray(ref)) { // When called with an array, set highlight for the whole spanning range of the array refs = ref; currentlyVisibleRef = Sefaria.normRef(ref); @@ -1467,8 +1469,8 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { currentlyVisibleRef, mode: "Text", ...options }; - if (filter.length > 0) { // there will be a filter such as ["Rashi"] if attemptConvertCommToBase is true - [panel, connectionPanel] = this.openPanelWithConnections(panelProps); + if (filter.length > 0) { // there will be a filter such as ["Rashi"] if attemptConvertCommentaryRefToBaseRef is true + [panel, connectionPanel] = this.makePanelWithConnectionsState(panelProps); } else { panel = this.makePanelState(panelProps); @@ -1486,7 +1488,7 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { this.saveLastPlace(panel, panelNumToSave, !!connectionPanel); } - openPanelWithConnections(panelProps) { + makePanelWithConnectionsState(panelProps) { // in the case of multipanel, create two panels based on panelProps let connectionPanel; // in mobile, connectionPanel will remain undefined if (this.props.multiPanel) { diff --git a/static/js/ReaderPanel.jsx b/static/js/ReaderPanel.jsx index 98ff9a5741..3fd44b2a0f 100644 --- a/static/js/ReaderPanel.jsx +++ b/static/js/ReaderPanel.jsx @@ -236,12 +236,12 @@ class ReaderPanel extends Component { highlightedRefsInSheet }); } - showBaseText(ref, replaceHistory, currVersions={en: null, he: null}, filter=[], attemptConvertCommToBase=true) { + showBaseText(ref, replaceHistory, currVersions={en: null, he: null}, filter=[], attemptConvertCommentaryRefToBaseRef=true) { // Set the current primary text `ref`, which may be either a string or an array of strings. // `replaceHistory` - bool whether to replace browser history rather than push for this change if (!ref) { return; } this.replaceHistory = Boolean(replaceHistory); - attemptConvertCommToBase = this.state.compare ? false : attemptConvertCommToBase; + attemptConvertCommentaryRefToBaseRef = this.state.compare ? false : attemptConvertCommentaryRefToBaseRef; // console.log("showBaseText", ref, replaceHistory); if (this.state.mode === "Connections" && this.props.masterPanelLanguage === "bilingual") { // Connections panels are forced to be mono-lingual. When opening a text from a connections panel, @@ -255,7 +255,8 @@ class ReaderPanel extends Component { return; } } - this.props.openPanelAt(this.props.panelPosition, ref, currVersions, {settings: this.state.settings}, true, attemptConvertCommToBase, this.replaceHistory); + this.props.openPanelAt(this.props.panelPosition, ref, currVersions, {settings: this.state.settings}, + true, attemptConvertCommentaryRefToBaseRef, this.replaceHistory); } openSheet(sheetRef, replaceHistory) { this.replaceHistory = Boolean(replaceHistory); diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index 323a21e545..dbbb4d2871 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -2187,56 +2187,58 @@ _media: {}, // only returns true for commentaries with a base_text_mapping to one and only one base_text_title return book?.dependence === "Commentary" && !!book?.base_text_titles && !!book?.base_text_mapping && book?.base_text_titles.length === 1; }, - convertCommToBase(parsedRef, book) { - // converts commentary refs to base refs: + isCommentaryRefWithBaseText(ref) { + // only returns true for commentaries with a base_text_mapping to one and only one base_text_title + let refToCheck = Array.isArray(ref) ? ref[0] : ref; + const parsedRef = Sefaria.parseRef(refToCheck); + const book = Sefaria.index(parsedRef.index); + return this.isCommentaryWithBaseText(book); + }, + convertCommentaryRefToBaseRef(commRef) { + // converts commentary ref, `commRef`, to base ref: // example input and output: Rashi on Genesis 1:2 => Genesis 1:2, // Rashi on Exodus 2:3:1 => Exodus 2:3 - if (!book || !this.isCommentaryWithBaseText(book)) { // if book is not isCommentaryWithBaseText just return the ref - return Sefaria.humanRef(parsedRef.ref); + const book = Sefaria.index(commRef.index); + if (!book || !this.isCommentaryWithBaseText(book)) { + // if book is not isCommentaryWithBaseText just return the ref + return Sefaria.humanRef(commRef.ref); } const base_text = book.base_text_titles[0]; const many_to_one = book.base_text_mapping.startsWith("many_to_one"); // four options, two start with many_to_one and two start with one_to_one - const parsedRefCopy = Object.create(parsedRef); // need to create a copy so that the Sefaria._parseRef cache isn't changed - if (parsedRef.sections.length <= 2 || !many_to_one) { + const commRefCopy = Object.create(commRef); // need to create a copy so that the Sefaria._parseRef cache isn't changed + if (commRef.sections.length <= 2 || !many_to_one) { // Rashi on Genesis 1:2 => Genesis 1:2 and Rashi on Genesis => Genesis. in this case, sections stay the same so just change the book title - parsedRefCopy.ref = parsedRef.ref.replace(book.title, base_text); - return Sefaria.humanRef(parsedRefCopy.ref); + commRef.ref = commRef.ref.replace(book.title, base_text); + return Sefaria.humanRef(commRefCopy.ref); } else if (many_to_one) { // Rashi on Genesis 1:2:4 => Genesis 1:2; sections and book title need to change - parsedRefCopy.sections = parsedRefCopy.sections.slice(0, parsedRef.sections.length - 1); - parsedRefCopy.toSections = parsedRefCopy.toSections.slice(0, parsedRef.toSections.length - 1); - parsedRefCopy.book = parsedRefCopy.index = parsedRefCopy.index.replace(book.title, base_text); - parsedRefCopy.ref = parsedRefCopy.ref.replace(book.title, base_text); - parsedRefCopy.ref = parsedRefCopy.ref.split(' ').slice(0, -1).join(' '); - return Sefaria.humanRef(Sefaria.makeRef(parsedRefCopy)); - } - return Sefaria.humanRef(parsedRef.ref); - }, - getBaseRefAndFilter(ref, attemptConvertCommToBase) { - // if `attemptConvertCommToBase`, this function converts a commentary ref (Rashi on Genesis 3:3:1) to a base ref (Genesis 3:3) - // and return the filter ["Rashi"] - // `ref` can be an array or a string - if (!attemptConvertCommToBase) { - return {ref: ref, filter: []}; + commRefCopy.sections = commRefCopy.sections.slice(0, commRef.sections.length - 1); + commRefCopy.toSections = commRefCopy.toSections.slice(0, commRef.toSections.length - 1); + commRefCopy.book = commRefCopy.index = commRefCopy.index.replace(book.title, base_text); + commRefCopy.ref = commRefCopy.ref.replace(book.title, base_text); + commRefCopy.ref = commRefCopy.ref.split(' ').slice(0, -1).join(' '); + return Sefaria.humanRef(Sefaria.makeRef(commRefCopy)); } - let parsedRefs = []; - let firstParsedRef, book; // get a parsed ref version of `ref` in order to access book's collective title, base_text_titles, and base_text_mapping - if (ref.constructor === Array) { - parsedRefs = ref.map(x => Sefaria.parseRef(x)); - firstParsedRef = parsedRefs[0]; - book = Sefaria.index(firstParsedRef.index); - ref = parsedRefs.map(x => Sefaria.convertCommToBase(x, book)); + return Sefaria.humanRef(commRef.ref); + }, + getBaseRefAndFilter(ref) { + // This is a helper function for openPanel. + // `ref` can be an array or a string + // This converts a commentary ref(s) (Rashi on Genesis 3:3:1) + // to a base ref(s) (Genesis 3:3) and returns the filter ["Rashi"]. + let filter, book; + if (Array.isArray(ref)) { + const parsedRefs = ref.map(x => Sefaria.parseRef(x)); // get a parsed ref version of `ref` in order to access book's collective title, base_text_titles, and base_text_mapping + book = Sefaria.index(parsedRefs[0].index); + ref = parsedRefs.map(x => Sefaria.convertCommentaryRefToBaseRef(x)); } else { - firstParsedRef = Sefaria.parseRef(ref); - book = Sefaria.index(firstParsedRef.index); - ref = Sefaria.convertCommToBase(firstParsedRef, book); - } - let filter = []; - if (this.isCommentaryWithBaseText(book)) { - filter = [book.collectiveTitle]; + const parsedRef = Sefaria.parseRef(ref); // get a parsed ref version of `ref` in order to access book's collective title, base_text_titles, and base_text_mapping + book = Sefaria.index(parsedRef.index); + ref = Sefaria.convertCommentaryRefToBaseRef(parsedRef); } + filter = book?.collectiveTitle ? [book.collectiveTitle] : []; return {ref: ref, filter: filter}; }, commentaryList: function(title, toc) { From e4be5d812c1895f9dabfb73b09b988f32925ef38 Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Tue, 20 Feb 2024 12:56:40 +0200 Subject: [PATCH 737/756] chore(doc): Refine readme --- README.mkd | 394 ++--------------------------------------------------- 1 file changed, 12 insertions(+), 382 deletions(-) diff --git a/README.mkd b/README.mkd index 49b49c8eef..831a91c2c2 100644 --- a/README.mkd +++ b/README.mkd @@ -1,391 +1,21 @@ -## New Interfaces for Jewish Texts +# Sefaria -[Sefaria](https://www.sefaria.org) is creating interfaces, apps (like a source sheet builder), and infrastructure (like an API and a structured dataset) for Jewish texts and textual learning. +[Sefaria](https://www.sefaria.org) is a digital library and platform for accessing Jewish texts and textual learning. We invite developers to leverage the largest open-source database of Jewish texts in history to build apps and services for the People of the Book. -You can find outputs of our entire database in [Sefaria-Export](https://github.com/Sefaria/Sefaria-Export). +## Getting Started +Visit our [Developer Portal](www.developers.sefaria.org) to read our [docs](https://developers.sefaria.org/docs/getting-started-1), play with our interactive [API Reference](https://developers.sefaria.org/reference/getting-started-with-your-api) and to learn more about Sefaria's open source data and technology. There, you can also find tutorials, installation instructions, and all of the most up-to-date information about our technology. -Interested developers should join the [sefaria-dev mailing list](https://groups.google.com/forum/#!forum/sefaria-dev). +Interested developers can join the [sefaria-dev mailing list](https://groups.google.com/forum/#!forum/sefaria-dev). -For general discussion questions about the project, please email [hello@sefaria.org](mailto:hello@sefaria.org). +## Questions? +- If you have questions about our API or our technology, you can [contact us](https://developers.sefaria.org/page/contact-us) via the [Developer Portal](www.developers.sefaria.org). +- For general questions about using Sefaria.org, please email [hello@sefaria.org](mailto:hello@sefaria.org). -If you find textual problems in our library, please email [corrections@sefaria.org](mailto:corrections@sefaria.org). +## Bugs and Corrections +- If you find textual problems in our library, please email [corrections@sefaria.org](mailto:corrections@sefaria.org). +- If you believe you have encountered a bug while using our website or mobile app, you can report it [here](https://sefaria.formstack.com/forms/bug_report). +- If you find bugs in the code, you can create a [GitHub Issue](https://github.com/sefaria/Sefaria-Project/issues?direction=desc&page=1&sort=created&state=open). -You can post bugs or request/discuss features on [GitHub Issues](https://github.com/sefaria/Sefaria-Project/issues?direction=desc&page=1&sort=created&state=open). Tackling an issue marked as a "Starter Project" is a good way to sink your teeth into Sefaria. - -If you're interested in working on a project you see listed here, please email the [sefaria-dev mailing list](https://groups.google.com/forum/#!forum/sefaria-dev). - - -*** - -### Getting Started -First clone the Sefaria-Project repository to a directory on your computer, then follow the instructions: - -*Note: if you are a developer that might want to contribute code to Sefaria, we suggest first making a fork of this repository by clicking the "Fork" button above when logged in to GitHub.* - -*Note for macOS users - Install `Homebrew`:* - - /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)" - - -There are two methods with which you can run the Sefaria-Project, docker-compose and local install - -#### Run with docker-compose -##### 1) Install Docker and Docker Compose - -Follow the instructions [here](https://docs.docker.com/docker-for-mac/install/) to install Docker and [here](https://docs.docker.com/compose/install/) to install Docker Compose. - -##### 2) Run the project -In your terminal run: - - docker-compose up - -This will build the project and run it. You should now have all the proper services set up, lets add some texts. - -##### 3) Connect to mongo and add texts: -Connect to mongo running on port 27018. We use 27018 instead of the standard port 27017 to avoid conflicts with any mongo instance you may already have running. - -Follow instructions in section 8 below to restore the mongo dump. - - -##### 4) Update your local settings file: -Copy the local settings file: - - cp sefaria/local_settings_example.py sefaria/local_settings.py - -Replace the following values: - - DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.postgresql', - 'NAME': 'sefaria', - 'USER': 'admin', - 'PASSWORD': 'admin', - 'HOST': 'postgres', - 'PORT': '', - } - } - - - MONGO_HOST = "db" - -Optionally, you can replace the cache values as well: - - MULTISERVER_REDIS_SERVER = "cache" - REDIS_HOST = "cache" - -and the respective values in CACHES - - -##### 5) Connect to the django container and run migrations: -In a new terminal window run: - - docker exec -it sefaria-project_web_1 bash - -This will connect you to the django container. Now run: - - python manage.py migrate - -##### 6) Run webpack: -In a new terminal window run: - - docker exec -it sefaria-project-node-1 bash - -This will connect you to the django container. Now run: - - npm run build-client - -or - - npm run watch-client - - -##### 7) Visit the site: -In your browser go to http://localhost:8000 - -If the server isn't running, you may need to run `docker-compose up` again. - - -#### Run locally -#### 1) Install Python 3.7 - -*We Recommend using the latest Python 3.7 as opposed to later versions of Python (esp 3.10 and up) since it has been known to cause some compatibility issues. These are solvable, but for an easier install experience, we currently recommend 3.7* - -###### Linux and macOS -Most UNIX systems come with a python interpreter pre-installed. However, this is generally still Python 2. The recommended way to get Python 3, and not mess up any software the OS is dependent on, is by using Pyenv. You can use the instructions [here](https://github.com/pyenv/pyenv#installation) and also [here](https://opensource.com/article/19/5/python-3-default-mac#what-we-should-do). - -In a later step, we can configure virtual environments to work with Pyenv so you can completely isolate your Sefaria stack. - -###### Windows: -The Pyenv repository above also has recommendations for Windows. - -In order to just plain install Python: -- Read the [Official Python documentation on Windows](https://docs.python.org/3.7/using/windows.html) -- Go to the [Python Download Page](https://www.python.org/downloads/release/python-375/) and download and install Python. -- Add the python directory to your OS' PATH variable if the installer has not done so. - -#### 2) Install virtualenv (Recommended, but optional): -If you work on many Python projects, you may want to keep Sefaria's python installation separate using Virtualenv. If you're happy having Sefaria requirements in your main Python installation, skip this step. - -##### Use With Pyenv (Recommended) -You can use virtualenv functionality while also using Pyenv; this allows you to further isolate your code and requirements from other projects. - -###### Unix & Windows -Instructions [here](https://github.com/pyenv/pyenv-virtualenv#installing-as-a-pyenv-plugin). -###### macOS: -Instructions [here](https://github.com/pyenv/pyenv-virtualenv#installing-with-homebrew-for-macos-users). - -Create [a pyenv virtualenv](https://github.com/pyenv/pyenv-virtualenv#using-pyenv-virtualenv-with-pyenv). - -In your Sefaria directory, run `pyenv local [venv-name]`. This will create a `.python-version` and write the version name provided to the file (e.g. `3.7.5/envs/sefaria-venv`). This should serve to activate the virtualenv whenever you are in the Sefaria directory. - -*Note: If, after following the installation and configuration instructions, running `python -V` still displays the system version, you may have to manually add the shims directory to your path.* If that does not correct the issue, you should check your bash init file (.zshrc, .bashrc, or the like) for the line `eval "$(pyenv init -)"` (not inside an if statement) and change it to `eval "$(pyenv init --path)"` and restart the shell. - -*Note: If you are using an IDE like PyCharm, you can (and should) configure the interpreter options on your Sefaria-Project to point to the python executable of this virtualenv (e.g. `~/.pyenv/versions/3.7.5/envs/sefaria-venv/bin/python3.7`)* - -##### Classic virtualenv -Install [virtualenv](http://pypi.python.org/pypi/virtualenv), then enter these commands: - -*Note: You may need to install Pip (see below) first in order to install virtualenv* - -*Note: You can perform this step from anywhere in your command line, but it might be easier and tidier to run this step from the root of your project directory that you just cloned. e.g `~/web-projects/Sefaria-Project $`* - - virtualenv venv --distribute - source venv/bin/activate - -Now you should see `(venv)` in front of your command prompt. The second command sets your shell to use the Python virtual environment that you've just created. This is something that you have to run every time you open a new shell and want to run the Sefaria demo. You can always tell if you're in the virtual environment by checking if `(venv)` is at the beginning of your command prompt. - -#### 3) Pip: -If you used pyenv, Pip should be available via the pyenv version of Python. -###### Unix -If you don't already have it in your Python installation, install [Pip](https://pip.pypa.io/en/stable/installing/). Then use it to install the required Python packages. - -###### Windows -Use instructions [here](http://www.tylerbutler.com/2012/05/how-to-install-python-pip-and-virtualenv-on-windows-with-powershell/) and then make sure that the scripts subfolder of the python installation directory is also in PATH. - - -*Note: this step (and **most** of the following command line instructions) must be run from the Sefaria-Project root directory* - -Please *temporarily* comment out the line `psycopg2==2.8.6` in `requirements.txt.` and then run: - - pip install -r requirements.txt - -If you are *not* using virtualenv, you may have to run it with sudo: `sudo pip install -r requirements.txt` - -*Note: You'll probably need to install the Python development libraries as well:* - -###### On Debian systems: - - sudo apt-get install python-dev python3-dev libpq-dev - -###### On Fedora systems: - - sudo dnf install python2-devel libpq-devel - - -*Note: If you see an error that `pg_config executable not found`, you need to install PostgreSQL. If on macOS you see an error while `Building wheel for psycopg2` about `linker command failed with exit code 1`, you may need to add the path to OpenSSL* - -``` -export LDFLAGS="-L/usr/local/opt/openssl/lib" -export CPPFLAGS="-I/usr/local/opt/openssl/include" -``` - -After installing the Python development libraries or other dependencies, run `pip install -r requirements.txt` again. - - -#### 4) Install gettext - -`gettext` is a GNU utility that Django uses to manage localizations. - -###### On macOS: - - brew install gettext - - -On some macOS systems `gettext` will still not run after installation and `django manage.py makemessages` will fail. In such a case, one easy solution is to add (replace x's with your gettext version number) to your .bashrc(or its equivalent on your system): - - export TEMP_PATH=$PATH - export PATH=$PATH:/usr/local/Cellar/gettext/0.xx.x/bin - - -###### On Debian systems - - sudo apt-get install gettext - -#### 5) Create a local settings file: - -*Note: this step must be run from the Sefaria-Project root directory* - - cd sefaria - cp local_settings_example.py local_settings.py - vim local_settings.py - -Replace the placeholder values with those matching your environment. For the most part, you should only have to specify values in the top part of the file where it directs you to change the given values. - -Note: Among the placeholder values that need to be replaced, set the `DATABASES` default `NAME` field to a path (including a file name) where Django can create a sqlite database. Using `/path/to/Sefaria-Project/db.sqlite` is sufficient, as we git-ignore all `.sqlite` files. - -You can name your local database (`sefaria` will be the default created by `mongorestore` below). You can leave `SEFARIA_DB_USER` ad `SEFARIA_DB_PASSWORD` blank if you don't need to run authentication on Mongo. - -#### 6) Create a log directory: -Create a directory called `log` under the project folder. To do this, run `mkdir log` from the project's root directory. -Make sure that the server user has write access to it by using a command such as `chmod 777 log`. - -#### 7) Get Mongo running: - -If you don't already have it, [install MongoDB](https://www.mongodb.com/docs/manual/administration/install-community/). Our current data dump requires MongoDB version 4.4 or later. After installing Mongo according to the instructions, you should have a mongo service automatically running in the background. - -Otherwise, run the mongo daemon with: - - mongod - -(Only use sudo if necessary, as it may result in a locked socket file being created that may prevent mongo from running later on). - -Or use your os service manager to have it start on startup. - -Mongo usually sets all the correct paths for itself to run properly nowadays, but see [here](https://www.mongodb.com/docs/manual/tutorial/manage-mongodb-processes/#start-mongod-processes) for details on setting a specific path for it to store data in. - - -#### 8) Put some texts in your database: - -MongoDB dumps of our database are available to download. - -The recommended dump (which is a more manageable size) is available [here](https://storage.googleapis.com/sefaria-mongo-backup/dump_small.tar.gz). - -A complete dump is also available [here](https://storage.googleapis.com/sefaria-mongo-backup/dump.tar.gz). The complete dump includes the `history` collections, which contains a complete revision history of every text in our library. For many applications, this data is not relevant. We recommend using the smaller dump unless you're specifically interested in texts revision history within Sefaria. - -Unzip the file and extract the `dump` folder. If you don't have an app for unzipping, this can be done from Command Prompt by navigating to the folder containing the download and using `tar -xf dump.tar.gz` or `tar -xf dump_small.tar.gz` (depending on which dump you downloaded). - -This `dump` must be restored as a MongoDB database. - -From MongoDB version 4.4, mongorestore comes separately from the MongoDB server, so if you don't already have it, you'll also need to download and unzip the [Database Tools](https://www.mongodb.com/try/download/database-tools?tck=docs_databasetools). You may then need to add its \bin\ directory to your PATH environment variables. - -Once you have the unzipped `dump`; from the folder which contains `dump`, run: - - mongorestore --drop - -This will create (or overwrite) a mongo database called `sefaria`. - -If you used the recommended dump, `dump_small.tar.gz`, create an empty collection inside the `sefaria` database called `history`. This can be done using the mongo client shell or a GUI you have installed, such as MongoDB Compass. - -#### 9) Set up Django's local server - -Sefaria is using Google's reCAPTCHA to verify the user is not a bot. For a deployment, you should register and use your own reCAPTCHA keys (https://pypi.org/project/django-recaptcha/#installation). -For local development, the default test keys would suffice. The warning can be suppressed by uncommenting the following in the local_settings.py file: - - SILENCED_SYSTEM_CHECKS = ['captcha.recaptcha_test_key_error'] - -`manage.py` is used to run and manage the local server. It is located in the root directory of the `Sefaria-Project` codebase. - -Django auth features run on a separate database. To init this database and set up Django's auth system, switch to the root directory of the `Sefaria-Project` code base, and run (from the project root): - - python manage.py migrate - - -#### 10) Install Node: - -*Note: Older versions of `Node` and `npm` ran into a file name length limit on Windows OS. This problem should be mitigated in newer versions on Windows 10.* - -Node is now required to run the site. Even if you choose to have js run only on the client, we are also using [Webpack](https://webpack.js.org/) to bundle our javascript. - -Instaling Node and npm from the main installers on Node's homepage may cause permission issues. For that reason, it is recommended to use one of the alternative methods (with a preference for a version manager like nvm) listed [here](https://nodejs.org/en/download/package-manager/). - -###### Debian, Ubuntu, and Linux Mint -You are better off using `apt-get nodejs` or following the instructions [here](https://github.com/nodesource/distributions/blob/master/README.md). They will install both Node and npm. - -###### macOS -use `brew install node` or [`nvm`](https://nodejs.org/en/download/package-manager/#nvm) - - -Now download the required Javascript libraries and install some global tools for development with the `setup` script (from the project root). - - npm install - npm run setup - -If the second command fails, you may have to install using `sudo npm run setup`. - - -#### Run Webpack - -To get the site running, you need to bundle the javascript with Webpack. Run: - - npm run build-client - -to bundle once. To watch the javascript for changes and automatically rebuild, run: - - npm run watch-client - - -#### Server-side rendering with Node: - -Sefaria uses React.js. To render HTML server-side, we use a Node.js server. For development, the site is fully functional without server-side rendering. For deploying in a production environment, however, server-side HTML is very important for bots and SEO. - -##### Install Redis - -To use server-side rendering, you must also install Redis Cache. `brew install redis` or `sudo apt-get install redis`. - -To run redis: `redis-server`. On macOS: `brew services start redis` - - -##### Configure Django and Node to use Redis as a shared datastore - -**Django** - -Update your `local_settings.py` file and replace the `CACHES` variable with the `CACHES` variable meant for server-side rendering, commented out in `local_settings_example.py`. - -Also, make sure the following are set: -``` -SESSION_CACHE_ALIAS = "default" #declares where Django's session engine will store data -USER_AGENTS_CACHE = 'default' #declares where the Django user agent middleware will store data (cannot be JSON encoded!) -SHARED_DATA_CACHE_ALIAS = 'shared' #Tells the application where to store data placed in cache by `sefaria.system.cache` shared-cache functions - -USE_NODE = True -NODE_HOST = "http://localhost:3000" #Or whatever host and port you set Node up to listen on - -``` - - -**Node** - -The following environment variables, defined in `./node/local_settings.js`, can be set to configure the node instance: - -| Env Var | Default | Description | -|---------|---------|-------------| -| `NODEJS_PORT` | `3000` | The port to be used by the NodeJs service | -| `DEBUG` | `false` | Determines whether the NodeJs service should run in debug mode | -| `REDIS_HOST` | `127.0.0.1` | The Redis instance to point Node to when running | -| `REDIS_PORT` | `6379` | The Default port Redis listens on | - - -These variables can be set via command line explicitly or set up to be defined when your machine's shell runs or set up in your IDE settings for running the node server. - -For development, you can run the Node server using nodemon with: - - npm start - - -To run webpack with server-side rendering, use: - - npm run build - -or - - npm run watch - -#### 11) Run the development server: - - python manage.py runserver - -You can also make it publicly available by specifying 0.0.0.0 for the host: - - python manage.py runserver 0.0.0.0:8000 - -## Command Line Interface - -The shell script `cli` will invoke a python interpreter with the core models loaded, and can be used as a standalone interface to texts or for testing. - - $ ./cli - >>> p = LinkSet(Ref("Genesis 13")) - >>> p.count() - 226 ## Thanks From a016cef36536ab3f9f1e154a11d453898bae55db Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Tue, 20 Feb 2024 13:08:12 +0200 Subject: [PATCH 738/756] chore: refactored Sefaria.sectionRef to derive sectionRef if not found in cache --- static/js/ConnectionsPanel.jsx | 14 +------------- static/js/sefaria/sefaria.js | 11 +++++++++-- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/static/js/ConnectionsPanel.jsx b/static/js/ConnectionsPanel.jsx index 889d9d6560..d4d060d6f2 100644 --- a/static/js/ConnectionsPanel.jsx +++ b/static/js/ConnectionsPanel.jsx @@ -184,19 +184,7 @@ class ConnectionsPanel extends Component { return !prevRefs.compare(nextRefs); } sectionRef() { - const humanRefForm = Sefaria.humanRef(this.props.srefs); - let sectionRef = Sefaria.sectionRef(humanRefForm); - if (!sectionRef) { - //Sefaria.sectionRef checks Sefaria ref cache, if a segment ref isn't there, derive section ref from the segment ref instead, - //for example, given this.props.srefs value of ["Genesis 3:3"] return "Genesis 3" - if (!!humanRefForm && typeof humanRefForm === "string") { - sectionRef = humanRefForm.split(":")[0]; - } - else { - sectionRef = this.props.srefs; - } - } - return sectionRef; + return Sefaria.sectionRef(Sefaria.humanRef(this.props.srefs), true) || this.props.srefs; } loadData() { let ref = this.sectionRef(); diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index dbbb4d2871..b8e50c523c 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -274,10 +274,17 @@ Sefaria = extend(Sefaria, { let index = Sefaria.index(pRef.index); return index && index.categories ? index.categories : []; }, - sectionRef: function(ref) { + sectionRef: function(ref, deriveIfNotFound=false) { // Returns the section level ref for `ref` or null if no data is available const oref = this.getRefFromCache(ref); + if (deriveIfNotFound && !oref) { //couldn't find `ref` in cache so try to derive it + const humanRefForm = Sefaria.humanRef(ref); + if (!!humanRefForm && humanRefForm.length > 0) { + return humanRefForm.split(":")[0]; // "Genesis 3:3" yields "Genesis 3" + } + } return oref ? oref.sectionRef : null; + }, splitSpanningRefNaive: function(ref){ if (ref.indexOf("-") == -1) { return ref; } @@ -2225,7 +2232,7 @@ _media: {}, getBaseRefAndFilter(ref) { // This is a helper function for openPanel. // `ref` can be an array or a string - // This converts a commentary ref(s) (Rashi on Genesis 3:3:1) + // This function converts a commentary ref(s) (Rashi on Genesis 3:3:1) // to a base ref(s) (Genesis 3:3) and returns the filter ["Rashi"]. let filter, book; if (Array.isArray(ref)) { From 495589690a4e50dd62f41bd9da9c563758f70298 Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Tue, 20 Feb 2024 13:40:08 +0200 Subject: [PATCH 739/756] chore(docs): Fix broken links --- README.mkd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.mkd b/README.mkd index 831a91c2c2..797753d831 100644 --- a/README.mkd +++ b/README.mkd @@ -3,12 +3,12 @@ [Sefaria](https://www.sefaria.org) is a digital library and platform for accessing Jewish texts and textual learning. We invite developers to leverage the largest open-source database of Jewish texts in history to build apps and services for the People of the Book. ## Getting Started -Visit our [Developer Portal](www.developers.sefaria.org) to read our [docs](https://developers.sefaria.org/docs/getting-started-1), play with our interactive [API Reference](https://developers.sefaria.org/reference/getting-started-with-your-api) and to learn more about Sefaria's open source data and technology. There, you can also find tutorials, installation instructions, and all of the most up-to-date information about our technology. +Visit our [Developer Portal](https://developers.sefaria.org/) to read our [docs](https://developers.sefaria.org/docs/getting-started-1), play with our interactive [API Reference](https://developers.sefaria.org/reference/getting-started-with-your-api) and to learn more about Sefaria's open source data and technology. There, you can also find tutorials, installation instructions, and all of the most up-to-date information about our technology. Interested developers can join the [sefaria-dev mailing list](https://groups.google.com/forum/#!forum/sefaria-dev). ## Questions? -- If you have questions about our API or our technology, you can [contact us](https://developers.sefaria.org/page/contact-us) via the [Developer Portal](www.developers.sefaria.org). +- If you have questions about our API or our technology, you can [contact us](https://developers.sefaria.org/page/contact-us) via the [Developer Portal](https://developers.sefaria.org/). - For general questions about using Sefaria.org, please email [hello@sefaria.org](mailto:hello@sefaria.org). ## Bugs and Corrections From 39e0383094b47d431c300a8146301ab8cc6c00ed Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Tue, 20 Feb 2024 13:41:20 +0200 Subject: [PATCH 740/756] chore(docs): Switch language --- README.mkd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.mkd b/README.mkd index 797753d831..b837d7bc28 100644 --- a/README.mkd +++ b/README.mkd @@ -1,6 +1,6 @@ # Sefaria -[Sefaria](https://www.sefaria.org) is a digital library and platform for accessing Jewish texts and textual learning. We invite developers to leverage the largest open-source database of Jewish texts in history to build apps and services for the People of the Book. +[Sefaria](https://www.sefaria.org) is a digital library and platform for accessing Jewish texts and textual learning. We invite developers to leverage the largest open-source database of Jewish texts in history to build apps and services utilizing the Jewish canon. ## Getting Started Visit our [Developer Portal](https://developers.sefaria.org/) to read our [docs](https://developers.sefaria.org/docs/getting-started-1), play with our interactive [API Reference](https://developers.sefaria.org/reference/getting-started-with-your-api) and to learn more about Sefaria's open source data and technology. There, you can also find tutorials, installation instructions, and all of the most up-to-date information about our technology. From 854f0192f1f9eca79e08bb35e2a18d164857db6e Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Wed, 21 Feb 2024 11:32:11 +0200 Subject: [PATCH 741/756] fix(Commentary Redirects): saveLastPlace wasn't behaving as it should in showBaseText Specifically, 2 duplicate saveLastPlace events were occurring when only one should. --- static/js/ReaderApp.jsx | 15 ++++++++------- static/js/ReaderPanel.jsx | 16 ++++++++++++---- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index 9aa74b66e4..6659d18a92 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -1422,11 +1422,11 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { this.state.panels = []; // temporarily clear panels directly in state, set properly with setState in openPanelAt this.openPanelAt(0, ref, currVersions, options); } - openPanelAt(n, ref, currVersions, options, replace, attemptConvertCommentaryRefToBaseRef=true, replaceHistory=false) { + openPanelAt(n, ref, currVersions, options, replace, convertCommentaryRefToBaseRef=true, replaceHistory=false, saveLastPlace=true) { // Open a new panel after `n` with the new ref // If `replace`, replace existing panel at `n`, otherwise insert new panel at `n` // If book level, Open book toc - // `attemptConvertCommentaryRefToBaseRef` if true and ref is commentary ref (Rashi on Genesis 3:3:1), open Genesis 3:3 with Rashi's comments in the sidebar + // `convertCommentaryRefToBaseRef` if true and ref is commentary ref (Rashi on Genesis 3:3:1), open Genesis 3:3 with Rashi's comments in the sidebar // `replaceHistory`: can be true when openPanelAt is called from showBaseText in cases of ref normalizing in TextRange when we want to replace history with normalized ref this.replaceHistory = Boolean(replaceHistory); const parsedRef = Sefaria.parseRef(ref); @@ -1445,7 +1445,7 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { }); } else { // Text let filter = []; - if (attemptConvertCommentaryRefToBaseRef && Sefaria.isCommentaryRefWithBaseText(ref)) { + if (convertCommentaryRefToBaseRef && Sefaria.isCommentaryRefWithBaseText(ref)) { ({ref, filter} = Sefaria.getBaseRefAndFilter(ref)); } let refs, currentlyVisibleRef, highlightedRefs; @@ -1469,7 +1469,7 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { currentlyVisibleRef, mode: "Text", ...options }; - if (filter.length > 0) { // there will be a filter such as ["Rashi"] if attemptConvertCommentaryRefToBaseRef is true + if (filter.length > 0) { // there will be a filter such as ["Rashi"] if convertCommentaryRefToBaseRef is true [panel, connectionPanel] = this.makePanelWithConnectionsState(panelProps); } else { @@ -1484,9 +1484,9 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { newPanels.push(connectionPanel); } this.setState({panels: newPanels}); - const panelNumToSave = !replaceHistory ? n+1 : n; - this.saveLastPlace(panel, panelNumToSave, !!connectionPanel); - + if (saveLastPlace) { + this.saveLastPlace(panel, n + 1, !!connectionPanel); + } } makePanelWithConnectionsState(panelProps) { // in the case of multipanel, create two panels based on panelProps @@ -1823,6 +1823,7 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { const hasSidebar = this.doesPanelHaveSidebar(n) || openingSidebar; // if panel is sheet, panel.refs isn't set if ((panel.mode !== 'Sheet' && !panel.refs.length ) || panel.mode === 'Connections') { return; } + console.log('saveLastPlace ', this.getHistoryObject(panel, hasSidebar)); Sefaria.saveUserHistory(this.getHistoryObject(panel, hasSidebar)); } currentlyConnecting() { diff --git a/static/js/ReaderPanel.jsx b/static/js/ReaderPanel.jsx index 3fd44b2a0f..2af79a97db 100644 --- a/static/js/ReaderPanel.jsx +++ b/static/js/ReaderPanel.jsx @@ -236,27 +236,35 @@ class ReaderPanel extends Component { highlightedRefsInSheet }); } - showBaseText(ref, replaceHistory, currVersions={en: null, he: null}, filter=[], attemptConvertCommentaryRefToBaseRef=true) { + showBaseText(ref, replaceHistory, currVersions={en: null, he: null}, filter=[], convertCommentaryRefToBaseRef=true) { // Set the current primary text `ref`, which may be either a string or an array of strings. // `replaceHistory` - bool whether to replace browser history rather than push for this change if (!ref) { return; } this.replaceHistory = Boolean(replaceHistory); - attemptConvertCommentaryRefToBaseRef = this.state.compare ? false : attemptConvertCommentaryRefToBaseRef; + convertCommentaryRefToBaseRef = this.state.compare ? false : convertCommentaryRefToBaseRef; // console.log("showBaseText", ref, replaceHistory); if (this.state.mode === "Connections" && this.props.masterPanelLanguage === "bilingual") { // Connections panels are forced to be mono-lingual. When opening a text from a connections panel, // allow it to return to bilingual. this.state.settings.language = "bilingual"; } - if (ref.constructor !== Array) { + let refs; + if (!Array.isArray(ref)) { const oRef = Sefaria.parseRef(ref); if (oRef.book === "Sheet") { this.openSheet(ref); return; } + refs = [ref]; + } + else { + refs = ref; + } + if (this.replaceHistory) { + this.props.saveLastPlace({ mode: "Text", refs, currVersions, settings: this.state.settings }, this.props.panelPosition); } this.props.openPanelAt(this.props.panelPosition, ref, currVersions, {settings: this.state.settings}, - true, attemptConvertCommentaryRefToBaseRef, this.replaceHistory); + true, convertCommentaryRefToBaseRef, this.replaceHistory, false); } openSheet(sheetRef, replaceHistory) { this.replaceHistory = Boolean(replaceHistory); From c5bbcee209d8db808b19a3da9bf39f8ec29aad44 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Wed, 21 Feb 2024 11:50:28 +0200 Subject: [PATCH 742/756] chore: remove console log statement --- static/js/ReaderApp.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index 6659d18a92..5dace7f929 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -1823,7 +1823,6 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { const hasSidebar = this.doesPanelHaveSidebar(n) || openingSidebar; // if panel is sheet, panel.refs isn't set if ((panel.mode !== 'Sheet' && !panel.refs.length ) || panel.mode === 'Connections') { return; } - console.log('saveLastPlace ', this.getHistoryObject(panel, hasSidebar)); Sefaria.saveUserHistory(this.getHistoryObject(panel, hasSidebar)); } currentlyConnecting() { From 665596d7a760fddb68cde677f31b723790642bda Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Wed, 21 Feb 2024 15:02:58 +0200 Subject: [PATCH 743/756] chore: added comments to sefaria.js for commentary redirects funcs --- static/js/ReaderApp.jsx | 1 + static/js/ReaderPanel.jsx | 6 ++++-- static/js/sefaria/sefaria.js | 26 +++++++++++++++++--------- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index 5dace7f929..4113e61159 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -1428,6 +1428,7 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { // If book level, Open book toc // `convertCommentaryRefToBaseRef` if true and ref is commentary ref (Rashi on Genesis 3:3:1), open Genesis 3:3 with Rashi's comments in the sidebar // `replaceHistory`: can be true when openPanelAt is called from showBaseText in cases of ref normalizing in TextRange when we want to replace history with normalized ref + // `saveLastPlace`: whether to save user history. this.replaceHistory = Boolean(replaceHistory); const parsedRef = Sefaria.parseRef(ref); const index = Sefaria.index(ref); // Do we have to worry about normalization, as in Header.subimtSearch()? diff --git a/static/js/ReaderPanel.jsx b/static/js/ReaderPanel.jsx index 2af79a97db..4a5447ea2f 100644 --- a/static/js/ReaderPanel.jsx +++ b/static/js/ReaderPanel.jsx @@ -237,8 +237,10 @@ class ReaderPanel extends Component { }); } showBaseText(ref, replaceHistory, currVersions={en: null, he: null}, filter=[], convertCommentaryRefToBaseRef=true) { - // Set the current primary text `ref`, which may be either a string or an array of strings. - // `replaceHistory` - bool whether to replace browser history rather than push for this change + /* Set the current primary text `ref`, which may be either a string or an array of strings. + * `replaceHistory` - bool whether to replace browser history rather than push for this change + * `` - + */ if (!ref) { return; } this.replaceHistory = Boolean(replaceHistory); convertCommentaryRefToBaseRef = this.state.compare ? false : convertCommentaryRefToBaseRef; diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index b8e50c523c..a837e42db3 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -2191,20 +2191,28 @@ _media: {}, return result; }, isCommentaryWithBaseText(book) { - // only returns true for commentaries with a base_text_mapping to one and only one base_text_title + /* Only returns true for commentaries with a base_text_mapping to one and only one base_text_title + * @param {Object} book: Corresponds to a book in Sefaria.toc + */ return book?.dependence === "Commentary" && !!book?.base_text_titles && !!book?.base_text_mapping && book?.base_text_titles.length === 1; }, isCommentaryRefWithBaseText(ref) { - // only returns true for commentaries with a base_text_mapping to one and only one base_text_title + /* This is a helper function for openPanelAt. Determines whether the ref(s) are part of a commentary + * with a base_text_mapping to one and only one base_text_title. + * Example: "Ibn Ezra on Genesis 3" returns True because this commentary has a base_text_mapping to one and only one book, Genesis. + * @param {string/array of strings} ref: if array, checks the first ref + */ let refToCheck = Array.isArray(ref) ? ref[0] : ref; const parsedRef = Sefaria.parseRef(refToCheck); const book = Sefaria.index(parsedRef.index); return this.isCommentaryWithBaseText(book); }, convertCommentaryRefToBaseRef(commRef) { - // converts commentary ref, `commRef`, to base ref: - // example input and output: Rashi on Genesis 1:2 => Genesis 1:2, - // Rashi on Exodus 2:3:1 => Exodus 2:3 + /* Converts commentary ref, `commRef`, to base ref: + @param {string} commRef - string to be converted. + Example input and output: commRef = "Rashi on Genesis 1:2" returns "Genesis 1:2", + commRef = "Rashi on Exodus 2:3:1" returns "Exodus 2:3" + */ const book = Sefaria.index(commRef.index); if (!book || !this.isCommentaryWithBaseText(book)) { // if book is not isCommentaryWithBaseText just return the ref @@ -2230,10 +2238,10 @@ _media: {}, return Sefaria.humanRef(commRef.ref); }, getBaseRefAndFilter(ref) { - // This is a helper function for openPanel. - // `ref` can be an array or a string - // This function converts a commentary ref(s) (Rashi on Genesis 3:3:1) - // to a base ref(s) (Genesis 3:3) and returns the filter ["Rashi"]. + /* This is a helper function for openPanelAt. This function converts a commentary ref(s) (Rashi on Genesis 3:3:1) + to a base ref(s) (Genesis 3:3) and returns the filter ["Rashi"]. + `ref` can be an array or a string, in which case the returned ref will be an array or string + */ let filter, book; if (Array.isArray(ref)) { const parsedRefs = ref.map(x => Sefaria.parseRef(x)); // get a parsed ref version of `ref` in order to access book's collective title, base_text_titles, and base_text_mapping From 2736f7ecd028fbc526a3c1e51ae9c3eb5ca08565 Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Wed, 21 Feb 2024 15:09:36 +0200 Subject: [PATCH 744/756] chore: add comment --- static/js/ReaderPanel.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/ReaderPanel.jsx b/static/js/ReaderPanel.jsx index 4a5447ea2f..30062dfc24 100644 --- a/static/js/ReaderPanel.jsx +++ b/static/js/ReaderPanel.jsx @@ -239,7 +239,7 @@ class ReaderPanel extends Component { showBaseText(ref, replaceHistory, currVersions={en: null, he: null}, filter=[], convertCommentaryRefToBaseRef=true) { /* Set the current primary text `ref`, which may be either a string or an array of strings. * `replaceHistory` - bool whether to replace browser history rather than push for this change - * `` - + * `convertCommentaryRefToBaseRef` - bool whether to try to convert commentary refs like "Rashi on Genesis 3:2" to "Genesis 3:2" */ if (!ref) { return; } this.replaceHistory = Boolean(replaceHistory); From 427c4f7ab251c82699242249118bac8de76045ab Mon Sep 17 00:00:00 2001 From: stevekaplan123 <stevek004@gmail.com> Date: Wed, 21 Feb 2024 15:46:50 +0200 Subject: [PATCH 745/756] chore: docstrings for showbasetext and openpanelat --- static/js/ReaderApp.jsx | 16 ++++++++++------ static/js/ReaderPanel.jsx | 4 ++-- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index 4113e61159..e7a0e90ad2 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -1423,12 +1423,16 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { this.openPanelAt(0, ref, currVersions, options); } openPanelAt(n, ref, currVersions, options, replace, convertCommentaryRefToBaseRef=true, replaceHistory=false, saveLastPlace=true) { - // Open a new panel after `n` with the new ref - // If `replace`, replace existing panel at `n`, otherwise insert new panel at `n` - // If book level, Open book toc - // `convertCommentaryRefToBaseRef` if true and ref is commentary ref (Rashi on Genesis 3:3:1), open Genesis 3:3 with Rashi's comments in the sidebar - // `replaceHistory`: can be true when openPanelAt is called from showBaseText in cases of ref normalizing in TextRange when we want to replace history with normalized ref - // `saveLastPlace`: whether to save user history. + /* Open a new panel or replace existing panel. If book level, Open book toc + * @param {int} n: Open new panel after `n` with the new ref + * @param {string} ref: ref to use for new panel. `ref` can refer to book, actual ref, or sheet. + * @param {Object} currVersions: Object with properties `en` and `he` + * @param {Object} options: options to use for new panel + * @param {bool} replace: whether to replace existing panel at `n`, otherwise insert new panel at `n` + * @param {bool} convertCommentaryRefToBaseRef: if true and ref is commentary ref (Rashi on Genesis 3:3:1), open Genesis 3:3 with Rashi's comments in the sidebar + * @param {bool} replaceHistory: can be true when openPanelAt is called from showBaseText in cases of ref normalizing in TextRange when we want to replace history with normalized ref + * @param {bool} saveLastPlace: whether to save user history. + */ this.replaceHistory = Boolean(replaceHistory); const parsedRef = Sefaria.parseRef(ref); const index = Sefaria.index(ref); // Do we have to worry about normalization, as in Header.subimtSearch()? diff --git a/static/js/ReaderPanel.jsx b/static/js/ReaderPanel.jsx index 30062dfc24..ef8c49166e 100644 --- a/static/js/ReaderPanel.jsx +++ b/static/js/ReaderPanel.jsx @@ -238,8 +238,8 @@ class ReaderPanel extends Component { } showBaseText(ref, replaceHistory, currVersions={en: null, he: null}, filter=[], convertCommentaryRefToBaseRef=true) { /* Set the current primary text `ref`, which may be either a string or an array of strings. - * `replaceHistory` - bool whether to replace browser history rather than push for this change - * `convertCommentaryRefToBaseRef` - bool whether to try to convert commentary refs like "Rashi on Genesis 3:2" to "Genesis 3:2" + * @param {bool} `replaceHistory` - whether to replace browser history rather than push for this change + * @param {bool} `convertCommentaryRefToBaseRef` - whether to try to convert commentary refs like "Rashi on Genesis 3:2" to "Genesis 3:2" */ if (!ref) { return; } this.replaceHistory = Boolean(replaceHistory); From 1d1a9a012dd4251807f60598202bb69ab20726bf Mon Sep 17 00:00:00 2001 From: Russel Neiss <russel.neiss@gmail.com> Date: Wed, 21 Feb 2024 11:35:13 -0600 Subject: [PATCH 746/756] fix: Escape search query in title content to prevent xss attack --- static/js/ReaderApp.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/static/js/ReaderApp.jsx b/static/js/ReaderApp.jsx index 5c30ecf5fc..fefde31366 100644 --- a/static/js/ReaderApp.jsx +++ b/static/js/ReaderApp.jsx @@ -474,7 +474,7 @@ class ReaderApp extends Component { break; case "search": const query = state.searchQuery ? encodeURIComponent(state.searchQuery) : ""; - hist.title = state.searchQuery ? state.searchQuery + " | " : ""; + hist.title = state.searchQuery ? state.searchQuery.stripHtml() + " | " : ""; hist.title += Sefaria._(siteName + " Search"); hist.url = "search" + (state.searchQuery ? (`&q=${query}&tab=${state.searchTab}` + state.textSearchState.makeURL({ prefix: 't', isStart: false }) + @@ -920,7 +920,7 @@ toggleSignUpModal(modalContentKind = SignUpModalKind.Default) { }); } } - + handleNavigationClick(ref, currVersions, options) { this.openPanel(ref, currVersions, options); } From 46ffefbebef97b0bf738549e583a89fd6cf32eac Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler.cohen@gmail.com> Date: Wed, 21 Feb 2024 22:42:08 -0500 Subject: [PATCH 747/756] Fix Hebrew on Powered By page Lev's original method was the better approach but messed up the header and footer. Given the time constraint to ship, a similar approach was done as the Ramban Landing page and others: English text was duplicated and passed as Hebrew text. --- static/js/StaticPages.jsx | 39 ++++++++++++++++++++++++++++---- templates/static/powered-by.html | 12 ++++++---- 2 files changed, 41 insertions(+), 10 deletions(-) diff --git a/static/js/StaticPages.jsx b/static/js/StaticPages.jsx index eaec651ec9..643a3ea7eb 100644 --- a/static/js/StaticPages.jsx +++ b/static/js/StaticPages.jsx @@ -1928,13 +1928,16 @@ const WordByWordPage = () => ( const PoweredByPage = () => ( - <StaticPage> + <StaticPage optionalClass="englishOnly"> <Header enTitle="Powered by Sefaria" enText="Did you know that Sefaria’s open data and API can be used by anyone to create new technological solutions for learning Torah? You can find it all for free in our GitHub repository!" + heTitle="Powered by Sefaria" heText="Did you know that Sefaria’s open data and API can be used by anyone to create new technological solutions for learning Torah? You can find it all for free in our GitHub repository!" enActionURL="https://github.com/Sefaria" enActionText="Create Something New" + heActionURL="https://github.com/Sefaria" + heActionText="Create Something New" newTab={true} /> <GreyBox> @@ -1942,6 +1945,9 @@ const PoweredByPage = () => ( <EnBlock padded={true}> <p>We do our best to ensure that the texts we put in our library come with a Creative Commons license, so the texts can be used and reused, for free. We also make all of our code available for open source use by other developers in any way they’d like. To date, there are more than 70 projects Powered by Sefaria, and nearly 100 sites linked to the Sefaria library through our Linker API.</p> </EnBlock> + <HeBlock padded={true}> + <p>We do our best to ensure that the texts we put in our library come with a Creative Commons license, so the texts can be used and reused, for free. We also make all of our code available for open source use by other developers in any way they’d like. To date, there are more than 70 projects Powered by Sefaria, and nearly 100 sites linked to the Sefaria library through our Linker API.</p> + </HeBlock> <Spacer/> </GreyBox> <GreyBox light={true}> @@ -1954,6 +1960,10 @@ const PoweredByPage = () => ( enImgAlt="Screenshot of AlHaTorah" borderColor={palette.colors.darkteal} link="https://alhatorah.org/" + heText="AlHaTorah is a website with a broad range of tools for studying Tanakh, including study guides broken down by parashah, biblical art, and interactive modules. Among the available sources, AlHaTorah makes use of biblical commentaries from Sefaria’s library." + heTitle="AlHaTorah" + heImg="/static/img/powered-by-landing-page/alhatorah.org_.png" + heImgAlt="Screenshot of AlHaTorah" /> <Feature enTitle="AllDaf" @@ -1962,6 +1972,10 @@ const PoweredByPage = () => ( enImgAlt="Screenshot of AllDaf" borderColor={palette.colors.yellow} link="https://alldaf.org/" + heTitle="AllDaf" + heText="This app for learning Daf Yomi from the Orthodox Union provides users with personalized feeds that adapt to learners’ interests, supporting study of the Daf in a user-friendly and approachable format. The English text on AllDaf is sourced from Sefaria." + heImg="/static/img/powered-by-landing-page/alldaf.org_.png" + heImgAlt="Screenshot of AllDaf" /> <Feature enTitle="Hadran" @@ -1970,6 +1984,10 @@ const PoweredByPage = () => ( enImgAlt="Screenshot of Hadran" borderColor={palette.colors.green} link="https://hadran.org.il/" + heTitle="Hadran" + heText="Founded in 2018 by a group of women studying Talmud together, Hadran aims to make the study of Talmud more accessible to Jewish women at all levels of learning. Among the resources they provide are guides to learning Daf Yomi, and these lessons use texts from Sefaria." + heImg="/static/img/powered-by-landing-page/hadran.org.il_daf_yevamot-63_.png" + heImgAlt="Screenshot of Hadran" /> <Feature enTitle="Dicta" @@ -1978,6 +1996,10 @@ const PoweredByPage = () => ( enImgAlt="Screenshot of Dicta" borderColor={palette.colors.lightblue} link="https://dicta.org.il/" + heTitle="Dicta" + heText="Dicta is a nonprofit research organization based in Israel that applies cutting-edge machine learning and natural language processing (the ability of a computer program to understand human language as it is spoken and written) to the analysis of Hebrew texts. Sefaria and Dicta often collaborate, sharing texts and splitting the costs of shared projects. Dicta offers a broad range of tools for free use by anyone, including the ability to add nikud (vocalization) to text as you type, intuitive Talmud and Bible search, and more." + heImg="/static/img/powered-by-landing-page/talmudsearch.dicta.org.il_.png" + heImgAlt="Screenshot of Dicta" /> <Feature enTitle="Artscroll Smart Siddur" @@ -1986,12 +2008,19 @@ const PoweredByPage = () => ( enImgAlt="Screenshot of Artscroll Smart Siddur" borderColor={palette.colors.red} link="https://www.artscroll.com/Categories/DSD.html" + heTitle="Artscroll Smart Siddur" + heText="This app converts the popular ArtSchool Siddur into a fully digital format, including hyperlinks to secondary sources, translations, and commentary, as well as the ability to customize your siddur experience. When you click on citations to non-ArtScroll books in the siddur's footnotes, they include texts from the Sefaria library." + heImg="/static/img/powered-by-landing-page/artscroll siddur.png" + heImgAlt="Screenshot of Artscroll Smart Siddur" /> <GreyBox> <H2Block en="Tell us about your projects!" he="Tell us about your projects!"/> <EnBlock padded={true}> <p>Have you used Sefaria’s data to build an app, visualization, website, or other digital tool? Tell us about it! We’d love to see your project. You can also reach out to us with your questions about our open source data and API by writing to us at <a href="mailto:hello@sefaria.org">hello@sefaria.org</a>.</p> </EnBlock> + <HeBlock padded={true}> + <p>Have you used Sefaria’s data to build an app, visualization, website, or other digital tool? Tell us about it! We’d love to see your project. You can also reach out to us with your questions about our open source data and API by writing to us at <a href="mailto:hello@sefaria.org">hello@sefaria.org</a>.</p> + </HeBlock> <Spacer/> </GreyBox> @@ -2014,19 +2043,19 @@ const PoweredByPage = () => ( ["Sefaria Sidebar Extension", "https://github.com/DovOps/SefariaSidebarExtension/"], ["Kindle Seforim", "https://kindleseforim.paritcher.com/"], ["The Jewish Story Through Books", "https://joshcooper417.github.io/"] - ].map(i => + ].map(([enText, hrefURL]) => <SimpleButton white={true} rounded={false} tall={true} newTab={true} - href={i[1]} - en={i[0]} + href={hrefURL} + en={enText} + he={enText} />) } </ButtonRow> - </StaticPage> ); diff --git a/templates/static/powered-by.html b/templates/static/powered-by.html index 165bca637a..f829cff99c 100644 --- a/templates/static/powered-by.html +++ b/templates/static/powered-by.html @@ -9,6 +9,13 @@ .staticPage .staticPageHeader { background-image: url(/static/img/powered-by-landing-page/PBS_header.png); } + +/* Page only has English content currently +* This will need to be changed in the future should the page become intermixed with Hebrew and English +*/ +.staticPage.englishOnly .int-he { + direction: ltr; +} {% endblock %} @@ -17,11 +24,6 @@ {% autoescape off %} DJANGO_VARS.containerId = 'PoweredByInner'; DJANGO_VARS.reactComponentName = 'PoweredByPage'; - ['body', '#content'].forEach(q => { - const cl = document.querySelector(q).classList; - cl.remove('interface-hebrew'); - cl.add('interface-english'); - }); {% endautoescape %} </script> {% endblock %} From e769123bf2c06ece147a5a4ae14809b2777baca2 Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler.cohen@gmail.com> Date: Wed, 21 Feb 2024 23:23:40 -0500 Subject: [PATCH 748/756] Update Powered By Page to incorporate Sefaria's new developer portal! * Change copy to remove references to GitHub * Link to the new developer portal * Add developers@sefaria.org email address --- static/js/StaticPages.jsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/static/js/StaticPages.jsx b/static/js/StaticPages.jsx index 643a3ea7eb..3101816523 100644 --- a/static/js/StaticPages.jsx +++ b/static/js/StaticPages.jsx @@ -1931,12 +1931,12 @@ const PoweredByPage = () => ( <StaticPage optionalClass="englishOnly"> <Header enTitle="Powered by Sefaria" - enText="Did you know that Sefaria’s open data and API can be used by anyone to create new technological solutions for learning Torah? You can find it all for free in our GitHub repository!" + enText="Did you know that Sefaria’s open data and API can be used by anyone to create new technological solutions for learning Torah? You can find it all for free in the Sefaria Developer Portal!" heTitle="Powered by Sefaria" - heText="Did you know that Sefaria’s open data and API can be used by anyone to create new technological solutions for learning Torah? You can find it all for free in our GitHub repository!" - enActionURL="https://github.com/Sefaria" + heText="Did you know that Sefaria’s open data and API can be used by anyone to create new technological solutions for learning Torah? You can find it all for free in the Sefaria Developer Portal!" + enActionURL="https://developers.sefaria.org/" enActionText="Create Something New" - heActionURL="https://github.com/Sefaria" + heActionURL="https://developers.sefaria.org/" heActionText="Create Something New" newTab={true} /> @@ -2016,10 +2016,10 @@ const PoweredByPage = () => ( <GreyBox> <H2Block en="Tell us about your projects!" he="Tell us about your projects!"/> <EnBlock padded={true}> - <p>Have you used Sefaria’s data to build an app, visualization, website, or other digital tool? Tell us about it! We’d love to see your project. You can also reach out to us with your questions about our open source data and API by writing to us at <a href="mailto:hello@sefaria.org">hello@sefaria.org</a>.</p> + <p>Have you used Sefaria’s data to build an app, visualization, website, or other digital tool? Tell us about it! We’d love to see your project. You can also reach out to us with your questions about our open source data and API by writing to us at <a href="mailto:developers@sefaria.org">developers@sefaria.org</a>.</p> </EnBlock> <HeBlock padded={true}> - <p>Have you used Sefaria’s data to build an app, visualization, website, or other digital tool? Tell us about it! We’d love to see your project. You can also reach out to us with your questions about our open source data and API by writing to us at <a href="mailto:hello@sefaria.org">hello@sefaria.org</a>.</p> + <p>Have you used Sefaria’s data to build an app, visualization, website, or other digital tool? Tell us about it! We’d love to see your project. You can also reach out to us with your questions about our open source data and API by writing to us at <a href="mailto:developers@sefaria.org">developers@sefaria.org</a>.</p> </HeBlock> <Spacer/> </GreyBox> From 2be8be2b1ffd40269954c3b74ed3c8ac239ea9a4 Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Thu, 22 Feb 2024 12:11:39 +0200 Subject: [PATCH 749/756] chore(emails): Switch to developers --- reader/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reader/views.py b/reader/views.py index 1f8cf7386a..7c24607b47 100644 --- a/reader/views.py +++ b/reader/views.py @@ -3309,7 +3309,7 @@ def global_activity(request, page=1): if page > 40: return render_template(request,'static/generic.html', None, { "title": "Activity Unavailable", - "content": "You have requested a page deep in Sefaria's history.<br><br>For performance reasons, this page is unavailable. If you need access to this information, please <a href='mailto:dev@sefaria.org'>email us</a>." + "content": "You have requested a page deep in Sefaria's history.<br><br>For performance reasons, this page is unavailable. If you need access to this information, please <a href='mailto:developers@sefaria.org'>email us</a>." }) if "api" in request.GET: @@ -3352,7 +3352,7 @@ def user_activity(request, slug, page=1): if page > 40: return render_template(request,'static/generic.html', None, { "title": "Activity Unavailable", - "content": "You have requested a page deep in Sefaria's history.<br><br>For performance reasons, this page is unavailable. If you need access to this information, please <a href='mailto:dev@sefaria.org'>email us</a>." + "content": "You have requested a page deep in Sefaria's history.<br><br>For performance reasons, this page is unavailable. If you need access to this information, please <a href='mailto:developers@sefaria.org'>email us</a>." }) q = {"user": profile.id} From 8c0d4f1f7b63d7fe0ce2b2cdddd526b23bc8dc1f Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Thu, 22 Feb 2024 12:26:35 +0200 Subject: [PATCH 750/756] chore(emails): Update email contacts --- reader/views.py | 4 ++-- static/js/s1/editor.js | 2 +- templates/500.html | 7 +++---- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/reader/views.py b/reader/views.py index 7c24607b47..cfdf7ae2b7 100644 --- a/reader/views.py +++ b/reader/views.py @@ -3309,7 +3309,7 @@ def global_activity(request, page=1): if page > 40: return render_template(request,'static/generic.html', None, { "title": "Activity Unavailable", - "content": "You have requested a page deep in Sefaria's history.<br><br>For performance reasons, this page is unavailable. If you need access to this information, please <a href='mailto:developers@sefaria.org'>email us</a>." + "content": "You have requested a page deep in Sefaria's history.<br><br>For performance reasons, this page is unavailable. If you need access to this information, please <a href='mailto:hello@sefaria.org'>email us</a>." }) if "api" in request.GET: @@ -3352,7 +3352,7 @@ def user_activity(request, slug, page=1): if page > 40: return render_template(request,'static/generic.html', None, { "title": "Activity Unavailable", - "content": "You have requested a page deep in Sefaria's history.<br><br>For performance reasons, this page is unavailable. If you need access to this information, please <a href='mailto:developers@sefaria.org'>email us</a>." + "content": "You have requested a page deep in Sefaria's history.<br><br>For performance reasons, this page is unavailable. If you need access to this information, please <a href='mailto:hello@sefaria.org'>email us</a>." }) q = {"user": profile.id} diff --git a/static/js/s1/editor.js b/static/js/s1/editor.js index 5d3de21610..93925068ab 100644 --- a/static/js/s1/editor.js +++ b/static/js/s1/editor.js @@ -607,7 +607,7 @@ $(function() { if (sjs.current.isComplex) { sjs.hideAbout(); - sjs.alert.message("This text is not user editable - please email dev@sefaria.org"); + sjs.alert.message("This text is not user editable - please email corrections@sefaria.org"); return; } if (!sjs._uid) { diff --git a/templates/500.html b/templates/500.html index de0a038b10..2fbd59d423 100644 --- a/templates/500.html +++ b/templates/500.html @@ -14,15 +14,14 @@ <h1> <p> <span class="int-en"> Unfortunately, we've encountered an error. - An email has been sent automatically to our developers. If you have any questions about the problem, or think you can help us figure out what went wrong, - please email our <a href="mailto:dev@sefaria.org" target="_blank">development team</a>. + please email <a href="mailto:hello@sefaria.org" target="_blank">us</a>. </span> <span class="int-he"> - לצערנו ארעה תקלה במערכת. מייל בעניין כבר נשלח אל המפתחים שלנו. + לצערנו ארעה תקלה במערכת. אם יש לכם שאלות נוספות לגבי התקלה, - או שביכלתכם להבין את התקלה ולעזור לאתר אותה, נשמח לשמוע <a href="mailto:dev@sefaria.org" target="_blank">במייל אל צוות המפתחים</a>. + או שביכלתכם להבין את התקלה ולעזור לאתר אותה, <a href="mailto:hello@sefaria.org" target="_blank">נשמח לשמוע</a>. </span> </p> </div> From 7fb2ab2eb172aed1ea911faaa90c7f041cfc23e1 Mon Sep 17 00:00:00 2001 From: saengel <sarah@sefaria.org> Date: Mon, 26 Feb 2024 10:32:30 +0200 Subject: [PATCH 751/756] chore(docs): Adjust language --- README.mkd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.mkd b/README.mkd index b837d7bc28..4f0967362a 100644 --- a/README.mkd +++ b/README.mkd @@ -1,6 +1,6 @@ # Sefaria -[Sefaria](https://www.sefaria.org) is a digital library and platform for accessing Jewish texts and textual learning. We invite developers to leverage the largest open-source database of Jewish texts in history to build apps and services utilizing the Jewish canon. +[Sefaria](https://www.sefaria.org) is a digital library and platform for accessing Jewish texts and text-based learning. We invite developers to leverage the largest open-source database of Jewish texts in history to build apps and services utilizing the Jewish canon. ## Getting Started Visit our [Developer Portal](https://developers.sefaria.org/) to read our [docs](https://developers.sefaria.org/docs/getting-started-1), play with our interactive [API Reference](https://developers.sefaria.org/reference/getting-started-with-your-api) and to learn more about Sefaria's open source data and technology. There, you can also find tutorials, installation instructions, and all of the most up-to-date information about our technology. From d55335a107a816103d2fe5257c12a6b50854d394 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Mon, 26 Feb 2024 14:21:24 +0200 Subject: [PATCH 752/756] fix(community page): change api param options["filtered"] to 1 rather than true. --- static/js/CommunityPage.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/CommunityPage.jsx b/static/js/CommunityPage.jsx index 50e9c368b0..8510b13093 100644 --- a/static/js/CommunityPage.jsx +++ b/static/js/CommunityPage.jsx @@ -76,7 +76,7 @@ CommunityPage.propTypes = { const RecentlyPublished = ({multiPanel, toggleSignUpModal}) => { const options = Sefaria.interfaceLang === "hebrew" ? {"lang": "hebrew"} : {}; - options["filtered"] = true; + options["filtered"] = 1; const pageSize = 18; const [nSheetsLoaded, setNSheetsLoded] = useState(0); // counting sheets loaded from the API, may be different than sheets displayed // Start with recent sheets in the cache, if any From 5e026561888ab26bb1fba77e35291f550df4735c Mon Sep 17 00:00:00 2001 From: Skyler Cohen <skyler@sefaria.org> Date: Mon, 26 Feb 2024 20:17:29 -0500 Subject: [PATCH 753/756] chore(powered-by): Remove some broken project links on the Powered By page --- static/js/StaticPages.jsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/static/js/StaticPages.jsx b/static/js/StaticPages.jsx index 3101816523..69396320d6 100644 --- a/static/js/StaticPages.jsx +++ b/static/js/StaticPages.jsx @@ -2027,17 +2027,14 @@ const PoweredByPage = () => ( <ButtonRow white={true} enTitle="Explore a few more projects" heTitle="Explore a few more projects"> {[["HaTanakh.com", "http://www.hatanakh.com/"], ["Koveah", "https://koveah.org/"], - ["Parasha Bytes", "https://parashabytes.zemon.name/bytes/"], ["Shnayim Mikra", "http://www.shnayim.com/"], ["Russel Neiss' Micrography", "https://github.com/rneiss/micrography"], ["Sefaria Wordpress Plugin", "https://github.com/JoshMB/sefaria-wp-plugin"], - ["Mizmor Shir", "http://mizmor-shir.herokuapp.com/"], ["Capish - Interactive Learning", "https://capish.me/"], ["Sefer Similarity Map", "https://jhostyk.github.io/SeferSimilarityMap/"], ["Sefaria Space: (Topic Museum + Text Mania)", "https://sefaria-space.web.app/"], ["T'Feeling", "https://tfeeling.netlify.app/"], ["The Taryag Mitzvos", "https://thetaryag.com/"], - ["Visualizations of Sefaria", "https://guedalia.github.io/testab/test"], ["Gematriaphone", "http://alexboxer.com/gematriaphone/"], ["Yamim Noraim Machzor", "https://play.google.com/store/apps/details?id=com.machzoryamimnoraim"], ["Sefaria Sidebar Extension", "https://github.com/DovOps/SefariaSidebarExtension/"], From f1e1b34579b984722d0e6a2d62d9f6ae8de97500 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Sun, 3 Mar 2024 19:51:54 +0200 Subject: [PATCH 754/756] feat(text api-v3): add strip_only_footnotes as optional value of return_format. this is useful for the native app. --- api/views.py | 2 +- sefaria/model/text_reuqest_adapter.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/api/views.py b/api/views.py index 0b5a3a5202..bd1525390a 100644 --- a/api/views.py +++ b/api/views.py @@ -7,7 +7,7 @@ class Text(View): - RETURN_FORMATS = ['default', 'wrap_all_entities', 'text_only'] + RETURN_FORMATS = ['default', 'wrap_all_entities', 'text_only', 'strip_only_footnotes'] def dispatch(self, request, *args, **kwargs): try: diff --git a/sefaria/model/text_reuqest_adapter.py b/sefaria/model/text_reuqest_adapter.py index e47e5fd047..d98aa31ba1 100644 --- a/sefaria/model/text_reuqest_adapter.py +++ b/sefaria/model/text_reuqest_adapter.py @@ -175,6 +175,10 @@ def make_named_entities_dict(): lambda string, _: text.AbstractTextRecord.remove_html(string), lambda string, _: ' '.join(string.split())] + elif self.return_format == 'strip_only_footnotes': + text_modification_funcs = [lambda string, _: text.AbstractTextRecord.strip_itags(string), + lambda string, _: ' '.join(string.split())] + else: return From 77068c8b7f499f9c942b3c6aa2131b9270c6f177 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Sun, 3 Mar 2024 20:07:07 +0200 Subject: [PATCH 755/756] text(text api-v3): add test for strip_only_footnotes. adjust test_error_return_format for the new value. --- api/tests.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/api/tests.py b/api/tests.py index c3b0ffefba..2080fe2549 100644 --- a/api/tests.py +++ b/api/tests.py @@ -211,8 +211,16 @@ def test_text_only(self): data = json.loads(response.content) self.assertFalse('<' in data['versions'][0]['text']) + def test_strip_only_footnotes(self): + vtitle = "The Contemporary Torah, Jewish Publication Society, 2006" + response = c.get(f"/api/v3/texts/Genesis%201:1?version=english|{vtitle}&return_format=strip_only_footnotes") + self.assertEqual(200, response.status_code) + data = json.loads(response.content) + self.assertFalse('<i class="footnote">' in data['versions'][0]['text']) + + def test_error_return_format(self): response = c.get(f"/api/v3/texts/Shulchan_Arukh%2C_Orach_Chayim.1:1?return_format=not_valid") self.assertEqual(400, response.status_code) data = json.loads(response.content) - self.assertEqual(data['error'], "return_format should be one of those formats: ['default', 'wrap_all_entities', 'text_only'].") + self.assertEqual(data['error'], "return_format should be one of those formats: ['default', 'wrap_all_entities', 'text_only', 'strip_only_footnotes'].") From 6e9487e49e1c04a8de22f133f08936d43468f813 Mon Sep 17 00:00:00 2001 From: YishaiGlasner <ishaigla@gmail.com> Date: Sun, 3 Mar 2024 20:07:54 +0200 Subject: [PATCH 756/756] fix(text api-v3): fix value from true to 1. --- api/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/tests.py b/api/tests.py index 2080fe2549..b9c9683d10 100644 --- a/api/tests.py +++ b/api/tests.py @@ -181,7 +181,7 @@ def test_api_get_text_no_version(self): def test_fill_in_missing_segments(self): vtitle = "Maimonides' Mishneh Torah, edited by Philip Birnbaum, New York, 1967" - response = c.get(f"/api/v3/texts/Mishneh_Torah,_Sabbath_1?version=english|{vtitle}&fill_in_missing_segments=true") + response = c.get(f"/api/v3/texts/Mishneh_Torah,_Sabbath_1?version=english|{vtitle}&fill_in_missing_segments=1") self.assertEqual(200, response.status_code) data = json.loads(response.content) self.assertTrue(len(data['versions'][0]['text']) > 2)