From afe569a79cb9b172d466cc0938594354cffbeb42 Mon Sep 17 00:00:00 2001 From: Eliezer Castro Date: Sun, 24 Aug 2025 12:15:39 -0300 Subject: [PATCH 1/8] feat: test case filtering (#276) --- backend/routes/cases/index.js | 32 +++- frontend/messages/en.json | 8 +- frontend/messages/ja.json | 8 +- frontend/messages/pt-BR.json | 8 +- .../[projectId]/folders/FolderItem.tsx | 11 +- .../folders/[folderId]/cases/CasesPane.tsx | 77 ++++++++- .../[folderId]/cases/TestCaseFilter.tsx | 153 +++++++++++++++++ .../[folderId]/cases/TestCaseTable.tsx | 154 ++++++++++++------ .../folders/[folderId]/cases/page.tsx | 25 +++ frontend/types/case.ts | 6 + frontend/utils/caseControl.ts | 19 ++- 11 files changed, 432 insertions(+), 69 deletions(-) create mode 100644 frontend/src/app/[locale]/projects/[projectId]/folders/[folderId]/cases/TestCaseFilter.tsx diff --git a/backend/routes/cases/index.js b/backend/routes/cases/index.js index 170cf1f..ac8b9a0 100644 --- a/backend/routes/cases/index.js +++ b/backend/routes/cases/index.js @@ -1,6 +1,6 @@ const express = require('express'); const router = express.Router(); -const { DataTypes } = require('sequelize'); +const { DataTypes, Op } = require('sequelize'); const defineCase = require('../../models/cases'); module.exports = function (sequelize) { @@ -9,17 +9,39 @@ module.exports = function (sequelize) { const { verifyProjectVisibleFromFolderId } = require('../../middleware/verifyVisible')(sequelize); router.get('/', verifySignedIn, verifyProjectVisibleFromFolderId, async (req, res) => { - const { folderId } = req.query; + const { folderId, priority, type } = req.query; if (!folderId) { return res.status(400).json({ error: 'folderId is required' }); } try { + const whereClause = { + folderId: folderId, + }; + + if (priority) { + const priorityValues = priority + .split(',') + .map((p) => parseInt(p.trim(), 10)) + .filter((p) => !isNaN(p)); + if (priorityValues.length > 0) { + whereClause.priority = { [Op.in]: priorityValues }; + } + } + + if (type) { + const typeValues = type + .split(',') + .map((t) => parseInt(t.trim(), 10)) + .filter((t) => !isNaN(t)); + if (typeValues.length > 0) { + whereClause.type = { [Op.in]: typeValues }; + } + } + const cases = await Case.findAll({ - where: { - folderId: folderId, - }, + where: whereClause, }); res.json(cases); } catch (error) { diff --git a/frontend/messages/en.json b/frontend/messages/en.json index 709bbb5..b846f46 100644 --- a/frontend/messages/en.json +++ b/frontend/messages/en.json @@ -203,7 +203,13 @@ "case_title": "Test Case Title", "case_description": "Test Case Description", "create": "Create", - "please_enter": "Please enter test case title" + "please_enter": "Please enter test case title", + "clear_all": "Clear all", + "apply": "Apply", + "select_priorities": "Select priorities", + "selected": "Selected", + "type": "Type", + "select_types": "Select types" }, "Case": { "back_to_cases": "Back to test cases", diff --git a/frontend/messages/ja.json b/frontend/messages/ja.json index 09f1788..b7cca94 100644 --- a/frontend/messages/ja.json +++ b/frontend/messages/ja.json @@ -204,7 +204,13 @@ "case_title": "テストケースタイトル", "case_description": "テストケース詳細", "create": "作成", - "please_enter": "テストケースタイトルを入力してください" + "please_enter": "テストケースタイトルを入力してください", + "clear_all": "すべてクリア", + "apply": "適用", + "select_priorities": "優先度を選択", + "selected": "選択済み", + "type": "タイプ", + "select_types": "タイプを選択" }, "Case": { "back_to_cases": "テストケース一覧に戻る", diff --git a/frontend/messages/pt-BR.json b/frontend/messages/pt-BR.json index 4d4d2cc..93a04b5 100644 --- a/frontend/messages/pt-BR.json +++ b/frontend/messages/pt-BR.json @@ -203,7 +203,13 @@ "case_title": "Título do Caso de Teste", "case_description": "Descrição do Caso de Teste", "create": "Criar", - "please_enter": "Por favor, insira o título do caso de teste" + "please_enter": "Por favor, insira o título do caso de teste", + "clear_all": "Limpar tudo", + "apply": "Aplicar", + "select_priorities": "Selecionar prioridades", + "selected": "Selecionado", + "type": "Tipo", + "select_types": "Selecionar tipos" }, "Case": { "back_to_cases": "Voltar para os casos de teste", diff --git a/frontend/src/app/[locale]/projects/[projectId]/folders/FolderItem.tsx b/frontend/src/app/[locale]/projects/[projectId]/folders/FolderItem.tsx index 2a3c995..08a026c 100644 --- a/frontend/src/app/[locale]/projects/[projectId]/folders/FolderItem.tsx +++ b/frontend/src/app/[locale]/projects/[projectId]/folders/FolderItem.tsx @@ -2,6 +2,7 @@ import { Button } from '@heroui/react'; import { ChevronDown, ChevronRight, Folder, Plus } from 'lucide-react'; import { NodeApi } from 'react-arborist'; import { useContext } from 'react'; +import { useSearchParams } from 'next/navigation'; import FolderEditMenu from './FolderEditMenu'; import { FolderType, FoldersMessages, TreeNodeData } from '@/types/folder'; @@ -33,6 +34,7 @@ export default function FolderItem({ onDeleteClick, }: FolderItemProps) { const router = useRouter(); + const searchParams = useSearchParams(); const context = useContext(TokenContext); const isSelected = selectedFolder && node.data.folderData.id === selectedFolder.id; @@ -69,11 +71,18 @@ export default function FolderItem({ ); + const handleClick = () => { + const currentParams = new URLSearchParams(searchParams.toString()); + const queryString = currentParams.toString(); + const url = `/projects/${projectId}/folders/${node.data.folderData.id}/cases${queryString ? `?${queryString}` : ''}`; + router.push(url, { locale }); + }; + return ( router.push(`/projects/${projectId}/folders/${node.data.folderData.id}/cases`, { locale })} + onClick={() => handleClick()} toggleButton={toggleButton} icon={} label={node.data.name} diff --git a/frontend/src/app/[locale]/projects/[projectId]/folders/[folderId]/cases/CasesPane.tsx b/frontend/src/app/[locale]/projects/[projectId]/folders/[folderId]/cases/CasesPane.tsx index dd06ccd..71f48e7 100644 --- a/frontend/src/app/[locale]/projects/[projectId]/folders/[folderId]/cases/CasesPane.tsx +++ b/frontend/src/app/[locale]/projects/[projectId]/folders/[folderId]/cases/CasesPane.tsx @@ -1,5 +1,6 @@ 'use client'; import { useState, useEffect, useContext } from 'react'; +import { useRouter, useSearchParams } from 'next/navigation'; import TestCaseTable from './TestCaseTable'; import CaseDialog from './CaseDialog'; import { TokenContext } from '@/utils/TokenProvider'; @@ -7,6 +8,7 @@ import { fetchCases, createCase, deleteCases, exportCases } from '@/utils/caseCo import { CaseType, CasesMessages } from '@/types/case'; import DeleteConfirmDialog from '@/components/DeleteConfirmDialog'; import { PriorityMessages } from '@/types/priority'; +import { TestTypeMessages } from '@/types/testType'; import { LocaleCodeType } from '@/types/locale'; import { logError } from '@/utils/errorHandler'; @@ -15,21 +17,63 @@ type Props = { folderId: string; messages: CasesMessages; priorityMessages: PriorityMessages; + testTypeMessages: TestTypeMessages; locale: LocaleCodeType; }; -export default function CasesPane({ projectId, folderId, messages, priorityMessages, locale }: Props) { +export default function CasesPane({ + projectId, + folderId, + messages, + priorityMessages, + testTypeMessages, + locale, +}: Props) { const [cases, setCases] = useState([]); const context = useContext(TokenContext); const [isCaseDialogOpen, setIsCaseDialogOpen] = useState(false); + const [priorityFilter, setPriorityFilter] = useState([]); + const [typeFilter, setTypeFilter] = useState([]); + const router = useRouter(); + const searchParams = useSearchParams(); useEffect(() => { async function fetchDataEffect() { if (!context.isSignedIn()) { return; } + + const priorityParam = searchParams.get('priority'); + let currentPriorityFilter: number[] = []; + if (priorityParam) { + currentPriorityFilter = priorityParam + .split(',') + .map((p) => parseInt(p.trim())) + .filter((p) => !isNaN(p)); + setPriorityFilter(currentPriorityFilter); + } else { + setPriorityFilter([]); + } + + const typeParam = searchParams.get('type'); + let currentTypeFilter: number[] = []; + if (typeParam) { + currentTypeFilter = typeParam + .split(',') + .map((t) => parseInt(t.trim())) + .filter((t) => !isNaN(t)); + setTypeFilter(currentTypeFilter); + } else { + setTypeFilter([]); + } + try { - const data = await fetchCases(context.token.access_token, Number(folderId)); + const data = await fetchCases( + context.token.access_token, + Number(folderId), + currentPriorityFilter.length > 0 ? currentPriorityFilter : undefined, + currentTypeFilter.length > 0 ? currentTypeFilter : undefined + ); setCases(data); } catch (error: unknown) { logError('Error fetching cases:', error); @@ -37,7 +81,7 @@ export default function CasesPane({ projectId, folderId, messages, priorityMessa } fetchDataEffect(); - }, [context, folderId]); + }, [context, folderId, searchParams]); const closeDialog = () => { setIsCaseDialogOpen(false); @@ -49,7 +93,6 @@ export default function CasesPane({ projectId, folderId, messages, priorityMessa closeDialog(); }; - // Delete confirm dialog const [isDeleteConfirmDialogOpen, setIsDeleteConfirmDialogOpen] = useState(false); const [deleteCaseIds, setDeleteCaseIds] = useState([]); const closeDeleteConfirmDialog = () => { @@ -79,6 +122,28 @@ export default function CasesPane({ projectId, folderId, messages, priorityMessa await exportCases(context.token.access_token, Number(folderId), type); }; + const handleFilterChange = (priorities: number[], types: number[]) => { + setPriorityFilter(priorities); + setTypeFilter(types); + + const currentParams = new URLSearchParams(searchParams.toString()); + + if (priorities.length > 0) { + currentParams.set('priority', priorities.join(',')); + } else { + currentParams.delete('priority'); + } + + if (types.length > 0) { + currentParams.set('type', types.join(',')); + } else { + currentParams.delete('type'); + } + + const newUrl = `${window.location.pathname}?${currentParams.toString()}`; + router.push(newUrl, { scroll: false }); + }; + return ( <> diff --git a/frontend/src/app/[locale]/projects/[projectId]/folders/[folderId]/cases/TestCaseFilter.tsx b/frontend/src/app/[locale]/projects/[projectId]/folders/[folderId]/cases/TestCaseFilter.tsx new file mode 100644 index 0000000..563d050 --- /dev/null +++ b/frontend/src/app/[locale]/projects/[projectId]/folders/[folderId]/cases/TestCaseFilter.tsx @@ -0,0 +1,153 @@ +import { useState, useEffect } from 'react'; +import { Button, Dropdown, DropdownTrigger, DropdownMenu, DropdownItem, Selection } from '@heroui/react'; +import { ChevronDown, Circle } from 'lucide-react'; +import { PriorityMessages } from '@/types/priority'; +import { TestTypeMessages } from '@/types/testType'; +import { CasesMessages } from '@/types/case'; +import { priorities, testTypes } from '@/config/selection'; + +type TestCaseFilterProps = { + messages: CasesMessages; + priorityMessages: PriorityMessages; + testTypeMessages: TestTypeMessages; + activePriorityFilters: number[]; + activeTypeFilters: number[]; + onFilterChange: (priorities: number[], types: number[]) => void; + handleFilterChange: () => void; +}; + +export default function TestCaseFilter({ + messages, + priorityMessages, + testTypeMessages, + activePriorityFilters, + activeTypeFilters, + onFilterChange, + handleFilterChange, +}: TestCaseFilterProps) { + const [selectedPriorities, setSelectedPriorities] = useState(new Set([])); + const [selectedTypes, setSelectedTypes] = useState(new Set([])); + + useEffect(() => { + if (activePriorityFilters.length > 0) { + const activeKeys = activePriorityFilters.map((index) => priorities[index]?.uid).filter(Boolean); + setSelectedPriorities(new Set(activeKeys)); + } else { + setSelectedPriorities(new Set([])); + } + }, [activePriorityFilters]); + + useEffect(() => { + if (activeTypeFilters.length > 0) { + const activeKeys = activeTypeFilters.map((index) => testTypes[index]?.uid).filter(Boolean); + setSelectedTypes(new Set(activeKeys)); + } else { + setSelectedTypes(new Set([])); + } + }, [activeTypeFilters]); + + const handlePrioritySelectionChange = (keys: Selection) => { + setSelectedPriorities(keys); + }; + + const handleTypeSelectionChange = (keys: Selection) => { + setSelectedTypes(keys); + }; + + const handleApplyFilter = () => { + let priorityIndices: number[] = []; + if (selectedPriorities !== 'all' && selectedPriorities.size > 0) { + priorityIndices = Array.from(selectedPriorities) + .map((key) => priorities.findIndex((priority) => priority.uid === key)) + .filter((index) => index !== -1); + } + + let typeIndices: number[] = []; + if (selectedTypes !== 'all' && selectedTypes.size > 0) { + typeIndices = Array.from(selectedTypes) + .map((key) => testTypes.findIndex((type) => type.uid === key)) + .filter((index) => index !== -1); + } + + onFilterChange(priorityIndices, typeIndices); + handleFilterChange(); + }; + + const handleClearFilter = () => { + setSelectedPriorities(new Set([])); + setSelectedTypes(new Set([])); + onFilterChange([], []); + handleFilterChange(); + }; + + const isFilterEmpty = + (selectedPriorities === 'all' || (selectedPriorities instanceof Set && selectedPriorities.size === 0)) && + (selectedTypes === 'all' || (selectedTypes instanceof Set && selectedTypes.size === 0)); + + return ( +
+
+

{messages.priority}

+ + + + + + {priorities.map((priority) => ( + +
+ + {priorityMessages[priority.uid]} +
+
+ ))} +
+
+
+ +
+

{messages.type}

+ + + + + + {testTypes.map((type) => ( + + {testTypeMessages[type.uid]} + + ))} + + +
+ +
+ + +
+
+ ); +} diff --git a/frontend/src/app/[locale]/projects/[projectId]/folders/[folderId]/cases/TestCaseTable.tsx b/frontend/src/app/[locale]/projects/[projectId]/folders/[folderId]/cases/TestCaseTable.tsx index 791b4f3..0ce1655 100644 --- a/frontend/src/app/[locale]/projects/[projectId]/folders/[folderId]/cases/TestCaseTable.tsx +++ b/frontend/src/app/[locale]/projects/[projectId]/folders/[folderId]/cases/TestCaseTable.tsx @@ -14,11 +14,15 @@ import { Selection, SortDescriptor, ButtonGroup, + cn, + Badge, } from '@heroui/react'; -import { Plus, MoreVertical, Trash, FileDown, ChevronDown, FileJson, FileSpreadsheet } from 'lucide-react'; +import { Plus, MoreVertical, Trash, FileDown, ChevronDown, Filter, FileJson, FileSpreadsheet } from 'lucide-react'; +import TestCaseFilter from './TestCaseFilter'; import { Link } from '@/src/i18n/routing'; import { CaseType, CasesMessages } from '@/types/case'; import { PriorityMessages } from '@/types/priority'; +import { TestTypeMessages } from '@/types/testType'; import TestCasePriority from '@/components/TestCasePriority'; import { LocaleCodeType } from '@/types/locale'; @@ -30,8 +34,12 @@ type Props = { onDeleteCase: (caseId: number) => void; onDeleteCases: (caseIds: number[]) => void; onExportCases: (type: string) => void; + onFilterChange: (priorities: number[], types: number[]) => void; + activePriorityFilters: number[]; + activeTypeFilters: number[]; messages: CasesMessages; priorityMessages: PriorityMessages; + testTypeMessages: TestTypeMessages; locale: LocaleCodeType; }; @@ -43,8 +51,12 @@ export default function TestCaseTable({ onDeleteCase, onDeleteCases, onExportCases, + onFilterChange, + activePriorityFilters, + activeTypeFilters, messages, priorityMessages, + testTypeMessages, locale, }: Props) { const headerColumns = [ @@ -60,6 +72,7 @@ export default function TestCaseTable({ direction: 'ascending', }); const [exportType, setExportType] = useState(new Set(['json'])); + const [showFilter, setShowFilter] = useState(false); const sortedItems = useMemo(() => { if (cases.length === 0) { @@ -78,6 +91,10 @@ export default function TestCaseTable({ onDeleteCase(deleteCaseId); }; + const handleFilterChange = () => { + setShowFilter(!showFilter); + }; + const renderCell = useCallback((testCase: CaseType, columnKey: string): ReactNode => { const cellValue = testCase[columnKey as keyof CaseType]; @@ -140,6 +157,8 @@ export default function TestCaseTable({ setExportType(new Set(Array.from(keys as Set))); }; + const hasActiveFilters = activePriorityFilters.length > 0 || activeTypeFilters.length > 0; + const classNames = useMemo( () => ({ wrapper: ['max-w-3xl'], @@ -161,69 +180,98 @@ export default function TestCaseTable({ return ( <> -
-

{messages.testCaseList}

- -
- {((selectedKeys !== 'all' && selectedKeys.size > 0) || selectedKeys === 'all') && ( +
+
+

{messages.testCaseList}

+
+ + + + {((selectedKeys !== 'all' && selectedKeys.size > 0) || selectedKeys === 'all') && ( + + )} + + + + + + + + }> + json + + }> + csv + + + + - )} - - - - - - - - }> - json - - }> - csv - - - - - +
+ {showFilter && ( + + )}
); diff --git a/frontend/types/case.ts b/frontend/types/case.ts index 6f10872..690185d 100644 --- a/frontend/types/case.ts +++ b/frontend/types/case.ts @@ -77,6 +77,12 @@ type CasesMessages = { caseDescription: string; create: string; pleaseEnter: string; + clearAll: string; + apply: string; + selectPriorities: string; + selected: string; + type: string; + selectTypes: string; }; type CaseMessages = { diff --git a/frontend/utils/caseControl.ts b/frontend/utils/caseControl.ts index ba1d5d1..97b7248 100644 --- a/frontend/utils/caseControl.ts +++ b/frontend/utils/caseControl.ts @@ -26,8 +26,20 @@ async function fetchCase(jwt: string, caseId: number) { } } -async function fetchCases(jwt: string, folderId: number) { - const url = `${apiServer}/cases?folderId=${folderId}`; +async function fetchCases(jwt: string, folderId: number, priority?: number[], type?: number[]) { + const queryParams = [`folderId=${folderId}`]; + + if (priority && priority.length > 0) { + queryParams.push(`priority=${priority.join(',')}`); + } + + if (type && type.length > 0) { + queryParams.push(`type=${type.join(',')}`); + } + + const query = queryParams.length > 0 ? `?${queryParams.join('&')}` : ''; + + const url = `${apiServer}/cases${query}`; try { const response = await fetch(url, { @@ -43,9 +55,10 @@ async function fetchCases(jwt: string, folderId: number) { } const data = await response.json(); - return data; + return data || []; } catch (error: unknown) { logError('Error fetching data', error); + return []; } } From f76a3cdaf53351e9c131bd603707f67a86effc40 Mon Sep 17 00:00:00 2001 From: Eliezer Castro Date: Sat, 30 Aug 2025 11:26:46 -0300 Subject: [PATCH 2/8] feat: search test case (#277) --- backend/routes/cases/index.js | 14 +- frontend/messages/en.json | 3 +- frontend/messages/ja.json | 3 +- frontend/messages/pt-BR.json | 3 +- .../folders/[folderId]/cases/CasesPane.tsx | 105 ++++++------ .../[folderId]/cases/TestCaseTable.tsx | 156 ++++++++++++------ .../folders/[folderId]/cases/page.tsx | 1 + frontend/types/case.ts | 1 + frontend/utils/caseControl.ts | 6 +- frontend/utils/highlightSearchTerm.tsx | 38 +++++ frontend/utils/parseQueryParam.ts | 8 + frontend/utils/useDebounce.ts | 30 ++++ 12 files changed, 266 insertions(+), 102 deletions(-) create mode 100644 frontend/utils/highlightSearchTerm.tsx create mode 100644 frontend/utils/parseQueryParam.ts create mode 100644 frontend/utils/useDebounce.ts diff --git a/backend/routes/cases/index.js b/backend/routes/cases/index.js index ac8b9a0..c525ba4 100644 --- a/backend/routes/cases/index.js +++ b/backend/routes/cases/index.js @@ -9,7 +9,7 @@ module.exports = function (sequelize) { const { verifyProjectVisibleFromFolderId } = require('../../middleware/verifyVisible')(sequelize); router.get('/', verifySignedIn, verifyProjectVisibleFromFolderId, async (req, res) => { - const { folderId, priority, type } = req.query; + const { folderId, priority, type, q } = req.query; if (!folderId) { return res.status(400).json({ error: 'folderId is required' }); @@ -20,6 +20,18 @@ module.exports = function (sequelize) { folderId: folderId, }; + if (q) { + const searchTerm = q.trim(); + + if (searchTerm.length > 100) { + return res.status(400).json({ error: 'Search term too long' }); + } + + if (searchTerm.length >= 2) { + whereClause[Op.or] = [{ title: { [Op.like]: `%${q}%` } }]; + } + } + if (priority) { const priorityValues = priority .split(',') diff --git a/frontend/messages/en.json b/frontend/messages/en.json index b846f46..e546a20 100644 --- a/frontend/messages/en.json +++ b/frontend/messages/en.json @@ -209,7 +209,8 @@ "select_priorities": "Select priorities", "selected": "Selected", "type": "Type", - "select_types": "Select types" + "select_types": "Select types", + "search_placeholder": "Type to search..." }, "Case": { "back_to_cases": "Back to test cases", diff --git a/frontend/messages/ja.json b/frontend/messages/ja.json index b7cca94..7bd7cf1 100644 --- a/frontend/messages/ja.json +++ b/frontend/messages/ja.json @@ -210,7 +210,8 @@ "select_priorities": "優先度を選択", "selected": "選択済み", "type": "タイプ", - "select_types": "タイプを選択" + "select_types": "タイプを選択", + "search_placeholder": "入力して検索..." }, "Case": { "back_to_cases": "テストケース一覧に戻る", diff --git a/frontend/messages/pt-BR.json b/frontend/messages/pt-BR.json index 93a04b5..e575d7c 100644 --- a/frontend/messages/pt-BR.json +++ b/frontend/messages/pt-BR.json @@ -209,7 +209,8 @@ "select_priorities": "Selecionar prioridades", "selected": "Selecionado", "type": "Tipo", - "select_types": "Selecionar tipos" + "select_types": "Selecionar tipos", + "search_placeholder": "Digite para buscar..." }, "Case": { "back_to_cases": "Voltar para os casos de teste", diff --git a/frontend/src/app/[locale]/projects/[projectId]/folders/[folderId]/cases/CasesPane.tsx b/frontend/src/app/[locale]/projects/[projectId]/folders/[folderId]/cases/CasesPane.tsx index 71f48e7..6bfbbf9 100644 --- a/frontend/src/app/[locale]/projects/[projectId]/folders/[folderId]/cases/CasesPane.tsx +++ b/frontend/src/app/[locale]/projects/[projectId]/folders/[folderId]/cases/CasesPane.tsx @@ -11,6 +11,7 @@ import { PriorityMessages } from '@/types/priority'; import { TestTypeMessages } from '@/types/testType'; import { LocaleCodeType } from '@/types/locale'; import { logError } from '@/utils/errorHandler'; +import { parseQueryParam } from '@/utils/parseQueryParam'; type Props = { projectId: string; @@ -30,62 +31,75 @@ export default function CasesPane({ locale, }: Props) { const [cases, setCases] = useState([]); - const context = useContext(TokenContext); const [isCaseDialogOpen, setIsCaseDialogOpen] = useState(false); const [priorityFilter, setPriorityFilter] = useState([]); const [typeFilter, setTypeFilter] = useState([]); + const [queryTerm, setQueryTerm] = useState(''); + const [isDeleteConfirmDialogOpen, setIsDeleteConfirmDialogOpen] = useState(false); + const [deleteCaseIds, setDeleteCaseIds] = useState([]); + const [isSearching, setIsSearching] = useState(false); + + const context = useContext(TokenContext); const router = useRouter(); const searchParams = useSearchParams(); + const updateUrlParams = (updates: { priority?: number[]; type?: number[]; q?: string }) => { + const currentParams = new URLSearchParams(searchParams.toString()); + + if (updates.priority && updates.priority.length > 0) { + currentParams.set('priority', updates.priority.join(',')); + } else { + currentParams.delete('priority'); + } + + if (updates.type && updates.type.length > 0) { + currentParams.set('type', updates.type.join(',')); + } else { + currentParams.delete('type'); + } + + if (updates.q) { + currentParams.set('q', updates.q); + } else { + currentParams.delete('q'); + } + + const newUrl = `${window.location.pathname}?${currentParams.toString()}`; + router.push(newUrl, { scroll: false }); + }; + useEffect(() => { async function fetchDataEffect() { - if (!context.isSignedIn()) { - return; - } + if (!context.isSignedIn()) return; - const priorityParam = searchParams.get('priority'); - let currentPriorityFilter: number[] = []; - if (priorityParam) { - currentPriorityFilter = priorityParam - .split(',') - .map((p) => parseInt(p.trim())) - .filter((p) => !isNaN(p)); - setPriorityFilter(currentPriorityFilter); - } else { - setPriorityFilter([]); - } + const priorityParam = parseQueryParam(searchParams.get('priority')); + const typeParam = parseQueryParam(searchParams.get('type')); + const queryParam = searchParams.get('q') || ''; - const typeParam = searchParams.get('type'); - let currentTypeFilter: number[] = []; - if (typeParam) { - currentTypeFilter = typeParam - .split(',') - .map((t) => parseInt(t.trim())) - .filter((t) => !isNaN(t)); - setTypeFilter(currentTypeFilter); - } else { - setTypeFilter([]); - } + setPriorityFilter(priorityParam); + setTypeFilter(typeParam); + setQueryTerm(queryParam); try { const data = await fetchCases( context.token.access_token, Number(folderId), - currentPriorityFilter.length > 0 ? currentPriorityFilter : undefined, - currentTypeFilter.length > 0 ? currentTypeFilter : undefined + priorityParam.length > 0 ? priorityParam : undefined, + typeParam.length > 0 ? typeParam : undefined, + queryParam || undefined ); setCases(data); } catch (error: unknown) { logError('Error fetching cases:', error); + } finally { + setIsSearching(false); } } fetchDataEffect(); }, [context, folderId, searchParams]); - const closeDialog = () => { - setIsCaseDialogOpen(false); - }; + const closeDialog = () => setIsCaseDialogOpen(false); const onSubmit = async (title: string, description: string) => { const newCase = await createCase(context.token.access_token, folderId, title, description); @@ -93,14 +107,12 @@ export default function CasesPane({ closeDialog(); }; - const [isDeleteConfirmDialogOpen, setIsDeleteConfirmDialogOpen] = useState(false); - const [deleteCaseIds, setDeleteCaseIds] = useState([]); const closeDeleteConfirmDialog = () => { setIsDeleteConfirmDialogOpen(false); setDeleteCaseIds([]); }; - const onDeleteCase = async (deleteCaseId: number) => { + const onDeleteCase = (deleteCaseId: number) => { setDeleteCaseIds([deleteCaseId]); setIsDeleteConfirmDialogOpen(true); }; @@ -125,23 +137,15 @@ export default function CasesPane({ const handleFilterChange = (priorities: number[], types: number[]) => { setPriorityFilter(priorities); setTypeFilter(types); + updateUrlParams({ priority: priorities, type: types, q: queryTerm }); + }; - const currentParams = new URLSearchParams(searchParams.toString()); - - if (priorities.length > 0) { - currentParams.set('priority', priorities.join(',')); - } else { - currentParams.delete('priority'); + const handleQueryChange = (q: string) => { + setQueryTerm(q); + if (q.trim()) { + setIsSearching(true); } - - if (types.length > 0) { - currentParams.set('type', types.join(',')); - } else { - currentParams.delete('type'); - } - - const newUrl = `${window.location.pathname}?${currentParams.toString()}`; - router.push(newUrl, { scroll: false }); + updateUrlParams({ priority: priorityFilter, type: typeFilter, q }); }; return ( @@ -155,12 +159,15 @@ export default function CasesPane({ onDeleteCases={onDeleteCases} onExportCases={onExportCases} onFilterChange={handleFilterChange} + onQueryChange={handleQueryChange} activePriorityFilters={priorityFilter} activeTypeFilters={typeFilter} messages={messages} priorityMessages={priorityMessages} testTypeMessages={testTypeMessages} locale={locale} + queryTerm={queryTerm} + isSearching={isSearching} /> diff --git a/frontend/src/app/[locale]/projects/[projectId]/folders/[folderId]/cases/TestCaseTable.tsx b/frontend/src/app/[locale]/projects/[projectId]/folders/[folderId]/cases/TestCaseTable.tsx index 0ce1655..83f48b3 100644 --- a/frontend/src/app/[locale]/projects/[projectId]/folders/[folderId]/cases/TestCaseTable.tsx +++ b/frontend/src/app/[locale]/projects/[projectId]/folders/[folderId]/cases/TestCaseTable.tsx @@ -1,4 +1,4 @@ -import { useState, useMemo, useCallback, ReactNode } from 'react'; +import { useState, useMemo, useCallback, ReactNode, useEffect } from 'react'; import { Table, TableHeader, @@ -16,8 +16,20 @@ import { ButtonGroup, cn, Badge, + Input, + Spinner, } from '@heroui/react'; -import { Plus, MoreVertical, Trash, FileDown, ChevronDown, Filter, FileJson, FileSpreadsheet } from 'lucide-react'; +import { + Plus, + MoreVertical, + Trash, + FileDown, + ChevronDown, + Filter, + FileJson, + FileSpreadsheet, + SearchIcon, +} from 'lucide-react'; import TestCaseFilter from './TestCaseFilter'; import { Link } from '@/src/i18n/routing'; import { CaseType, CasesMessages } from '@/types/case'; @@ -25,6 +37,8 @@ import { PriorityMessages } from '@/types/priority'; import { TestTypeMessages } from '@/types/testType'; import TestCasePriority from '@/components/TestCasePriority'; import { LocaleCodeType } from '@/types/locale'; +import useDebounce from '@/utils/useDebounce'; +import { highlightSearchTerm } from '@/utils/highlightSearchTerm'; type Props = { projectId: string; @@ -35,12 +49,15 @@ type Props = { onDeleteCases: (caseIds: number[]) => void; onExportCases: (type: string) => void; onFilterChange: (priorities: number[], types: number[]) => void; + onQueryChange: (q: string) => void; + queryTerm: string; activePriorityFilters: number[]; activeTypeFilters: number[]; messages: CasesMessages; priorityMessages: PriorityMessages; testTypeMessages: TestTypeMessages; locale: LocaleCodeType; + isSearching: boolean; }; export default function TestCaseTable({ @@ -52,12 +69,15 @@ export default function TestCaseTable({ onDeleteCases, onExportCases, onFilterChange, + onQueryChange, activePriorityFilters, activeTypeFilters, messages, priorityMessages, testTypeMessages, locale, + queryTerm, + isSearching, }: Props) { const headerColumns = [ { name: messages.id, uid: 'id', sortable: true }, @@ -73,6 +93,15 @@ export default function TestCaseTable({ }); const [exportType, setExportType] = useState(new Set(['json'])); const [showFilter, setShowFilter] = useState(false); + const [localQueryTerm, setLocalQueryTerm] = useState(queryTerm); + + const debouncedQuery = useDebounce((value: string) => { + onQueryChange(value); + }, 500); + + useEffect(() => { + setLocalQueryTerm(queryTerm); + }, [queryTerm]); const sortedItems = useMemo(() => { if (cases.length === 0) { @@ -95,52 +124,65 @@ export default function TestCaseTable({ setShowFilter(!showFilter); }; - const renderCell = useCallback((testCase: CaseType, columnKey: string): ReactNode => { - const cellValue = testCase[columnKey as keyof CaseType]; - - switch (columnKey) { - case 'id': - return {cellValue as number}; - case 'title': - return ( - - ); - case 'priority': - return ; - case 'actions': - return ( - - - - - - handleDeleteCase(testCase.id)} - > - {messages.deleteCase} - - - - ); - default: - return cellValue as string; + const handleQueryChange = (value: string) => { + setLocalQueryTerm(value); + if (value.length >= 2 || value.length === 0) { + debouncedQuery(value); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }; + + const renderCell = useCallback( + (testCase: CaseType, columnKey: string): ReactNode => { + const cellValue = testCase[columnKey as keyof CaseType]; + + switch (columnKey) { + case 'id': + return {cellValue as number}; + case 'title': + return ( + + ); + case 'priority': + return ; + case 'actions': + return ( + + + + + + handleDeleteCase(testCase.id)} + > + {messages.deleteCase} + + + + ); + default: + return cellValue as string; + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, + [localQueryTerm] + ); const handleDeleteCases = () => { let deleteCaseIds: number[]; @@ -183,7 +225,25 @@ export default function TestCaseTable({

{messages.testCaseList}

-
+
+ } + endContent={isSearching && } + type="search" + value={localQueryTerm} + onValueChange={handleQueryChange} + aria-label={messages.searchPlaceholder} + maxLength={100} + /> 0) { @@ -37,6 +37,10 @@ async function fetchCases(jwt: string, folderId: number, priority?: number[], ty queryParams.push(`type=${type.join(',')}`); } + if (q) { + queryParams.push(`q=${q}`); + } + const query = queryParams.length > 0 ? `?${queryParams.join('&')}` : ''; const url = `${apiServer}/cases${query}`; diff --git a/frontend/utils/highlightSearchTerm.tsx b/frontend/utils/highlightSearchTerm.tsx new file mode 100644 index 0000000..eb1216c --- /dev/null +++ b/frontend/utils/highlightSearchTerm.tsx @@ -0,0 +1,38 @@ +import { cn } from '@heroui/theme'; +import { ReactNode } from 'react'; + +interface HighlightSearchTermProps { + text: string; + searchTerm: string; + className?: string; + minSearchLength?: number; +} + +export function highlightSearchTerm({ + text, + searchTerm, + className, + minSearchLength = 2, +}: HighlightSearchTermProps): ReactNode { + if (!text || !searchTerm || searchTerm.length < minSearchLength) { + return text || null; + } + + const regex = new RegExp(`(${searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi'); + const parts = text.split(regex); + + return ( + <> + {parts.map((part, index) => { + if (regex.test(part)) { + return ( + + {part} + + ); + } + return part; + })} + + ); +} diff --git a/frontend/utils/parseQueryParam.ts b/frontend/utils/parseQueryParam.ts new file mode 100644 index 0000000..b4d8f4a --- /dev/null +++ b/frontend/utils/parseQueryParam.ts @@ -0,0 +1,8 @@ +export const parseQueryParam = (param: string | null): number[] => { + return param + ? param + .split(',') + .map((p) => parseInt(p.trim())) + .filter((p) => !isNaN(p)) + : []; +}; diff --git a/frontend/utils/useDebounce.ts b/frontend/utils/useDebounce.ts new file mode 100644 index 0000000..7c3a82f --- /dev/null +++ b/frontend/utils/useDebounce.ts @@ -0,0 +1,30 @@ +import { useRef, useCallback, useEffect } from 'react'; + +export default function useDebounce void>( + fn: T, + delay: number +): (...args: Parameters) => void { + const timeoutRef = useRef(null); + + const debounceFn = useCallback( + (...args: Parameters): void => { + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + } + timeoutRef.current = setTimeout(() => { + fn(...args); + }, delay); + }, + [fn, delay] + ); + + useEffect(() => { + return () => { + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + } + }; + }, []); + + return debounceFn; +} From a7882dd76941a7554168ffb6fe3de34c4886a908 Mon Sep 17 00:00:00 2001 From: kimatata <117462761+kimatata@users.noreply.github.com> Date: Sat, 30 Aug 2025 23:38:07 +0900 Subject: [PATCH 3/8] fix: eslint warnings (#278) --- eslint.config.mjs | 2 +- .../[projectId]/folders/[folderId]/cases/TestCaseTable.tsx | 6 +++--- frontend/utils/useDebounce.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 045d8f7..1be3b86 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -12,7 +12,7 @@ import eslintPluginNext from '@next/eslint-plugin-next'; export default tseslint.config( { name: 'unittcms/ignore-globally', - ignores: ['**/node_modules/', '**/.next/', '**/docs/'], + ignores: ['**/node_modules/', '**/.next/', '**/docs/', '**/coverage/'], }, { name: 'unittcms/load-plugins', diff --git a/frontend/src/app/[locale]/projects/[projectId]/folders/[folderId]/cases/TestCaseTable.tsx b/frontend/src/app/[locale]/projects/[projectId]/folders/[folderId]/cases/TestCaseTable.tsx index 83f48b3..401ad69 100644 --- a/frontend/src/app/[locale]/projects/[projectId]/folders/[folderId]/cases/TestCaseTable.tsx +++ b/frontend/src/app/[locale]/projects/[projectId]/folders/[folderId]/cases/TestCaseTable.tsx @@ -95,8 +95,8 @@ export default function TestCaseTable({ const [showFilter, setShowFilter] = useState(false); const [localQueryTerm, setLocalQueryTerm] = useState(queryTerm); - const debouncedQuery = useDebounce((value: string) => { - onQueryChange(value); + const debouncedQuery = useDebounce((value: unknown) => { + onQueryChange(value as string); }, 500); useEffect(() => { @@ -179,8 +179,8 @@ export default function TestCaseTable({ default: return cellValue as string; } - // eslint-disable-next-line react-hooks/exhaustive-deps }, + // eslint-disable-next-line react-hooks/exhaustive-deps [localQueryTerm] ); diff --git a/frontend/utils/useDebounce.ts b/frontend/utils/useDebounce.ts index 7c3a82f..1c6cf8e 100644 --- a/frontend/utils/useDebounce.ts +++ b/frontend/utils/useDebounce.ts @@ -1,6 +1,6 @@ import { useRef, useCallback, useEffect } from 'react'; -export default function useDebounce void>( +export default function useDebounce void>( fn: T, delay: number ): (...args: Parameters) => void { From 72fd3af24f71fd709fff46582da11e58941ecceb Mon Sep 17 00:00:00 2001 From: kimatata <117462761+kimatata@users.noreply.github.com> Date: Sat, 13 Sep 2025 12:11:22 +0900 Subject: [PATCH 4/8] chore: test code for cases index (#279) --- backend/routes/cases/{index.js => index.mjs} | 16 ++-- backend/routes/cases/index.test.js | 74 +++++++++++++++ package-lock.json | 97 +++++++++++++------- package.json | 2 +- 4 files changed, 148 insertions(+), 41 deletions(-) rename backend/routes/cases/{index.js => index.mjs} (78%) create mode 100644 backend/routes/cases/index.test.js diff --git a/backend/routes/cases/index.js b/backend/routes/cases/index.mjs similarity index 78% rename from backend/routes/cases/index.js rename to backend/routes/cases/index.mjs index c525ba4..a5bb515 100644 --- a/backend/routes/cases/index.js +++ b/backend/routes/cases/index.mjs @@ -1,12 +1,14 @@ -const express = require('express'); +import express from 'express'; const router = express.Router(); -const { DataTypes, Op } = require('sequelize'); -const defineCase = require('../../models/cases'); +import { DataTypes, Op } from 'sequelize'; +import defineCase from '../../models/cases'; +import authMiddleware from '../../middleware/auth.js'; +import visibilityMiddleware from '../../middleware/verifyVisible'; -module.exports = function (sequelize) { +export default function (sequelize) { + const { verifySignedIn } = authMiddleware(sequelize); const Case = defineCase(sequelize, DataTypes); - const { verifySignedIn } = require('../../middleware/auth')(sequelize); - const { verifyProjectVisibleFromFolderId } = require('../../middleware/verifyVisible')(sequelize); + const { verifyProjectVisibleFromFolderId } = visibilityMiddleware(sequelize); router.get('/', verifySignedIn, verifyProjectVisibleFromFolderId, async (req, res) => { const { folderId, priority, type, q } = req.query; @@ -63,4 +65,4 @@ module.exports = function (sequelize) { }); return router; -}; +} diff --git a/backend/routes/cases/index.test.js b/backend/routes/cases/index.test.js new file mode 100644 index 0000000..c8845f2 --- /dev/null +++ b/backend/routes/cases/index.test.js @@ -0,0 +1,74 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import request from 'supertest'; +import express from 'express'; +import { Sequelize, Op } from 'sequelize'; +import casesIndexRoute from './index'; + +// mock of authentication middleware +vi.mock('../../middleware/auth', () => ({ + default: () => ({ + verifySignedIn: vi.fn((req, res, next) => { + req.userId = 1; + next(); + }), + }), +})); +vi.mock('../../middleware/verifyVisible', () => ({ + default: () => ({ + verifyProjectVisibleFromFolderId: vi.fn((req, res, next) => { + next(); + }), + }), +})); + +// mock defineCase +const mockCase = { + findAll: vi.fn(), +}; +vi.mock('../../models/cases', () => ({ + default: () => mockCase, +})); + +describe('GET /cases', () => { + let app; + const sequelize = new Sequelize({ + dialect: 'sqlite', + logging: false, + }); + + beforeEach(() => { + app = express(); + app.use(express.json()); + + // Mount the index route + app.use('/cases', casesIndexRoute(sequelize)); + }); + + it('should return 400 if folderId is missing', async () => { + const res = await request(app).get('/cases'); + expect(res.status).toBe(400); + expect(res.body.error).toBe('folderId is required'); + }); + + it('should call findAll with correct where clause for folderId only', async () => { + mockCase.findAll.mockResolvedValue([{ id: 1 }]); + const res = await request(app).get('/cases?folderId=1'); + expect(res.status).toBe(200); + expect(mockCase.findAll).toHaveBeenCalledWith({ where: { folderId: '1' } }); + expect(res.body).toEqual([{ id: 1 }]); + }); + + it('should build a where clause based on query parameters.', async () => { + mockCase.findAll.mockResolvedValue([{ id: 1 }]); + const res = await request(app).get('/cases?folderId=1&priority=1,2&type=3'); + expect(res.status).toBe(200); + + expect(mockCase.findAll).toHaveBeenCalledWith({ + where: { + folderId: '1', + priority: { [Op.in]: [1, 2] }, + type: { [Op.in]: [3] }, + }, + }); + }); +}); diff --git a/package-lock.json b/package-lock.json index e4c0519..80bf588 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,7 +24,7 @@ "prettier": "^3.3.3", "sequelize": "^6.37.4", "sqlite3": "^5.1.7", - "supertest": "^7.0.0", + "supertest": "^7.1.4", "typescript-eslint": "^8.24.1", "vitest": "^1.6.0" } @@ -803,6 +803,19 @@ "fast-glob": "3.3.1" } }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -873,6 +886,16 @@ "node": ">=10" } }, + "node_modules/@paralleldrive/cuid2": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.2.2.tgz", + "integrity": "sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "^1.1.5" + } + }, "node_modules/@playwright/test": { "version": "1.51.1", "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.51.1.tgz", @@ -2123,7 +2146,8 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/assertion-error": { "version": "1.1.0", @@ -2153,7 +2177,8 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/available-typed-arrays": { "version": "1.0.7", @@ -2521,6 +2546,7 @@ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, + "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" }, @@ -2533,6 +2559,7 @@ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/sindresorhus" } @@ -2596,7 +2623,8 @@ "version": "2.1.4", "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/cross-spawn": { "version": "7.0.6", @@ -2767,6 +2795,7 @@ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.4.0" } @@ -2811,6 +2840,7 @@ "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", "dev": true, + "license": "ISC", "dependencies": { "asap": "^2.0.0", "wrappy": "1" @@ -3762,7 +3792,8 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fastq": { "version": "1.19.1", @@ -3887,14 +3918,16 @@ } }, "node_modules/form-data": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", - "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", "dev": true, + "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { @@ -3902,15 +3935,19 @@ } }, "node_modules/formidable": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.2.tgz", - "integrity": "sha512-Jqc1btCy3QzRbJaICGwKcBfGWuLADRerLzDqi2NwSt/UkXLsHJw2TVResiaoBufHVHy9aSgClOHCeJsSsFLTbg==", + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz", + "integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==", "dev": true, + "license": "MIT", "dependencies": { + "@paralleldrive/cuid2": "^2.2.2", "dezalgo": "^1.0.4", - "hexoid": "^2.0.0", "once": "^1.4.0" }, + "engines": { + "node": ">=14.0.0" + }, "funding": { "url": "https://ko-fi.com/tunnckoCore/commissions" } @@ -4303,15 +4340,6 @@ "node": ">= 0.4" } }, - "node_modules/hexoid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-2.0.0.tgz", - "integrity": "sha512-qlspKUK7IlSQv2o+5I7yhUd7TxlOG2Vr5LTa3ve2XSNVKAL/n/u/7KLvKmFNimomDIKvZFXWHv0T12mv7rT8Aw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -7310,20 +7338,21 @@ "dev": true }, "node_modules/superagent": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-9.0.2.tgz", - "integrity": "sha512-xuW7dzkUpcJq7QnhOsnNUgtYp3xRwpt2F7abdRYIpCsAt0hhUqia0EdxyXZQQpNmGtsCzYHryaKSV3q3GJnq7w==", + "version": "10.2.3", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.2.3.tgz", + "integrity": "sha512-y/hkYGeXAj7wUMjxRbB21g/l6aAEituGXM9Rwl4o20+SX3e8YOSV6BxFXl+dL3Uk0mjSL3kCbNkwURm8/gEDig==", "dev": true, + "license": "MIT", "dependencies": { - "component-emitter": "^1.3.0", + "component-emitter": "^1.3.1", "cookiejar": "^2.1.4", - "debug": "^4.3.4", + "debug": "^4.3.7", "fast-safe-stringify": "^2.1.1", - "form-data": "^4.0.0", - "formidable": "^3.5.1", + "form-data": "^4.0.4", + "formidable": "^3.5.4", "methods": "^1.1.2", "mime": "2.6.0", - "qs": "^6.11.0" + "qs": "^6.11.2" }, "engines": { "node": ">=14.18.0" @@ -7334,6 +7363,7 @@ "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", "dev": true, + "license": "MIT", "bin": { "mime": "cli.js" }, @@ -7342,13 +7372,14 @@ } }, "node_modules/supertest": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.1.0.tgz", - "integrity": "sha512-5QeSO8hSrKghtcWEoPiO036fxH0Ii2wVQfFZSP0oqQhmjk8bOLhDFXr4JrvaFmPuEWUoq4znY3uSi8UzLKxGqw==", + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.1.4.tgz", + "integrity": "sha512-tjLPs7dVyqgItVFirHYqe2T+MfWc2VOBQ8QFKKbWTA3PU7liZR8zoSpAi/C1k1ilm9RsXIKYf197oap9wXGVYg==", "dev": true, + "license": "MIT", "dependencies": { "methods": "^1.1.2", - "superagent": "^9.0.1" + "superagent": "^10.2.3" }, "engines": { "node": ">=14.18.0" diff --git a/package.json b/package.json index 83940e3..0d4c8c0 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "prettier": "^3.3.3", "sequelize": "^6.37.4", "sqlite3": "^5.1.7", - "supertest": "^7.0.0", + "supertest": "^7.1.4", "typescript-eslint": "^8.24.1", "vitest": "^1.6.0" } From 761869736e91e2c61d4812efd4cc8c1b17af8596 Mon Sep 17 00:00:00 2001 From: kimatata <117462761+kimatata@users.noreply.github.com> Date: Mon, 15 Sep 2025 22:46:46 +0900 Subject: [PATCH 5/8] chore: Unify backend express project into ESM (#281) --- Dockerfile | 6 +- backend/config/config.js | 9 +- backend/index.js | 3 +- backend/middleware/auth.js | 12 +- backend/middleware/verifyEditable.js | 18 +- backend/middleware/verifyVisible.js | 16 +- .../migrations/20240201220834-create-users.js | 85 +- .../20240204090345-create-projects.js | 89 +- .../20240212022145-create-folders.js | 89 +- .../migrations/20240212082050-create-runs.js | 97 +- .../migrations/20240212084228-create-cases.js | 137 +- .../migrations/20240229095914-create-steps.js | 61 +- .../20240229100158-create-cases-steps.js | 95 +- .../20240316200815-create-attachments.js | 69 +- ...20240316201158-create-cases-attachments.js | 87 +- .../20240407182431-create-runs-cases.js | 95 +- .../20240529221509-create-members.js | 95 +- .../migrations/20240930124156-rename_table.js | 43 +- ...-rename-path-to-filename-in-attachments.js | 20 +- ...175803-add-cascade-delete-parent-folder.js | 37 +- backend/models/attachments.js | 2 +- backend/models/caseAttachments.js | 2 +- backend/models/caseSteps.js | 2 +- backend/models/cases.js | 2 +- backend/models/folders.js | 2 +- backend/models/index.js | 36 +- backend/models/members.js | 2 +- backend/models/projects.js | 2 +- backend/models/runCases.js | 2 +- backend/models/runs.js | 2 +- backend/models/steps.js | 2 +- backend/models/users.js | 8 +- backend/package-lock.json | 926 +++++------ backend/package.json | 5 +- backend/routes/attachments/delete.js | 18 +- backend/routes/attachments/download.js | 19 +- backend/routes/attachments/new.js | 22 +- backend/routes/cases/delete.js | 16 +- backend/routes/cases/download.js | 18 +- backend/routes/cases/edit.js | 16 +- backend/routes/cases/{index.mjs => index.js} | 6 +- backend/routes/cases/index.test.js | 6 +- backend/routes/cases/indexByProjectId.js | 22 +- backend/routes/cases/new.js | 16 +- backend/routes/cases/show.js | 20 +- backend/routes/folders/delete.js | 16 +- backend/routes/folders/edit.js | 16 +- backend/routes/folders/index.js | 16 +- backend/routes/folders/new.js | 16 +- backend/routes/health/index.js | 6 +- backend/routes/home/index.js | 24 +- backend/routes/index.js | 13 +- backend/routes/members/check.js | 15 +- backend/routes/members/delete.js | 16 +- backend/routes/members/edit.js | 16 +- backend/routes/members/index.js | 18 +- backend/routes/members/new.js | 18 +- backend/routes/projects/delete.js | 20 +- backend/routes/projects/edit.js | 16 +- backend/routes/projects/index.js | 15 +- backend/routes/projects/new.js | 13 +- backend/routes/projects/show.js | 18 +- backend/routes/runcases/edit.js | 16 +- backend/routes/runcases/index.js | 16 +- backend/routes/runs/delete.js | 16 +- backend/routes/runs/download.js | 27 +- backend/routes/runs/edit.js | 16 +- backend/routes/runs/index.js | 16 +- backend/routes/runs/new.js | 16 +- backend/routes/runs/show.js | 18 +- backend/routes/steps/edit.js | 18 +- backend/routes/users/authSettings.js | 2 +- backend/routes/users/find.js | 13 +- backend/routes/users/index.js | 13 +- backend/routes/users/search.js | 15 +- backend/routes/users/signin.js | 16 +- backend/routes/users/signup.js | 16 +- .../routes/users/{update.mjs => update.js} | 0 backend/routes/users/update.test.js | 6 +- backend/seeders/seed.js | 1367 ++++++++--------- backend/server.js | 186 ++- combined_server.js => entrypoint.js | 54 +- package.json | 1 + 83 files changed, 2129 insertions(+), 2328 deletions(-) rename backend/routes/cases/{index.mjs => index.js} (97%) rename backend/routes/users/{update.mjs => update.js} (100%) rename combined_server.js => entrypoint.js (65%) diff --git a/Dockerfile b/Dockerfile index 8368652..810b225 100644 --- a/Dockerfile +++ b/Dockerfile @@ -61,11 +61,11 @@ COPY backend/package.json backend/package-lock.json* ./ RUN npm ci WORKDIR /app -# Copy custom combined_server.js that combines frontend and backend -COPY combined_server.js ./ +# Copy custom entrypoint.js that combines frontend and backend +COPY entrypoint.js ./ # Expose the port EXPOSE 8000 # Run database migrations and start the combined server -CMD ["node", "combined_server.js"] +CMD ["node", "entrypoint.js"] diff --git a/backend/config/config.js b/backend/config/config.js index 962fb95..17ba1f6 100644 --- a/backend/config/config.js +++ b/backend/config/config.js @@ -1,7 +1,12 @@ -const path = require('path'); +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + const databasePath = path.resolve(__dirname, '../database/database.sqlite'); -module.exports = { +export default { development: { dialect: 'sqlite', storage: databasePath, diff --git a/backend/index.js b/backend/index.js index 13a5f6b..eb52ffe 100644 --- a/backend/index.js +++ b/backend/index.js @@ -1,5 +1,4 @@ -const server = require('./server'); - +import server from './server.js'; const PORT = process.env.PORT || 8001; server.listen(PORT, () => { diff --git a/backend/middleware/auth.js b/backend/middleware/auth.js index daf6e53..4df1b5c 100644 --- a/backend/middleware/auth.js +++ b/backend/middleware/auth.js @@ -1,9 +1,9 @@ -const jwt = require('jsonwebtoken'); -const { DataTypes } = require('sequelize'); -const { roles, defaultDangerKey } = require('../routes/users/authSettings'); -const defineUser = require('../models/users'); +import jwt from 'jsonwebtoken'; +import { DataTypes } from 'sequelize'; +import { roles, defaultDangerKey } from '../routes/users/authSettings.js'; +import defineUser from '../models/users.js'; -function authMiddleware(sequelize) { +export default function authMiddleware(sequelize) { /** * Verify user sined in * @@ -53,5 +53,3 @@ function authMiddleware(sequelize) { verifyAdmin, }; } - -module.exports = authMiddleware; diff --git a/backend/middleware/verifyEditable.js b/backend/middleware/verifyEditable.js index af6120b..7be9aa2 100644 --- a/backend/middleware/verifyEditable.js +++ b/backend/middleware/verifyEditable.js @@ -1,12 +1,12 @@ -const { DataTypes } = require('sequelize'); -const { memberRoles } = require('../routes/users/authSettings'); -const defineMember = require('../models/members'); -const defineProject = require('../models/projects'); -const defineFolder = require('../models/folders'); -const defineCase = require('../models/cases'); -const defineRun = require('../models/runs'); +import { DataTypes } from 'sequelize'; +import { memberRoles } from '../routes/users/authSettings.js'; +import defineMember from '../models/members.js'; +import defineProject from '../models/projects.js'; +import defineFolder from '../models/folders.js'; +import defineCase from '../models/cases.js'; +import defineRun from '../models/runs.js'; -function verifyEditableMiddleware(sequelize) { +export default function verifyEditableMiddleware(sequelize) { /** * Verify user has project * (have to be called after verifySignedIn() middleware) @@ -291,5 +291,3 @@ function verifyEditableMiddleware(sequelize) { verifyProjectReporterFromRunId, }; } - -module.exports = verifyEditableMiddleware; diff --git a/backend/middleware/verifyVisible.js b/backend/middleware/verifyVisible.js index 59a608c..640e2ce 100644 --- a/backend/middleware/verifyVisible.js +++ b/backend/middleware/verifyVisible.js @@ -1,11 +1,11 @@ -const { DataTypes } = require('sequelize'); -const defineMember = require('../models/members'); -const defineProject = require('../models/projects'); -const defineFolder = require('../models/folders'); -const defineCase = require('../models/cases'); -const defineRun = require('../models/runs'); +import { DataTypes } from 'sequelize'; +import defineMember from '../models/members.js'; +import defineProject from '../models/projects.js'; +import defineFolder from '../models/folders.js'; +import defineCase from '../models/cases.js'; +import defineRun from '../models/runs.js'; -function verifyVisibleMiddleware(sequelize) { +export default function verifyVisibleMiddleware(sequelize) { /** * Verify user can read project by projectId * (have to be called after verifySignedIn() middleware) @@ -160,5 +160,3 @@ function verifyVisibleMiddleware(sequelize) { verifyProjectVisibleFromRunId, }; } - -module.exports = verifyVisibleMiddleware; diff --git a/backend/migrations/20240201220834-create-users.js b/backend/migrations/20240201220834-create-users.js index 04e7d06..ef708e9 100644 --- a/backend/migrations/20240201220834-create-users.js +++ b/backend/migrations/20240201220834-create-users.js @@ -1,46 +1,41 @@ -'use strict'; +export async function up(queryInterface, Sequelize) { + await queryInterface.createTable('users', { + id: { + type: Sequelize.INTEGER, + primaryKey: true, + autoIncrement: true, + }, + email: { + type: Sequelize.STRING, + allowNull: false, + unique: true, + }, + password: { + type: Sequelize.STRING, + allowNull: false, + }, + username: { + type: Sequelize.STRING, + allowNull: false, + }, + role: { + type: Sequelize.INTEGER, + allowNull: false, + }, + avatarPath: { + type: Sequelize.STRING, + }, + createdAt: { + type: Sequelize.DATE, + allowNull: false, + }, + updatedAt: { + type: Sequelize.DATE, + allowNull: false, + }, + }); +} -/** @type {import('sequelize-cli').Migration} */ -module.exports = { - up: async (queryInterface, Sequelize) => { - await queryInterface.createTable('users', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - }, - email: { - type: Sequelize.STRING, - allowNull: false, - unique: true, - }, - password: { - type: Sequelize.STRING, - allowNull: false, - }, - username: { - type: Sequelize.STRING, - allowNull: false, - }, - role: { - type: Sequelize.INTEGER, - allowNull: false, - }, - avatarPath: { - type: Sequelize.STRING, - }, - createdAt: { - type: Sequelize.DATE, - allowNull: false, - }, - updatedAt: { - type: Sequelize.DATE, - allowNull: false, - }, - }); - }, - - down: async (queryInterface) => { - await queryInterface.dropTable('users'); - }, -}; +export async function down(queryInterface) { + await queryInterface.dropTable('users'); +} diff --git a/backend/migrations/20240204090345-create-projects.js b/backend/migrations/20240204090345-create-projects.js index 1603d3a..adc272d 100644 --- a/backend/migrations/20240204090345-create-projects.js +++ b/backend/migrations/20240204090345-create-projects.js @@ -1,48 +1,43 @@ -'use strict'; +export async function up(queryInterface, Sequelize) { + await queryInterface.createTable('projects', { + id: { + type: Sequelize.INTEGER, + primaryKey: true, + autoIncrement: true, + }, + name: { + type: Sequelize.STRING, + allowNull: false, + }, + detail: { + type: Sequelize.STRING, + allowNull: true, + }, + isPublic: { + type: Sequelize.BOOLEAN, + allowNull: false, + }, + userId: { + type: Sequelize.INTEGER, + allowNull: false, + references: { + model: 'users', + key: 'id', + onDelete: 'CASCADE', + onUpdate: 'CASCADE', + }, + }, + createdAt: { + type: Sequelize.DATE, + allowNull: false, + }, + updatedAt: { + type: Sequelize.DATE, + allowNull: false, + }, + }); +} -/** @type {import('sequelize-cli').Migration} */ -module.exports = { - up: async (queryInterface, Sequelize) => { - await queryInterface.createTable('projects', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - }, - name: { - type: Sequelize.STRING, - allowNull: false, - }, - detail: { - type: Sequelize.STRING, - allowNull: true, - }, - isPublic: { - type: Sequelize.BOOLEAN, - allowNull: false, - }, - userId: { - type: Sequelize.INTEGER, - allowNull: false, - references: { - model: 'users', - key: 'id', - onDelete: 'CASCADE', - onUpdate: 'CASCADE', - }, - }, - createdAt: { - type: Sequelize.DATE, - allowNull: false, - }, - updatedAt: { - type: Sequelize.DATE, - allowNull: false, - }, - }); - }, - - down: async (queryInterface) => { - await queryInterface.dropTable('projects'); - }, -}; +export async function down(queryInterface) { + await queryInterface.dropTable('projects'); +} diff --git a/backend/migrations/20240212022145-create-folders.js b/backend/migrations/20240212022145-create-folders.js index 64806ab..0fcca8f 100644 --- a/backend/migrations/20240212022145-create-folders.js +++ b/backend/migrations/20240212022145-create-folders.js @@ -1,48 +1,43 @@ -'use strict'; +export async function up(queryInterface, Sequelize) { + await queryInterface.createTable('folders', { + id: { + type: Sequelize.INTEGER, + primaryKey: true, + autoIncrement: true, + }, + name: { + type: Sequelize.STRING, + allowNull: false, + }, + detail: { + type: Sequelize.STRING, + allowNull: true, + }, + parentFolderId: { + type: Sequelize.INTEGER, + allowNull: true, + }, + projectId: { + type: Sequelize.INTEGER, + allowNull: false, + references: { + model: 'projects', + key: 'id', + onDelete: 'CASCADE', + onUpdate: 'CASCADE', + }, + }, + createdAt: { + type: Sequelize.DATE, + allowNull: false, + }, + updatedAt: { + type: Sequelize.DATE, + allowNull: false, + }, + }); +} -/** @type {import('sequelize-cli').Migration} */ -module.exports = { - up: async (queryInterface, Sequelize) => { - await queryInterface.createTable('folders', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - }, - name: { - type: Sequelize.STRING, - allowNull: false, - }, - detail: { - type: Sequelize.STRING, - allowNull: true, - }, - parentFolderId: { - type: Sequelize.INTEGER, - allowNull: true, - }, - projectId: { - type: Sequelize.INTEGER, - allowNull: false, - references: { - model: 'projects', - key: 'id', - onDelete: 'CASCADE', - onUpdate: 'CASCADE', - }, - }, - createdAt: { - type: Sequelize.DATE, - allowNull: false, - }, - updatedAt: { - type: Sequelize.DATE, - allowNull: false, - }, - }); - }, - - async down(queryInterface) { - await queryInterface.dropTable('folders'); - }, -}; +export async function down(queryInterface) { + await queryInterface.dropTable('folders'); +} diff --git a/backend/migrations/20240212082050-create-runs.js b/backend/migrations/20240212082050-create-runs.js index 55e9fc0..4485667 100644 --- a/backend/migrations/20240212082050-create-runs.js +++ b/backend/migrations/20240212082050-create-runs.js @@ -1,52 +1,47 @@ -'use strict'; +export async function up(queryInterface, Sequelize) { + await queryInterface.createTable('runs', { + id: { + type: Sequelize.INTEGER, + primaryKey: true, + autoIncrement: true, + }, + name: { + type: Sequelize.STRING, + allowNull: false, + }, + configurations: { + type: Sequelize.STRING, + allowNull: true, + }, + description: { + type: Sequelize.STRING, + allowNull: true, + }, + state: { + type: Sequelize.INTEGER, + allowNull: true, + }, + projectId: { + type: Sequelize.INTEGER, + allowNull: false, + references: { + model: 'projects', + key: 'id', + }, + onUpdate: 'CASCADE', + onDelete: 'CASCADE', + }, + createdAt: { + allowNull: false, + type: Sequelize.DATE, + }, + updatedAt: { + allowNull: false, + type: Sequelize.DATE, + }, + }); +} -/** @type {import('sequelize-cli').Migration} */ -module.exports = { - async up(queryInterface, Sequelize) { - await queryInterface.createTable('runs', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - }, - name: { - type: Sequelize.STRING, - allowNull: false, - }, - configurations: { - type: Sequelize.STRING, - allowNull: true, - }, - description: { - type: Sequelize.STRING, - allowNull: true, - }, - state: { - type: Sequelize.INTEGER, - allowNull: true, - }, - projectId: { - type: Sequelize.INTEGER, - allowNull: false, - references: { - model: 'projects', - key: 'id', - }, - onUpdate: 'CASCADE', - onDelete: 'CASCADE', - }, - createdAt: { - allowNull: false, - type: Sequelize.DATE, - }, - updatedAt: { - allowNull: false, - type: Sequelize.DATE, - }, - }); - }, - - async down(queryInterface) { - await queryInterface.dropTable('runs'); - }, -}; +export async function down(queryInterface) { + await queryInterface.dropTable('runs'); +} diff --git a/backend/migrations/20240212084228-create-cases.js b/backend/migrations/20240212084228-create-cases.js index 4ae2213..9677646 100644 --- a/backend/migrations/20240212084228-create-cases.js +++ b/backend/migrations/20240212084228-create-cases.js @@ -1,72 +1,67 @@ -'use strict'; +export async function up(queryInterface, Sequelize) { + await queryInterface.createTable('cases', { + id: { + type: Sequelize.INTEGER, + primaryKey: true, + autoIncrement: true, + }, + folderId: { + type: Sequelize.INTEGER, + allowNull: false, + references: { + model: 'folders', + key: 'id', + }, + onUpdate: 'CASCADE', + onDelete: 'CASCADE', + }, + title: { + type: Sequelize.STRING, + allowNull: false, + }, + state: { + type: Sequelize.INTEGER, + allowNull: false, + }, + priority: { + type: Sequelize.INTEGER, + allowNull: false, + }, + type: { + type: Sequelize.INTEGER, + allowNull: false, + }, + automationStatus: { + type: Sequelize.INTEGER, + allowNull: false, + }, + description: { + type: Sequelize.STRING, + allowNull: true, + }, + template: { + type: Sequelize.INTEGER, + allowNull: false, + }, + preConditions: { + type: Sequelize.STRING, + allowNull: true, + }, + expectedResults: { + type: Sequelize.STRING, + allowNull: true, + }, + createdAt: { + allowNull: false, + type: Sequelize.DATE, + }, + updatedAt: { + allowNull: false, + type: Sequelize.DATE, + }, + }); +} -/** @type {import('sequelize-cli').Migration} */ -module.exports = { - async up(queryInterface, Sequelize) { - await queryInterface.createTable('cases', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - }, - folderId: { - type: Sequelize.INTEGER, - allowNull: false, - references: { - model: 'folders', - key: 'id', - }, - onUpdate: 'CASCADE', - onDelete: 'CASCADE', - }, - title: { - type: Sequelize.STRING, - allowNull: false, - }, - state: { - type: Sequelize.INTEGER, - allowNull: false, - }, - priority: { - type: Sequelize.INTEGER, - allowNull: false, - }, - type: { - type: Sequelize.INTEGER, - allowNull: false, - }, - automationStatus: { - type: Sequelize.INTEGER, - allowNull: false, - }, - description: { - type: Sequelize.STRING, - allowNull: true, - }, - template: { - type: Sequelize.INTEGER, - allowNull: false, - }, - preConditions: { - type: Sequelize.STRING, - allowNull: true, - }, - expectedResults: { - type: Sequelize.STRING, - allowNull: true, - }, - createdAt: { - allowNull: false, - type: Sequelize.DATE, - }, - updatedAt: { - allowNull: false, - type: Sequelize.DATE, - }, - }); - }, - - async down(queryInterface) { - await queryInterface.dropTable('cases'); - }, -}; +export async function down(queryInterface) { + await queryInterface.dropTable('cases'); +} diff --git a/backend/migrations/20240229095914-create-steps.js b/backend/migrations/20240229095914-create-steps.js index 9662b15..f680cb5 100644 --- a/backend/migrations/20240229095914-create-steps.js +++ b/backend/migrations/20240229095914-create-steps.js @@ -1,34 +1,29 @@ -'use strict'; +export async function up(queryInterface, Sequelize) { + await queryInterface.createTable('steps', { + id: { + type: Sequelize.INTEGER, + primaryKey: true, + autoIncrement: true, + }, + step: { + type: Sequelize.STRING, + allowNull: false, + }, + result: { + type: Sequelize.STRING, + allowNull: false, + }, + createdAt: { + type: Sequelize.DATE, + allowNull: false, + }, + updatedAt: { + type: Sequelize.DATE, + allowNull: false, + }, + }); +} -/** @type {import('sequelize-cli').Migration} */ -module.exports = { - up: async (queryInterface, Sequelize) => { - await queryInterface.createTable('steps', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - }, - step: { - type: Sequelize.STRING, - allowNull: false, - }, - result: { - type: Sequelize.STRING, - allowNull: false, - }, - createdAt: { - type: Sequelize.DATE, - allowNull: false, - }, - updatedAt: { - type: Sequelize.DATE, - allowNull: false, - }, - }); - }, - - down: async (queryInterface) => { - await queryInterface.dropTable('steps'); - }, -}; +export async function down(queryInterface) { + await queryInterface.dropTable('steps'); +} diff --git a/backend/migrations/20240229100158-create-cases-steps.js b/backend/migrations/20240229100158-create-cases-steps.js index ff66e7e..e2b9b67 100644 --- a/backend/migrations/20240229100158-create-cases-steps.js +++ b/backend/migrations/20240229100158-create-cases-steps.js @@ -1,52 +1,47 @@ -'use strict'; +export async function up(queryInterface, Sequelize) { + await queryInterface.createTable('caseSteps', { + id: { + type: Sequelize.INTEGER, + primaryKey: true, + autoIncrement: true, + }, + caseId: { + type: Sequelize.INTEGER, + references: { + model: 'cases', + key: 'id', + }, + onUpdate: 'CASCADE', + onDelete: 'CASCADE', + }, + stepId: { + type: Sequelize.INTEGER, + references: { + model: 'steps', + key: 'id', + }, + onUpdate: 'CASCADE', + onDelete: 'CASCADE', + }, + stepNo: { + type: Sequelize.INTEGER, + allowNull: false, + }, + createdAt: { + type: Sequelize.DATE, + allowNull: false, + }, + updatedAt: { + type: Sequelize.DATE, + allowNull: false, + }, + }); -/** @type {import('sequelize-cli').Migration} */ -module.exports = { - up: async (queryInterface, Sequelize) => { - await queryInterface.createTable('caseSteps', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - }, - caseId: { - type: Sequelize.INTEGER, - references: { - model: 'cases', - key: 'id', - }, - onUpdate: 'CASCADE', - onDelete: 'CASCADE', - }, - stepId: { - type: Sequelize.INTEGER, - references: { - model: 'steps', - key: 'id', - }, - onUpdate: 'CASCADE', - onDelete: 'CASCADE', - }, - stepNo: { - type: Sequelize.INTEGER, - allowNull: false, - }, - createdAt: { - type: Sequelize.DATE, - allowNull: false, - }, - updatedAt: { - type: Sequelize.DATE, - allowNull: false, - }, - }); + await queryInterface.addIndex('caseSteps', ['caseId', 'stepId'], { + unique: true, + }); +} - await queryInterface.addIndex('caseSteps', ['caseId', 'stepId'], { - unique: true, - }); - }, - - down: async (queryInterface) => { - await queryInterface.dropTable('caseSteps'); - }, -}; +export async function down(queryInterface) { + await queryInterface.dropTable('caseSteps'); +} diff --git a/backend/migrations/20240316200815-create-attachments.js b/backend/migrations/20240316200815-create-attachments.js index bc2044e..4310c34 100644 --- a/backend/migrations/20240316200815-create-attachments.js +++ b/backend/migrations/20240316200815-create-attachments.js @@ -1,38 +1,33 @@ -'use strict'; +export async function up(queryInterface, Sequelize) { + await queryInterface.createTable('attachments', { + id: { + type: Sequelize.INTEGER, + primaryKey: true, + autoIncrement: true, + }, + title: { + type: Sequelize.STRING, + allowNull: false, + }, + detail: { + type: Sequelize.STRING, + allowNull: true, + }, + path: { + type: Sequelize.STRING, + allowNull: false, + }, + createdAt: { + allowNull: false, + type: Sequelize.DATE, + }, + updatedAt: { + allowNull: false, + type: Sequelize.DATE, + }, + }); +} -/** @type {import('sequelize-cli').Migration} */ -module.exports = { - async up(queryInterface, Sequelize) { - await queryInterface.createTable('attachments', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - }, - title: { - type: Sequelize.STRING, - allowNull: false, - }, - detail: { - type: Sequelize.STRING, - allowNull: true, - }, - path: { - type: Sequelize.STRING, - allowNull: false, - }, - createdAt: { - allowNull: false, - type: Sequelize.DATE, - }, - updatedAt: { - allowNull: false, - type: Sequelize.DATE, - }, - }); - }, - - async down(queryInterface) { - await queryInterface.dropTable('attachments'); - }, -}; +export async function down(queryInterface) { + await queryInterface.dropTable('attachments'); +} diff --git a/backend/migrations/20240316201158-create-cases-attachments.js b/backend/migrations/20240316201158-create-cases-attachments.js index d7712c4..fb16013 100644 --- a/backend/migrations/20240316201158-create-cases-attachments.js +++ b/backend/migrations/20240316201158-create-cases-attachments.js @@ -1,48 +1,43 @@ -'use strict'; +export async function up(queryInterface, Sequelize) { + await queryInterface.createTable('caseAttachments', { + id: { + type: Sequelize.INTEGER, + primaryKey: true, + autoIncrement: true, + }, + caseId: { + type: Sequelize.INTEGER, + references: { + model: 'cases', + key: 'id', + }, + onUpdate: 'CASCADE', + onDelete: 'CASCADE', + }, + attachmentId: { + type: Sequelize.INTEGER, + references: { + model: 'attachments', + key: 'id', + }, + onUpdate: 'CASCADE', + onDelete: 'CASCADE', + }, + createdAt: { + type: Sequelize.DATE, + allowNull: false, + }, + updatedAt: { + type: Sequelize.DATE, + allowNull: false, + }, + }); -/** @type {import('sequelize-cli').Migration} */ -module.exports = { - up: async (queryInterface, Sequelize) => { - await queryInterface.createTable('caseAttachments', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - }, - caseId: { - type: Sequelize.INTEGER, - references: { - model: 'cases', - key: 'id', - }, - onUpdate: 'CASCADE', - onDelete: 'CASCADE', - }, - attachmentId: { - type: Sequelize.INTEGER, - references: { - model: 'attachments', - key: 'id', - }, - onUpdate: 'CASCADE', - onDelete: 'CASCADE', - }, - createdAt: { - type: Sequelize.DATE, - allowNull: false, - }, - updatedAt: { - type: Sequelize.DATE, - allowNull: false, - }, - }); + await queryInterface.addIndex('caseAttachments', ['caseId', 'attachmentId'], { + unique: true, + }); +} - await queryInterface.addIndex('caseAttachments', ['caseId', 'attachmentId'], { - unique: true, - }); - }, - - down: async (queryInterface) => { - await queryInterface.dropTable('caseAttachments'); - }, -}; +export async function down(queryInterface) { + await queryInterface.dropTable('caseAttachments'); +} diff --git a/backend/migrations/20240407182431-create-runs-cases.js b/backend/migrations/20240407182431-create-runs-cases.js index dbc2d22..c57c98a 100644 --- a/backend/migrations/20240407182431-create-runs-cases.js +++ b/backend/migrations/20240407182431-create-runs-cases.js @@ -1,52 +1,47 @@ -'use strict'; +export async function up(queryInterface, Sequelize) { + await queryInterface.createTable('runCases', { + id: { + type: Sequelize.INTEGER, + primaryKey: true, + autoIncrement: true, + }, + runId: { + type: Sequelize.INTEGER, + references: { + model: 'runs', + key: 'id', + }, + onUpdate: 'CASCADE', + onDelete: 'CASCADE', + }, + caseId: { + type: Sequelize.INTEGER, + references: { + model: 'cases', + key: 'id', + }, + onUpdate: 'CASCADE', + onDelete: 'CASCADE', + }, + status: { + type: Sequelize.INTEGER, + allowNull: false, + }, + createdAt: { + type: Sequelize.DATE, + allowNull: false, + }, + updatedAt: { + type: Sequelize.DATE, + allowNull: false, + }, + }); -/** @type {import('sequelize-cli').Migration} */ -module.exports = { - up: async (queryInterface, Sequelize) => { - await queryInterface.createTable('runCases', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - }, - runId: { - type: Sequelize.INTEGER, - references: { - model: 'runs', - key: 'id', - }, - onUpdate: 'CASCADE', - onDelete: 'CASCADE', - }, - caseId: { - type: Sequelize.INTEGER, - references: { - model: 'cases', - key: 'id', - }, - onUpdate: 'CASCADE', - onDelete: 'CASCADE', - }, - status: { - type: Sequelize.INTEGER, - allowNull: false, - }, - createdAt: { - type: Sequelize.DATE, - allowNull: false, - }, - updatedAt: { - type: Sequelize.DATE, - allowNull: false, - }, - }); + await queryInterface.addIndex('runCases', ['runId', 'caseId'], { + unique: true, + }); +} - await queryInterface.addIndex('runCases', ['runId', 'caseId'], { - unique: true, - }); - }, - - down: async (queryInterface) => { - await queryInterface.dropTable('runCases'); - }, -}; +export async function down(queryInterface) { + await queryInterface.dropTable('runCases'); +} diff --git a/backend/migrations/20240529221509-create-members.js b/backend/migrations/20240529221509-create-members.js index ed7576a..400f282 100644 --- a/backend/migrations/20240529221509-create-members.js +++ b/backend/migrations/20240529221509-create-members.js @@ -1,52 +1,47 @@ -'use strict'; +export async function up(queryInterface, Sequelize) { + await queryInterface.createTable('members', { + id: { + type: Sequelize.INTEGER, + primaryKey: true, + autoIncrement: true, + }, + userId: { + type: Sequelize.INTEGER, + references: { + model: 'users', + key: 'id', + }, + onUpdate: 'CASCADE', + onDelete: 'CASCADE', + }, + projectId: { + type: Sequelize.INTEGER, + references: { + model: 'projects', + key: 'id', + }, + onUpdate: 'CASCADE', + onDelete: 'CASCADE', + }, + role: { + type: Sequelize.INTEGER, + allowNull: false, + }, + createdAt: { + type: Sequelize.DATE, + allowNull: false, + }, + updatedAt: { + type: Sequelize.DATE, + allowNull: false, + }, + }); -/** @type {import('sequelize-cli').Migration} */ -module.exports = { - up: async (queryInterface, Sequelize) => { - await queryInterface.createTable('members', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - }, - userId: { - type: Sequelize.INTEGER, - references: { - model: 'users', - key: 'id', - }, - onUpdate: 'CASCADE', - onDelete: 'CASCADE', - }, - projectId: { - type: Sequelize.INTEGER, - references: { - model: 'projects', - key: 'id', - }, - onUpdate: 'CASCADE', - onDelete: 'CASCADE', - }, - role: { - type: Sequelize.INTEGER, - allowNull: false, - }, - createdAt: { - type: Sequelize.DATE, - allowNull: false, - }, - updatedAt: { - type: Sequelize.DATE, - allowNull: false, - }, - }); + await queryInterface.addIndex('members', ['userId', 'projectId'], { + unique: true, + }); +} - await queryInterface.addIndex('members', ['userId', 'projectId'], { - unique: true, - }); - }, - - down: async (queryInterface) => { - await queryInterface.dropTable('members'); - }, -}; +export async function down(queryInterface) { + await queryInterface.dropTable('members'); +} diff --git a/backend/migrations/20240930124156-rename_table.js b/backend/migrations/20240930124156-rename_table.js index c8e572d..6526f24 100644 --- a/backend/migrations/20240930124156-rename_table.js +++ b/backend/migrations/20240930124156-rename_table.js @@ -1,26 +1,21 @@ -'use strict'; +export async function up(queryInterface) { + /** + * Add altering commands here. + * + * Example: + * await queryInterface.createTable('users', { id: Sequelize.INTEGER }); + */ -/** @type {import('sequelize-cli').Migration} */ -module.exports = { - async up(queryInterface) { - /** - * Add altering commands here. - * - * Example: - * await queryInterface.createTable('users', { id: Sequelize.INTEGER }); - */ + await queryInterface.renameColumn('users', 'createdAt', 'created_at'); + await queryInterface.renameColumn('users', 'updatedAt', 'updated_at'); + await queryInterface.renameColumn('users', 'avatarPath', 'avatar_path'); +} - await queryInterface.renameColumn('users', 'createdAt', 'created_at'); - await queryInterface.renameColumn('users', 'updatedAt', 'updated_at'); - await queryInterface.renameColumn('users', 'avatarPath', 'avatar_path'); - }, - - async down() { - /** - * Add reverting commands here. - * - * Example: - * await queryInterface.dropTable('users'); - */ - }, -}; +export async function down() { + /** + * Add reverting commands here. + * + * Example: + * await queryInterface.dropTable('users'); + */ +} diff --git a/backend/migrations/20250712000000-rename-path-to-filename-in-attachments.js b/backend/migrations/20250712000000-rename-path-to-filename-in-attachments.js index 5f615b2..dc4d8b3 100644 --- a/backend/migrations/20250712000000-rename-path-to-filename-in-attachments.js +++ b/backend/migrations/20250712000000-rename-path-to-filename-in-attachments.js @@ -1,13 +1,9 @@ -'use strict'; +export async function up(queryInterface) { + // Rename column 'path' to 'filename' in 'Attachments' table + await queryInterface.renameColumn('Attachments', 'path', 'filename'); +} -module.exports = { - up: async (queryInterface) => { - // Rename column 'path' to 'filename' in 'Attachments' table - await queryInterface.renameColumn('Attachments', 'path', 'filename'); - }, - - down: async (queryInterface) => { - // Revert column name from 'filename' back to 'path' - await queryInterface.renameColumn('Attachments', 'filename', 'path'); - }, -}; +export async function down(queryInterface) { + // Revert column name from 'filename' back to 'path' + await queryInterface.renameColumn('Attachments', 'filename', 'path'); +} diff --git a/backend/migrations/20250804175803-add-cascade-delete-parent-folder.js b/backend/migrations/20250804175803-add-cascade-delete-parent-folder.js index 608372d..fbbb9e3 100644 --- a/backend/migrations/20250804175803-add-cascade-delete-parent-folder.js +++ b/backend/migrations/20250804175803-add-cascade-delete-parent-folder.js @@ -1,22 +1,17 @@ -'use strict'; +export async function up(queryInterface) { + await queryInterface.addConstraint('folders', { + fields: ['parentFolderId'], + type: 'foreign key', + name: 'fk_folders_parentFolderId', + references: { + table: 'folders', + field: 'id', + }, + onDelete: 'CASCADE', + onUpdate: 'CASCADE', + }); +} -/** @type {import('sequelize-cli').Migration} */ -module.exports = { - async up(queryInterface) { - await queryInterface.addConstraint('folders', { - fields: ['parentFolderId'], - type: 'foreign key', - name: 'fk_folders_parentFolderId', - references: { - table: 'folders', - field: 'id', - }, - onDelete: 'CASCADE', - onUpdate: 'CASCADE', - }); - }, - - async down(queryInterface) { - await queryInterface.removeConstraint('folders', 'fk_folders_parentFolderId'); - }, -}; +export async function down(queryInterface) { + await queryInterface.removeConstraint('folders', 'fk_folders_parentFolderId'); +} diff --git a/backend/models/attachments.js b/backend/models/attachments.js index 2f47dc2..c4e5ce0 100644 --- a/backend/models/attachments.js +++ b/backend/models/attachments.js @@ -23,4 +23,4 @@ function defineAttachment(sequelize, DataTypes) { return Attachment; } -module.exports = defineAttachment; +export default defineAttachment; diff --git a/backend/models/caseAttachments.js b/backend/models/caseAttachments.js index 6cb6065..8e3a73d 100644 --- a/backend/models/caseAttachments.js +++ b/backend/models/caseAttachments.js @@ -24,4 +24,4 @@ function defineCaseAttachment(sequelize, DataTypes) { return CaseAttachment; } -module.exports = defineCaseAttachment; +export default defineCaseAttachment; diff --git a/backend/models/caseSteps.js b/backend/models/caseSteps.js index 39713ba..c92b145 100644 --- a/backend/models/caseSteps.js +++ b/backend/models/caseSteps.js @@ -28,4 +28,4 @@ function defineCaseStep(sequelize, DataTypes) { return CaseStep; } -module.exports = defineCaseStep; +export default defineCaseStep; diff --git a/backend/models/cases.js b/backend/models/cases.js index 10258f5..684bdd4 100644 --- a/backend/models/cases.js +++ b/backend/models/cases.js @@ -60,4 +60,4 @@ function defineCase(sequelize, DataTypes) { return Case; } -module.exports = defineCase; +export default defineCase; diff --git a/backend/models/folders.js b/backend/models/folders.js index 9cdd238..582036f 100644 --- a/backend/models/folders.js +++ b/backend/models/folders.js @@ -37,4 +37,4 @@ function defineFolder(sequelize, DataTypes) { return Folder; } -module.exports = defineFolder; +export default defineFolder; diff --git a/backend/models/index.js b/backend/models/index.js index e57f031..8db8e95 100644 --- a/backend/models/index.js +++ b/backend/models/index.js @@ -1,12 +1,13 @@ -'use strict'; - -const fs = require('fs'); -const path = require('path'); -const process = require('process'); -const Sequelize = require('sequelize'); +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; +import process from 'process'; +import Sequelize from 'sequelize'; +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); const basename = path.basename(__filename); -const env = process.env.NODE_ENV || 'development'; -const config = require(__dirname + '/../config/config.json')[env]; +import configDefault from '../config/config'; +const config = configDefault[process.env.NODE_ENV || 'development']; const db = {}; let sequelize; @@ -16,14 +17,15 @@ if (config.use_env_variable) { sequelize = new Sequelize(config.database, config.username, config.password, config); } -fs.readdirSync(__dirname) - .filter((file) => { - return file.indexOf('.') !== 0 && file !== basename && file.slice(-3) === '.js' && file.indexOf('.test.js') === -1; - }) - .forEach((file) => { - const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes); - db[model.name] = model; - }); +const modelFiles = fs.readdirSync(__dirname).filter((file) => { + return file.indexOf('.') !== 0 && file !== basename && file.slice(-3) === '.js' && file.indexOf('.test.js') === -1; +}); + +for (const file of modelFiles) { + const module = await import(path.join(__dirname, file)); + const model = module.default(sequelize, Sequelize.DataTypes); + db[model.name] = model; +} Object.keys(db).forEach((modelName) => { if (db[modelName].associate) { @@ -34,4 +36,4 @@ Object.keys(db).forEach((modelName) => { db.sequelize = sequelize; db.Sequelize = Sequelize; -module.exports = db; +export default db; diff --git a/backend/models/members.js b/backend/models/members.js index 2ecefe2..3e3e4b5 100644 --- a/backend/models/members.js +++ b/backend/models/members.js @@ -28,4 +28,4 @@ function defineMember(sequelize, DataTypes) { return Member; } -module.exports = defineMember; +export default defineMember; diff --git a/backend/models/projects.js b/backend/models/projects.js index b7edb96..f8767aa 100644 --- a/backend/models/projects.js +++ b/backend/models/projects.js @@ -32,4 +32,4 @@ function defineProject(sequelize, DataTypes) { return Project; } -module.exports = defineProject; +export default defineProject; diff --git a/backend/models/runCases.js b/backend/models/runCases.js index dd14e1b..668ba00 100644 --- a/backend/models/runCases.js +++ b/backend/models/runCases.js @@ -28,4 +28,4 @@ function defineRunCase(sequelize, DataTypes) { return RunCase; } -module.exports = defineRunCase; +export default defineRunCase; diff --git a/backend/models/runs.js b/backend/models/runs.js index f763e1d..eda5df0 100644 --- a/backend/models/runs.js +++ b/backend/models/runs.js @@ -34,4 +34,4 @@ function defineRun(sequelize, DataTypes) { return Run; } -module.exports = defineRun; +export default defineRun; diff --git a/backend/models/steps.js b/backend/models/steps.js index 2ad620f..ce23244 100644 --- a/backend/models/steps.js +++ b/backend/models/steps.js @@ -19,4 +19,4 @@ function defineStep(sequelize, DataTypes) { return Step; } -module.exports = defineStep; +export default defineStep; diff --git a/backend/models/users.js b/backend/models/users.js index 8d37df8..ebd0df4 100644 --- a/backend/models/users.js +++ b/backend/models/users.js @@ -1,9 +1,3 @@ -/** - * - * @param {import('sequelize').Sequelize} sequelize - * @param {*} DataTypes - * @returns - */ function defineUser(sequelize, DataTypes) { const User = sequelize.define( 'User', @@ -39,4 +33,4 @@ function defineUser(sequelize, DataTypes) { return User; } -module.exports = defineUser; +export default defineUser; diff --git a/backend/package-lock.json b/backend/package-lock.json index 0981383..9c8eee9 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -16,8 +16,8 @@ "jsonwebtoken": "^9.0.2", "multer": "^1.4.5-lts.1", "papaparse": "^5.5.2", - "sequelize": "^6.37.5", - "sequelize-cli": "^6.6.2", + "sequelize": "^6.37.7", + "sequelize-cli": "^6.6.3", "sqlite3": "^5.1.7", "xmlbuilder2": "^3.1.1" } @@ -32,6 +32,7 @@ "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", @@ -45,9 +46,10 @@ } }, "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", "engines": { "node": ">=12" }, @@ -56,9 +58,10 @@ } }, "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", "engines": { "node": ">=12" }, @@ -69,12 +72,14 @@ "node_modules/@isaacs/cliui/node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" }, "node_modules/@isaacs/cliui/node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -88,9 +93,10 @@ } }, "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" }, @@ -105,6 +111,7 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", @@ -218,7 +225,8 @@ "node_modules/@one-ini/wasm": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", - "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==" + "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", + "license": "MIT" }, "node_modules/@oozcitak/dom": { "version": "1.15.10", @@ -268,6 +276,7 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", "optional": true, "engines": { "node": ">=14" @@ -286,27 +295,31 @@ "version": "4.1.12", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "license": "MIT", "dependencies": { "@types/ms": "*" } }, "node_modules/@types/ms": { - "version": "0.7.34", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", - "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" }, "node_modules/@types/node": { - "version": "20.11.16", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.16.tgz", - "integrity": "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ==", + "version": "24.4.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.4.0.tgz", + "integrity": "sha512-gUuVEAK4/u6F9wRLznPUU4WGUacSEBDPoC2TrBkw3GAnOLHBL45QdfHOXp1kJ4ypBGLxTOB+t7NJLpKoC3gznQ==", + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.11.0" } }, "node_modules/@types/validator": { - "version": "13.11.8", - "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.11.8.tgz", - "integrity": "sha512-c/hzNDBh7eRF+KbCf+OoZxKbnkpaK/cKp9iLQWqB7muXtM+MtL9SUUH8vCFcLn6dH1Qm05jiexK0ofWY7TfOhQ==" + "version": "13.15.3", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.3.tgz", + "integrity": "sha512-7bcUmDyS6PN3EuD9SlGGOxM77F8WLVsrwkxyWxKnxzmXoequ6c7741QBrANq6htVRGOITJ7z72mTP6Z4XyuG+Q==", + "license": "MIT" }, "node_modules/abbrev": { "version": "1.1.1", @@ -394,6 +407,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -463,6 +477,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "license": "ISC", "engines": { "node": ">= 4.0.0" } @@ -543,7 +558,8 @@ "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "license": "MIT" }, "node_modules/body-parser": { "version": "1.20.3", @@ -693,25 +709,11 @@ "node": ">=6" } }, - "node_modules/cli-color": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-2.0.3.tgz", - "integrity": "sha512-OkoZnxyC4ERN3zLzZaY9Emb7f/MhBOIpePv0Ycok0fJYT+Ouo00UBEIwsVsr0yoow++n5YWlSUgST9GKhNHiRQ==", - "dependencies": { - "d": "^1.0.1", - "es5-ext": "^0.10.61", - "es6-iterator": "^2.0.3", - "memoizee": "^0.4.15", - "timers-ext": "^0.1.7" - }, - "engines": { - "node": ">=0.10" - } - }, "node_modules/cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", @@ -722,6 +724,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -732,7 +735,8 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" }, "node_modules/color-support": { "version": "1.1.3", @@ -746,6 +750,7 @@ "version": "10.0.1", "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "license": "MIT", "engines": { "node": ">=14" } @@ -773,6 +778,7 @@ "version": "1.1.13", "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "license": "MIT", "dependencies": { "ini": "^1.3.4", "proto-list": "~1.2.1" @@ -833,9 +839,10 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -845,15 +852,6 @@ "node": ">= 8" } }, - "node_modules/d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "dependencies": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -933,12 +931,14 @@ "node_modules/dottie": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.6.tgz", - "integrity": "sha512-iGCHkfUc5kFekGiqhe8B/mdaurD+lakO9txNnTvKtA6PISrw86LgqHvRzWYPyoE2Ph5aMIrCw9/uko6XHTKCwA==" + "integrity": "sha512-iGCHkfUc5kFekGiqhe8B/mdaurD+lakO9txNnTvKtA6PISrw86LgqHvRzWYPyoE2Ph5aMIrCw9/uko6XHTKCwA==", + "license": "MIT" }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", @@ -952,6 +952,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.4.tgz", "integrity": "sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==", + "license": "MIT", "dependencies": { "@one-ini/wasm": "0.1.1", "commander": "^10.0.0", @@ -966,9 +967,10 @@ } }, "node_modules/editorconfig/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } @@ -977,6 +979,7 @@ "version": "9.0.1", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -1068,55 +1071,11 @@ "node": ">= 0.4" } }, - "node_modules/es5-ext": { - "version": "0.10.64", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", - "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", - "hasInstallScript": true, - "dependencies": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "esniff": "^2.0.1", - "next-tick": "^1.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", - "dependencies": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "node_modules/es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "dependencies": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, - "node_modules/es6-weak-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", - "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", - "dependencies": { - "d": "1", - "es5-ext": "^0.10.46", - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.1" - } - }, "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", "engines": { "node": ">=6" } @@ -1126,25 +1085,6 @@ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, - "node_modules/esniff": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", - "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", - "dependencies": { - "d": "^1.0.1", - "es5-ext": "^0.10.62", - "event-emitter": "^0.3.5", - "type": "^2.7.2" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esniff/node_modules/type": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", - "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==" - }, "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", @@ -1165,15 +1105,6 @@ "node": ">= 0.6" } }, - "node_modules/event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", - "dependencies": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, "node_modules/expand-template": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", @@ -1237,19 +1168,6 @@ "express": "4 || 5 || ^5.0.0-beta.1" } }, - "node_modules/ext": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", - "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", - "dependencies": { - "type": "^2.7.2" - } - }, - "node_modules/ext/node_modules/type": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", - "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" - }, "node_modules/file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", @@ -1273,11 +1191,12 @@ } }, "node_modules/foreground-child": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", - "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", "dependencies": { - "cross-spawn": "^7.0.0", + "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" }, "engines": { @@ -1291,6 +1210,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", "engines": { "node": ">=14" }, @@ -1323,6 +1243,7 @@ "version": "9.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "license": "MIT", "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", @@ -1380,6 +1301,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" } @@ -1481,9 +1403,10 @@ "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" }, "node_modules/hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, @@ -1651,7 +1574,8 @@ "integrity": "sha512-6I/HUDeYFfuNCVS3td055BaXBwKYuzw7K3ExVMStBowKo9oOAMJIXIHvdyR3iboTCp1b+1i5DSkIZTcwIktuDw==", "engines": [ "node >= 0.4.0" - ] + ], + "license": "MIT" }, "node_modules/inflight": { "version": "1.0.6", @@ -1694,11 +1618,15 @@ } }, "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", "dependencies": { - "hasown": "^2.0.0" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -1718,11 +1646,6 @@ "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", "optional": true }, - "node_modules/is-promise": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", - "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" - }, "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -1734,15 +1657,13 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "node_modules/jackspeak": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", - "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" }, - "engines": { - "node": ">=14" - }, "funding": { "url": "https://github.com/sponsors/isaacs" }, @@ -1751,14 +1672,16 @@ } }, "node_modules/js-beautify": { - "version": "1.14.11", - "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.14.11.tgz", - "integrity": "sha512-rPogWqAfoYh1Ryqqh2agUpVfbxAhbjuN1SmU86dskQUKouRiggUTCO4+2ym9UPXllc2WAp0J+T5qxn7Um3lCdw==", + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.4.tgz", + "integrity": "sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==", + "license": "MIT", "dependencies": { "config-chain": "^1.1.13", - "editorconfig": "^1.0.3", - "glob": "^10.3.3", - "nopt": "^7.2.0" + "editorconfig": "^1.0.4", + "glob": "^10.4.2", + "js-cookie": "^3.0.5", + "nopt": "^7.2.1" }, "bin": { "css-beautify": "js/bin/css-beautify.js", @@ -1773,43 +1696,45 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/js-beautify/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, "node_modules/js-beautify/node_modules/glob": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", - "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^2.3.5", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/js-beautify/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -1821,17 +1746,19 @@ } }, "node_modules/js-beautify/node_modules/minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" } }, "node_modules/js-beautify/node_modules/nopt": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.0.tgz", - "integrity": "sha512-CVDtwCdhYIvnAzFoJ6NJ6dX3oga9/HyciQDnG1vQDjSLMeKLJ4A93ZqYKDrgYSr1FBY5/hMYC+2VCi24pgpkGA==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", + "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", + "license": "ISC", "dependencies": { "abbrev": "^2.0.0" }, @@ -1842,6 +1769,15 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/js-yaml": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", @@ -1861,9 +1797,10 @@ "optional": true }, "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "license": "MIT", "dependencies": { "universalify": "^2.0.0" }, @@ -1919,7 +1856,8 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" }, "node_modules/lodash.includes": { "version": "4.3.0", @@ -1967,14 +1905,6 @@ "node": ">=10" } }, - "node_modules/lru-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", - "integrity": "sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==", - "dependencies": { - "es5-ext": "~0.10.2" - } - }, "node_modules/make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -2032,21 +1962,6 @@ "node": ">= 0.6" } }, - "node_modules/memoizee": { - "version": "0.4.15", - "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.15.tgz", - "integrity": "sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ==", - "dependencies": { - "d": "^1.0.1", - "es5-ext": "^0.10.53", - "es6-weak-map": "^2.0.3", - "event-emitter": "^0.3.5", - "is-promise": "^2.2.2", - "lru-queue": "^0.1.0", - "next-tick": "^1.1.0", - "timers-ext": "^0.1.7" - } - }, "node_modules/merge-descriptors": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", @@ -2231,14 +2146,16 @@ "version": "2.30.1", "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "license": "MIT", "engines": { "node": "*" } }, "node_modules/moment-timezone": { - "version": "0.5.45", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.45.tgz", - "integrity": "sha512-HIWmqA86KcmCAhnMAN0wuDOARV/525R2+lOLotuGFzn4HO+FH+/645z2wx0Dt3iDv6/p61SIvKnDstISainhLQ==", + "version": "0.5.48", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.48.tgz", + "integrity": "sha512-f22b8LV1gbTO2ms2j2z13MuPogNoh5UzxL3nzNAYKGraILnbGc9NEE6dyiiiLv46DGRb8A4kg8UKWLjPthxBHw==", + "license": "MIT", "dependencies": { "moment": "^2.29.4" }, @@ -2292,11 +2209,6 @@ "node": ">= 0.6" } }, - "node_modules/next-tick": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", - "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" - }, "node_modules/node-abi": { "version": "3.54.0", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.54.0.tgz", @@ -2441,6 +2353,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, "node_modules/papaparse": { "version": "5.5.2", "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.5.2.tgz", @@ -2466,6 +2384,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", "engines": { "node": ">=8" } @@ -2473,35 +2392,36 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" }, "node_modules/path-scurry": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", - "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", "dependencies": { - "lru-cache": "^9.1.1 || ^10.0.0", + "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=16 || 14 >=14.18" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", - "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", - "engines": { - "node": "14 || >=16.14" - } + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" }, "node_modules/path-scurry/node_modules/minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" } @@ -2512,9 +2432,16 @@ "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==" }, "node_modules/pg-connection-string": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", - "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==" + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz", + "integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" }, "node_modules/prebuild-install": { "version": "7.1.1", @@ -2568,7 +2495,8 @@ "node_modules/proto-list": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", - "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==" + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "license": "ISC" }, "node_modules/proxy-addr": { "version": "2.0.7", @@ -2664,22 +2592,27 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "license": "MIT", "dependencies": { - "is-core-module": "^2.13.0", + "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -2694,9 +2627,10 @@ } }, "node_modules/retry-as-promised": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.0.4.tgz", - "integrity": "sha512-XgmCoxKWkDofwH8WddD0w85ZfqYz+ZHlr5yo+3YUCfycWawU56T5ckWXsScsj5B8tqUcIG67DxXByo3VUgiAdA==" + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.1.1.tgz", + "integrity": "sha512-hMD7odLOt3LkTjcif8aRZqi/hybjpLNgSk5oF5FCowfCjok6LukpN2bDX7R5wDmbgBQFn7YoBxSagmtXHaJYJw==", + "license": "MIT" }, "node_modules/rimraf": { "version": "3.0.2", @@ -2787,15 +2721,16 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/sequelize": { - "version": "6.37.5", - "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.37.5.tgz", - "integrity": "sha512-10WA4poUb3XWnUROThqL2Apq9C2NhyV1xHPMZuybNMCucDsbbFuKg51jhmyvvAUyUqCiimwTZamc3AHhMoBr2Q==", + "version": "6.37.7", + "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.37.7.tgz", + "integrity": "sha512-mCnh83zuz7kQxxJirtFD7q6Huy6liPanI67BSlbzSYgVNl5eXVdE2CN1FuAeZwG1SNpGsNRCV+bJAVVnykZAFA==", "funding": [ { "type": "opencollective", "url": "https://opencollective.com/sequelize" } ], + "license": "MIT", "dependencies": { "@types/debug": "^4.1.8", "@types/validator": "^13.7.17", @@ -2848,14 +2783,15 @@ } }, "node_modules/sequelize-cli": { - "version": "6.6.2", - "resolved": "https://registry.npmjs.org/sequelize-cli/-/sequelize-cli-6.6.2.tgz", - "integrity": "sha512-V8Oh+XMz2+uquLZltZES6MVAD+yEnmMfwfn+gpXcDiwE3jyQygLt4xoI0zG8gKt6cRcs84hsKnXAKDQjG/JAgg==", + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/sequelize-cli/-/sequelize-cli-6.6.3.tgz", + "integrity": "sha512-1YYPrcSRt/bpMDDSKM5ubY1mnJ2TEwIaGZcqITw4hLtGtE64nIqaBnLtMvH8VKHg6FbWpXTiFNc2mS/BtQCXZw==", + "license": "MIT", "dependencies": { - "cli-color": "^2.0.3", "fs-extra": "^9.1.0", - "js-beautify": "^1.14.5", + "js-beautify": "1.15.4", "lodash": "^4.17.21", + "picocolors": "^1.1.1", "resolve": "^1.22.1", "umzug": "^2.3.0", "yargs": "^16.2.0" @@ -2872,16 +2808,18 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-7.1.0.tgz", "integrity": "sha512-G9c0qlIWQSK29pR/5U2JF5dDQeqqHRragoyahj/Nx4KOOQ3CPPfzxnfqFPCSB7x5UgjOgnZ61nSxz+fjDpRlJg==", + "license": "MIT", "engines": { "node": ">= 10.0.0" } }, "node_modules/sequelize/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -2893,9 +2831,10 @@ } }, "node_modules/sequelize/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" }, "node_modules/serve-static": { "version": "1.16.2", @@ -2941,6 +2880,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" }, @@ -2952,6 +2892,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", "engines": { "node": ">=8" } @@ -3170,6 +3111,7 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -3195,6 +3137,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -3214,6 +3157,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -3289,15 +3233,6 @@ "node": ">=8" } }, - "node_modules/timers-ext": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz", - "integrity": "sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==", - "dependencies": { - "es5-ext": "~0.10.46", - "next-tick": "1" - } - }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -3309,7 +3244,8 @@ "node_modules/toposort-class": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", - "integrity": "sha512-OsLcGGbYF3rMjPUf8oKktyvCiUxSbqMMS39m33MAjLTC1DVIH6x3WSt63/M77ihI09+Sdfk1AXvfhCEeUmC7mg==" + "integrity": "sha512-OsLcGGbYF3rMjPUf8oKktyvCiUxSbqMMS39m33MAjLTC1DVIH6x3WSt63/M77ihI09+Sdfk1AXvfhCEeUmC7mg==", + "license": "MIT" }, "node_modules/tr46": { "version": "0.0.3", @@ -3327,11 +3263,6 @@ "node": "*" } }, - "node_modules/type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" - }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -3353,6 +3284,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/umzug/-/umzug-2.3.0.tgz", "integrity": "sha512-Z274K+e8goZK8QJxmbRPhl89HPO1K+ORFtm6rySPhFKfKc5GHhqdzD0SGhSWHkzoXasqJuItdhorSvY7/Cgflw==", + "license": "MIT", "dependencies": { "bluebird": "^3.7.2" }, @@ -3361,9 +3293,10 @@ } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.11.0.tgz", + "integrity": "sha512-kt1ZriHTi7MU+Z/r9DOdAI3ONdaR3M3csEaRc6ewa4f4dTvX4cQCbJ4NkEn0ohE4hHtq85+PhPSTY+pO/1PwgA==", + "license": "MIT" }, "node_modules/unique-filename": { "version": "1.1.1", @@ -3387,6 +3320,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "license": "MIT", "engines": { "node": ">= 10.0.0" } @@ -3416,14 +3350,16 @@ "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", "bin": { "uuid": "dist/bin/uuid" } }, "node_modules/validator": { - "version": "13.11.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz", - "integrity": "sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==", + "version": "13.15.15", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.15.tgz", + "integrity": "sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A==", + "license": "MIT", "engines": { "node": ">= 0.10" } @@ -3476,6 +3412,7 @@ "version": "0.5.0", "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz", "integrity": "sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==", + "license": "MIT", "dependencies": { "@types/node": "*" } @@ -3484,6 +3421,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -3501,6 +3439,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -3544,6 +3483,7 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", "engines": { "node": ">=10" } @@ -3557,6 +3497,7 @@ "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "license": "MIT", "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", @@ -3574,6 +3515,7 @@ "version": "20.2.9", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "license": "ISC", "engines": { "node": ">=10" } @@ -3600,14 +3542,14 @@ }, "dependencies": { "ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==" + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==" }, "ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==" + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==" }, "emoji-regex": { "version": "9.2.2", @@ -3625,9 +3567,9 @@ } }, "strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", "requires": { "ansi-regex": "^6.0.1" } @@ -3786,22 +3728,22 @@ } }, "@types/ms": { - "version": "0.7.34", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", - "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==" }, "@types/node": { - "version": "20.11.16", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.16.tgz", - "integrity": "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ==", + "version": "24.4.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.4.0.tgz", + "integrity": "sha512-gUuVEAK4/u6F9wRLznPUU4WGUacSEBDPoC2TrBkw3GAnOLHBL45QdfHOXp1kJ4ypBGLxTOB+t7NJLpKoC3gznQ==", "requires": { - "undici-types": "~5.26.4" + "undici-types": "~7.11.0" } }, "@types/validator": { - "version": "13.11.8", - "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.11.8.tgz", - "integrity": "sha512-c/hzNDBh7eRF+KbCf+OoZxKbnkpaK/cKp9iLQWqB7muXtM+MtL9SUUH8vCFcLn6dH1Qm05jiexK0ofWY7TfOhQ==" + "version": "13.15.3", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.3.tgz", + "integrity": "sha512-7bcUmDyS6PN3EuD9SlGGOxM77F8WLVsrwkxyWxKnxzmXoequ6c7741QBrANq6htVRGOITJ7z72mTP6Z4XyuG+Q==" }, "abbrev": { "version": "1.1.1", @@ -4100,18 +4042,6 @@ "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "optional": true }, - "cli-color": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-2.0.3.tgz", - "integrity": "sha512-OkoZnxyC4ERN3zLzZaY9Emb7f/MhBOIpePv0Ycok0fJYT+Ouo00UBEIwsVsr0yoow++n5YWlSUgST9GKhNHiRQ==", - "requires": { - "d": "^1.0.1", - "es5-ext": "^0.10.61", - "es6-iterator": "^2.0.3", - "memoizee": "^0.4.15", - "timers-ext": "^0.1.7" - } - }, "cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -4213,24 +4143,15 @@ } }, "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "requires": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, - "d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "requires": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -4312,9 +4233,9 @@ }, "dependencies": { "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "requires": { "balanced-match": "^1.0.0" } @@ -4397,75 +4318,16 @@ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" }, - "es5-ext": { - "version": "0.10.64", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", - "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", - "requires": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "esniff": "^2.0.1", - "next-tick": "^1.1.0" - } - }, - "es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", - "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "requires": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, - "es6-weak-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", - "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", - "requires": { - "d": "1", - "es5-ext": "^0.10.46", - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.1" - } - }, "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==" }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, - "esniff": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", - "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", - "requires": { - "d": "^1.0.1", - "es5-ext": "^0.10.62", - "event-emitter": "^0.3.5", - "type": "^2.7.2" - }, - "dependencies": { - "type": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", - "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==" - } - } - }, "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", @@ -4476,15 +4338,6 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" }, - "event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", - "requires": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, "expand-template": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", @@ -4534,21 +4387,6 @@ "integrity": "sha512-KS3efpnpIDVIXopMc65EMbWbUht7qvTCdtCR2dD/IZmi9MIkopYESwyRqLgv8Pfu589+KqDqOdzJWW7AHoACeg==", "requires": {} }, - "ext": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", - "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", - "requires": { - "type": "^2.7.2" - }, - "dependencies": { - "type": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", - "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" - } - } - }, "file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", @@ -4569,11 +4407,11 @@ } }, "foreground-child": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", - "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "requires": { - "cross-spawn": "^7.0.0", + "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" }, "dependencies": { @@ -4716,9 +4554,9 @@ "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" }, "hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "requires": { "function-bind": "^1.1.2" } @@ -4873,11 +4711,11 @@ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" }, "is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "requires": { - "hasown": "^2.0.0" + "hasown": "^2.0.2" } }, "is-fullwidth-code-point": { @@ -4891,11 +4729,6 @@ "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", "optional": true }, - "is-promise": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", - "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" - }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -4907,23 +4740,24 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "jackspeak": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", - "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "requires": { "@isaacs/cliui": "^8.0.2", "@pkgjs/parseargs": "^0.11.0" } }, "js-beautify": { - "version": "1.14.11", - "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.14.11.tgz", - "integrity": "sha512-rPogWqAfoYh1Ryqqh2agUpVfbxAhbjuN1SmU86dskQUKouRiggUTCO4+2ym9UPXllc2WAp0J+T5qxn7Um3lCdw==", + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.4.tgz", + "integrity": "sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==", "requires": { "config-chain": "^1.1.13", - "editorconfig": "^1.0.3", - "glob": "^10.3.3", - "nopt": "^7.2.0" + "editorconfig": "^1.0.4", + "glob": "^10.4.2", + "js-cookie": "^3.0.5", + "nopt": "^7.2.1" }, "dependencies": { "abbrev": { @@ -4932,48 +4766,54 @@ "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==" }, "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "requires": { "balanced-match": "^1.0.0" } }, "glob": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", - "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "requires": { "foreground-child": "^3.1.0", - "jackspeak": "^2.3.5", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" } }, "minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "requires": { "brace-expansion": "^2.0.1" } }, "minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==" + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==" }, "nopt": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.0.tgz", - "integrity": "sha512-CVDtwCdhYIvnAzFoJ6NJ6dX3oga9/HyciQDnG1vQDjSLMeKLJ4A93ZqYKDrgYSr1FBY5/hMYC+2VCi24pgpkGA==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", + "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", "requires": { "abbrev": "^2.0.0" } } } }, + "js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==" + }, "js-yaml": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", @@ -4990,9 +4830,9 @@ "optional": true }, "jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", "requires": { "graceful-fs": "^4.1.6", "universalify": "^2.0.0" @@ -5089,14 +4929,6 @@ "yallist": "^4.0.0" } }, - "lru-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", - "integrity": "sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==", - "requires": { - "es5-ext": "~0.10.2" - } - }, "make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -5141,21 +4973,6 @@ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" }, - "memoizee": { - "version": "0.4.15", - "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.15.tgz", - "integrity": "sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ==", - "requires": { - "d": "^1.0.1", - "es5-ext": "^0.10.53", - "es6-weak-map": "^2.0.3", - "event-emitter": "^0.3.5", - "is-promise": "^2.2.2", - "lru-queue": "^0.1.0", - "next-tick": "^1.1.0", - "timers-ext": "^0.1.7" - } - }, "merge-descriptors": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", @@ -5283,9 +5100,9 @@ "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==" }, "moment-timezone": { - "version": "0.5.45", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.45.tgz", - "integrity": "sha512-HIWmqA86KcmCAhnMAN0wuDOARV/525R2+lOLotuGFzn4HO+FH+/645z2wx0Dt3iDv6/p61SIvKnDstISainhLQ==", + "version": "0.5.48", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.48.tgz", + "integrity": "sha512-f22b8LV1gbTO2ms2j2z13MuPogNoh5UzxL3nzNAYKGraILnbGc9NEE6dyiiiLv46DGRb8A4kg8UKWLjPthxBHw==", "requires": { "moment": "^2.29.4" } @@ -5329,11 +5146,6 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" }, - "next-tick": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", - "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" - }, "node-abi": { "version": "3.54.0", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.54.0.tgz", @@ -5428,6 +5240,11 @@ "aggregate-error": "^3.0.0" } }, + "package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==" + }, "papaparse": { "version": "5.5.2", "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.5.2.tgz", @@ -5454,23 +5271,23 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "path-scurry": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", - "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "requires": { - "lru-cache": "^9.1.1 || ^10.0.0", + "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "dependencies": { "lru-cache": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", - "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==" + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" }, "minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==" + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==" } } }, @@ -5480,9 +5297,14 @@ "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==" }, "pg-connection-string": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", - "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==" + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz", + "integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==" + }, + "picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, "prebuild-install": { "version": "7.1.1", @@ -5609,11 +5431,11 @@ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==" }, "resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", "requires": { - "is-core-module": "^2.13.0", + "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" } @@ -5625,9 +5447,9 @@ "optional": true }, "retry-as-promised": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.0.4.tgz", - "integrity": "sha512-XgmCoxKWkDofwH8WddD0w85ZfqYz+ZHlr5yo+3YUCfycWawU56T5ckWXsScsj5B8tqUcIG67DxXByo3VUgiAdA==" + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.1.1.tgz", + "integrity": "sha512-hMD7odLOt3LkTjcif8aRZqi/hybjpLNgSk5oF5FCowfCjok6LukpN2bDX7R5wDmbgBQFn7YoBxSagmtXHaJYJw==" }, "rimraf": { "version": "3.0.2", @@ -5688,9 +5510,9 @@ } }, "sequelize": { - "version": "6.37.5", - "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.37.5.tgz", - "integrity": "sha512-10WA4poUb3XWnUROThqL2Apq9C2NhyV1xHPMZuybNMCucDsbbFuKg51jhmyvvAUyUqCiimwTZamc3AHhMoBr2Q==", + "version": "6.37.7", + "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.37.7.tgz", + "integrity": "sha512-mCnh83zuz7kQxxJirtFD7q6Huy6liPanI67BSlbzSYgVNl5eXVdE2CN1FuAeZwG1SNpGsNRCV+bJAVVnykZAFA==", "requires": { "@types/debug": "^4.1.8", "@types/validator": "^13.7.17", @@ -5711,29 +5533,29 @@ }, "dependencies": { "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "requires": { - "ms": "2.1.2" + "ms": "^2.1.3" } }, "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" } } }, "sequelize-cli": { - "version": "6.6.2", - "resolved": "https://registry.npmjs.org/sequelize-cli/-/sequelize-cli-6.6.2.tgz", - "integrity": "sha512-V8Oh+XMz2+uquLZltZES6MVAD+yEnmMfwfn+gpXcDiwE3jyQygLt4xoI0zG8gKt6cRcs84hsKnXAKDQjG/JAgg==", + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/sequelize-cli/-/sequelize-cli-6.6.3.tgz", + "integrity": "sha512-1YYPrcSRt/bpMDDSKM5ubY1mnJ2TEwIaGZcqITw4hLtGtE64nIqaBnLtMvH8VKHg6FbWpXTiFNc2mS/BtQCXZw==", "requires": { - "cli-color": "^2.0.3", "fs-extra": "^9.1.0", - "js-beautify": "^1.14.5", + "js-beautify": "1.15.4", "lodash": "^4.17.21", + "picocolors": "^1.1.1", "resolve": "^1.22.1", "umzug": "^2.3.0", "yargs": "^16.2.0" @@ -6026,15 +5848,6 @@ } } }, - "timers-ext": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz", - "integrity": "sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==", - "requires": { - "es5-ext": "~0.10.46", - "next-tick": "1" - } - }, "toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -6058,11 +5871,6 @@ "safe-buffer": "^5.0.1" } }, - "type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" - }, "type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -6086,9 +5894,9 @@ } }, "undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.11.0.tgz", + "integrity": "sha512-kt1ZriHTi7MU+Z/r9DOdAI3ONdaR3M3csEaRc6ewa4f4dTvX4cQCbJ4NkEn0ohE4hHtq85+PhPSTY+pO/1PwgA==" }, "unique-filename": { "version": "1.1.1", @@ -6134,9 +5942,9 @@ "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" }, "validator": { - "version": "13.11.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz", - "integrity": "sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==" + "version": "13.15.15", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.15.tgz", + "integrity": "sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A==" }, "vary": { "version": "1.1.2", diff --git a/backend/package.json b/backend/package.json index 60980e0..0a22155 100644 --- a/backend/package.json +++ b/backend/package.json @@ -2,6 +2,7 @@ "name": "unittcms-backend", "version": "1.0.0", "private": true, + "type": "module", "scripts": { "start": "node --env-file=.env index", "dev": "node index", @@ -19,8 +20,8 @@ "jsonwebtoken": "^9.0.2", "multer": "^1.4.5-lts.1", "papaparse": "^5.5.2", - "sequelize": "^6.37.5", - "sequelize-cli": "^6.6.2", + "sequelize": "^6.37.7", + "sequelize-cli": "^6.6.3", "sqlite3": "^5.1.7", "xmlbuilder2": "^3.1.1" } diff --git a/backend/routes/attachments/delete.js b/backend/routes/attachments/delete.js index 2ded08e..653ec47 100644 --- a/backend/routes/attachments/delete.js +++ b/backend/routes/attachments/delete.js @@ -1,13 +1,17 @@ -const fs = require('fs'); -const path = require('path'); -const express = require('express'); +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; +import express from 'express'; const router = express.Router(); -const { DataTypes } = require('sequelize'); -const defineAttachment = require('../../models/attachments'); +import { DataTypes } from 'sequelize'; +import defineAttachment from '../../models/attachments.js'; +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); -module.exports = function (sequelize) { +export default function (sequelize) { const Attachment = defineAttachment(sequelize, DataTypes); + // TODO middleware to verify user permission to delete attachment router.delete('/:attachmentId', async (req, res) => { const attachmentId = req.params.attachmentId; const t = await sequelize.transaction(); @@ -41,4 +45,4 @@ module.exports = function (sequelize) { }); return router; -}; +} diff --git a/backend/routes/attachments/download.js b/backend/routes/attachments/download.js index c2d8d28..201257b 100644 --- a/backend/routes/attachments/download.js +++ b/backend/routes/attachments/download.js @@ -1,12 +1,17 @@ -const fs = require('fs'); -const path = require('path'); -const express = require('express'); +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; +import express from 'express'; const router = express.Router(); -const { DataTypes } = require('sequelize'); -const defineAttachment = require('../../models/attachments'); +import { DataTypes } from 'sequelize'; +import defineAttachment from '../../models/attachments.js'; +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); -module.exports = function (sequelize) { +export default function (sequelize) { const Attachment = defineAttachment(sequelize, DataTypes); + + // TODO middleware to verify user permission to get attachment router.get('/download/:attachmentId', async (req, res) => { const attachmentId = req.params.attachmentId; @@ -30,4 +35,4 @@ module.exports = function (sequelize) { }); return router; -}; +} diff --git a/backend/routes/attachments/new.js b/backend/routes/attachments/new.js index 2771a60..9233b06 100644 --- a/backend/routes/attachments/new.js +++ b/backend/routes/attachments/new.js @@ -1,13 +1,16 @@ -const fs = require('fs'); -const path = require('path'); -const express = require('express'); +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; +import multer from 'multer'; +import express from 'express'; const router = express.Router(); -const multer = require('multer'); -const { DataTypes } = require('sequelize'); -const defineAttachment = require('../../models/attachments'); -const defineCaseAttachment = require('../../models/caseAttachments'); +import { DataTypes } from 'sequelize'; +import defineAttachment from '../../models/attachments.js'; +import defineCaseAttachment from '../../models/caseAttachments.js'; +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); -module.exports = function (sequelize) { +export default function (sequelize) { const Attachment = defineAttachment(sequelize, DataTypes); const CaseAttachment = defineCaseAttachment(sequelize, DataTypes); @@ -46,6 +49,7 @@ module.exports = function (sequelize) { const upload = multer({ storage }); + // TODO middleware to verify user permission to upload files router.post('/', upload.array('files', 10), async (req, res) => { const t = await sequelize.transaction(); try { @@ -79,4 +83,4 @@ module.exports = function (sequelize) { }); return router; -}; +} diff --git a/backend/routes/cases/delete.js b/backend/routes/cases/delete.js index 62e880d..86974b3 100644 --- a/backend/routes/cases/delete.js +++ b/backend/routes/cases/delete.js @@ -1,11 +1,13 @@ -const express = require('express'); +import express from 'express'; const router = express.Router(); -const { DataTypes } = require('sequelize'); -const defineCase = require('../../models/cases'); +import { DataTypes } from 'sequelize'; +import defineCase from '../../models/cases.js'; +import authMiddleware from '../../middleware/auth.js'; +import editableMiddleware from '../../middleware/verifyEditable.js'; -module.exports = function (sequelize) { - const { verifySignedIn } = require('../../middleware/auth')(sequelize); - const { verifyProjectDeveloperFromProjectId } = require('../../middleware/verifyEditable')(sequelize); +export default function (sequelize) { + const { verifySignedIn } = authMiddleware(sequelize); + const { verifyProjectDeveloperFromProjectId } = editableMiddleware(sequelize); const Case = defineCase(sequelize, DataTypes); router.post('/bulkdelete', verifySignedIn, verifyProjectDeveloperFromProjectId, async (req, res) => { @@ -24,4 +26,4 @@ module.exports = function (sequelize) { }); return router; -}; +} diff --git a/backend/routes/cases/download.js b/backend/routes/cases/download.js index 147d9e7..52169a9 100644 --- a/backend/routes/cases/download.js +++ b/backend/routes/cases/download.js @@ -1,13 +1,15 @@ -const express = require('express'); +import express from 'express'; const router = express.Router(); -const { DataTypes } = require('sequelize'); -const Papa = require('papaparse'); -const defineCase = require('../../models/cases'); +import { DataTypes } from 'sequelize'; +import Papa from 'papaparse'; +import defineCase from '../../models/cases.js'; +import authMiddleware from '../../middleware/auth.js'; +import visibilityMiddleware from '../../middleware/verifyVisible.js'; -module.exports = function (sequelize) { +export default function (sequelize) { const Case = defineCase(sequelize, DataTypes); - const { verifySignedIn } = require('../../middleware/auth')(sequelize); - const { verifyProjectVisibleFromFolderId } = require('../../middleware/verifyVisible')(sequelize); + const { verifySignedIn } = authMiddleware(sequelize); + const { verifyProjectVisibleFromFolderId } = visibilityMiddleware(sequelize); router.get('/download', verifySignedIn, verifyProjectVisibleFromFolderId, async (req, res) => { const { folderId, type } = req.query; @@ -51,4 +53,4 @@ module.exports = function (sequelize) { }); return router; -}; +} diff --git a/backend/routes/cases/edit.js b/backend/routes/cases/edit.js index bb5848c..6479dc3 100644 --- a/backend/routes/cases/edit.js +++ b/backend/routes/cases/edit.js @@ -1,11 +1,13 @@ -const express = require('express'); +import express from 'express'; const router = express.Router(); -const { DataTypes } = require('sequelize'); -const defineCase = require('../../models/cases'); +import { DataTypes } from 'sequelize'; +import defineCase from '../../models/cases.js'; +import authMiddleware from '../../middleware/auth.js'; +import editableMiddleware from '../../middleware/verifyEditable.js'; -module.exports = function (sequelize) { - const { verifySignedIn } = require('../../middleware/auth')(sequelize); - const { verifyProjectDeveloperFromCaseId } = require('../../middleware/verifyEditable')(sequelize); +export default function (sequelize) { + const { verifySignedIn } = authMiddleware(sequelize); + const { verifyProjectDeveloperFromCaseId } = editableMiddleware(sequelize); const Case = defineCase(sequelize, DataTypes); router.put('/:caseId', verifySignedIn, verifyProjectDeveloperFromCaseId, async (req, res) => { @@ -30,4 +32,4 @@ module.exports = function (sequelize) { }); return router; -}; +} diff --git a/backend/routes/cases/index.mjs b/backend/routes/cases/index.js similarity index 97% rename from backend/routes/cases/index.mjs rename to backend/routes/cases/index.js index a5bb515..d32a0b3 100644 --- a/backend/routes/cases/index.mjs +++ b/backend/routes/cases/index.js @@ -1,14 +1,14 @@ import express from 'express'; const router = express.Router(); import { DataTypes, Op } from 'sequelize'; -import defineCase from '../../models/cases'; +import defineCase from '../../models/cases.js'; import authMiddleware from '../../middleware/auth.js'; -import visibilityMiddleware from '../../middleware/verifyVisible'; +import visibilityMiddleware from '../../middleware/verifyVisible.js'; export default function (sequelize) { const { verifySignedIn } = authMiddleware(sequelize); - const Case = defineCase(sequelize, DataTypes); const { verifyProjectVisibleFromFolderId } = visibilityMiddleware(sequelize); + const Case = defineCase(sequelize, DataTypes); router.get('/', verifySignedIn, verifyProjectVisibleFromFolderId, async (req, res) => { const { folderId, priority, type, q } = req.query; diff --git a/backend/routes/cases/index.test.js b/backend/routes/cases/index.test.js index c8845f2..ce1181f 100644 --- a/backend/routes/cases/index.test.js +++ b/backend/routes/cases/index.test.js @@ -5,7 +5,7 @@ import { Sequelize, Op } from 'sequelize'; import casesIndexRoute from './index'; // mock of authentication middleware -vi.mock('../../middleware/auth', () => ({ +vi.mock('../../middleware/auth.js', () => ({ default: () => ({ verifySignedIn: vi.fn((req, res, next) => { req.userId = 1; @@ -13,7 +13,7 @@ vi.mock('../../middleware/auth', () => ({ }), }), })); -vi.mock('../../middleware/verifyVisible', () => ({ +vi.mock('../../middleware/verifyVisible.js', () => ({ default: () => ({ verifyProjectVisibleFromFolderId: vi.fn((req, res, next) => { next(); @@ -25,7 +25,7 @@ vi.mock('../../middleware/verifyVisible', () => ({ const mockCase = { findAll: vi.fn(), }; -vi.mock('../../models/cases', () => ({ +vi.mock('../../models/cases.js', () => ({ default: () => mockCase, })); diff --git a/backend/routes/cases/indexByProjectId.js b/backend/routes/cases/indexByProjectId.js index 4c99002..aa57e35 100644 --- a/backend/routes/cases/indexByProjectId.js +++ b/backend/routes/cases/indexByProjectId.js @@ -1,12 +1,14 @@ -const express = require('express'); +import express from 'express'; const router = express.Router(); -const { DataTypes } = require('sequelize'); -const defineProject = require('../../models/projects'); -const defineFolder = require('../../models/folders'); -const defineCase = require('../../models/cases'); -const defineRunCase = require('../../models/runCases'); +import { DataTypes } from 'sequelize'; +import defineProject from '../../models/projects.js'; +import defineFolder from '../../models/folders.js'; +import defineCase from '../../models/cases.js'; +import defineRunCase from '../../models/runCases.js'; +import authMiddleware from '../../middleware/auth.js'; +import visibilityMiddleware from '../../middleware/verifyVisible.js'; -module.exports = function (sequelize) { +export default function (sequelize) { const Project = defineProject(sequelize, DataTypes); const Folder = defineFolder(sequelize, DataTypes); const Case = defineCase(sequelize, DataTypes); @@ -17,8 +19,8 @@ module.exports = function (sequelize) { Case.belongsTo(Folder, { foreignKey: 'folderId' }); Case.hasMany(RunCase, { foreignKey: 'caseId' }); RunCase.belongsTo(Case, { foreignKey: 'caseId' }); - const { verifySignedIn } = require('../../middleware/auth')(sequelize); - const { verifyProjectVisibleFromProjectId } = require('../../middleware/verifyVisible')(sequelize); + const { verifySignedIn } = authMiddleware(sequelize); + const { verifyProjectVisibleFromProjectId } = visibilityMiddleware(sequelize); router.get('/byproject', verifySignedIn, verifyProjectVisibleFromProjectId, async (req, res) => { const { projectId } = req.query; @@ -51,4 +53,4 @@ module.exports = function (sequelize) { }); return router; -}; +} diff --git a/backend/routes/cases/new.js b/backend/routes/cases/new.js index 958f36f..d1937a2 100644 --- a/backend/routes/cases/new.js +++ b/backend/routes/cases/new.js @@ -1,7 +1,9 @@ -const express = require('express'); +import express from 'express'; const router = express.Router(); -const { DataTypes } = require('sequelize'); -const defineCase = require('../../models/cases'); +import { DataTypes } from 'sequelize'; +import defineCase from '../../models/cases.js'; +import authMiddleware from '../../middleware/auth.js'; +import editableMiddleware from '../../middleware/verifyEditable.js'; const requiredFields = ['title', 'state', 'priority', 'type', 'automationStatus', 'template']; @@ -13,9 +15,9 @@ function isEmpty(value) { } } -module.exports = function (sequelize) { - const { verifySignedIn } = require('../../middleware/auth')(sequelize); - const { verifyProjectDeveloperFromFolderId } = require('../../middleware/verifyEditable')(sequelize); +export default function (sequelize) { + const { verifySignedIn } = authMiddleware(sequelize); + const { verifyProjectDeveloperFromFolderId } = editableMiddleware(sequelize); const Case = defineCase(sequelize, DataTypes); router.post('/', verifySignedIn, verifyProjectDeveloperFromFolderId, async (req, res) => { @@ -56,4 +58,4 @@ module.exports = function (sequelize) { }); return router; -}; +} diff --git a/backend/routes/cases/show.js b/backend/routes/cases/show.js index a54ee05..eef32f9 100644 --- a/backend/routes/cases/show.js +++ b/backend/routes/cases/show.js @@ -1,11 +1,13 @@ -const express = require('express'); +import express from 'express'; const router = express.Router(); -const { DataTypes } = require('sequelize'); -const defineCase = require('../../models/cases'); -const defineStep = require('../../models/steps'); -const defineAttachment = require('../../models/attachments'); +import { DataTypes } from 'sequelize'; +import defineCase from '../../models/cases.js'; +import defineStep from '../../models/steps.js'; +import defineAttachment from '../../models/attachments.js'; +import authMiddleware from '../../middleware/auth.js'; +import visibilityMiddleware from '../../middleware/verifyVisible.js'; -module.exports = function (sequelize) { +export default function (sequelize) { const Case = defineCase(sequelize, DataTypes); const Step = defineStep(sequelize, DataTypes); const Attachment = defineAttachment(sequelize, DataTypes); @@ -13,8 +15,8 @@ module.exports = function (sequelize) { Step.belongsToMany(Case, { through: 'caseSteps' }); Case.belongsToMany(Attachment, { through: 'caseAttachments' }); Attachment.belongsToMany(Case, { through: 'caseAttachments' }); - const { verifySignedIn } = require('../../middleware/auth')(sequelize); - const { verifyProjectVisibleFromCaseId } = require('../../middleware/verifyVisible')(sequelize); + const { verifySignedIn } = authMiddleware(sequelize); + const { verifyProjectVisibleFromCaseId } = visibilityMiddleware(sequelize); router.get('/:caseId', verifySignedIn, verifyProjectVisibleFromCaseId, async (req, res) => { const caseId = req.params.caseId; @@ -43,4 +45,4 @@ module.exports = function (sequelize) { }); return router; -}; +} diff --git a/backend/routes/folders/delete.js b/backend/routes/folders/delete.js index 2eeb061..b34ce55 100644 --- a/backend/routes/folders/delete.js +++ b/backend/routes/folders/delete.js @@ -1,11 +1,13 @@ -const express = require('express'); +import express from 'express'; const router = express.Router(); -const { DataTypes } = require('sequelize'); -const defineFolder = require('../../models/folders'); +import { DataTypes } from 'sequelize'; +import defineFolder from '../../models/folders.js'; +import authMiddleware from '../../middleware/auth.js'; +import editableMiddleware from '../../middleware/verifyEditable.js'; -module.exports = function (sequelize) { - const { verifySignedIn } = require('../../middleware/auth')(sequelize); - const { verifyProjectDeveloperFromFolderId } = require('../../middleware/verifyEditable')(sequelize); +export default function (sequelize) { + const { verifySignedIn } = authMiddleware(sequelize); + const { verifyProjectDeveloperFromFolderId } = editableMiddleware(sequelize); const Folder = defineFolder(sequelize, DataTypes); router.delete('/:folderId', verifySignedIn, verifyProjectDeveloperFromFolderId, async (req, res) => { @@ -24,4 +26,4 @@ module.exports = function (sequelize) { }); return router; -}; +} diff --git a/backend/routes/folders/edit.js b/backend/routes/folders/edit.js index d0472f2..a6b9f74 100644 --- a/backend/routes/folders/edit.js +++ b/backend/routes/folders/edit.js @@ -1,11 +1,13 @@ -const express = require('express'); +import express from 'express'; const router = express.Router(); -const { DataTypes } = require('sequelize'); -const defineFolder = require('../../models/folders'); +import { DataTypes } from 'sequelize'; +import defineFolder from '../../models/folders.js'; +import authMiddleware from '../../middleware/auth.js'; +import editableMiddleware from '../../middleware/verifyEditable.js'; -module.exports = function (sequelize) { - const { verifySignedIn } = require('../../middleware/auth')(sequelize); - const { verifyProjectDeveloperFromFolderId } = require('../../middleware/verifyEditable')(sequelize); +export default function (sequelize) { + const { verifySignedIn } = authMiddleware(sequelize); + const { verifyProjectDeveloperFromFolderId } = editableMiddleware(sequelize); const Folder = defineFolder(sequelize, DataTypes); router.put('/:folderId', verifySignedIn, verifyProjectDeveloperFromFolderId, async (req, res) => { @@ -30,4 +32,4 @@ module.exports = function (sequelize) { }); return router; -}; +} diff --git a/backend/routes/folders/index.js b/backend/routes/folders/index.js index 611796e..e9d7186 100644 --- a/backend/routes/folders/index.js +++ b/backend/routes/folders/index.js @@ -1,11 +1,13 @@ -const express = require('express'); +import express from 'express'; const router = express.Router(); -const { DataTypes } = require('sequelize'); -const defineFolder = require('../../models/folders'); +import { DataTypes } from 'sequelize'; +import defineFolder from '../../models/folders.js'; +import authMiddleware from '../../middleware/auth.js'; +import visibilityMiddleware from '../../middleware/verifyVisible.js'; -module.exports = function (sequelize) { - const { verifySignedIn } = require('../../middleware/auth')(sequelize); - const { verifyProjectVisibleFromProjectId } = require('../../middleware/verifyVisible')(sequelize); +export default function (sequelize) { + const { verifySignedIn } = authMiddleware(sequelize); + const { verifyProjectVisibleFromProjectId } = visibilityMiddleware(sequelize); const Folder = defineFolder(sequelize, DataTypes); router.get('/', verifySignedIn, verifyProjectVisibleFromProjectId, async (req, res) => { @@ -29,4 +31,4 @@ module.exports = function (sequelize) { }); return router; -}; +} diff --git a/backend/routes/folders/new.js b/backend/routes/folders/new.js index f184556..633ecc7 100644 --- a/backend/routes/folders/new.js +++ b/backend/routes/folders/new.js @@ -1,11 +1,13 @@ -const express = require('express'); +import express from 'express'; const router = express.Router(); -const { DataTypes } = require('sequelize'); -const defineFolder = require('../../models/folders'); +import { DataTypes } from 'sequelize'; +import defineFolder from '../../models/folders.js'; +import authMiddleware from '../../middleware/auth.js'; +import editableMiddleware from '../../middleware/verifyEditable.js'; -module.exports = function (sequelize) { - const { verifySignedIn } = require('../../middleware/auth')(sequelize); - const { verifyProjectDeveloperFromProjectId } = require('../../middleware/verifyEditable')(sequelize); +export default function (sequelize) { + const { verifySignedIn } = authMiddleware(sequelize); + const { verifyProjectDeveloperFromProjectId } = editableMiddleware(sequelize); const Folder = defineFolder(sequelize, DataTypes); router.post('/', verifySignedIn, verifyProjectDeveloperFromProjectId, async (req, res) => { @@ -31,4 +33,4 @@ module.exports = function (sequelize) { }); return router; -}; +} diff --git a/backend/routes/health/index.js b/backend/routes/health/index.js index 6252d2c..15df310 100644 --- a/backend/routes/health/index.js +++ b/backend/routes/health/index.js @@ -1,10 +1,10 @@ -const express = require('express'); +import express from 'express'; const router = express.Router(); -module.exports = function () { +export default function () { router.get('/', async (req, res) => { res.json({ status: 'ok' }); }); return router; -}; +} diff --git a/backend/routes/home/index.js b/backend/routes/home/index.js index 292d81e..0fbfe32 100644 --- a/backend/routes/home/index.js +++ b/backend/routes/home/index.js @@ -1,15 +1,17 @@ -const express = require('express'); +import express from 'express'; const router = express.Router(); -const { DataTypes } = require('sequelize'); -const defineProject = require('../../models/projects'); -const defineFolder = require('../../models/folders'); -const defineCase = require('../../models/cases'); -const defineRun = require('../../models/runs'); -const defineRunCase = require('../../models/runCases'); +import { DataTypes } from 'sequelize'; +import defineProject from '../../models/projects.js'; +import defineFolder from '../../models/folders.js'; +import defineCase from '../../models/cases.js'; +import defineRun from '../../models/runs.js'; +import defineRunCase from '../../models/runCases.js'; +import authMiddleware from '../../middleware/auth.js'; +import visibilityMiddleware from '../../middleware/verifyVisible.js'; -module.exports = function (sequelize) { - const { verifySignedIn } = require('../../middleware/auth')(sequelize); - const { verifyProjectVisibleFromProjectId } = require('../../middleware/verifyVisible')(sequelize); +export default function (sequelize) { + const { verifySignedIn } = authMiddleware(sequelize); + const { verifyProjectVisibleFromProjectId } = visibilityMiddleware(sequelize); const Project = defineProject(sequelize, DataTypes); const Folder = defineFolder(sequelize, DataTypes); @@ -49,4 +51,4 @@ module.exports = function (sequelize) { }); return router; -}; +} diff --git a/backend/routes/index.js b/backend/routes/index.js index 8424234..78732d2 100644 --- a/backend/routes/index.js +++ b/backend/routes/index.js @@ -1,9 +1,10 @@ -const express = require('express'); +import express from 'express'; const router = express.Router(); -// "/" GET -router.get('/', (req, res) => { - res.send('This is UnitTCMS API server'); -}); +export default function () { + router.get('/', (req, res) => { + res.send('This is UnitTCMS API server'); + }); -module.exports = router; + return router; +} diff --git a/backend/routes/members/check.js b/backend/routes/members/check.js index f68ddd1..af72925 100644 --- a/backend/routes/members/check.js +++ b/backend/routes/members/check.js @@ -1,11 +1,12 @@ -const express = require('express'); +import express from 'express'; const router = express.Router(); -const { DataTypes } = require('sequelize'); -const defineMember = require('../../models/members'); -const defineProject = require('../../models/projects'); +import { DataTypes } from 'sequelize'; +import defineMember from '../../models/members.js'; +import defineProject from '../../models/projects.js'; +import authMiddleware from '../../middleware/auth.js'; -module.exports = function (sequelize) { - const { verifySignedIn } = require('../../middleware/auth')(sequelize); +export default function (sequelize) { + const { verifySignedIn } = authMiddleware(sequelize); const Member = defineMember(sequelize, DataTypes); const Project = defineProject(sequelize, DataTypes); @@ -45,4 +46,4 @@ module.exports = function (sequelize) { }); return router; -}; +} diff --git a/backend/routes/members/delete.js b/backend/routes/members/delete.js index dc02283..e250c79 100644 --- a/backend/routes/members/delete.js +++ b/backend/routes/members/delete.js @@ -1,11 +1,13 @@ -const express = require('express'); +import express from 'express'; const router = express.Router(); -const { DataTypes } = require('sequelize'); -const defineMember = require('../../models/members'); +import { DataTypes } from 'sequelize'; +import defineMember from '../../models/members.js'; +import authMiddleware from '../../middleware/auth.js'; +import editableMiddleware from '../../middleware/verifyEditable.js'; -module.exports = function (sequelize) { - const { verifySignedIn } = require('../../middleware/auth')(sequelize); - const { verifyProjectManagerFromProjectId } = require('../../middleware/verifyEditable')(sequelize); +export default function (sequelize) { + const { verifySignedIn } = authMiddleware(sequelize); + const { verifyProjectManagerFromProjectId } = editableMiddleware(sequelize); const Member = defineMember(sequelize, DataTypes); router.delete('/', verifySignedIn, verifyProjectManagerFromProjectId, async (req, res) => { @@ -34,4 +36,4 @@ module.exports = function (sequelize) { }); return router; -}; +} diff --git a/backend/routes/members/edit.js b/backend/routes/members/edit.js index 6c7c64e..a8ba61e 100644 --- a/backend/routes/members/edit.js +++ b/backend/routes/members/edit.js @@ -1,11 +1,13 @@ -const express = require('express'); +import express from 'express'; const router = express.Router(); -const { DataTypes } = require('sequelize'); -const defineMember = require('../../models/members'); +import { DataTypes } from 'sequelize'; +import defineMember from '../../models/members.js'; +import authMiddleware from '../../middleware/auth.js'; +import editableMiddleware from '../../middleware/verifyEditable.js'; -module.exports = function (sequelize) { - const { verifySignedIn } = require('../../middleware/auth')(sequelize); - const { verifyProjectManagerFromProjectId } = require('../../middleware/verifyEditable')(sequelize); +export default function (sequelize) { + const { verifySignedIn } = authMiddleware(sequelize); + const { verifyProjectManagerFromProjectId } = editableMiddleware(sequelize); const Member = defineMember(sequelize, DataTypes); router.put('/', verifySignedIn, verifyProjectManagerFromProjectId, async (req, res) => { @@ -38,4 +40,4 @@ module.exports = function (sequelize) { }); return router; -}; +} diff --git a/backend/routes/members/index.js b/backend/routes/members/index.js index 415ebc1..b165537 100644 --- a/backend/routes/members/index.js +++ b/backend/routes/members/index.js @@ -1,12 +1,14 @@ -const express = require('express'); +import express from 'express'; const router = express.Router(); -const { DataTypes } = require('sequelize'); -const defineUser = require('../../models/users'); -const defineMember = require('../../models/members'); +import { DataTypes } from 'sequelize'; +import defineUser from '../../models/users.js'; +import defineMember from '../../models/members.js'; +import authMiddleware from '../../middleware/auth.js'; +import visibilityMiddleware from '../../middleware/verifyVisible.js'; -module.exports = function (sequelize) { - const { verifySignedIn } = require('../../middleware/auth')(sequelize); - const { verifyProjectVisibleFromProjectId } = require('../../middleware/verifyVisible')(sequelize); +export default function (sequelize) { + const { verifySignedIn } = authMiddleware(sequelize); + const { verifyProjectVisibleFromProjectId } = visibilityMiddleware(sequelize); const User = defineUser(sequelize, DataTypes); const Member = defineMember(sequelize, DataTypes); Member.belongsTo(User, { foreignKey: 'userId' }); @@ -37,4 +39,4 @@ module.exports = function (sequelize) { }); return router; -}; +} diff --git a/backend/routes/members/new.js b/backend/routes/members/new.js index f82ca44..e1d5aac 100644 --- a/backend/routes/members/new.js +++ b/backend/routes/members/new.js @@ -1,12 +1,14 @@ -const express = require('express'); +import express from 'express'; const router = express.Router(); -const { DataTypes } = require('sequelize'); -const defineMember = require('../../models/members'); -const { memberRoles } = require('../../routes/users/authSettings'); +import { DataTypes } from 'sequelize'; +import defineMember from '../../models/members.js'; +import { memberRoles } from '../../routes/users/authSettings.js'; +import authMiddleware from '../../middleware/auth.js'; +import editableMiddleware from '../../middleware/verifyEditable.js'; -module.exports = function (sequelize) { - const { verifySignedIn } = require('../../middleware/auth')(sequelize); - const { verifyProjectManagerFromProjectId } = require('../../middleware/verifyEditable')(sequelize); +export default function (sequelize) { + const { verifySignedIn } = authMiddleware(sequelize); + const { verifyProjectManagerFromProjectId } = editableMiddleware(sequelize); const Member = defineMember(sequelize, DataTypes); router.post('/', verifySignedIn, verifyProjectManagerFromProjectId, async (req, res) => { @@ -41,4 +43,4 @@ module.exports = function (sequelize) { }); return router; -}; +} diff --git a/backend/routes/projects/delete.js b/backend/routes/projects/delete.js index 40a1ab3..416cf35 100644 --- a/backend/routes/projects/delete.js +++ b/backend/routes/projects/delete.js @@ -1,13 +1,15 @@ -const express = require('express'); +import express from 'express'; const router = express.Router(); -const { DataTypes } = require('sequelize'); -const defineProject = require('../../models/projects'); -const defineFolder = require('../../models/folders'); -const defineRun = require('../../models/runs'); +import { DataTypes } from 'sequelize'; +import defineProject from '../../models/projects.js'; +import defineFolder from '../../models/folders.js'; +import defineRun from '../../models/runs.js'; +import authMiddleware from '../../middleware/auth.js'; +import editableMiddleware from '../../middleware/verifyEditable.js'; -module.exports = function (sequelize) { - const { verifySignedIn } = require('../../middleware/auth')(sequelize); - const { verifyProjectOwner } = require('../../middleware/verifyEditable')(sequelize); +export default function (sequelize) { + const { verifySignedIn } = authMiddleware(sequelize); + const { verifyProjectOwner } = editableMiddleware(sequelize); const Project = defineProject(sequelize, DataTypes); const Folder = defineFolder(sequelize, DataTypes); const Run = defineRun(sequelize, DataTypes); @@ -38,4 +40,4 @@ module.exports = function (sequelize) { }); return router; -}; +} diff --git a/backend/routes/projects/edit.js b/backend/routes/projects/edit.js index 1130999..8890b94 100644 --- a/backend/routes/projects/edit.js +++ b/backend/routes/projects/edit.js @@ -1,11 +1,13 @@ -const express = require('express'); +import express from 'express'; const router = express.Router(); -const { DataTypes } = require('sequelize'); -const defineProject = require('../../models/projects'); +import { DataTypes } from 'sequelize'; +import defineProject from '../../models/projects.js'; +import authMiddleware from '../../middleware/auth.js'; +import editableMiddleware from '../../middleware/verifyEditable.js'; -module.exports = function (sequelize) { - const { verifySignedIn } = require('../../middleware/auth')(sequelize); - const { verifyProjectOwner } = require('../../middleware/verifyEditable')(sequelize); +export default function (sequelize) { + const { verifySignedIn } = authMiddleware(sequelize); + const { verifyProjectOwner } = editableMiddleware(sequelize); const Project = defineProject(sequelize, DataTypes); router.put('/:projectId', verifySignedIn, verifyProjectOwner, async (req, res) => { @@ -29,4 +31,4 @@ module.exports = function (sequelize) { }); return router; -}; +} diff --git a/backend/routes/projects/index.js b/backend/routes/projects/index.js index 032eac2..f42cceb 100644 --- a/backend/routes/projects/index.js +++ b/backend/routes/projects/index.js @@ -1,11 +1,12 @@ -const express = require('express'); +import express from 'express'; const router = express.Router(); -const { DataTypes, Op } = require('sequelize'); -const defineProject = require('../../models/projects'); -const defineMember = require('../../models/members'); +import { DataTypes, Op } from 'sequelize'; +import defineProject from '../../models/projects.js'; +import defineMember from '../../models/members.js'; +import authMiddleware from '../../middleware/auth.js'; -module.exports = function (sequelize) { - const { verifySignedIn } = require('../../middleware/auth')(sequelize); +export default function (sequelize) { + const { verifySignedIn } = authMiddleware(sequelize); const Project = defineProject(sequelize, DataTypes); const Member = defineMember(sequelize, DataTypes); Project.hasMany(Member, { foreignKey: 'projectId' }); @@ -49,4 +50,4 @@ module.exports = function (sequelize) { }); return router; -}; +} diff --git a/backend/routes/projects/new.js b/backend/routes/projects/new.js index 247ef54..0310108 100644 --- a/backend/routes/projects/new.js +++ b/backend/routes/projects/new.js @@ -1,10 +1,11 @@ -const express = require('express'); +import express from 'express'; const router = express.Router(); -const { DataTypes } = require('sequelize'); -const defineProject = require('../../models/projects'); +import { DataTypes } from 'sequelize'; +import defineProject from '../../models/projects.js'; +import authMiddleware from '../../middleware/auth.js'; -module.exports = function (sequelize) { - const { verifySignedIn } = require('../../middleware/auth')(sequelize); +export default function (sequelize) { + const { verifySignedIn } = authMiddleware(sequelize); const Project = defineProject(sequelize, DataTypes); router.post('/', verifySignedIn, async (req, res) => { @@ -24,4 +25,4 @@ module.exports = function (sequelize) { }); return router; -}; +} diff --git a/backend/routes/projects/show.js b/backend/routes/projects/show.js index d83bf01..e6281de 100644 --- a/backend/routes/projects/show.js +++ b/backend/routes/projects/show.js @@ -1,12 +1,14 @@ -const express = require('express'); +import express from 'express'; const router = express.Router(); -const { DataTypes } = require('sequelize'); -const defineProject = require('../../models/projects'); -const defineFolder = require('../../models/folders'); +import { DataTypes } from 'sequelize'; +import defineProject from '../../models/projects.js'; +import defineFolder from '../../models/folders.js'; +import authMiddleware from '../../middleware/auth.js'; +import visibilityMiddleware from '../../middleware/verifyVisible.js'; -module.exports = function (sequelize) { - const { verifySignedIn } = require('../../middleware/auth')(sequelize); - const { verifyProjectVisibleFromProjectId } = require('../../middleware/verifyVisible')(sequelize); +export default function (sequelize) { + const { verifySignedIn } = authMiddleware(sequelize); + const { verifyProjectVisibleFromProjectId } = visibilityMiddleware(sequelize); const Project = defineProject(sequelize, DataTypes); const Folder = defineFolder(sequelize, DataTypes); Project.hasMany(Folder, { foreignKey: 'projectId' }); @@ -36,4 +38,4 @@ module.exports = function (sequelize) { }); return router; -}; +} diff --git a/backend/routes/runcases/edit.js b/backend/routes/runcases/edit.js index 962f42d..f77b8ee 100644 --- a/backend/routes/runcases/edit.js +++ b/backend/routes/runcases/edit.js @@ -1,11 +1,13 @@ -const express = require('express'); +import express from 'express'; const router = express.Router(); -const { DataTypes } = require('sequelize'); -const defineRunCase = require('../../models/runCases'); +import { DataTypes } from 'sequelize'; +import defineRunCase from '../../models/runCases.js'; +import authMiddleware from '../../middleware/auth.js'; +import editableMiddleware from '../../middleware/verifyEditable.js'; -module.exports = function (sequelize) { - const { verifySignedIn } = require('../../middleware/auth')(sequelize); - const { verifyProjectReporterFromRunId } = require('../../middleware/verifyEditable')(sequelize); +export default function (sequelize) { + const { verifySignedIn } = authMiddleware(sequelize); + const { verifyProjectReporterFromRunId } = editableMiddleware(sequelize); const RunCase = defineRunCase(sequelize, DataTypes); router.post('/update', verifySignedIn, verifyProjectReporterFromRunId, async (req, res) => { @@ -71,4 +73,4 @@ module.exports = function (sequelize) { }); return router; -}; +} diff --git a/backend/routes/runcases/index.js b/backend/routes/runcases/index.js index 7b17d2c..0122b6a 100644 --- a/backend/routes/runcases/index.js +++ b/backend/routes/runcases/index.js @@ -1,11 +1,13 @@ -const express = require('express'); +import express from 'express'; const router = express.Router(); -const { DataTypes } = require('sequelize'); -const defineRunCase = require('../../models/runCases'); +import { DataTypes } from 'sequelize'; +import defineRunCase from '../../models/runCases.js'; +import authMiddleware from '../../middleware/auth.js'; +import visibilityMiddleware from '../../middleware/verifyVisible.js'; -module.exports = function (sequelize) { - const { verifySignedIn } = require('../../middleware/auth')(sequelize); - const { verifyProjectVisibleFromRunId } = require('../../middleware/verifyVisible')(sequelize); +export default function (sequelize) { + const { verifySignedIn } = authMiddleware(sequelize); + const { verifyProjectVisibleFromRunId } = visibilityMiddleware(sequelize); const RunCase = defineRunCase(sequelize, DataTypes); router.get('/', verifySignedIn, verifyProjectVisibleFromRunId, async (req, res) => { @@ -29,4 +31,4 @@ module.exports = function (sequelize) { }); return router; -}; +} diff --git a/backend/routes/runs/delete.js b/backend/routes/runs/delete.js index 2bde53c..fe6add7 100644 --- a/backend/routes/runs/delete.js +++ b/backend/routes/runs/delete.js @@ -1,11 +1,13 @@ -const express = require('express'); +import express from 'express'; const router = express.Router(); -const { DataTypes } = require('sequelize'); -const defineRun = require('../../models/runs'); +import { DataTypes } from 'sequelize'; +import defineRun from '../../models/runs.js'; +import authMiddleware from '../../middleware/auth.js'; +import editableMiddleware from '../../middleware/verifyEditable.js'; -module.exports = function (sequelize) { - const { verifySignedIn } = require('../../middleware/auth')(sequelize); - const { verifyProjectReporterFromRunId } = require('../../middleware/verifyEditable')(sequelize); +export default function (sequelize) { + const { verifySignedIn } = authMiddleware(sequelize); + const { verifyProjectReporterFromRunId } = editableMiddleware(sequelize); const Run = defineRun(sequelize, DataTypes); router.delete('/:runId', verifySignedIn, verifyProjectReporterFromRunId, async (req, res) => { @@ -24,4 +26,4 @@ module.exports = function (sequelize) { }); return router; -}; +} diff --git a/backend/routes/runs/download.js b/backend/routes/runs/download.js index fa52597..27301d9 100644 --- a/backend/routes/runs/download.js +++ b/backend/routes/runs/download.js @@ -1,17 +1,18 @@ -const express = require('express'); +import express from 'express'; const router = express.Router(); -const Papa = require('papaparse'); -const { create } = require('xmlbuilder2'); +import { DataTypes } from 'sequelize'; +import Papa from 'papaparse'; +import { create } from 'xmlbuilder2'; +import defineRun from '../../models/runs.js'; +import defineRunCase from '../../models/runCases.js'; +import defineCase from '../../models/cases.js'; +import defineFolder from '../../models/folders.js'; +import authMiddleware from '../../middleware/auth.js'; +import visibilityMiddleware from '../../middleware/verifyVisible.js'; -const defineRun = require('../../models/runs'); -const defineRunCase = require('../../models/runCases'); -const defineCase = require('../../models/cases'); -const defineFolder = require('../../models/folders'); - -module.exports = function (sequelize) { - const { DataTypes } = require('sequelize'); - const { verifySignedIn } = require('../../middleware/auth')(sequelize); - const { verifyProjectVisibleFromRunId } = require('../../middleware/verifyVisible')(sequelize); +export default function (sequelize) { + const { verifySignedIn } = authMiddleware(sequelize); + const { verifyProjectVisibleFromRunId } = visibilityMiddleware(sequelize); const Run = defineRun(sequelize, DataTypes); const RunCase = defineRunCase(sequelize, DataTypes); @@ -123,4 +124,4 @@ module.exports = function (sequelize) { }); return router; -}; +} diff --git a/backend/routes/runs/edit.js b/backend/routes/runs/edit.js index 5f05b80..ac8a5ba 100644 --- a/backend/routes/runs/edit.js +++ b/backend/routes/runs/edit.js @@ -1,12 +1,14 @@ -const express = require('express'); +import express from 'express'; const router = express.Router(); -const { DataTypes } = require('sequelize'); -const defineRun = require('../../models/runs'); +import { DataTypes } from 'sequelize'; +import defineRun from '../../models/runs.js'; +import authMiddleware from '../../middleware/auth.js'; +import editableMiddleware from '../../middleware/verifyEditable.js'; -module.exports = function (sequelize) { +export default function (sequelize) { const Run = defineRun(sequelize, DataTypes); - const { verifySignedIn } = require('../../middleware/auth')(sequelize); - const { verifyProjectReporterFromRunId } = require('../../middleware/verifyEditable')(sequelize); + const { verifySignedIn } = authMiddleware(sequelize); + const { verifyProjectReporterFromRunId } = editableMiddleware(sequelize); router.put('/:runId', verifySignedIn, verifyProjectReporterFromRunId, async (req, res) => { const runId = req.params.runId; @@ -27,4 +29,4 @@ module.exports = function (sequelize) { }); return router; -}; +} diff --git a/backend/routes/runs/index.js b/backend/routes/runs/index.js index 1314912..d4a54e3 100644 --- a/backend/routes/runs/index.js +++ b/backend/routes/runs/index.js @@ -1,11 +1,13 @@ -const express = require('express'); +import express from 'express'; const router = express.Router(); -const { DataTypes } = require('sequelize'); -const defineRun = require('../../models/runs'); +import { DataTypes } from 'sequelize'; +import defineRun from '../../models/runs.js'; +import authMiddleware from '../../middleware/auth.js'; +import visibilityMiddleware from '../../middleware/verifyVisible.js'; -module.exports = function (sequelize) { - const { verifySignedIn } = require('../../middleware/auth')(sequelize); - const { verifyProjectVisibleFromProjectId } = require('../../middleware/verifyVisible')(sequelize); +export default function (sequelize) { + const { verifySignedIn } = authMiddleware(sequelize); + const { verifyProjectVisibleFromProjectId } = visibilityMiddleware(sequelize); const Run = defineRun(sequelize, DataTypes); router.get('/', verifySignedIn, verifyProjectVisibleFromProjectId, async (req, res) => { @@ -29,4 +31,4 @@ module.exports = function (sequelize) { }); return router; -}; +} diff --git a/backend/routes/runs/new.js b/backend/routes/runs/new.js index 72731d0..2ed61ed 100644 --- a/backend/routes/runs/new.js +++ b/backend/routes/runs/new.js @@ -1,11 +1,13 @@ -const express = require('express'); +import express from 'express'; const router = express.Router(); -const { DataTypes } = require('sequelize'); -const defineRun = require('../../models/runs'); +import { DataTypes } from 'sequelize'; +import defineRun from '../../models/runs.js'; +import authMiddleware from '../../middleware/auth.js'; +import editableMiddleware from '../../middleware/verifyEditable.js'; -module.exports = function (sequelize) { - const { verifySignedIn } = require('../../middleware/auth')(sequelize); - const { verifyProjectReporterFromProjectId } = require('../../middleware/verifyEditable')(sequelize); +export default function (sequelize) { + const { verifySignedIn } = authMiddleware(sequelize); + const { verifyProjectReporterFromProjectId } = editableMiddleware(sequelize); const Run = defineRun(sequelize, DataTypes); router.post('/', verifySignedIn, verifyProjectReporterFromProjectId, async (req, res) => { @@ -32,4 +34,4 @@ module.exports = function (sequelize) { }); return router; -}; +} diff --git a/backend/routes/runs/show.js b/backend/routes/runs/show.js index c84b9e6..bc86651 100644 --- a/backend/routes/runs/show.js +++ b/backend/routes/runs/show.js @@ -1,12 +1,14 @@ -const express = require('express'); +import express from 'express'; const router = express.Router(); -const { DataTypes, literal } = require('sequelize'); -const defineRun = require('../../models/runs'); -const defineRunCase = require('../../models/runCases'); +import { DataTypes, literal } from 'sequelize'; +import defineRun from '../../models/runs.js'; +import defineRunCase from '../../models/runCases.js'; +import authMiddleware from '../../middleware/auth.js'; +import visibilityMiddleware from '../../middleware/verifyVisible.js'; -module.exports = function (sequelize) { - const { verifySignedIn } = require('../../middleware/auth')(sequelize); - const { verifyProjectVisibleFromRunId } = require('../../middleware/verifyVisible')(sequelize); +export default function (sequelize) { + const { verifySignedIn } = authMiddleware(sequelize); + const { verifyProjectVisibleFromRunId } = visibilityMiddleware(sequelize); const Run = defineRun(sequelize, DataTypes); const RunCase = defineRunCase(sequelize, DataTypes); @@ -40,4 +42,4 @@ module.exports = function (sequelize) { }); return router; -}; +} diff --git a/backend/routes/steps/edit.js b/backend/routes/steps/edit.js index 2fac1f7..6c39d91 100644 --- a/backend/routes/steps/edit.js +++ b/backend/routes/steps/edit.js @@ -1,14 +1,16 @@ -const express = require('express'); +import express from 'express'; const router = express.Router(); -const { DataTypes } = require('sequelize'); -const defineStep = require('../../models/steps'); -const defineCaseStep = require('../../models/caseSteps'); +import { DataTypes } from 'sequelize'; +import defineStep from '../../models/steps.js'; +import defineCaseStep from '../../models/caseSteps.js'; +import authMiddleware from '../../middleware/auth.js'; +import editableMiddleware from '../../middleware/verifyEditable.js'; -module.exports = function (sequelize) { +export default function (sequelize) { const Step = defineStep(sequelize, DataTypes); const CaseStep = defineCaseStep(sequelize, DataTypes); - const { verifySignedIn } = require('../../middleware/auth')(sequelize); - const { verifyProjectDeveloperFromCaseId } = require('../../middleware/verifyEditable')(sequelize); + const { verifySignedIn } = authMiddleware(sequelize); + const { verifyProjectDeveloperFromCaseId } = editableMiddleware(sequelize); router.post('/update', verifySignedIn, verifyProjectDeveloperFromCaseId, async (req, res) => { const caseId = req.query.caseId; @@ -87,4 +89,4 @@ module.exports = function (sequelize) { }); return router; -}; +} diff --git a/backend/routes/users/authSettings.js b/backend/routes/users/authSettings.js index 1947eba..9720bda 100644 --- a/backend/routes/users/authSettings.js +++ b/backend/routes/users/authSettings.js @@ -2,4 +2,4 @@ const roles = [{ uid: 'administrator' }, { uid: 'user' }]; const memberRoles = [{ uid: 'manager' }, { uid: 'developer' }, { uid: 'reporter' }]; const defaultDangerKey = 'test-plat-default-key'; -module.exports = { roles, memberRoles, defaultDangerKey }; +export { roles, memberRoles, defaultDangerKey }; diff --git a/backend/routes/users/find.js b/backend/routes/users/find.js index 07897c2..af64cdf 100644 --- a/backend/routes/users/find.js +++ b/backend/routes/users/find.js @@ -1,10 +1,11 @@ -const express = require('express'); +import express from 'express'; const router = express.Router(); -const { DataTypes } = require('sequelize'); -const defineUser = require('../../models/users'); +import { DataTypes } from 'sequelize'; +import defineUser from '../../models/users.js'; +import authMiddleware from '../../middleware/auth.js'; -module.exports = function (sequelize) { - const { verifySignedIn } = require('../../middleware/auth')(sequelize); +export default function (sequelize) { + const { verifySignedIn } = authMiddleware(sequelize); const User = defineUser(sequelize, DataTypes); router.get('/find/:userId', verifySignedIn, async (req, res) => { @@ -33,4 +34,4 @@ module.exports = function (sequelize) { }); return router; -}; +} diff --git a/backend/routes/users/index.js b/backend/routes/users/index.js index fa2ab03..6b0364d 100644 --- a/backend/routes/users/index.js +++ b/backend/routes/users/index.js @@ -1,10 +1,11 @@ -const express = require('express'); +import express from 'express'; const router = express.Router(); -const { DataTypes } = require('sequelize'); -const defineUser = require('../../models/users'); +import { DataTypes } from 'sequelize'; +import defineUser from '../../models/users.js'; +import authMiddleware from '../../middleware/auth.js'; -module.exports = function (sequelize) { - const { verifySignedIn, verifyAdmin } = require('../../middleware/auth')(sequelize); +export default function (sequelize) { + const { verifySignedIn, verifyAdmin } = authMiddleware(sequelize); const User = defineUser(sequelize, DataTypes); router.get('/', verifySignedIn, verifyAdmin, async (req, res) => { @@ -20,4 +21,4 @@ module.exports = function (sequelize) { }); return router; -}; +} diff --git a/backend/routes/users/search.js b/backend/routes/users/search.js index e8d9c84..5398d1b 100644 --- a/backend/routes/users/search.js +++ b/backend/routes/users/search.js @@ -1,11 +1,12 @@ -const express = require('express'); +import express from 'express'; const router = express.Router(); -const { DataTypes, Op } = require('sequelize'); -const defineUser = require('../../models/users'); -const defineMember = require('../../models/members'); +import { DataTypes, Op } from 'sequelize'; +import defineUser from '../../models/users.js'; +import defineMember from '../../models/members.js'; +import authMiddleware from '../../middleware/auth.js'; -module.exports = function (sequelize) { - const { verifySignedIn } = require('../../middleware/auth')(sequelize); +export default function (sequelize) { + const { verifySignedIn } = authMiddleware(sequelize); const User = defineUser(sequelize, DataTypes); const Member = defineMember(sequelize, DataTypes); @@ -42,4 +43,4 @@ module.exports = function (sequelize) { }); return router; -}; +} diff --git a/backend/routes/users/signin.js b/backend/routes/users/signin.js index 69459b2..ae5f3f3 100644 --- a/backend/routes/users/signin.js +++ b/backend/routes/users/signin.js @@ -1,12 +1,12 @@ -const express = require('express'); +import express from 'express'; const router = express.Router(); -const { DataTypes } = require('sequelize'); -const bcrypt = require('bcrypt'); -const jwt = require('jsonwebtoken'); -const defineUser = require('../../models/users'); -const { defaultDangerKey } = require('./authSettings'); +import { DataTypes } from 'sequelize'; +import bcrypt from 'bcrypt'; +import jwt from 'jsonwebtoken'; +import defineUser from '../../models/users.js'; +import { defaultDangerKey } from './authSettings.js'; -module.exports = function (sequelize) { +export default function (sequelize) { const User = defineUser(sequelize, DataTypes); const secretKey = process.env.SECRET_KEY || defaultDangerKey; @@ -39,4 +39,4 @@ module.exports = function (sequelize) { }); return router; -}; +} diff --git a/backend/routes/users/signup.js b/backend/routes/users/signup.js index ee2fae1..c1b0e98 100644 --- a/backend/routes/users/signup.js +++ b/backend/routes/users/signup.js @@ -1,12 +1,12 @@ -const express = require('express'); +import express from 'express'; const router = express.Router(); -const { DataTypes } = require('sequelize'); -const bcrypt = require('bcrypt'); -const jwt = require('jsonwebtoken'); -const defineUser = require('../../models/users'); -const { roles, defaultDangerKey } = require('./authSettings'); +import { DataTypes } from 'sequelize'; +import bcrypt from 'bcrypt'; +import jwt from 'jsonwebtoken'; +import defineUser from '../../models/users.js'; +import { roles, defaultDangerKey } from './authSettings.js'; -module.exports = function (sequelize) { +export default function (sequelize) { const User = defineUser(sequelize, DataTypes); const secretKey = process.env.SECRET_KEY || defaultDangerKey; @@ -42,4 +42,4 @@ module.exports = function (sequelize) { }); return router; -}; +} diff --git a/backend/routes/users/update.mjs b/backend/routes/users/update.js similarity index 100% rename from backend/routes/users/update.mjs rename to backend/routes/users/update.js diff --git a/backend/routes/users/update.test.js b/backend/routes/users/update.test.js index 1eaa514..2e9acd4 100644 --- a/backend/routes/users/update.test.js +++ b/backend/routes/users/update.test.js @@ -3,13 +3,13 @@ import request from 'supertest'; import express from 'express'; import { Sequelize } from 'sequelize'; import updateRoute from './update'; -import { roles } from './authSettings'; +import { roles } from './authSettings.js'; const adminRoleIndex = roles.findIndex((entry) => entry.uid === 'administrator'); const userRoleIndex = roles.findIndex((entry) => entry.uid === 'user'); // mock of authentication middleware -vi.mock('../../middleware/auth', () => ({ +vi.mock('../../middleware/auth.js', () => ({ default: () => ({ verifySignedIn: vi.fn((req, res, next) => { req.userId = 1; // Mock user ID @@ -27,7 +27,7 @@ const mockUser = { count: vi.fn(), update: vi.fn(), }; -vi.mock('../../models/users', () => ({ +vi.mock('../../models/users.js', () => ({ default: () => mockUser, })); diff --git a/backend/seeders/seed.js b/backend/seeders/seed.js index 674bfa3..b5c70f8 100644 --- a/backend/seeders/seed.js +++ b/backend/seeders/seed.js @@ -1,702 +1,695 @@ -'use strict'; -const fs = require('fs'); -const bcrypt = require('bcrypt'); +import fs from 'fs'; +import bcrypt from 'bcrypt'; -module.exports = { - up: async (queryInterface) => { - const hashedPassword = await bcrypt.hash('password', 10); +export async function up(queryInterface) { + const hashedPassword = await bcrypt.hash('password', 10); - // Add projects table records - await queryInterface.bulkInsert('users', [ - { - email: 'admin@example.com', - password: hashedPassword, - username: 'Admin', - role: 0, - avatar_path: null, - created_at: new Date(), - updated_at: new Date(), - }, - { - email: 'samuel@example.com', - password: hashedPassword, - username: 'Samuel Golden', - role: 1, - avatar_path: null, - created_at: new Date(), - updated_at: new Date(), - }, - { - email: 'trey@example.com', - password: hashedPassword, - username: 'Trey Fisher', - role: 1, - avatar_path: null, - created_at: new Date(), - updated_at: new Date(), - }, - { - email: 'zoe@example.com', - password: hashedPassword, - username: 'Zoe Woodward', - role: 1, - avatar_path: null, - created_at: new Date(), - updated_at: new Date(), - }, - { - email: 'roger@example.com', - password: hashedPassword, - username: 'Roger Hess', - role: 1, - avatar_path: null, - created_at: new Date(), - updated_at: new Date(), - }, - { - email: 'jasmine@example.com', - password: hashedPassword, - username: 'Jasmine Moody', - role: 1, - avatar_path: null, - created_at: new Date(), - updated_at: new Date(), - }, - { - email: 'tatsuya@example.com', - password: hashedPassword, - username: 'Suzuki Tatsuya', - role: 1, - avatar_path: null, - created_at: new Date(), - updated_at: new Date(), - }, - { - email: 'eri@example.com', - password: hashedPassword, - username: 'Sato Eri', - role: 1, - avatar_path: null, - created_at: new Date(), - updated_at: new Date(), - }, - ]); + // Add projects table records + await queryInterface.bulkInsert('users', [ + { + email: 'admin@example.com', + password: hashedPassword, + username: 'Admin', + role: 0, + avatar_path: null, + created_at: new Date(), + updated_at: new Date(), + }, + { + email: 'samuel@example.com', + password: hashedPassword, + username: 'Samuel Golden', + role: 1, + avatar_path: null, + created_at: new Date(), + updated_at: new Date(), + }, + { + email: 'trey@example.com', + password: hashedPassword, + username: 'Trey Fisher', + role: 1, + avatar_path: null, + created_at: new Date(), + updated_at: new Date(), + }, + { + email: 'zoe@example.com', + password: hashedPassword, + username: 'Zoe Woodward', + role: 1, + avatar_path: null, + created_at: new Date(), + updated_at: new Date(), + }, + { + email: 'roger@example.com', + password: hashedPassword, + username: 'Roger Hess', + role: 1, + avatar_path: null, + created_at: new Date(), + updated_at: new Date(), + }, + { + email: 'jasmine@example.com', + password: hashedPassword, + username: 'Jasmine Moody', + role: 1, + avatar_path: null, + created_at: new Date(), + updated_at: new Date(), + }, + { + email: 'tatsuya@example.com', + password: hashedPassword, + username: 'Suzuki Tatsuya', + role: 1, + avatar_path: null, + created_at: new Date(), + updated_at: new Date(), + }, + { + email: 'eri@example.com', + password: hashedPassword, + username: 'Sato Eri', + role: 1, + avatar_path: null, + created_at: new Date(), + updated_at: new Date(), + }, + ]); - // Add projects table records - await queryInterface.bulkInsert('projects', [ - { - name: 'UnitTCMS Test', - detail: "Test Plat's Manual test", - userId: 1, - isPublic: true, - createdAt: new Date(2024, 5, 4), - updatedAt: new Date(), - }, - { - name: 'Iron Muscle App', - detail: 'Pre-release overall evaluation', - userId: 1, - isPublic: true, - createdAt: new Date(2024, 6, 10), - updatedAt: new Date(), - }, - { - name: 'MS200', - detail: '', - userId: 1, - isPublic: true, - createdAt: new Date(2024, 6, 24), - updatedAt: new Date(), - }, - ]); + // Add projects table records + await queryInterface.bulkInsert('projects', [ + { + name: 'UnitTCMS Test', + detail: "Test Plat's Manual test", + userId: 1, + isPublic: true, + createdAt: new Date(2024, 5, 4), + updatedAt: new Date(), + }, + { + name: 'Iron Muscle App', + detail: 'Pre-release overall evaluation', + userId: 1, + isPublic: true, + createdAt: new Date(2024, 6, 10), + updatedAt: new Date(), + }, + { + name: 'MS200', + detail: '', + userId: 1, + isPublic: true, + createdAt: new Date(2024, 6, 24), + updatedAt: new Date(), + }, + ]); - // Add folders table records - await queryInterface.bulkInsert('folders', [ - { - name: 'Account', - detail: '', - projectId: 1, - createdAt: new Date(), - updatedAt: new Date(), - }, - { - name: 'Contact', - detail: '', - projectId: 1, - createdAt: new Date(), - updatedAt: new Date(), - }, - { - name: 'Account', - detail: '', - projectId: 2, - createdAt: new Date(), - updatedAt: new Date(), - }, - { - name: 'Tranning history', - detail: '', - projectId: 2, - createdAt: new Date(), - updatedAt: new Date(), - }, - { - name: 'Timeline', - detail: '', - projectId: 2, - createdAt: new Date(), - updatedAt: new Date(), - }, - { - name: 'Chart', - detail: '', - projectId: 1, - createdAt: new Date(), - updatedAt: new Date(), - }, - { - name: 'Simulate', - detail: '', - projectId: 1, - createdAt: new Date(), - updatedAt: new Date(), - }, - ]); + // Add folders table records + await queryInterface.bulkInsert('folders', [ + { + name: 'Account', + detail: '', + projectId: 1, + createdAt: new Date(), + updatedAt: new Date(), + }, + { + name: 'Contact', + detail: '', + projectId: 1, + createdAt: new Date(), + updatedAt: new Date(), + }, + { + name: 'Account', + detail: '', + projectId: 2, + createdAt: new Date(), + updatedAt: new Date(), + }, + { + name: 'Tranning history', + detail: '', + projectId: 2, + createdAt: new Date(), + updatedAt: new Date(), + }, + { + name: 'Timeline', + detail: '', + projectId: 2, + createdAt: new Date(), + updatedAt: new Date(), + }, + { + name: 'Chart', + detail: '', + projectId: 1, + createdAt: new Date(), + updatedAt: new Date(), + }, + { + name: 'Simulate', + detail: '', + projectId: 1, + createdAt: new Date(), + updatedAt: new Date(), + }, + ]); - // Add runs table records - await queryInterface.bulkInsert('runs', [ - { - name: 'First Test Run', - projectId: 1, - configurations: 1, - description: '5/14 - 5/31', - state: 4, - createdAt: new Date(), - updatedAt: new Date(), - }, - { - name: 'evaluation 1st', - projectId: 2, - configurations: 1, - description: '5/14 - 5/31', - state: 4, - createdAt: new Date(), - updatedAt: new Date(), - }, - { - name: 'evaluation 2nd', - projectId: 2, - configurations: 1, - description: '6/1 - 6/12', - state: 1, - createdAt: new Date(), - updatedAt: new Date(), - }, - ]); + // Add runs table records + await queryInterface.bulkInsert('runs', [ + { + name: 'First Test Run', + projectId: 1, + configurations: 1, + description: '5/14 - 5/31', + state: 4, + createdAt: new Date(), + updatedAt: new Date(), + }, + { + name: 'evaluation 1st', + projectId: 2, + configurations: 1, + description: '5/14 - 5/31', + state: 4, + createdAt: new Date(), + updatedAt: new Date(), + }, + { + name: 'evaluation 2nd', + projectId: 2, + configurations: 1, + description: '6/1 - 6/12', + state: 1, + createdAt: new Date(), + updatedAt: new Date(), + }, + ]); - // Add cases table records - await queryInterface.bulkInsert('cases', [ - { - title: 'Signup', - state: 1, - priority: 1, - type: 4, - automationStatus: 1, - description: 'User can signup from signup form.', - template: 1, - preConditions: 'Not signed in', - expectedResults: '', - folderId: 1, - createdAt: new Date(), - updatedAt: new Date(), - }, - { - title: 'Signin', - state: 1, - priority: 1, - type: 4, - automationStatus: 1, - description: 'User can signin from signin form.', - template: 1, - preConditions: 'Not signed in', - expectedResults: '', - folderId: 1, - createdAt: new Date(), - updatedAt: new Date(), - }, - { - title: 'Contact', - state: 1, - priority: 1, - type: 4, - automationStatus: 1, - description: "User can send inquiry from 'contact us' form.", - template: 0, - preConditions: '', - expectedResults: '', - folderId: 2, - createdAt: new Date(), - updatedAt: new Date(), - }, - { - title: 'Register account with email address', - state: 1, - priority: 2, - type: 4, - automationStatus: 1, - description: 'Enter email addres and password in the form and click the Sign Up button.', - template: 0, - preConditions: '- You are not signed in\n- You have no account.', - expectedResults: 'You can sign up and you will be automatically redirected to your account page.', - folderId: 3, - createdAt: new Date(), - updatedAt: new Date(), - }, - { - title: 'Sign in with email', - state: 1, - priority: 2, - type: 4, - automationStatus: 1, - description: 'nter email addres and password in the form and click the Sign In button.', - template: 0, - preConditions: 'You are not signed in', - expectedResults: 'You can sign in and you will be automatically redirected to your account page.', - folderId: 3, - createdAt: new Date(), - updatedAt: new Date(), - }, - { - title: 'Social Account Sign In', - state: 1, - priority: 1, - type: 4, - automationStatus: 1, - description: 'You can sign in to the app with your Google account.', - template: 0, - preConditions: 'You are not signed in', - expectedResults: 'You can sign in and you will be automatically redirected to your account page.', - folderId: 3, - createdAt: new Date(), - updatedAt: new Date(), - }, - { - title: 'Edit Account', - state: 1, - priority: 3, - type: 4, - automationStatus: 1, - description: 'You can update user name and avatar', - template: 0, - preConditions: 'Prepare sample avatar image file(.png, .svg, .jpg)', - expectedResults: 'You can resister avatar image', - folderId: 3, - createdAt: new Date(), - updatedAt: new Date(), - }, - { - title: 'View Account', - state: 1, - priority: 0, - type: 1, - automationStatus: 1, - description: 'You cannot view not your account info', - template: 1, - preConditions: 'nothing', - expectedResults: '', - folderId: 3, - createdAt: new Date(), - updatedAt: new Date(), - }, - { - title: 'Muscle memory (Selection of muscle train part)', - state: 1, - priority: 2, - type: 1, - automationStatus: 1, - description: 'Selection of muscle train part', - template: 1, - preConditions: 'nothing', - expectedResults: 'Able to choose which parts of your body to train', - folderId: 4, - createdAt: new Date(), - updatedAt: new Date(), - }, - { - title: 'Muscle Memory (training sessions selection)', - state: 1, - priority: 2, - type: 1, - automationStatus: 1, - description: 'sessions selection', - template: 1, - preConditions: 'nothing', - expectedResults: 'Able to select the number of muscle training sessions', - folderId: 4, - createdAt: new Date(), - updatedAt: new Date(), - }, - { - title: 'Muscle Memory(closing dialog)', - state: 1, - priority: 3, - type: 3, - automationStatus: 1, - description: 'dialog functional check', - template: 1, - preConditions: 'open the dialog', - expectedResults: 'able to close the dialog by clicking "x" button', - folderId: 4, - createdAt: new Date(), - updatedAt: new Date(), - }, - { - title: 'Muscle Memory(selectio dialog)', - state: 1, - priority: 3, - type: 3, - automationStatus: 1, - description: 'responsibility of selection dialog', - template: 1, - preConditions: 'access the app by mobile device', - expectedResults: 'dialog will be displayed properly', - folderId: 4, - createdAt: new Date(), - updatedAt: new Date(), - }, - { - title: 'Chart display', - state: 1, - priority: 1, - type: 4, - automationStatus: 1, - description: '', - template: 0, - preConditions: '', - expectedResults: '', - folderId: 6, - createdAt: new Date(), - updatedAt: new Date(), - }, - { - title: 'Chart update', - state: 1, - priority: 1, - type: 4, - automationStatus: 1, - description: '', - template: 0, - preConditions: '', - expectedResults: '', - folderId: 6, - createdAt: new Date(), - updatedAt: new Date(), - }, - { - title: 'Chart palette color change', - state: 1, - priority: 1, - type: 4, - automationStatus: 1, - description: '', - template: 0, - preConditions: '', - expectedResults: '', - folderId: 6, - createdAt: new Date(), - updatedAt: new Date(), - }, - { - title: 'Simlation exec', - state: 1, - priority: 1, - type: 4, - automationStatus: 1, - description: '', - template: 0, - preConditions: '', - expectedResults: '', - folderId: 7, - createdAt: new Date(), - updatedAt: new Date(), - }, - { - title: 'Simlation update', - state: 1, - priority: 1, - type: 4, - automationStatus: 1, - description: '', - template: 0, - preConditions: '', - expectedResults: '', - folderId: 7, - createdAt: new Date(), - updatedAt: new Date(), - }, - { - title: 'Simlation result palette color', - state: 1, - priority: 1, - type: 4, - automationStatus: 1, - description: '', - template: 0, - preConditions: '', - expectedResults: '', - folderId: 7, - createdAt: new Date(), - updatedAt: new Date(), - }, - { - title: 'Simlation button disable condition', - state: 1, - priority: 1, - type: 4, - automationStatus: 1, - description: '', - template: 0, - preConditions: '', - expectedResults: '', - folderId: 7, - createdAt: new Date(), - updatedAt: new Date(), - }, - ]); + // Add cases table records + await queryInterface.bulkInsert('cases', [ + { + title: 'Signup', + state: 1, + priority: 1, + type: 4, + automationStatus: 1, + description: 'User can signup from signup form.', + template: 1, + preConditions: 'Not signed in', + expectedResults: '', + folderId: 1, + createdAt: new Date(), + updatedAt: new Date(), + }, + { + title: 'Signin', + state: 1, + priority: 1, + type: 4, + automationStatus: 1, + description: 'User can signin from signin form.', + template: 1, + preConditions: 'Not signed in', + expectedResults: '', + folderId: 1, + createdAt: new Date(), + updatedAt: new Date(), + }, + { + title: 'Contact', + state: 1, + priority: 1, + type: 4, + automationStatus: 1, + description: "User can send inquiry from 'contact us' form.", + template: 0, + preConditions: '', + expectedResults: '', + folderId: 2, + createdAt: new Date(), + updatedAt: new Date(), + }, + { + title: 'Register account with email address', + state: 1, + priority: 2, + type: 4, + automationStatus: 1, + description: 'Enter email addres and password in the form and click the Sign Up button.', + template: 0, + preConditions: '- You are not signed in\n- You have no account.', + expectedResults: 'You can sign up and you will be automatically redirected to your account page.', + folderId: 3, + createdAt: new Date(), + updatedAt: new Date(), + }, + { + title: 'Sign in with email', + state: 1, + priority: 2, + type: 4, + automationStatus: 1, + description: 'nter email addres and password in the form and click the Sign In button.', + template: 0, + preConditions: 'You are not signed in', + expectedResults: 'You can sign in and you will be automatically redirected to your account page.', + folderId: 3, + createdAt: new Date(), + updatedAt: new Date(), + }, + { + title: 'Social Account Sign In', + state: 1, + priority: 1, + type: 4, + automationStatus: 1, + description: 'You can sign in to the app with your Google account.', + template: 0, + preConditions: 'You are not signed in', + expectedResults: 'You can sign in and you will be automatically redirected to your account page.', + folderId: 3, + createdAt: new Date(), + updatedAt: new Date(), + }, + { + title: 'Edit Account', + state: 1, + priority: 3, + type: 4, + automationStatus: 1, + description: 'You can update user name and avatar', + template: 0, + preConditions: 'Prepare sample avatar image file(.png, .svg, .jpg)', + expectedResults: 'You can resister avatar image', + folderId: 3, + createdAt: new Date(), + updatedAt: new Date(), + }, + { + title: 'View Account', + state: 1, + priority: 0, + type: 1, + automationStatus: 1, + description: 'You cannot view not your account info', + template: 1, + preConditions: 'nothing', + expectedResults: '', + folderId: 3, + createdAt: new Date(), + updatedAt: new Date(), + }, + { + title: 'Muscle memory (Selection of muscle train part)', + state: 1, + priority: 2, + type: 1, + automationStatus: 1, + description: 'Selection of muscle train part', + template: 1, + preConditions: 'nothing', + expectedResults: 'Able to choose which parts of your body to train', + folderId: 4, + createdAt: new Date(), + updatedAt: new Date(), + }, + { + title: 'Muscle Memory (training sessions selection)', + state: 1, + priority: 2, + type: 1, + automationStatus: 1, + description: 'sessions selection', + template: 1, + preConditions: 'nothing', + expectedResults: 'Able to select the number of muscle training sessions', + folderId: 4, + createdAt: new Date(), + updatedAt: new Date(), + }, + { + title: 'Muscle Memory(closing dialog)', + state: 1, + priority: 3, + type: 3, + automationStatus: 1, + description: 'dialog functional check', + template: 1, + preConditions: 'open the dialog', + expectedResults: 'able to close the dialog by clicking "x" button', + folderId: 4, + createdAt: new Date(), + updatedAt: new Date(), + }, + { + title: 'Muscle Memory(selectio dialog)', + state: 1, + priority: 3, + type: 3, + automationStatus: 1, + description: 'responsibility of selection dialog', + template: 1, + preConditions: 'access the app by mobile device', + expectedResults: 'dialog will be displayed properly', + folderId: 4, + createdAt: new Date(), + updatedAt: new Date(), + }, + { + title: 'Chart display', + state: 1, + priority: 1, + type: 4, + automationStatus: 1, + description: '', + template: 0, + preConditions: '', + expectedResults: '', + folderId: 6, + createdAt: new Date(), + updatedAt: new Date(), + }, + { + title: 'Chart update', + state: 1, + priority: 1, + type: 4, + automationStatus: 1, + description: '', + template: 0, + preConditions: '', + expectedResults: '', + folderId: 6, + createdAt: new Date(), + updatedAt: new Date(), + }, + { + title: 'Chart palette color change', + state: 1, + priority: 1, + type: 4, + automationStatus: 1, + description: '', + template: 0, + preConditions: '', + expectedResults: '', + folderId: 6, + createdAt: new Date(), + updatedAt: new Date(), + }, + { + title: 'Simlation exec', + state: 1, + priority: 1, + type: 4, + automationStatus: 1, + description: '', + template: 0, + preConditions: '', + expectedResults: '', + folderId: 7, + createdAt: new Date(), + updatedAt: new Date(), + }, + { + title: 'Simlation update', + state: 1, + priority: 1, + type: 4, + automationStatus: 1, + description: '', + template: 0, + preConditions: '', + expectedResults: '', + folderId: 7, + createdAt: new Date(), + updatedAt: new Date(), + }, + { + title: 'Simlation result palette color', + state: 1, + priority: 1, + type: 4, + automationStatus: 1, + description: '', + template: 0, + preConditions: '', + expectedResults: '', + folderId: 7, + createdAt: new Date(), + updatedAt: new Date(), + }, + { + title: 'Simlation button disable condition', + state: 1, + priority: 1, + type: 4, + automationStatus: 1, + description: '', + template: 0, + preConditions: '', + expectedResults: '', + folderId: 7, + createdAt: new Date(), + updatedAt: new Date(), + }, + ]); - // Add steps table records - await queryInterface.bulkInsert('steps', [ - { - step: 'Check accout status', - result: 'Never have account', - createdAt: new Date(), - updatedAt: new Date(), - }, - { - step: "Enter signup form and then, click 'signup' button.", - result: 'Automatically redirect to the account page', - createdAt: new Date(), - updatedAt: new Date(), - }, - { - step: 'Check signin status', - result: 'Not signed in', - createdAt: new Date(), - updatedAt: new Date(), - }, - { - step: "Enter signin form and then, click 'signin button.'", - result: 'Automatically redirect to the account page', - createdAt: new Date(), - updatedAt: new Date(), - }, - { - step: 'Open browser with secret mode', - result: 'Open Local Storage in Developer Tools and confirm that there is no session.', - createdAt: new Date(), - updatedAt: new Date(), - }, - { - step: "move to 'account/userinfo?userid=xxx' page", - result: 'Redirected to an error page with a 403 error.', - createdAt: new Date(), - updatedAt: new Date(), - }, - ]); + // Add steps table records + await queryInterface.bulkInsert('steps', [ + { + step: 'Check accout status', + result: 'Never have account', + createdAt: new Date(), + updatedAt: new Date(), + }, + { + step: "Enter signup form and then, click 'signup' button.", + result: 'Automatically redirect to the account page', + createdAt: new Date(), + updatedAt: new Date(), + }, + { + step: 'Check signin status', + result: 'Not signed in', + createdAt: new Date(), + updatedAt: new Date(), + }, + { + step: "Enter signin form and then, click 'signin button.'", + result: 'Automatically redirect to the account page', + createdAt: new Date(), + updatedAt: new Date(), + }, + { + step: 'Open browser with secret mode', + result: 'Open Local Storage in Developer Tools and confirm that there is no session.', + createdAt: new Date(), + updatedAt: new Date(), + }, + { + step: "move to 'account/userinfo?userid=xxx' page", + result: 'Redirected to an error page with a 403 error.', + createdAt: new Date(), + updatedAt: new Date(), + }, + ]); - // Add case-step join table - await queryInterface.bulkInsert('caseSteps', [ - { - caseId: 1, - stepId: 1, - stepNo: 1, - createdAt: new Date(), - updatedAt: new Date(), - }, - { - caseId: 1, - stepId: 2, - stepNo: 2, - createdAt: new Date(), - updatedAt: new Date(), - }, - { - caseId: 2, - stepId: 3, - stepNo: 1, - createdAt: new Date(), - updatedAt: new Date(), - }, - { - caseId: 2, - stepId: 4, - stepNo: 2, - createdAt: new Date(), - updatedAt: new Date(), - }, - { - caseId: 8, - stepId: 5, - stepNo: 1, - createdAt: new Date(), - updatedAt: new Date(), - }, - { - caseId: 8, - stepId: 6, - stepNo: 2, - createdAt: new Date(), - updatedAt: new Date(), - }, - ]); + // Add case-step join table + await queryInterface.bulkInsert('caseSteps', [ + { + caseId: 1, + stepId: 1, + stepNo: 1, + createdAt: new Date(), + updatedAt: new Date(), + }, + { + caseId: 1, + stepId: 2, + stepNo: 2, + createdAt: new Date(), + updatedAt: new Date(), + }, + { + caseId: 2, + stepId: 3, + stepNo: 1, + createdAt: new Date(), + updatedAt: new Date(), + }, + { + caseId: 2, + stepId: 4, + stepNo: 2, + createdAt: new Date(), + updatedAt: new Date(), + }, + { + caseId: 8, + stepId: 5, + stepNo: 1, + createdAt: new Date(), + updatedAt: new Date(), + }, + { + caseId: 8, + stepId: 6, + stepNo: 2, + createdAt: new Date(), + updatedAt: new Date(), + }, + ]); - await queryInterface.bulkInsert('attachments', [ - { - title: 'Selenium logo', - detail: '', - filename: '861px-Selenium_Logo.png', - createdAt: new Date(), - updatedAt: new Date(), - }, - { - title: 'vitest logo', - detail: '', - filename: 'logo-shadow.svg', - createdAt: new Date(), - updatedAt: new Date(), - }, - ]); + await queryInterface.bulkInsert('attachments', [ + { + title: 'Selenium logo', + detail: '', + filename: '861px-Selenium_Logo.png', + createdAt: new Date(), + updatedAt: new Date(), + }, + { + title: 'vitest logo', + detail: '', + filename: 'logo-shadow.svg', + createdAt: new Date(), + updatedAt: new Date(), + }, + ]); - // copy sample files to uploads folder - const sampleFolderPath = 'public/sample'; - const uploadsFolderPath = 'public/uploads'; - const SeleniumLogoFileName = '861px-Selenium_Logo.png'; - const vitestLogoFileName = 'logo-shadow.svg'; - if (!fs.existsSync(uploadsFolderPath)) { - fs.mkdirSync(uploadsFolderPath, { recursive: true }); + // copy sample files to uploads folder + const sampleFolderPath = 'public/sample'; + const uploadsFolderPath = 'public/uploads'; + const SeleniumLogoFileName = '861px-Selenium_Logo.png'; + const vitestLogoFileName = 'logo-shadow.svg'; + if (!fs.existsSync(uploadsFolderPath)) { + fs.mkdirSync(uploadsFolderPath, { recursive: true }); + } + fs.copyFile(`${sampleFolderPath}/${SeleniumLogoFileName}`, `${uploadsFolderPath}/${SeleniumLogoFileName}`, (err) => { + if (err) { + console.log(err); } - fs.copyFile( - `${sampleFolderPath}/${SeleniumLogoFileName}`, - `${uploadsFolderPath}/${SeleniumLogoFileName}`, - (err) => { - if (err) { - console.log(err); - } - } - ); - fs.copyFile(`${sampleFolderPath}/${vitestLogoFileName}`, `${uploadsFolderPath}/${vitestLogoFileName}`, (err) => { - if (err) { - console.log(err); - } - }); + }); + fs.copyFile(`${sampleFolderPath}/${vitestLogoFileName}`, `${uploadsFolderPath}/${vitestLogoFileName}`, (err) => { + if (err) { + console.log(err); + } + }); - await queryInterface.bulkInsert('caseAttachments', [ - { - caseId: 1, - attachmentId: 1, - createdAt: new Date(), - updatedAt: new Date(), - }, - { - caseId: 1, - attachmentId: 2, - createdAt: new Date(), - updatedAt: new Date(), - }, - ]); + await queryInterface.bulkInsert('caseAttachments', [ + { + caseId: 1, + attachmentId: 1, + createdAt: new Date(), + updatedAt: new Date(), + }, + { + caseId: 1, + attachmentId: 2, + createdAt: new Date(), + updatedAt: new Date(), + }, + ]); - await queryInterface.bulkInsert('runCases', [ - { - runId: 1, - caseId: 1, - status: 1, - createdAt: new Date(2024, 5, 15), - updatedAt: new Date(2024, 5, 15), - }, - { - runId: 1, - caseId: 2, - status: 1, - createdAt: new Date(2024, 5, 15), - updatedAt: new Date(2024, 5, 15), - }, - { - runId: 1, - caseId: 13, - status: 1, - createdAt: new Date(2024, 5, 18), - updatedAt: new Date(2024, 5, 18), - }, - { - runId: 1, - caseId: 14, - status: 1, - createdAt: new Date(2024, 5, 18), - updatedAt: new Date(2024, 5, 18), - }, - { - runId: 1, - caseId: 15, - status: 1, - createdAt: new Date(2024, 5, 18), - updatedAt: new Date(2024, 5, 18), - }, - { - runId: 1, - caseId: 16, - status: 0, - createdAt: new Date(2024, 5, 15), - updatedAt: new Date(2024, 5, 15), - }, - { - runId: 1, - caseId: 17, - status: 0, - createdAt: new Date(2024, 5, 16), - updatedAt: new Date(2024, 5, 16), - }, - { - runId: 1, - caseId: 18, - status: 2, - createdAt: new Date(2024, 5, 16), - updatedAt: new Date(2024, 5, 16), - }, - { - runId: 1, - caseId: 19, - status: 3, - createdAt: new Date(2024, 5, 17), - updatedAt: new Date(2024, 5, 17), - }, - ]); + await queryInterface.bulkInsert('runCases', [ + { + runId: 1, + caseId: 1, + status: 1, + createdAt: new Date(2024, 5, 15), + updatedAt: new Date(2024, 5, 15), + }, + { + runId: 1, + caseId: 2, + status: 1, + createdAt: new Date(2024, 5, 15), + updatedAt: new Date(2024, 5, 15), + }, + { + runId: 1, + caseId: 13, + status: 1, + createdAt: new Date(2024, 5, 18), + updatedAt: new Date(2024, 5, 18), + }, + { + runId: 1, + caseId: 14, + status: 1, + createdAt: new Date(2024, 5, 18), + updatedAt: new Date(2024, 5, 18), + }, + { + runId: 1, + caseId: 15, + status: 1, + createdAt: new Date(2024, 5, 18), + updatedAt: new Date(2024, 5, 18), + }, + { + runId: 1, + caseId: 16, + status: 0, + createdAt: new Date(2024, 5, 15), + updatedAt: new Date(2024, 5, 15), + }, + { + runId: 1, + caseId: 17, + status: 0, + createdAt: new Date(2024, 5, 16), + updatedAt: new Date(2024, 5, 16), + }, + { + runId: 1, + caseId: 18, + status: 2, + createdAt: new Date(2024, 5, 16), + updatedAt: new Date(2024, 5, 16), + }, + { + runId: 1, + caseId: 19, + status: 3, + createdAt: new Date(2024, 5, 17), + updatedAt: new Date(2024, 5, 17), + }, + ]); - await queryInterface.bulkInsert('members', [ - { - userId: 2, - projectId: 1, - role: 0, - createdAt: new Date(), - updatedAt: new Date(), - }, - { - userId: 7, - projectId: 1, - role: 2, - createdAt: new Date(), - updatedAt: new Date(), - }, - { - userId: 8, - projectId: 1, - role: 1, - createdAt: new Date(), - updatedAt: new Date(), - }, - ]); - }, + await queryInterface.bulkInsert('members', [ + { + userId: 2, + projectId: 1, + role: 0, + createdAt: new Date(), + updatedAt: new Date(), + }, + { + userId: 7, + projectId: 1, + role: 2, + createdAt: new Date(), + updatedAt: new Date(), + }, + { + userId: 8, + projectId: 1, + role: 1, + createdAt: new Date(), + updatedAt: new Date(), + }, + ]); +} - down: async () => { - // do nothing - }, -}; +export async function down() { + // do nothing +} diff --git a/backend/server.js b/backend/server.js index 062ef73..66ded8e 100644 --- a/backend/server.js +++ b/backend/server.js @@ -1,11 +1,12 @@ -const path = require('path'); -const express = require('express'); -const RateLimit = require('express-rate-limit'); -const { Sequelize } = require('sequelize'); +import path from 'path'; +import express from 'express'; +import cors from 'cors'; +import RateLimit from 'express-rate-limit'; +import { Sequelize } from 'sequelize'; +const __dirname = path.dirname(new URL(import.meta.url).pathname); const app = express(); // enable frontend access -const cors = require('cors'); const frontendOrigin = process.env.FRONTEND_ORIGIN || 'http://localhost:8000'; const corsOptions = { origin: frontendOrigin, @@ -36,116 +37,113 @@ const sequelize = new Sequelize({ }); // "/" -const indexRoute = require('./routes/index'); -app.use('/', indexRoute); +import indexRoute from './routes/index.js'; +app.use('/', indexRoute()); // "/health" -const healthIndexRoute = require('./routes/health/index')(); -app.use('/health', healthIndexRoute); +import healthIndexRoute from './routes/health/index.js'; +app.use('/health', healthIndexRoute()); // "users" -const usersIndexRoute = require('./routes/users/index')(sequelize); -const usersFindRoute = require('./routes/users/find')(sequelize); -const usersSearchRoute = require('./routes/users/search')(sequelize); -const signUpRoute = require('./routes/users/signup')(sequelize); -const signInRoute = require('./routes/users/signin')(sequelize); -app.use('/users', usersIndexRoute); -app.use('/users', usersFindRoute); -app.use('/users', usersSearchRoute); -app.use('/users', signUpRoute); -app.use('/users', signInRoute); -// ESM import -(async () => { - const updateRoute = await import('./routes/users/update.mjs'); - app.use('/users', updateRoute.default(sequelize)); -})(); +import usersIndexRoute from './routes/users/index.js'; +import usersFindRoute from './routes/users/find.js'; +import usersSearchRoute from './routes/users/search.js'; +import usersUpdateRoute from './routes/users/update.js'; +import signUpRoute from './routes/users/signup.js'; +import signInRoute from './routes/users/signin.js'; +app.use('/users', usersIndexRoute(sequelize)); +app.use('/users', usersFindRoute(sequelize)); +app.use('/users', usersSearchRoute(sequelize)); +app.use('/users', usersUpdateRoute(sequelize)); +app.use('/users', signUpRoute(sequelize)); +app.use('/users', signInRoute(sequelize)); // "/projects" -const projectsIndexRoute = require('./routes/projects/index')(sequelize); -const projectsShowRoute = require('./routes/projects/show')(sequelize); -const projectsNewRoute = require('./routes/projects/new')(sequelize); -const projectsEditRoute = require('./routes/projects/edit')(sequelize); -const projectsDeleteRoute = require('./routes/projects/delete')(sequelize); -app.use('/projects', projectsIndexRoute); -app.use('/projects', projectsShowRoute); -app.use('/projects', projectsNewRoute); -app.use('/projects', projectsEditRoute); -app.use('/projects', projectsDeleteRoute); +import projectsIndexRoute from './routes/projects/index.js'; +import projectsShowRoute from './routes/projects/show.js'; +import projectsNewRoute from './routes/projects/new.js'; +import projectsEditRoute from './routes/projects/edit.js'; +import projectsDeleteRoute from './routes/projects/delete.js'; +app.use('/projects', projectsIndexRoute(sequelize)); +app.use('/projects', projectsShowRoute(sequelize)); +app.use('/projects', projectsNewRoute(sequelize)); +app.use('/projects', projectsEditRoute(sequelize)); +app.use('/projects', projectsDeleteRoute(sequelize)); // "/folders" -const foldersIndexRoute = require('./routes/folders/index')(sequelize); -const foldersNewRoute = require('./routes/folders/new')(sequelize); -const foldersEditRoute = require('./routes/folders/edit')(sequelize); -const foldersDeleteRoute = require('./routes/folders/delete')(sequelize); -app.use('/folders', foldersIndexRoute); -app.use('/folders', foldersNewRoute); -app.use('/folders', foldersEditRoute); -app.use('/folders', foldersDeleteRoute); +import foldersIndexRoute from './routes/folders/index.js'; +import foldersNewRoute from './routes/folders/new.js'; +import foldersEditRoute from './routes/folders/edit.js'; +import foldersDeleteRoute from './routes/folders/delete.js'; +app.use('/folders', foldersIndexRoute(sequelize)); +app.use('/folders', foldersNewRoute(sequelize)); +app.use('/folders', foldersEditRoute(sequelize)); +app.use('/folders', foldersDeleteRoute(sequelize)); // "/cases" -// "/cases" -const casesDownloadRoute = require('./routes/cases/download')(sequelize); -const casesIndexRoute = require('./routes/cases/index')(sequelize); -const casesIndexByProjectIdRoute = require('./routes/cases/indexByProjectId')(sequelize); -const casesShowRoute = require('./routes/cases/show')(sequelize); -const casesNewRoute = require('./routes/cases/new')(sequelize); -const casesEditRoute = require('./routes/cases/edit')(sequelize); -const casesDeleteRoute = require('./routes/cases/delete')(sequelize); -app.use('/cases', casesDownloadRoute); -app.use('/cases', casesIndexRoute); -app.use('/cases', casesIndexByProjectIdRoute); -app.use('/cases', casesShowRoute); -app.use('/cases', casesNewRoute); -app.use('/cases', casesEditRoute); -app.use('/cases', casesDeleteRoute); +import casesDownloadRoute from './routes/cases/download.js'; +import casesIndexRoute from './routes/cases/index.js'; +import casesIndexByProjectIdRoute from './routes/cases/indexByProjectId.js'; +import casesShowRoute from './routes/cases/show.js'; +import casesNewRoute from './routes/cases/new.js'; +import casesEditRoute from './routes/cases/edit.js'; +import casesDeleteRoute from './routes/cases/delete.js'; +app.use('/cases', casesDownloadRoute(sequelize)); +app.use('/cases', casesIndexRoute(sequelize)); +app.use('/cases', casesIndexByProjectIdRoute(sequelize)); +app.use('/cases', casesShowRoute(sequelize)); +app.use('/cases', casesNewRoute(sequelize)); +app.use('/cases', casesEditRoute(sequelize)); +app.use('/cases', casesDeleteRoute(sequelize)); // "/steps" -const stepsEditRoute = require('./routes/steps/edit')(sequelize); -app.use('/steps', stepsEditRoute); +import stepsEditRoute from './routes/steps/edit.js'; +app.use('/steps', stepsEditRoute(sequelize)); // "/attachments" -const attachmentsNewRoute = require('./routes/attachments/new')(sequelize); -const attachmentsDeleteRoute = require('./routes/attachments/delete')(sequelize); -const attachmentsDownloadRoute = require('./routes/attachments/download')(sequelize); -app.use('/attachments', attachmentsNewRoute); -app.use('/attachments', attachmentsDeleteRoute); -app.use('/attachments', attachmentsDownloadRoute); +import attachmentsNewRoute from './routes/attachments/new.js'; +import attachmentsDeleteRoute from './routes/attachments/delete.js'; +import attachmentsDownloadRoute from './routes/attachments/download.js'; +app.use('/attachments', attachmentsNewRoute(sequelize)); +app.use('/attachments', attachmentsDeleteRoute(sequelize)); +app.use('/attachments', attachmentsDownloadRoute(sequelize)); // "/runs" -const runsDownloadRoute = require('./routes/runs/download')(sequelize); -const runsIndexRoute = require('./routes/runs/index')(sequelize); -const runsShowRoute = require('./routes/runs/show')(sequelize); -const runsNewRoute = require('./routes/runs/new')(sequelize); -const runsEditRoute = require('./routes/runs/edit')(sequelize); -const runDeleteRoute = require('./routes/runs/delete')(sequelize); -app.use('/runs', runsDownloadRoute); -app.use('/runs', runsIndexRoute); -app.use('/runs', runsShowRoute); -app.use('/runs', runsNewRoute); -app.use('/runs', runsEditRoute); -app.use('/runs', runDeleteRoute); +import runsDownloadRoute from './routes/runs/download.js'; +import runsIndexRoute from './routes/runs/index.js'; +import runsShowRoute from './routes/runs/show.js'; +import runsNewRoute from './routes/runs/new.js'; +import runsEditRoute from './routes/runs/edit.js'; +import runDeleteRoute from './routes/runs/delete.js'; + +app.use('/runs', runsDownloadRoute(sequelize)); +app.use('/runs', runsIndexRoute(sequelize)); +app.use('/runs', runsShowRoute(sequelize)); +app.use('/runs', runsNewRoute(sequelize)); +app.use('/runs', runsEditRoute(sequelize)); +app.use('/runs', runDeleteRoute(sequelize)); // "/runcases" -const runCaseIndexRoute = require('./routes/runcases/index')(sequelize); -const runCaseEditRoute = require('./routes/runcases/edit')(sequelize); -app.use('/runcases', runCaseIndexRoute); -app.use('/runcases', runCaseEditRoute); +import runCaseIndexRoute from './routes/runcases/index.js'; +import runCaseEditRoute from './routes/runcases/edit.js'; +app.use('/runcases', runCaseIndexRoute(sequelize)); +app.use('/runcases', runCaseEditRoute(sequelize)); // "/members" -const membersIndexRoute = require('./routes/members/index')(sequelize); -const membersNewRoute = require('./routes/members/new')(sequelize); -const membersEditRoute = require('./routes/members/edit')(sequelize); -const membersDeleteRoute = require('./routes/members/delete')(sequelize); -const membersCheckRoute = require('./routes/members/check')(sequelize); -app.use('/members', membersIndexRoute); -app.use('/members', membersNewRoute); -app.use('/members', membersEditRoute); -app.use('/members', membersDeleteRoute); -app.use('/members', membersCheckRoute); +import membersIndexRoute from './routes/members/index.js'; +import membersNewRoute from './routes/members/new.js'; +import membersEditRoute from './routes/members/edit.js'; +import membersDeleteRoute from './routes/members/delete.js'; +import membersCheckRoute from './routes/members/check.js'; +app.use('/members', membersIndexRoute(sequelize)); +app.use('/members', membersNewRoute(sequelize)); +app.use('/members', membersEditRoute(sequelize)); +app.use('/members', membersDeleteRoute(sequelize)); +app.use('/members', membersCheckRoute(sequelize)); // "/home" -const homeIndexRoute = require('./routes/home/index')(sequelize); -app.use('/home', homeIndexRoute); +import homeIndexRoute from './routes/home/index.js'; +app.use('/home', homeIndexRoute(sequelize)); if (!process.env.SECRET_KEY) { console.log( @@ -154,4 +152,4 @@ if (!process.env.SECRET_KEY) { } // Export the app instead of starting the server -module.exports = app; +export default app; diff --git a/combined_server.js b/entrypoint.js similarity index 65% rename from combined_server.js rename to entrypoint.js index bfcca11..9f5b83b 100644 --- a/combined_server.js +++ b/entrypoint.js @@ -1,9 +1,15 @@ +import { createServer as createHttpServer } from 'http'; +import path from 'path'; +import fs from 'fs'; +import { execSync } from 'child_process'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + // Use express from backend node_modules -const { createServer: createHttpServer } = require('http'); -const path = require('path'); -const fs = require('fs'); -const { execSync } = require('child_process'); -const express = require('./backend/node_modules/express'); +import expressModule from './backend/node_modules/express/index.js'; +const express = expressModule.default || expressModule; async function runMigrations() { try { @@ -13,9 +19,7 @@ async function runMigrations() { cwd: path.join(__dirname, 'backend'), stdio: 'inherit', }); - console.log('Database migrations completed successfully.'); - if (process.env.IS_DEMO === 'true' || process.env.IS_DEMO === '1') { console.log('Demo mode detected. Seeding the database...'); execSync('npx sequelize-cli db:seed:all', { @@ -30,15 +34,14 @@ async function runMigrations() { } } -function startServer() { +async function startServer() { try { const server = express(); const httpServer = createHttpServer(server); // Import the backend app - const backendApp = require('./backend/server'); - - // Use the backend app for /api routes + const backendAppModule = await import('./backend/server.js'); + const backendApp = backendAppModule.default || backendAppModule; server.use('/api', backendApp); // For Next.js standalone build @@ -46,21 +49,18 @@ function startServer() { const nextServerPath = './node_modules/next/dist/server/next.js'; if (fs.existsSync(nextServerPath)) { // Import Next.js - const next = require(nextServerPath); + const nextModule = await import(nextServerPath); + const next = nextModule.default || nextModule; // Initialize Next.js app const dev = process.env.NODE_ENV !== 'production'; const nextApp = next({ dev, dir: path.join(__dirname, '.') }); const handle = nextApp.getRequestHandler(); + await nextApp.prepare(); + console.log('nextjs prepared'); - // Prepare Next.js - nextApp.prepare().then(() => { - console.log('nextjs prepared'); - // Use Next.js to handle all other routes - server.all('*', (req, res) => { - return handle(req, res); - }); - }); + // Use Next.js to handle all other routes + server.all('*', (req, res) => handle(req, res)); } else { console.error('Next.js module not found at:', nextServerPath); server.all('*', (req, res) => { @@ -79,9 +79,11 @@ function startServer() { } } -runMigrations().then(() => { - startServer(); -}).catch(error => { - console.error('Failed to start application:', error); - process.exit(1); -}); +runMigrations() + .then(() => { + startServer(); + }) + .catch((error) => { + console.error('Failed to start application:', error); + process.exit(1); + }); diff --git a/package.json b/package.json index 0d4c8c0..f168b20 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "name": "unittcms", "version": "1.0.0", "private": true, + "type": "module", "scripts": { "format:check": "prettier --check frontend/**/*.{js,ts,jsx,tsx,json} backend/**/*.{js,ts,json}", "format": "prettier --write frontend/**/*.{js,ts,jsx,tsx,json} backend/**/*.{js,ts,json}", From 81805c605aa84ab270b13bad741ee0d63184a0c0 Mon Sep 17 00:00:00 2001 From: kimatata <117462761+kimatata@users.noreply.github.com> Date: Fri, 19 Sep 2025 21:02:09 +0900 Subject: [PATCH 6/8] chore: Improve UI consistency (#283) --- .../[folderId]/cases/TestCaseFilter.tsx | 4 +- .../[folderId]/cases/TestCaseTable.tsx | 86 ++++++++----------- .../[projectId]/runs/[runId]/RunEditor.tsx | 68 ++++++++------- 3 files changed, 75 insertions(+), 83 deletions(-) diff --git a/frontend/src/app/[locale]/projects/[projectId]/folders/[folderId]/cases/TestCaseFilter.tsx b/frontend/src/app/[locale]/projects/[projectId]/folders/[folderId]/cases/TestCaseFilter.tsx index 563d050..bdef510 100644 --- a/frontend/src/app/[locale]/projects/[projectId]/folders/[folderId]/cases/TestCaseFilter.tsx +++ b/frontend/src/app/[locale]/projects/[projectId]/folders/[folderId]/cases/TestCaseFilter.tsx @@ -90,7 +90,7 @@ export default function TestCaseFilter({

{messages.priority}

- + )}
- {((selectedKeys !== 'all' && selectedKeys.size > 0) || selectedKeys === 'all') && ( - - )} - - - - - - - + + + + + } onPress={() => onExportCases('json')}> + json + + } + onPress={() => onExportCases('csv')} + > + csv + + +
- - - - - - - + + + + + } + onPress={() => exportRun(tokenContext.token.access_token, Number(testRun.id), 'xml')} + > + xml + + } + onPress={() => exportRun(tokenContext.token.access_token, Number(testRun.id), 'json')} + > + json + + } + onPress={() => exportRun(tokenContext.token.access_token, Number(testRun.id), 'csv')} + > + csv + + + - - - {priorities.map((priority) => ( - -
- - {priorityMessages[priority.uid]} -
-
- ))} -
- +
+
+

{messages.caseTitle}

+ } + type="search" + value={title} + onValueChange={setTitle} + maxLength={100} + />
- -
-

{messages.type}

- - - - - - {testTypes.map((type) => ( - - {testTypeMessages[type.uid]} - - ))} - - +
+
+

{messages.priority}

+ + + + + + {priorities.map((priority) => ( + +
+ + {priorityMessages[priority.uid]} +
+
+ ))} +
+
+
+
+

{messages.type}

+ + + + + + {testTypes.map((type) => ( + + {testTypeMessages[type.uid]} + + ))} + + +
- -
- -
diff --git a/frontend/src/app/[locale]/projects/[projectId]/folders/[folderId]/cases/TestCaseTable.tsx b/frontend/src/app/[locale]/projects/[projectId]/folders/[folderId]/cases/TestCaseTable.tsx index a496f01..bf62b65 100644 --- a/frontend/src/app/[locale]/projects/[projectId]/folders/[folderId]/cases/TestCaseTable.tsx +++ b/frontend/src/app/[locale]/projects/[projectId]/folders/[folderId]/cases/TestCaseTable.tsx @@ -1,4 +1,4 @@ -import { useState, useMemo, useCallback, ReactNode, useEffect } from 'react'; +import { useState, useMemo, useCallback, ReactNode } from 'react'; import { Table, TableHeader, @@ -13,22 +13,12 @@ import { DropdownItem, Selection, SortDescriptor, - cn, Badge, - Input, - Spinner, + Popover, + PopoverContent, + PopoverTrigger, } from '@heroui/react'; -import { - Plus, - MoreVertical, - Trash, - FileDown, - ChevronDown, - Filter, - FileJson, - FileSpreadsheet, - SearchIcon, -} from 'lucide-react'; +import { Plus, MoreVertical, Trash, FileDown, ChevronDown, Filter, FileJson, FileSpreadsheet } from 'lucide-react'; import TestCaseFilter from './TestCaseFilter'; import { Link } from '@/src/i18n/routing'; import { CaseType, CasesMessages } from '@/types/case'; @@ -36,7 +26,6 @@ import { PriorityMessages } from '@/types/priority'; import { TestTypeMessages } from '@/types/testType'; import TestCasePriority from '@/components/TestCasePriority'; import { LocaleCodeType } from '@/types/locale'; -import useDebounce from '@/utils/useDebounce'; import { highlightSearchTerm } from '@/utils/highlightSearchTerm'; type Props = { @@ -47,16 +36,14 @@ type Props = { onDeleteCase: (caseId: number) => void; onDeleteCases: (caseIds: number[]) => void; onExportCases: (type: string) => void; - onFilterChange: (priorities: number[], types: number[]) => void; - onQueryChange: (q: string) => void; - queryTerm: string; + onFilterChange: (query: string, priorities: number[], types: number[]) => void; + activeTitleFilter: string; activePriorityFilters: number[]; activeTypeFilters: number[]; messages: CasesMessages; priorityMessages: PriorityMessages; testTypeMessages: TestTypeMessages; locale: LocaleCodeType; - isSearching: boolean; }; export default function TestCaseTable({ @@ -68,15 +55,13 @@ export default function TestCaseTable({ onDeleteCases, onExportCases, onFilterChange, - onQueryChange, + activeTitleFilter, activePriorityFilters, activeTypeFilters, messages, priorityMessages, testTypeMessages, locale, - queryTerm, - isSearching, }: Props) { const headerColumns = [ { name: messages.id, uid: 'id', sortable: true }, @@ -91,15 +76,6 @@ export default function TestCaseTable({ direction: 'ascending', }); const [showFilter, setShowFilter] = useState(false); - const [localQueryTerm, setLocalQueryTerm] = useState(queryTerm); - - const debouncedQuery = useDebounce((value: unknown) => { - onQueryChange(value as string); - }, 500); - - useEffect(() => { - setLocalQueryTerm(queryTerm); - }, [queryTerm]); const sortedItems = useMemo(() => { if (cases.length === 0) { @@ -118,17 +94,6 @@ export default function TestCaseTable({ onDeleteCase(deleteCaseId); }; - const handleFilterChange = () => { - setShowFilter(!showFilter); - }; - - const handleQueryChange = (value: string) => { - setLocalQueryTerm(value); - if (value.length >= 2 || value.length === 0) { - debouncedQuery(value); - } - }; - const renderCell = useCallback( (testCase: CaseType, columnKey: string): ReactNode => { const cellValue = testCase[columnKey as keyof CaseType]; @@ -148,7 +113,7 @@ export default function TestCaseTable({ > {highlightSearchTerm({ text: cellValue as string, - searchTerm: localQueryTerm, + searchTerm: activeTitleFilter, })} ); @@ -179,7 +144,7 @@ export default function TestCaseTable({ } }, // eslint-disable-next-line react-hooks/exhaustive-deps - [localQueryTerm] + [activeTitleFilter] ); const handleDeleteCases = () => { @@ -193,7 +158,7 @@ export default function TestCaseTable({ setSelectedKeys(new Set([])); }; - const hasActiveFilters = activePriorityFilters.length > 0 || activeTypeFilters.length > 0; + const activeFilterNum = (activeTitleFilter ? 1 : 0) + activePriorityFilters.length + activeTypeFilters.length; const classNames = useMemo( () => ({ @@ -233,42 +198,35 @@ export default function TestCaseTable({ {messages.delete} )} - } - endContent={isSearching && } - type="search" - value={localQueryTerm} - onValueChange={handleQueryChange} - aria-label={messages.searchPlaceholder} - maxLength={100} - /> - - - + + + + + + { + setShowFilter(false); + onFilterChange(newTitleFilter, newPriorityFilters, newTypeFilters); + }} + /> + +
- {showFilter && ( - - )}
(false); const [isUpdating, setIsUpdating] = useState(false); const [isDirty, setIsDirty] = useState(false); - const [exportType, setExportType] = useState(new Set(['xml'])); const router = useRouter(); useFormGuard(isDirty, messages.areYouSureLeave); @@ -201,10 +199,6 @@ export default function RunEditor({ setIsDirty(false); }; - const handleExportTypeChange = (keys: Selection) => { - setExportType(new Set(Array.from(keys as Set))); - }; - return ( <>
diff --git a/frontend/types/case.ts b/frontend/types/case.ts index 4304e80..a90e828 100644 --- a/frontend/types/case.ts +++ b/frontend/types/case.ts @@ -77,13 +77,13 @@ type CasesMessages = { caseDescription: string; create: string; pleaseEnter: string; + filter: string; clearAll: string; apply: string; selectPriorities: string; selected: string; type: string; selectTypes: string; - searchPlaceholder: string; }; type CaseMessages = { diff --git a/frontend/utils/caseControl.ts b/frontend/utils/caseControl.ts index fced7d1..0b8dfb7 100644 --- a/frontend/utils/caseControl.ts +++ b/frontend/utils/caseControl.ts @@ -26,9 +26,13 @@ async function fetchCase(jwt: string, caseId: number) { } } -async function fetchCases(jwt: string, folderId: number, priority?: number[], type?: number[], q?: string) { +async function fetchCases(jwt: string, folderId: number, title?: string, priority?: number[], type?: number[]) { const queryParams = [`folderId=${folderId}`]; + if (title) { + queryParams.push(`title=${title}`); + } + if (priority && priority.length > 0) { queryParams.push(`priority=${priority.join(',')}`); } @@ -37,10 +41,6 @@ async function fetchCases(jwt: string, folderId: number, priority?: number[], ty queryParams.push(`type=${type.join(',')}`); } - if (q) { - queryParams.push(`q=${q}`); - } - const query = queryParams.length > 0 ? `?${queryParams.join('&')}` : ''; const url = `${apiServer}/cases${query}`; diff --git a/frontend/utils/highlightSearchTerm.tsx b/frontend/utils/highlightSearchTerm.tsx index eb1216c..d5aa355 100644 --- a/frontend/utils/highlightSearchTerm.tsx +++ b/frontend/utils/highlightSearchTerm.tsx @@ -12,7 +12,7 @@ export function highlightSearchTerm({ text, searchTerm, className, - minSearchLength = 2, + minSearchLength = 1, }: HighlightSearchTermProps): ReactNode { if (!text || !searchTerm || searchTerm.length < minSearchLength) { return text || null; From 9baab8471c31dbbd0cf47a4a9559c2c66a1120b4 Mon Sep 17 00:00:00 2001 From: kimatata <117462761+kimatata@users.noreply.github.com> Date: Tue, 23 Sep 2025 11:27:24 +0900 Subject: [PATCH 8/8] chore: update version (#285) --- frontend/src/app/[locale]/health/HealthPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/[locale]/health/HealthPage.tsx b/frontend/src/app/[locale]/health/HealthPage.tsx index 441e7df..7b1f74f 100644 --- a/frontend/src/app/[locale]/health/HealthPage.tsx +++ b/frontend/src/app/[locale]/health/HealthPage.tsx @@ -62,7 +62,7 @@ export default function HealthPage({ messages, locale }: Props) { {messages.unittcms_version} - 1.0.0-beta.18 + 1.0.0-beta.19 {messages.api_server}