diff --git a/README.md b/README.md
index 157da765e476a083bfa1d35434e78806559c8cda..b23fc97ffecd9a71e9645d86128f254cc93dd400 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@ Ce projet utilise l'**API d'ARISE** et **AriseID Connect** (OIDC / OAuth2).
 
 ## Développement local
 
-*Pour une présentation détaillée, lire la section [technologies](#technologies)*
+_Pour une présentation détaillée, lire la section [technologies](#technologies)_
 
 Prérequis : [Node](https://nodejs.org/en/download)
 Optionnel : [nvm (Node Version Manager)](https://github.com/nvm-sh/nvm#installing-and-updating)
@@ -43,7 +43,7 @@ pnpm preview
 
 ## Technologies
 
-Les pages utilisent le SSR (Server Side Rendering), ce qui implique d'avoir un serveur web pour 
+Les pages utilisent le SSR (Server Side Rendering), ce qui implique d'avoir un serveur web pour
 faire le pré-rendu des pages.
 
 [NodeJS](https://nodejs.org) est un runtime JavaScript qui est ici utilisé comme serveur web.
diff --git a/package.json b/package.json
index e0ddcd14b0c9d930a86467d1cc9fb26edb2636e8..f8e3ea65a7ca037ef1751af715c12a50d5070a53 100644
--- a/package.json
+++ b/package.json
@@ -4,7 +4,7 @@
   "private": true,
   "type": "module",
   "engines": {
-    "node": "22.x"
+    "node": "20.x"
   },
   "packageManager": "pnpm@9.10.0",
   "scripts": {
@@ -45,6 +45,7 @@
   "dependencies": {
     "@arise/aidc-sveltekit": "^0.4.1",
     "@urql/core": "^5.0.6",
+    "array-from-async": "^3.0.0",
     "better-sqlite3": "^11.2.1",
     "formsnap": "^1.0.1",
     "gql.tada": "^1.8.6",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index d44a7819753ea17300fa3e5c2360b48c9e7f76df..b346967cb5bb83de1da006e7e29a8cf44aa67ec5 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -14,6 +14,9 @@ importers:
       '@urql/core':
         specifier: ^5.0.6
         version: 5.0.6(graphql@15.9.0)
+      array-from-async:
+        specifier: ^3.0.0
+        version: 3.0.0
       better-sqlite3:
         specifier: ^11.2.1
         version: 11.2.1
@@ -1021,6 +1024,9 @@ packages:
   arktype@2.0.0-beta.0:
     resolution: {integrity: sha512-fE3ssMiXjr/bLqFPzlDhRlXngdyHQreu7p7i8+dtcY1CA+f8WrVUcue6JxywhnqEJXPG4HOcIwQcC+q4VfeUMQ==}
 
+  array-from-async@3.0.0:
+    resolution: {integrity: sha512-gV8/L4y2QB5JTXL9DMdtspGyed2M3V6nMnSN+nNg8ejyUlAAbKAjRS6pfWWINjU/MuFJFMGWPazHPor7hThXQw==}
+
   array-union@2.1.0:
     resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
     engines: {node: '>=8'}
@@ -3359,6 +3365,8 @@ snapshots:
       '@ark/util': 0.1.0
     optional: true
 
+  array-from-async@3.0.0: {}
+
   array-union@2.1.0: {}
 
   autoprefixer@10.4.20(postcss@8.4.41):
diff --git a/renovate.json b/renovate.json
index 5db72dd6a94fc07d18b0e6c0cf76e67ce982f1f9..22a994324a0eaa41756cba09e125fdca2ed29fce 100644
--- a/renovate.json
+++ b/renovate.json
@@ -1,6 +1,4 @@
 {
   "$schema": "https://docs.renovatebot.com/renovate-schema.json",
-  "extends": [
-    "config:recommended"
-  ]
+  "extends": ["config:recommended"]
 }
diff --git a/src/globals.d.ts b/src/globals.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f6ad80b6cebf04a22a4bfabe69c97631290f8cf7
--- /dev/null
+++ b/src/globals.d.ts
@@ -0,0 +1,3 @@
+declare module "array-from-async" {
+  export default function <T>(iterable: AsyncIterable<T>): Promise<T[]>;
+}
diff --git a/src/lib/data.ts b/src/lib/data.ts
index b4db74a3317d90d68e3d7e715fb5261e76aca724..7fbe75bee1b0801b06f177da3ed764d35629f032 100644
--- a/src/lib/data.ts
+++ b/src/lib/data.ts
@@ -1,6 +1,7 @@
 import { pageIterator } from "$lib/graphql/query";
 import { client } from "./graphql";
 import { PROMOTION_QUERY, USER_DETAILS_QUERY } from "./graphql/queries";
+import fromAsync from "array-from-async";
 
 type UserId = string;
 type PromoCache = {
@@ -37,7 +38,7 @@ async function cachePromotionImages(year: number) {
 }
 
 async function fetchPromotion(year: number): Promise<PromoCache> {
-  const array = await Array.fromAsync(pageIterator(PROMOTION_QUERY, { year }));
+  const array = await fromAsync(pageIterator(PROMOTION_QUERY, { year }));
   const users = array.map((node) => node.id);
 
   return {
@@ -73,9 +74,7 @@ export function getPromotionRange(
   min: number,
   max = min,
 ): Promise<Set<UserId>> {
-  return Array.fromAsync(promotionIterator(min, max)).then(
-    (array) => new Set(array),
-  );
+  return fromAsync(promotionIterator(min, max)).then((array) => new Set(array));
 }
 
 setInterval(() => {