feat: add a search modal using the database (wip)

This commit is contained in:
Sonny
2024-05-25 03:40:08 +02:00
committed by Sonny
parent b28499a69a
commit 56c05f1bf6
21 changed files with 535 additions and 621 deletions

View File

@@ -10,3 +10,7 @@ prod:
seed:
node ace db:seed
down:
-docker compose down
-docker compose -f dev.docker-compose.yml down

View File

@@ -0,0 +1,17 @@
import type { HttpContext } from '@adonisjs/core/http';
import db from '@adonisjs/lucid/services/db';
export default class SearchesController {
async search({ request, auth }: HttpContext) {
const term = request.qs()?.term;
if (!term) {
return console.warn('qs term null');
}
const { rows } = await db.rawQuery('SELECT * FROM search_text(?, ?)', [
term,
auth.user!.id,
]);
return { results: rows };
}
}

View File

@@ -1,9 +0,0 @@
import vine from '@vinejs/vine';
export const searchValidator = vine.compile(
vine.object({
searchTerm: vine.string().trim().minLength(1).maxLength(254),
links: vine.boolean(),
collections: vine.boolean(),
})
);

View File

@@ -0,0 +1,52 @@
import { BaseSchema } from '@adonisjs/lucid/schema';
export default class extends BaseSchema {
async up() {
this.schema.raw(`
CREATE EXTENSION IF NOT EXISTS unaccent;
CREATE EXTENSION IF NOT EXISTS pg_trgm;
`);
this.schema.raw(`
CREATE INDEX ON links USING gin(to_tsvector('english', name));
CREATE INDEX ON collections USING gin(to_tsvector('english', name));
CREATE INDEX ON links USING gin(to_tsvector('french', name));
CREATE INDEX ON collections USING gin(to_tsvector('french', name));
`);
this.schema.raw(`
CREATE OR REPLACE FUNCTION search_text(search_query TEXT, p_author_id INTEGER)
RETURNS TABLE (
id INTEGER,
type TEXT,
name VARCHAR(254),
collection_id INTEGER,
matched_part TEXT,
rank DOUBLE PRECISION
)
AS $$
BEGIN
RETURN QUERY
SELECT links.id, 'link' AS type, links.name, collections.id AS collection_id,
ts_headline('english', unaccent(links.name), plainto_tsquery('english', unaccent(search_query))) AS matched_part,
ts_rank_cd(to_tsvector('english', unaccent(links.name)), plainto_tsquery('english', unaccent(search_query)))::DOUBLE PRECISION AS rank
FROM links
LEFT JOIN collections ON links.collection_id = collections.id
WHERE unaccent(links.name) ILIKE '%' || unaccent(search_query) || '%'
AND (p_author_id IS NULL OR links.author_id = p_author_id)
UNION ALL
SELECT collections.id, 'collection' AS type, collections.name, NULL AS collection_id,
ts_headline('english', unaccent(collections.name), plainto_tsquery('english', unaccent(search_query))) AS matched_part,
ts_rank_cd(to_tsvector('english', unaccent(collections.name)), plainto_tsquery('english', unaccent(search_query)))::DOUBLE PRECISION AS rank
FROM collections
WHERE unaccent(collections.name) ILIKE '%' || unaccent(search_query) || '%'
AND (p_author_id IS NULL OR collections.author_id = p_author_id)
ORDER BY rank DESC NULLS LAST, matched_part DESC NULLS LAST;
END;
$$
LANGUAGE plpgsql;
`);
}
async down() {
this.schema.raw('DROP FUNCTION IF EXISTS search_text');
}
}

View File

