Browse Source

configure biome for app/src/server/util dir

Futa Arai 5 months ago
parent
commit
9d180aff6d

+ 6 - 0
apps/app/.eslintrc.js

@@ -48,6 +48,12 @@ module.exports = {
     'src/components/**',
     'src/components/**',
     'src/services/**',
     'src/services/**',
     'src/pages/**',
     'src/pages/**',
+    'src/server/crowi/**',
+    'src/server/events/**',
+    'src/server/interfaces/**',
+    'src/server/util/**',
+    'src/server/app.ts',
+    'src/server/repl.ts',
   ],
   ],
   settings: {
   settings: {
     // resolve path aliases by eslint-import-resolver-typescript
     // resolve path aliases by eslint-import-resolver-typescript

+ 6 - 4
apps/app/src/server/util/apiPaginate.js

@@ -5,7 +5,7 @@ const OFFSET_DEFAULT = 0;
 
 
 const DEFAULT_MAX_RESULT_WINDOW = 10000;
 const DEFAULT_MAX_RESULT_WINDOW = 10000;
 
 
-const parseIntValue = function(value, defaultValue, maxLimit) {
+const parseIntValue = (value, defaultValue, maxLimit) => {
   if (!value) {
   if (!value) {
     return defaultValue;
     return defaultValue;
   }
   }
@@ -20,19 +20,21 @@ const parseIntValue = function(value, defaultValue, maxLimit) {
 
 
 function ApiPaginate() {}
 function ApiPaginate() {}
 
 
-ApiPaginate.parseOptionsForElasticSearch = function(params) {
+ApiPaginate.parseOptionsForElasticSearch = (params) => {
   const limit = parseIntValue(params.limit, LIMIT_DEFAULT, LIMIT_MAX);
   const limit = parseIntValue(params.limit, LIMIT_DEFAULT, LIMIT_MAX);
   const offset = parseIntValue(params.offset, OFFSET_DEFAULT);
   const offset = parseIntValue(params.offset, OFFSET_DEFAULT);
 
 
   // See https://github.com/crowi/crowi/pull/293
   // See https://github.com/crowi/crowi/pull/293
   if (limit + offset > DEFAULT_MAX_RESULT_WINDOW) {
   if (limit + offset > DEFAULT_MAX_RESULT_WINDOW) {
-    throw new Error(`(limit + offset) must be less than or equal to ${DEFAULT_MAX_RESULT_WINDOW}`);
+    throw new Error(
+      `(limit + offset) must be less than or equal to ${DEFAULT_MAX_RESULT_WINDOW}`,
+    );
   }
   }
 
 
   return { limit, offset };
   return { limit, offset };
 };
 };
 
 
-ApiPaginate.parseOptions = function(params) {
+ApiPaginate.parseOptions = (params) => {
   const limit = parseIntValue(params.limit, LIMIT_DEFAULT, LIMIT_MAX);
   const limit = parseIntValue(params.limit, LIMIT_DEFAULT, LIMIT_MAX);
   const offset = parseIntValue(params.offset, OFFSET_DEFAULT);
   const offset = parseIntValue(params.offset, OFFSET_DEFAULT);
 
 

+ 4 - 6
apps/app/src/server/util/apiResponse.js

@@ -1,7 +1,6 @@
-function ApiResponse() {
-}
+function ApiResponse() {}
 
 
-ApiResponse.error = function(err, code, data) {
+ApiResponse.error = (err, code, data) => {
   const result = {};
   const result = {};
 
 
   result.ok = false;
   result.ok = false;
@@ -10,15 +9,14 @@ ApiResponse.error = function(err, code, data) {
 
 
   if (err instanceof Error) {
   if (err instanceof Error) {
     result.error = err.toString();
     result.error = err.toString();
-  }
-  else {
+  } else {
     result.error = err;
     result.error = err;
   }
   }
 
 
   return result;
   return result;
 };
 };
 
 
-ApiResponse.success = function(data) {
+ApiResponse.success = (data) => {
   const result = data || {};
   const result = data || {};
 
 
   result.ok = true;
   result.ok = true;

+ 0 - 1
apps/app/src/server/util/batch-stream.js

@@ -26,7 +26,6 @@ function createBatchStream(batchSize) {
       }
       }
       callback();
       callback();
     },
     },
-
   });
   });
 }
 }
 
 

+ 4 - 2
apps/app/src/server/util/collect-ancestor-paths.ts

@@ -1,5 +1,4 @@
 import { dirname } from 'node:path';
 import { dirname } from 'node:path';
-
 import { isTopPage } from '@growi/core/dist/utils/page-path-utils';
 import { isTopPage } from '@growi/core/dist/utils/page-path-utils';
 
 
 /**
 /**
@@ -8,7 +7,10 @@ import { isTopPage } from '@growi/core/dist/utils/page-path-utils';
  * @param {string[]} ancestorPaths
  * @param {string[]} ancestorPaths
  * @returns {string[]}
  * @returns {string[]}
  */
  */
-export const collectAncestorPaths = (path: string, ancestorPaths: string[] = []): string[] => {
+export const collectAncestorPaths = (
+  path: string,
+  ancestorPaths: string[] = [],
+): string[] => {
   if (isTopPage(path)) return ancestorPaths;
   if (isTopPage(path)) return ancestorPaths;
 
 
   const parentPath = dirname(path);
   const parentPath = dirname(path);

+ 1 - 2
apps/app/src/server/util/compare-objectId.spec.ts

@@ -38,7 +38,7 @@ describe('Objectid comparison utils', () => {
       });
       });
     });
     });
 
 
-    describe('When arrays don\'t have intersection', () => {
+    describe("When arrays don't have intersection", () => {
       const arr1 = [id1, id2];
       const arr1 = [id1, id2];
       const arr2 = [id3, id4];
       const arr2 = [id3, id4];
 
 
@@ -47,5 +47,4 @@ describe('Objectid comparison utils', () => {
       });
       });
     });
     });
   });
   });
-
 });
 });

+ 25 - 14
apps/app/src/server/util/compare-objectId.ts

@@ -11,11 +11,14 @@ const ObjectId = mongoose.Types.ObjectId;
  * @param potentialSubset array that is potentially a subset of arr
  * @param potentialSubset array that is potentially a subset of arr
  * @returns Whether or not arr includes all elements of potentialSubset
  * @returns Whether or not arr includes all elements of potentialSubset
  */
  */
-export const includesObjectIds = (arr: ObjectIdLike[], potentialSubset: ObjectIdLike[]): boolean => {
-  const _arr = arr.map(i => i.toString());
-  const _potentialSubset = potentialSubset.map(i => i.toString());
+export const includesObjectIds = (
+  arr: ObjectIdLike[],
+  potentialSubset: ObjectIdLike[],
+): boolean => {
+  const _arr = arr.map((i) => i.toString());
+  const _potentialSubset = potentialSubset.map((i) => i.toString());
 
 
-  return _potentialSubset.every(id => _arr.includes(id));
+  return _potentialSubset.every((id) => _arr.includes(id));
 };
 };
 
 
 /**
 /**
@@ -24,11 +27,14 @@ export const includesObjectIds = (arr: ObjectIdLike[], potentialSubset: ObjectId
  * @param arr2 another array with ObjectIds
  * @param arr2 another array with ObjectIds
  * @returns Whether or not arr1 and arr2 have an intersection
  * @returns Whether or not arr1 and arr2 have an intersection
  */
  */
