From 5b9e534b3f6bd664a4c492cc228522457abb20cf Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Mon, 16 Jan 2023 21:31:07 +0100 Subject: [PATCH] chore(turbo): morph all the things --- Gemfile.lock | 19 ++++--- app/helpers/turbo_stream_helper.rb | 52 ++++++------------- .../controllers/menu_button_controller.ts | 11 ++-- .../controllers/turbo_controller.ts | 25 ++------- .../conditions/_update.turbo_stream.haml | 2 +- .../_add_admin_form.html.haml | 4 +- .../create.turbo_stream.haml | 5 +- .../destroy.turbo_stream.haml | 8 ++- .../types_de_champ/_insert.turbo_stream.haml | 4 +- app/views/api_tokens/index.turbo_stream.haml | 2 +- .../show.turbo_stream.haml | 2 +- .../_password_confirmation.html.haml | 2 +- .../dossiers/_state_button.html.haml | 2 +- .../update_filter.turbo_stream.haml | 2 +- app/views/invites/_form.html.haml | 2 +- app/views/invites/create.turbo_stream.haml | 2 +- app/views/invites/destroy.turbo_stream.haml | 5 +- .../generate_dolist_report.turbo_stream.haml | 2 +- package.json | 4 +- .../piece_justificative_controller_spec.rb | 2 +- yarn.lock | 30 +++++------ 21 files changed, 81 insertions(+), 106 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 7e08b705a..557559d7f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -167,7 +167,7 @@ GEM coderay (1.1.3) coercible (1.0.0) descendants_tracker (~> 0.0.1) - concurrent-ruby (1.1.10) + concurrent-ruby (1.2.0) content_disposition (1.0.0) crack (0.4.5) rexml @@ -398,8 +398,11 @@ GEM loofah (2.19.1) crass (~> 1.0.2) nokogiri (>= 1.5.9) - mail (2.7.1) + mail (2.8.0.1) mini_mime (>= 0.1.1) + net-imap + net-pop + net-smtp mailjet (1.6.0) activesupport (>= 3.1.0) rack (>= 1.4.0) @@ -536,7 +539,7 @@ GEM activesupport (>= 4.2) choice (~> 0.2.0) ruby-graphviz (~> 1.2) - rails-html-sanitizer (1.4.4) + rails-html-sanitizer (1.5.0) loofah (~> 2.19, >= 2.19.1) rails-i18n (7.0.3) i18n (>= 0.7, < 2) @@ -695,9 +698,9 @@ GEM spring (2.1.1) spring-commands-rspec (1.0.4) spring (>= 0.9.1) - sprockets (4.1.1) + sprockets (4.2.0) concurrent-ruby (~> 1.0) - rack (> 1, < 3) + rack (>= 2.2.4, < 4) sprockets-rails (3.4.2) actionpack (>= 5.2) activesupport (>= 5.2) @@ -720,8 +723,10 @@ GEM timecop (0.9.4) timeout (0.1.1) ttfunk (1.7.0) - turbo-rails (0.8.3) - rails (>= 6.0.0) + turbo-rails (1.3.2) + actionpack (>= 6.0.0) + activejob (>= 6.0.0) + railties (>= 6.0.0) typhoeus (1.4.0) ethon (>= 0.9.0) tzinfo (2.0.5) diff --git a/app/helpers/turbo_stream_helper.rb b/app/helpers/turbo_stream_helper.rb index 11095adae..578ff552e 100644 --- a/app/helpers/turbo_stream_helper.rb +++ b/app/helpers/turbo_stream_helper.rb @@ -7,68 +7,48 @@ module TurboStreamHelper include ActionView::Helpers::TagHelper def show(target, delay: nil) - turbo_stream_simple_action_tag :show, target: target, delay: delay + turbo_stream_action_tag :show, target:, delay: end def show_all(targets, delay: nil) - turbo_stream_simple_action_tag :show, targets: targets, delay: delay + turbo_stream_action_tag :show, targets:, delay: end def hide(target, delay: nil) - turbo_stream_simple_action_tag :hide, target: target, delay: delay + turbo_stream_action_tag :hide, target:, delay: end def hide_all(targets, delay: nil) - turbo_stream_simple_action_tag :hide, targets: targets, delay: delay + turbo_stream_action_tag :hide, targets:, delay: end - def focus(target) - turbo_stream_simple_action_tag :focus, target: target + def focus(target, delay: nil) + turbo_stream_action_tag :focus, target:, delay: end - def focus_all(targets) - turbo_stream_simple_action_tag :focus, targets: targets + def focus_all(targets, delay: nil) + turbo_stream_action_tag :focus, targets:, delay: end - def enable(target) - turbo_stream_simple_action_tag :enable, target: target + def enable(target, delay: nil) + turbo_stream_action_tag :enable, target:, delay: end - def enable_all(targets) - turbo_stream_simple_action_tag :enable, targets: targets + def enable_all(targets, delay: nil) + turbo_stream_action_tag :enable, targets:, delay: end - def disable(target) - turbo_stream_simple_action_tag :disable, target: target + def disable(target, delay: nil) + turbo_stream_action_tag :disable, target:, delay: end - def disable_all(targets) - turbo_stream_simple_action_tag :disable, targets: targets - end - - def morph(target, content = nil, **rendering, &block) - action :morph, target, content, **rendering, &block - end - - def morph_all(targets, content = nil, **rendering, &block) - action_all :morph, targets, content, **rendering, &block + def disable_all(targets, delay: nil) + turbo_stream_action_tag :disable, targets:, delay: end def dispatch(type, detail = nil) content = detail.present? ? tag.script(cdata_section(detail.to_json), type: 'application/json') : nil action_all :append, 'head', tag.dispatch_event(content, type:) end - - private - - def turbo_stream_simple_action_tag(action, target: nil, targets: nil, **attributes) - if (target = convert_to_turbo_stream_dom_id(target)) - tag.turbo_stream('', **attributes.merge(action: action, target: target)) - elsif (targets = convert_to_turbo_stream_dom_id(targets, include_selector: true)) - tag.turbo_stream('', **attributes.merge(action: action, targets: targets)) - else - tag.turbo_stream('', **attributes.merge(action: action)) - end - end end end diff --git a/app/javascript/controllers/menu_button_controller.ts b/app/javascript/controllers/menu_button_controller.ts index 6c2d8ca06..f1a31e02d 100644 --- a/app/javascript/controllers/menu_button_controller.ts +++ b/app/javascript/controllers/menu_button_controller.ts @@ -6,7 +6,6 @@ export class MenuButtonController extends ApplicationController { declare readonly buttonTarget: HTMLButtonElement; declare readonly menuTarget: HTMLElement; - #isOpen = false; #teardown?: () => void; connect() { @@ -17,6 +16,10 @@ export class MenuButtonController extends ApplicationController { this.#teardown?.(); } + private get isOpen() { + return (this.element as HTMLElement).classList.contains('open'); + } + private get isMenu() { return !(this.element as HTMLElement).dataset.popover; } @@ -57,7 +60,7 @@ export class MenuButtonController extends ApplicationController { if (this.buttonTarget == target || this.buttonTarget.contains(target)) { event.preventDefault(); - if (this.#isOpen) { + if (this.isOpen) { this.close(); } else { this.open(); @@ -98,7 +101,6 @@ export class MenuButtonController extends ApplicationController { document.body.addEventListener('click', onClickBody); }); - this.#isOpen = true; this.#teardown = () => document.body.removeEventListener('click', onClickBody); } @@ -108,7 +110,6 @@ export class MenuButtonController extends ApplicationController { this.menuTarget.parentElement?.classList.remove('open'); this.#teardown?.(); this.setFocusToMenuitem(null); - this.#isOpen = false; } private isClickOutside(target: HTMLElement) { @@ -116,7 +117,7 @@ export class MenuButtonController extends ApplicationController { target.isConnected && !this.element.contains(target) && !target.closest('reach-portal') && - this.#isOpen + this.isOpen ); } diff --git a/app/javascript/controllers/turbo_controller.ts b/app/javascript/controllers/turbo_controller.ts index 419686c1c..bfd1b0027 100644 --- a/app/javascript/controllers/turbo_controller.ts +++ b/app/javascript/controllers/turbo_controller.ts @@ -30,7 +30,8 @@ export class TurboController extends ApplicationController { connect() { this.#actions = new Actions({ element: document.documentElement, - schema: { forceAttribute: 'data-turbo-force', hiddenClassName: 'hidden' } + schema: { forceAttribute: 'data-turbo-force', hiddenClassName: 'hidden' }, + debug: false }); // actions#observe() is an interface over specialized mutation observers. @@ -50,31 +51,11 @@ export class TurboController extends ApplicationController { // see: https://turbo.hotwired.dev/handbook/streams#custom-actions this.onGlobal('turbo:before-stream-render', (event: StreamRenderEvent) => { - const fallbackToDefaultActions = event.detail.render; event.detail.render = (streamElement: StreamElement) => - this.renderStreamElement(streamElement, fallbackToDefaultActions); + this.actions.applyActions([parseTurboStream(streamElement)]); }); } - private renderStreamElement( - streamElement: StreamElement, - fallbackRender: (streamElement: StreamElement) => void - ) { - switch (streamElement.action) { - // keep turbo default behavior to avoid risks going all in on coldwire - case 'replace': - case 'update': - fallbackRender(streamElement); - break; - case 'morph': - streamElement.setAttribute('action', 'replace'); - this.actions.applyActions([parseTurboStream(streamElement)]); - break; - default: - this.actions.applyActions([parseTurboStream(streamElement)]); - } - } - private startSpinner() { this.#submitting = true; this.actions.show({ targets: this.spinnerTargets }); diff --git a/app/views/administrateurs/conditions/_update.turbo_stream.haml b/app/views/administrateurs/conditions/_update.turbo_stream.haml index 558809b94..9f4430bb5 100644 --- a/app/views/administrateurs/conditions/_update.turbo_stream.haml +++ b/app/views/administrateurs/conditions/_update.turbo_stream.haml @@ -9,7 +9,7 @@ - rendered = render @condition_component - if rendered.present? - = turbo_stream.morph dom_id(@tdc.stable_self, :conditions) do + = turbo_stream.replace dom_id(@tdc.stable_self, :conditions) do - rendered - else = turbo_stream.remove dom_id(@tdc.stable_self, :conditions) diff --git a/app/views/administrateurs/procedure_administrateurs/_add_admin_form.html.haml b/app/views/administrateurs/procedure_administrateurs/_add_admin_form.html.haml index 2aa4a8acd..f35a460f3 100644 --- a/app/views/administrateurs/procedure_administrateurs/_add_admin_form.html.haml +++ b/app/views/administrateurs/procedure_administrateurs/_add_admin_form.html.haml @@ -1,9 +1,9 @@ = form_for procedure.administrateurs.new(user: User.new), url: { controller: 'procedure_administrateurs' }, html: { class: 'form', id: "new_administrateur" }, - data: { turbo: true } do |f| + data: { turbo: true, turbo_force: true } do |f| = f.label :email do Ajouter un administrateur %p.notice Renseignez l’email d’un administrateur déjà enregistré sur #{APPLICATION_NAME} pour lui permettre de modifier « #{procedure.libelle} ». - = f.email_field :email, placeholder: 'marie.dupont@exemple.fr', required: true, disabled: disabled_as_super_admin + = f.email_field :email, placeholder: 'marie.dupont@exemple.fr', autofocus: true, required: true, disabled: disabled_as_super_admin = f.submit 'Ajouter comme administrateur', class: 'button primary send', disabled: disabled_as_super_admin diff --git a/app/views/administrateurs/procedure_administrateurs/create.turbo_stream.haml b/app/views/administrateurs/procedure_administrateurs/create.turbo_stream.haml index ea1bb3da3..0e32ab85e 100644 --- a/app/views/administrateurs/procedure_administrateurs/create.turbo_stream.haml +++ b/app/views/administrateurs/procedure_administrateurs/create.turbo_stream.haml @@ -1,4 +1,5 @@ - if @administrateur.present? - = turbo_stream.update 'administrateurs', - render(Procedure::ProcedureAdministrateurs::AdministrateurComponent.with_collection(@procedure.administrateurs.order('users.email'), procedure: @procedure)) + = turbo_stream.update 'administrateurs' do + = render Procedure::ProcedureAdministrateurs::AdministrateurComponent.with_collection(@procedure.administrateurs.order('users.email'), procedure: @procedure) = turbo_stream.replace "new_administrateur", partial: 'add_admin_form', locals: { procedure: @procedure, disabled_as_super_admin: administrateur_as_manager? } + = turbo_stream.focus 'administrateur_email' diff --git a/app/views/administrateurs/procedure_administrateurs/destroy.turbo_stream.haml b/app/views/administrateurs/procedure_administrateurs/destroy.turbo_stream.haml index e4aee0e28..cc0ec3e73 100644 --- a/app/views/administrateurs/procedure_administrateurs/destroy.turbo_stream.haml +++ b/app/views/administrateurs/procedure_administrateurs/destroy.turbo_stream.haml @@ -1,2 +1,6 @@ -= turbo_stream.update 'administrateurs', - render(Procedure::ProcedureAdministrateurs::AdministrateurComponent.with_collection(@procedure.administrateurs.order('users.email'), procedure: @procedure)) += turbo_stream.update 'administrateurs' do + = render Procedure::ProcedureAdministrateurs::AdministrateurComponent.with_collection(@procedure.administrateurs.order('users.email'), procedure: @procedure) +- if @procedure.administrateurs.one? + = turbo_stream.focus 'administrateur_email' +- else + = turbo_stream.focus_all '#administrateurs tr:first-child input[type="submit"]' diff --git a/app/views/administrateurs/types_de_champ/_insert.turbo_stream.haml b/app/views/administrateurs/types_de_champ/_insert.turbo_stream.haml index 3c71d36b4..fe1769549 100644 --- a/app/views/administrateurs/types_de_champ/_insert.turbo_stream.haml +++ b/app/views/administrateurs/types_de_champ/_insert.turbo_stream.haml @@ -18,11 +18,11 @@ - render @created - @morphed&.each do |champ_component| - = turbo_stream.morph dom_id(champ_component.coordinate, :type_de_champ_editor) do + = turbo_stream.replace dom_id(champ_component.coordinate, :type_de_champ_editor) do - render champ_component - if @coordinate.present? - = turbo_stream.morph dom_id(@coordinate.revision, :estimated_fill_duration) do + = turbo_stream.replace dom_id(@coordinate.revision, :estimated_fill_duration) do - render TypesDeChampEditor::EstimatedFillDurationComponent.new(revision: @coordinate.revision, is_annotation: @coordinate.private?) = turbo_stream.dispatch 'sortable:sort' diff --git a/app/views/api_tokens/index.turbo_stream.haml b/app/views/api_tokens/index.turbo_stream.haml index 1ea8888ff..b6c304eb1 100644 --- a/app/views/api_tokens/index.turbo_stream.haml +++ b/app/views/api_tokens/index.turbo_stream.haml @@ -1,2 +1,2 @@ -= turbo_stream.morph dom_id(current_administrateur, :profil_api_token) do += turbo_stream.replace dom_id(current_administrateur, :profil_api_token) do = render Profile::APITokenCardComponent.new created_api_token: @api_token, created_packed_token: @packed_token diff --git a/app/views/champs/piece_justificative/show.turbo_stream.haml b/app/views/champs/piece_justificative/show.turbo_stream.haml index ce5032dab..68cbad262 100644 --- a/app/views/champs/piece_justificative/show.turbo_stream.haml +++ b/app/views/champs/piece_justificative/show.turbo_stream.haml @@ -1,5 +1,5 @@ = fields_for @champ.input_name, @champ do |form| - = turbo_stream.morph @champ.input_group_id do + = turbo_stream.replace @champ.input_group_id do = render EditableChamp::EditableChampComponent.new champ: @champ, form: form - @champ.piece_justificative_file.attachments.each do |attachment| diff --git a/app/views/france_connect/particulier/_password_confirmation.html.haml b/app/views/france_connect/particulier/_password_confirmation.html.haml index 0fa778734..82ecbb3fa 100644 --- a/app/views/france_connect/particulier/_password_confirmation.html.haml +++ b/app/views/france_connect/particulier/_password_confirmation.html.haml @@ -3,7 +3,7 @@ %br = t('.fill_in_password') -= form_tag france_connect_particulier_merge_with_existing_account_path, data: { turbo: true }, class: 'mt-2 form fconnect-form' do += form_tag france_connect_particulier_merge_with_existing_account_path, data: { turbo: true, turbo_force: true }, class: 'mt-2 form fconnect-form' do = hidden_field_tag :merge_token, merge_token = hidden_field_tag :email, email = label_tag :password, t('views.registrations.new.password_label', min_length: 8) diff --git a/app/views/instructeurs/dossiers/_state_button.html.haml b/app/views/instructeurs/dossiers/_state_button.html.haml index 875af4cc8..296edd63b 100644 --- a/app/views/instructeurs/dossiers/_state_button.html.haml +++ b/app/views/instructeurs/dossiers/_state_button.html.haml @@ -1,4 +1,4 @@ -.dropdown{ data: { controller: 'menu-button', popover: 'true' } } +.dropdown{ data: { controller: 'menu-button', popover: 'true', turbo_force: true } } -# Dropdown button title %button.fr-btn.dropdown-button{ class: button_or_label_class(dossier), data: { menu_button_target: 'button' } } = dossier_display_state dossier diff --git a/app/views/instructeurs/procedures/update_filter.turbo_stream.haml b/app/views/instructeurs/procedures/update_filter.turbo_stream.haml index 55679210d..05598d95b 100644 --- a/app/views/instructeurs/procedures/update_filter.turbo_stream.haml +++ b/app/views/instructeurs/procedures/update_filter.turbo_stream.haml @@ -1,2 +1,2 @@ -= turbo_stream.morph 'filter-component' do += turbo_stream.replace 'filter-component' do = render Dossiers::FilterComponent.new(procedure: @procedure, procedure_presentation: @procedure_presentation, statut: @statut, field_id: @field) diff --git a/app/views/invites/_form.html.haml b/app/views/invites/_form.html.haml index 8743ca2fb..49dffeb35 100644 --- a/app/views/invites/_form.html.haml +++ b/app/views/invites/_form.html.haml @@ -15,7 +15,7 @@ %p= t('views.invites.form.invite_to_edit_line1') %p= t('views.invites.form.invite_to_edit_line2') - = form_tag dossier_invites_path(dossier), data: { turbo: true }, method: :post, class: 'form' do + = form_tag dossier_invites_path(dossier), data: { turbo: true, turbo_force: true }, method: :post, class: 'form' do .row .col %span diff --git a/app/views/invites/create.turbo_stream.haml b/app/views/invites/create.turbo_stream.haml index 6d420243e..d31956ae4 100644 --- a/app/views/invites/create.turbo_stream.haml +++ b/app/views/invites/create.turbo_stream.haml @@ -1,2 +1,2 @@ = turbo_stream.replace_all '.invite-user-action', partial: 'invites/dropdown', locals: { dossier: @dossier } -= turbo_stream.focus_all '.invite-user-action > button' += turbo_stream.focus 'invite_email' diff --git a/app/views/invites/destroy.turbo_stream.haml b/app/views/invites/destroy.turbo_stream.haml index 64b203eec..34f54f0b2 100644 --- a/app/views/invites/destroy.turbo_stream.haml +++ b/app/views/invites/destroy.turbo_stream.haml @@ -1,3 +1,6 @@ - if @dossier.present? = turbo_stream.replace_all '.invite-user-action', partial: 'invites/dropdown', locals: { dossier: @dossier } - = turbo_stream.focus_all '.invite-user-action > button' + - if @dossier.invites.empty? + = turbo_stream.focus 'invite_email' + - else + = turbo_stream.focus_all '#invites-form ul a:first-child' diff --git a/app/views/manager/email_events/generate_dolist_report.turbo_stream.haml b/app/views/manager/email_events/generate_dolist_report.turbo_stream.haml index efe704452..91620b3c6 100644 --- a/app/views/manager/email_events/generate_dolist_report.turbo_stream.haml +++ b/app/views/manager/email_events/generate_dolist_report.turbo_stream.haml @@ -1,2 +1,2 @@ -= turbo_stream.morph "dolist-report-form" do += turbo_stream.replace "dolist-report-form" do = @message diff --git a/package.json b/package.json index d2882b7eb..9aa2c8660 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "dependencies": { - "@coldwired/actions": "^0.8.0", - "@coldwired/turbo-stream": "^0.8.0", + "@coldwired/actions": "^0.8.1", + "@coldwired/turbo-stream": "^0.8.1", "@gouvfr/dsfr": "^1.7.2", "@graphiql/plugin-explorer": "^0.1.11", "@graphiql/toolkit": "^0.8.0", diff --git a/spec/controllers/champs/piece_justificative_controller_spec.rb b/spec/controllers/champs/piece_justificative_controller_spec.rb index ac09d58d6..aa983d745 100644 --- a/spec/controllers/champs/piece_justificative_controller_spec.rb +++ b/spec/controllers/champs/piece_justificative_controller_spec.rb @@ -32,7 +32,7 @@ describe Champs::PieceJustificativeController, type: :controller do it 'renders the attachment template as Javascript' do subject expect(response.status).to eq(200) - expect(response.body).to include("") + expect(response.body).to include("") end it 'updates dossier.last_champ_updated_at' do diff --git a/yarn.lock b/yarn.lock index 9c3e54afb..e829c488a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -511,28 +511,28 @@ "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" -"@coldwired/actions@^0.8.0": - version "0.8.0" - resolved "https://registry.yarnpkg.com/@coldwired/actions/-/actions-0.8.0.tgz#a5e92c0badbc37ffa7c9e70d2aa7982739fbba5f" - integrity sha512-IbqyC2ToDv1JWBVkk33ywDrftD34r3WBLokub7Tlon8xS6FQQVejwmgqIqiQ3iLE0jiXOqKz2an3Y1e69mJ55A== +"@coldwired/actions@^0.8.1": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@coldwired/actions/-/actions-0.8.1.tgz#c309a2a38f5a0af11c03c4cf8e16c6496c7f9894" + integrity sha512-d+rgz+FHeKRcqPS8c8mPRuwYsbfDwIExJIOdpexAmhhW67c1V3xo4+oAwNSE395/qU5/JXmOnHPeqA6t6Fx9ew== dependencies: - "@coldwired/utils" "^0.4.1" + "@coldwired/utils" "^0.8.1" morphdom "^2.6.1" tiny-invariant "^1.3.1" -"@coldwired/turbo-stream@^0.8.0": - version "0.8.0" - resolved "https://registry.yarnpkg.com/@coldwired/turbo-stream/-/turbo-stream-0.8.0.tgz#596202ecbca88f9b264d5055bedcb94298db626b" - integrity sha512-/ZKdAcgDkcH+YkW2RyxXGMM6OdpKp4nVpFGTYnor+4mR/jjEdnTRw+3wnZJQfgPzLMG39SXEmY4mp9xQM/qA3Q== +"@coldwired/turbo-stream@^0.8.1": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@coldwired/turbo-stream/-/turbo-stream-0.8.1.tgz#3f2f52b8cd59906c2387b78949c66fad79cb0eae" + integrity sha512-rxDzEObIZ4MQQzM2joXV2yjOLcveHBDkghDu6EiiCAMl95wV/O6dMTM1KW3+l6tjd0a5hTTKrhoNJKuWLTV5GA== dependencies: - "@coldwired/actions" "^0.8.0" - "@coldwired/utils" "^0.4.1" + "@coldwired/actions" "^0.8.1" + "@coldwired/utils" "^0.8.1" tiny-invariant "^1.3.1" -"@coldwired/utils@^0.4.1": - version "0.4.1" - resolved "https://registry.yarnpkg.com/@coldwired/utils/-/utils-0.4.1.tgz#50a615c81948754f4c7f118df05a44e078f28568" - integrity sha512-a673URA77AUBm8u5bWGtEmMi9oMv8HBidPT1GtRsaYma1W23Hd2Emn+oD6vUyGLfL+hJGT91w+Y2Y3x9BgLqwQ== +"@coldwired/utils@^0.8.1": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@coldwired/utils/-/utils-0.8.1.tgz#bd20e69d12d938b43ac03d4020416188e8c8107b" + integrity sha512-pT3vLSNFKXAZUi3IgomLXNiDB/l4Qer3/uHP6I6XzLY/q7xIBarhSmcuYUsgxs4jlH8sIsgAjU+PP0qGXc3QAg== "@esbuild/android-arm64@0.16.14": version "0.16.14"