فهرست منبع

Merge pull request #10323 from growilabs/fix/downgrade-node-version

support: Downgrade to Node.js v20
Yuki Takei 6 ماه پیش
والد
کامیت
87a084fa80
39فایلهای تغییر یافته به همراه113 افزوده شده و 82 حذف شده
  1. 1 1
      .devcontainer/app/devcontainer.json
  2. 1 1
      .devcontainer/compose.extend.template.yml
  3. 5 5
      .github/mergify.yml
  4. 5 5
      .github/workflows/ci-app-prod.yml
  5. 1 1
      .github/workflows/ci-app.yml
  6. 2 2
      .github/workflows/ci-pdf-converter.yml
  7. 3 3
      .github/workflows/ci-slackbot-proxy.yml
  8. 1 1
      .github/workflows/list-unhealthy-branches.yml
  9. 1 1
      .github/workflows/release-pdf-converter.yml
  10. 1 1
      .github/workflows/release-slackbot-proxy.yml
  11. 2 2
      .github/workflows/release-subpackages.yml
  12. 2 2
      .github/workflows/release.yml
  13. 3 3
      README.md
  14. 3 3
      README_JP.md
  15. 53 17
      apps/app/bin/print-memory-consumption.ts
  16. 2 2
      apps/app/docker/Dockerfile
  17. 1 1
      apps/app/src/features/external-user-group/client/components/ExternalUserGroup/ExternalUserGroupManagement.tsx
  18. 1 0
      apps/app/src/features/external-user-group/server/models/external-user-group-relation.ts
  19. 1 0
      apps/app/src/features/external-user-group/server/models/external-user-group.ts
  20. 2 2
      apps/pdf-converter/docker/Dockerfile
  21. 2 2
      apps/slackbot-proxy/docker/Dockerfile
  22. 1 1
      package.json
  23. 1 1
      packages/presentation/src/client/components/GrowiSlides.tsx
  24. 1 1
      packages/presentation/src/client/services/renderer/extract-sections.ts
  25. 1 1
      packages/remark-attachment-refs/src/client/components/ExtractedAttachments.tsx
  26. 2 2
      packages/remark-attachment-refs/src/server/routes/refs.ts
  27. 1 1
      packages/remark-drawio/src/components/DrawioViewer.tsx
  28. 0 8
      packages/remark-growi-directive/src/mdast-util-growi-directive/lib/index.js
  29. 0 2
      packages/remark-growi-directive/src/micromark-extension-growi-directive/lib/factory-attributes.js
  30. 1 1
      packages/remark-lsx/src/client/components/Lsx.tsx
  31. 1 1
      packages/remark-lsx/src/server/index.ts
  32. 1 1
      packages/slack/src/middlewares/parse-slack-interaction-request.ts
  33. 1 1
      packages/slack/src/middlewares/verify-growi-to-slack-request.ts
  34. 2 2
      packages/slack/src/middlewares/verify-slack-request.ts
  35. 0 2
      packages/slack/src/utils/block-kit-builder.ts
  36. 1 1
      packages/slack/src/utils/response-url-validator.ts
  37. 3 0
      packages/ui/src/components/PagePath/PageListMeta.tsx
  38. 2 1
      packages/ui/src/components/PagePath/PagePathLabel.tsx
  39. 1 0
      packages/ui/src/components/UserPicture.tsx

+ 1 - 1
.devcontainer/app/devcontainer.json

@@ -8,7 +8,7 @@
 
   "features": {
     "ghcr.io/devcontainers/features/node:1": {
-      "version": "22.17.0"
+      "version": "20.18.3"
     }
   },
 

+ 1 - 1
.devcontainer/compose.extend.template.yml

@@ -3,7 +3,7 @@
 services:
   pdf-converter:
     # enabling devcontainer 'features' was not working for secondary devcontainer (https://github.com/devcontainers/features/issues/1175)
-    image: mcr.microsoft.com/vscode/devcontainers/javascript-node:1-22
+    image: mcr.microsoft.com/vscode/devcontainers/javascript-node:1-20
     volumes:
       - ..:/workspace/growi:delegated
       - pnpm-store:/workspace/.pnpm-store

