From 3b2c33bdb7ffc9487abafeda20dad6bca9909840 Mon Sep 17 00:00:00 2001
From: vmaubert <github@vcmb.dev>
Date: Mon, 29 Jul 2024 11:35:18 +0200
Subject: [PATCH] =?UTF-8?q?chore(api):=20ajoute=20une=20r=C3=A8gle=20eslin?=
 =?UTF-8?q?t=20pour=20interdir=20l'utilisation=20de=20sort()=20(#1386)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/api/package.json                         |  4 ++++
 packages/api/src/api/rest/format/titres.ts        | 10 +++++++---
 packages/api/src/api/rest/titre-heritage.ts       |  4 ++--
 packages/api/src/api/rest/titres.ts               |  3 ++-
 packages/api/src/business/entreprises-guyane.ts   |  2 +-
 ...itres-etapes-administrations-locales-update.ts | 15 ++++++++-------
 .../processes/titres-etapes-areas-update.ts       |  7 +++++--
 .../business/rules-demarches/machine-common.ts    |  3 +--
 .../business/rules-demarches/machine-helper.ts    |  2 +-
 .../rules-demarches/machine-test-helper.ts        | 12 ++++++------
 .../titre-demarche-depot-demande-date-find.ts     |  2 +-
 .../src/business/utils/titre-elements-sort-asc.ts |  2 +-
 .../utils/titre-etape-heritage-props-find.ts      | 10 ++++++++--
 .../api/src/business/utils/titre-etapes-sort.ts   |  8 ++++----
 .../validations/utilisateur-updation-validate.ts  |  6 +++++-
 .../20230605080141_refactor-communes-forets.ts    |  8 ++++----
 .../api/src/tools/demarches/tests-creation.ts     |  2 +-
 packages/api/src/tools/phases/tests-creation.ts   |  2 +-
 packages/api/src/tools/territoires-update.ts      |  4 ++--
 19 files changed, 64 insertions(+), 42 deletions(-)

diff --git a/packages/api/package.json b/packages/api/package.json
index adab214b4..2b31181ed 100644
--- a/packages/api/package.json
+++ b/packages/api/package.json
@@ -182,6 +182,10 @@
           "message": "leftJoinRelation is deprecated. Use leftJoinRelated instead.",
           "selector": "Identifier[name='leftJoinRelation']"
         },