-export const hasIntersection = (arr1: ObjectIdLike[], arr2: ObjectIdLike[]): boolean => {
-  const _arr1 = arr1.map(i => i.toString());
-  const _arr2 = arr2.map(i => i.toString());
+export const hasIntersection = (
+  arr1: ObjectIdLike[],
+  arr2: ObjectIdLike[],
+): boolean => {
+  const _arr1 = arr1.map((i) => i.toString());
+  const _arr2 = arr2.map((i) => i.toString());
 
 
-  return _arr1.some(item => _arr2.includes(item));
+  return _arr1.some((item) => _arr2.includes(item));
 };
 };
 
 
 /**
 /**
@@ -37,19 +43,24 @@ export const hasIntersection = (arr1: ObjectIdLike[], arr2: ObjectIdLike[]): boo
  * @param testIds Array of mongoose.Types.ObjectId
  * @param testIds Array of mongoose.Types.ObjectId
  * @returns Array of mongoose.Types.ObjectId
  * @returns Array of mongoose.Types.ObjectId
  */
  */
-export const excludeTestIdsFromTargetIds = <T extends { toString: any } = IObjectId>(
-  targetIds: T[], testIds: ObjectIdLike[],
+export const excludeTestIdsFromTargetIds = <
+  T extends { toString: any } = IObjectId,
+>(
+  targetIds: T[],
+  testIds: ObjectIdLike[],
 ): T[] => {
 ): T[] => {
   // cast to string
   // cast to string
-  const arr1 = targetIds.map(e => e.toString());
-  const arr2 = testIds.map(e => e.toString());
+  const arr1 = targetIds.map((e) => e.toString());
+  const arr2 = testIds.map((e) => e.toString());
 
 
   // filter
   // filter
-  const excluded = arr1.filter(e => !arr2.includes(e));
+  const excluded = arr1.filter((e) => !arr2.includes(e));
   // cast to ObjectId
   // cast to ObjectId
   const shouldReturnString = (arr: any[]): arr is string[] => {
   const shouldReturnString = (arr: any[]): arr is string[] => {
     return typeof arr[0] === 'string';
     return typeof arr[0] === 'string';
   };
   };
 
 
-  return shouldReturnString(targetIds) ? excluded : excluded.map(e => new ObjectId(e));
+  return shouldReturnString(targetIds)
+    ? excluded
+    : excluded.map((e) => new ObjectId(e));
 };
 };

+ 2 - 3
apps/app/src/server/util/createApiRouter.ts

@@ -1,4 +1,5 @@
 import express, { type Router } from 'express';
 import express, { type Router } from 'express';
+
 import CertifyOrigin from '~/server/middlewares/certify-origin';
 import CertifyOrigin from '~/server/middlewares/certify-origin';
 
 
 function createApiRouter(): Router {
 function createApiRouter(): Router {
@@ -7,6 +8,4 @@ function createApiRouter(): Router {
   return router;
   return router;
 }
 }
 
 
-export {
-  createApiRouter,
-};
+export { createApiRouter };

+ 7 - 6
apps/app/src/server/util/createGrowiPagesFromImports.js

@@ -14,7 +14,7 @@ module.exports = (crowi) => {
    *    user: Object
    *    user: Object
    * }]
    * }]
    */
    */