+ 5 - 5
.github/mergify.yml

@@ -7,17 +7,17 @@ queue_rules:
       - check-success ~= ci-app-launch-dev
       - -check-failure ~= ci-app-
       - -check-failure ~= ci-slackbot-
-      - -check-failure ~= test-prod-node22 /
+      - -check-failure ~= test-prod-node20 /
     merge_conditions:
       - check-success ~= ci-app-lint
       - check-success ~= ci-app-test
       - check-success ~= ci-app-launch-dev
-      - check-success ~= test-prod-node22 / build-prod
-      - check-success ~= test-prod-node22 / launch-prod
-      - check-success ~= test-prod-node22 / run-playwright
+      - check-success = test-prod-node20 / build-prod
+      - check-success = test-prod-node20 / launch-prod
+      - check-success ~= test-prod-node20 / run-playwright
       - -check-failure ~= ci-app-
       - -check-failure ~= ci-slackbot-
-      - -check-failure ~= test-prod-node22 /
+      - -check-failure ~= test-prod-node20 /
 
 pull_request_rules:
   - name: Automatic queue to merge

+ 5 - 5
.github/workflows/ci-app-prod.yml

@@ -39,8 +39,8 @@ concurrency:
 
 jobs:
 
-  test-prod-node20:
-    uses: growilabs/growi/.github/workflows/reusable-app-prod.yml@master
+  test-prod-node18:
+    uses: weseek/growi/.github/workflows/reusable-app-prod.yml@master
     if: |
       ( github.event_name == 'push'
         || github.base_ref == 'master'
@@ -48,13 +48,13 @@ jobs:
         || startsWith( github.base_ref, 'release/' )
         || startsWith( github.head_ref, 'mergify/merge-queue/' ))
     with:
-      node-version: 20.x
+      node-version: 18.x
       skip-e2e-test: true
     secrets:
       SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
 
 
-  test-prod-node22:
+  test-prod-node20:
     uses: growilabs/growi/.github/workflows/reusable-app-prod.yml@master
     if: |
       ( github.event_name == 'push'
@@ -63,7 +63,7 @@ jobs:
         || startsWith( github.base_ref, 'release/' )
         || startsWith( github.head_ref, 'mergify/merge-queue/' ))
     with:
-      node-version: 22.x
+      node-version: 20.x
       skip-e2e-test: ${{ contains( github.event.pull_request.labels.*.name, 'dependencies' ) }}
     secrets:
       SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

+ 1 - 1
.github/workflows/ci-app.yml

@@ -44,7 +44,7 @@ jobs:
 
     strategy:
       matrix:
-        node-version: [22.x]
+        node-version: [20.x]
 
     steps:
       - uses: actions/checkout@v4

+ 2 - 2
.github/workflows/ci-pdf-converter.yml

@@ -29,7 +29,7 @@ jobs:
 
     strategy:
       matrix:
-        node-version: [22.x]
+        node-version: [20.x]
 
     steps:
     - uses: actions/checkout@v4
@@ -104,7 +104,7 @@ jobs:
 
     strategy:
       matrix:
-        node-version: [22.x]
+        node-version: [20.x]
 
     steps:
     - uses: actions/checkout@v4

+ 3 - 3
.github/workflows/ci-slackbot-proxy.yml

@@ -30,7 +30,7 @@ jobs:
 
     strategy:
       matrix:
-        node-version: [22.x]
+        node-version: [20.x]
 
     steps:
     - uses: actions/checkout@v4
@@ -85,7 +85,7 @@ jobs:
 
     strategy:
       matrix:
-        node-version: [22.x]
+        node-version: [20.x]
 
     services:
       mysql:
@@ -163,7 +163,7 @@ jobs:
 
     strategy:
       matrix:
-        node-version: [22.x]
+        node-version: [20.x]
 
     services:
       mysql:

+ 1 - 1
.github/workflows/list-unhealthy-branches.yml

@@ -16,7 +16,7 @@ jobs:
 
     - uses: actions/setup-node@v4
       with:
-        node-version: '20'
+        node-version: '18'
 
     - name: List branches
       id: list-branches