+        {
+                "message": "sort is deprecated. Use toSorted instead.",
+                "selector": "Identifier[name='sort']"
+              },
         {
           "message": "no 'run' call from PgTyped allowed. Use dbQueryAndValidate.",
           "selector": "CallExpression[callee.property.name='run'][arguments.length=2]"
diff --git a/packages/api/src/api/rest/format/titres.ts b/packages/api/src/api/rest/format/titres.ts
index 4f3ab697b..dea3db586 100644
--- a/packages/api/src/api/rest/format/titres.ts
+++ b/packages/api/src/api/rest/format/titres.ts
@@ -34,7 +34,7 @@ const titreContenuTableFormat = (titre: ITitre): Record<string, string> => {
     return {}
   }
 
-  const orderedDemarches = [...titre.demarches].filter(d => !DemarchesTypes[d.typeId].travaux).sort((a, b) => (b.ordre ?? 0) - (a.ordre ?? 0))
+  const orderedDemarches = titre.demarches.filter(d => !DemarchesTypes[d.typeId].travaux).toSorted((a, b) => (b.ordre ?? 0) - (a.ordre ?? 0))
 
   const demarche = orderedDemarches[0]
   const etapes = (demarche.etapes ?? []).map(etape => {
@@ -50,7 +50,7 @@ const titreContenuTableFormat = (titre: ITitre): Record<string, string> => {
   return getDemarcheContenu(etapes, titre.typeId)
 }
 
-export const titresTableFormat = async (pool: Pool, titres: ITitre[]) => {
+export const titresTableFormat = async (pool: Pool, titres: ITitre[]): Promise<Record<string, string | number | null | undefined>[]> => {
   const communesIndex = await getCommunesIndex(
     pool,
     titres.flatMap(titre => titre.communes?.map(({ id }) => id) ?? [])
@@ -116,7 +116,11 @@ export const titresTableFormat = async (pool: Pool, titres: ITitre[]) => {
   })
 }
 
-export const titreGeojsonPropertiesFormat = (communesIndex: Record<CommuneId, string>, entreprisesIndex: Record<EntrepriseId, GetEntreprises>, titre: ITitre) => {
+export const titreGeojsonPropertiesFormat = (
+  communesIndex: Record<CommuneId, string>,
+  entreprisesIndex: Record<EntrepriseId, GetEntreprises>,
+  titre: ITitre
+): Record<string, string | (string | null)[] | { nom: string; surface: number }[] | number | null | undefined> => {
   if (!titre.secteursMaritime) {
     throw new Error('les secteurs maritimes ne sont pas chargés')
   }
diff --git a/packages/api/src/api/rest/titre-heritage.ts b/packages/api/src/api/rest/titre-heritage.ts
index 930637a4c..f9098b404 100644
--- a/packages/api/src/api/rest/titre-heritage.ts
+++ b/packages/api/src/api/rest/titre-heritage.ts
@@ -7,10 +7,10 @@ import { TitrePropTitreEtapeFindDemarche } from 'camino-common/src/titres'
 export const getMostRecentEtapeFondamentaleValide = <F extends Pick<DemarcheEtape, 'etape_statut_id' | 'etape_type_id' | 'ordre' | 'is_brouillon'>>(
   titreDemarches: TitrePropTitreEtapeFindDemarche<F>[]
 ): F | null => {
-  const titreDemarchesDesc: TitrePropTitreEtapeFindDemarche<F>[] = [...titreDemarches].sort((a, b) => b.ordre - a.ordre)
+  const titreDemarchesDesc: TitrePropTitreEtapeFindDemarche<F>[] = titreDemarches.toSorted((a, b) => b.ordre - a.ordre)
 
   for (const titreDemarche of titreDemarchesDesc) {
-    const titreEtapeDesc = [...titreDemarche.etapes].sort((a, b) => b.ordre - a.ordre)
+    const titreEtapeDesc = titreDemarche.etapes.toSorted((a, b) => b.ordre - a.ordre)
     for (const titreEtape of titreEtapeDesc) {
       if (isEtapeTypeIdFondamentale(titreEtape.etape_type_id) && isFondamentalesStatutOk(titreEtape.etape_statut_id) && titreEtape.is_brouillon === ETAPE_IS_NOT_BROUILLON) {
         return titreEtape
diff --git a/packages/api/src/api/rest/titres.ts b/packages/api/src/api/rest/titres.ts
index 79d1651ec..46cd999c2 100644
--- a/packages/api/src/api/rest/titres.ts
+++ b/packages/api/src/api/rest/titres.ts
@@ -266,7 +266,8 @@ export const titresAdministrations =
               throw new Error('les démarches ne sont pas chargées')
             }
 
-            const demarcheLaPlusRecente = titre.demarches.length > 0 ? titre.demarches.sort(({ ordre: ordreA }, { ordre: ordreB }) => (ordreA ?? 0) - (ordreB ?? 0))[titre.demarches.length - 1] : null
+            const demarcheLaPlusRecente =
+              titre.demarches.length > 0 ? titre.demarches.toSorted(({ ordre: ordreA }, { ordre: ordreB }) => (ordreA ?? 0) - (ordreB ?? 0))[titre.demarches.length - 1] : null
             let enAttenteDeAdministration = false
             const prochainesEtapes: EtapeTypeId[] = []
             let derniereEtape: {
diff --git a/packages/api/src/business/entreprises-guyane.ts b/packages/api/src/business/entreprises-guyane.ts
index e9d40a24e..0a7cc464f 100644
--- a/packages/api/src/business/entreprises-guyane.ts
+++ b/packages/api/src/business/entreprises-guyane.ts
@@ -46,7 +46,7 @@ export const subscribeUsersToGuyaneExploitants = async (): Promise<ResultAggrega
 
       return acc
     }, {})
-  const users = Object.values(reduced).sort((a, b) => a.email.localeCompare(b.email))
+  const users = Object.values(reduced).toSorted((a, b) => a.email.localeCompare(b.email))
   await exploitantsGuyaneSubscriberUpdate(
     users.map(user => ({
       email: user.email,
diff --git a/packages/api/src/business/processes/titres-etapes-administrations-locales-update.ts b/packages/api/src/business/processes/titres-etapes-administrations-locales-update.ts
index 0394f007a..5fa81f881 100644
--- a/packages/api/src/business/processes/titres-etapes-administrations-locales-update.ts
+++ b/packages/api/src/business/processes/titres-etapes-administrations-locales-update.ts
@@ -20,23 +20,24 @@ const titresEtapesAdministrationsLocalesBuild = (etapes: ITitreEtape[]): ITitreE
     )
 
     titresEtapesAdministrationsLocales.push({
-      titreEtapeAdministrationsLocalesOld: titreEtape.administrationsLocales?.sort() ?? [],
-      titreEtapeAdministrationsLocales: titreEtapeAdministrationsLocales.sort(),
+      titreEtapeAdministrationsLocalesOld: titreEtape.administrationsLocales?.toSorted() ?? [],
+      titreEtapeAdministrationsLocales: titreEtapeAdministrationsLocales.toSorted(),
       titreEtapeId: titreEtape.id,
     })
 
     return titresEtapesAdministrationsLocales
   }, [])
 
-export const titresEtapesAdministrationsLocalesUpdate = async (titresEtapesIds?: string[]) => {
+type AdministrationsByEtapeId = {
+  titreEtapeId: string
+  administrations: AdministrationId[]
+}
+export const titresEtapesAdministrationsLocalesUpdate = async (titresEtapesIds?: string[]): Promise<{ titresEtapesAdministrationsLocalesUpdated: AdministrationsByEtapeId[] }> => {
   console.info()
   console.info('administrations locales associées aux étapes…')
 
   const etapes = await titresEtapesGet({ titresEtapesIds }, { fields: { id: {} } }, userSuper)
-  const titresEtapesAdministrationsLocalesUpdated: {
-    titreEtapeId: string
-    administrations: AdministrationId[]
-  }[] = []
+  const titresEtapesAdministrationsLocalesUpdated: AdministrationsByEtapeId[] = []
 
   if (etapes.length > 0) {
     const titresEtapesAdministrationsLocales = titresEtapesAdministrationsLocalesBuild(etapes)
diff --git a/packages/api/src/business/processes/titres-etapes-areas-update.ts b/packages/api/src/business/processes/titres-etapes-areas-update.ts
index 5804d3e60..c8f2e66f8 100644
--- a/packages/api/src/business/processes/titres-etapes-areas-update.ts
+++ b/packages/api/src/business/processes/titres-etapes-areas-update.ts
@@ -80,7 +80,7 @@ async function intersectForets(titreEtape: Pick<ITitreEtape, 'forets' | 'id'>, f
 }
 
 async function intersectCommunes(titreEtape: Pick<ITitreEtape, 'communes' | 'id' | 'geojson4326Perimetre'>, communes: DeepReadonly<{ id: CommuneId; surface: M2 }[]>) {
-  const communesNew: { id: CommuneId; surface: M2 }[] = communes.map(({ id, surface }) => ({ id: toCommuneId(id), surface })).sort((a, b) => a.id.localeCompare(b.id))
+  const communesNew: { id: CommuneId; surface: M2 }[] = communes.map(({ id, surface }) => ({ id: toCommuneId(id), surface })).toSorted((a, b) => a.id.localeCompare(b.id))
   if (titreEtape.communes?.length !== communesNew.length || titreEtape.communes.some((value, index) => value.id !== communesNew[index].id || value.surface !== communesNew[index].surface)) {
     console.info(`Mise à jour des communes sur l'étape ${titreEtape.id}, ancien: '${JSON.stringify(titreEtape.communes)}', nouveaux: '${JSON.stringify(communesNew)}'`)
     await knex('titres_etapes')
@@ -90,7 +90,10 @@ async function intersectCommunes(titreEtape: Pick<ITitreEtape, 'communes' | 'id'
 }
 
 async function intersectSecteursMaritime(titreEtape: Pick<ITitreEtape, 'secteursMaritime' | 'id'>, secteursMaritime: DeepReadonly<SecteursMaritimesIds[]>) {
-  const secteurMaritimeNew: SecteursMaritimes[] = [...secteursMaritime.map(id => getSecteurMaritime(id))].filter(onlyUnique).sort()
+  const secteurMaritimeNew: SecteursMaritimes[] = secteursMaritime
+    .map(id => getSecteurMaritime(id))
+    .filter(onlyUnique)
+    .toSorted()
   if (titreEtape.secteursMaritime?.length !== secteurMaritimeNew.length || titreEtape.secteursMaritime.some((value, index) => value !== secteurMaritimeNew[index])) {
     console.info(`Mise à jour des secteurs maritimes sur l'étape ${titreEtape.id}, ancien: '${titreEtape.secteursMaritime}', nouveaux: '${secteurMaritimeNew}'`)
     await knex('titres_etapes')
diff --git a/packages/api/src/business/rules-demarches/machine-common.ts b/packages/api/src/business/rules-demarches/machine-common.ts
index 9b5ca2e28..ad9160941 100644
--- a/packages/api/src/business/rules-demarches/machine-common.ts
+++ b/packages/api/src/business/rules-demarches/machine-common.ts
@@ -50,9 +50,8 @@ export type TitreEtapeForMachine = z.infer<typeof titreEtapeForMachineValidator>
 export const toMachineEtapes = (etapes: (Pick<Partial<TitreEtapeForMachine>, 'ordre'> & Omit<TitreEtapeForMachine, 'id' | 'ordre'>)[]): Etape[] => {
   // TODO 2022-10-12 si on appelle titreEtapesSortAscByOrdre on se retrouve avec une grosse dépendance cyclique
   return etapes
-    .slice()
     .filter(dbEtape => dbEtape.isBrouillon === ETAPE_IS_NOT_BROUILLON)
-    .sort((a, b) => a.ordre! - b.ordre!)
+    .toSorted((a, b) => a.ordre! - b.ordre!)
     .map(dbEtape => toMachineEtape(dbEtape))
 }
 
diff --git a/packages/api/src/business/rules-demarches/machine-helper.ts b/packages/api/src/business/rules-demarches/machine-helper.ts
index a5f41b55f..fb3163c89 100644
--- a/packages/api/src/business/rules-demarches/machine-helper.ts
+++ b/packages/api/src/business/rules-demarches/machine-helper.ts
@@ -47,7 +47,7 @@ export abstract class CaminoMachine<CaminoContext extends CaminoCommonContext, C
   }
 
   public orderMachine(etapes: readonly Etape[]): readonly Etape[] {
-    const sortedEtapes = etapes.slice().sort((a, b) => a.date.localeCompare(b.date))
+    const sortedEtapes = etapes.toSorted((a, b) => a.date.localeCompare(b.date))
 
     const solution = this.findSolution(sortedEtapes)
 
diff --git a/packages/api/src/business/rules-demarches/machine-test-helper.ts b/packages/api/src/business/rules-demarches/machine-test-helper.ts
index 084dd76d2..b779c76cc 100644
--- a/packages/api/src/business/rules-demarches/machine-test-helper.ts
+++ b/packages/api/src/business/rules-demarches/machine-test-helper.ts
@@ -25,7 +25,7 @@ expect.extend({
     { machine, date }: { machine: CaminoMachine<C, T>; date: CaminoDate },
     events: T['type'][]
   ) {
-    events.sort()
+    events = events.toSorted()
     const passEvents: (typeof events)[number][] = getNextEvents(service.getSnapshot())
       .filter((event: string) => machine.isEvent(event))
       .filter((event: (typeof events)[number]) => {
@@ -33,8 +33,8 @@ expect.extend({
 
         return events.some(event => service.getSnapshot().can(event) && service.getSnapshot().status !== 'done')
       })
+      .toSorted()
 
-    passEvents.sort()
     if (passEvents.length !== events.length || passEvents.some((entry, index) => entry !== events[index])) {
       return {
         pass: false,
@@ -49,7 +49,7 @@ expect.extend({
   },
 })
 
-export const interpretMachine = <T extends EventObject, C extends CaminoCommonContext>(machine: CaminoMachine<C, T>, etapes: readonly Etape[]) => {
+export const interpretMachine = <T extends EventObject, C extends CaminoCommonContext>(machine: CaminoMachine<C, T>, etapes: readonly Etape[]): Actor<(typeof machine)['machine']> => {
   const service = createActor(machine.machine)
 
   service.start()
@@ -69,7 +69,7 @@ export const interpretMachine = <T extends EventObject, C extends CaminoCommonCo
 
             return events.some(event => service.getSnapshot().can(event) && service.getSnapshot().status !== 'done')
           })
-          .sort()}'`
+          .toSorted()}'`
       )
     }
     service.send(event)
@@ -82,7 +82,7 @@ export const setDateAndOrderAndInterpretMachine = <T extends EventObject, C exte
   machine: CaminoMachine<C, T>,
   initDate: `${number}-${number}-${number}`,
   etapes: readonly (EtapeTypeEtapeStatutValidPair & Omit<Etape, 'date' | 'etapeTypeId' | 'etapeStatutId'> & { addDays?: number })[]
-) => {
+): { service: Actor<(typeof machine)['machine']>; dateFin: CaminoDate; etapes: Etape[] } => {
   const firstDate = toCaminoDate(initDate)
   let index = 0
   const fullEtapes = etapes.map(etape => {
@@ -98,6 +98,6 @@ export const setDateAndOrderAndInterpretMachine = <T extends EventObject, C exte
 
   return { service, dateFin: dateAddDays(firstDate, etapes.length), etapes: fullEtapes }
 }
-export const orderAndInterpretMachine = <T extends EventObject, C extends CaminoCommonContext>(machine: CaminoMachine<C, T>, etapes: readonly Etape[]) => {
+export const orderAndInterpretMachine = <T extends EventObject, C extends CaminoCommonContext>(machine: CaminoMachine<C, T>, etapes: readonly Etape[]): Actor<(typeof machine)['machine']> => {
   return interpretMachine(machine, machine.orderMachine(etapes))
 }
diff --git a/packages/api/src/business/rules/titre-demarche-depot-demande-date-find.ts b/packages/api/src/business/rules/titre-demarche-depot-demande-date-find.ts
index 59719fe40..647f0641e 100644
--- a/packages/api/src/business/rules/titre-demarche-depot-demande-date-find.ts
+++ b/packages/api/src/business/rules/titre-demarche-depot-demande-date-find.ts
@@ -18,5 +18,5 @@ export const titreDemarcheDepotDemandeDateFind = (titreEtapes: DeepReadonly<Pick
     return titreEtapeDemande.date
   }
 
-  return titreEtapes.map(te => te.date).sort()[0]
+  return titreEtapes.map(te => te.date).toSorted()[0]
 }
diff --git a/packages/api/src/business/utils/titre-elements-sort-asc.ts b/packages/api/src/business/utils/titre-elements-sort-asc.ts
index 9852e71bd..fb867a9dc 100644
--- a/packages/api/src/business/utils/titre-elements-sort-asc.ts
+++ b/packages/api/src/business/utils/titre-elements-sort-asc.ts
@@ -6,7 +6,7 @@ import { titreEtapesSortAscByOrdre } from './titre-etapes-sort'
 
 export type TitreDemarcheSortAscMinimalDemarche = Pick<ITitreDemarche, 'typeId' | 'ordre'> & { etapes?: Pick<ITitreEtape, 'ordre' | 'date'>[] }
 export const titreDemarcheSortAsc = <T extends TitreDemarcheSortAscMinimalDemarche>(titreElements: T[]): T[] =>
-  titreElements.slice().sort((a, b) => {
+  titreElements.toSorted((a, b) => {
     const aHasEtapes = a.etapes && a.etapes.length
     const bHasEtapes = b.etapes && b.etapes.length
 
diff --git a/packages/api/src/business/utils/titre-etape-heritage-props-find.ts b/packages/api/src/business/utils/titre-etape-heritage-props-find.ts
index 3b53ec741..4332bab8d 100644
--- a/packages/api/src/business/utils/titre-etape-heritage-props-find.ts
+++ b/packages/api/src/business/utils/titre-etape-heritage-props-find.ts
@@ -17,7 +17,7 @@ const propertyArrayCheck = (newValue: string[], prevValue: string[], propId: str
     if (propId === 'substances') {
       return newValue.toString() === prevValue.toString()
     } else if (['titulaires', 'amodiataires'].includes(propId)) {
-      return [...newValue].sort().toString() === [...prevValue].sort().toString()
+      return newValue.toSorted().toString() === prevValue.toSorted().toString()
     }
   }
 
@@ -40,7 +40,13 @@ const titreEtapePropCheck = (propId: string, oldValue?: IPropValue | null, newVa
   return oldValue === newValue
 }
 
-export const titreEtapeHeritagePropsFind = (titreEtape: ITitreEtape, prevTitreEtape?: ITitreEtape | null) => {
+export const titreEtapeHeritagePropsFind = (
+  titreEtape: ITitreEtape,
+  prevTitreEtape?: ITitreEtape | null
+): {
+  hasChanged: boolean
+  titreEtape: ITitreEtape
+} => {
   let hasChanged = false
 
   let newTitreEtape = titreEtape
diff --git a/packages/api/src/business/utils/titre-etapes-sort.ts b/packages/api/src/business/utils/titre-etapes-sort.ts
index b753b4ace..5af35ee41 100644
--- a/packages/api/src/business/utils/titre-etapes-sort.ts
+++ b/packages/api/src/business/utils/titre-etapes-sort.ts
@@ -9,10 +9,10 @@ import { isNotNullNorUndefinedNorEmpty } from 'camino-common/src/typescript-tool
 import { ETAPE_IS_BROUILLON } from 'camino-common/src/etape'
 
 // classe les étapes selon leur ordre inverse: 3, 2, 1.
-export const titreEtapesSortDescByOrdre = <T extends Pick<ITitreEtape, 'ordre'>>(titreEtapes: T[]): T[] => titreEtapes.slice().sort((a, b) => b.ordre! - a.ordre!)
+export const titreEtapesSortDescByOrdre = <T extends Pick<ITitreEtape, 'ordre'>>(titreEtapes: T[]): T[] => titreEtapes.toSorted((a, b) => b.ordre! - a.ordre!)
 
 // classe les étapes selon leur ordre: 1, 2, 3, …
-export const titreEtapesSortAscByOrdre = <T extends Pick<ITitreEtape, 'ordre'>>(titreEtapes: T[]): T[] => titreEtapes.slice().sort((a, b) => a.ordre! - b.ordre!)
+export const titreEtapesSortAscByOrdre = <T extends Pick<ITitreEtape, 'ordre'>>(titreEtapes: T[]): T[] => titreEtapes.toSorted((a, b) => a.ordre! - b.ordre!)
 // classe les étapes selon leur dates, ordre et etapesTypes.ordre le cas échéant
 export const titreEtapesSortAscByDate = <T extends TitreEtapeForMachine>(titreEtapes: T[], demarcheId: DemarcheId, demarcheTypeId: DemarcheTypeId, titreTypeId: TitreTypeId): T[] => {
   const demarcheDefinition = demarcheDefinitionFind(titreTypeId, demarcheTypeId, titreEtapes, demarcheId)
@@ -34,7 +34,7 @@ export const titreEtapesSortAscByDate = <T extends TitreEtapeForMachine>(titreEt
     // On remet les brouillons à la bonne date, car la machine les a ignorés
     const etapesInBrouillon = titreEtapes.filter(({ isBrouillon }) => isBrouillon)
     if (isNotNullNorUndefinedNorEmpty(etapesInBrouillon)) {
-      return [...result, ...etapesInBrouillon].sort((a, b) => {
+      return [...result, ...etapesInBrouillon].toSorted((a, b) => {
         if (a.isBrouillon === ETAPE_IS_BROUILLON || b.isBrouillon === ETAPE_IS_BROUILLON) {
           return a.date.localeCompare(b.date)
         }
@@ -45,7 +45,7 @@ export const titreEtapesSortAscByDate = <T extends TitreEtapeForMachine>(titreEt
 
     return result
   } else {
-    return titreEtapes.slice().sort((a, b) => {
+    return titreEtapes.toSorted((a, b) => {
       const dateA = new Date(a.date)
       const dateB = new Date(b.date)
 
diff --git a/packages/api/src/business/validations/utilisateur-updation-validate.ts b/packages/api/src/business/validations/utilisateur-updation-validate.ts
index f91d91ff2..4ea82c80b 100644
--- a/packages/api/src/business/validations/utilisateur-updation-validate.ts
+++ b/packages/api/src/business/validations/utilisateur-updation-validate.ts
@@ -60,7 +60,11 @@ export const utilisateurUpdationValidate = (user: UserNotNull, utilisateur: Util
       throw new Error('droits insuffisants pour modifier les administrations')
     }
 
-    if (!isAdministrationAdmin(user) && isEntrepriseOrBureauDEtude(utilisateurOld) && !equalStringArrays(utilisateurOld.entreprises.map(({ id }) => id).sort(), utilisateur.entreprises.sort())) {
+    if (
+      !isAdministrationAdmin(user) &&
+      isEntrepriseOrBureauDEtude(utilisateurOld) &&
+      !equalStringArrays(utilisateurOld.entreprises.map(({ id }) => id).toSorted(), utilisateur.entreprises.toSorted())
+    ) {
       throw new Error('droits insuffisants pour modifier les entreprises')
     }
   }
diff --git a/packages/api/src/knex/migrations/20230605080141_refactor-communes-forets.ts b/packages/api/src/knex/migrations/20230605080141_refactor-communes-forets.ts
index d2f3fd941..9c1ea5f2a 100644
--- a/packages/api/src/knex/migrations/20230605080141_refactor-communes-forets.ts
+++ b/packages/api/src/knex/migrations/20230605080141_refactor-communes-forets.ts
@@ -1,6 +1,6 @@
 import { Knex } from 'knex'
 
-export const up = async (knex: Knex) => {
+export const up = async (knex: Knex): Promise<void> => {
   await knex.raw('alter table communes drop column departement_id')
   await knex.raw("alter table titres_etapes add column forets jsonb DEFAULT '[]'::jsonb NOT NULL")
   await knex.raw("alter table titres_etapes add column communes jsonb DEFAULT '[]'::jsonb NOT NULL")
@@ -19,7 +19,7 @@ export const up = async (knex: Knex) => {
 
   for (const titreEtapeId in foretsByTitreEtapes) {
     await knex('titres_etapes')
-      .update({ forets: JSON.stringify(foretsByTitreEtapes[titreEtapeId].sort()) })
+      .update({ forets: JSON.stringify(foretsByTitreEtapes[titreEtapeId].toSorted()) })
       .where('id', titreEtapeId)
   }
 
@@ -37,7 +37,7 @@ export const up = async (knex: Knex) => {
 
   for (const titreEtapeId in communesByTitreEtapes) {
     await knex('titres_etapes')
-      .update({ communes: JSON.stringify(communesByTitreEtapes[titreEtapeId].sort((a: any, b: any) => a.id.localeCompare(b.id))) })
+      .update({ communes: JSON.stringify(communesByTitreEtapes[titreEtapeId].toSorted((a: any, b: any) => a.id.localeCompare(b.id))) })
       .where('id', titreEtapeId)
   }
   await knex.raw('drop table titres_forets')
@@ -45,4 +45,4 @@ export const up = async (knex: Knex) => {
   await knex.raw('drop table forets')
 }
 
-export const down = () => ({})
+export const down = (): void => {}
diff --git a/packages/api/src/tools/demarches/tests-creation.ts b/packages/api/src/tools/demarches/tests-creation.ts
index ccf1678bd..18ed1061c 100644
--- a/packages/api/src/tools/demarches/tests-creation.ts
+++ b/packages/api/src/tools/demarches/tests-creation.ts
@@ -35,7 +35,7 @@ const writeEtapesForTest = async () => {
         const etapes: Etape[] = toMachineEtapes(
           (
             demarche?.etapes
-              ?.sort((a, b) => (a.ordre ?? 0) - (b.ordre ?? 0))
+              ?.toSorted((a, b) => (a.ordre ?? 0) - (b.ordre ?? 0))
               ?.map(etape => {
                 if (etape?.contenu?.arm) {
                   etape.contenu = { arm: etape.contenu?.arm }
diff --git a/packages/api/src/tools/phases/tests-creation.ts b/packages/api/src/tools/phases/tests-creation.ts
index 8006e4ffd..1c7c75619 100644
--- a/packages/api/src/tools/phases/tests-creation.ts
+++ b/packages/api/src/tools/phases/tests-creation.ts
@@ -117,7 +117,7 @@ const writePhasesForTest = async () => {
   const result: TitrePhasesTest[] = Object.keys(titres).map(titreId => {
     const titre = titres[titreId]
 
-    return [titre.titreTypeId, titre.demarches.sort((a, b) => (a.ordre ?? Infinity) - (b.ordre ?? Infinity))]
+    return [titre.titreTypeId, titre.demarches.toSorted((a, b) => (a.ordre ?? Infinity) - (b.ordre ?? Infinity))]
   })
 
   writeFileSync(`src/business/rules/titre-phases-find.cas.json`, JSON.stringify(result))
diff --git a/packages/api/src/tools/territoires-update.ts b/packages/api/src/tools/territoires-update.ts
index 0a2f942ba..4db52ecc7 100644
--- a/packages/api/src/tools/territoires-update.ts
+++ b/packages/api/src/tools/territoires-update.ts
@@ -145,7 +145,7 @@ const foretsUpdate = async () => {
   }
 
   if (ids.some(id => !ForetIds.includes(id)) || ForetIds.some(fId => !ids.includes(fId))) {
-    console.error(`les forêts ne sont pas à jour dans le common: ${[...ForetIds].sort()} --> ${ids.sort()}`)
+    console.error(`les forêts ne sont pas à jour dans le common: ${ForetIds.toSorted()} --> ${ids.toSorted()}`)
   }
 }
 
@@ -248,7 +248,7 @@ const sdomZonesUpdate = async () => {
   }
 }
 
-export async function updateTerritoires(pool: Pool) {
+export async function updateTerritoires(pool: Pool): Promise<void> {
   console.info('Mise à jour des territoires')
   try {
     await communesUpdate(pool)
-- 
GitLab