@@ -17,6 +17,7 @@ interface ModalProps {
title?: string;
children: ReactNode;
opened: boolean;
hideCloseBtn?: boolean;
close: () => void;
}
@@ -25,13 +26,14 @@ export default function Modal({
title,
children,
opened = true,
hideCloseBtn = false,
close,
}: ModalProps) {
const modalRef = useRef<HTMLDivElement>(null);
const { setGlobalHotkeysEnabled } = useGlobalHotkeys();
useClickOutside(modalRef, close);
useShortcut('ESCAPE_KEY', close, { ignoreGlobalHotkeysStatus: true });
useShortcut('ESCAPE_KEY', close);
useEffect(() => setGlobalHotkeysEnabled(!opened), [opened]);
@@ -44,12 +46,17 @@ export default function Modal({
createPortal(
<ModalWrapper>
<ModalContainer ref={modalRef}>
<ModalHeader>
{title && <TextEllipsis>{title}</TextEllipsis>}
<ModalCloseBtn onClick={close}>
<IoClose size={20} />
</ModalCloseBtn>
</ModalHeader>
{!hideCloseBtn ||
(title && (
<ModalHeader>
{title && <TextEllipsis>{title}</TextEllipsis>}
{!hideCloseBtn && (
<ModalCloseBtn onClick={close}>
<IoClose size={20} />
</ModalCloseBtn>
)}
</ModalHeader>
))}
<ModalBody>{children}</ModalBody>
</ModalContainer>
</ModalWrapper>,

View File

@@ -1,17 +1,21 @@
import type Collection from '#models/collection';
import styled from '@emotion/styled';
import { Link } from '@inertiajs/react';
import { route } from '@izzyjs/route/client';
import { AiFillFolderOpen, AiOutlineFolder } from 'react-icons/ai';
import TextEllipsis from '~/components/common/text_ellipsis';
import { Item } from '~/components/dashboard/side_nav/nav_item';
import useActiveCollection from '~/hooks/use_active_collection';
import { appendCollectionId } from '~/lib/navigation';
const CollectionItemStyle = styled(Item)<{ isActive: boolean }>(
({ theme, isActive }) => ({
cursor: 'pointer',
color: isActive ? theme.colors.primary : theme.colors.font,
backgroundColor: theme.colors.secondary,
})
);
const CollectionItemStyle = styled(Item, {
shouldForwardProp: (propName) => propName !== 'isActive',
})<{ isActive: boolean }>(({ theme, isActive }) => ({
cursor: 'pointer',
color: isActive ? theme.colors.primary : theme.colors.font,
backgroundColor: theme.colors.secondary,
}));
const CollectionItemLink = CollectionItemStyle.withComponent(Link);
const LinksCount = styled.div(({ theme }) => ({
minWidth: 'fit-content',
@@ -25,13 +29,13 @@ export default function CollectionItem({
}: {
collection: Collection;
}) {
const { activeCollection, setActiveCollection } = useActiveCollection();
const { activeCollection } = useActiveCollection();
const isActiveCollection = collection.id === activeCollection?.id;
const FolderIcon = isActiveCollection ? AiFillFolderOpen : AiOutlineFolder;
return (
<CollectionItemStyle
onClick={() => setActiveCollection(collection)}
<CollectionItemLink
href={appendCollectionId(route('dashboard').url, collection.id)}
isActive={isActiveCollection}
>
<FolderIcon css={{ minWidth: '24px' }} size={24} />
@@ -39,6 +43,6 @@ export default function CollectionItem({
{collection.links.length > 0 && (
<LinksCount> {collection.links.length}</LinksCount>
)}
</CollectionItemStyle>
</CollectionItemLink>
);
}

View File

@@ -0,0 +1,147 @@
import styled from '@emotion/styled';
import { route } from '@izzyjs/route/client';
import { FormEvent, useCallback, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { AiOutlineFolder } from 'react-icons/ai';
import { CiLink } from 'react-icons/ci';
import { IoIosSearch } from 'react-icons/io';
import Modal from '~/components/common/modal/modal';
import UnstyledList from '~/components/common/unstyled/unstyled_list';
import useCollections from '~/hooks/use_collections';
import useToggle from '~/hooks/use_modal';
import useShortcut from '~/hooks/use_shortcut';
import { makeRequest } from '~/lib/request';
import { SearchResult } from '~/types/search';
const SearchInput = styled.input(({ theme }) => ({
width: '100%',
fontSize: '20px',
color: theme.colors.font,
backgroundColor: 'transparent',
paddingLeft: 0,
border: '1px solid transparent',
}));
interface SearchModalProps {
openItem: any;
}
function SearchModal({ openItem: OpenItem }: SearchModalProps) {
const { t } = useTranslation();
const { collections } = useCollections();
const [searchTerm, setSearchTerm] = useState<string>('');
const [results, setResults] = useState<SearchResult[]>([]);
const searchModal = useToggle(!!searchTerm);
const handleCloseModal = useCallback(() => {
searchModal.close();
setSearchTerm('');
}, [searchModal]);
const handleSearchInputChange = (value: string) => setSearchTerm(value);
const handleSubmit = (event: FormEvent<HTMLFormElement>) =>
event.preventDefault();
useShortcut('OPEN_SEARCH_KEY', searchModal.open, {
enabled: !searchModal.isShowing,
});
useShortcut('ESCAPE_KEY', handleCloseModal, {
enabled: searchModal.isShowing,
});
useEffect(() => {
if (searchTerm.trim() === '') {
return setResults([]);
}
const { url, method } = route('search', { qs: { term: searchTerm } });
makeRequest({
method,
url,
}).then(({ results: _results }) => setResults(_results));
}, [searchTerm]);
return (
<>
<OpenItem onClick={searchModal.open}>
<IoIosSearch /> {t('common:search')}
</OpenItem>
<Modal
close={handleCloseModal}
opened={searchModal.isShowing}
hideCloseBtn
>
<form
onSubmit={handleSubmit}
css={{
width: '100%',
display: 'flex',
gap: '0.5em',
flexDirection: 'column',
}}
>
<div
css={{
display: 'flex',
gap: '0.35em',
alignItems: 'center',
justifyContent: 'center',
}}
>
<label htmlFor="search" css={{ display: 'flex' }}>
<IoIosSearch size={24} />
</label>
<SearchInput
name="search"
id="search"
onChange={({ target }) => handleSearchInputChange(target.value)}
value={searchTerm}
placeholder={t('common:search')}
autoFocus
/>
</div>
{results.length > 0 && (
<UnstyledList css={{ maxHeight: '500px', overflow: 'auto' }}>
{results.map((result) => {
const ItemIcon =
result.type === 'link' ? CiLink : AiOutlineFolder;
const collection =
result.type === 'link'
? collections.find((c) => c.id === result.collection_id)
: undefined;
return (
<li
key={result.type + result.id.toString()}
css={{
fontSize: '16px',
display: 'flex',
gap: '0.35em',
alignItems: 'center',
}}
>
<ItemIcon size={24} />
<span
dangerouslySetInnerHTML={{
__html: result.matched_part ?? result.name,
}}
/>
{collection && <>({collection.name})</>}
</li>
);
})}
</UnstyledList>
)}
<button
type="submit"
disabled={searchTerm.length === 0}
style={{ display: 'none' }}
>
{t('common:confirm')}
</button>
</form>
</Modal>
</>
);
}
export default SearchModal;

View File

@@ -2,8 +2,8 @@ import styled from '@emotion/styled';
import { route } from '@izzyjs/route/client';
import { useTranslation } from 'react-i18next';
import { AiOutlineFolderAdd } from 'react-icons/ai';
import { IoAdd } from 'react-icons/io5';
import { MdOutlineAdminPanelSettings } from 'react-icons/md';
import { IoAdd, IoShieldHalfSharp } from 'react-icons/io5';
import SearchModal from '~/components/dashboard/search/search_modal';
import FavoriteList from '~/components/dashboard/side_nav/favorite/favorite_list';
import { Item, ItemLink } from '~/components/dashboard/side_nav/nav_item';
import UserCard from '~/components/dashboard/side_nav/user_card';
@@ -40,6 +40,8 @@ const AddButton = styled(ItemLink)(({ theme }) => ({
},
}));
const SearchButton = AddButton.withComponent(Item);
export default function SideNavigation() {
const { t } = useTranslation('common');
const { activeCollection } = useActiveCollection();
@@ -48,9 +50,10 @@ export default function SideNavigation() {
<div css={{ paddingInline: '10px' }}>
<UserCard />
<AdminButton>
<MdOutlineAdminPanelSettings /> {t('admin')}
<IoShieldHalfSharp /> {t('admin')}
</AdminButton>
<ModalSettings openItem={SettingsButton} />
<SearchModal openItem={SearchButton} />
<AddButton
href={appendCollectionId(
route('link.create-form').url,

View File

@@ -43,7 +43,8 @@ export default function FormLink({
handleSubmit,
}: FormLinkProps) {
const { t } = useTranslation('common');
const collectionId = useSearchParam('collectionId') ?? collections?.[0].id;
const collectionId =
Number(useSearchParam('collectionId')) ?? collections?.[0].id;
const onSubmit = (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();

View File

@@ -31,7 +31,7 @@ interface FormLayoutProps {
disableHomeLink?: boolean;
submitBtnDanger?: boolean;
collectionId?: string;
collectionId?: number;
}
export default function FormLayout({

View File

@@ -1 +1,2 @@
export const LS_LANG_KEY = 'language';
export const GOOGLE_SEARCH_URL = 'https://google.com/search?q=';

View File

@@ -1,43 +0,0 @@
import { buildSearchItem, formatSearchItem } from 'lib/search';
import { useMemo } from 'react';
import useCollections from '~/hooks/use_collections';
import { SearchItem, SearchResult } from '~/types/search';
export default function useSearchItem({
searchTerm = '',
disableLinks = false,
disableCollections = false,
}: {
searchTerm: string;
disableLinks?: boolean;
disableCollections?: boolean;
}) {
const { collections } = useCollections();
const itemsSearch: SearchItem[] = useMemo<SearchItem[]>(() => {
return collections.reduce((acc, collection) => {
const collectionItem = buildSearchItem(collection, 'collection');
const items: SearchItem[] = collection.links.map((link) =>
buildSearchItem(link, 'link')
);
return [...acc, ...items, collectionItem];
}, [] as SearchItem[]);
}, [collections]);
const itemsCompletion: SearchResult[] = useMemo(() => {
return itemsSearch.reduce((acc, item) => {
const formattedItem = formatSearchItem(item, searchTerm);
if (
(!disableLinks && item.type === 'link') ||
(!disableCollections && item.type === 'collection')
) {
return formattedItem ? [...acc, formattedItem] : acc;
}
return acc;
}, [] as SearchResult[]);
}, [itemsSearch, searchTerm, disableLinks, disableCollections]);
return itemsCompletion;
}

View File

@@ -2,18 +2,27 @@ import KEYS from '#constants/keys';
import { useHotkeys } from 'react-hotkeys-hook';
import useGlobalHotkeys from '~/hooks/use_global_hotkeys';
type ShortcutOptions = { ignoreGlobalHotkeysStatus?: boolean };
type ShortcutOptions = {
enabled?: boolean;
};
export default function useShortcut(
key: keyof typeof KEYS,
cb: () => void,
options: ShortcutOptions = {
ignoreGlobalHotkeysStatus: false,
{ enabled }: ShortcutOptions = {
enabled: true,
}
) {
const { globalHotkeysEnabled } = useGlobalHotkeys();
return useHotkeys(KEYS[key], cb, {
enabled: !options.ignoreGlobalHotkeysStatus ? globalHotkeysEnabled : true,
enableOnFormTags: ['INPUT'],
});
return useHotkeys(
KEYS[key],
(event) => {
event.preventDefault();
cb();
},
{
enabled: key === 'ESCAPE_KEY' ? true : enabled && globalHotkeysEnabled,
enableOnFormTags: ['INPUT'],
}
);
}

View File

@@ -1,54 +0,0 @@
import Collection from '#models/collection';
import Link from '#models/link';
import { route } from '@izzyjs/route/client';
import { appendCollectionId } from '~/lib/navigation';
import { SearchItem, SearchResult } from '~/types/search';
export function buildSearchItem(
item: Collection | Link,
type: SearchItem['type']
): SearchItem {
return {
id: item.id,
name: item.name,
url:
type === 'link'
? (item as Link).url
: appendCollectionId(route('dashboard').url, item.id),
type,
collection: type === 'link' ? (item as Link).collection : undefined,
};
}
export function formatSearchItem(
item: SearchItem,
searchTerm: string
): SearchResult | null {
const lowerCaseSearchTerm = searchTerm.toLowerCase().trim();
const lowerCaseName = item.name.toLowerCase().trim();
let currentIndex = 0;
let formattedName = '';
for (const [i, element] of Object(lowerCaseName).entries()) {
if (element === lowerCaseSearchTerm[currentIndex]) {
formattedName += `<b>${item.name[i]}</b>`;
currentIndex++;
} else {
formattedName += item.name[i];
}
}
if (currentIndex !== lowerCaseSearchTerm.length) {
// Search term not fully matched
return null;
}
return {
id: item.id,
name: <div dangerouslySetInnerHTML={{ __html: formattedName }} />,
url: item.url,
type: item.type,
collection: item.collection,
};
}

View File

@@ -105,19 +105,28 @@ function DashboardProviders(
const favoritesContextValue = useMemo(() => ({ favorites }), [favorites]);
const globalHotkeysContextValue = useMemo(
() => ({
globalHotkeysEnabled: globalHotkeysEnabled,
globalHotkeysEnabled,
setGlobalHotkeysEnabled,
}),
[globalHotkeysEnabled]
);
useShortcut('OPEN_CREATE_LINK_KEY', () =>
router.visit(
appendCollectionId(route('link.create-form').url, activeCollection?.id)
)
useShortcut(
'OPEN_CREATE_LINK_KEY',
() =>
router.visit(
appendCollectionId(route('link.create-form').url, activeCollection?.id)
),
{
enabled: globalHotkeysEnabled,
}
);
useShortcut('OPEN_CREATE_COLLECTION_KEY', () =>
router.visit(route('collection.create-form').url)
useShortcut(
'OPEN_CREATE_COLLECTION_KEY',
() => router.visit(route('collection.create-form').url),
{
enabled: globalHotkeysEnabled,
}
);
return (
<CollectionsContext.Provider value={collectionsContextValue}>

View File

@@ -13,7 +13,8 @@ export default function CreateLinkPage({
collections: Collection[];
}) {
const { t } = useTranslation('common');
const collectionId = useSearchParam('collectionId') ?? collections[0].id;
const collectionId =
Number(useSearchParam('collectionId')) ?? collections[0].id;
const { data, setData, submit, processing } = useForm({
name: '',
description: '',

View File

@@ -1,17 +1,18 @@
import type Link from '#models/link';
export interface SearchItem {
id: string;
name: string;
url: string;
type: 'collection' | 'link';
collection?: undefined | Link['collection'];
}
export interface SearchResult {
id: string;
name: React.ReactNode; // React node to support bold text
url: string;
type: 'collection' | 'link';
collection?: undefined | Link['collection'];
}
export type SearchResult =
| {
id: string;
type: 'collection';
name: string;
matched_part?: string;
rank?: number;
}
| {
id: string;
type: 'link';
name: string;
matched_part?: string;
rank?: number;
collection_id: number;
};

667
package-lock.json generated
View File

@@ -27,6 +27,7 @@
"edge.js": "^6.0.2",
"hex-rgb": "^5.0.0",
"i18next": "^23.11.4",
"knex": "^3.1.0",
"luxon": "^3.4.4",
"node-html-parser": "^6.1.13",
"pg": "^8.11.5",
@@ -39,8 +40,7 @@
"react-icons": "^5.2.1",
"react-swipeable": "^7.0.1",
"react-toggle": "^4.1.3",
"reflect-metadata": "^0.2.2",
"uuid": "^9.0.1"
"reflect-metadata": "^0.2.2"
},
"devDependencies": {
"@adonisjs/assembler": "^7.5.2",
@@ -57,7 +57,6 @@
"@types/react": "^18.3.2",
"@types/react-dom": "^18.3.0",
"@types/react-toggle": "^4.0.5",
"@types/uuid": "^9.0.8",
"@vitejs/plugin-react": "^4.2.1",
"eslint": "^8.57.0",
"hot-hook": "^0.2.6",
@@ -1797,67 +1796,6 @@
}
}
},
"node_modules/@adonisjs/lucid/node_modules/colorette": {
"version": "2.0.19",
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz",
"integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ=="
},
"node_modules/@adonisjs/lucid/node_modules/commander": {
"version": "10.0.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",
"integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==",
"engines": {
"node": ">=14"
}
},
"node_modules/@adonisjs/lucid/node_modules/escalade": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
"integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==",
"engines": {
"node": ">=6"
}
},
"node_modules/@adonisjs/lucid/node_modules/esm": {
"version": "3.2.25",
"resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz",
"integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==",
"engines": {
"node": ">=6"
}
},
"node_modules/@adonisjs/lucid/node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/@adonisjs/lucid/node_modules/get-package-type": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
"integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/@adonisjs/lucid/node_modules/getopts": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/getopts/-/getopts-2.3.0.tgz",
"integrity": "sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA=="
},
"node_modules/@adonisjs/lucid/node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/@adonisjs/lucid/node_modules/igniculus": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/igniculus/-/igniculus-1.5.0.tgz",
@@ -1866,25 +1804,6 @@
"node": ">=4.0.0"
}
},
"node_modules/@adonisjs/lucid/node_modules/interpret": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz",
"integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==",
"engines": {
"node": ">= 0.10"
}
},
"node_modules/@adonisjs/lucid/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==",
"dependencies": {
"hasown": "^2.0.0"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/@adonisjs/lucid/node_modules/kleur": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
@@ -1893,56 +1812,6 @@
"node": ">=6"
}
},
"node_modules/@adonisjs/lucid/node_modules/knex": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/knex/-/knex-3.1.0.tgz",
"integrity": "sha512-GLoII6hR0c4ti243gMs5/1Rb3B+AjwMOfjYm97pu0FOQa7JH56hgBxYf5WK2525ceSbBY1cjeZ9yk99GPMB6Kw==",
"dependencies": {
"colorette": "2.0.19",
"commander": "^10.0.0",
"debug": "4.3.4",
"escalade": "^3.1.1",
"esm": "^3.2.25",
"get-package-type": "^0.1.0",
"getopts": "2.3.0",
"interpret": "^2.2.0",
"lodash": "^4.17.21",
"pg-connection-string": "2.6.2",
"rechoir": "^0.8.0",
"resolve-from": "^5.0.0",
"tarn": "^3.0.2",
"tildify": "2.0.0"
},
"bin": {
"knex": "bin/cli.js"
},
"engines": {
"node": ">=16"
},
"peerDependenciesMeta": {
"better-sqlite3": {
"optional": true
},
"mysql": {
"optional": true
},
"mysql2": {
"optional": true
},
"pg": {
"optional": true
},
"pg-native": {
"optional": true
},
"sqlite3": {
"optional": true
},
"tedious": {
"optional": true
}
}
},
"node_modules/@adonisjs/lucid/node_modules/knex-dynamic-connection": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/knex-dynamic-connection/-/knex-dynamic-connection-3.1.1.tgz",
@@ -1955,83 +1824,6 @@
"node": ">=14.0.0"
}
},
"node_modules/@adonisjs/lucid/node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"node_modules/@adonisjs/lucid/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=="
},
"node_modules/@adonisjs/lucid/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=="
},
"node_modules/@adonisjs/lucid/node_modules/rechoir": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz",
"integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==",
"dependencies": {
"resolve": "^1.20.0"
},
"engines": {
"node": ">= 10.13.0"
}
},
"node_modules/@adonisjs/lucid/node_modules/resolve": {
"version": "1.22.8",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
"integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
"dependencies": {
"is-core-module": "^2.13.0",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
},
"bin": {
"resolve": "bin/resolve"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/@adonisjs/lucid/node_modules/resolve-from": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
"integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
"engines": {
"node": ">=8"
}
},
"node_modules/@adonisjs/lucid/node_modules/supports-preserve-symlinks-flag": {
"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==",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/@adonisjs/lucid/node_modules/tarn": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/tarn/-/tarn-3.0.2.tgz",
"integrity": "sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==",
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/@adonisjs/lucid/node_modules/tildify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/tildify/-/tildify-2.0.0.tgz",
"integrity": "sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw==",
"engines": {
"node": ">=8"
}
},
"node_modules/@adonisjs/presets": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@adonisjs/presets/-/presets-2.4.1.tgz",
@@ -2741,15 +2533,6 @@
"integrity": "sha512-132O1XCd7zcTkzS3FgkAzKmnBuNJjK8WjcTtNuoylj7MYbqw5eXehjQ5OK91g0zm7OTKIPeaAG4CPoRfD9M1Mg==",
"dev": true
},
"node_modules/@babel/core/node_modules/escalade": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
"integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==",
"dev": true,
"engines": {
"node": ">=6"
}
},
"node_modules/@babel/core/node_modules/escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
@@ -3674,12 +3457,6 @@
"source-map": "^0.6.1"
}
},
"node_modules/@japa/runner/node_modules/getopts": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/getopts/-/getopts-2.3.0.tgz",
"integrity": "sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA==",
"devOptional": true
},
"node_modules/@japa/runner/node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
@@ -5180,12 +4957,6 @@
"@types/react": "*"
}
},
"node_modules/@types/uuid": {
"version": "9.0.8",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz",
"integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==",
"dev": true
},
"node_modules/@types/validator": {
"version": "13.11.10",
"resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.11.10.tgz",
@@ -6537,15 +6308,6 @@
"is-callable": "^1.1.3"
}
},
"node_modules/api-contract-validator/node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"dev": true,
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/api-contract-validator/node_modules/function.prototype.name": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz",
@@ -6706,18 +6468,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/api-contract-validator/node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"dev": true,
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/api-contract-validator/node_modules/internal-slot": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz",
@@ -7097,12 +6847,6 @@
"node": ">=0.10.0"
}
},
"node_modules/api-contract-validator/node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
},
"node_modules/api-contract-validator/node_modules/lodash.flatten": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
@@ -7812,14 +7556,6 @@
"node": ">=0.8.0"
}
},
"node_modules/babel-plugin-macros/node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/babel-plugin-macros/node_modules/has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
@@ -7828,17 +7564,6 @@
"node": ">=4"
}
},
"node_modules/babel-plugin-macros/node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/babel-plugin-macros/node_modules/import-fresh": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
@@ -7859,17 +7584,6 @@
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
"integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="
},
"node_modules/babel-plugin-macros/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==",
"dependencies": {
"hasown": "^2.0.0"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/babel-plugin-macros/node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -7913,11 +7627,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/babel-plugin-macros/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=="
},
"node_modules/babel-plugin-macros/node_modules/path-type": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
@@ -7931,22 +7640,6 @@
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz",
"integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew=="
},
"node_modules/babel-plugin-macros/node_modules/resolve": {
"version": "1.22.8",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
"integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
"dependencies": {
"is-core-module": "^2.13.0",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
},
"bin": {
"resolve": "bin/resolve"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/babel-plugin-macros/node_modules/resolve-from": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
@@ -7966,17 +7659,6 @@
"node": ">=4"
}
},
"node_modules/babel-plugin-macros/node_modules/supports-preserve-symlinks-flag": {
"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==",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/babel-plugin-macros/node_modules/yaml": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
@@ -8265,6 +7947,14 @@
"integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
"dev": true
},
"node_modules/commander": {
"version": "10.0.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",
"integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==",
"engines": {
"node": ">=14"
}
},
"node_modules/convert-source-map": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
@@ -9209,6 +8899,14 @@
"node": ">=12"
}
},
"node_modules/escalade": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
"integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==",
"engines": {
"node": ">=6"
}
},
"node_modules/escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
@@ -9738,15 +9436,6 @@
"node": ">=8"
}
},
"node_modules/eslint-plugin-unicorn/node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"dev": true,
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/eslint-plugin-unicorn/node_modules/has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
@@ -9756,18 +9445,6 @@
"node": ">=4"
}
},
"node_modules/eslint-plugin-unicorn/node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"dev": true,
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/eslint-plugin-unicorn/node_modules/hosted-git-info": {
"version": "2.8.9",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
@@ -9804,18 +9481,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/eslint-plugin-unicorn/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==",
"dev": true,
"dependencies": {
"hasown": "^2.0.0"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/eslint-plugin-unicorn/node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -9858,12 +9523,6 @@
"node": ">=8"
}
},
"node_modules/eslint-plugin-unicorn/node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
},
"node_modules/eslint-plugin-unicorn/node_modules/min-indent": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
@@ -9957,12 +9616,6 @@
"node": ">=8"
}
},
"node_modules/eslint-plugin-unicorn/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==",
"dev": true
},
"node_modules/eslint-plugin-unicorn/node_modules/picocolors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz",
@@ -10049,23 +9702,6 @@
"jsesc": "bin/jsesc"
}
},
"node_modules/eslint-plugin-unicorn/node_modules/resolve": {
"version": "1.22.8",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
"integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
"dev": true,
"dependencies": {
"is-core-module": "^2.13.0",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
},
"bin": {
"resolve": "bin/resolve"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/eslint-plugin-unicorn/node_modules/safe-regex": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz",
@@ -10143,18 +9779,6 @@
"node": ">=4"
}
},
"node_modules/eslint-plugin-unicorn/node_modules/supports-preserve-symlinks-flag": {
"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==",
"dev": true,
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/eslint-plugin-unicorn/node_modules/type-fest": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
@@ -11173,6 +10797,14 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/esm": {
"version": "3.2.25",
"resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz",
"integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==",
"engines": {
"node": ">=6"
}
},
"node_modules/execa": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz",
@@ -11573,6 +11205,22 @@
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-package-type": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
"integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/get-port": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/get-port/-/get-port-7.1.0.tgz",
@@ -11585,6 +11233,22 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/getopts": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/getopts/-/getopts-2.3.0.tgz",
"integrity": "sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA=="
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/he": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
@@ -11711,6 +11375,25 @@
"@babel/runtime": "^7.23.2"
}
},
"node_modules/interpret": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz",
"integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==",
"engines": {
"node": ">= 0.10"
}
},
"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==",
"dependencies": {
"hasown": "^2.0.0"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/joycon": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz",
@@ -11805,6 +11488,61 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/knex": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/knex/-/knex-3.1.0.tgz",
"integrity": "sha512-GLoII6hR0c4ti243gMs5/1Rb3B+AjwMOfjYm97pu0FOQa7JH56hgBxYf5WK2525ceSbBY1cjeZ9yk99GPMB6Kw==",
"dependencies": {
"colorette": "2.0.19",
"commander": "^10.0.0",
"debug": "4.3.4",
"escalade": "^3.1.1",
"esm": "^3.2.25",
"get-package-type": "^0.1.0",
"getopts": "2.3.0",
"interpret": "^2.2.0",
"lodash": "^4.17.21",
"pg-connection-string": "2.6.2",
"rechoir": "^0.8.0",
"resolve-from": "^5.0.0",
"tarn": "^3.0.2",
"tildify": "2.0.0"
},
"bin": {
"knex": "bin/cli.js"
},
"engines": {
"node": ">=16"
},
"peerDependenciesMeta": {
"better-sqlite3": {
"optional": true
},
"mysql": {
"optional": true
},
"mysql2": {
"optional": true
},
"pg": {
"optional": true
},
"pg-native": {
"optional": true
},
"sqlite3": {
"optional": true
},
"tedious": {
"optional": true
}
}
},
"node_modules/knex/node_modules/colorette": {
"version": "2.0.19",
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz",
"integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ=="
},
"node_modules/lilconfig": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.0.0.tgz",
@@ -12211,6 +11949,11 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"node_modules/lodash.isequal": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
@@ -12347,6 +12090,11 @@
"node": ">=14.0.0"
}
},
"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=="
},
"node_modules/pg": {
"version": "8.11.5",
"resolved": "https://registry.npmjs.org/pg/-/pg-8.11.5.tgz",
@@ -12379,6 +12127,11 @@
"integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==",
"optional": true
},
"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=="
},
"node_modules/pg-pool": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.2.tgz",
@@ -12761,14 +12514,6 @@
"node": ">= 0.4"
}
},
"node_modules/qs/node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/qs/node_modules/get-intrinsic": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
@@ -12831,17 +12576,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/qs/node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/qs/node_modules/object-inspect": {
"version": "1.13.1",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
@@ -13133,15 +12867,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/read-package-up/node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"dev": true,
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/read-package-up/node_modules/has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
@@ -13151,18 +12876,6 @@
"node": ">=4"
}
},
"node_modules/read-package-up/node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"dev": true,
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/read-package-up/node_modules/hosted-git-info": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz",
@@ -13187,18 +12900,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/read-package-up/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==",
"dev": true,
"dependencies": {
"hasown": "^2.0.0"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/read-package-up/node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -13499,11 +13200,46 @@
"safe-buffer": "~5.2.0"
}
},
"node_modules/rechoir": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz",
"integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==",
"dependencies": {
"resolve": "^1.20.0"
},
"engines": {
"node": ">= 10.13.0"
}
},
"node_modules/reflect-metadata": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz",
"integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q=="
},
"node_modules/resolve": {
"version": "1.22.8",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
"integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
"dependencies": {
"is-core-module": "^2.13.0",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
},
"bin": {
"resolve": "bin/resolve"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/resolve-from": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
"integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
"engines": {
"node": ">=8"
}
},
"node_modules/rollup": {
"version": "4.17.2",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.17.2.tgz",
@@ -14018,6 +13754,33 @@
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
"integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw=="
},
"node_modules/supports-preserve-symlinks-flag": {
"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==",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/tarn": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/tarn/-/tarn-3.0.2.tgz",
"integrity": "sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==",
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/tildify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/tildify/-/tildify-2.0.0.tgz",
"integrity": "sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw==",
"engines": {
"node": ">=8"
}
},
"node_modules/ts-morph": {
"version": "22.0.0",
"resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-22.0.0.tgz",
@@ -14153,18 +13916,6 @@
"node": ">=14.17"
}
},
"node_modules/uuid": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
],
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/v8-compile-cache-lib": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",