+ 1 - 1
.github/workflows/release-pdf-converter.yml

@@ -72,7 +72,7 @@ jobs:
 
     strategy:
       matrix:
-        node-version: [22.x]
+        node-version: [20.x]
 
     steps:
     - uses: actions/checkout@v4

+ 1 - 1
.github/workflows/release-slackbot-proxy.yml

@@ -92,7 +92,7 @@ jobs:
 
     - uses: actions/setup-node@v4
       with:
-        node-version: '20'
+        node-version: '18'
         cache: 'pnpm'
 
     - name: Install dependencies

+ 2 - 2
.github/workflows/release-subpackages.yml

@@ -32,7 +32,7 @@ jobs:
 
     - uses: actions/setup-node@v4
       with:
-        node-version: '22'
+        node-version: '20'
         cache: 'pnpm'
 
     - name: Install dependencies
@@ -75,7 +75,7 @@ jobs:
 
     - uses: actions/setup-node@v4
       with:
-        node-version: '22'
+        node-version: '20'
         cache: 'pnpm'
 
     - name: Install dependencies

+ 2 - 2
.github/workflows/release.yml

@@ -27,7 +27,7 @@ jobs:
 
     - uses: actions/setup-node@v4
       with:
-        node-version: '22'
+        node-version: '20'
         cache: 'pnpm'
 
     - name: Install dependencies
@@ -198,7 +198,7 @@ jobs:
 
     - uses: actions/setup-node@v4
       with:
-        node-version: '22'
+        node-version: '20'
         cache: 'pnpm'
 
     - name: Install dependencies

+ 3 - 3
README.md

