[
  {
    "path": ".editorconfig",
    "content": "[*]\ncharset = utf-8\nend_of_line = lf\nindent_size = 4\nindent_style = space\ninsert_final_newline = true\nmax_line_length = 600\ntab_width = 4\ntrim_trailing_whitespace = true\nij_continuation_indent_size = 8\nij_formatter_off_tag = @formatter:off\nij_formatter_on_tag = @formatter:on\nij_formatter_tags_enabled = false\nij_smart_tabs = false\nij_visual_guides = none\nij_wrap_on_typing = false\n\n[*.blade.php]\nij_blade_keep_indents_on_empty_lines = false\n\n[*.css]\nij_css_align_closing_brace_with_properties = false\nij_css_blank_lines_around_nested_selector = 1\nij_css_blank_lines_between_blocks = 1\nij_css_brace_placement = end_of_line\nij_css_enforce_quotes_on_format = false\nij_css_hex_color_long_format = true\nij_css_hex_color_lower_case = false\nij_css_hex_color_short_format = false\nij_css_hex_color_upper_case = true\nij_css_keep_blank_lines_in_code = 2\nij_css_keep_indents_on_empty_lines = false\nij_css_keep_single_line_blocks = false\nij_css_properties_order = position,display,visibility,float,clear,top,right,bottom,left,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,order,z-index,width,height,min-width,min-height,max-width,max-height,box-sizing,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,overflow,overflow-x,overflow-y,content,resize,opacity,outline,outline-width,outline-style,outline-color,outline-offset,box-decoration-break,box-shadow,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,background,background-color,background-image,background-position,background-position-x,background-position-y,background-size,background-repeat,background-origin,background-clip,background-attachment,scroll-behavior,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,letter-spacing,word-spacing,tab-size,align-content,align-items,align-self,text-align,text-align-last,text-indent,text-justify,justify-content,vertical-align,color,text-shadow,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,quotes,user-select,white-space,word-wrap,word-break,hyphens,list-style,list-style-position,list-style-type,list-style-image,table-layout,empty-cells,caption-side,border-spacing,border-collapse,nav-index,nav-left,nav-up,nav-right,nav-down,zoom,counter-reset,counter-increment,cursor,pointer-events\nij_css_space_after_colon = true\nij_css_space_before_opening_brace = true\nij_css_use_double_quotes = true\nij_css_value_alignment = do_not_align\n\n[*.feature]\nindent_size = 2\nij_gherkin_keep_indents_on_empty_lines = false\n\n[*.haml]\nindent_size = 2\nij_haml_keep_indents_on_empty_lines = false\n\n[*.less]\nindent_size = 2\nij_less_align_closing_brace_with_properties = false\nij_less_blank_lines_around_nested_selector = 1\nij_less_blank_lines_between_blocks = 1\nij_less_brace_placement = 0\nij_less_enforce_quotes_on_format = false\nij_less_hex_color_long_format = true\nij_less_hex_color_lower_case = false\nij_less_hex_color_short_format = false\nij_less_hex_color_upper_case = true\nij_less_keep_blank_lines_in_code = 2\nij_less_keep_indents_on_empty_lines = false\nij_less_keep_single_line_blocks = false\nij_less_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow\nij_less_space_after_colon = true\nij_less_space_before_opening_brace = true\nij_less_use_double_quotes = true\nij_less_value_alignment = 0\n\n[*.sass]\nindent_size = 2\nij_sass_align_closing_brace_with_properties = false\nij_sass_blank_lines_around_nested_selector = 1\nij_sass_blank_lines_between_blocks = 1\nij_sass_brace_placement = 0\nij_sass_enforce_quotes_on_format = false\nij_sass_hex_color_long_format = false\nij_sass_hex_color_lower_case = false\nij_sass_hex_color_short_format = false\nij_sass_hex_color_upper_case = false\nij_sass_keep_blank_lines_in_code = 2\nij_sass_keep_indents_on_empty_lines = false\nij_sass_keep_single_line_blocks = false\nij_sass_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow\nij_sass_space_after_colon = true\nij_sass_space_before_opening_brace = true\nij_sass_use_double_quotes = true\nij_sass_value_alignment = 0\n\n[*.scss]\nij_scss_align_closing_brace_with_properties = false\nij_scss_blank_lines_around_nested_selector = 1\nij_scss_blank_lines_between_blocks = 1\nij_scss_brace_placement = 0\nij_scss_enforce_quotes_on_format = false\nij_scss_hex_color_long_format = true\nij_scss_hex_color_lower_case = false\nij_scss_hex_color_short_format = false\nij_scss_hex_color_upper_case = true\nij_scss_keep_blank_lines_in_code = 1\nij_scss_keep_indents_on_empty_lines = false\nij_scss_keep_single_line_blocks = false\nij_scss_properties_order = position,display,visibility,float,clear,top,right,bottom,left,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,order,z-index,width,height,min-width,min-height,max-width,max-height,box-sizing,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,overflow,overflow-x,overflow-y,content,resize,opacity,outline,outline-width,outline-style,outline-color,outline-offset,box-decoration-break,box-shadow,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,background,background-color,background-image,background-position,background-position-x,background-position-y,background-size,background-repeat,background-origin,background-clip,background-attachment,scroll-behavior,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,letter-spacing,word-spacing,tab-size,align-content,align-items,align-self,text-align,text-align-last,text-indent,text-justify,justify-content,vertical-align,color,text-shadow,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,quotes,user-select,white-space,word-wrap,word-break,hyphens,list-style,list-style-position,list-style-type,list-style-image,table-layout,empty-cells,caption-side,border-spacing,border-collapse,nav-index,nav-left,nav-up,nav-right,nav-down,zoom,counter-reset,counter-increment,cursor,pointer-events\nij_scss_space_after_colon = true\nij_scss_space_before_opening_brace = true\nij_scss_use_double_quotes = true\nij_scss_value_alignment = 0\n\n[*.twig]\nij_twig_keep_indents_on_empty_lines = false\nij_twig_spaces_inside_comments_delimiters = true\nij_twig_spaces_inside_delimiters = true\nij_twig_spaces_inside_variable_delimiters = true\n\n[*.vue]\nij_continuation_indent_size = 4\nij_vue_indent_children_of_top_level = template\nij_vue_interpolation_new_line_after_start_delimiter = true\nij_vue_interpolation_new_line_before_end_delimiter = true\nij_vue_interpolation_wrap = off\nij_vue_keep_indents_on_empty_lines = false\nij_vue_spaces_within_interpolation_expressions = true\n\n[.editorconfig]\nij_editorconfig_align_group_field_declarations = false\nij_editorconfig_space_after_colon = false\nij_editorconfig_space_after_comma = true\nij_editorconfig_space_before_colon = false\nij_editorconfig_space_before_comma = false\nij_editorconfig_spaces_around_assignment_operators = true\n\n[{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.rng,*.tld,*.wsdl,*.xml,*.xsd,*.xsl,*.xslt,*.xul,phpunit.xml.dist}]\nij_xml_align_attributes = true\nij_xml_align_text = false\nij_xml_attribute_wrap = normal\nij_xml_block_comment_at_first_column = true\nij_xml_keep_blank_lines = 2\nij_xml_keep_indents_on_empty_lines = false\nij_xml_keep_line_breaks = true\nij_xml_keep_line_breaks_in_text = true\nij_xml_keep_whitespaces = false\nij_xml_keep_whitespaces_around_cdata = preserve\nij_xml_keep_whitespaces_inside_cdata = false\nij_xml_line_comment_at_first_column = true\nij_xml_space_after_tag_name = false\nij_xml_space_around_equals_in_attribute = false\nij_xml_space_inside_empty_tag = false\nij_xml_text_wrap = normal\n\n[{*.ats,*.ts}]\nij_continuation_indent_size = 4\nij_typescript_align_imports = false\nij_typescript_align_multiline_array_initializer_expression = false\nij_typescript_align_multiline_binary_operation = false\nij_typescript_align_multiline_chained_methods = false\nij_typescript_align_multiline_extends_list = false\nij_typescript_align_multiline_for = true\nij_typescript_align_multiline_parameters = true\nij_typescript_align_multiline_parameters_in_calls = false\nij_typescript_align_multiline_ternary_operation = false\nij_typescript_align_object_properties = 0\nij_typescript_align_union_types = false\nij_typescript_align_var_statements = 0\nij_typescript_array_initializer_new_line_after_left_brace = false\nij_typescript_array_initializer_right_brace_on_new_line = false\nij_typescript_array_initializer_wrap = off\nij_typescript_assignment_wrap = off\nij_typescript_binary_operation_sign_on_next_line = false\nij_typescript_binary_operation_wrap = off\nij_typescript_blacklist_imports = rxjs/Rx,node_modules/**,**/node_modules/**,@angular/material,@angular/material/typings/**\nij_typescript_blank_lines_after_imports = 1\nij_typescript_blank_lines_around_class = 1\nij_typescript_blank_lines_around_field = 0\nij_typescript_blank_lines_around_field_in_interface = 0\nij_typescript_blank_lines_around_function = 1\nij_typescript_blank_lines_around_method = 1\nij_typescript_blank_lines_around_method_in_interface = 1\nij_typescript_block_brace_style = end_of_line\nij_typescript_call_parameters_new_line_after_left_paren = false\nij_typescript_call_parameters_right_paren_on_new_line = false\nij_typescript_call_parameters_wrap = off\nij_typescript_catch_on_new_line = false\nij_typescript_chained_call_dot_on_new_line = true\nij_typescript_class_brace_style = end_of_line\nij_typescript_comma_on_new_line = false\nij_typescript_do_while_brace_force = never\nij_typescript_else_on_new_line = false\nij_typescript_enforce_trailing_comma = keep\nij_typescript_extends_keyword_wrap = off\nij_typescript_extends_list_wrap = off\nij_typescript_field_prefix = _\nij_typescript_file_name_style = relaxed\nij_typescript_finally_on_new_line = false\nij_typescript_for_brace_force = never\nij_typescript_for_statement_new_line_after_left_paren = false\nij_typescript_for_statement_right_paren_on_new_line = false\nij_typescript_for_statement_wrap = off\nij_typescript_force_quote_style = false\nij_typescript_force_semicolon_style = false\nij_typescript_function_expression_brace_style = end_of_line\nij_typescript_if_brace_force = never\nij_typescript_import_merge_members = global\nij_typescript_import_prefer_absolute_path = global\nij_typescript_import_sort_members = true\nij_typescript_import_sort_module_name = false\nij_typescript_import_use_node_resolution = true\nij_typescript_imports_wrap = on_every_item\nij_typescript_indent_case_from_switch = true\nij_typescript_indent_chained_calls = true\nij_typescript_indent_package_children = 0\nij_typescript_jsdoc_include_types = false\nij_typescript_jsx_attribute_value = braces\nij_typescript_keep_blank_lines_in_code = 2\nij_typescript_keep_first_column_comment = true\nij_typescript_keep_indents_on_empty_lines = false\nij_typescript_keep_line_breaks = true\nij_typescript_keep_simple_blocks_in_one_line = false\nij_typescript_keep_simple_methods_in_one_line = false\nij_typescript_line_comment_add_space = true\nij_typescript_line_comment_at_first_column = false\nij_typescript_method_brace_style = end_of_line\nij_typescript_method_call_chain_wrap = off\nij_typescript_method_parameters_new_line_after_left_paren = false\nij_typescript_method_parameters_right_paren_on_new_line = false\nij_typescript_method_parameters_wrap = off\nij_typescript_object_literal_wrap = on_every_item\nij_typescript_parentheses_expression_new_line_after_left_paren = false\nij_typescript_parentheses_expression_right_paren_on_new_line = false\nij_typescript_place_assignment_sign_on_next_line = false\nij_typescript_prefer_as_type_cast = false\nij_typescript_prefer_explicit_types_function_expression_returns = false\nij_typescript_prefer_explicit_types_function_returns = false\nij_typescript_prefer_explicit_types_vars_fields = false\nij_typescript_prefer_parameters_wrap = false\nij_typescript_reformat_c_style_comments = false\nij_typescript_space_after_colon = true\nij_typescript_space_after_comma = true\nij_typescript_space_after_dots_in_rest_parameter = false\nij_typescript_space_after_generator_mult = true\nij_typescript_space_after_property_colon = true\nij_typescript_space_after_quest = true\nij_typescript_space_after_type_colon = true\nij_typescript_space_after_unary_not = false\nij_typescript_space_before_async_arrow_lparen = true\nij_typescript_space_before_catch_keyword = true\nij_typescript_space_before_catch_left_brace = true\nij_typescript_space_before_catch_parentheses = true\nij_typescript_space_before_class_lbrace = true\nij_typescript_space_before_class_left_brace = true\nij_typescript_space_before_colon = true\nij_typescript_space_before_comma = false\nij_typescript_space_before_do_left_brace = true\nij_typescript_space_before_else_keyword = true\nij_typescript_space_before_else_left_brace = true\nij_typescript_space_before_finally_keyword = true\nij_typescript_space_before_finally_left_brace = true\nij_typescript_space_before_for_left_brace = true\nij_typescript_space_before_for_parentheses = true\nij_typescript_space_before_for_semicolon = false\nij_typescript_space_before_function_left_parenth = true\nij_typescript_space_before_generator_mult = false\nij_typescript_space_before_if_left_brace = true\nij_typescript_space_before_if_parentheses = true\nij_typescript_space_before_method_call_parentheses = false\nij_typescript_space_before_method_left_brace = true\nij_typescript_space_before_method_parentheses = false\nij_typescript_space_before_property_colon = false\nij_typescript_space_before_quest = true\nij_typescript_space_before_switch_left_brace = true\nij_typescript_space_before_switch_parentheses = true\nij_typescript_space_before_try_left_brace = true\nij_typescript_space_before_type_colon = false\nij_typescript_space_before_unary_not = false\nij_typescript_space_before_while_keyword = true\nij_typescript_space_before_while_left_brace = true\nij_typescript_space_before_while_parentheses = true\nij_typescript_spaces_around_additive_operators = true\nij_typescript_spaces_around_arrow_function_operator = true\nij_typescript_spaces_around_assignment_operators = true\nij_typescript_spaces_around_bitwise_operators = true\nij_typescript_spaces_around_equality_operators = true\nij_typescript_spaces_around_logical_operators = true\nij_typescript_spaces_around_multiplicative_operators = true\nij_typescript_spaces_around_relational_operators = true\nij_typescript_spaces_around_shift_operators = true\nij_typescript_spaces_around_unary_operator = false\nij_typescript_spaces_within_array_initializer_brackets = false\nij_typescript_spaces_within_brackets = false\nij_typescript_spaces_within_catch_parentheses = false\nij_typescript_spaces_within_for_parentheses = false\nij_typescript_spaces_within_if_parentheses = false\nij_typescript_spaces_within_imports = false\nij_typescript_spaces_within_interpolation_expressions = false\nij_typescript_spaces_within_method_call_parentheses = false\nij_typescript_spaces_within_method_parentheses = false\nij_typescript_spaces_within_object_literal_braces = false\nij_typescript_spaces_within_object_type_braces = true\nij_typescript_spaces_within_parentheses = false\nij_typescript_spaces_within_switch_parentheses = false\nij_typescript_spaces_within_type_assertion = false\nij_typescript_spaces_within_union_types = true\nij_typescript_spaces_within_while_parentheses = false\nij_typescript_special_else_if_treatment = true\nij_typescript_ternary_operation_signs_on_next_line = false\nij_typescript_ternary_operation_wrap = off\nij_typescript_union_types_wrap = on_every_item\nij_typescript_use_chained_calls_group_indents = false\nij_typescript_use_double_quotes = true\nij_typescript_use_explicit_js_extension = global\nij_typescript_use_path_mapping = always\nij_typescript_use_public_modifier = false\nij_typescript_use_semicolon_after_statement = true\nij_typescript_var_declaration_wrap = normal\nij_typescript_while_brace_force = never\nij_typescript_while_on_new_line = false\nij_typescript_wrap_comments = false\n\n[{*.bash,*.sh,*.zsh}]\nindent_size = 2\ntab_width = 2\nij_shell_binary_ops_start_line = false\nij_shell_keep_column_alignment_padding = false\nij_shell_minify_program = false\nij_shell_redirect_followed_by_space = false\nij_shell_switch_cases_indented = false\nij_shell_use_unix_line_separator = true\n\n[{*.cjs,*.js}]\nij_continuation_indent_size = 4\nij_javascript_align_imports = false\nij_javascript_align_multiline_array_initializer_expression = false\nij_javascript_align_multiline_binary_operation = false\nij_javascript_align_multiline_chained_methods = false\nij_javascript_align_multiline_extends_list = false\nij_javascript_align_multiline_for = true\nij_javascript_align_multiline_parameters = true\nij_javascript_align_multiline_parameters_in_calls = false\nij_javascript_align_multiline_ternary_operation = false\nij_javascript_align_object_properties = 0\nij_javascript_align_union_types = false\nij_javascript_align_var_statements = 0\nij_javascript_array_initializer_new_line_after_left_brace = true\nij_javascript_array_initializer_right_brace_on_new_line = false\nij_javascript_array_initializer_wrap = on_every_item\nij_javascript_assignment_wrap = off\nij_javascript_binary_operation_sign_on_next_line = false\nij_javascript_binary_operation_wrap = normal\nij_javascript_blacklist_imports = rxjs/Rx,node_modules/**,**/node_modules/**,@angular/material,@angular/material/typings/**\nij_javascript_blank_lines_after_imports = 1\nij_javascript_blank_lines_around_class = 1\nij_javascript_blank_lines_around_field = 0\nij_javascript_blank_lines_around_function = 1\nij_javascript_blank_lines_around_method = 1\nij_javascript_block_brace_style = end_of_line\nij_javascript_call_parameters_new_line_after_left_paren = false\nij_javascript_call_parameters_right_paren_on_new_line = false\nij_javascript_call_parameters_wrap = normal\nij_javascript_catch_on_new_line = false\nij_javascript_chained_call_dot_on_new_line = true\nij_javascript_class_brace_style = end_of_line\nij_javascript_comma_on_new_line = false\nij_javascript_do_while_brace_force = never\nij_javascript_else_on_new_line = false\nij_javascript_enforce_trailing_comma = whenmultiline\nij_javascript_extends_keyword_wrap = off\nij_javascript_extends_list_wrap = off\nij_javascript_field_prefix = _\nij_javascript_file_name_style = relaxed\nij_javascript_finally_on_new_line = false\nij_javascript_for_brace_force = never\nij_javascript_for_statement_new_line_after_left_paren = false\nij_javascript_for_statement_right_paren_on_new_line = false\nij_javascript_for_statement_wrap = off\nij_javascript_force_quote_style = true\nij_javascript_force_semicolon_style = true\nij_javascript_function_expression_brace_style = end_of_line\nij_javascript_if_brace_force = never\nij_javascript_import_merge_members = global\nij_javascript_import_prefer_absolute_path = global\nij_javascript_import_sort_members = true\nij_javascript_import_sort_module_name = false\nij_javascript_import_use_node_resolution = true\nij_javascript_imports_wrap = on_every_item\nij_javascript_indent_case_from_switch = true\nij_javascript_indent_chained_calls = true\nij_javascript_indent_package_children = 0\nij_javascript_jsx_attribute_value = braces\nij_javascript_keep_blank_lines_in_code = 1\nij_javascript_keep_first_column_comment = true\nij_javascript_keep_indents_on_empty_lines = false\nij_javascript_keep_line_breaks = true\nij_javascript_keep_simple_blocks_in_one_line = false\nij_javascript_keep_simple_methods_in_one_line = false\nij_javascript_line_comment_add_space = true\nij_javascript_line_comment_at_first_column = false\nij_javascript_method_brace_style = end_of_line\nij_javascript_method_call_chain_wrap = on_every_item\nij_javascript_method_parameters_new_line_after_left_paren = true\nij_javascript_method_parameters_right_paren_on_new_line = false\nij_javascript_method_parameters_wrap = normal\nij_javascript_object_literal_wrap = on_every_item\nij_javascript_parentheses_expression_new_line_after_left_paren = false\nij_javascript_parentheses_expression_right_paren_on_new_line = false\nij_javascript_place_assignment_sign_on_next_line = false\nij_javascript_prefer_as_type_cast = false\nij_javascript_prefer_explicit_types_function_expression_returns = false\nij_javascript_prefer_explicit_types_function_returns = false\nij_javascript_prefer_explicit_types_vars_fields = false\nij_javascript_prefer_parameters_wrap = false\nij_javascript_reformat_c_style_comments = false\nij_javascript_space_after_colon = true\nij_javascript_space_after_comma = true\nij_javascript_space_after_dots_in_rest_parameter = false\nij_javascript_space_after_generator_mult = true\nij_javascript_space_after_property_colon = true\nij_javascript_space_after_quest = true\nij_javascript_space_after_type_colon = true\nij_javascript_space_after_unary_not = false\nij_javascript_space_before_async_arrow_lparen = true\nij_javascript_space_before_catch_keyword = true\nij_javascript_space_before_catch_left_brace = true\nij_javascript_space_before_catch_parentheses = true\nij_javascript_space_before_class_lbrace = true\nij_javascript_space_before_class_left_brace = true\nij_javascript_space_before_colon = true\nij_javascript_space_before_comma = false\nij_javascript_space_before_do_left_brace = true\nij_javascript_space_before_else_keyword = true\nij_javascript_space_before_else_left_brace = true\nij_javascript_space_before_finally_keyword = true\nij_javascript_space_before_finally_left_brace = true\nij_javascript_space_before_for_left_brace = true\nij_javascript_space_before_for_parentheses = true\nij_javascript_space_before_for_semicolon = false\nij_javascript_space_before_function_left_parenth = false\nij_javascript_space_before_generator_mult = false\nij_javascript_space_before_if_left_brace = true\nij_javascript_space_before_if_parentheses = true\nij_javascript_space_before_method_call_parentheses = false\nij_javascript_space_before_method_left_brace = true\nij_javascript_space_before_method_parentheses = false\nij_javascript_space_before_property_colon = false\nij_javascript_space_before_quest = true\nij_javascript_space_before_switch_left_brace = true\nij_javascript_space_before_switch_parentheses = true\nij_javascript_space_before_try_left_brace = true\nij_javascript_space_before_type_colon = false\nij_javascript_space_before_unary_not = false\nij_javascript_space_before_while_keyword = true\nij_javascript_space_before_while_left_brace = true\nij_javascript_space_before_while_parentheses = true\nij_javascript_spaces_around_additive_operators = true\nij_javascript_spaces_around_arrow_function_operator = true\nij_javascript_spaces_around_assignment_operators = true\nij_javascript_spaces_around_bitwise_operators = true\nij_javascript_spaces_around_equality_operators = true\nij_javascript_spaces_around_logical_operators = true\nij_javascript_spaces_around_multiplicative_operators = true\nij_javascript_spaces_around_relational_operators = true\nij_javascript_spaces_around_shift_operators = true\nij_javascript_spaces_around_unary_operator = false\nij_javascript_spaces_within_array_initializer_brackets = false\nij_javascript_spaces_within_brackets = false\nij_javascript_spaces_within_catch_parentheses = false\nij_javascript_spaces_within_for_parentheses = false\nij_javascript_spaces_within_if_parentheses = false\nij_javascript_spaces_within_imports = false\nij_javascript_spaces_within_interpolation_expressions = false\nij_javascript_spaces_within_method_call_parentheses = false\nij_javascript_spaces_within_method_parentheses = false\nij_javascript_spaces_within_object_literal_braces = true\nij_javascript_spaces_within_object_type_braces = true\nij_javascript_spaces_within_parentheses = false\nij_javascript_spaces_within_switch_parentheses = false\nij_javascript_spaces_within_type_assertion = false\nij_javascript_spaces_within_union_types = true\nij_javascript_spaces_within_while_parentheses = false\nij_javascript_special_else_if_treatment = true\nij_javascript_ternary_operation_signs_on_next_line = false\nij_javascript_ternary_operation_wrap = on_every_item\nij_javascript_union_types_wrap = on_every_item\nij_javascript_use_chained_calls_group_indents = false\nij_javascript_use_double_quotes = false\nij_javascript_use_explicit_js_extension = global\nij_javascript_use_path_mapping = always\nij_javascript_use_public_modifier = false\nij_javascript_use_semicolon_after_statement = true\nij_javascript_var_declaration_wrap = normal\nij_javascript_while_brace_force = never\nij_javascript_while_on_new_line = false\nij_javascript_wrap_comments = false\n\n[{*.cjsx,*.coffee}]\nindent_size = 2\ntab_width = 2\nij_continuation_indent_size = 2\nij_coffeescript_align_function_body = false\nij_coffeescript_align_imports = false\nij_coffeescript_align_multiline_array_initializer_expression = true\nij_coffeescript_align_multiline_parameters = true\nij_coffeescript_align_multiline_parameters_in_calls = false\nij_coffeescript_align_object_properties = 0\nij_coffeescript_align_union_types = false\nij_coffeescript_align_var_statements = 0\nij_coffeescript_array_initializer_new_line_after_left_brace = false\nij_coffeescript_array_initializer_right_brace_on_new_line = false\nij_coffeescript_array_initializer_wrap = normal\nij_coffeescript_blacklist_imports = rxjs/Rx,node_modules/**,**/node_modules/**,@angular/material,@angular/material/typings/**\nij_coffeescript_blank_lines_around_function = 1\nij_coffeescript_call_parameters_new_line_after_left_paren = false\nij_coffeescript_call_parameters_right_paren_on_new_line = false\nij_coffeescript_call_parameters_wrap = normal\nij_coffeescript_chained_call_dot_on_new_line = true\nij_coffeescript_comma_on_new_line = false\nij_coffeescript_enforce_trailing_comma = keep\nij_coffeescript_field_prefix = _\nij_coffeescript_file_name_style = relaxed\nij_coffeescript_force_quote_style = false\nij_coffeescript_force_semicolon_style = false\nij_coffeescript_function_expression_brace_style = end_of_line\nij_coffeescript_import_merge_members = global\nij_coffeescript_import_prefer_absolute_path = global\nij_coffeescript_import_sort_members = true\nij_coffeescript_import_sort_module_name = false\nij_coffeescript_import_use_node_resolution = true\nij_coffeescript_imports_wrap = on_every_item\nij_coffeescript_indent_chained_calls = true\nij_coffeescript_indent_package_children = 0\nij_coffeescript_jsx_attribute_value = braces\nij_coffeescript_keep_blank_lines_in_code = 2\nij_coffeescript_keep_first_column_comment = true\nij_coffeescript_keep_indents_on_empty_lines = false\nij_coffeescript_keep_line_breaks = true\nij_coffeescript_keep_simple_methods_in_one_line = false\nij_coffeescript_method_parameters_new_line_after_left_paren = false\nij_coffeescript_method_parameters_right_paren_on_new_line = false\nij_coffeescript_method_parameters_wrap = off\nij_coffeescript_object_literal_wrap = on_every_item\nij_coffeescript_prefer_as_type_cast = false\nij_coffeescript_prefer_explicit_types_function_expression_returns = false\nij_coffeescript_prefer_explicit_types_function_returns = false\nij_coffeescript_prefer_explicit_types_vars_fields = false\nij_coffeescript_reformat_c_style_comments = false\nij_coffeescript_space_after_comma = true\nij_coffeescript_space_after_dots_in_rest_parameter = false\nij_coffeescript_space_after_generator_mult = true\nij_coffeescript_space_after_property_colon = true\nij_coffeescript_space_after_type_colon = true\nij_coffeescript_space_after_unary_not = false\nij_coffeescript_space_before_async_arrow_lparen = true\nij_coffeescript_space_before_class_lbrace = true\nij_coffeescript_space_before_comma = false\nij_coffeescript_space_before_function_left_parenth = true\nij_coffeescript_space_before_generator_mult = false\nij_coffeescript_space_before_property_colon = false\nij_coffeescript_space_before_type_colon = false\nij_coffeescript_space_before_unary_not = false\nij_coffeescript_spaces_around_additive_operators = true\nij_coffeescript_spaces_around_arrow_function_operator = true\nij_coffeescript_spaces_around_assignment_operators = true\nij_coffeescript_spaces_around_bitwise_operators = true\nij_coffeescript_spaces_around_equality_operators = true\nij_coffeescript_spaces_around_logical_operators = true\nij_coffeescript_spaces_around_multiplicative_operators = true\nij_coffeescript_spaces_around_relational_operators = true\nij_coffeescript_spaces_around_shift_operators = true\nij_coffeescript_spaces_around_unary_operator = false\nij_coffeescript_spaces_within_array_initializer_braces = false\nij_coffeescript_spaces_within_array_initializer_brackets = false\nij_coffeescript_spaces_within_imports = false\nij_coffeescript_spaces_within_index_brackets = false\nij_coffeescript_spaces_within_interpolation_expressions = false\nij_coffeescript_spaces_within_method_call_parentheses = false\nij_coffeescript_spaces_within_method_parentheses = false\nij_coffeescript_spaces_within_object_braces = false\nij_coffeescript_spaces_within_object_literal_braces = false\nij_coffeescript_spaces_within_object_type_braces = true\nij_coffeescript_spaces_within_range_brackets = false\nij_coffeescript_spaces_within_type_assertion = false\nij_coffeescript_spaces_within_union_types = true\nij_coffeescript_union_types_wrap = on_every_item\nij_coffeescript_use_chained_calls_group_indents = false\nij_coffeescript_use_double_quotes = true\nij_coffeescript_use_explicit_js_extension = global\nij_coffeescript_use_path_mapping = always\nij_coffeescript_use_public_modifier = false\nij_coffeescript_use_semicolon_after_statement = false\nij_coffeescript_var_declaration_wrap = normal\n\n[{*.ctp,*.hphp,*.inc,*.module,*.php,*.php4,*.php5,*.phtml,yii_te}]\nij_continuation_indent_size = 4\nij_php_align_assignments = false\nij_php_align_class_constants = false\nij_php_align_group_field_declarations = false\nij_php_align_inline_comments = false\nij_php_align_key_value_pairs = false\nij_php_align_match_arm_bodies = false\nij_php_align_multiline_array_initializer_expression = false\nij_php_align_multiline_binary_operation = false\nij_php_align_multiline_chained_methods = false\nij_php_align_multiline_extends_list = true\nij_php_align_multiline_for = true\nij_php_align_multiline_parameters = false\nij_php_align_multiline_parameters_in_calls = true\nij_php_align_multiline_ternary_operation = false\nij_php_align_named_arguments = false\nij_php_align_phpdoc_comments = false\nij_php_align_phpdoc_param_names = false\nij_php_anonymous_brace_style = end_of_line\nij_php_api_weight = 28\nij_php_array_initializer_new_line_after_left_brace = true\nij_php_array_initializer_right_brace_on_new_line = true\nij_php_array_initializer_wrap = on_every_item\nij_php_assignment_wrap = off\nij_php_attributes_wrap = off\nij_php_author_weight = 28\nij_php_binary_operation_sign_on_next_line = false\nij_php_binary_operation_wrap = off\nij_php_blank_lines_after_class_header = 0\nij_php_blank_lines_after_function = 1\nij_php_blank_lines_after_imports = 1\nij_php_blank_lines_after_opening_tag = 1\nij_php_blank_lines_after_package = 1\nij_php_blank_lines_around_class = 1\nij_php_blank_lines_around_constants = 0\nij_php_blank_lines_around_field = 0\nij_php_blank_lines_around_method = 1\nij_php_blank_lines_before_class_end = 0\nij_php_blank_lines_before_imports = 1\nij_php_blank_lines_before_method_body = 0\nij_php_blank_lines_before_package = 1\nij_php_blank_lines_before_return_statement = 0\nij_php_blank_lines_between_imports = 1\nij_php_block_brace_style = end_of_line\nij_php_call_parameters_new_line_after_left_paren = true\nij_php_call_parameters_right_paren_on_new_line = true\nij_php_call_parameters_wrap = on_every_item\nij_php_catch_on_new_line = false\nij_php_category_weight = 28\nij_php_class_brace_style = next_line\nij_php_comma_after_last_array_element = true\nij_php_concat_spaces = true\nij_php_copyright_weight = 28\nij_php_deprecated_weight = 28\nij_php_do_while_brace_force = always\nij_php_else_if_style = combine\nij_php_else_on_new_line = false\nij_php_example_weight = 28\nij_php_extends_keyword_wrap = off\nij_php_extends_list_wrap = on_every_item\nij_php_fields_default_visibility = private\nij_php_filesource_weight = 28\nij_php_finally_on_new_line = false\nij_php_for_brace_force = always\nij_php_for_statement_new_line_after_left_paren = true\nij_php_for_statement_right_paren_on_new_line = true\nij_php_for_statement_wrap = off\nij_php_force_short_declaration_array_style = false\nij_php_getters_setters_naming_style = camel_case\nij_php_getters_setters_order_style = getters_first\nij_php_global_weight = 28\nij_php_group_use_wrap = on_every_item\nij_php_if_brace_force = always\nij_php_if_lparen_on_next_line = false\nij_php_if_rparen_on_next_line = false\nij_php_ignore_weight = 28\nij_php_import_sorting = alphabetic\nij_php_indent_break_from_case = true\nij_php_indent_case_from_switch = true\nij_php_indent_code_in_php_tags = false\nij_php_internal_weight = 28\nij_php_keep_blank_lines_after_lbrace = 0\nij_php_keep_blank_lines_before_right_brace = 0\nij_php_keep_blank_lines_in_code = 2\nij_php_keep_blank_lines_in_declarations = 2\nij_php_keep_control_statement_in_one_line = true\nij_php_keep_first_column_comment = true\nij_php_keep_indents_on_empty_lines = false\nij_php_keep_line_breaks = true\nij_php_keep_rparen_and_lbrace_on_one_line = true\nij_php_keep_simple_classes_in_one_line = false\nij_php_keep_simple_methods_in_one_line = false\nij_php_lambda_brace_style = end_of_line\nij_php_license_weight = 28\nij_php_line_comment_add_space = false\nij_php_line_comment_at_first_column = true\nij_php_link_weight = 28\nij_php_lower_case_boolean_const = true\nij_php_lower_case_keywords = true\nij_php_lower_case_null_const = true\nij_php_method_brace_style = next_line\nij_php_method_call_chain_wrap = off\nij_php_method_parameters_new_line_after_left_paren = true\nij_php_method_parameters_right_paren_on_new_line = true\nij_php_method_parameters_wrap = on_every_item\nij_php_method_weight = 28\nij_php_modifier_list_wrap = false\nij_php_multiline_chained_calls_semicolon_on_new_line = false\nij_php_namespace_brace_style = 1\nij_php_new_line_after_php_opening_tag = true\nij_php_null_type_position = in_the_end\nij_php_package_weight = 28\nij_php_param_weight = 0\nij_php_parameters_attributes_wrap = off\nij_php_parentheses_expression_new_line_after_left_paren = false\nij_php_parentheses_expression_right_paren_on_new_line = false\nij_php_phpdoc_blank_line_before_tags = true\nij_php_phpdoc_blank_lines_around_parameters = true\nij_php_phpdoc_keep_blank_lines = true\nij_php_phpdoc_param_spaces_between_name_and_description = 1\nij_php_phpdoc_param_spaces_between_tag_and_type = 1\nij_php_phpdoc_param_spaces_between_type_and_name = 1\nij_php_phpdoc_use_fqcn = true\nij_php_phpdoc_wrap_long_lines = false\nij_php_place_assignment_sign_on_next_line = false\nij_php_place_parens_for_constructor = 0\nij_php_property_read_weight = 28\nij_php_property_weight = 28\nij_php_property_write_weight = 28\nij_php_return_type_on_new_line = false\nij_php_return_weight = 1\nij_php_see_weight = 28\nij_php_since_weight = 28\nij_php_sort_phpdoc_elements = true\nij_php_space_after_colon = true\nij_php_space_after_colon_in_enum_backed_type = true\nij_php_space_after_colon_in_named_argument = true\nij_php_space_after_colon_in_return_type = true\nij_php_space_after_comma = true\nij_php_space_after_for_semicolon = true\nij_php_space_after_quest = true\nij_php_space_after_type_cast = false\nij_php_space_after_unary_not = false\nij_php_space_before_array_initializer_left_brace = false\nij_php_space_before_catch_keyword = true\nij_php_space_before_catch_left_brace = true\nij_php_space_before_catch_parentheses = true\nij_php_space_before_class_left_brace = true\nij_php_space_before_closure_left_parenthesis = true\nij_php_space_before_colon = true\nij_php_space_before_colon_in_enum_backed_type = false\nij_php_space_before_colon_in_named_argument = false\nij_php_space_before_colon_in_return_type = false\nij_php_space_before_comma = false\nij_php_space_before_do_left_brace = true\nij_php_space_before_else_keyword = true\nij_php_space_before_else_left_brace = true\nij_php_space_before_finally_keyword = true\nij_php_space_before_finally_left_brace = true\nij_php_space_before_for_left_brace = true\nij_php_space_before_for_parentheses = true\nij_php_space_before_for_semicolon = false\nij_php_space_before_if_left_brace = true\nij_php_space_before_if_parentheses = true\nij_php_space_before_method_call_parentheses = false\nij_php_space_before_method_left_brace = true\nij_php_space_before_method_parentheses = false\nij_php_space_before_quest = true\nij_php_space_before_short_closure_left_parenthesis = false\nij_php_space_before_switch_left_brace = true\nij_php_space_before_switch_parentheses = true\nij_php_space_before_try_left_brace = true\nij_php_space_before_unary_not = false\nij_php_space_before_while_keyword = true\nij_php_space_before_while_left_brace = true\nij_php_space_before_while_parentheses = true\nij_php_space_between_ternary_quest_and_colon = false\nij_php_spaces_around_additive_operators = true\nij_php_spaces_around_arrow = false\nij_php_spaces_around_assignment_in_declare = false\nij_php_spaces_around_assignment_operators = true\nij_php_spaces_around_bitwise_operators = true\nij_php_spaces_around_equality_operators = true\nij_php_spaces_around_logical_operators = true\nij_php_spaces_around_multiplicative_operators = true\nij_php_spaces_around_null_coalesce_operator = true\nij_php_spaces_around_pipe_in_union_type = false\nij_php_spaces_around_relational_operators = true\nij_php_spaces_around_shift_operators = true\nij_php_spaces_around_unary_operator = false\nij_php_spaces_around_var_within_brackets = false\nij_php_spaces_within_array_initializer_braces = false\nij_php_spaces_within_brackets = false\nij_php_spaces_within_catch_parentheses = false\nij_php_spaces_within_for_parentheses = false\nij_php_spaces_within_if_parentheses = false\nij_php_spaces_within_method_call_parentheses = false\nij_php_spaces_within_method_parentheses = false\nij_php_spaces_within_parentheses = false\nij_php_spaces_within_short_echo_tags = true\nij_php_spaces_within_switch_parentheses = false\nij_php_spaces_within_while_parentheses = false\nij_php_special_else_if_treatment = false\nij_php_subpackage_weight = 28\nij_php_ternary_operation_signs_on_next_line = false\nij_php_ternary_operation_wrap = off\nij_php_throws_weight = 2\nij_php_todo_weight = 28\nij_php_unknown_tag_weight = 28\nij_php_upper_case_boolean_const = false\nij_php_upper_case_null_const = false\nij_php_uses_weight = 28\nij_php_var_weight = 28\nij_php_variable_naming_style = mixed\nij_php_version_weight = 28\nij_php_while_brace_force = always\nij_php_while_on_new_line = false\n\n[{*.har,*.jsb2,*.jsb3,*.json,.babelrc,.eslintrc,.stylelintrc,bowerrc,composer.lock,jest.config}]\nindent_size = 2\nij_json_keep_blank_lines_in_code = 0\nij_json_keep_indents_on_empty_lines = false\nij_json_keep_line_breaks = true\nij_json_space_after_colon = true\nij_json_space_after_comma = true\nij_json_space_before_colon = true\nij_json_space_before_comma = false\nij_json_spaces_within_braces = false\nij_json_spaces_within_brackets = false\nij_json_wrap_long_lines = false\n\n[{*.htm,*.html,*.ng,*.sht,*.shtm,*.shtml}]\nij_html_add_new_line_before_tags = body,div,p,form,h1,h2,h3\nij_html_align_attributes = true\nij_html_align_text = false\nij_html_attribute_wrap = normal\nij_html_block_comment_at_first_column = true\nij_html_do_not_align_children_of_min_lines = 0\nij_html_do_not_break_if_inline_tags = title,h1,h2,h3,h4,h5,h6,p\nij_html_do_not_indent_children_of_tags = html,body,thead,tbody,tfoot\nij_html_enforce_quotes = false\nij_html_inline_tags = a,abbr,acronym,b,basefont,bdo,big,br,cite,cite,code,dfn,em,font,i,img,input,kbd,label,q,s,samp,select,small,span,strike,strong,sub,sup,textarea,tt,u,var\nij_html_keep_blank_lines = 2\nij_html_keep_indents_on_empty_lines = false\nij_html_keep_line_breaks = true\nij_html_keep_line_breaks_in_text = true\nij_html_keep_whitespaces = false\nij_html_keep_whitespaces_inside = span,pre,textarea\nij_html_line_comment_at_first_column = true\nij_html_new_line_after_last_attribute = never\nij_html_new_line_before_first_attribute = never\nij_html_quote_style = double\nij_html_remove_new_line_before_tags = br\nij_html_space_after_tag_name = false\nij_html_space_around_equality_in_attribute = false\nij_html_space_inside_empty_tag = false\nij_html_text_wrap = normal\n\n[{*.markdown,*.md}]\nij_markdown_force_one_space_after_blockquote_symbol = true\nij_markdown_force_one_space_after_header_symbol = true\nij_markdown_force_one_space_after_list_bullet = true\nij_markdown_force_one_space_between_words = true\nij_markdown_keep_indents_on_empty_lines = false\nij_markdown_max_lines_around_block_elements = 1\nij_markdown_max_lines_around_header = 1\nij_markdown_max_lines_between_paragraphs = 1\nij_markdown_min_lines_around_block_elements = 1\nij_markdown_min_lines_around_header = 1\nij_markdown_min_lines_between_paragraphs = 1\n\n[{*.yaml,*.yml}]\nindent_size = 2\nij_yaml_align_values_properties = do_not_align\nij_yaml_autoinsert_sequence_marker = true\nij_yaml_block_mapping_on_new_line = false\nij_yaml_indent_sequence_value = true\nij_yaml_keep_indents_on_empty_lines = false\nij_yaml_keep_line_breaks = true\nij_yaml_sequence_on_new_line = false\nij_yaml_space_before_colon = false\nij_yaml_spaces_within_braces = true\nij_yaml_spaces_within_brackets = true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/BUG.yml",
    "content": "name: Bug Report\ndescription: File a bug report\ntitle: \"[Bug]: \"\nlabels: [ bug, triage ]\nassignees:\n  - MacroMan\nbody:\n  - type: textarea\n    id: what-happened\n    attributes:\n      label: What happened?\n      description: Please provide all information required to reproduce the bug\n    validations:\n      required: true\n  - type: dropdown\n    id: version\n    attributes:\n      label: Version\n      description: What version of Spaces-API is this occuring on? Versions below 3 will not be fixed. Please upgrade.\n      options:\n        - 3.5.0\n        - 3.4.0\n        - 3.3.0\n        - 3.2.0\n        - 3.1.0\n        - 3.0.0\n    validations:\n      required: true\n  - type: input\n    id: php-version\n    attributes:\n      label: What version of PHP are you using?\n  - type: textarea\n    id: logs\n    attributes:\n      label: Relevant log output\n      description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.\n      render: shell\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/FEATURE.yml",
    "content": "name: Feature request\ndescription: Request new functionality\ntitle: \"[Feature]: \"\nlabels: [ new ]\nassignees:\n  - MacroMan\nbody:\n  - type: textarea\n    id: idea\n    attributes:\n      label: Description\n      description: Please provide as much detail as you can about what you'd like to see. If you can, a pull request is the fastest way to add new features.\n    validations:\n      required: true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\n"
  },
  {
    "path": ".gitignore",
    "content": ".DS_Store\nvendor/\n.idea/\n.env\n.phpunit.result.cache\n"
  },
  {
    "path": ".phpdoc-md",
    "content": "<?php\n\nreturn (object)[\n    'rootNamespace' => 'SpacesAPI',\n    'destDirectory' => 'docs',\n    'format' => 'github',\n    'classes' => [\n        '\\SpacesAPI\\Spaces',\n        '\\SpacesAPI\\Space',\n        '\\SpacesAPI\\File',\n    ],\n];\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2020 Devang Srivastava\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# This library is deprecated\nWe recommend using the [official SDK](https://github.com/DigitalOceanPHP/Client) or the [Laravel package](https://github.com/GrahamCampbell/Laravel-DigitalOcean)\n\nAll issues will be closed and new PRs will not be accepted\n\n## Installation\nInstall via composer\n```\ncomposer require sociallydev/spaces-api\n```\n\n## Quick start\n\nObtain API keys from the [Digital Ocean Applications & API dashboard](https://cloud.digitalocean.com/account/api/tokens)\n\n```php\nuse SpacesAPI\\Spaces;\n\n// Connect to a space\n$spaces = new Spaces('api-key', 'api-secret');\n$space = $spaces->space('space-name');\n\n// Download a file\n$file = $space->file('remote-file-1.txt');\n$file->download('local/file/path/file.txt');\n\n// Upload text to a file\n$file2 = $space->uploadText(\"Lorem ipsum\",\"remote-file-2.txt\");\n\n// Get a signed public link, valid for 2 hours\n$file2url = $file2->getSignedURL(\"2 hours\");\n\n// Make a copy\n$file3 = $file2->copy('remote-file-3.txt');\n\n// Move or rename a file\n$file2->move('new-filename.txt')\n\n// Make a file public and get the URL\n$file3->makePublic();\n$file3url = $file3->getURL();\n```\n\nSee more examples in [docs/Examples.md](docs/Examples.md)\n\n## Upgrading?\nVersion 3 has many changes over version 2, so we have written a [migration guide](docs/Upgrade2-3.md)\n\n## API reference\n* [\\SpacesAPI\\Spaces](docs/Spaces.md)\n* [\\SpacesAPI\\Space](docs/Space.md)\n* [\\SpacesAPI\\File](docs/File.md)\n"
  },
  {
    "path": "SpacesAPI/Exceptions/AuthenticationException.php",
    "content": "<?php\n\nnamespace SpacesAPI\\Exceptions;\n\nuse Exception;\n\nclass AuthenticationException extends Exception {}\n"
  },
  {
    "path": "SpacesAPI/Exceptions/FileDoesntExistException.php",
    "content": "<?php\n\nnamespace SpacesAPI\\Exceptions;\n\nuse Exception;\n\nclass FileDoesntExistException extends Exception\n{\n}\n"
  },
  {
    "path": "SpacesAPI/Exceptions/SpaceDoesntExistException.php",
    "content": "<?php\n\nnamespace SpacesAPI\\Exceptions;\n\nuse Exception;\n\nclass SpaceDoesntExistException extends Exception\n{\n}\n"
  },
  {
    "path": "SpacesAPI/Exceptions/SpaceExistsException.php",
    "content": "<?php\n\nnamespace SpacesAPI\\Exceptions;\n\nuse Exception;\n\nclass SpaceExistsException extends Exception {}\n"
  },
  {
    "path": "SpacesAPI/Exceptions/SpacesException.php",
    "content": "<?php\n\nnamespace SpacesAPI\\Exceptions;\n\nuse Exception;\n\nclass SpacesException extends Exception\n{\n}\n"
  },
  {
    "path": "SpacesAPI/File.php",
    "content": "<?php\n\nnamespace SpacesAPI;\n\nuse SpacesAPI\\Exceptions\\FileDoesntExistException;\n\nuse function PHPUnit\\Framework\\isNull;\n\n/**\n * Represents a single file\n *\n * You wouldn't normally instantiate this class directly,\n * Rather obtain an instance from `\\SpacesAPI\\Space::list()`, `\\SpacesAPI\\Spaces::file()`, `\\SpacesAPI\\Spaces::uploadText()` or `\\SpacesAPI\\Spaces::uploadFile()`\n *\n * @property string $filename\n * @property string $expiration\n * @property string $e_tag\n * @property int $last_modified\n * @property string $content_type\n * @property int $content_length\n */\nclass File\n{\n    use StringFunctions;\n\n    /**\n     * @var \\SpacesAPI\\Space\n     */\n    private $space;\n\n    /**\n     * The name of the current space\n     *\n     * @var string\n     */\n    private $space_name;\n\n    /**\n     * @var \\Aws\\S3\\S3Client\n     */\n    private $s3;\n\n    private $_expiration;\n    private $_e_tag;\n    private $_filename;\n    private $_last_modified;\n    private $_content_type;\n    private $_content_length;\n\n    /**\n     * @param \\SpacesAPI\\Space $space An instance of `\\SpacesAPI\\Space`\n     * @param string $filename The filename of a file\n     * @param array $info Any information already known about the file (eg content_length, content_type, etc). Default `[]`\n     * @param bool $validate Check that the file exists\n     *\n     * @throws \\SpacesAPI\\Exceptions\\FileDoesntExistException If validation is `true` and the file doesn't exist\n     */\n    public function __construct(Space $space, string $filename, array $info = [], bool $validate = true)\n    {\n        $this->space = $space;\n        $this->space_name = $space->getName();\n        $this->s3 = $space->getS3Client();\n        $this->_filename = $filename;\n\n        if ($validate && !$this->s3->doesObjectExist($this->space_name, $filename)) {\n            throw new FileDoesntExistException(\"File $filename doesn't exist\");\n        }\n\n        if (count($info) > 0) {\n            $this->setFileInfo($info);\n        }\n    }\n\n    /**\n     * Magic getter to make the properties read-only\n     *\n     * @param string $name\n     *\n     * @return null\n     */\n    public function __get(string $name)\n    {\n        if (!property_exists($this, \"_$name\")) {\n            trigger_error(\"Undefined property: SpacesAPI\\File::$name\", E_USER_NOTICE);\n            return null;\n        }\n\n        if (!$this->{\"_$name\"}) {\n            $this->fetchFileInfo();\n        }\n\n        return $this->{\"_$name\"};\n    }\n\n    /**\n     * @param array $info\n     */\n    private function setFileInfo(array $info): void\n    {\n        foreach ($info as $_property => $value) {\n            $property = \"_\" . $this->pascalCaseToCamelCase($_property);\n\n            if ($property == 'size') {\n                $property = 'content_length';\n            }\n\n            if (property_exists($this, $property)) {\n                $this->$property = $value;\n            }\n        }\n    }\n\n    /**\n     *\n     */\n    private function fetchFileInfo(): void\n    {\n        $this->setFileInfo(\n            Result::parse(\n                $this->s3->headObject([\n                                          \"Bucket\" => $this->space_name,\n                                          \"Key\" => $this->_filename,\n                                      ])\n            )\n        );\n    }\n\n    /**\n     * Is this file publicly accessible\n     *\n     * @return bool\n     */\n    public function isPublic(): bool\n    {\n        $acl = Result::parse(\n            $this->s3->getObjectAcl([\n                                        \"Bucket\" => $this->space_name,\n                                        \"Key\" => $this->_filename,\n                                    ])\n        );\n\n        return (\n            isset($acl['Grants'][0]['Grantee']['URI']) &&\n            $acl['Grants'][0]['Grantee']['URI'] == \"http://acs.amazonaws.com/groups/global/AllUsers\" &&\n            $acl['Grants'][0]['Permission'] == \"READ\"\n        );\n    }\n\n    /**\n     * Make a file public or privately accessible\n     *\n     * @param bool $public\n     */\n    private function updatePrivacy(bool $public): void\n    {\n        $this->s3->putObjectAcl([\n                                    \"Bucket\" => $this->space_name,\n                                    \"Key\" => $this->_filename,\n                                    \"ACL\" => ($public) ? \"public-read\" : \"private\",\n                                ]);\n    }\n\n    /**\n     * Make file publicly accessible\n     */\n    public function makePublic(): void\n    {\n        $this->updatePrivacy(true);\n    }\n\n    /**\n     * Make file non-publicly accessible\n     */\n    public function makePrivate(): void\n    {\n        $this->updatePrivacy(false);\n    }\n\n    /**\n     * Get the file contents as a string\n     *\n     * @return string\n     */\n    public function getContents(): string\n    {\n        return $this->s3->getObject([\n                                        \"Bucket\" => $this->space_name,\n                                        \"Key\" => $this->_filename,\n                                    ])[\"Body\"]->getContents();\n    }\n\n    /**\n     * Download the file to a local location\n     *\n     * @param string $saveAs\n     *\n     * @return void\n     */\n    public function download(string $saveAs): void\n    {\n        $this->s3->getObject([\n                                 \"Bucket\" => $this->space_name,\n                                 \"Key\" => $this->_filename,\n                                 \"SaveAs\" => $saveAs,\n                             ]);\n    }\n\n    /**\n     * Copy the file on the space\n     *\n     * @param string $newFilename\n     * @param false $public\n     *\n     * @return \\SpacesAPI\\File\n     */\n    public function copy(string $newFilename): File\n    {\n        $this->s3->copy(\n            $this->space_name,\n            $this->_filename,\n            $this->space_name,\n            $newFilename,\n            ($this->isPublic()) ? 'public-read' : 'private'\n        );\n\n        return new self($this->space, $newFilename);\n    }\n\n    /**\n     * @param string $newFilename\n     *\n     * @return \\SpacesAPI\\File\n     */\n    public function move(string $newFilename): File\n    {\n        $this->copy($newFilename);\n        $this->delete();\n        $this->_filename = $newFilename;\n        $this->fetchFileInfo();\n\n        return $this;\n    }\n\n    /**\n     * Get the public URL\n     * This URL will not work if the file is private\n     *\n     * @return string\n     * @see getSignedURL\n     *\n     */\n    public function getURL(): string\n    {\n        return $this->s3->getObjectUrl($this->space_name, $this->_filename);\n    }\n\n    /**\n     * Get a signed URL, which will work for private files\n     *\n     * @param string|\\DateTime|int $validFor Can be any string recognised by strtotime(), an instance of DateTime or a unix timestamp\n     *\n     * @return string\n     */\n    public function getSignedURL($validFor = \"15 minutes\"): string\n    {\n        return (string)$this->s3->createPresignedRequest(\n            $this->s3->getCommand(\"GetObject\", [\n                \"Bucket\" => $this->space_name,\n                \"Key\" => $this->_filename,\n            ]),\n            $validFor\n        )->getUri();\n    }\n\n    /**\n     * Permanently delete this file\n     */\n    public function delete(): void\n    {\n        $this->s3->deleteObject([\n                                    \"Bucket\" => $this->space_name,\n                                    \"Key\" => $this->_filename,\n                                ]);\n    }\n}\n"
  },
  {
    "path": "SpacesAPI/Result.php",
    "content": "<?php\n\nnamespace SpacesAPI;\n\nuse Aws\\Api\\DateTimeResult;\n\n/**\n * AWS Results parser\n */\nclass Result\n{\n    /**\n     * Convert AWS result object into plain, multidimensional array\n     *\n     * @param $data\n     *\n     * @return array|mixed\n     */\n    public static function parse($data) {\n        if (gettype($data) == \"object\" && get_class($data) == \\Aws\\Result::class) {\n            $data = $data->toArray();\n        }\n\n        foreach ($data as $key => $value) {\n            if (is_array($value)) {\n                $data[$key] = self::parse($value);\n                continue;\n            }\n\n            if (gettype($value) == \"object\" && get_class($value) == DateTimeResult::class) {\n                $data[$key] = strtotime($value);\n            }\n        }\n\n        return $data;\n    }\n}\n"
  },
  {
    "path": "SpacesAPI/Space.php",
    "content": "<?php\n\nnamespace SpacesAPI;\n\nuse Aws\\S3\\Exception\\S3Exception;\nuse Aws\\S3\\S3Client;\nuse SpacesAPI\\Exceptions\\SpaceDoesntExistException;\n\n/**\n * Represents a space once connected/created\n *\n * You wouldn't normally instantiate this class directly,\n * Rather obtain an instance from `\\SpacesAPI\\Spaces::space()` or `\\SpacesAPI\\Spaces::create()`\n */\nclass Space\n{\n    /**\n     * AWS S3 client\n     *\n     * @var \\Aws\\S3\\S3Client\n     */\n    private $s3;\n\n    /**\n     * The name of the current space\n     *\n     * @var string\n     */\n    private $name;\n\n    /**\n     * Load a space\n     *\n     * You wouldn't normally call this directly,\n     * rather obtain an instance from `\\SpacesAPI\\Spaces::space()` or `\\SpacesAPI\\Spaces::create()`\n     *\n     * @param \\Aws\\S3\\S3Client $s3 An authenticated S3Client instance\n     * @param string $name Space name\n     * @param bool $validate Check that the space exists\n     *\n     * @throws \\SpacesAPI\\Exceptions\\SpaceDoesntExistException If validation is `true` and the space doesn't exist\n     */\n    public function __construct(S3Client $s3, string $name, bool $validate = true)\n    {\n        $this->s3 = $s3;\n        $this->name = $name;\n\n        if ($validate && !$this->s3->doesBucketExist($name)) {\n            throw new SpaceDoesntExistException(\"Space '$this->name' does not exist\");\n        }\n    }\n\n    /**\n     * Get the current AWS S3 client instance\n     *\n     * For internal library use\n     *\n     * @return \\Aws\\S3\\S3Client\n     */\n    public function getS3Client(): S3Client\n    {\n        return $this->s3;\n    }\n\n    /**\n     * Get the name of this space\n     *\n     * @return string\n     */\n    public function getName(): string\n    {\n        return $this->name;\n    }\n\n    /**\n     * Update space privacy\n     *\n     * @param bool $public\n     */\n    private function updatePrivacy(bool $public): void\n    {\n        $this->s3->putBucketAcl([\n                                    \"Bucket\" => $this->name,\n                                    \"ACL\" => ($public) ? \"public-read\" : \"private\",\n                                ]);\n    }\n\n    /**\n     * Enable file listing\n     */\n    public function makePublic(): void\n    {\n        $this->updatePrivacy(true);\n    }\n\n    /**\n     * Disable file listing\n     */\n    public function makePrivate(): void\n    {\n        $this->updatePrivacy(false);\n    }\n\n    /**\n     * Is file listing enabled?\n     *\n     * @return bool\n     */\n    public function isPublic(): bool\n    {\n        $acl = Result::parse($this->s3->getBucketAcl([\"Bucket\" => $this->name]));\n\n        return (\n            isset($acl['Grants'][0]['Grantee']['URI']) &&\n            $acl['Grants'][0]['Grantee']['URI'] == \"http://acs.amazonaws.com/groups/global/AllUsers\" &&\n            $acl['Grants'][0]['Permission'] == \"READ\"\n        );\n    }\n\n    /**\n     * Destroy/Delete this space, along with all files\n     */\n    public function destroy(): void\n    {\n        $this->s3->deleteMatchingObjects($this->name, \"\", \"(.*?)\");\n        $this->s3->deleteBucket([\"Bucket\" => $this->name]);\n    }\n\n    /**\n     * Get the CORS configuration for the space\n     *\n     * @return array|null An array of CORS rules or null if no rules exist\n     */\n    public function getCORS(): ?array\n    {\n        try {\n            return Result::parse(\n                $this->s3->getBucketCors([\n                                             \"Bucket\" => $this->name,\n                                         ])\n            )['CORSRules'];\n        } catch (S3Exception $e) {\n            return null;\n        }\n    }\n\n    /**\n     * Get the CORS rules, removing the origin specified\n     *\n     * @param string $origin\n     *\n     * @return array\n     */\n    private function getCORSRemovingOrigin(string $origin): array\n    {\n        if (!$CORSRules = $this->getCORS()) {\n            return [];\n        }\n\n        foreach ($CORSRules as $i => $cors) {\n            if ($cors['AllowedOrigins'][0] == $origin) {\n                array_splice($CORSRules, $i, 1);\n            }\n        }\n\n        return $CORSRules;\n    }\n\n    /**\n     * Set the CORS rules\n     *\n     * @param array $rules\n     */\n    private function putCORS(array $rules): void\n    {\n        $this->s3->putBucketCors([\n                                     \"Bucket\" => $this->name,\n                                     \"CORSConfiguration\" => [\n                                         \"CORSRules\" => $rules,\n                                     ],\n                                 ]);\n    }\n\n    /**\n     * Add an origin to the CORS settings on this space\n     *\n     * @param string $origin eg `http://example.com`\n     * @param array $methods Array items must be one of `GET`, `PUT`, `DELETE`, `POST` and `HEAD`\n     * @param int $maxAge Access Control Max Age\n     * @param array $headers Allowed Headers\n     */\n    public function addCORSOrigin(string $origin, array $methods, int $maxAge = 0, array $headers = []): void\n    {\n        $rules = $this->getCORSRemovingOrigin($origin);\n\n        $this->putCORS(\n            array_merge($rules, [\n                [\n                    \"AllowedHeaders\" => $headers,\n                    \"AllowedMethods\" => $methods,\n                    \"AllowedOrigins\" => [$origin],\n                    \"MaxAgeSeconds\" => $maxAge,\n                ],\n            ])\n        );\n    }\n\n    /**\n     * Remove an origin from the CORS settings on this space\n     *\n     * @param string $origin eg `http://example.com`\n     */\n    public function removeCORSOrigin(string $origin): void\n    {\n        $rules = $this->getCORSRemovingOrigin($origin);\n\n        if (empty($rules)) {\n            $this->removeAllCORSOrigins();\n        } else {\n            $this->putCORS($rules);\n        }\n    }\n\n    /**\n     * Delete all CORS rules\n     */\n    public function removeAllCORSOrigins(): void\n    {\n        $this->s3->deleteBucketCors([\n                                        'Bucket' => $this->name,\n                                    ]);\n    }\n\n    /**\n     * List all files in the space (recursively)\n     *\n     * @param string $directory The directory to list files in. Empty string for root directory\n     *\n     * @return array\n     */\n    public function listFiles(string $directory = \"\"): array\n    {\n        $rawFiles = $this->rawListFiles($directory);\n        $files = [];\n\n        foreach ($rawFiles as $fileInfo) {\n            $files[$fileInfo['Key']] = new File($this, $fileInfo['Key'], $fileInfo, false);\n        }\n\n        return ['files' => $files];\n    }\n\n    /**\n     * @param string $directory The directory to list files in. Empty string for root directory\n     * @param string|null $continuationToken Used internally to work around request limits (1000 files per request)\n     *\n     * @return array\n     */\n    private function rawListFiles(string $directory = \"\", ?string $continuationToken = null): array\n    {\n        $data = Result::parse(\n            $this->s3->listObjectsV2([\n                                         \"Bucket\" => $this->name,\n                                         \"Prefix\" => $directory,\n                                         \"MaxKeys\" => 1000,\n//                                         \"StartAfter\" => 0, // For skipping files, maybe for future limit/skip ability\n                                         \"FetchOwner\" => false,\n                                         \"ContinuationToken\" => $continuationToken,\n                                     ])\n        );\n\n        if (!isset($data['Contents'])) {\n            return [];\n        }\n\n        $files = $data['Contents'];\n\n        if (isset($data[\"NextContinuationToken\"]) && $data[\"NextContinuationToken\"] != \"\") {\n            $files = array_merge($files, $this->rawListFiles($directory, $data[\"NextContinuationToken\"]));\n        }\n\n        return $files;\n    }\n\n    /**\n     * Upload a string of text to file\n     *\n     * @param string $text The text to upload\n     * @param string $filename The filepath/name to save to\n     * @param array $params Any extra parameters. [See here](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#upload-property)\n     * @param bool $private True for the file to be private, false to allow public access\n     *\n     * @return \\SpacesAPI\\File\n     */\n    public function uploadText(string $text, string $filename, array $params = [], bool $private = true): File\n    {\n        $this->s3->upload($this->name, $filename, $text, ($private) ? 'private' : 'public-read', $params);\n        return new File($this, $filename, [], false);\n    }\n\n    /**\n     * Upload a file\n     *\n     * @param string $filepath The path to the file, including the filename. Relative and absolute paths are accepted.\n     * @param string|null $filename The remote filename. If `null`, the local filename will be used.\n     * @param string|null $mimeType The file mime type to pass as ContentType for the file (e.g. 'image/jpeg').\n     * @param bool $private True for the file to be private, false to allow public access.\n     *\n     * @return \\SpacesAPI\\File\n     */\n    public function uploadFile(string $filepath, ?string $filename = null, ?string $mimeType = null, bool $private = true): File\n    {\n        $this->s3->putObject([\n                                 'Bucket' => $this->name,\n                                 'Key' => ($filename) ?: basename($filepath),\n                                 'SourceFile' => $filepath,\n                                 'ContentType' => $mimeType,\n                                 'ACL' => ($private) ? 'private' : 'public-read'\n                             ]);\n\n        return new File($this, ($filename) ?: basename($filepath), [], false);\n    }\n\n    /**\n     * Get an instance of \\SpacesAPI\\File for a given filename\n     *\n     * @param string $filename\n     * @package bool $validate\n     *\n     * @return \\SpacesAPI\\File\n     * @throws \\SpacesAPI\\Exceptions\\FileDoesntExistException Thrown if the file doesn't exist\n     */\n    public function file(string $filename, bool $validate = true): File\n    {\n        return new File($this, $filename, [], $validate);\n    }\n\n    /**\n     * Recursively upload an entire directory\n     *\n     * @param string $local The local directory to upload\n     * @param string|null $remote The remote directory to place the files in. `null` to place in the root\n     */\n    public function uploadDirectory(string $local, ?string $remote = null): void\n    {\n        $this->s3->uploadDirectory($local, $this->name, $remote);\n    }\n\n    /**\n     * Recursively download an entire directory.\n     *\n     * @param string $local The local directory to save the directories/files in\n     * @param string|null $remote The remote directory to download. `null` to download the entire space\n     */\n    public function downloadDirectory(string $local, ?string $remote = null): void\n    {\n        $this->s3->downloadBucket($local, $this->name, $remote);\n    }\n\n    /**\n     * Delete an entire directory, including its contents\n     *\n     * @param string $path The directory to delete\n     */\n    public function deleteDirectory(string $path): void\n    {\n        $this->s3->deleteMatchingObjects($this->name, $path);\n    }\n}\n"
  },
  {
    "path": "SpacesAPI/Spaces.php",
    "content": "<?php\n\nnamespace SpacesAPI;\n\nuse Aws\\S3\\Exception\\S3Exception;\nuse Aws\\S3\\S3Client;\nuse SpacesAPI\\Exceptions\\AuthenticationException;\nuse SpacesAPI\\Exceptions\\SpaceExistsException;\n\n/**\n * Represents the connection to Digital Ocean spaces.\n * The entry point for managing spaces.\n *\n * Instantiate your connection with `new \\SpacesAPI\\Spaces(\"access-key\", \"secret-key\", \"region\")`\n *\n * Obtain your access and secret keys from the [DigitalOcean Applications & API dashboard](https://cloud.digitalocean.com/account/api/tokens)\n */\nclass Spaces\n{\n    /**\n     * @var \\Aws\\S3\\S3Client\n     */\n    private $s3;\n\n    /**\n     * Initialise the API\n     *\n     * @param string $accessKey Digital Ocean API access key\n     * @param string $secretKey Digital Ocean API secret key\n     * @param string $region Region, defaults to ams3\n     * @param string $host API endpoint, defaults to digitaloceanspaces.com\n     *\n     * @throws \\SpacesAPI\\Exceptions\\AuthenticationException Authentication failed\n     */\n    public function __construct(string $accessKey, string $secretKey, string $region = \"ams3\", string $host = \"digitaloceanspaces.com\")\n    {\n        $this->s3 = new S3Client([\n                                     \"version\" => \"latest\",\n                                     \"region\" => $region,\n                                     \"endpoint\" => \"https://$region.$host\",\n                                     \"credentials\" => [\"key\" => $accessKey, \"secret\" => $secretKey],\n                                     \"ua_append\" => \"SociallyDev-Spaces-API/2\",\n                                 ]);\n\n        try {\n            $this->s3->headBucket([\"Bucket\" => 'auth-check']);\n        } catch (S3Exception $e) {\n            if ($e->getStatusCode() == 403) {\n                throw new AuthenticationException(\"Authentication failed\");\n            }\n        }\n    }\n\n    /**\n     * List all your spaces\n     *\n     * @return array An array of \\SpacesAPI\\Space instances\n     */\n    public function list(): array\n    {\n        $spaces = [];\n\n        foreach (Result::parse($this->s3->listBuckets()['Buckets']) as $bucket) {\n            $spaces[$bucket['Name']] = new Space($this->s3, $bucket['Name'], false);\n        }\n\n        return $spaces;\n    }\n\n    /**\n     * Create a new space\n     *\n     * @param string $name The name of the new space\n     * @param bool $public Enable file listing. Default `false`\n     *\n     * @return \\SpacesAPI\\Space The newly created space\n     * @throws \\SpacesAPI\\Exceptions\\SpaceExistsException The named space already exists\n     */\n    public function create(string $name, bool $public = false): Space\n    {\n        try {\n            $this->s3->createBucket([\n                                        \"ACL\" => ($public) ? \"public-read\" : \"private\",\n                                        \"Bucket\" => $name,\n                                    ]);\n        } catch (S3Exception $e) {\n            throw new SpaceExistsException($e->getAwsErrorMessage());\n        }\n\n        return new Space($this->s3, $name, false);\n    }\n\n    /**\n     * Use an existing space\n     *\n     * @param string $name The name of the space\n     *\n     * @return \\SpacesAPI\\Space The loaded space\n     * @throws \\SpacesAPI\\Exceptions\\SpaceDoesntExistException The named space doesn't exist\n     */\n    public function space(string $name): Space\n    {\n        return new Space($this->s3, $name);\n    }\n}\n"
  },
  {
    "path": "SpacesAPI/StringFunctions.php",
    "content": "<?php\n\nnamespace SpacesAPI;\n\ntrait StringFunctions\n{\n    public function pascalCaseToCamelCase(string $name): string\n    {\n        return strtolower(preg_replace(\"/([a-z])([A-Z])/\", \"$1_$2\", $name));\n    }\n}\n"
  },
  {
    "path": "composer.json",
    "content": "{\n  \"name\": \"sociallydev/spaces-api\",\n  \"description\": \"Library for accessing Digital Ocean spaces\",\n  \"version\":\"3.6.1\",\n  \"type\": \"library\",\n  \"license\": \"MIT\",\n  \"authors\": [\n    {\n      \"name\": \"Devang Srivastava\",\n      \"email\": \"hey@devang.dev\",\n      \"homepage\": \"https://devang.dev\"\n    },\n    {\n      \"name\": \"David Wakelin\",\n      \"email\": \"helllo@davidwakelin.co.uk\",\n      \"homepage\": \"https://davidwakelin.co.uk\"\n    }\n  ],\n  \"autoload\": {\n    \"psr-4\": {\n      \"SpacesAPI\\\\\": \"SpacesAPI\"\n    }\n  },\n  \"require\": {\n    \"php\": \">=7.3\",\n    \"aws/aws-sdk-php\": \"^3.52\"\n  },\n  \"require-dev\": {\n    \"clean/phpdoc-md\": \"^0.19.1\",\n    \"phpunit/phpunit\": \"^9.5.13\",\n    \"vlucas/phpdotenv\": \"^v5.4.1\"\n  }\n}\n"
  },
  {
    "path": "docs/Examples.md",
    "content": "# Examples\n\n## Connecting to Digital Ocean and selecting a space\n\n```php\nuse SpacesAPI\\Spaces;\n\n$spaces = new Spaces('api-key', 'api-secret');\n$space = $spaces->space('space-name');\n```\n[API docs for \\SpacesAPI\\Spaces](Spaces.md)\n\n## Creating a new space\n```php\n$spaces = new Spaces('api-key', 'api-secret');\n$space = $spaces->create('new-space-name');\n```\n[API docs for creating a space](Spaces.md#spacescreate)\n\n## Listing files\n\n```php\n$space = new Spaces('api-key', 'api-secret')->space('my-space-name');\n$files = $space->listFiles();\n\nforeach ($files['files'] as $file) {\n    echo \"{$file->filename}\\n\";\n}\n```\n[API docs for listing files](Space.md#spacelistfiles)\n\n## Uploading a file\n\n```php\n$space = new Spaces('api-key', 'api-secret')->space('my-space-name');\n$file = $space->uploadFile('./localfile.txt', 'remote-filename.txt');\n```\n[API docs for uploading files](Space.md#spaceuploadfile)\n\n## Uploading text to a file\n```php\n$space = new Spaces('api-key', 'api-secret')->space('my-space-name');\n$file = $space->uploadText('Lorem ipsum', 'remote-filename.txt');\n```\n\n[API docs for uploading text](Space.md#spaceuploadtext)\n\n## Downloading a file\n```php\n$space = new Spaces('api-key', 'api-secret')->space('my-space-name');\n$space->file('filename.txt')->download('./localfile.txt');\n```\n\n[API docs for downloading a file](File.md#filedownload)\n\n## Get the contents of a file\n\n```php\n$space = new Spaces('api-key', 'api-secret')->space('my-space-name');\necho $space->file('filename.txt')->getContents();\n```\n\n[API docs for getting the contents of a file](File.md#filegetcontents)\n\n## Deleting a file\n```php\n$space = new Spaces('api-key', 'api-secret')->space('my-space-name');\n$space->file('filename.txt')->delete();\n```\n\n[API docs for deleting a file](File.md#filedelete)\n\n## Get a public URL\n\n```php\n$space = new Spaces('api-key', 'api-secret')->space('my-space-name');\n$space->file('filename.txt')->getURL();\n```\n\n[API docs for getting the public URL](File.md#filegeturl)\n\n## Get a signed URL\n#### a time limited link to provide access to a private file\n\n```php\n$space = new Spaces('api-key', 'api-secret')->space('my-space-name');\n$space->file('filename.txt')->getSignedURL(\"1 day\");\n```\n\n[API docs for getting a signed URL](File.md#filegetsignedurl)\n\n## Make a file publicly accessible\n```php\n$space = new Spaces('api-key', 'api-secret')->space('my-space-name');\n$space->file('filename.txt')->makePublic();\n```\n\n[API docs for setting file privacy](File.md#spacemakeprivate)\n"
  },
  {
    "path": "docs/File.md",
    "content": "# SpacesAPI\\File\n\nRepresents a single file\n\nYou wouldn't normally instantiate this class directly,\nRather obtain an instance from `\\SpacesAPI\\Space::list()`, `\\SpacesAPI\\Spaces::file()`, `\\SpacesAPI\\Spaces::uploadText()` or `\\SpacesAPI\\Spaces::uploadFile()`\n\n\n## Properties\n| name | Type | Description |\n| --- | --- | --- |\n| `expiration` | `string` | |\n| `e_tag` | `string` | |\n| `last_modified` | `int` | Last modified date as unix timestamp |\n| `content_type` | `string` | THe mime type of the file |\n| `content_length` | `int` | The size of the file in bytes |\n\n\n## Methods\n\n| Name | Description |\n|------|-------------|\n|[__construct](#file__construct)||\n|[copy](#filecopy)|Copy the file on the space|\n|[delete](#filedelete)|Permanently delete this file|\n|[download](#filedownload)|Download the file to a local location|\n|[getContents](#filegetcontents)|Get the file contents as a string|\n|[getSignedURL](#filegetsignedurl)|Get a signed URL, which will work for private files|\n|[getURL](#filegeturl)|Get the public URL. This URL will not work if the file is private|\n|[isPublic](#fileispublic)|Is this file publicly accessible|\n|[makePrivate](#filemakeprivate)|Make file non-publicly accessible|\n|[makePublic](#filemakepublic)|Make file publicly accessible|\n|[move](#filemove)|Move and/or rename file|\n\n\n\n\n### File::__construct\n\n**Description**\n\n```php\n __construct (\\SpacesAPI\\Space $space, string $filename, array $info = [], bool $validate = true)\n```\n\n**Parameters**\n\n* `(\\SpacesAPI\\Space) $space` : An instance of `\\SpacesAPI\\Space`\n* `(string) $filename` : The filename of a file\n* `(array) $info` : Any information already known about the file (eg content_length, content_type, etc). Default `[]`\n* `(bool) $validate` : Check that the file exists. Default `true`\n\n**Return Values**\n\n`void`\n\n**Throws Exceptions**\n\n`\\SpacesAPI\\Exceptions\\FileDoesntExistException` : If validation is `true` and the file doesn't exist\n\n\n<hr />\n\n\n### File::copy\n\n**Description**\n\n```php\npublic copy (string $newFilename, bool $public = false)\n```\n\nCopy the file on the space\n\n\n\n**Parameters**\n\n* `(string) $newFilename`\n* `(bool) $public`\n\n**Return Values**\n\n`\\SpacesAPI\\File` : An instance for the new file\n\n\n\n\n<hr />\n\n\n### File::delete\n\n**Description**\n\n```php\npublic delete (void)\n```\n\nPermanently delete this file\n\n\n\n**Parameters**\n\n`This function has no parameters.`\n\n**Return Values**\n\n`void`\n\n\n<hr />\n\n\n### File::download\n\n**Description**\n\n```php\npublic download (string $saveAs)\n```\n\nDownload the file to a local location\n\n\n\n**Parameters**\n\n* `(string) $saveAs` Then filepath including the filename. This can be a relative or absolute path.\n\n**Return Values**\n\n`void`\n\n\n\n\n<hr />\n\n\n### File::getContents\n\n**Description**\n\n```php\npublic getContents (void)\n```\n\nGet the file contents as a string\n\n\n\n**Parameters**\n\n`This function has no parameters.`\n\n**Return Values**\n\n`string`\n\n\n\n\n<hr />\n\n\n### File::getSignedURL\n\n**Description**\n\n```php\npublic getSignedURL (string|\\DateTime|int $validFor)\n```\n\nGet a signed URL, which will work for private files\n\n\n\n**Parameters**\n\n* `(string|\\DateTime|int) $validFor`\n: Can be any string recognised by strtotime(), an instance of `\\DateTime` or a unix timestamp\n\n**Return Values**\n\n`string`\n\n\n\n\n<hr />\n\n\n### File::getURL\n\n**Description**\n\n```php\npublic getURL (void)\n```\n\nGet the public URL. This URL will not work if the file is private\n\n\n\n**Parameters**\n\n`This function has no parameters.`\n\n**Return Values**\n\n`string`\n\n\n\n\n<hr />\n\n\n### File::isPublic\n\n**Description**\n\n```php\npublic isPublic (void)\n```\n\nIs this file publicly accessible?\n\n\n\n**Parameters**\n\n`This function has no parameters.`\n\n**Return Values**\n\n`bool`\n\n\n\n\n<hr />\n\n\n### File::makePrivate\n\n**Description**\n\n```php\npublic makePrivate (void)\n```\n\nMake file non-publicly accessible\n\n\n\n**Parameters**\n\n`This function has no parameters.`\n\n**Return Values**\n\n`void`\n\n\n<hr />\n\n\n### File::makePublic\n\n**Description**\n\n```php\npublic makePublic (void)\n```\n\nMake file publicly accessible\n\n\n\n**Parameters**\n\n`This function has no parameters.`\n\n**Return Values**\n\n`void`\n\n\n<hr />\n\n\n### File::move\n\n**Description**\n\n```php\npublic move (string $newFilename)\n```\n\nMove or rename a file\nThe instance of `File` is now the moved object\n\n**Parameters**\n\n* `(string) $newFilename`\n\n**Return Values**\n\n`\\SpacesAPI\\File` : An instance for the new file\n\n\n<hr />\n"
  },
  {
    "path": "docs/Space.md",
    "content": "# SpacesAPI\\Space\n\nRepresents a space once connected/created\n\nYou wouldn't normally instantiate this class directly,\nRather obtain an instance from `\\SpacesAPI\\Spaces::space()` or `\\SpacesAPI\\Spaces::create()`\n\n\n## Methods\n\n| Name | Description |\n|------|-------------|\n|[__construct](#space__construct)|Load a space|\n|[addCORSOrigin](#spaceaddcorsorigin)|Add an origin to the CORS settings on this space|\n|[deleteDirectory](#spacedeletedirectory)|Delete an entire directory, including its contents|\n|[destroy](#spacedestroy)|Destroy/Delete this space|\n|[downloadDirectory](#spacedownloaddirectory)|Recursively download an entire directory.|\n|[file](#spacefile)|Get an instance of \\SpacesAPI\\File for a given filename|\n|[getCORS](#spacegetcors)|Get the CORS configuration for the space|\n|[getName](#spacegetname)|Get the name of this space|\n|[getS3Client](#spacegets3client)|Get the current AWS S3 client instance (internal use)|\n|[isPublic](#spaceispublic)|Is file listing enabled?|\n|[listFiles](#spacelistfiles)|List all files in the space (recursively)|\n|[makePrivate](#spacemakeprivate)|Disable file listing|\n|[makePublic](#spacemakepublic)|Enable file listing|\n|[removeCORSOrigin](#spaceremovecorsorigin)|Remove an origin from the CORS settings on this space|\n|[removeAllCORSOrigins](#spacedeleteallcorsorigins)|Delete all CORS rules|\n|[uploadDirectory](#spaceuploaddirectory)|Recursively upload an entire directory|\n|[uploadFile](#spaceuploadfile)|Upload a file|\n|[uploadText](#spaceuploadtext)|Upload a string of text to file|\n\n\n\n\n### Space::__construct\n\n**Description**\n\n```php\npublic __construct (\\Aws\\S3\\S3Client $s3, string $name, bool $validate = true)\n```\n\nLoad a space\n\nYou wouldn't normally call this directly,\nrather obtain an instance from `\\SpacesAPI\\Spaces::space()` or `\\SpacesAPI\\Spaces::create()`\n\n**Parameters**\n\n* `(\\Aws\\S3\\S3Client) $s3`\n: An authenticated S3Client instance\n* `(string) $name`\n: Space name\n* `(bool) $validate`\n: Check that the space exists. Default `true`\n\n**Return Values**\n\n`void`\n\n\n**Throws Exceptions**\n\n\n`\\SpacesAPI\\Exceptions\\SpaceDoesntExistException` : If validation is `true` and the space doesn't exist\n\n\n<hr />\n\n\n### Space::addCORSOrigin\n\n**Description**\n\n```php\npublic addCORSOrigin (string $origin, array $methods, int $maxAge = 0, array $headers => [])\n```\n\nAdd an origin to the CORS settings on this space\n\n\n\n**Parameters**\n\n* `(string) $origin`\n: eg `http://example.com`\n* `(array) $methods`\n: Array items must be one of `GET`, `PUT`, `DELETE`, `POST` and `HEAD`\n* `(int) $maxAge`\n: Access Control Max Age. Default `0`\n* `(array) $headers`\n: Allowed Headers. Default `[]`\n\n**Return Values**\n\n`void`\n\n\n<hr />\n\n\n### Space::deleteDirectory\n\n**Description**\n\n```php\npublic deleteDirectory (string $path)\n```\n\nDelete an entire directory, including its contents\n\n\n\n**Parameters**\n\n* `(string) $path`\n: The directory to delete\n\n**Return Values**\n\n`void`\n\n\n<hr />\n\n\n### Space::destroy\n\n**Description**\n\n```php\npublic destroy (void)\n```\n\nDestroy/Delete this space, along with all files.\n\n\n\n**Parameters**\n\n`This function has no parameters.`\n\n**Return Values**\n\n`void`\n\n\n<hr />\n\n\n### Space::downloadDirectory\n\n**Description**\n\n```php\npublic downloadDirectory (string $local, string|null $remote = null)\n```\n\nRecursively download an entire directory.\n\n\n\n**Parameters**\n\n* `(string) $local`\n: The local directory to save the directories/files in\n* `(string|null) $remote`\n: The remote directory to download. `null` to download the entire space. Default `null`\n\n**Return Values**\n\n`void`\n\n\n<hr />\n\n\n### Space::file\n\n**Description**\n\n```php\npublic file (string $filename, bool $validate = true)\n```\n\nGet an instance of \\SpacesAPI\\File for a given filename\n\n\n\n**Parameters**\n\n* `(string) $filename`\n* `(bool) $validate`\n: Whether to validate the file exits. Defaults to `true`\n\n**Return Values**\n\n`\\SpacesAPI\\File`\n\n\n\n\n**Throws Exceptions**\n\n\n`\\SpacesAPI\\Exceptions\\FileDoesntExistException`\n> Thrown if the file doesn't exist\n\n<hr />\n\n\n### Space::getCORS\n\n**Description**\n\n```php\npublic getCORS (void)\n```\n\nGet the CORS configuration for the space\n\n\n\n**Parameters**\n\n`This function has no parameters.`\n\n**Return Values**\n\n`array|null`\n\n> An array of CORS rules or `null` if no rules exist\n\n\n<hr />\n\n\n### Space::getName\n\n**Description**\n\n```php\npublic getName (void)\n```\n\nGet the name of this space\n\n\n\n**Parameters**\n\n`This function has no parameters.`\n\n**Return Values**\n\n`string`\n\n\n\n\n<hr />\n\n\n### Space::getS3Client\n\n**Description**\n\n```php\npublic getS3Client (void)\n```\n\nGet the current AWS S3 client instance\n\nFor internal library use. It is unlikely you will need to access this object, but can do so to gain access to the underlying S3Client for andvanced usage.\n\n**Parameters**\n\n`This function has no parameters.`\n\n**Return Values**\n\n`\\Aws\\S3\\S3Client`\n\n\n\n\n<hr />\n\n\n### Space::isPublic\n\n**Description**\n\n```php\npublic isPublic (void)\n```\n\nIs file listing enabled?\n\n\n\n**Parameters**\n\n`This function has no parameters.`\n\n**Return Values**\n\n`bool`\n\n\n\n\n<hr />\n\n\n### Space::listFiles\n\n**Description**\n\n```php\npublic listFiles (string $directory = \"\")\n```\n\nList all files in the space (recursively)\n\n\n\n**Parameters**\n\n* `(string) $directory`\n: The directory to list files in. Empty string for root directory\n\n**Return Values**\n\n`array`\n\n> An array of `\\SpacesAPI\\File` instances indexed by the file name\n\n<hr />\n\n\n### Space::makePrivate\n\n**Description**\n\n```php\npublic makePrivate (void)\n```\n\nDisable file listing\n\n\n\n**Parameters**\n\n`This function has no parameters.`\n\n**Return Values**\n\n`void`\n\n\n<hr />\n\n\n### Space::makePublic\n\n**Description**\n\n```php\npublic makePublic (void)\n```\n\nEnable file listing\n\n\n\n**Parameters**\n\n`This function has no parameters.`\n\n**Return Values**\n\n`void`\n\n\n<hr />\n\n\n### Space::removeCORSOrigin\n\n**Description**\n\n```php\npublic removeCORSOrigin (string $origin)\n```\n\nRemove an origin from the CORS settings on this space\n\n\n\n**Parameters**\n\n* `(string) $origin`\n: eg `http://example.com`\n\n**Return Values**\n\n`void`\n\n\n<hr />\n\n### Space::removeAllCORSOrigins\n\n**Description**\n\n```php\npublic deleteAllCORSOrigins (void)\n```\n\nDelete all CORS rules\n\n**Parameters**\n\n`This function has no parameters.`\n\n**Return Values**\n\n`void`\n\n\n<hr />\n\n\n### Space::uploadDirectory\n\n**Description**\n\n```php\npublic uploadDirectory (string $local, string|null $remote = null)\n```\n\nRecursively upload an entire directory\n\n\n\n**Parameters**\n\n* `(string) $local`\n: The local directory to upload\n* `(string|null) $remote`\n: The remote directory to place the files in. `null` to place in the root. Default `null`\n\n**Return Values**\n\n`void`\n\n\n<hr />\n\n\n### Space::uploadFile\n\n**Description**\n\n```php\npublic uploadFile (string $filepath, string|null $filename = null, string|null $mimeType = null, bool $private = true)\n```\n\nUpload a file\n\n\n\n**Parameters**\n\n* `(string) $filepath`\n: The path to the file, including the filename. Relative and absolute paths are accepted.\n* `(string|null) $filename`\n: The remote filename. If `null`, the local filename will be used. Default `null`\n* `(string|null) $mimeType`\n: The files mimeType. If `null` the mimeType is inferred from the file by DigitalOcean Spaces. Default `null`\n* `(bool)` $private\n: If `true` then the file is private, if `false` the file is publicly available. Default `true`\n\n**Return Values**\n\n`\\SpacesAPI\\File`\n\n\n\n\n<hr />\n\n\n### Space::uploadText\n\n**Description**\n\n```php\npublic uploadText (string $text, string $filename, array $params = [], bool $private = true)\n```\n\nUpload a string of text to file\n\n\n\n**Parameters**\n\n* `(string) $text`\n: The text to upload\n* `(string) $filename`\n: The filepath/name to save to\n* `(array) $params`\n: Any extra parameters. [See here](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#upload-property)\n* `(bool) $private`\n: True for the file to be private, false to allow public access\n\n**Return Values**\n\n`\\SpacesAPI\\File`\n\n\n\n\n<hr />\n\n"
  },
  {
    "path": "docs/Spaces.md",
    "content": "# SpacesAPI\\Spaces\n\nRepresents the connection to Digital Ocean spaces.\n\nThe entry point for managing spaces.\n\nInstantiate your connection with `new \\SpacesAPI\\Spaces(\"access-key\", \"secret-key\", \"region\")`\n\nObtain your access and secret keys from the [DigitalOcean Applications & API dashboard](https://cloud.digitalocean.com/account/api/tokens)\n\n\n\n\n\n## Methods\n\n| Name | Description |\n|------|-------------|\n|[__construct](#spaces__construct)|Initialise the API|\n|[create](#spacescreate)|Create a new space|\n|[list](#spaceslist)|List all your spaces|\n|[space](#spacesspace)|Use an existing space|\n\n\n\n\n### Spaces::__construct\n\n**Description**\n\n```php\npublic __construct (string $accessKey, string $secretKey, string $region = \"ams3\", string $host = \"digitaloceanspaces.com)\n```\n\nInitialise the API\n\n\n\n**Parameters**\n\n* `(string) $accessKey`\n: Digital Ocean API access key\n* `(string) $secretKey`\n: Digital Ocean API secret key\n* `(string) $region`\n: Region, defaults to `ams3`\n* `(string) $host`\n: API endpoint, defaults to `digitaloceanspaces.com`\n\n**Return Values**\n\n`void`\n\n\n**Throws Exceptions**\n\n\n`\\SpacesAPI\\Exceptions\\AuthenticationException`\n> Authentication failed\n\n<hr />\n\n\n### Spaces::create\n\n**Description**\n\n```php\npublic create (string $name, bool $public = false)\n```\n\nCreate a new space\n\n\n\n**Parameters**\n\n* `(string) $name`\n: The name of the new space\n* `(bool) $public`\n: Enable file listing. Default `false`\n\n**Return Values**\n\n`\\SpacesAPI\\Space`\n\n> The newly created space\n\n\n**Throws Exceptions**\n\n\n`\\SpacesAPI\\Exceptions\\SpaceExistsException`\n> The named space already exists\n\n<hr />\n\n\n### Spaces::list\n\n**Description**\n\n```php\npublic list (void)\n```\n\nList all your spaces\n\n\n\n**Parameters**\n\n`This function has no parameters.`\n\n**Return Values**\n\n`array`\n\n> An array of `\\SpacesAPI\\Space` instances indexed by the space name\n\n\n<hr />\n\n\n### Spaces::space\n\n**Description**\n\n```php\npublic space (string $name)\n```\n\nUse an existing space\n\n\n\n**Parameters**\n\n* `(string) $name`\n: The name of the space\n\n**Return Values**\n\n`\\SpacesAPI\\Space`\n\n> The loaded space\n\n\n**Throws Exceptions**\n\n\n`\\SpacesAPI\\Exceptions\\SpaceDoesntExistException`\n> The named space doesn't exist\n\n<hr />\n\n"
  },
  {
    "path": "docs/Upgrade2-3.md",
    "content": "# Upgrade guide for v2 to v3\n\nA lot has changed in this version release, so please read carefully to ensure you make all necessary changes.\n\n## Namespace\nSpaces-API is now namespaced, so you will need to add `use SpacesAPI\\Spaces;` to the top of your files.\n\n## Method changes\n### `Spaces::__construct()`\nRegion is now passed into the Spaces constructor instead of the Space.\n\nOld signature:\n```\nSpaces::__construct($accessKey, $secretKey, $host = \"digitaloceanspaces.com\")\n```\n\nNew signature\n```\nSpaces::__construct(string $accessKey, string $secretKey, string $region = \"ams3\", string $host = \"digitaloceanspaces.com\")\n```\n\n***\n\n### `Spaces::listSpaces()`\nMethod name has changed to `Spaces::list()`\n\n***\n\n### `Spaces::space()`\nSignature has changed as the region is now passed into the `Spaces` constructor\n\nOld signature:\n\n```\nSpaces::space($name, $region = \"ams3\")\n```\n\nNew signature\n\n```\nSpaces::space(string $name)\n```\n\n***\n\n### `Space::__contruct()`\nSignature has changed as S3 credentials are no longer passed in, nor the region or host.\n\nOld signature:\n\n```\nSpace::__construct($name, $region, $accessKey, $secretKey, $host)\n```\n\nNew signature\n\n```\nSpace::__construct(S3Client $s3, string $name)\n```\n\n***\n\n### `Space::create()`\nThis method has moved to the `Spaces` class and the signature has changed\n\nOld signature:\n\n```\nSpace::create($privacy = \"private\")\n```\n\nNew signature\n\n```\nSpaces::create(string $name, bool $public = false)\n```\n\n***\n\n### `Space::downloadToDirectory()`\nMethod name has changed. Parameter names have changed, but function the same way.\n\nOld signature:\n\n```\nSpace::downloadToDirectory($directory, $filesStartingAs = \"\")\n```\n\nNew signature\n\n```\nSpaces::downloadDirectory(string $local, ?string $remote = null)\n```\n\n***\n\n### `Space::upload()`\nMethod name and signature has changed\n\nOld signature:\n\n```\nSpace::upload($text, $saveAs, $privacy = \"private\", $params = array())\n```\n\nNew signature\n\n```\nSpaces::uploadText(string $text, string $filename, array $params = [])\n```\n\n***\n\n### `Space::uploadFile()`\n`$privacy` parameter removed from signature. Parameter names have changed, but function the same way.\n\nOld signature:\n\n```\nSpace::uploadFile($filePath, $saveAs = \"\", $privacy = \"private\")\n```\n\nNew signature\n\n```\nSpaces::uploadFile(string $filepath, ?string $filename = null)\n```\n\n***\n\n### `Spaces::downloadFile()`\nMethod has moved to `File`, changed name and changed signature\n\nOld signature:\n\n```\nSpace::downloadFile($file, $saveTo = false)\n```\n\nNew signature\n\n```\nFile::download(string $saveAs)\n```\n\n***\n\n### `Space::copyFile()`\nMethod has moved to `File`, changed name and changed signature\n\nOld signature:\n\n```\nSpace::copyFile($filePath, $saveAs, $toSpace = \"\", $privacy = \"private\")\n```\n\nNew signature\n\n```\nFile::copy(string $newFilename, bool $public = false)\n```\n\n***\n\n### `Space::listFiles()`\nMethod signature has changed. This shouldn't have much impact as almost no-one should be using the second/third argument.\n\nOld signature:\n\n```\nSpace::listFiles($ofFolder = \"\", $autoIterate = true, $continueAfter = null)\n```\n\nNew signature\n\n```\nSpace::listFiles(string $directory = \"\", ?string $continuationToken = null)\n```\n\n***\n\n### `Space::fileExists()`\nMethod removed.\n\nIf you need to check for file existence, instantiate a `File` object from the space, and catch the `FileDoesntExistException`\n\n```php\ntry {\n    $space->file(\"filename.txt\");\n} catch (FileDoesntExistException $e) {\n    // Uh oh, the file doesn't exist\n}\n```\n\n***\n\n### `Space::fileInfo()`\nMethod removed\n\nFile information is now stored in properties on the `File` object\n\n```php\n$file = $space->file('filename.txt');\n$file->content_type;\n$file->content_length;\n$file->expiration;\n$file->e_tag;\n$file->last_modified;\n```\n\n***\n\n### `Space::url()`\nMethod moved to `File` and signature has changed\n\nOld signature:\n\n```\nSpace::url($path)\n```\n\nNew signature\n\n```\nFile::getURL()\n```\n\n***\n\n### `Space::signedURL()`\nMethod moved to `File` and signature has changes\n\nOld signature:\n\n```\nSpace::signedURL($path, $validFor = \"15 minutes\")\n```\n\nNew signature\n\n```\nFile::getSignedURL($validFor = \"15 minutes\")\n```\n\n***\n\n### `Space::deleteFolder()`\nMethod name has changed. Parameter name has changed, but meaning remains the same.\n\nOld signature:\n\n```\nSpace::deleteFolder($prefixOrPath)\n```\n\nNew signature\n\n```\nFile::deleteDirectory(string $path)\n```\n\n***\n\n### `Space::deleteFile()`\nMethod has moved to `File` and signature has changed.\n\nOld signature:\n\n```\nSpace::deleteFile($path)\n```\n\nNew signature\n\n```\nFile::delete()\n```\n\n***\n\n### `Space::filePrivacy()`\nMethod removed. Use `File::makePublic()` or `File::makePrivate()` instead.\n\n***\n\n### `Space::setCORS()`\nMethod removed. Use `Space::addCORSOrigin()`, `Space::removeCORSOrigin()` or `Space::removeAllCORSOrigins()` instead\n\n### `Space::getLifecycleRules()`\nRemoved with no replacement.\n\n### `Space::setLifecycleRules()`\n\nRemoved with no replacement.\n"
  },
  {
    "path": "tests/FileTest.php",
    "content": "<?php\n\nuse Dotenv\\Dotenv;\nuse PHPUnit\\Framework\\TestCase;\nuse SpacesAPI\\Exceptions\\FileDoesntExistException;\nuse SpacesAPI\\Exceptions\\SpaceDoesntExistException;\nuse SpacesAPI\\Spaces;\n\nclass FileTest extends TestCase\n{\n    private static $tempSpaceName;\n    private static $space;\n    private static $file;\n\n    public static function setUpBeforeClass(): void\n    {\n        $dotenv = Dotenv::createImmutable(__DIR__ . \"/..\");\n        $dotenv->load();\n        $dotenv->required(['SPACES_KEY', 'SPACES_SECRET']);\n\n        $spaces = new Spaces($_ENV['SPACES_KEY'], $_ENV['SPACES_SECRET']);\n\n        self::$tempSpaceName = md5(time());\n\n        self::$space = $spaces->create(self::$tempSpaceName);\n        self::$file = self::$space->uploadText('Lorem ipsum', 'lorem-ipsum.txt');\n    }\n\n    public static function tearDownAfterClass(): void\n    {\n        self::$space->destroy();\n    }\n\n    public function testCanUpdatePrivacy()\n    {\n        $this->assertFalse(self::$file->isPublic());\n\n        self::$file->makePublic();\n        $this->assertTrue(self::$file->isPublic());\n\n        self::$file->makePrivate();\n        $this->assertFalse(self::$file->isPublic());\n    }\n\n    public function testCanGetContents()\n    {\n        $this->assertEquals(\"Lorem ipsum\", self::$file->getContents());\n    }\n\n    public function testCanDownloadFile()\n    {\n        $filename = sys_get_temp_dir() . \"/lorem.txt\";\n        self::$file->download($filename);\n\n        $this->assertEquals(\"Lorem ipsum\", file_get_contents($filename));\n    }\n\n    public function testCanCopyFile()\n    {\n        $this->expectNotToPerformAssertions();\n        self::$file->copy('lorem-ipsum-2.txt');\n        self::$space->file('lorem-ipsum-2.txt');\n    }\n\n    public function testCanMoveRenameFile()\n    {\n        $file = self::$file->copy('test.txt');\n        $file->move('renamed-file.txt');\n\n        $this->assertEquals(\"renamed-file.txt\", $file->filename);\n\n        $this->expectException(FileDoesntExistException::class);\n        self::$space->file('test.txt');\n    }\n\n    public function testCanGetURL()\n    {\n        $this->assertStringContainsString('lorem-ipsum.txt', self::$file->getURL());\n        $this->assertStringContainsString('lorem-ipsum.txt', self::$file->getSignedURL());\n    }\n\n    public function testCanDeleteFile()\n    {\n        self::$file->delete();\n\n        $this->expectException(FileDoesntExistException::class);\n        self::$space->file('lorem-ipsum.txt');\n    }\n}\n"
  },
  {
    "path": "tests/SpaceTest.php",
    "content": "<?php\n\nuse Dotenv\\Dotenv;\nuse PHPUnit\\Framework\\TestCase;\nuse SpacesAPI\\Exceptions\\FileDoesntExistException;\nuse SpacesAPI\\Exceptions\\SpaceDoesntExistException;\nuse SpacesAPI\\File;\nuse SpacesAPI\\Spaces;\n\nclass SpaceTest extends TestCase\n{\n    private static $tempSpaceName;\n    private static $space;\n\n    public static function setUpBeforeClass(): void\n    {\n        $dotenv = Dotenv::createImmutable(__DIR__ . \"/..\");\n        $dotenv->load();\n        $dotenv->required(['SPACES_KEY', 'SPACES_SECRET']);\n\n        $spaces = new Spaces($_ENV['SPACES_KEY'], $_ENV['SPACES_SECRET']);\n\n        self::$tempSpaceName = md5(time());\n\n        self::$space = $spaces->create(self::$tempSpaceName);\n    }\n\n    public static function tearDownAfterClass(): void\n    {\n        self::$space->destroy();\n    }\n\n    public function testCanUpdateSpacePrivacy()\n    {\n        $this->assertFalse(self::$space->isPublic());\n\n        self::$space->makePublic();\n        $this->assertTrue(self::$space->isPublic());\n\n        self::$space->makePrivate();\n        $this->assertFalse(self::$space->isPublic());\n    }\n\n    public function testCanAddCORSRule()\n    {\n        $this->assertNull(self::$space->getCORS());\n\n        self::$space->addCORSOrigin(\"http://example.com\", ['GET', 'PUT'], 3200, ['custom-header']);\n\n        $cors = self::$space->getCORS();\n        $this->assertIsArray($cors);\n        $this->assertEquals('custom-header', $cors[0]['AllowedHeaders'][0]);\n        $this->assertEquals('GET', $cors[0]['AllowedMethods'][0]);\n        $this->assertEquals('PUT', $cors[0]['AllowedMethods'][1]);\n        $this->assertEquals('http://example.com', $cors[0]['AllowedOrigins'][0]);\n        $this->assertEquals(3200, $cors[0]['MaxAgeSeconds']);\n\n        self::$space->removeCORSOrigin('http://example.com');\n        $this->assertNull(self::$space->getCORS());\n    }\n\n    public function testFileDoesntExistException()\n    {\n        $this->expectException(FileDoesntExistException::class);\n        self::$space->file(\"non-existent.txt\");\n    }\n\n    public function testCanUploadText()\n    {\n        $file = self::$space->uploadText(\"Lorem ipsum\", \"lorem-ipsum.txt\");\n        $this->assertInstanceOf(File::class, $file);\n        $this->assertFalse($file->isPublic());\n    }\n\n    public function testCanPublicUploadText()\n    {\n        $file = self::$space->uploadText(\"Lorem ipsum\", \"lorem-ipsum.txt\", [], false);\n        $this->assertInstanceOf(File::class, $file);\n        $this->assertTrue($file->isPublic());\n    }\n\n    public function testCanUploadFile()\n    {\n        $tmpFile = tempnam(sys_get_temp_dir(), 'spaces-test');\n        $file = self::$space->uploadFile($tmpFile, 'upload-test.txt');\n        $this->assertInstanceOf(File::class, $file);\n    }\n\n    public function testCanUploadFileWithMimeType()\n    {\n        $tmpFile = tempnam(sys_get_temp_dir(), 'spaces-test');\n        $file = self::$space->uploadFile($tmpFile, 'upload-test.txt', 'text/plain');\n        $this->assertInstanceOf(File::class, $file);\n        $this->assertEquals('text/plain', $file->content_type);\n    }\n\n    /**\n     * @depends testCanUploadText\n     * @depends testCanUploadFile\n     */\n    public function testFileExists()\n    {\n        $file = self::$space->file('lorem-ipsum.txt');\n        $this->assertInstanceOf(File::class, $file);\n\n        $file = self::$space->file('upload-test.txt');\n        $this->assertInstanceOf(File::class, $file);\n    }\n\n    /**\n     * @depends testCanUploadText\n     * @depends testCanUploadFile\n     */\n    public function testCanListFiles()\n    {\n        $files = self::$space->listFiles()['files'];\n        $this->assertIsArray($files);\n        $this->assertCount(2, $files);\n        $this->assertInstanceOf(File::class, $files[array_key_first($files)]);\n\n        foreach ($files as $filename => $file) {\n            $this->assertEquals($file->filename, $filename);\n        }\n\n        foreach ($files as $file) {\n            $file->delete();\n        }\n    }\n\n    public function testCanUploadDirectory()\n    {\n        $localDirectory = sys_get_temp_dir() . \"/spaces-upload-test\";\n        @mkdir($localDirectory);\n        for($i=1; $i<=10; $i++) {\n            file_put_contents(\"$localDirectory/test-$i.txt\", \"Lorem ipsum $i\");\n        }\n\n        self::$space->uploadDirectory($localDirectory, 'remote-dir');\n\n        $list = self::$space->listFiles()['files'];\n        $this->assertIsArray($list);\n        $this->assertCount(10, $list);\n\n        for ($i = 1; $i <= 10; $i++) {\n            unlink(\"$localDirectory/test-$i.txt\");\n        }\n\n        $this->assertCount(2, scandir($localDirectory));\n    }\n\n    /**\n     * @depends testCanUploadDirectory\n     */\n    public function testCanDownloadDirectory()\n    {\n        $localDirectory = sys_get_temp_dir() . \"/spaces-upload-test\";\n        $this->assertCount(2, scandir($localDirectory));\n\n        self::$space->downloadDirectory($localDirectory, 'remote-dir');\n\n        $this->assertCount(12, scandir($localDirectory));\n\n        for ($i = 1; $i <= 10; $i++) {\n            unlink(\"$localDirectory/test-$i.txt\");\n        }\n\n        $this->assertCount(2, scandir($localDirectory));\n    }\n\n    public function testCanDeleteDirectory()\n    {\n        $list = self::$space->listFiles()['files'];\n        $this->assertIsArray($list);\n        $this->assertCount(10, $list);\n\n        self::$space->deleteDirectory('remote-dir');\n\n        $list = self::$space->listFiles()['files'];\n        $this->assertIsArray($list);\n        $this->assertCount(0, $list);\n    }\n}\n"
  },
  {
    "path": "tests/SpacesTest.php",
    "content": "<?php\n\nuse Dotenv\\Dotenv;\nuse PHPUnit\\Framework\\TestCase;\nuse SpacesAPI\\Exceptions\\AuthenticationException;\nuse SpacesAPI\\Exceptions\\SpaceDoesntExistException;\nuse SpacesAPI\\Exceptions\\SpaceExistsException;\nuse SpacesAPI\\Space;\nuse SpacesAPI\\Spaces;\n\nclass SpacesTest extends TestCase\n{\n    private static $tempSpaceName;\n\n    public static function setUpBeforeClass(): void\n    {\n        $dotenv = Dotenv::createImmutable(__DIR__ . \"/..\");\n        $dotenv->load();\n        $dotenv->required(['SPACES_KEY', 'SPACES_SECRET']);\n\n        // This should hopefully always be unique amongst all DO spaces\n        self::$tempSpaceName = md5(time());\n    }\n\n    public static function tearDownAfterClass(): void\n    {\n        (new Spaces($_ENV['SPACES_KEY'], $_ENV['SPACES_SECRET']))->space(self::$tempSpaceName)->destroy();\n    }\n\n    public function testAuthenticationCanFail()\n    {\n        $this->expectException(AuthenticationException::class);\n        new Spaces(\"fake\", \"fake\");\n    }\n\n    public function testCanAuthenticate()\n    {\n        $this->expectNotToPerformAssertions();\n        return new Spaces($_ENV['SPACES_KEY'], $_ENV['SPACES_SECRET']);\n    }\n\n    /**\n     * @depends testCanAuthenticate\n     */\n    public function testCreateSpaceFailsWithExistingSpace(Spaces $spaces)\n    {\n        $this->expectException(SpaceExistsException::class);\n        $spaces->create('test');\n    }\n\n    /**\n     * @depends testCanAuthenticate\n     */\n    public function testCanCreateSpace(Spaces $spaces)\n    {\n        $space = $spaces->create(self::$tempSpaceName);\n        $this->assertInstanceOf(Space::class, $space);\n\n        return $space;\n    }\n\n    /**\n     * @depends testCanAuthenticate\n     */\n    public function testCanListSpaces(Spaces $spaces)\n    {\n        $list = $spaces->list();\n        $this->assertIsArray($list);\n\n        $spaceFound = false;\n        foreach ($list as $name => $space) {\n            if ($name == self::$tempSpaceName && $space->getName() == self::$tempSpaceName) {\n                $spaceFound = true;\n            }\n        }\n\n        $this->assertTrue($spaceFound);\n    }\n\n    /**\n     * @depends testCanAuthenticate\n     */\n    public function testUseSpaceFailsWithNonExistentSpace(Spaces $spaces)\n    {\n        $this->expectException(SpaceDoesntExistException::class);\n        $spaces->space(md5(time()));\n    }\n}\n"
  }
]