View File

@@ -48,7 +48,6 @@
"@types/react": "^18.3.2",
"@types/react-dom": "^18.3.0",
"@types/react-toggle": "^4.0.5",
"@types/uuid": "^9.0.8",
"@vitejs/plugin-react": "^4.2.1",
"eslint": "^8.57.0",
"hot-hook": "^0.2.6",
@@ -79,6 +78,7 @@
"edge.js": "^6.0.2",
"hex-rgb": "^5.0.0",
"i18next": "^23.11.4",
"knex": "^3.1.0",
"luxon": "^3.4.4",
"node-html-parser": "^6.1.13",
"pg": "^8.11.5",
@@ -91,8 +91,7 @@
"react-icons": "^5.2.1",
"react-swipeable": "^7.0.1",
"react-toggle": "^4.1.3",
"reflect-metadata": "^0.2.2",
"uuid": "^9.0.1"
"reflect-metadata": "^0.2.2"
},
"hotHook": {
"boundaries": [
@@ -116,4 +115,4 @@
"lint-staged": {
"*.js,*.ts,*.jsx,*.tsx": "eslint --cache --fix"
}
}
}

View File

@@ -3,3 +3,4 @@ import './routes/auth.js';
import './routes/collection.js';
import './routes/favicon.js';
import './routes/link.js';
import './routes/search.js';

13
start/routes/search.ts Normal file
View File

@@ -0,0 +1,13 @@
import { middleware } from '#start/kernel';
import router from '@adonisjs/core/services/router';
const SearchesController = () => import('#controllers/searches_controller');
/**
* Search routes
*/
router
.group(() => {
router.get('/search', [SearchesController, 'search']).as('search');
})
.middleware([middleware.auth()]);