@@ -81,9 +81,9 @@ See [GROWI Docs: Environment Variables](https://docs.growi.org/en/admin-guide/ad
 
 ## Dependencies
 
-- Node.js v20.x or v22.x
-- npm 10.x
-- pnpm 10.x
+- Node.js v18.x or v20.x
+- npm 6.x
+- pnpm 9.x
 - [Turborepo](https://turbo.build/repo)
 - MongoDB v6.x or v8.x
 

+ 3 - 3
README_JP.md

@@ -81,9 +81,9 @@ Crowi からの移行は **[こちら](https://docs.growi.org/en/admin-guide/mig
 
 ## 依存関係
 
-- Node.js v20.x or v22.x
-- npm 10.x
-- pnpm 10.x
+- Node.js v18.x or v20.x
+- npm 6.x
+- pnpm 9.x
 - [Turborepo](https://turbo.build/repo)
 - MongoDB v6.x or v8.x
 

+ 53 - 17
apps/app/bin/print-memory-consumption.ts

@@ -55,7 +55,10 @@ class NodeMemoryConsumptionChecker {
   }
 
   // Helper method to get pressure status and icon
-  private getPressureInfo(percentage: number): { status: string; icon: string } {
+  private getPressureInfo(percentage: number): {
+    status: string;
+    icon: string;
+  } {
     if (percentage > 90) return { status: 'HIGH PRESSURE', icon: '🔴' };
     if (percentage > 70) return { status: 'MODERATE PRESSURE', icon: '🟡' };
     return { status: 'LOW PRESSURE', icon: '🟢' };
@@ -71,9 +74,13 @@ class NodeMemoryConsumptionChecker {
     return new Promise((resolve, reject) => {
       get(url, (res) => {
         let data = '';
-        res.on('data', (chunk) => { data += chunk; });
+        res.on('data', (chunk) => {
+          data += chunk;
+        });
         res.on('end', () => resolve(data));
-      }).on('error', (err) => reject(this.createError(`Cannot connect to ${url}: ${err.message}`)));
+      }).on('error', (err) =>
+        reject(this.createError(`Cannot connect to ${url}: ${err.message}`)),
+      );
     });
   }
 
@@ -218,9 +225,20 @@ class NodeMemoryConsumptionChecker {
       return;
     }
 
-    const [heapUsedMB, heapTotalMB, heapLimitMB, rssMB, externalMB, arrayBuffersMB] = [
-      this.toMB(info.heapUsed), this.toMB(info.heapTotal), this.toMB(info.heapLimit || 0),
-      this.toMB(info.rss), this.toMB(info.external), this.toMB(info.arrayBuffers)
+    const [
+      heapUsedMB,
+      heapTotalMB,
+      heapLimitMB,
+      rssMB,
+      externalMB,
+      arrayBuffersMB,
+    ] = [
+      this.toMB(info.heapUsed),
+      this.toMB(info.heapTotal),
+      this.toMB(info.heapLimit || 0),
+      this.toMB(info.rss),
+      this.toMB(info.external),
+      this.toMB(info.arrayBuffers),
     ];
 
     console.log('\n📊 Node.js Memory Information');
@@ -237,20 +255,35 @@ class NodeMemoryConsumptionChecker {
     // Heap Limits
     console.log('\n🔸 Heap Limits:');
     if (info.heapLimit) {
-      const limitType = info.heapLimitSource === 'explicit' ? 'Explicit Limit' : 'Default Limit';
-      const limitSource = info.heapLimitSource === 'explicit' ? '(from --max-old-space-size)' : '(system default)';
-      console.log(`  ${limitType}: ${heapLimitMB.toFixed(2)} MB ${limitSource}`);
-      console.log(`  Global Usage:   ${((heapUsedMB / heapLimitMB) * 100).toFixed(2)}% of maximum`);
+      const limitType =
+        info.heapLimitSource === 'explicit'
+          ? 'Explicit Limit'
+          : 'Default Limit';
+      const limitSource =
+        info.heapLimitSource === 'explicit'
+          ? '(from --max-old-space-size)'
+          : '(system default)';
+      console.log(
+        `  ${limitType}: ${heapLimitMB.toFixed(2)} MB ${limitSource}`,
+      );
+      console.log(
+        `  Global Usage:   ${((heapUsedMB / heapLimitMB) * 100).toFixed(2)}% of maximum`,
+      );
     }
 
     // Heap Pressure Analysis
     const heapPressure = (info.heapUsed / info.heapTotal) * 100;
-    const { status: pressureStatus, icon: pressureIcon } = this.getPressureInfo(heapPressure);
+    const { status: pressureStatus, icon: pressureIcon } =
+      this.getPressureInfo(heapPressure);
     console.log('\n� Memory Pressure Analysis:');
-    console.log(`  Current Pool:   ${pressureIcon} ${pressureStatus} (${heapPressure.toFixed(1)}% of allocated heap)`);
+    console.log(
+      `  Current Pool:   ${pressureIcon} ${pressureStatus} (${heapPressure.toFixed(1)}% of allocated heap)`,
+    );
 
     if (heapPressure > 90) {
-      console.log('  📝 Note: High pressure is normal - Node.js will allocate more heap as needed');
+      console.log(
+        '  📝 Note: High pressure is normal - Node.js will allocate more heap as needed',
+      );
     }
 
     // System Information
@@ -271,10 +304,13 @@ class NodeMemoryConsumptionChecker {
     console.log('\n📋 Summary:');
     if (info.heapLimit) {
       const heapUsagePercent = (heapUsedMB / heapLimitMB) * 100;
-      console.log(`Heap Memory: ${heapUsedMB.toFixed(2)} MB / ${heapLimitMB.toFixed(2)} MB (${heapUsagePercent.toFixed(2)}%)`);
-      console.log(heapUsagePercent > 80
-        ? '⚠️  Consider increasing heap limit with --max-old-space-size if needed'
-        : '✅ Memory usage is within healthy limits'
+      console.log(
+        `Heap Memory: ${heapUsedMB.toFixed(2)} MB / ${heapLimitMB.toFixed(2)} MB (${heapUsagePercent.toFixed(2)}%)`,
+      );
+      console.log(
+        heapUsagePercent > 80
+          ? '⚠️  Consider increasing heap limit with --max-old-space-size if needed'
+          : '✅ Memory usage is within healthy limits',
       );
     }
 

+ 2 - 2
apps/app/docker/Dockerfile

@@ -6,7 +6,7 @@ ARG PNPM_HOME="/root/.local/share/pnpm"
 ##
 ## base
 ##
-FROM node:22-slim AS base
+FROM node:20-slim AS base
 
 ARG OPT_DIR
 ARG PNPM_HOME
@@ -72,7 +72,7 @@ RUN tar -zcf /tmp/packages.tar.gz \
 ##
 ## release
 ##
-FROM node:22-slim
+FROM node:20-slim
 LABEL maintainer="Yuki Takei <yuki@weseek.co.jp>"
 
 ARG OPT_DIR

+ 1 - 1
apps/app/src/features/external-user-group/client/components/ExternalUserGroup/ExternalUserGroupManagement.tsx

@@ -153,7 +153,7 @@ export const ExternalGroupManagement: FC = () => {
         hideDeleteModal();
 
         toastSuccess(`Deleted ${selectedExternalUserGroup?.name} group.`);
-      } catch (err) {
+      } catch {
         toastError(new Error('Unable to delete the groups'));
       }
     },

+ 1 - 0
apps/app/src/features/external-user-group/server/models/external-user-group-relation.ts

@@ -15,6 +15,7 @@ export interface ExternalUserGroupRelationDocument
 
 export interface ExternalUserGroupRelationModel
   extends Model<ExternalUserGroupRelationDocument> {
+  // biome-ignore lint/suspicious/noExplicitAny: ignore
   [x: string]: any; // for old methods
 
   PAGE_ITEMS: 50;

+ 1 - 0
apps/app/src/features/external-user-group/server/models/external-user-group.ts

@@ -12,6 +12,7 @@ export interface ExternalUserGroupDocument
 
 export interface ExternalUserGroupModel
   extends Model<ExternalUserGroupDocument> {
+  // biome-ignore lint/suspicious/noExplicitAny: ignore
   [x: string]: any; // for old methods
 
   PAGE_ITEMS: 10;

+ 2 - 2
apps/pdf-converter/docker/Dockerfile

@@ -6,7 +6,7 @@ ARG PNPM_HOME="/root/.local/share/pnpm"
 ##
 ## base
 ##
-FROM node:22-slim AS base
+FROM node:20-slim AS base
 
 ARG OPT_DIR
 ARG PNPM_HOME
@@ -63,7 +63,7 @@ RUN tar -zcf /tmp/packages.tar.gz \
 ##
 ## release
 ##
-FROM node:22-slim
+FROM node:20-slim
 LABEL maintainer="Yuki Takei <yuki@weseek.co.jp>"
 
 ARG OPT_DIR

+ 2 - 2
apps/slackbot-proxy/docker/Dockerfile

@@ -3,7 +3,7 @@
 ##
 ## base
 ##
-FROM node:22-slim AS base
+FROM node:20-slim AS base
 
 ENV optDir="/opt"
 
@@ -52,7 +52,7 @@ RUN tar -zcf packages.tar.gz \
 ##
 ## release
 ##
-FROM node:22-slim
+FROM node:20-slim
 LABEL maintainer="Yuki Takei <yuki@weseek.co.jp>"
 
 ENV NODE_ENV="production"

+ 1 - 1
package.json

@@ -116,6 +116,6 @@
     }
   },
   "engines": {
-    "node": "^20 || ^22"
+    "node": "^18 || ^20"
   }
 }

+ 1 - 1
packages/presentation/src/client/components/GrowiSlides.tsx

@@ -30,7 +30,7 @@ export const GrowiSlides = (props: Props): JSX.Element => {
     rendererOptions.remarkPlugins == null ||
     rendererOptions.components == null
   ) {
-    return <></>;
+    return;
   }
 
   rendererOptions.remarkPlugins.push([

+ 1 - 1
packages/presentation/src/client/services/renderer/extract-sections.ts

@@ -61,7 +61,7 @@ export const remarkPlugin: Plugin<[ExtractSectionsPluginParams]> = (
 
   return (tree) => {
     // wrap with <section>
-    visit(tree, startCondition, (node, index, parent: Parent) => {
+    visit(tree, startCondition, (node, _index, parent: Parent) => {
       if (parent == null || parent.type !== 'root' || node.type === 'yaml') {
         return;
       }

+ 1 - 1
packages/remark-attachment-refs/src/client/components/ExtractedAttachments.tsx

@@ -107,7 +107,7 @@ export const ExtractedAttachments = React.memo(
 
     // eslint-disable-next-line @typescript-eslint/no-unused-vars
     const renderExtractedImage = useCallback(
-      (attachment: IAttachmentHasId, index: number) => {
+      (attachment: IAttachmentHasId, _index: number) => {
         const { options } = refsContext;
 
         // determine alt

+ 2 - 2
packages/remark-attachment-refs/src/server/routes/refs.ts

@@ -62,7 +62,7 @@ function addDepthCondition(query, pagePath, optionsDepth) {
 
 type RequestWithUser = Request & { user: HydratedDocument<IUser> };
 
-const loginRequiredFallback = (req, res) => {
+const loginRequiredFallback = (_req, res) => {
   return res.status(403).send('login required');
 };
 
@@ -203,7 +203,7 @@ export const routesFactory = (crowi): any => {
 
         try {
           regex = generateRegexp(regexOptionValue);
-        } catch (err) {
+        } catch {
           res.status(400).send("the 'regex' option is invalid as RegExp.");
           return;
         }

+ 1 - 1
packages/remark-drawio/src/components/DrawioViewer.tsx

@@ -162,7 +162,7 @@ export const DrawioViewer = memo((props: DrawioViewerProps): JSX.Element => {
     }
 
     const observer = new ResizeObserver((entries) => {
-      for (const entry of entries) {
+      for (const _entry of entries) {
         // setElementWidth(entry.contentRect.width);
         onRenderingStart?.();
         renderDrawioWithDebounce();

+ 0 - 8
packages/remark-growi-directive/src/mdast-util-growi-directive/lib/index.js

@@ -22,8 +22,6 @@ import { stringifyEntitiesLight } from 'stringify-entities';
 
 const own = {}.hasOwnProperty;
 
-const shortcut = /^[^\t\n\r "#'.<=>`}]+$/;
-
 export const DirectiveType = Object.freeze({
   Text: 'textGrowiPluginDirective',
   Leaf: 'leafGrowiPluginDirective',
@@ -223,12 +221,6 @@ function attributes(node, state) {
   const attrs = node.attributes || {};
   /** @type {Array.<string>} */
   const values = [];
-  /** @type {string|undefined} */
-  let classesFull;
-  /** @type {string|undefined} */
-  let classes;
-  /** @type {string|undefined} */
-  let id;
   /** @type {string} */
   let key;
 

+ 0 - 2
packages/remark-growi-directive/src/micromark-extension-growi-directive/lib/factory-attributes.js

@@ -50,8 +50,6 @@ export function factoryAttributes(
   attributeValueData,
   disallowEol,
 ) {
-  /** @type {string} */
-  let type;
   /** @type {Code|undefined} */
   let marker;
 

+ 1 - 1
packages/remark-lsx/src/client/components/Lsx.tsx

@@ -93,7 +93,7 @@ const LsxSubstance = React.memo(
 
     const contents = useMemo(() => {
       if (data == null) {
-        return <></>;
+        return;
       }
 
       const depthRange = lsxContext.getOptDepth();

+ 1 - 1
packages/remark-lsx/src/server/index.ts

@@ -5,7 +5,7 @@ import { FilterXSS } from 'xss';
 import type { LsxApiOptions } from '../interfaces/api';
 import { listPages } from './routes/list-pages';
 
-const loginRequiredFallback = (req: Request, res: Response) => {
+const loginRequiredFallback = (_req: Request, res: Response) => {
   return res.status(403).send('login required');
 };
 

+ 1 - 1
packages/slack/src/middlewares/parse-slack-interaction-request.ts

@@ -5,7 +5,7 @@ import { InteractionPayloadAccessor } from '../utils/interaction-payload-accesso
 
 export const parseSlackInteractionRequest = (
   req: RequestFromSlack,
-  res: Response,
+  _res: Response,
   next: NextFunction,
 ): void => {
   // There is no payload in the request from slack

+ 1 - 1
packages/slack/src/middlewares/verify-growi-to-slack-request.ts

@@ -14,7 +14,7 @@ const logger = loggerFactory(
  */
 export const verifyGrowiToSlackRequest = (
   req: RequestFromGrowi,
-  res: Response,
+  _res: Response,
   next: NextFunction,
 ): void => {
   const str = req.headers['x-growi-gtop-tokens'];

+ 2 - 2
packages/slack/src/middlewares/verify-slack-request.ts

@@ -16,7 +16,7 @@ const logger = loggerFactory('@growi/slack:middlewares:verify-slack-request');
 export const verifySlackRequest = (
   // biome-ignore lint/suspicious/noExplicitAny: ignore
   req: RequestFromSlack & { rawBody: any },
-  res: Response,
+  _res: Response,
   next: NextFunction,
 ): void => {
   const signingSecret = req.slackSigningSecret;
@@ -40,7 +40,7 @@ export const verifySlackRequest = (
   }
 
   // protect against replay attacks
-  const time = Math.floor(new Date().getTime() / 1000);
+  const time = Math.floor(Date.now() / 1000);
   if (Math.abs(time - timestamp) > 300) {
     const message = 'Verification failed.';
     logger.warn(message, { body: req.body });

+ 0 - 2
packages/slack/src/utils/block-kit-builder.ts

@@ -1,5 +1,4 @@
 import type {
-  Action,
   ActionsBlock,
   ActionsBlockElement,
   Button,
@@ -10,7 +9,6 @@ import type {
   InputBlock,
   MultiSelect,
   Option,
-  Overflow,
   PlainTextInput,
   RadioButtons,
   SectionBlock,

+ 1 - 1
packages/slack/src/utils/response-url-validator.ts

@@ -36,7 +36,7 @@ export function isValidResponseUrl(
     }
 
     return false;
-  } catch (error) {
+  } catch {
     // Invalid URL format
     return false;
   }

+ 3 - 0
packages/ui/src/components/PagePath/PageListMeta.tsx

@@ -18,6 +18,7 @@ const SeenUsersCount = (props: SeenUsersCountProps): JSX.Element => {
   const { count, shouldSpaceOutIcon, basisViewersCount } = props;
 
   if (count === 0) {
+    // biome-ignore lint/complexity/noUselessFragments: ignore
     return <></>;
   }
 
@@ -25,6 +26,7 @@ const SeenUsersCount = (props: SeenUsersCountProps): JSX.Element => {
     basisViewersCount != null &&
     basisViewersCount <= SEEN_USERS_HIDE_THRES__ACTIVE_USERS_COUNT
   ) {
+    // biome-ignore lint/complexity/noUselessFragments: ignore
     return <></>;
   }
 
@@ -35,6 +37,7 @@ const SeenUsersCount = (props: SeenUsersCountProps): JSX.Element => {
   );
 
   if (strengthLevel > MAX_STRENGTH_LEVEL) {
+    // biome-ignore lint/complexity/noUselessFragments: ignore
     return <></>;
   }
 

+ 2 - 1
packages/ui/src/components/PagePath/PagePathLabel.tsx

@@ -14,7 +14,7 @@ const TextElement: FC<TextElemProps> = (props: TextElemProps) => (
         dangerouslySetInnerHTML={{ __html: props.children?.toString() || '' }}
       />
     ) : (
-      <>{props.children}</>
+      props.children
     )}
   </>
 );
@@ -48,6 +48,7 @@ export const PagePathLabel: FC<Props> = (props: Props) => {
     );
   } else if (isFormerOnly) {
     textElem = dPagePath.isFormerRoot ? (
+      // biome-ignore lint/complexity/noUselessFragments: ignore
       <>/</>
     ) : (
       <TextElement isHTML={isPathIncludedHtml}>{dPagePath.former}</TextElement>

+ 1 - 0
packages/ui/src/components/UserPicture.tsx

@@ -181,6 +181,7 @@ export const UserPicture = memo((userProps: Props): JSX.Element => {
     .filter(Boolean)
     .join(' ');
 
+  // biome-ignore lint/performance/noImgElement: ignore
   const imgElement = <img src={src} alt={displayName} className={className} />;
   const baseProps = { displayName, size, children: imgElement };