-  const createGrowiPages = async(pages) => {
+  const createGrowiPages = async (pages) => {
     const promises = [];
     const promises = [];
     const errors = [];
     const errors = [];
 
 
@@ -28,14 +28,15 @@ module.exports = (crowi) => {
 
 
       if (isCreatableName && !isPageNameTaken) {
       if (isCreatableName && !isPageNameTaken) {
         try {
         try {
-          const promise = crowi.pageService.create(path, body, user, { grant: Page.GRANT_PUBLIC, grantUserGroupId: null });
+          const promise = crowi.pageService.create(path, body, user, {
+            grant: Page.GRANT_PUBLIC,
+            grantUserGroupId: null,
+          });
           promises.push(promise);
           promises.push(promise);
-        }
-        catch (err) {
+        } catch (err) {
           errors.push(err);
           errors.push(err);
         }
         }
-      }
-      else {
+      } else {
         if (!isCreatableName) {
         if (!isCreatableName) {
           errors.push(new Error(`${path} is not a creatable name in GROWI`));
           errors.push(new Error(`${path} is not a creatable name in GROWI`));
         }
         }

+ 4 - 2
apps/app/src/server/util/createRedirectToForUnauthenticated.ts

@@ -1,6 +1,8 @@
-import { USER_STATUS, type IUserStatus } from '@growi/core';
+import { type IUserStatus, USER_STATUS } from '@growi/core';
 
 
-export const createRedirectToForUnauthenticated = (userStatus: IUserStatus): string | null => {
+export const createRedirectToForUnauthenticated = (
+  userStatus: IUserStatus,
+): string | null => {
   switch (userStatus) {
   switch (userStatus) {
     case USER_STATUS.REGISTERED:
     case USER_STATUS.REGISTERED:
       return '/login/error/registered';
       return '/login/error/registered';

+ 1 - 5
apps/app/src/server/util/formUtil.js

@@ -1,10 +1,6 @@
-
-
 module.exports = {
 module.exports = {
   normalizeCRLFFilter(value) {
   normalizeCRLFFilter(value) {
-    return value
-      .replace(/\r\n/g, '\n')
-      .replace(/\r/g, '\n');
+    return value.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
   },
   },
   stringToArrayFilter(value) {
   stringToArrayFilter(value) {
     if (!value || value === '') {
     if (!value || value === '') {

+ 3 - 3
apps/app/src/server/util/getToday.js

@@ -2,10 +2,10 @@
  * getToday
  * getToday
  */
  */
 
 
-module.exports = function() {
+module.exports = () => {
   const today = new Date();
   const today = new Date();
-  const month = (`0${today.getMonth() + 1}`).slice(-2);
-  const day = (`0${today.getDate()}`).slice(-2);
+  const month = `0${today.getMonth() + 1}`.slice(-2);
+  const day = `0${today.getDate()}`.slice(-2);
   const dateString = `${today.getFullYear()}/${month}/${day}`;
   const dateString = `${today.getFullYear()}/${month}/${day}`;
 
 
   return dateString;
   return dateString;

+ 5 - 4
apps/app/src/server/util/granted-group.ts

@@ -1,8 +1,10 @@
-import { type IGrantedGroup, GroupType } from '@growi/core';
+import { GroupType, type IGrantedGroup } from '@growi/core';
 
 
 import type { ObjectIdLike } from '../interfaces/mongoose-utils';
 import type { ObjectIdLike } from '../interfaces/mongoose-utils';
 
 
-export const divideByType = (grantedGroups: IGrantedGroup[] | null): {
+export const divideByType = (
+  grantedGroups: IGrantedGroup[] | null,
+): {
   grantedUserGroups: ObjectIdLike[];
   grantedUserGroups: ObjectIdLike[];
   grantedExternalUserGroups: ObjectIdLike[];
   grantedExternalUserGroups: ObjectIdLike[];
 } => {
 } => {
@@ -17,8 +19,7 @@ export const divideByType = (grantedGroups: IGrantedGroup[] | null): {
     const id = typeof group.item === 'string' ? group.item : group.item._id;
     const id = typeof group.item === 'string' ? group.item : group.item._id;
     if (group.type === GroupType.userGroup) {
     if (group.type === GroupType.userGroup) {
       grantedUserGroups.push(id);
       grantedUserGroups.push(id);
-    }
-    else {
+    } else {
       grantedExternalUserGroups.push(id);
       grantedExternalUserGroups.push(id);
     }
     }
   });
   });

+ 34 - 30
apps/app/src/server/util/importer.js

@@ -55,29 +55,33 @@ module.exports = (crowi) => {
    */
    */
   const importPostsFromEsa = (pageNum, user, errors) => {
   const importPostsFromEsa = (pageNum, user, errors) => {
     return new Promise((resolve, reject) => {
     return new Promise((resolve, reject) => {
-      esaClient.posts({ page: pageNum, per_page: 100 }).then(async(res) => {
-        const nextPage = res.next_page;
-        const postsReceived = res.posts;
-
-        const data = convertEsaDataForGrowi(postsReceived, user);
-        const newErrors = await createGrowiPages(data);
-
-        if (nextPage) {
-          return resolve(importPostsFromEsa(nextPage, user, errors.concat(newErrors)));
-        }
-
-        resolve(errors.concat(newErrors));
-
-      }).catch((err) => {
-        reject(new Error(`error in page ${pageNum}: ${err}`));
-      });
+      esaClient
+        .posts({ page: pageNum, per_page: 100 })
+        .then(async (res) => {
+          const nextPage = res.next_page;
+          const postsReceived = res.posts;
+
+          const data = convertEsaDataForGrowi(postsReceived, user);
+          const newErrors = await createGrowiPages(data);
+
+          if (nextPage) {
+            return resolve(
+              importPostsFromEsa(nextPage, user, errors.concat(newErrors)),
+            );
+          }
+
+          resolve(errors.concat(newErrors));
+        })
+        .catch((err) => {
+          reject(new Error(`error in page ${pageNum}: ${err}`));
+        });
     });
     });
   };
   };
 
 
   /**
   /**
    * Import page data from qiita to GROWI
    * Import page data from qiita to GROWI
    */
    */
-  importer.importDataFromQiita = async(user) => {
+  importer.importDataFromQiita = async (user) => {
     const firstPage = 1;
     const firstPage = 1;
     const errors = await importPostsFromQiita(firstPage, user, []);
     const errors = await importPostsFromQiita(firstPage, user, []);
     return errors;
     return errors;
@@ -87,7 +91,7 @@ module.exports = (crowi) => {
    * post page data from qiita and create GROWI page
    * post page data from qiita and create GROWI page
    * @param {string} pageNum default value is '1'
    * @param {string} pageNum default value is '1'
    */
    */
-  const importPostsFromQiita = async(pageNum, user, errors) => {
+  const importPostsFromQiita = async (pageNum, user, errors) => {
     const perPage = '100';
     const perPage = '100';
     const res = await crowi.restQiitaAPIService.getQiitaPages(pageNum, perPage);
     const res = await crowi.restQiitaAPIService.getQiitaPages(pageNum, perPage);
     const next = pageNum * perPage;
     const next = pageNum * perPage;
@@ -116,11 +120,9 @@ module.exports = (crowi) => {
 
 
       if (category && name) {
       if (category && name) {
         path = `${category}/${name}`;
         path = `${category}/${name}`;
-      }
-      else if (category) {
+      } else if (category) {
         path = category;
         path = category;
-      }
-      else if (name) {
+      } else if (name) {
         path = name;
         path = name;
       }
       }
 
 
@@ -156,14 +158,14 @@ module.exports = (crowi) => {
   /**
   /**
    * Import page data from esa to GROWI
    * Import page data from esa to GROWI
    */
    */
-  importer.testConnectionToEsa = async() => {
+  importer.testConnectionToEsa = async () => {
     await getTeamNameFromEsa();
     await getTeamNameFromEsa();
   };
   };
 
 
   /**
   /**
    * Import page data from qiita to GROWI
    * Import page data from qiita to GROWI
    */
    */
-  importer.testConnectionToQiita = async() => {
+  importer.testConnectionToQiita = async () => {
     await crowi.restQiitaAPIService.getQiitaUser();
     await crowi.restQiitaAPIService.getQiitaUser();
   };
   };
 
 
@@ -173,12 +175,14 @@ module.exports = (crowi) => {
   const getTeamNameFromEsa = () => {
   const getTeamNameFromEsa = () => {
     return new Promise((resolve, reject) => {
     return new Promise((resolve, reject) => {
       const team = configManager.getConfig('importer:esa:team_name');
       const team = configManager.getConfig('importer:esa:team_name');
-      esaClient.team(team).then((res) => {
-        resolve(res);
-      }).catch((err) => {
-        return reject(err);
-      });
-
+      esaClient
+        .team(team)
+        .then((res) => {
+          resolve(res);
+        })
+        .catch((err) => {
+          return reject(err);
+        });
     });
     });
   };
   };
 
 

+ 13 - 19
apps/app/src/server/util/is-simple-request.spec.ts

@@ -4,11 +4,8 @@ import { mock } from 'vitest-mock-extended';
 import isSimpleRequest from './is-simple-request';
 import isSimpleRequest from './is-simple-request';
 
 
 describe('isSimpleRequest', () => {
 describe('isSimpleRequest', () => {
-
-
   // method
   // method
   describe('When request method is checked', () => {
   describe('When request method is checked', () => {
-
     // allow
     // allow
     describe('When allowed method is given', () => {
     describe('When allowed method is given', () => {
       const allowedMethods = ['GET', 'HEAD', 'POST'];
       const allowedMethods = ['GET', 'HEAD', 'POST'];
@@ -30,13 +27,10 @@ describe('isSimpleRequest', () => {
         expect(isSimpleRequest(reqMock)).toBe(false);
         expect(isSimpleRequest(reqMock)).toBe(false);
       });
       });
     });
     });
-
   });
   });
 
 
-
   // headers
   // headers
   describe('When request headers are checked', () => {
   describe('When request headers are checked', () => {
-
     // allow(Other than content-type)
     // allow(Other than content-type)
     describe('When only safe headers are given', () => {
     describe('When only safe headers are given', () => {
       const safeHeaders = [
       const safeHeaders = [
@@ -94,13 +88,16 @@ describe('isSimpleRequest', () => {
         'X-Requested-With',
         'X-Requested-With',
         'X-CSRF-Token',
         'X-CSRF-Token',
       ];
       ];
-      it.each(unsafeHeaders)('returns false for unsafe header: %s', (headerName) => {
-        const reqMock = mock<Request>({
-          method: 'POST',
-          headers: { [headerName]: 'test-value' },
-        });
-        expect(isSimpleRequest(reqMock)).toBe(false);
-      });
+      it.each(unsafeHeaders)(
+        'returns false for unsafe header: %s',
+        (headerName) => {
+          const reqMock = mock<Request>({
+            method: 'POST',
+            headers: { [headerName]: 'test-value' },
+          });
+          expect(isSimpleRequest(reqMock)).toBe(false);
+        },
+      );
       // combination
       // combination
       it('returns false when safe and unsafe headers are mixed', () => {
       it('returns false when safe and unsafe headers are mixed', () => {
         const reqMock = mock<Request>();
         const reqMock = mock<Request>();
@@ -112,13 +109,10 @@ describe('isSimpleRequest', () => {
         expect(isSimpleRequest(reqMock)).toBe(false);
         expect(isSimpleRequest(reqMock)).toBe(false);
       });
       });
     });
     });
-
   });
   });
 
 
-
   // content-type
   // content-type
   describe('When content-type is checked', () => {
   describe('When content-type is checked', () => {
-
     // allow
     // allow
     describe('When a safe content-type is given', () => {
     describe('When a safe content-type is given', () => {
       const safeContentTypes = [
       const safeContentTypes = [
@@ -164,17 +158,17 @@ describe('isSimpleRequest', () => {
         expect(isSimpleRequest(reqMock)).toBe(false);
         expect(isSimpleRequest(reqMock)).toBe(false);
       });
       });
     });
     });
-
   });
   });
 
 
   // integration
   // integration
   describe('When multiple conditions are checked', () => {
   describe('When multiple conditions are checked', () => {
-
     describe('When all conditions are met', () => {
     describe('When all conditions are met', () => {
       it('returns true', () => {
       it('returns true', () => {
         const reqMock = mock<Request>();
         const reqMock = mock<Request>();
         reqMock.method = 'POST';
         reqMock.method = 'POST';
-        reqMock.headers = { 'content-type': 'application/x-www-form-urlencoded' };
+        reqMock.headers = {
+          'content-type': 'application/x-www-form-urlencoded',
+        };
         expect(isSimpleRequest(reqMock)).toBe(true);
         expect(isSimpleRequest(reqMock)).toBe(true);
       });
       });
     });
     });

+ 9 - 5
apps/app/src/server/util/is-simple-request.ts

@@ -3,7 +3,7 @@ import type { Request } from 'express';
 import type { AccessTokenParserReq } from '~/server/middlewares/access-token-parser/interfaces';
 import type { AccessTokenParserReq } from '~/server/middlewares/access-token-parser/interfaces';
 
 
 const allowedMethods = ['GET', 'HEAD', 'POST'] as const;
 const allowedMethods = ['GET', 'HEAD', 'POST'] as const;
-type AllowedMethod = typeof allowedMethods[number];
+type AllowedMethod = (typeof allowedMethods)[number];
 function isAllowedMethod(method: string): method is AllowedMethod {
 function isAllowedMethod(method: string): method is AllowedMethod {
   return allowedMethods.includes(method as AllowedMethod);
   return allowedMethods.includes(method as AllowedMethod);
 }
 }
@@ -21,7 +21,7 @@ const safeRequestHeaders = [
   'viewport-width',
   'viewport-width',
   'width',
   'width',
 ] as const;
 ] as const;
-type SafeRequestHeader = typeof safeRequestHeaders[number];
+type SafeRequestHeader = (typeof safeRequestHeaders)[number];
 
 
 function isSafeRequestHeader(header: string): header is SafeRequestHeader {
 function isSafeRequestHeader(header: string): header is SafeRequestHeader {
   return safeRequestHeaders.includes(header.toLowerCase() as SafeRequestHeader);
   return safeRequestHeaders.includes(header.toLowerCase() as SafeRequestHeader);
@@ -32,10 +32,14 @@ const allowedContentTypes = [
   'multipart/form-data',
   'multipart/form-data',
   'text/plain',
   'text/plain',
 ] as const;
 ] as const;
-type AllowedContentType = typeof allowedContentTypes[number];
+type AllowedContentType = (typeof allowedContentTypes)[number];
 
 
-function isAllowedContentType(contentType: string): contentType is AllowedContentType {
-  return allowedContentTypes.some(allowed => contentType.toLowerCase().startsWith(allowed));
+function isAllowedContentType(
+  contentType: string,
+): contentType is AllowedContentType {
+  return allowedContentTypes.some((allowed) =>
+    contentType.toLowerCase().startsWith(allowed),
+  );
 }
 }
 
 
 const isSimpleRequest = (req: Request | AccessTokenParserReq): boolean => {
 const isSimpleRequest = (req: Request | AccessTokenParserReq): boolean => {

+ 12 - 9
apps/app/src/server/util/locale-utils.ts

@@ -1,6 +1,5 @@
-import type { IncomingHttpHeaders } from 'http';
-
 import { Lang } from '@growi/core/dist/interfaces';
 import { Lang } from '@growi/core/dist/interfaces';
+import type { IncomingHttpHeaders } from 'http';
 
 
 import * as i18nextConfig from '^/config/i18next.config';
 import * as i18nextConfig from '^/config/i18next.config';
 
 
@@ -18,17 +17,21 @@ const ACCEPT_LANG_MAP = {
  */
  */
 const getPreferredLanguage = (sortedAcceptLanguagesArray: string[]): Lang => {
 const getPreferredLanguage = (sortedAcceptLanguagesArray: string[]): Lang => {
   for (const lang of sortedAcceptLanguagesArray) {
   for (const lang of sortedAcceptLanguagesArray) {
-    const matchingLang = Object.keys(ACCEPT_LANG_MAP).find(key => lang.includes(key));
+    const matchingLang = Object.keys(ACCEPT_LANG_MAP).find((key) =>
+      lang.includes(key),
+    );
     if (matchingLang) return ACCEPT_LANG_MAP[matchingLang];
     if (matchingLang) return ACCEPT_LANG_MAP[matchingLang];
   }
   }
   return i18nextConfig.defaultLang;
   return i18nextConfig.defaultLang;
 };
 };
 
 
 /**
 /**
-  * Detect locale from browser accept language
-  * @param headers
-  */
-export const detectLocaleFromBrowserAcceptLanguage = (headers: IncomingHttpHeaders): Lang => {
+ * Detect locale from browser accept language
+ * @param headers
+ */
+export const detectLocaleFromBrowserAcceptLanguage = (
+  headers: IncomingHttpHeaders,
+): Lang => {
   // 1. get the header accept-language
   // 1. get the header accept-language
   // ex. "ja,ar-SA;q=0.8,en;q=0.6,en-CA;q=0.4,en-US;q=0.2"
   // ex. "ja,ar-SA;q=0.8,en;q=0.6,en-CA;q=0.4,en-US;q=0.2"
   const acceptLanguages = headers['accept-language'];
   const acceptLanguages = headers['accept-language'];
@@ -45,7 +48,7 @@ export const detectLocaleFromBrowserAcceptLanguage = (headers: IncomingHttpHeade
   const acceptLanguagesDict = acceptLanguages
   const acceptLanguagesDict = acceptLanguages
     .replace(/\s+/g, '')
     .replace(/\s+/g, '')
     .split(',')
     .split(',')
-    .map(item => item.split(/\s*;\s*q\s*=\s*/))
+    .map((item) => item.split(/\s*;\s*q\s*=\s*/))
     .reduce((acc, [key, value = '1']) => {
     .reduce((acc, [key, value = '1']) => {
       acc[value] = key;
       acc[value] = key;
       return acc;
       return acc;
@@ -55,7 +58,7 @@ export const detectLocaleFromBrowserAcceptLanguage = (headers: IncomingHttpHeade
   // ex. [ 'ja', 'ar-SA', 'en', 'en-CA', 'en-US' ]
   // ex. [ 'ja', 'ar-SA', 'en', 'en-CA', 'en-US' ]
   const sortedAcceptLanguagesArray = Object.keys(acceptLanguagesDict)
   const sortedAcceptLanguagesArray = Object.keys(acceptLanguagesDict)
     .sort((x, y) => y.localeCompare(x))
     .sort((x, y) => y.localeCompare(x))
-    .map(item => acceptLanguagesDict[item]);
+    .map((item) => acceptLanguagesDict[item]);
 
 
   return getPreferredLanguage(sortedAcceptLanguagesArray);
   return getPreferredLanguage(sortedAcceptLanguagesArray);
 };
 };

+ 30 - 14
apps/app/src/server/util/mongoose-utils.ts

@@ -1,33 +1,49 @@
+import type { ConnectOptions, Document, Model } from 'mongoose';
 import mongoose from 'mongoose';
 import mongoose from 'mongoose';
-import type {
-  Model, Document, ConnectOptions,
-} from 'mongoose';
 
 
 // suppress DeprecationWarning: current Server Discovery and Monitoring engine is deprecated, and will be removed in a future version
 // suppress DeprecationWarning: current Server Discovery and Monitoring engine is deprecated, and will be removed in a future version
 type ConnectionOptionsExtend = {
 type ConnectionOptionsExtend = {
-  useUnifiedTopology: boolean
-}
+  useUnifiedTopology: boolean;
+};
 
 
 export const getMongoUri = (): string => {
 export const getMongoUri = (): string => {
   const { env } = process;
   const { env } = process;
 
 
-  return env.MONGOLAB_URI // for B.C.
-    || env.MONGODB_URI // MONGOLAB changes their env name
-    || env.MONGOHQ_URL
-    || env.MONGO_URI
-    || ((env.NODE_ENV === 'test') ? 'mongodb://mongo/growi_test' : 'mongodb://mongo/growi');
+  return (
+    env.MONGOLAB_URI || // for B.C.
+    env.MONGODB_URI || // MONGOLAB changes their env name
+    env.MONGOHQ_URL ||
+    env.MONGO_URI ||
+    (env.NODE_ENV === 'test'
+      ? 'mongodb://mongo/growi_test'
+      : 'mongodb://mongo/growi')
+  );
 };
 };
 
 
-export const getModelSafely = <Interface, Method = Interface>(modelName: string): Method & Model<Interface & Document> | null => {
+export const getModelSafely = <Interface, Method = Interface>(
+  modelName: string,
+): (Method & Model<Interface & Document>) | null => {
   if (mongoose.modelNames().includes(modelName)) {
   if (mongoose.modelNames().includes(modelName)) {
-    return mongoose.model<Interface & Document, Method & Model<Interface & Document>>(modelName);
+    return mongoose.model<
+      Interface & Document,
+      Method & Model<Interface & Document>
+    >(modelName);
   }
   }
   return null;
   return null;
 };
 };
 
 
 // TODO: Do not use any type
 // TODO: Do not use any type
-export const getOrCreateModel = <Interface, Method>(modelName: string, schema: any): Method & Model<Interface & Document> => {
-  return getModelSafely(modelName) ?? mongoose.model<Interface & Document, Method & Model<Interface & Document>>(modelName, schema);
+export const getOrCreateModel = <Interface, Method>(
+  modelName: string,
+  schema: any,
+): Method & Model<Interface & Document> => {
+  return (
+    getModelSafely(modelName) ??
+    mongoose.model<Interface & Document, Method & Model<Interface & Document>>(
+      modelName,
+      schema,
+    )
+  );
 };
 };
 
 
 // supress deprecation warnings
 // supress deprecation warnings

+ 0 - 1
apps/app/src/server/util/project-dir-utils.ts

@@ -1,7 +1,6 @@
 import fs from 'node:fs';
 import fs from 'node:fs';
 import path from 'node:path';
 import path from 'node:path';
 import process from 'node:process';
 import process from 'node:process';
-
 import { isServer } from '@growi/core/dist/utils/browser-utils';
 import { isServer } from '@growi/core/dist/utils/browser-utils';
 
 
 const isCurrentDirRoot = isServer() && fs.existsSync('./next.config.js');
 const isCurrentDirRoot = isServer() && fs.existsSync('./next.config.js');

+ 6 - 6
apps/app/src/server/util/runtime-versions.ts

@@ -6,19 +6,18 @@ type RuntimeVersions = {
   pnpm: string | undefined;
   pnpm: string | undefined;
 };
 };
 
 
-
 // define original types because the object returned is not according to the official type definition
 // define original types because the object returned is not according to the official type definition
 type SatisfiedVersionInfo = {
 type SatisfiedVersionInfo = {
   isSatisfied: true;
   isSatisfied: true;
   version: {
   version: {
     version: string;
     version: string;
-  }
-}
+  };
+};
 
 
 type NotfoundVersionInfo = {
 type NotfoundVersionInfo = {
   isSatisfied: true;
   isSatisfied: true;
   notfound: true;
   notfound: true;
-}
+};
 
 
 type VersionInfo = SatisfiedVersionInfo | NotfoundVersionInfo;
 type VersionInfo = SatisfiedVersionInfo | NotfoundVersionInfo;
 
 
@@ -26,7 +25,9 @@ function isNotfoundVersionInfo(info: VersionInfo): info is NotfoundVersionInfo {
   return 'notfound' in info;
   return 'notfound' in info;
 }
 }
 
 
-function isSatisfiedVersionInfo(info: VersionInfo): info is SatisfiedVersionInfo {
+function isSatisfiedVersionInfo(
+  info: VersionInfo,
+): info is SatisfiedVersionInfo {
   return 'version' in info;
   return 'version' in info;
 }
 }
 
 
@@ -42,7 +43,6 @@ const getVersion = (versionInfo: VersionInfo): string | undefined => {
   return undefined;
   return undefined;
 };
 };
 
 
-
 export function getRuntimeVersions(): Promise<RuntimeVersions> {
 export function getRuntimeVersions(): Promise<RuntimeVersions> {
   return new Promise((resolve, reject) => {
   return new Promise((resolve, reject) => {
     checkNodeVersion({}, (error, result) => {
     checkNodeVersion({}, (error, result) => {

+ 14 - 6
apps/app/src/server/util/scope-util.spec.ts

@@ -1,16 +1,20 @@
 import { SCOPE } from '@growi/core/dist/interfaces';
 import { SCOPE } from '@growi/core/dist/interfaces';
-import { describe, it, expect } from 'vitest';
-
+import { describe, expect, it } from 'vitest';
 
 
 import {
 import {
-  isValidScope, hasAllScope, extractAllScope, extractScopes,
+  extractAllScope,
+  extractScopes,
+  hasAllScope,
+  isValidScope,
 } from './scope-utils';
 } from './scope-utils';
 
 
 describe('scope-utils', () => {
 describe('scope-utils', () => {
   describe('isValidScope', () => {
   describe('isValidScope', () => {
     it('should return true for valid scopes', () => {
     it('should return true for valid scopes', () => {
       expect(isValidScope(SCOPE.READ.USER_SETTINGS.API.API_TOKEN)).toBe(true);
       expect(isValidScope(SCOPE.READ.USER_SETTINGS.API.API_TOKEN)).toBe(true);
-      expect(isValidScope(SCOPE.WRITE.USER_SETTINGS.API.ACCESS_TOKEN)).toBe(true);
+      expect(isValidScope(SCOPE.WRITE.USER_SETTINGS.API.ACCESS_TOKEN)).toBe(
+        true,
+      );
       expect(isValidScope(SCOPE.READ.ADMIN.APP)).toBe(true);
       expect(isValidScope(SCOPE.READ.ADMIN.APP)).toBe(true);
     });
     });
 
 
@@ -29,7 +33,9 @@ describe('scope-utils', () => {
 
 
     it('should return false for specific scopes', () => {
     it('should return false for specific scopes', () => {
       expect(hasAllScope(SCOPE.READ.USER_SETTINGS.API.API_TOKEN)).toBe(false);
       expect(hasAllScope(SCOPE.READ.USER_SETTINGS.API.API_TOKEN)).toBe(false);
-      expect(hasAllScope(SCOPE.WRITE.USER_SETTINGS.API.ACCESS_TOKEN)).toBe(false);
+      expect(hasAllScope(SCOPE.WRITE.USER_SETTINGS.API.ACCESS_TOKEN)).toBe(
+        false,
+      );
     });
     });
   });
   });
 
 
@@ -83,7 +89,9 @@ describe('scope-utils', () => {
       ];
       ];
       const extracted = extractScopes(scopes);
       const extracted = extractScopes(scopes);
 
 
-      const accessTokenScopes = extracted.filter(s => s.endsWith('access_token'));
+      const accessTokenScopes = extracted.filter((s) =>
+        s.endsWith('access_token'),
+      );
       expect(accessTokenScopes).toHaveLength(2); // Only READ and WRITE, no duplicates
       expect(accessTokenScopes).toHaveLength(2); // Only READ and WRITE, no duplicates
     });
     });
   });
   });

+ 9 - 5
apps/app/src/server/util/scope-utils.ts

@@ -1,9 +1,14 @@
 import {
 import {
-  ACTION, ALL_SIGN, SCOPE, type Scope,
+  ACTION,
+  ALL_SIGN,
+  SCOPE,
+  type Scope,
 } from '@growi/core/dist/interfaces';
 } from '@growi/core/dist/interfaces';
 
 
 export const isValidScope = (scope: Scope): boolean => {
 export const isValidScope = (scope: Scope): boolean => {
-  const scopeParts = scope.split(':').map(x => (x === ALL_SIGN ? 'ALL' : x.toUpperCase()));
+  const scopeParts = scope
+    .split(':')
+    .map((x) => (x === ALL_SIGN ? 'ALL' : x.toUpperCase()));
   let obj: any = SCOPE;
   let obj: any = SCOPE;
   scopeParts.forEach((part) => {
   scopeParts.forEach((part) => {
     if (obj[part] == null) {
     if (obj[part] == null) {
@@ -60,7 +65,7 @@ export const extractAllScope = (scope: Scope): Scope[] => {
     return [scope];
     return [scope];
   }
   }
   const result = [] as Scope[];
   const result = [] as Scope[];
-  const scopeParts = scope.split(':').map(x => (x.toUpperCase()));
+  const scopeParts = scope.split(':').map((x) => x.toUpperCase());
   let obj: any = SCOPE;
   let obj: any = SCOPE;
   scopeParts.forEach((part) => {
   scopeParts.forEach((part) => {
     if (part === ALL_SIGN) {
     if (part === ALL_SIGN) {
@@ -71,10 +76,9 @@ export const extractAllScope = (scope: Scope): Scope[] => {
   getAllScopeValuesFromObj(obj).forEach((value) => {
   getAllScopeValuesFromObj(obj).forEach((value) => {
     result.push(value);
     result.push(value);
   });
   });
-  return result.filter(scope => !hasAllScope(scope));
+  return result.filter((scope) => !hasAllScope(scope));
 };
 };
 
 
-
 /**
 /**
  * Extracts scopes from a given array of scopes
  * Extracts scopes from a given array of scopes
  * And delete all scopes
  * And delete all scopes

+ 4 - 2
apps/app/src/server/util/slack-integration.ts

@@ -2,10 +2,12 @@ import type { IChannelOptionalId } from '@growi/slack';
 import { getSupportedGrowiActionsRegExp } from '@growi/slack/dist/utils/get-supported-growi-actions-regexps';
 import { getSupportedGrowiActionsRegExp } from '@growi/slack/dist/utils/get-supported-growi-actions-regexps';
 import { permissionParser } from '@growi/slack/dist/utils/permission-parser';
 import { permissionParser } from '@growi/slack/dist/utils/permission-parser';
 
 
-type CommandPermission = { [key:string]: string[] | boolean }
+type CommandPermission = { [key: string]: string[] | boolean };
 
 
 export const checkPermission = (
 export const checkPermission = (
-    commandPermission: CommandPermission, commandOrActionIdOrCallbackId: string, fromChannel: IChannelOptionalId,
+  commandPermission: CommandPermission,
+  commandOrActionIdOrCallbackId: string,
+  fromChannel: IChannelOptionalId,
 ): boolean => {
 ): boolean => {
   let isPermitted = false;
   let isPermitted = false;
 
 

+ 15 - 12
apps/app/src/server/util/slack-legacy.ts

@@ -1,37 +1,40 @@
 import type { ChatPostMessageArguments } from '@slack/web-api';
 import type { ChatPostMessageArguments } from '@slack/web-api';
 import { WebClient } from '@slack/web-api';
 import { WebClient } from '@slack/web-api';
-import { IncomingWebhook, type IncomingWebhookSendArguments } from '@slack/webhook';
+import {
+  IncomingWebhook,
+  type IncomingWebhookSendArguments,
+} from '@slack/webhook';
 
 
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
 
 
 const logger = loggerFactory('growi:util:slack-legacy');
 const logger = loggerFactory('growi:util:slack-legacy');
 
 
-
 interface SlackLegacyUtil {
 interface SlackLegacyUtil {
-  postMessage(messageObj: IncomingWebhookSendArguments | ChatPostMessageArguments): Promise<void>,
+  postMessage(
+    messageObj: IncomingWebhookSendArguments | ChatPostMessageArguments,
+  ): Promise<void>;
 }
 }
 
 
 // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
 // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
 export const slackLegacyUtilFactory = (configManager: any): SlackLegacyUtil => {
 export const slackLegacyUtilFactory = (configManager: any): SlackLegacyUtil => {
-
-  const postWithIwh = async(messageObj: IncomingWebhookSendArguments) => {
-    const webhook = new IncomingWebhook(configManager.getConfig('slack:incomingWebhookUrl'));
+  const postWithIwh = async (messageObj: IncomingWebhookSendArguments) => {
+    const webhook = new IncomingWebhook(
+      configManager.getConfig('slack:incomingWebhookUrl'),
+    );
     try {
     try {
       await webhook.send(messageObj);
       await webhook.send(messageObj);
-    }
-    catch (error) {
+    } catch (error) {
       logger.debug('Post error', error);
       logger.debug('Post error', error);
       logger.debug('Sent data to slack is:', messageObj);
       logger.debug('Sent data to slack is:', messageObj);
       throw error;
       throw error;
     }
     }
   };
   };
 
 
-  const postWithWebApi = async(messageObj?: ChatPostMessageArguments) => {
+  const postWithWebApi = async (messageObj?: ChatPostMessageArguments) => {
     const client = new WebClient(configManager.getConfig('slack:token'));
     const client = new WebClient(configManager.getConfig('slack:token'));
     try {
     try {
       await client.chat.postMessage(messageObj);
       await client.chat.postMessage(messageObj);
-    }
-    catch (error) {
+    } catch (error) {
       logger.debug('Post error', error);
       logger.debug('Post error', error);
       logger.debug('Sent data to slack is:', messageObj);
       logger.debug('Sent data to slack is:', messageObj);
       throw error;
       throw error;
@@ -39,7 +42,7 @@ export const slackLegacyUtilFactory = (configManager: any): SlackLegacyUtil => {
   };
   };
 
 
   return {
   return {
-    postMessage: async(messageObj) => {
+    postMessage: async (messageObj) => {
       // when incoming Webhooks is prioritized
       // when incoming Webhooks is prioritized
       if (configManager.getConfig('slack:isIncomingWebhookPrioritized')) {
       if (configManager.getConfig('slack:isIncomingWebhookPrioritized')) {
         if (configManager.getConfig('slack:incomingWebhookUrl')) {
         if (configManager.getConfig('slack:incomingWebhookUrl')) {

+ 55 - 29
apps/app/src/server/util/slack.js

@@ -10,15 +10,14 @@ const logger = loggerFactory('growi:util:slack');
 
 
 /* eslint-disable no-use-before-define */
 /* eslint-disable no-use-before-define */
 
 
-const convertMarkdownToMarkdown = function(body, siteUrl) {
-  return body
+const convertMarkdownToMarkdown = (body, siteUrl) =>
+  body
     .replace(/\n\*\s(.+)/g, '\n• $1')
     .replace(/\n\*\s(.+)/g, '\n• $1')
     .replace(/#{1,}\s?(.+)/g, '\n*$1*')
     .replace(/#{1,}\s?(.+)/g, '\n*$1*')
     .replace(/(\[(.+)\]\((https?:\/\/.+)\))/g, '<$3|$2>')
     .replace(/(\[(.+)\]\((https?:\/\/.+)\))/g, '<$3|$2>')
     .replace(/(\[(.+)\]\((\/.+)\))/g, `<${siteUrl}$3|$2>`);
     .replace(/(\[(.+)\]\((\/.+)\))/g, `<${siteUrl}$3|$2>`);
-};
 
 
-const prepareAttachmentTextForCreate = function(page, siteUrl) {
+const prepareAttachmentTextForCreate = (page, siteUrl) => {
   let body = page.revision.body;
   let body = page.revision.body;
   if (body.length > 2000) {
   if (body.length > 2000) {
     body = `${body.substr(0, 2000)}...`;
     body = `${body.substr(0, 2000)}...`;
@@ -33,7 +32,7 @@ const prepareAttachmentTextForCreate = function(page, siteUrl) {
  * @param {string} siteUrl
  * @param {string} siteUrl
  * @param {IRevisionHasId} previousRevision
  * @param {IRevisionHasId} previousRevision
  */
  */
-const prepareAttachmentTextForUpdate = function(page, siteUrl, previousRevision) {
+const prepareAttachmentTextForUpdate = (page, siteUrl, previousRevision) => {
   if (previousRevision == null) {
   if (previousRevision == null) {
     return;
     return;
   }
   }
@@ -46,15 +45,13 @@ const prepareAttachmentTextForUpdate = function(page, siteUrl, previousRevision)
     const value = line.value.replace(/\r\n|\r/g, '\n'); // eslint-disable-line no-unused-vars
     const value = line.value.replace(/\r\n|\r/g, '\n'); // eslint-disable-line no-unused-vars
     if (line.added) {
     if (line.added) {
       diffText += `${line.value} ... :lower_left_fountain_pen:`;
       diffText += `${line.value} ... :lower_left_fountain_pen:`;
-    }
-    else if (line.removed) {
+    } else if (line.removed) {
       // diffText += '-' + line.value.replace(/(.+)?\n/g, '- $1\n');
       // diffText += '-' + line.value.replace(/(.+)?\n/g, '- $1\n');
       // 1以下は無視
       // 1以下は無視
       if (line.count > 1) {
       if (line.count > 1) {
         diffText += `:wastebasket: ... ${line.count} lines\n`;
         diffText += `:wastebasket: ... ${line.count} lines\n`;
       }
       }
-    }
-    else {
+    } else {
       // diffText += '...\n';
       // diffText += '...\n';
     }
     }
   });
   });
@@ -64,7 +61,7 @@ const prepareAttachmentTextForUpdate = function(page, siteUrl, previousRevision)
   return diffText;
   return diffText;
 };
 };
 
 
-const prepareAttachmentTextForComment = function(comment) {
+const prepareAttachmentTextForComment = (comment) => {
   let body = comment.comment;
   let body = comment.comment;
   if (body.length > 2000) {
   if (body.length > 2000) {
     body = `${body.substr(0, 2000)}...`;
     body = `${body.substr(0, 2000)}...`;
@@ -77,27 +74,39 @@ const prepareAttachmentTextForComment = function(comment) {
   return body;
   return body;
 };
 };
 
 
-const generateSlackMessageTextForPage = function(path, pageId, user, siteUrl, updateType) {
+const generateSlackMessageTextForPage = (
+  path,
+  pageId,
+  user,
+  siteUrl,
+  updateType,
+) => {
   let text;
   let text;
 
 
   const pageUrl = `<${urljoin(siteUrl, pageId)}|${path}>`;
   const pageUrl = `<${urljoin(siteUrl, pageId)}|${path}>`;
   if (updateType === 'create') {
   if (updateType === 'create') {
     text = `:rocket: ${user.username} created a new page! ${pageUrl}`;
     text = `:rocket: ${user.username} created a new page! ${pageUrl}`;
-  }
-  else {
+  } else {
     text = `:heavy_check_mark: ${user.username} updated ${pageUrl}`;
     text = `:heavy_check_mark: ${user.username} updated ${pageUrl}`;
   }
   }
 
 
   return text;
   return text;
 };
 };
 
 
-export const prepareSlackMessageForPage = (page, user, appTitle, siteUrl, channel, updateType, previousRevision) => {
+export const prepareSlackMessageForPage = (
+  page,
+  user,
+  appTitle,
+  siteUrl,
+  channel,
+  updateType,
+  previousRevision,
+) => {
   let body = page.revision.body;
   let body = page.revision.body;
 
 
   if (updateType === 'create') {
   if (updateType === 'create') {
     body = prepareAttachmentTextForCreate(page, siteUrl);
     body = prepareAttachmentTextForCreate(page, siteUrl);
-  }
-  else {
+  } else {
     body = prepareAttachmentTextForUpdate(page, siteUrl, previousRevision);
     body = prepareAttachmentTextForUpdate(page, siteUrl, previousRevision);
   }
   }
 
 
@@ -116,16 +125,29 @@ export const prepareSlackMessageForPage = (page, user, appTitle, siteUrl, channe
   }
   }
 
 
   const message = {
   const message = {
-    channel: (channel != null) ? `#${channel}` : undefined,
+    channel: channel != null ? `#${channel}` : undefined,
     username: appTitle,
     username: appTitle,
-    text: generateSlackMessageTextForPage(page.path, page.id, user, siteUrl, updateType),
+    text: generateSlackMessageTextForPage(
+      page.path,
+      page.id,
+      user,
+      siteUrl,
+      updateType,
+    ),
     attachments: [attachment],
     attachments: [attachment],
   };
   };
 
 
   return message;
   return message;
 };
 };
 
 
-export const prepareSlackMessageForComment = (comment, user, appTitle, siteUrl, channel, path) => {
+export const prepareSlackMessageForComment = (
+  comment,
+  user,
+  appTitle,
+  siteUrl,
+  channel,
+  path,
+) => {
   const body = prepareAttachmentTextForComment(comment);
   const body = prepareAttachmentTextForComment(comment);
 
 
   const attachment = {
   const attachment = {
@@ -144,7 +166,7 @@ export const prepareSlackMessageForComment = (comment, user, appTitle, siteUrl,
   const text = `:speech_balloon: ${user.username} commented on ${pageUrl}`;
   const text = `:speech_balloon: ${user.username} commented on ${pageUrl}`;
 
 
   const message = {
   const message = {
-    channel: (channel != null) ? `#${channel}` : undefined,
+    channel: channel != null ? `#${channel}` : undefined,
     username: appTitle,
     username: appTitle,
     text,
     text,
     attachments: [attachment],
     attachments: [attachment],
@@ -154,14 +176,18 @@ export const prepareSlackMessageForComment = (comment, user, appTitle, siteUrl,
 };
 };
 
 
 /**
 /**
-   * For GlobalNotification
-   *
-   * @param {string} messageBody
-   * @param {string} attachmentBody
-   * @param {string} slackChannel
-  */
-export const prepareSlackMessageForGlobalNotification = (messageBody, attachmentBody, appTitle, slackChannel) => {
-
+ * For GlobalNotification
+ *
+ * @param {string} messageBody
+ * @param {string} attachmentBody
+ * @param {string} slackChannel
+ */
+export const prepareSlackMessageForGlobalNotification = (
+  messageBody,
+  attachmentBody,
+  appTitle,
+  slackChannel,
+) => {
   const attachment = {
   const attachment = {
     color: '#263a3c',
     color: '#263a3c',
     text: attachmentBody,
     text: attachmentBody,
@@ -169,7 +195,7 @@ export const prepareSlackMessageForGlobalNotification = (messageBody, attachment
   };
   };
 
 
   const message = {
   const message = {
-    channel: (slackChannel != null) ? `#${slackChannel}` : undefined,
+    channel: slackChannel != null ? `#${slackChannel}` : undefined,
     username: appTitle,
     username: appTitle,
     text: messageBody,
     text: messageBody,
     attachments: JSON.stringify([attachment]),
     attachments: JSON.stringify([attachment]),

+ 13 - 4
apps/app/src/server/util/stream.spec.ts

@@ -1,4 +1,4 @@
-import { Readable, Writable, pipeline } from 'stream';
+import { pipeline, Readable, Writable } from 'stream';
 import { promisify } from 'util';
 import { promisify } from 'util';
 
 
 import { getBufferToFixedSizeTransform } from './stream';
 import { getBufferToFixedSizeTransform } from './stream';
@@ -7,11 +7,16 @@ const pipelinePromise = promisify(pipeline);
 
 
 describe('stream util', () => {
 describe('stream util', () => {
   describe('getBufferToFixedSizeTransform', () => {
   describe('getBufferToFixedSizeTransform', () => {
-    it('should buffer data to fixed size and push to next stream', async() => {
+    it('should buffer data to fixed size and push to next stream', async () => {
       const bufferSize = 10;
       const bufferSize = 10;
       const chunks: Buffer[] = [];
       const chunks: Buffer[] = [];
 
 
-      const readable = Readable.from([Buffer.from('1234567890A'), Buffer.from('BCDE'), Buffer.from('FGH'), Buffer.from('IJKL')]);
+      const readable = Readable.from([
+        Buffer.from('1234567890A'),
+        Buffer.from('BCDE'),
+        Buffer.from('FGH'),
+        Buffer.from('IJKL'),
+      ]);
       const transform = getBufferToFixedSizeTransform(bufferSize);
       const transform = getBufferToFixedSizeTransform(bufferSize);
       const writable = new Writable({
       const writable = new Writable({
         write(chunk: Buffer, encoding, callback) {
         write(chunk: Buffer, encoding, callback) {
@@ -20,7 +25,11 @@ describe('stream util', () => {
         },
         },
       });
       });
 
 
-      const expectedChunks = [Buffer.from('1234567890'), Buffer.from('ABCDEFGHIJ'), Buffer.from('KL')];
+      const expectedChunks = [
+        Buffer.from('1234567890'),
+        Buffer.from('ABCDEFGHIJ'),
+        Buffer.from('KL'),
+      ];
 
 
       await pipelinePromise(readable, transform, writable);
       await pipelinePromise(readable, transform, writable);
 
 

+ 5 - 5
apps/app/src/server/util/stream.ts

@@ -1,17 +1,14 @@
 import { Transform } from 'stream';
 import { Transform } from 'stream';
 
 
 export const convertStreamToBuffer = (stream: any): Promise<Buffer> => {
 export const convertStreamToBuffer = (stream: any): Promise<Buffer> => {
-
   return new Promise((resolve, reject) => {
   return new Promise((resolve, reject) => {
-
     const buffer: Uint8Array[] = [];
     const buffer: Uint8Array[] = [];
 
 
     stream.on('data', (chunk: Uint8Array) => {
     stream.on('data', (chunk: Uint8Array) => {
       buffer.push(chunk);
       buffer.push(chunk);
     });
     });
     stream.on('end', () => resolve(Buffer.concat(buffer)));
     stream.on('end', () => resolve(Buffer.concat(buffer)));
-    stream.on('error', err => reject(err));
-
+    stream.on('error', (err) => reject(err));
   });
   });
 };
 };
 
 
@@ -29,7 +26,10 @@ export const getBufferToFixedSizeTransform = (size: number): Transform => {
         // - If the remaining chunk size is larger than the remaining buffer size:
         // - If the remaining chunk size is larger than the remaining buffer size:
         //     - Fill the buffer, and upload => dataSize is the remaining buffer size
         //     - Fill the buffer, and upload => dataSize is the remaining buffer size
         //     - The remaining chunk after upload will be added to buffer in the next iteration
         //     - The remaining chunk after upload will be added to buffer in the next iteration
-        const dataSize = Math.min(size - filledBufferSize, chunk.length - offset);
+        const dataSize = Math.min(
+          size - filledBufferSize,
+          chunk.length - offset,
+        );
         // Add chunk data to buffer
         // Add chunk data to buffer
         chunk.copy(buffer, filledBufferSize, offset, offset + dataSize);
         chunk.copy(buffer, filledBufferSize, offset, offset + dataSize);
         filledBufferSize += dataSize;
         filledBufferSize += dataSize;

+ 0 - 1
biome.json

@@ -34,7 +34,6 @@
       "!apps/app/src/server/models",
       "!apps/app/src/server/models",
       "!apps/app/src/server/routes",
       "!apps/app/src/server/routes",
       "!apps/app/src/server/service",
       "!apps/app/src/server/service",
-      "!apps/app/src/server/util",
       "!apps/app/src/services",
       "!apps/app/src/services",
       "!apps/app/src/stores"
       "!apps/app/src/stores"
     ]
     ]