diff --git a/app/Controllers/Http/Api/AuthorsController.ts b/app/Controllers/Http/Api/AuthorsController.ts index 599da80..bc964a9 100644 --- a/app/Controllers/Http/Api/AuthorsController.ts +++ b/app/Controllers/Http/Api/AuthorsController.ts @@ -9,12 +9,14 @@ export default class AuthorsController { // where exists (select * from gba.documents inner join gba.link_documents_persons on "documents"."id" = "link_documents_persons"."document_id" // where ("link_documents_persons"."role" = 'author') and ("persons"."id" = "link_documents_persons"."person_id")); const authors = await Person.query() + .where('name_type', 'Personal') .whereHas('datasets', (dQuery) => { dQuery.wherePivot('role', 'author'); }) .withCount('datasets', (query) => { query.as('datasets_count'); - }); + }) + .orderBy('datasets_count', 'desc'); return authors; } diff --git a/app/Controllers/Http/Api/AvatarController.ts b/app/Controllers/Http/Api/AvatarController.ts index 0b337d9..74fe135 100644 --- a/app/Controllers/Http/Api/AvatarController.ts +++ b/app/Controllers/Http/Api/AvatarController.ts @@ -1,104 +1,135 @@ import type { HttpContext } from '@adonisjs/core/http'; import { StatusCodes } from 'http-status-codes'; +import redis from '@adonisjs/redis/services/main'; -const prefixes = ['von', 'van']; +const PREFIXES = ['von', 'van']; +const DEFAULT_SIZE = 50; +const FONT_SIZE_RATIO = 0.4; +const COLOR_LIGHTENING_PERCENT = 60; +const COLOR_DARKENING_FACTOR = 0.6; export default class AvatarController { public async generateAvatar({ request, response }: HttpContext) { try { - const { name, size } = request.only(['name', 'size']); + const { name, size = DEFAULT_SIZE } = request.only(['name', 'size']); + if (!name) { + return response.status(StatusCodes.BAD_REQUEST).json({ error: 'Name is required' }); + } + + // Build a unique cache key for the given name and size + const cacheKey = `avatar:${name.trim().toLowerCase()}-${size}`; + const cachedSvg = await redis.get(cacheKey); + if (cachedSvg) { + this.setResponseHeaders(response); + return response.send(cachedSvg); + } const initials = this.getInitials(name); + const colors = this.generateColors(name); + const svgContent = this.createSvg(size, colors, initials); - const originalColor = this.getColorFromName(name); - const backgroundColor = this.lightenColor(originalColor, 60); - const textColor = this.darkenColor(originalColor); - - const svgContent = ` - - - ${initials} - - `; - - response.header('Content-type', 'image/svg+xml'); - response.header('Cache-Control', 'no-cache'); - response.header('Pragma', 'no-cache'); - response.header('Expires', '0'); + // // Cache the generated avatar for future use, e.g. 1 hour expiry + await redis.setex(cacheKey, 3600, svgContent); + this.setResponseHeaders(response); return response.send(svgContent); } catch (error) { - return response.status(StatusCodes.OK).json({ error: error.message }); + return response.status(StatusCodes.INTERNAL_SERVER_ERROR).json({ error: error.message }); } } - private getInitials(name: string) { - const parts = name.split(' '); - let initials = ''; + private getInitials(name: string): string { + const parts = name + .trim() + .split(' ') + .filter((part) => part.length > 0); + + if (parts.length === 0) { + return 'NA'; + } if (parts.length >= 2) { - const firstName = parts[0]; - const lastName = parts[parts.length - 1]; - - const firstInitial = firstName.charAt(0).toUpperCase(); - const lastInitial = lastName.charAt(0).toUpperCase(); - - if (prefixes.includes(lastName.toLowerCase()) && lastName === lastName.toUpperCase()) { - initials = firstInitial + lastName.charAt(1).toUpperCase(); - } else { - initials = firstInitial + lastInitial; - } - } else if (parts.length === 1) { - initials = parts[0].substring(0, 2).toUpperCase(); + return this.getMultiWordInitials(parts); } - - return initials; + return parts[0].substring(0, 2).toUpperCase(); } - private getColorFromName(name: string) { + private getMultiWordInitials(parts: string[]): string { + const firstName = parts[0]; + const lastName = parts[parts.length - 1]; + const firstInitial = firstName.charAt(0).toUpperCase(); + const lastInitial = lastName.charAt(0).toUpperCase(); + + if (PREFIXES.includes(lastName.toLowerCase()) && lastName === lastName.toUpperCase()) { + return firstInitial + lastName.charAt(1).toUpperCase(); + } + return firstInitial + lastInitial; + } + + private generateColors(name: string): { background: string; text: string } { + const baseColor = this.getColorFromName(name); + return { + background: this.lightenColor(baseColor, COLOR_LIGHTENING_PERCENT), + text: this.darkenColor(baseColor), + }; + } + + private createSvg(size: number, colors: { background: string; text: string }, initials: string): string { + const fontSize = size * FONT_SIZE_RATIO; + return ` + + + ${initials} + + `; + } + + private setResponseHeaders(response: HttpContext['response']): void { + response.header('Content-type', 'image/svg+xml'); + response.header('Cache-Control', 'no-cache'); + response.header('Pragma', 'no-cache'); + response.header('Expires', '0'); + } + + private getColorFromName(name: string): string { let hash = 0; for (let i = 0; i < name.length; i++) { hash = name.charCodeAt(i) + ((hash << 5) - hash); } - let color = '#'; + + const colorParts = []; for (let i = 0; i < 3; i++) { const value = (hash >> (i * 8)) & 0xff; - color += ('00' + value.toString(16)).substr(-2); + colorParts.push(value.toString(16).padStart(2, '0')); } - return color.replace('#', ''); + return colorParts.join(''); } - private lightenColor(hexColor: string, percent: number) { - let r = parseInt(hexColor.substring(0, 2), 16); - let g = parseInt(hexColor.substring(2, 4), 16); - let b = parseInt(hexColor.substring(4, 6), 16); + private lightenColor(hexColor: string, percent: number): string { + const r = parseInt(hexColor.substring(0, 2), 16); + const g = parseInt(hexColor.substring(2, 4), 16); + const b = parseInt(hexColor.substring(4, 6), 16); - r = Math.floor((r * (100 + percent)) / 100); - g = Math.floor((g * (100 + percent)) / 100); - b = Math.floor((b * (100 + percent)) / 100); + const lightenValue = (value: number) => Math.min(255, Math.floor((value * (100 + percent)) / 100)); - r = r < 255 ? r : 255; - g = g < 255 ? g : 255; - b = b < 255 ? b : 255; + const newR = lightenValue(r); + const newG = lightenValue(g); + const newB = lightenValue(b); - const lighterHex = ((r << 16) | (g << 8) | b).toString(16); - - return lighterHex.padStart(6, '0'); + return ((newR << 16) | (newG << 8) | newB).toString(16).padStart(6, '0'); } - private darkenColor(hexColor: string) { + private darkenColor(hexColor: string): string { const r = parseInt(hexColor.slice(0, 2), 16); const g = parseInt(hexColor.slice(2, 4), 16); const b = parseInt(hexColor.slice(4, 6), 16); - const darkerR = Math.round(r * 0.6); - const darkerG = Math.round(g * 0.6); - const darkerB = Math.round(b * 0.6); + const darkenValue = (value: number) => Math.round(value * COLOR_DARKENING_FACTOR); - const darkerColor = ((darkerR << 16) + (darkerG << 8) + darkerB).toString(16); + const darkerR = darkenValue(r); + const darkerG = darkenValue(g); + const darkerB = darkenValue(b); - return darkerColor.padStart(6, '0'); + return ((darkerR << 16) + (darkerG << 8) + darkerB).toString(16).padStart(6, '0'); } } diff --git a/app/Controllers/Http/Api/DatasetController.ts b/app/Controllers/Http/Api/DatasetController.ts index 41befe6..b80949d 100644 --- a/app/Controllers/Http/Api/DatasetController.ts +++ b/app/Controllers/Http/Api/DatasetController.ts @@ -6,10 +6,15 @@ import { StatusCodes } from 'http-status-codes'; // node ace make:controller Author export default class DatasetController { public async index({}: HttpContext) { - // select * from gba.persons - // where exists (select * from gba.documents inner join gba.link_documents_persons on "documents"."id" = "link_documents_persons"."document_id" - // where ("link_documents_persons"."role" = 'author') and ("persons"."id" = "link_documents_persons"."person_id")); - const datasets = await Dataset.query().where('server_state', 'published').orWhere('server_state', 'deleted'); + // Select datasets with server_state 'published' or 'deleted' and sort by the last published date + const datasets = await Dataset.query() + .where(function (query) { + query.where('server_state', 'published') + .orWhere('server_state', 'deleted'); + }) + .preload('titles') + .preload('identifier') + .orderBy('server_date_published', 'desc'); return datasets; } diff --git a/app/Controllers/Http/Api/FileController.ts b/app/Controllers/Http/Api/FileController.ts index 3c1f3d4..8080479 100644 --- a/app/Controllers/Http/Api/FileController.ts +++ b/app/Controllers/Http/Api/FileController.ts @@ -14,7 +14,7 @@ export default class FileController { // where: { id: id }, // }); if (file) { - const filePath = '/storage/app/public/' + file.pathName; + const filePath = '/storage/app/data/' + file.pathName; const ext = path.extname(filePath); const fileName = file.label + ext; try { diff --git a/app/Controllers/Http/Api/UserController.ts b/app/Controllers/Http/Api/UserController.ts index 975e5e2..1d7a14a 100644 --- a/app/Controllers/Http/Api/UserController.ts +++ b/app/Controllers/Http/Api/UserController.ts @@ -9,6 +9,24 @@ import BackupCode from '#models/backup_code'; // Here we are generating secret and recovery codes for the user that’s enabling 2FA and storing them to our database. export default class UserController { + public async getSubmitters({ response }: HttpContext) { + try { + const submitters = await User.query() + .preload('roles', (query) => { + query.where('name', 'submitter') + }) + .whereHas('roles', (query) => { + query.where('name', 'submitter') + }) + .exec(); + return submitters; + } catch (error) { + return response.status(StatusCodes.INTERNAL_SERVER_ERROR).json({ + message: 'Invalid TOTP state', + }); + } + } + public async enable({ auth, response, request }: HttpContext) { const user = (await User.find(auth.user?.id)) as User; // await user.load('totp_secret'); diff --git a/app/Controllers/Http/Api/collections_controller.ts b/app/Controllers/Http/Api/collections_controller.ts new file mode 100644 index 0000000..04dcdb2 --- /dev/null +++ b/app/Controllers/Http/Api/collections_controller.ts @@ -0,0 +1,36 @@ +import type { HttpContext } from '@adonisjs/core/http'; +import Collection from '#models/collection'; + +export default class CollectionsController { + public async show({ params, response }: HttpContext) { + // Get the collection id from route parameters + const collectionId = params.id; + + // Find the selected collection by id + const collection = await Collection.find(collectionId); + if (!collection) { + return response.status(404).json({ message: 'Collection not found' }); + } + + // Query for narrower concepts: collections whose parent_id equals the selected collection's id + const narrowerCollections = await Collection.query().where('parent_id', collection.id) || []; + + // For broader concept, if the selected collection has a parent_id fetch that record (otherwise null) + const broaderCollection: Collection[] | never[] | null = await (async () => { + if (collection.parent_id) { + // Try to fetch the parent... + const parent = await Collection.find(collection.parent_id) + // If found, return it wrapped in an array; if not found, return null (or empty array if you prefer) + return parent ? [parent] : null + } + return [] + })() + + // Return the selected collection along with its narrower and broader concepts in JSON format + return response.json({ + selectedCollection: collection, + narrowerCollections, + broaderCollection, + }); + } +} diff --git a/app/Controllers/Http/Oai/OaiController.ts b/app/Controllers/Http/Oai/OaiController.ts index 6048ca4..db49a32 100644 --- a/app/Controllers/Http/Oai/OaiController.ts +++ b/app/Controllers/Http/Oai/OaiController.ts @@ -19,14 +19,13 @@ import XmlModel from '#app/Library/XmlModel'; import logger from '@adonisjs/core/services/logger'; import ResumptionToken from '#app/Library/Oai/ResumptionToken'; // import Config from '@ioc:Adonis/Core/Config'; -import config from '@adonisjs/core/services/config' +import config from '@adonisjs/core/services/config'; // import { inject } from '@adonisjs/fold'; -import { inject } from '@adonisjs/core' +import { inject } from '@adonisjs/core'; // import { TokenWorkerContract } from "MyApp/Models/TokenWorker"; import TokenWorkerContract from '#library/Oai/TokenWorkerContract'; import { ModelQueryBuilderContract } from '@adonisjs/lucid/types/model'; - interface XslTParameter { [key: string]: any; } @@ -35,12 +34,14 @@ interface Dictionary { [index: string]: string; } -interface ListParameter { +interface PagingParameter { cursor: number; - totalIds: number; + totalLength: number; start: number; - reldocIds: (number | null)[]; + nextDocIds: number[]; + activeWorkIds: number[]; metadataPrefix: string; + queryParams: Object; } @inject() @@ -49,6 +50,7 @@ export default class OaiController { private sampleRegEx = /^[A-Za-zäüÄÜß0-9\-_.!~]+$/; private xsltParameter: XslTParameter; + private firstPublishedDataset: Dataset | null; /** * Holds xml representation of document information to be processed. * @@ -57,7 +59,6 @@ export default class OaiController { private xml: XMLBuilder; private proc; - constructor(public tokenWorker: TokenWorkerContract) { // Load the XSLT file this.proc = readFileSync('public/assets2/datasetxml2oai.sef.json'); @@ -85,9 +86,9 @@ export default class OaiController { let earliestDateFromDb; // const oaiRequest: OaiParameter = request.body; try { - const firstPublishedDataset: Dataset | null = await Dataset.earliestPublicationDate(); - firstPublishedDataset != null && - (earliestDateFromDb = firstPublishedDataset.server_date_published.toFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")); + this.firstPublishedDataset = await Dataset.earliestPublicationDate(); + this.firstPublishedDataset != null && + (earliestDateFromDb = this.firstPublishedDataset.server_date_published.toFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")); this.xsltParameter['earliestDatestamp'] = earliestDateFromDb; // start the request await this.handleRequest(oaiRequest, request); @@ -162,22 +163,19 @@ export default class OaiController { } else if (verb == 'GetRecord') { await this.handleGetRecord(oaiRequest); } else if (verb == 'ListRecords') { - await this.handleListRecords(oaiRequest); + // Get browser fingerprint from the request: + const browserFingerprint = this.getBrowserFingerprint(request); + await this.handleListRecords(oaiRequest, browserFingerprint); } else if (verb == 'ListIdentifiers') { - await this.handleListIdentifiers(oaiRequest); + // Get browser fingerprint from the request: + const browserFingerprint = this.getBrowserFingerprint(request); + await this.handleListIdentifiers(oaiRequest, browserFingerprint); } else if (verb == 'ListSets') { await this.handleListSets(); } else { this.handleIllegalVerb(); } } else { - // // try { - // // console.log("Async code example.") - // const err = new PageNotFoundException("verb not found"); - // throw err; - // // } catch (error) { // manually catching - // // next(error); // passing to default middleware error handler - // // } throw new OaiModelException( StatusCodes.INTERNAL_SERVER_ERROR, 'The verb provided in the request is illegal.', @@ -187,11 +185,11 @@ export default class OaiController { } protected handleIdentify() { - const email = process.env.OAI_EMAIL || 'repository@geosphere.at'; - const repositoryName = 'Tethys RDR'; - const repIdentifier = 'tethys.at'; - const sampleIdentifier = 'oai:' + repIdentifier + ':1'; //$this->_configuration->getSampleIdentifier(); - + // Get configuration values from environment or a dedicated configuration service + const email = process.env.OAI_EMAIL ?? 'repository@geosphere.at'; + const repositoryName = process.env.OAI_REPOSITORY_NAME ?? 'Tethys RDR'; + const repIdentifier = process.env.OAI_REP_IDENTIFIER ?? 'tethys.at'; + const sampleIdentifier = `oai:${repIdentifier}:1`; // Dataset::earliestPublicationDate()->server_date_published->format('Y-m-d\TH:i:s\Z') : null; // earliestDateFromDb!= null && (this.xsltParameter['earliestDatestamp'] = earliestDateFromDb?.server_date_published); @@ -216,7 +214,7 @@ export default class OaiController { const sets: { [key: string]: string } = { 'open_access': 'Set for open access licenses', - 'openaire_data': "OpenAIRE", + 'openaire_data': 'OpenAIRE', 'doc-type:ResearchData': 'Set for document type ResearchData', ...(await this.getSetsForDatasetTypes()), ...(await this.getSetsForCollections()), @@ -234,7 +232,15 @@ export default class OaiController { const repIdentifier = 'tethys.at'; this.xsltParameter['repIdentifier'] = repIdentifier; + // Validate that required parameter exists early + if (!('identifier' in oaiRequest)) { + throw new BadOaiModelException('The prefix of the identifier argument is unknown.'); + } + + // Validate and extract the dataset identifier from the request const dataId = this.validateAndGetIdentifier(oaiRequest); + + // Retrieve dataset with associated XML cache and collection roles const dataset = await Dataset.query() .where('publish_id', dataId) .preload('xmlCache') @@ -251,59 +257,61 @@ export default class OaiController { ); } + // Validate and set the metadata prefix parameter const metadataPrefix = this.validateAndGetMetadataPrefix(oaiRequest); this.xsltParameter['oai_metadataPrefix'] = metadataPrefix; - // do not deliver datasets which are restricted by document state defined in deliveringStates + + // Ensure that the dataset is in an exportable state this.validateDatasetState(dataset); - // add xml elements + // Build the XML for the dataset record and add it to the root node const datasetNode = this.xml.root().ele('Datasets'); await this.createXmlRecord(dataset, datasetNode); } - protected async handleListIdentifiers(oaiRequest: Dictionary) { - !this.tokenWorker.isConnected && (await this.tokenWorker.connect()); + protected async handleListIdentifiers(oaiRequest: Dictionary, browserFingerprint: string) { + if (!this.tokenWorker.isConnected) { + await this.tokenWorker.connect(); + } const maxIdentifier: number = config.get('oai.max.listidentifiers', 100); - await this.handleLists(oaiRequest, maxIdentifier); + await this.handleLists(oaiRequest, maxIdentifier, browserFingerprint); } - protected async handleListRecords(oaiRequest: Dictionary) { - !this.tokenWorker.isConnected && (await this.tokenWorker.connect()); + protected async handleListRecords(oaiRequest: Dictionary, browserFingerprint: string) { + if (!this.tokenWorker.isConnected) { + await this.tokenWorker.connect(); + } const maxRecords: number = config.get('oai.max.listrecords', 100); - await this.handleLists(oaiRequest, maxRecords); + await this.handleLists(oaiRequest, maxRecords, browserFingerprint); } - private async handleLists(oaiRequest: Dictionary, maxRecords: number) { - maxRecords = maxRecords || 100; + private async handleLists(oaiRequest: Dictionary, maxRecords: number, browserFingerprint: string) { const repIdentifier = 'tethys.at'; this.xsltParameter['repIdentifier'] = repIdentifier; const datasetNode = this.xml.root().ele('Datasets'); - // list initialisation - const numWrapper: ListParameter = { + const paginationParams: PagingParameter ={ cursor: 0, - totalIds: 0, + totalLength: 0, start: maxRecords + 1, - reldocIds: [], + nextDocIds: [], + activeWorkIds: [], metadataPrefix: '', + queryParams: {}, }; - // resumptionToken is defined if ('resumptionToken' in oaiRequest) { - await this.handleResumptionToken(oaiRequest, maxRecords, numWrapper); + await this.handleResumptionToken(oaiRequest, maxRecords, paginationParams); } else { - // no resumptionToken is given - await this.handleNoResumptionToken(oaiRequest, numWrapper); + await this.handleNoResumptionToken(oaiRequest, paginationParams, maxRecords); } - // handling of document ids - const restIds = numWrapper.reldocIds as number[]; - const workIds = restIds.splice(0, maxRecords) as number[]; // array_splice(restIds, 0, maxRecords); + const nextIds: number[] = paginationParams.nextDocIds; + const workIds: number[] = paginationParams.activeWorkIds; - // no records returned - if (workIds.length == 0) { + if (workIds.length === 0) { throw new OaiModelException( StatusCodes.INTERNAL_SERVER_ERROR, 'The combination of the given values results in an empty list.', @@ -311,169 +319,218 @@ export default class OaiController { ); } - const datasets: Dataset[] = await Dataset.query() + const datasets = await Dataset.query() .whereIn('publish_id', workIds) .preload('xmlCache') .preload('collections', (builder) => { builder.preload('collectionRole'); }) .orderBy('publish_id'); - for (const dataset of datasets) { await this.createXmlRecord(dataset, datasetNode); } - - // store the further Ids in a resumption-file - const countRestIds = restIds.length; //84 - if (countRestIds > 0) { - const token = new ResumptionToken(); - token.startPosition = numWrapper.start; //101 - token.totalIds = numWrapper.totalIds; //184 - token.documentIds = restIds; //101 -184 - token.metadataPrefix = numWrapper.metadataPrefix; - - // $tokenWorker->storeResumptionToken($token); - const res: string = await this.tokenWorker.set(token); - - // set parameters for the resumptionToken-node - // const res = token.ResumptionId; - this.setParamResumption(res, numWrapper.cursor, numWrapper.totalIds); - } + await this.setResumptionToken(nextIds, paginationParams, browserFingerprint); } - private async handleResumptionToken(oaiRequest: Dictionary, maxRecords: number, numWrapper: ListParameter) { - const resParam = oaiRequest['resumptionToken']; //e.g. "158886496600000" + private async handleNoResumptionToken(oaiRequest: Dictionary, paginationParams: PagingParameter, maxRecords: number) { + this.validateMetadataPrefix(oaiRequest, paginationParams); + const finder: ModelQueryBuilderContract = Dataset.query().whereIn( + 'server_state', + this.deliveringDocumentStates, + ); + this.applySetFilter(finder, oaiRequest); + this.applyDateFilters(finder, oaiRequest); + await this.fetchAndSetResults(finder, paginationParams, oaiRequest, maxRecords); + } + + private async fetchAndSetResults( + finder: ModelQueryBuilderContract, + paginationParams: PagingParameter, + oaiRequest: Dictionary, + maxRecords: number + ) { + const totalResult = await finder + .clone() + .count('* as total') + .first() + .then((res) => res?.$extras.total); + paginationParams.totalLength = Number(totalResult); + + const combinedRecords: Dataset[] = await finder.select('publish_id').orderBy('publish_id').offset(0).limit(maxRecords*2); + + paginationParams.activeWorkIds = combinedRecords.slice(0, 100).map((dat) => Number(dat.publish_id)); + paginationParams.nextDocIds = combinedRecords.slice(100).map((dat) => Number(dat.publish_id)); + + // No resumption token was used – set queryParams from the current oaiRequest + paginationParams.queryParams = { + ...oaiRequest, + deliveringStates: this.deliveringDocumentStates, + }; + + // paginationParams.totalLength = 230; + } + + private async handleResumptionToken(oaiRequest: Dictionary, maxRecords: number, paginationParams: PagingParameter) { + const resParam = oaiRequest['resumptionToken']; const token = await this.tokenWorker.get(resParam); if (!token) { throw new OaiModelException(StatusCodes.INTERNAL_SERVER_ERROR, 'cache is outdated.', OaiErrorCodes.BADRESUMPTIONTOKEN); } - numWrapper.cursor = token.startPosition - 1; //startet dann bei Index 10 - numWrapper.start = token.startPosition + maxRecords; - numWrapper.totalIds = token.totalIds; - numWrapper.reldocIds = token.documentIds; - numWrapper.metadataPrefix = token.metadataPrefix; + // this.setResumptionParameters(token, maxRecords, paginationParams); + paginationParams.cursor = token.startPosition - 1; + paginationParams.start = token.startPosition + maxRecords; + paginationParams.totalLength = token.totalIds; + paginationParams.activeWorkIds = token.documentIds; + paginationParams.metadataPrefix = token.metadataPrefix; + paginationParams.queryParams = token.queryParams; + this.xsltParameter['oai_metadataPrefix'] = token.metadataPrefix; - this.xsltParameter['oai_metadataPrefix'] = numWrapper.metadataPrefix; + const finder = this.buildDatasetQueryViaToken(token); + const nextRecords: Dataset[] = await this.fetchNextRecords(finder, token, maxRecords); + paginationParams.nextDocIds = nextRecords.map((dat) => Number(dat.publish_id)); } - private async handleNoResumptionToken(oaiRequest: Dictionary, numWrapper: ListParameter) { - // no resumptionToken is given - if ('metadataPrefix' in oaiRequest) { - numWrapper.metadataPrefix = oaiRequest['metadataPrefix']; - } else { + private async setResumptionToken(nextIds: number[], paginationParams: PagingParameter, browserFingerprint: string) { + const countRestIds = nextIds.length; + if (countRestIds > 0) { + // const token = this.createResumptionToken(paginationParams, nextIds); + const token = new ResumptionToken(); + token.startPosition = paginationParams.start; + token.totalIds = paginationParams.totalLength; + token.documentIds = nextIds; + token.metadataPrefix = paginationParams.metadataPrefix; + token.queryParams = paginationParams.queryParams; + const res: string = await this.tokenWorker.set(token, browserFingerprint); + this.setParamResumption(res, paginationParams.cursor, paginationParams.totalLength); + } + } + + private buildDatasetQueryViaToken(token: ResumptionToken) { + const finder = Dataset.query(); + const originalQuery = token.queryParams || {}; + const deliveringStates = originalQuery.deliveringStates || this.deliveringDocumentStates; + + finder.whereIn('server_state', deliveringStates); + this.applySetFilter(finder, originalQuery); + this.applyDateFilters(finder, originalQuery); + + return finder; + } + + private async fetchNextRecords(finder: ModelQueryBuilderContract, token: ResumptionToken, maxRecords: number) { + return finder + .select('publish_id') + .orderBy('publish_id') + .offset(token.startPosition - 1 + maxRecords) + .limit(100); + } + + private validateMetadataPrefix(oaiRequest: Dictionary, paginationParams: PagingParameter) { + if (!('metadataPrefix' in oaiRequest)) { throw new OaiModelException( StatusCodes.INTERNAL_SERVER_ERROR, 'The prefix of the metadata argument is unknown.', OaiErrorCodes.BADARGUMENT, ); } - this.xsltParameter['oai_metadataPrefix'] = numWrapper.metadataPrefix; + paginationParams.metadataPrefix = oaiRequest['metadataPrefix']; + this.xsltParameter['oai_metadataPrefix'] = paginationParams.metadataPrefix; + } - let finder: ModelQueryBuilderContract = Dataset.query(); - // add server state restrictions - finder.whereIn('server_state', this.deliveringDocumentStates); - if ('set' in oaiRequest) { - const set = oaiRequest['set'] as string; - const setArray = set.split(':'); + private applySetFilter(finder: ModelQueryBuilderContract, queryParams: any) { + if ('set' in queryParams) { + const [setType, setValue] = queryParams['set'].split(':'); - if (setArray[0] == 'data-type') { - if (setArray.length == 2 && setArray[1]) { - finder.where('type', setArray[1]); - } - } else if (setArray[0] == 'open_access') { - const openAccessLicences = ['CC-BY-4.0', 'CC-BY-SA-4.0']; - finder.andWhereHas('licenses', (query) => { - query.whereIn('name', openAccessLicences); - }); - } else if (setArray[0] == 'ddc') { - if (setArray.length == 2 && setArray[1] != '') { - finder.andWhereHas('collections', (query) => { - query.where('number', setArray[1]); + switch (setType) { + case 'data-type': + setValue && finder.where('type', setValue); + break; + case 'open_access': + finder.andWhereHas('licenses', (query) => { + query.whereIn('name', ['CC-BY-4.0', 'CC-BY-SA-4.0']); }); - } + break; + case 'ddc': + setValue && + finder.andWhereHas('collections', (query) => { + query.where('number', setValue); + }); + break; } } + } - // const timeZone = "Europe/Vienna"; // Canonical time zone name - // &from=2020-09-03&until2020-09-03 - // &from=2020-09-11&until=2021-05-11 - if ('from' in oaiRequest && 'until' in oaiRequest) { - const from = oaiRequest['from'] as string; - let fromDate = dayjs(from); //.tz(timeZone); - const until = oaiRequest['until'] as string; - let untilDate = dayjs(until); //.tz(timeZone); - if (!fromDate.isValid() || !untilDate.isValid()) { - throw new OaiModelException(StatusCodes.INTERNAL_SERVER_ERROR, 'Date Parameter is not valid.', OaiErrorCodes.BADARGUMENT); - } - fromDate = dayjs.tz(from, 'Europe/Vienna'); - untilDate = dayjs.tz(until, 'Europe/Vienna'); + private applyDateFilters(finder: ModelQueryBuilderContract, queryParams: any) { + const { from, until } = queryParams; - if (from.length != until.length) { - throw new OaiModelException( - StatusCodes.INTERNAL_SERVER_ERROR, - 'The request has different granularities for the from and until parameters.', - OaiErrorCodes.BADARGUMENT, - ); - } - fromDate.hour() == 0 && (fromDate = fromDate.startOf('day')); - untilDate.hour() == 0 && (untilDate = untilDate.endOf('day')); + if (from && until) { + this.handleFromUntilFilter(finder, from, until); + } else if (from) { + this.handleFromFilter(finder, from); + } else if (until) { + this.handleUntilFilter(finder, until); + } + } - finder.whereBetween('server_date_published', [fromDate.format('YYYY-MM-DD HH:mm:ss'), untilDate.format('YYYY-MM-DD HH:mm:ss')]); - } else if ('from' in oaiRequest && !('until' in oaiRequest)) { - const from = oaiRequest['from'] as string; - let fromDate = dayjs(from); - if (!fromDate.isValid()) { - throw new OaiModelException( - StatusCodes.INTERNAL_SERVER_ERROR, - 'From date parameter is not valid.', - OaiErrorCodes.BADARGUMENT, - ); - } - fromDate = dayjs.tz(from, 'Europe/Vienna'); - fromDate.hour() == 0 && (fromDate = fromDate.startOf('day')); + private handleFromUntilFilter(finder: ModelQueryBuilderContract, from: string, until: string) { + const fromDate = this.parseDateWithValidation(from, 'From'); + const untilDate = this.parseDateWithValidation(until, 'Until'); - const now = dayjs(); - if (fromDate.isAfter(now)) { - throw new OaiModelException( - StatusCodes.INTERNAL_SERVER_ERROR, - 'Given from date is greater than now. The given values results in an empty list.', - OaiErrorCodes.NORECORDSMATCH, - ); - } else { - finder.andWhere('server_date_published', '>=', fromDate.format('YYYY-MM-DD HH:mm:ss')); - } - } else if (!('from' in oaiRequest) && 'until' in oaiRequest) { - const until = oaiRequest['until'] as string; - let untilDate = dayjs(until); - if (!untilDate.isValid()) { - throw new OaiModelException( - StatusCodes.INTERNAL_SERVER_ERROR, - 'Until date parameter is not valid.', - OaiErrorCodes.BADARGUMENT, - ); - } - untilDate = dayjs.tz(until, 'Europe/Vienna'); - untilDate.hour() == 0 && (untilDate = untilDate.endOf('day')); - - const firstPublishedDataset: Dataset = (await Dataset.earliestPublicationDate()) as Dataset; - const earliestPublicationDate = dayjs(firstPublishedDataset.server_date_published.toISO()); //format("YYYY-MM-DDThh:mm:ss[Z]")); - if (earliestPublicationDate.isAfter(untilDate)) { - throw new OaiModelException( - StatusCodes.INTERNAL_SERVER_ERROR, - `earliestDatestamp is greater than given until date. - The given values results in an empty list.`, - OaiErrorCodes.NORECORDSMATCH, - ); - } else { - finder.andWhere('server_date_published', '<=', untilDate.format('YYYY-MM-DD HH:mm:ss')); - } + if (from.length !== until.length) { + throw new OaiModelException( + StatusCodes.INTERNAL_SERVER_ERROR, + 'The request has different granularities for the from and until parameters.', + OaiErrorCodes.BADARGUMENT, + ); } - let reldocIdsDocs = await finder.select('publish_id').orderBy('publish_id'); - numWrapper.reldocIds = reldocIdsDocs.map((dat) => dat.publish_id); - numWrapper.totalIds = numWrapper.reldocIds.length; //212 + finder.whereBetween('server_date_published', [fromDate.format('YYYY-MM-DD HH:mm:ss'), untilDate.format('YYYY-MM-DD HH:mm:ss')]); + } + + private handleFromFilter(finder: ModelQueryBuilderContract, from: string) { + const fromDate = this.parseDateWithValidation(from, 'From'); + const now = dayjs(); + + if (fromDate.isAfter(now)) { + throw new OaiModelException( + StatusCodes.INTERNAL_SERVER_ERROR, + 'Given from date is greater than now. The given values results in an empty list.', + OaiErrorCodes.NORECORDSMATCH, + ); + } + + finder.andWhere('server_date_published', '>=', fromDate.format('YYYY-MM-DD HH:mm:ss')); + } + + private handleUntilFilter(finder: ModelQueryBuilderContract, until: string) { + const untilDate = this.parseDateWithValidation(until, 'Until'); + + const earliestPublicationDate = dayjs(this.firstPublishedDataset?.server_date_published.toISO()); + + if (earliestPublicationDate.isAfter(untilDate)) { + throw new OaiModelException( + StatusCodes.INTERNAL_SERVER_ERROR, + 'earliestDatestamp is greater than given until date. The given values results in an empty list.', + OaiErrorCodes.NORECORDSMATCH, + ); + } + + finder.andWhere('server_date_published', '<=', untilDate.format('YYYY-MM-DD HH:mm:ss')); + } + + private parseDateWithValidation(dateStr: string, label: string) { + let date = dayjs(dateStr); + if (!date.isValid()) { + throw new OaiModelException( + StatusCodes.INTERNAL_SERVER_ERROR, + `${label} date parameter is not valid.`, + OaiErrorCodes.BADARGUMENT, + ); + } + date = dayjs.tz(dateStr, 'Europe/Vienna'); + return date.hour() === 0 ? (label === 'From' ? date.startOf('day') : date.endOf('day')) : date; } private setParamResumption(res: string, cursor: number, totalIds: number) { @@ -641,4 +698,30 @@ export default class OaiController { this.xsltParameter['oai_error_code'] = 'badVerb'; this.xsltParameter['oai_error_message'] = 'The verb provided in the request is illegal.'; } + + /** + * Helper method to build a browser fingerprint by combining: + * - User-Agent header, + * - the IP address, + * - Accept-Language header, + * - current timestamp rounded to the hour. + * + * Every new hour, this will return a different fingerprint. + */ + private getBrowserFingerprint(request: Request): string { + const userAgent = request.header('user-agent') || 'unknown'; + // Check for X-Forwarded-For header to use the client IP from the proxy if available. + const xForwardedFor = request.header('x-forwarded-for'); + let ip = request.ip(); + // console.log(ip); + if (xForwardedFor) { + // X-Forwarded-For may contain a comma-separated list of IPs; the first one is the client IP. + ip = xForwardedFor.split(',')[0].trim(); + // console.log('xforwardedfor ip' + ip); + } + const locale = request.header('accept-language') || 'default'; + // Round the current time to the start of the hour. + const timestampHour = dayjs().startOf('hour').format('YYYY-MM-DDTHH'); + return `${userAgent}-${ip}-${locale}-${timestampHour}`; + } } diff --git a/app/Controllers/Http/Submitter/DatasetController.ts b/app/Controllers/Http/Submitter/DatasetController.ts index 6c28008..d7409a8 100644 --- a/app/Controllers/Http/Submitter/DatasetController.ts +++ b/app/Controllers/Http/Submitter/DatasetController.ts @@ -8,6 +8,7 @@ import Description from '#models/description'; import Language from '#models/language'; import Coverage from '#models/coverage'; import Collection from '#models/collection'; +import CollectionRole from '#models/collection_role'; import dayjs from 'dayjs'; import Person from '#models/person'; import db from '@adonisjs/lucid/services/db'; @@ -501,7 +502,7 @@ export default class DatasetController { } // save collection - const collection: Collection | null = await Collection.query().where('id', 21).first(); + const collection: Collection | null = await Collection.query().where('id', 594).first(); collection && (await dataset.useTransaction(trx).related('collections').attach([collection.id])); // save coverage @@ -545,7 +546,7 @@ export default class DatasetController { overwrite: true, // overwrite in case of conflict disk: 'local', }); - + // save file metadata into db const newFile = new File(); newFile.pathName = `${datasetFolder}/${fileName}`; @@ -1183,16 +1184,16 @@ export default class DatasetController { const datasetFolder = `files/${params.id}`; // const folderExists = await drive.use('local').exists(datasetFolder); // if (folderExists) { - // const dirListing = drive.list(datasetFolder); - // const folderContents = await dirListing.toArray(); - // if (folderContents.length === 0) { - // await drive.delete(datasetFolder); - // } - await drive.use('local').deleteAll(datasetFolder); - // delete dataset wirh relation in db - await dataset.delete(); - session.flash({ message: 'You have deleted 1 dataset!' }); - return response.redirect().toRoute('dataset.list'); + // const dirListing = drive.list(datasetFolder); + // const folderContents = await dirListing.toArray(); + // if (folderContents.length === 0) { + // await drive.delete(datasetFolder); + // } + await drive.use('local').deleteAll(datasetFolder); + // delete dataset wirh relation in db + await dataset.delete(); + session.flash({ message: 'You have deleted 1 dataset!' }); + return response.redirect().toRoute('dataset.list'); // } else { // // session.flash({ // // warning: `You cannot delete this dataset! Invalid server_state: "${dataset.server_state}"!`, @@ -1209,7 +1210,7 @@ export default class DatasetController { throw error; } else if (error instanceof Exception) { // General exception handling - session.flash({ error: error.message}); + session.flash({ error: error.message }); return response.redirect().back(); } else { session.flash({ error: 'An error occurred while deleting the dataset.' }); @@ -1217,4 +1218,34 @@ export default class DatasetController { } } } + + public async categorize({ inertia, request, response }: HttpContext) { + const id = request.param('id'); + // Preload dataset and its "collections" relation + const dataset = await Dataset.query().where('id', id).preload('collections').firstOrFail(); + const validStates = ['inprogress', 'rejected_editor']; + if (!validStates.includes(dataset.server_state)) { + // session.flash('errors', 'Invalid server state!'); + return response + .flash( + 'warning', + `Invalid server state. Dataset with id ${id} cannot be edited. Datset has server state ${dataset.server_state}.`, + ) + .redirect() + .toRoute('dataset.list'); + } + + const collectionRoles = await CollectionRole.query() + .preload('collections', (coll: Collection) => { + // preloa only top level collection with noparent_id + coll.whereNull('parent_id').orderBy('number', 'asc'); + }) + .exec(); + + return inertia.render('Submitter/Dataset/Category', { + collectionRoles: collectionRoles, + dataset: dataset, + relatedCollections: dataset.collections, + }); + } } diff --git a/app/Library/Oai/ResumptionToken.ts b/app/Library/Oai/ResumptionToken.ts index 5eca661..56bbea4 100644 --- a/app/Library/Oai/ResumptionToken.ts +++ b/app/Library/Oai/ResumptionToken.ts @@ -4,6 +4,7 @@ export default class ResumptionToken { private _resumptionId = ''; private _startPosition = 0; private _totalIds = 0; + private _queryParams: Record = {}; get key(): string { return this.metadataPrefix + this.startPosition + this.totalIds; @@ -48,4 +49,12 @@ export default class ResumptionToken { set totalIds(totalIds: number) { this._totalIds = totalIds; } + + get queryParams(): Record { + return this._queryParams; + } + + set queryParams(params: Record) { + this._queryParams = params; + } } diff --git a/app/Library/Oai/TokenWorkerContract.ts b/app/Library/Oai/TokenWorkerContract.ts index 94dae70..2491817 100644 --- a/app/Library/Oai/TokenWorkerContract.ts +++ b/app/Library/Oai/TokenWorkerContract.ts @@ -6,6 +6,6 @@ export default abstract class TokenWorkerContract { abstract connect(): void; abstract close(): void; abstract get(key: string): Promise; - abstract set(token: ResumptionToken): Promise; + abstract set(token: ResumptionToken, browserFingerprint: string): Promise; } diff --git a/app/Library/Oai/TokenWorkerSerice.ts b/app/Library/Oai/TokenWorkerSerice.ts index ee63f97..9cb9ccc 100644 --- a/app/Library/Oai/TokenWorkerSerice.ts +++ b/app/Library/Oai/TokenWorkerSerice.ts @@ -40,14 +40,64 @@ export default class TokenWorkerService implements TokenWorkerContract { return result !== undefined && result !== null; } - public async set(token: ResumptionToken): Promise { - const uniqueName = await this.generateUniqueName(); + /** + * Simplified set method that stores the token using a browser fingerprint key. + * If the token for that fingerprint already exists and its documentIds match the new token, + * then the fingerprint key is simply returned. + */ + public async set(token: ResumptionToken, browserFingerprint: string): Promise { + // Generate a 15-digit unique number string based on the fingerprint + const uniqueNumberKey = this.createUniqueNumberFromFingerprint(browserFingerprint, token.documentIds, token.totalIds); + // Optionally, you could prefix it if desired, e.g. 'rs_' + uniqueNumberKey + const fingerprintKey = uniqueNumberKey; + + // const fingerprintKey = `rs_fp_${browserFingerprint}`; + const existingTokenString = await this.cache.get(fingerprintKey); + + if (existingTokenString) { + const existingToken = this.parseToken(existingTokenString); + if (this.arraysAreEqual(existingToken.documentIds, token.documentIds)) { + return fingerprintKey; + } + } const serialToken = JSON.stringify(token); - await this.cache.setEx(uniqueName, this.ttl, serialToken); - return uniqueName; + await this.cache.setEx(fingerprintKey, this.ttl, serialToken); + return fingerprintKey; } + // Updated helper method to generate a unique key based on fingerprint and documentIds + private createUniqueNumberFromFingerprint(browserFingerprint: string, documentIds: number[], totalIds: number): string { + // Combine the fingerprint, document IDs and totalIds to produce the input string + const combined = browserFingerprint + ':' + documentIds.join('-') + ':' + totalIds; + // Simple hash algorithm + let hash = 0; + for (let i = 0; i < combined.length; i++) { + hash = (hash << 5) - hash + combined.charCodeAt(i); + hash |= 0; // Convert to 32-bit integer + } + // Ensure positive number and limit it to at most 15 digits + const positiveHash = Math.abs(hash) % 1000000000000000; + // Pad with trailing zeros to ensure a 15-digit string + return positiveHash.toString().padEnd(15, '0'); + } + + // Add a helper function to compare two arrays of numbers with identical order + private arraysAreEqual(arr1: number[], arr2: number[]): boolean { + if (arr1.length !== arr2.length) { + return false; + } + return arr1.every((num, index) => num === arr2[index]); + } + + // public async set(token: ResumptionToken): Promise { + // const uniqueName = await this.generateUniqueName(); + + // const serialToken = JSON.stringify(token); + // await this.cache.setEx(uniqueName, this.ttl, serialToken); + // return uniqueName; + // } + private async generateUniqueName(): Promise { let fc = 0; const uniqueId = dayjs().unix().toString(); diff --git a/app/models/dataset.ts b/app/models/dataset.ts index 73ca3a0..1233a28 100644 --- a/app/models/dataset.ts +++ b/app/models/dataset.ts @@ -209,6 +209,15 @@ export default class Dataset extends DatasetExtension { return mainTitle ? mainTitle.value : null; } + @computed({ + serializeAs: 'doi_identifier', + }) + public get doiIdentifier() { + // return `${this.firstName} ${this.lastName}`; + const identifier: DatasetIdentifier = this.identifier; + return identifier ? identifier.value : null; + } + @manyToMany(() => Person, { pivotForeignKey: 'document_id', pivotRelatedForeignKey: 'person_id', diff --git a/app/models/person.ts b/app/models/person.ts index 3feff8a..4e6b60b 100644 --- a/app/models/person.ts +++ b/app/models/person.ts @@ -51,7 +51,7 @@ export default class Person extends BaseModel { serializeAs: 'name', }) public get fullName() { - return `${this.firstName} ${this.lastName}`; + return [this.firstName, this.lastName].filter(Boolean).join(' '); } // @computed() @@ -64,10 +64,13 @@ export default class Person extends BaseModel { // return '2023-03-21 08:45:00'; // } - @computed() + + @computed({ + serializeAs: 'dataset_count', + }) public get datasetCount() { const stock = this.$extras.datasets_count; //my pivot column name was "stock" - return stock; + return Number(stock); } @computed() diff --git a/commands/validate_checksum.ts b/commands/validate_checksum.ts index a2d8096..a11b38b 100644 --- a/commands/validate_checksum.ts +++ b/commands/validate_checksum.ts @@ -88,7 +88,7 @@ export default class ValidateChecksum extends BaseCommand { ); // Construct the file path - const filePath = '/storage/app/public/' + file.pathName; + const filePath = '/storage/app/data/' + file.pathName; try { // Calculate the MD5 checksum of the file diff --git a/config/app.ts b/config/app.ts index 23ad925..c0e7b2b 100644 --- a/config/app.ts +++ b/config/app.ts @@ -80,7 +80,8 @@ export const http = defineConfig({ | headers. | */ - trustProxy: proxyAddr.compile('loopback'), + // trustProxy: proxyAddr.compile('loopback'), + trustProxy: proxyAddr.compile(['127.0.0.1', '::1/128']), /* |-------------------------------------------------------------------------- diff --git a/database/migrations/acl_4_accounts.ts b/database/migrations/acl_4_accounts.ts index ba2373f..1237635 100644 --- a/database/migrations/acl_4_accounts.ts +++ b/database/migrations/acl_4_accounts.ts @@ -18,6 +18,7 @@ export default class Accounts extends BaseSchema { table.text("two_factor_recovery_codes").nullable(); table.smallint('state').nullable(); table.bigint('last_counter').nullable(); + table.string('avatar').nullable(); }); } @@ -43,6 +44,7 @@ export default class Accounts extends BaseSchema { // two_factor_recovery_codes text COLLATE pg_catalog."default", // state smallint, // last_counter bigint, +// avatar character varying(255), // ) // ALTER TABLE gba.accounts @@ -85,3 +87,6 @@ export default class Accounts extends BaseSchema { // GRANT ALL ON SEQUENCE gba.totp_secrets_id_seq TO tethys_admin; // ALTER TABLE gba.totp_secrets ALTER COLUMN id SET DEFAULT nextval('gba.totp_secrets_id_seq'); + + +// ALTER TABLE "accounts" ADD COLUMN "avatar" VARCHAR(255) NULL diff --git a/database/migrations/dataset_7_collections.ts b/database/migrations/dataset_7_collections.ts index 7e2b3a9..4fe4b8b 100644 --- a/database/migrations/dataset_7_collections.ts +++ b/database/migrations/dataset_7_collections.ts @@ -54,3 +54,8 @@ export default class Collections extends BaseSchema { // ON UPDATE CASCADE // ON DELETE CASCADE // ) + + +// change to normal intzeger: +// ALTER TABLE collections ALTER COLUMN id DROP DEFAULT; +// DROP SEQUENCE IF EXISTS collections_id_seq; diff --git a/package-lock.json b/package-lock.json index 7baeb0a..528ae02 100644 --- a/package-lock.json +++ b/package-lock.json @@ -180,9 +180,9 @@ } }, "node_modules/@adonisjs/auth": { - "version": "9.3.1", - "resolved": "https://registry.npmjs.org/@adonisjs/auth/-/auth-9.3.1.tgz", - "integrity": "sha512-FQ1ylsH/PRjqMsnjtplKgcWM7CEfmCGuIRbj0lFzTb9xajRTjjgBLWNIhUW/NocmkZP2T2rjV0TnDT1tQUe44Q==", + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/@adonisjs/auth/-/auth-9.3.2.tgz", + "integrity": "sha512-ZaOeSEsCMZkXlfS3YYp2fXh6Eri/uq87mHmhW/uN8/Iww7VlpJbaacGokwHk1gBvU3hScYNVYwiazn3jkcNygw==", "license": "MIT", "dependencies": { "@adonisjs/presets": "^2.6.4", @@ -483,16 +483,16 @@ } }, "node_modules/@adonisjs/http-server": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@adonisjs/http-server/-/http-server-7.4.0.tgz", - "integrity": "sha512-2Me8ytUu0Sm0jYJs2SAiYQX3ECF6clOJwPE04cswsAwEnqSFLZkflD3c6rApjLjZO6P1wFlo090HNaZCFrlcMQ==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@adonisjs/http-server/-/http-server-7.5.0.tgz", + "integrity": "sha512-krO0c1doG20sKUWOtreHbT9mDSmNah4aIG+xp3fRKwpoCxDRao1wQCN4qtpsUJVVJH/qzx4p1cC6kQiJsJrCfg==", "license": "MIT", "dependencies": { "@paralleldrive/cuid2": "^2.2.2", - "@poppinss/macroable": "^1.0.3", + "@poppinss/macroable": "^1.0.4", "@poppinss/matchit": "^3.1.2", - "@poppinss/middleware": "^3.2.4", - "@poppinss/utils": "^6.8.3", + "@poppinss/middleware": "^3.2.5", + "@poppinss/utils": "^6.9.2", "@sindresorhus/is": "^7.0.1", "accepts": "^1.3.8", "content-disposition": "^0.5.4", @@ -504,7 +504,7 @@ "mime-types": "^2.1.35", "on-finished": "^2.4.1", "proxy-addr": "^2.0.7", - "qs": "^6.13.1", + "qs": "^6.14.0", "tmp-cache": "^1.1.0", "type-is": "^1.6.18", "vary": "^1.1.2", @@ -1331,9 +1331,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz", - "integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", + "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==", "cpu": [ "ppc64" ], @@ -1347,9 +1347,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz", - "integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz", + "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==", "cpu": [ "arm" ], @@ -1363,9 +1363,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz", - "integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz", + "integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==", "cpu": [ "arm64" ], @@ -1379,9 +1379,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz", - "integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz", + "integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==", "cpu": [ "x64" ], @@ -1395,9 +1395,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz", - "integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz", + "integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==", "cpu": [ "arm64" ], @@ -1411,9 +1411,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz", - "integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz", + "integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==", "cpu": [ "x64" ], @@ -1427,9 +1427,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz", - "integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz", + "integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==", "cpu": [ "arm64" ], @@ -1443,9 +1443,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz", - "integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz", + "integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==", "cpu": [ "x64" ], @@ -1459,9 +1459,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz", - "integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz", + "integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==", "cpu": [ "arm" ], @@ -1475,9 +1475,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz", - "integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz", + "integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==", "cpu": [ "arm64" ], @@ -1491,9 +1491,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz", - "integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz", + "integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==", "cpu": [ "ia32" ], @@ -1507,9 +1507,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz", - "integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz", + "integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==", "cpu": [ "loong64" ], @@ -1523,9 +1523,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz", - "integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz", + "integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==", "cpu": [ "mips64el" ], @@ -1539,9 +1539,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz", - "integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz", + "integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==", "cpu": [ "ppc64" ], @@ -1555,9 +1555,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz", - "integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz", + "integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==", "cpu": [ "riscv64" ], @@ -1571,9 +1571,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz", - "integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz", + "integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==", "cpu": [ "s390x" ], @@ -1587,9 +1587,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz", - "integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz", + "integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==", "cpu": [ "x64" ], @@ -1603,9 +1603,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz", - "integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz", + "integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==", "cpu": [ "arm64" ], @@ -1619,9 +1619,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz", - "integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz", + "integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==", "cpu": [ "x64" ], @@ -1635,9 +1635,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz", - "integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz", + "integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==", "cpu": [ "arm64" ], @@ -1651,9 +1651,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz", - "integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz", + "integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==", "cpu": [ "x64" ], @@ -1667,9 +1667,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz", - "integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz", + "integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==", "cpu": [ "x64" ], @@ -1683,9 +1683,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz", - "integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz", + "integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==", "cpu": [ "arm64" ], @@ -1699,9 +1699,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz", - "integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz", + "integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==", "cpu": [ "ia32" ], @@ -1715,9 +1715,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz", - "integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz", + "integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==", "cpu": [ "x64" ], @@ -1823,9 +1823,9 @@ } }, "node_modules/@faker-js/faker": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-9.5.0.tgz", - "integrity": "sha512-3qbjLv+fzuuCg3umxc9/7YjrEXNaKwHgmig949nfyaTx8eL4FAsvFbu+1JcFUj1YAXofhaDn6JdEUBTYuk0Ssw==", + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-9.5.1.tgz", + "integrity": "sha512-0fzMEDxkExR2cn731kpDaCCnBGBUOIXEi2S1N5l8Hltp6aPf4soTMJ+g4k8r2sI5oB+rpwIW8Uy/6jkwGpnWPg==", "funding": [ { "type": "opencollective", @@ -1839,16 +1839,22 @@ } }, "node_modules/@fontsource/archivo-black": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@fontsource/archivo-black/-/archivo-black-5.1.1.tgz", - "integrity": "sha512-3hmXvgYkXDsQ7msb+YrwU+6B5j4q7LBfVev1G4T8I5yRcRTo6H8OEA8tf0vULvcAGfOOhl38aRK/2I2+RLE/rQ==", - "license": "OFL-1.1" + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/@fontsource/archivo-black/-/archivo-black-5.2.5.tgz", + "integrity": "sha512-tdBRFgA0CgxVqj3mBM96aiXRBoOp51X3IW2e8/t59AVr0NwiBcB+c3C+p5dd7Np/UT/vqdmjb/gK/HaFpulhIA==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } }, "node_modules/@fontsource/inter": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@fontsource/inter/-/inter-5.1.1.tgz", - "integrity": "sha512-weN3E+rq0Xb3Z93VHJ+Rc7WOQX9ETJPTAJ+gDcaMHtjft67L58sfS65rAjC5tZUXQ2FdZ/V1/sSzCwZ6v05kJw==", - "license": "OFL-1.1" + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/@fontsource/inter/-/inter-5.2.5.tgz", + "integrity": "sha512-kbsPKj0S4p44JdYRFiW78Td8Ge2sBVxi/PIBwmih+RpSXUdvS9nbs1HIiuUSPtRMi14CqLEZ/fbk7dj7vni1Sg==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } }, "node_modules/@headlessui/vue": { "version": "1.7.23", @@ -1918,9 +1924,9 @@ } }, "node_modules/@inertiajs/core": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@inertiajs/core/-/core-2.0.3.tgz", - "integrity": "sha512-JvXzqc2XAt3WgEDMyxCyXO6bDLMCsBjFsYREU1/+3wtNTib7QKwK71+aF+MrhILpz+kRTi29TsLqnbkPHBAZjw==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@inertiajs/core/-/core-2.0.4.tgz", + "integrity": "sha512-gCUqpwBRYOhz0hwBDWca2lkk+Mc+36GvbRoE0rEvYFpzQAMMP0xFhH9h8hr7VWTn+vVOZRuDvakI+4cazwtvCg==", "license": "MIT", "dependencies": { "axios": "^1.6.0", @@ -1949,12 +1955,12 @@ } }, "node_modules/@inertiajs/vue3": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@inertiajs/vue3/-/vue3-2.0.3.tgz", - "integrity": "sha512-2ykoHN+yl6CECY7f/O3S95tZRl7JXTnLTKoPMrR64+a7j32LTHucmkfJi8TnVzE46mqLj6gqH60DVVk1lHu1Yg==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@inertiajs/vue3/-/vue3-2.0.4.tgz", + "integrity": "sha512-QbVpWHhIEEjbhPoJmUBvt1SxreZmiPgq2gr1bnvhrGcwtGzDoVTN+1sTO5PBX0SebVQhabHHlJS8SzwoxGLgKw==", "license": "MIT", "dependencies": { - "@inertiajs/core": "2.0.3", + "@inertiajs/core": "2.0.4", "lodash.clonedeep": "^4.5.0", "lodash.isequal": "^4.5.0" }, @@ -2487,9 +2493,9 @@ } }, "node_modules/@opensearch-project/opensearch": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@opensearch-project/opensearch/-/opensearch-3.3.0.tgz", - "integrity": "sha512-EJSeyVEy4fMLsbIoCYisz2VFRRXBfkxKSEBVSFEzXMT71pAF26+XhQiv+nhzqAu5w18qS1bhAAIMUriT5bThNQ==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@opensearch-project/opensearch/-/opensearch-3.4.0.tgz", + "integrity": "sha512-NdVWpBxa2mA8fQvn3jWgoDnIHk/xNtxrPA8joQVBP75CJMEr/379wzDpnIq+HZn3E0zyE0VQOxDV+LbbyGkefA==", "license": "Apache-2.0", "dependencies": { "aws4": "^1.11.0", @@ -2829,9 +2835,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.8.tgz", - "integrity": "sha512-q217OSE8DTp8AFHuNHXo0Y86e1wtlfVrXiAlwkIvGRQv9zbc6mE3sjIVfwI8sYUyNxwOg0j/Vm1RKM04JcWLJw==", + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.9.tgz", + "integrity": "sha512-qZdlImWXur0CFakn2BJ2znJOdqYZKiedEPEVNTBrpfPjc/YuTGcaYZcdmNFTkUj3DU0ZM/AElcM8Ybww3xVLzA==", "cpu": [ "arm" ], @@ -2842,9 +2848,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.8.tgz", - "integrity": "sha512-Gigjz7mNWaOL9wCggvoK3jEIUUbGul656opstjaUSGC3eT0BM7PofdAJaBfPFWWkXNVAXbaQtC99OCg4sJv70Q==", + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.9.tgz", + "integrity": "sha512-4KW7P53h6HtJf5Y608T1ISKvNIYLWRKMvfnG0c44M6In4DQVU58HZFEVhWINDZKp7FZps98G3gxwC1sb0wXUUg==", "cpu": [ "arm64" ], @@ -2855,9 +2861,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.8.tgz", - "integrity": "sha512-02rVdZ5tgdUNRxIUrFdcMBZQoaPMrxtwSb+/hOfBdqkatYHR3lZ2A2EGyHq2sGOd0Owk80oV3snlDASC24He3Q==", + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.9.tgz", + "integrity": "sha512-0CY3/K54slrzLDjOA7TOjN1NuLKERBgk9nY5V34mhmuu673YNb+7ghaDUs6N0ujXR7fz5XaS5Aa6d2TNxZd0OQ==", "cpu": [ "arm64" ], @@ -2868,9 +2874,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.8.tgz", - "integrity": "sha512-qIP/elwR/tq/dYRx3lgwK31jkZvMiD6qUtOycLhTzCvrjbZ3LjQnEM9rNhSGpbLXVJYQ3rq39A6Re0h9tU2ynw==", + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.9.tgz", + "integrity": "sha512-eOojSEAi/acnsJVYRxnMkPFqcxSMFfrw7r2iD9Q32SGkb/Q9FpUY1UlAu1DH9T7j++gZ0lHjnm4OyH2vCI7l7Q==", "cpu": [ "x64" ], @@ -2881,9 +2887,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.8.tgz", - "integrity": "sha512-IQNVXL9iY6NniYbTaOKdrlVP3XIqazBgJOVkddzJlqnCpRi/yAeSOa8PLcECFSQochzqApIOE1GHNu3pCz+BDA==", + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.9.tgz", + "integrity": "sha512-2lzjQPJbN5UnHm7bHIUKFMulGTQwdvOkouJDpPysJS+QFBGDJqcfh+CxxtG23Ik/9tEvnebQiylYoazFMAgrYw==", "cpu": [ "arm64" ], @@ -2894,9 +2900,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.8.tgz", - "integrity": "sha512-TYXcHghgnCqYFiE3FT5QwXtOZqDj5GmaFNTNt3jNC+vh22dc/ukG2cG+pi75QO4kACohZzidsq7yKTKwq/Jq7Q==", + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.9.tgz", + "integrity": "sha512-SLl0hi2Ah2H7xQYd6Qaiu01kFPzQ+hqvdYSoOtHYg/zCIFs6t8sV95kaoqjzjFwuYQLtOI0RZre/Ke0nPaQV+g==", "cpu": [ "x64" ], @@ -2907,9 +2913,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.8.tgz", - "integrity": "sha512-A4iphFGNkWRd+5m3VIGuqHnG3MVnqKe7Al57u9mwgbyZ2/xF9Jio72MaY7xxh+Y87VAHmGQr73qoKL9HPbXj1g==", + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.9.tgz", + "integrity": "sha512-88I+D3TeKItrw+Y/2ud4Tw0+3CxQ2kLgu3QvrogZ0OfkmX/DEppehus7L3TS2Q4lpB+hYyxhkQiYPJ6Mf5/dPg==", "cpu": [ "arm" ], @@ -2920,9 +2926,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.8.tgz", - "integrity": "sha512-S0lqKLfTm5u+QTxlFiAnb2J/2dgQqRy/XvziPtDd1rKZFXHTyYLoVL58M/XFwDI01AQCDIevGLbQrMAtdyanpA==", + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.9.tgz", + "integrity": "sha512-3qyfWljSFHi9zH0KgtEPG4cBXHDFhwD8kwg6xLfHQ0IWuH9crp005GfoUUh/6w9/FWGBwEHg3lxK1iHRN1MFlA==", "cpu": [ "arm" ], @@ -2933,9 +2939,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.8.tgz", - "integrity": "sha512-jpz9YOuPiSkL4G4pqKrus0pn9aYwpImGkosRKwNi+sJSkz+WU3anZe6hi73StLOQdfXYXC7hUfsQlTnjMd3s1A==", + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.9.tgz", + "integrity": "sha512-6TZjPHjKZUQKmVKMUowF3ewHxctrRR09eYyvT5eFv8w/fXarEra83A2mHTVJLA5xU91aCNOUnM+DWFMSbQ0Nxw==", "cpu": [ "arm64" ], @@ -2946,9 +2952,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.8.tgz", - "integrity": "sha512-KdSfaROOUJXgTVxJNAZ3KwkRc5nggDk+06P6lgi1HLv1hskgvxHUKZ4xtwHkVYJ1Rep4GNo+uEfycCRRxht7+Q==", + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.9.tgz", + "integrity": "sha512-LD2fytxZJZ6xzOKnMbIpgzFOuIKlxVOpiMAXawsAZ2mHBPEYOnLRK5TTEsID6z4eM23DuO88X0Tq1mErHMVq0A==", "cpu": [ "arm64" ], @@ -2959,9 +2965,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.8.tgz", - "integrity": "sha512-NyF4gcxwkMFRjgXBM6g2lkT58OWztZvw5KkV2K0qqSnUEqCVcqdh2jN4gQrTn/YUpAcNKyFHfoOZEer9nwo6uQ==", + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.9.tgz", + "integrity": "sha512-dRAgTfDsn0TE0HI6cmo13hemKpVHOEyeciGtvlBTkpx/F65kTvShtY/EVyZEIfxFkV5JJTuQ9tP5HGBS0hfxIg==", "cpu": [ "loong64" ], @@ -2972,9 +2978,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.8.tgz", - "integrity": "sha512-LMJc999GkhGvktHU85zNTDImZVUCJ1z/MbAJTnviiWmmjyckP5aQsHtcujMjpNdMZPT2rQEDBlJfubhs3jsMfw==", + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.9.tgz", + "integrity": "sha512-PHcNOAEhkoMSQtMf+rJofwisZqaU8iQ8EaSps58f5HYll9EAY5BSErCZ8qBDMVbq88h4UxaNPlbrKqfWP8RfJA==", "cpu": [ "ppc64" ], @@ -2985,9 +2991,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.8.tgz", - "integrity": "sha512-xAQCAHPj8nJq1PI3z8CIZzXuXCstquz7cIOL73HHdXiRcKk8Ywwqtx2wrIy23EcTn4aZ2fLJNBB8d0tQENPCmw==", + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.9.tgz", + "integrity": "sha512-Z2i0Uy5G96KBYKjeQFKbbsB54xFOL5/y1P5wNBsbXB8yE+At3oh0DVMjQVzCJRJSfReiB2tX8T6HUFZ2k8iaKg==", "cpu": [ "riscv64" ], @@ -2998,9 +3004,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.8.tgz", - "integrity": "sha512-DdePVk1NDEuc3fOe3dPPTb+rjMtuFw89gw6gVWxQFAuEqqSdDKnrwzZHrUYdac7A7dXl9Q2Vflxpme15gUWQFA==", + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.9.tgz", + "integrity": "sha512-U+5SwTMoeYXoDzJX5dhDTxRltSrIax8KWwfaaYcynuJw8mT33W7oOgz0a+AaXtGuvhzTr2tVKh5UO8GVANTxyQ==", "cpu": [ "s390x" ], @@ -3011,9 +3017,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.8.tgz", - "integrity": "sha512-8y7ED8gjxITUltTUEJLQdgpbPh1sUQ0kMTmufRF/Ns5tI9TNMNlhWtmPKKHCU0SilX+3MJkZ0zERYYGIVBYHIA==", + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.9.tgz", + "integrity": "sha512-FwBHNSOjUTQLP4MG7y6rR6qbGw4MFeQnIBrMe161QGaQoBQLqSUEKlHIiVgF3g/mb3lxlxzJOpIBhaP+C+KP2A==", "cpu": [ "x64" ], @@ -3024,9 +3030,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.8.tgz", - "integrity": "sha512-SCXcP0ZpGFIe7Ge+McxY5zKxiEI5ra+GT3QRxL0pMMtxPfpyLAKleZODi1zdRHkz5/BhueUrYtYVgubqe9JBNQ==", + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.9.tgz", + "integrity": "sha512-cYRpV4650z2I3/s6+5/LONkjIz8MBeqrk+vPXV10ORBnshpn8S32bPqQ2Utv39jCiDcO2eJTuSlPXpnvmaIgRA==", "cpu": [ "x64" ], @@ -3037,9 +3043,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.8.tgz", - "integrity": "sha512-YHYsgzZgFJzTRbth4h7Or0m5O74Yda+hLin0irAIobkLQFRQd1qWmnoVfwmKm9TXIZVAD0nZ+GEb2ICicLyCnQ==", + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.9.tgz", + "integrity": "sha512-z4mQK9dAN6byRA/vsSgQiPeuO63wdiDxZ9yg9iyX2QTzKuQM7T4xlBoeUP/J8uiFkqxkcWndWi+W7bXdPbt27Q==", "cpu": [ "arm64" ], @@ -3050,9 +3056,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.8.tgz", - "integrity": "sha512-r3NRQrXkHr4uWy5TOjTpTYojR9XmF0j/RYgKCef+Ag46FWUTltm5ziticv8LdNsDMehjJ543x/+TJAek/xBA2w==", + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.9.tgz", + "integrity": "sha512-KB48mPtaoHy1AwDNkAJfHXvHp24H0ryZog28spEs0V48l3H1fr4i37tiyHsgKZJnCmvxsbATdZGBpbmxTE3a9w==", "cpu": [ "ia32" ], @@ -3063,9 +3069,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.8.tgz", - "integrity": "sha512-U0FaE5O1BCpZSeE6gBl3c5ObhePQSfk9vDRToMmTkbhCOgW4jqvtS5LGyQ76L1fH8sM0keRp4uDTsbjiUyjk0g==", + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.9.tgz", + "integrity": "sha512-AyleYRPU7+rgkMWbEh71fQlrzRfeP6SyMnRf9XX4fCdDPAJumdSBqYEcWPMzVQ4ScAl7E4oFfK0GUVn77xSwbw==", "cpu": [ "x64" ], @@ -3121,9 +3127,9 @@ "license": "CC0-1.0" }, "node_modules/@swc/wasm": { - "version": "1.10.18", - "resolved": "https://registry.npmjs.org/@swc/wasm/-/wasm-1.10.18.tgz", - "integrity": "sha512-TgoMYjQ2/9UfUaw7WuKj7Svew6kaNOqkjV4nKoc2tf34e+7GxL2KPoXvM2b1RkPxNocv85glcQpS9KMk8FqpBA==", + "version": "1.11.7", + "resolved": "https://registry.npmjs.org/@swc/wasm/-/wasm-1.11.7.tgz", + "integrity": "sha512-OS11ClTQFldQJRX1RRsT3B3MQVplAdMthXH6ha7WCNAsoP3Rtzq/MUJ7LpkBIUV9Ty8Xk5lPzfeRNscFQ6sFEw==", "dev": true, "license": "Apache-2.0" }, @@ -3153,9 +3159,9 @@ } }, "node_modules/@tanstack/virtual-core": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.0.tgz", - "integrity": "sha512-NBKJP3OIdmZY3COJdWkSonr50FMVIi+aj5ZJ7hI/DTpEKg2RMfo/KvP8A3B/zOSpMgIe52B5E2yn7rryULzA6g==", + "version": "3.13.2", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.2.tgz", + "integrity": "sha512-Qzz4EgzMbO5gKrmqUondCjiHcuu4B1ftHb0pjCut661lXZdGoHeze9f/M8iwsK1t5LGR6aNuNGU7mxkowaW6RQ==", "dev": true, "license": "MIT", "funding": { @@ -3164,13 +3170,13 @@ } }, "node_modules/@tanstack/vue-virtual": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@tanstack/vue-virtual/-/vue-virtual-3.13.0.tgz", - "integrity": "sha512-EPgcTc41KGJAK2N2Ux2PeUnG3cPpdkldTib05nwq+0zdS2Ihpbq8BsWXz/eXPyNc5noDBh1GBgAe36yMYiW6WA==", + "version": "3.13.2", + "resolved": "https://registry.npmjs.org/@tanstack/vue-virtual/-/vue-virtual-3.13.2.tgz", + "integrity": "sha512-z4swzjdhzCh95n9dw9lTvw+t3iwSkYRlVkYkra3C9mul/m5fTzHR7KmtkwH4qXMTXGJUbngtC/bz2cHQIHkO8g==", "dev": true, "license": "MIT", "dependencies": { - "@tanstack/virtual-core": "3.13.0" + "@tanstack/virtual-core": "3.13.2" }, "funding": { "type": "github", @@ -3181,17 +3187,17 @@ } }, "node_modules/@tokenizer/inflate": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.2.6.tgz", - "integrity": "sha512-SdR/i05U7Xhnsq36iyIq/ZiGGw4PKzw4ww3bOq80Pjj4wyXpqyTcgrgdDdGlcatnlvzNJx8CQw3hp6QZvkUwhA==", + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.2.7.tgz", + "integrity": "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==", "license": "MIT", "dependencies": { - "debug": "^4.3.7", + "debug": "^4.4.0", "fflate": "^0.8.2", "token-types": "^6.0.0" }, "engines": { - "node": ">=16" + "node": ">=18" }, "funding": { "type": "github", @@ -3328,9 +3334,9 @@ "license": "MIT" }, "node_modules/@types/chai": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.0.1.tgz", - "integrity": "sha512-5T8ajsg3M/FOncpLYW7sdOcD6yf4+722sze/tc4KQV0P8Z2rAr3SAuHCIkYmYpt8VbcQlnz8SxlOlPQYefe4cA==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.0.tgz", + "integrity": "sha512-FWnQYdrG9FAC8KgPVhDFfrPL1FBsL3NtIt2WsxKvwu/61K6HiuDF3xAb7c7w/k9ML2QOUHcwTgU7dKLFPK6sBg==", "dev": true, "license": "MIT", "dependencies": { @@ -3560,9 +3566,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.13.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.4.tgz", - "integrity": "sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg==", + "version": "22.13.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.9.tgz", + "integrity": "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw==", "license": "MIT", "dependencies": { "undici-types": "~6.20.0" @@ -3747,9 +3753,9 @@ "license": "MIT" }, "node_modules/@types/ws": { - "version": "8.5.14", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.14.tgz", - "integrity": "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8svvI3hMyvN0kKCJMvTJP/x6Y/EoQbepff882wL+Sn5QsXb3etnamgrJq4isrBxSJj5L2AuXcI0+bgkoAXGUJw==", "dev": true, "license": "MIT", "dependencies": { @@ -4068,9 +4074,9 @@ } }, "node_modules/@vavite/multibuild/node_modules/@types/node": { - "version": "18.19.76", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.76.tgz", - "integrity": "sha512-yvR7Q9LdPz2vGpmpJX5LolrgRdWvB67MJKDPSgIIzpFbaf9a1j/f5DnLp5VDyHGMR0QZHlTr1afsD87QCXFHKw==", + "version": "18.19.79", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.79.tgz", + "integrity": "sha512-90K8Oayimbctc5zTPHPfZloc/lGVs7f3phUAAMcTgEPtg8kKquGZDERC8K4vkBYkQQh48msiYUslYtxTWvqcAg==", "license": "MIT", "dependencies": { "undici-types": "~5.26.4" @@ -4449,9 +4455,9 @@ } }, "node_modules/acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -4805,9 +4811,9 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.7.9", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", - "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.1.tgz", + "integrity": "sha512-NN+fvwH/kV01dYUQ3PTOZns4LWtWhOFCAhQ/pHb88WQ1hNe5V/dvFwc4VJcDL11LT9xSX0QtsR8sWUuyOuOq7g==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", @@ -5134,13 +5140,13 @@ } }, "node_modules/call-bound": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", - "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "get-intrinsic": "^1.2.6" + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" }, "engines": { "node": ">= 0.4" @@ -5182,9 +5188,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001700", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001700.tgz", - "integrity": "sha512-2S6XIXwaE7K7erT8dY+kLQcpa5ms63XlRkMkReXjle+kf6c5g38vyMl+Z5y8dSxOFDhcFe+nxnn261PLxBSQsQ==", + "version": "1.0.30001702", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001702.tgz", + "integrity": "sha512-LoPe/D7zioC0REI5W73PeR1e1MLCipRGq/VkovJnd6Df+QVqT+vT33OXCp8QUd7kA7RZrHWxb1B36OQKI/0gOA==", "dev": true, "funding": [ { @@ -6347,9 +6353,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.102", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.102.tgz", - "integrity": "sha512-eHhqaja8tE/FNpIiBrvBjFV/SSKpyWHLvxuR9dPTdo+3V9ppdLmFB7ZZQ98qNovcngPLYIz0oOBF9P0FfZef5Q==", + "version": "1.5.112", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.112.tgz", + "integrity": "sha512-oen93kVyqSb3l+ziUgzIOlWt/oOuy4zRmpwestMn4rhFWAoFJeFuCVte9F2fASjeZZo7l/Cif9TiyrdW4CwEMA==", "dev": true, "license": "ISC" }, @@ -6522,9 +6528,9 @@ } }, "node_modules/esbuild": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", - "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", + "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==", "hasInstallScript": true, "license": "MIT", "bin": { @@ -6534,31 +6540,31 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.24.2", - "@esbuild/android-arm": "0.24.2", - "@esbuild/android-arm64": "0.24.2", - "@esbuild/android-x64": "0.24.2", - "@esbuild/darwin-arm64": "0.24.2", - "@esbuild/darwin-x64": "0.24.2", - "@esbuild/freebsd-arm64": "0.24.2", - "@esbuild/freebsd-x64": "0.24.2", - "@esbuild/linux-arm": "0.24.2", - "@esbuild/linux-arm64": "0.24.2", - "@esbuild/linux-ia32": "0.24.2", - "@esbuild/linux-loong64": "0.24.2", - "@esbuild/linux-mips64el": "0.24.2", - "@esbuild/linux-ppc64": "0.24.2", - "@esbuild/linux-riscv64": "0.24.2", - "@esbuild/linux-s390x": "0.24.2", - "@esbuild/linux-x64": "0.24.2", - "@esbuild/netbsd-arm64": "0.24.2", - "@esbuild/netbsd-x64": "0.24.2", - "@esbuild/openbsd-arm64": "0.24.2", - "@esbuild/openbsd-x64": "0.24.2", - "@esbuild/sunos-x64": "0.24.2", - "@esbuild/win32-arm64": "0.24.2", - "@esbuild/win32-ia32": "0.24.2", - "@esbuild/win32-x64": "0.24.2" + "@esbuild/aix-ppc64": "0.25.0", + "@esbuild/android-arm": "0.25.0", + "@esbuild/android-arm64": "0.25.0", + "@esbuild/android-x64": "0.25.0", + "@esbuild/darwin-arm64": "0.25.0", + "@esbuild/darwin-x64": "0.25.0", + "@esbuild/freebsd-arm64": "0.25.0", + "@esbuild/freebsd-x64": "0.25.0", + "@esbuild/linux-arm": "0.25.0", + "@esbuild/linux-arm64": "0.25.0", + "@esbuild/linux-ia32": "0.25.0", + "@esbuild/linux-loong64": "0.25.0", + "@esbuild/linux-mips64el": "0.25.0", + "@esbuild/linux-ppc64": "0.25.0", + "@esbuild/linux-riscv64": "0.25.0", + "@esbuild/linux-s390x": "0.25.0", + "@esbuild/linux-x64": "0.25.0", + "@esbuild/netbsd-arm64": "0.25.0", + "@esbuild/netbsd-x64": "0.25.0", + "@esbuild/openbsd-arm64": "0.25.0", + "@esbuild/openbsd-x64": "0.25.0", + "@esbuild/sunos-x64": "0.25.0", + "@esbuild/win32-arm64": "0.25.0", + "@esbuild/win32-ia32": "0.25.0", + "@esbuild/win32-x64": "0.25.0" } }, "node_modules/escalade": { @@ -6659,9 +6665,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.0.1.tgz", - "integrity": "sha512-lZBts941cyJyeaooiKxAtzoPHTN+GbQTJFAIdQbRhA4/8whaAraEh47Whw/ZFfrjNSnlAxqfm9i0XVAEkULjCw==", + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.0.2.tgz", + "integrity": "sha512-1105/17ZIMjmCOJOPNfVdbXafLCLj3hPmkmB7dLgt7XsQ/zkxSuDerE/xgO3RxoHysR1N1whmquY0lSn2O0VLg==", "dev": true, "license": "MIT", "bin": { @@ -7121,9 +7127,9 @@ } }, "node_modules/fastq": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz", - "integrity": "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", "license": "ISC", "dependencies": { "reusify": "^1.0.4" @@ -7178,9 +7184,9 @@ } }, "node_modules/file-type": { - "version": "20.1.0", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-20.1.0.tgz", - "integrity": "sha512-XoxU+lETfCf+bYK3SXkxFusAvmtYQl1u/ZC4zw1DBLEsHUvh339uwYucgQnnSMz1mRCWYJrCzsbJJ95hsQbZ8A==", + "version": "20.4.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-20.4.0.tgz", + "integrity": "sha512-+NZeExsi4G6EWaMbSmvBeCoqsj9EqNvOj1o/0uPVPW4O51FSCmxFlNEp/PitsqBMCbax4cGoaYmnUK5FLTuG4g==", "license": "MIT", "dependencies": { "@tokenizer/inflate": "^0.2.6", @@ -7278,9 +7284,9 @@ } }, "node_modules/find-up-simple": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.0.tgz", - "integrity": "sha512-q7Us7kcjj2VMePAa02hDAF6d+MzsdsAWEwYyOpwUtlerRBkOEPBCRZrAV4XfcSN8fHAgaD0hP7miwoay6DCprw==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.1.tgz", + "integrity": "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==", "dev": true, "license": "MIT", "engines": { @@ -7443,13 +7449,13 @@ } }, "node_modules/foreground-child": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", - "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "dev": true, "license": "ISC", "dependencies": { - "cross-spawn": "^7.0.0", + "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" }, "engines": { @@ -7717,17 +7723,17 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", - "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.1", + "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "get-proto": "^1.0.0", + "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", @@ -8479,9 +8485,9 @@ } }, "node_modules/ioredis": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.5.0.tgz", - "integrity": "sha512-7CutT89g23FfSa8MDoIFs2GYYa0PaNiW/OrT+nRyjRXHDZd17HmIgy+reOQ/yhh72NznNjGuS8kbCAcA4Ro4mw==", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.6.0.tgz", + "integrity": "sha512-tBZlIIWbndeWBWCXWZiqtOF/yxf6yZX3tAlTJ7nfo5jhd6dctNxF7QnYlZLZ1a0o0pDoen7CgZqO+zjNaFbJAg==", "license": "MIT", "dependencies": { "@ioredis/commands": "^1.1.1", @@ -10169,10 +10175,13 @@ "license": "BlueOak-1.0.0" }, "node_modules/package-manager-detector": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-0.2.9.tgz", - "integrity": "sha512-+vYvA/Y31l8Zk8dwxHhL3JfTuHPm6tlxM2A3GeQyl7ovYnSp1+mzAxClxaOr0qO1TtPxbQxetI7v5XqKLJZk7Q==", - "license": "MIT" + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-0.2.11.tgz", + "integrity": "sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==", + "license": "MIT", + "dependencies": { + "quansync": "^0.2.7" + } }, "node_modules/parent-module": { "version": "1.0.1", @@ -10873,9 +10882,9 @@ } }, "node_modules/prettier": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.1.tgz", - "integrity": "sha512-hPpFQvHwL3Qv5AdRvBFMhnKo4tYxp0ReXiPn2bxkiohEX6mBeBwEpBSQTkD458RaaDKQMYSp4hX4UtfUTA5wDw==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", "dev": true, "license": "MIT", "bin": { @@ -11065,6 +11074,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/quansync": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.8.tgz", + "integrity": "sha512-4+saucphJMazjt7iOM27mbFCk+D9dd/zmgMDCzRZ8MEoBfYp7lAvoN38et/phRQF6wOPMy/OROBGgoWeSKyluA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ], + "license": "MIT" + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -11437,9 +11462,9 @@ } }, "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", "license": "MIT", "engines": { "iojs": ">=1.0.0", @@ -11469,9 +11494,9 @@ "license": "MIT" }, "node_modules/rollup": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.8.tgz", - "integrity": "sha512-489gTVMzAYdiZHFVA/ig/iYFllCcWFHMvUHI1rpFmkoUtRlQxqh6/yiNqnYibjMZ2b/+FUQwldG+aLsEt6bglQ==", + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.9.tgz", + "integrity": "sha512-nF5XYqWWp9hx/LrpC8sZvvvmq0TeTjQgaZHYmAgwysT9nh8sWnZhBnM8ZyVbbJFIQBLwHDNoMqsBZBbUo4U8sQ==", "license": "MIT", "dependencies": { "@types/estree": "1.0.6" @@ -11484,25 +11509,25 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.34.8", - "@rollup/rollup-android-arm64": "4.34.8", - "@rollup/rollup-darwin-arm64": "4.34.8", - "@rollup/rollup-darwin-x64": "4.34.8", - "@rollup/rollup-freebsd-arm64": "4.34.8", - "@rollup/rollup-freebsd-x64": "4.34.8", - "@rollup/rollup-linux-arm-gnueabihf": "4.34.8", - "@rollup/rollup-linux-arm-musleabihf": "4.34.8", - "@rollup/rollup-linux-arm64-gnu": "4.34.8", - "@rollup/rollup-linux-arm64-musl": "4.34.8", - "@rollup/rollup-linux-loongarch64-gnu": "4.34.8", - "@rollup/rollup-linux-powerpc64le-gnu": "4.34.8", - "@rollup/rollup-linux-riscv64-gnu": "4.34.8", - "@rollup/rollup-linux-s390x-gnu": "4.34.8", - "@rollup/rollup-linux-x64-gnu": "4.34.8", - "@rollup/rollup-linux-x64-musl": "4.34.8", - "@rollup/rollup-win32-arm64-msvc": "4.34.8", - "@rollup/rollup-win32-ia32-msvc": "4.34.8", - "@rollup/rollup-win32-x64-msvc": "4.34.8", + "@rollup/rollup-android-arm-eabi": "4.34.9", + "@rollup/rollup-android-arm64": "4.34.9", + "@rollup/rollup-darwin-arm64": "4.34.9", + "@rollup/rollup-darwin-x64": "4.34.9", + "@rollup/rollup-freebsd-arm64": "4.34.9", + "@rollup/rollup-freebsd-x64": "4.34.9", + "@rollup/rollup-linux-arm-gnueabihf": "4.34.9", + "@rollup/rollup-linux-arm-musleabihf": "4.34.9", + "@rollup/rollup-linux-arm64-gnu": "4.34.9", + "@rollup/rollup-linux-arm64-musl": "4.34.9", + "@rollup/rollup-linux-loongarch64-gnu": "4.34.9", + "@rollup/rollup-linux-powerpc64le-gnu": "4.34.9", + "@rollup/rollup-linux-riscv64-gnu": "4.34.9", + "@rollup/rollup-linux-s390x-gnu": "4.34.9", + "@rollup/rollup-linux-x64-gnu": "4.34.9", + "@rollup/rollup-linux-x64-musl": "4.34.9", + "@rollup/rollup-win32-arm64-msvc": "4.34.9", + "@rollup/rollup-win32-ia32-msvc": "4.34.9", + "@rollup/rollup-win32-x64-msvc": "4.34.9", "fsevents": "~2.3.2" } }, @@ -12788,9 +12813,9 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.11", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.11.tgz", - "integrity": "sha512-RVCsMfuD0+cTt3EwX8hSl2Ks56EbFHWmhluwcqoPKtBnfjiT6olaq7PRIRfhyU8nnC2MrnDrBLfrD/RGE+cVXQ==", + "version": "5.3.13", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.13.tgz", + "integrity": "sha512-JG3pBixF6kx2o0Yfz2K6pqh72DpwTI08nooHd06tcj5WyIt5SsSiUYqRT+kemrGUNSuSzVhwfZ28aO8gogajNQ==", "dev": true, "license": "MIT", "peer": true, @@ -13176,9 +13201,9 @@ } }, "node_modules/type-fest": { - "version": "4.35.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.35.0.tgz", - "integrity": "sha512-2/AwEFQDFEy30iOLjrvHDIH7e4HEWH+f1Yl1bI5XMqzuoCUqwYCdxachgsgv0og/JdVZUhbfjcJAoHj5L1753A==", + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.37.0.tgz", + "integrity": "sha512-S/5/0kFftkq27FPNye0XM1e2NsnoD/3FS+pBmbjmmtLT6I+i344KoOf7pvXreaFsDamWeaJX55nczA1m5PsBDg==", "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=16" @@ -13285,9 +13310,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz", - "integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", "dev": true, "funding": [ { @@ -13394,13 +13419,13 @@ } }, "node_modules/vite": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.1.1.tgz", - "integrity": "sha512-4GgM54XrwRfrOp297aIYspIti66k56v16ZnqHvrIM7mG+HjDlAwS7p+Srr7J6fGvEdOJ5JcQ/D9T7HhtdXDTzA==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.0.tgz", + "integrity": "sha512-7dPxoo+WsT/64rDcwoOjk76XHj+TqNTIvHKcuMQ1k4/SeHDaQt5GFAeLYzrimZrMpn/O6DtdI03WUjdxuPM0oQ==", "license": "MIT", "dependencies": { - "esbuild": "^0.24.2", - "postcss": "^8.5.2", + "esbuild": "^0.25.0", + "postcss": "^8.5.3", "rollup": "^4.30.1" }, "bin": { @@ -14073,9 +14098,9 @@ "license": "ISC" }, "node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==", "dev": true, "license": "MIT", "engines": { diff --git a/resources/js/Components/AsideMenuItem.vue b/resources/js/Components/AsideMenuItem.vue index 4e52d40..52e4bbc 100644 --- a/resources/js/Components/AsideMenuItem.vue +++ b/resources/js/Components/AsideMenuItem.vue @@ -1,162 +1,143 @@ diff --git a/resources/js/Components/CardBoxClient.vue b/resources/js/Components/CardBoxClient.vue index bf17b32..b89ed7a 100644 --- a/resources/js/Components/CardBoxClient.vue +++ b/resources/js/Components/CardBoxClient.vue @@ -1,6 +1,6 @@ - +// const pillText = computed(() => props.text ?? `${props.progress}%`); +// diff --git a/resources/js/Components/CardBoxDataset.vue b/resources/js/Components/CardBoxDataset.vue new file mode 100644 index 0000000..72d3f4d --- /dev/null +++ b/resources/js/Components/CardBoxDataset.vue @@ -0,0 +1,107 @@ + + + diff --git a/resources/js/Components/TableSampleClients.vue b/resources/js/Components/TableSampleClients.vue index f2a062a..9fb1c01 100644 --- a/resources/js/Components/TableSampleClients.vue +++ b/resources/js/Components/TableSampleClients.vue @@ -1,17 +1,19 @@ - \ No newline at end of file +
+ + {{ checkedRow.login }} + +
+ + + + + + + + + + + + + + + + + + + +
+ + LoginEmailCreated +
+ +
+ +
+ + +
+ +
+
+ {{ client.login }} + + {{ client.email }} + + + {{ client.created_at ? dayjs(client.created_at).format('MMM D, YYYY h:mm A') : 'N/A' }} + + + + + + +
+
+ + + + + Page {{ currentPageHuman }} of {{ numPages }} + +
+ diff --git a/resources/js/Components/UserAvatar.vue b/resources/js/Components/UserAvatar.vue index 8aedd1a..6484d83 100644 --- a/resources/js/Components/UserAvatar.vue +++ b/resources/js/Components/UserAvatar.vue @@ -1,4 +1,4 @@ - diff --git a/resources/js/Pages/Editor/Dataset/Doi.vue b/resources/js/Pages/Editor/Dataset/Doi.vue index 412622b..096afcb 100644 --- a/resources/js/Pages/Editor/Dataset/Doi.vue +++ b/resources/js/Pages/Editor/Dataset/Doi.vue @@ -26,17 +26,6 @@ const errors: Ref = computed(() => { return usePage().props.errors; }); -// const form = useForm({ -// preferred_reviewer: '', -// preferred_reviewer_email: '', -// preferation: 'yes_preferation', - -// // preferation: '', -// // isPreferationRequired: false, -// }); - -// const isPreferationRequired = computed(() => form.preferation === 'yes_preferation'); - const handleSubmit = async (e) => { e.preventDefault(); // Notification.showInfo(`doi implementation is in developement. Create DOI for dataset ${props.dataset.publish_id} later on`); diff --git a/resources/js/Pages/Map.vue b/resources/js/Pages/Map.vue index 15d40e3..4c19625 100644 --- a/resources/js/Pages/Map.vue +++ b/resources/js/Pages/Map.vue @@ -1,13 +1,13 @@ diff --git a/resources/js/Pages/Submitter/Dataset/Index.vue b/resources/js/Pages/Submitter/Dataset/Index.vue index 0cf8ca5..65ab0b1 100644 --- a/resources/js/Pages/Submitter/Dataset/Index.vue +++ b/resources/js/Pages/Submitter/Dataset/Index.vue @@ -2,7 +2,7 @@ // import { Head, Link, useForm, usePage } from '@inertiajs/inertia-vue3'; import { Head, usePage } from '@inertiajs/vue3'; import { ComputedRef } from 'vue'; -import { mdiSquareEditOutline, mdiTrashCan, mdiAlertBoxOutline, mdiLockOpen } from '@mdi/js'; +import { mdiSquareEditOutline, mdiTrashCan, mdiAlertBoxOutline, mdiLockOpen, mdiLibraryShelves } from '@mdi/js'; import { computed } from 'vue'; import LayoutAuthenticated from '@/Layouts/LayoutAuthenticated.vue'; import SectionMain from '@/Components/SectionMain.vue'; @@ -139,7 +139,10 @@ const formatServerState = (state: string) => { :route-name="stardust.route('dataset.release', [dataset.id])" color="info" :icon="mdiLockOpen" :label="'Release'" small /> + color="info" :icon="mdiSquareEditOutline" :label="'Edit'" small /> + diff --git a/resources/js/Stores/main.ts b/resources/js/Stores/main.ts index 8d51088..39ca0c0 100644 --- a/resources/js/Stores/main.ts +++ b/resources/js/Stores/main.ts @@ -2,6 +2,56 @@ import { defineStore } from 'pinia'; import axios from 'axios'; import { Dataset } from '@/Dataset'; import menu from '@/menu'; +// import type Person from '#models/person'; + +export interface User { + id: number; + login: string; + firstName: string; + lastName: string; + email: string; + password: string; + created_at: DateTime; + updatedAt: DateTime; + lastLoginAt: DateTime; + isActive: boolean; + isVerified: boolean; + roles: string[]; + permissions: string[]; + settings: Record; + profile: { + avatar: string; + bio: string; + location: string; + website: string; + social: { + twitter: string; + facebook: string; + linkedin: string; + github: string; + } + }; + metadata: Record; + verifyPassword: (plainPassword: string) => Promise; +} + +interface DateTime { + get: (unit: keyof DateTime) => number; + getPossibleOffsets: () => DateTime[]; + toRelativeCalendar: (options?: ToRelativeCalendarOptions) => string | null; + toFormat: (format: string) => string; + toISO: () => string; + toJSON: () => string; + toString: () => string; + toLocaleString: (options?: Intl.DateTimeFormatOptions) => string; + toUTC: () => DateTime; + toLocal: () => DateTime; + valueOf: () => number; + toMillis: () => number; + toSeconds: () => number; + toUnixInteger: () => number; +} + export interface Person { id: number; @@ -9,10 +59,12 @@ export interface Person { email: string; name_type: string; identifier_orcid: string; - datasetCount: string; + dataset_count: number; created_at: string; } + + interface TransactionItem { amount: number; account: string; @@ -61,7 +113,7 @@ export const MainService = defineStore('main', { isFieldFocusRegistered: false, /* Sample data for starting dashboard(commonly used) */ - clients: [], + clients: [] as Array, history: [] as Array, // api based data @@ -184,7 +236,7 @@ export const MainService = defineStore('main', { this.totpState = state; }, - async fetchChartData(year: string) { + fetchChartData(year: string) { // sampleDataKey= authors or datasets axios .get(`/api/statistic/${year}`) diff --git a/resources/js/app.ts b/resources/js/app.ts index 560b28e..e98664d 100644 --- a/resources/js/app.ts +++ b/resources/js/app.ts @@ -8,6 +8,7 @@ import { createPinia } from 'pinia'; import { StyleService } from '@/Stores/style.service'; import { LayoutService } from '@/Stores/layout'; import { LocaleStore } from '@/Stores/locale'; +import { MainService } from './Stores/main'; import { darkModeKey, styleKey } from '@/config'; import type { DefineComponent } from 'vue'; import { resolvePageComponent } from '@adonisjs/inertia/helpers'; @@ -80,7 +81,7 @@ const layoutService = LayoutService(pinia); const localeService = LocaleStore(pinia); localeService.initializeLocale(); -// const mainService = MainService(pinia); +const mainService = MainService(pinia); // mainService.setUser(user); /* App style */ @@ -90,6 +91,12 @@ styleService.setStyle(localStorage[styleKey] ?? 'basic'); if ((!localStorage[darkModeKey] && window.matchMedia('(prefers-color-scheme: dark)').matches) || localStorage[darkModeKey] === '1') { styleService.setDarkMode(true); } +// mainService.fetch('clients'); +// mainService.fetch('history'); +mainService.fetchApi('clients'); +mainService.fetchApi('authors'); +mainService.fetchApi('datasets'); +mainService.fetchChartData("2022"); /* Collapse mobile aside menu on route change */ Inertia.on('navigate', () => { diff --git a/resources/js/menu.ts b/resources/js/menu.ts index b8fa697..544b0cb 100644 --- a/resources/js/menu.ts +++ b/resources/js/menu.ts @@ -12,6 +12,7 @@ import { mdiShieldCrownOutline, mdiLicense, mdiFileDocument, + mdiLibraryShelves } from '@mdi/js'; export default [ @@ -111,6 +112,11 @@ export default [ icon: mdiPublish, label: 'Create Dataset', }, + // { + // route: 'dataset.categorize', + // icon: mdiLibraryShelves, + // label: 'Library Classification', + // }, ], }, { diff --git a/start/routes.ts b/start/routes.ts index af9c289..b93a5de 100644 --- a/start/routes.ts +++ b/start/routes.ts @@ -314,9 +314,11 @@ router .as('dataset.deleteUpdate') .use([middleware.auth(), middleware.can(['dataset-delete'])]); router.get('/person', [PersonController, 'index']).as('person.index').use([middleware.auth()]); - router.get('/dataset/categorize', ({ inertia }: HttpContext) => { - return inertia.render('Submitter/Dataset/Category'); - }); + router + .get('/dataset/:id/categorize', [DatasetController, 'categorize']) + .as('dataset.categorize') + .where('id', router.matchers.number()) + .use([middleware.auth(), middleware.can(['dataset-edit'])]); }) .prefix('submitter'); diff --git a/start/routes/api.ts b/start/routes/api.ts index e417e3f..32bc6c2 100644 --- a/start/routes/api.ts +++ b/start/routes/api.ts @@ -6,10 +6,12 @@ import HomeController from '#controllers/Http/Api/HomeController'; import FileController from '#controllers/Http/Api/FileController'; import AvatarController from '#controllers/Http/Api/AvatarController'; import UserController from '#controllers/Http/Api/UserController'; +import CollectionsController from '#controllers/Http/Api/collections_controller'; import { middleware } from '../kernel.js'; // API router .group(() => { + router.get('clients', [UserController, 'getSubmitters']).as('client.index'); router.get('authors', [AuthorsController, 'index']).as('author.index'); router.get('datasets', [DatasetController, 'index']).as('dataset.index'); router.get('persons', [AuthorsController, 'persons']).as('author.persons'); @@ -32,6 +34,8 @@ router .post('/twofactor_backupcodes/settings/create', [UserController, 'createCodes']) .as('apps.twofactor_backupcodes.create') .use(middleware.auth()); + + router.get('collections/:id', [CollectionsController, 'show']).as('collection.show') }) // .namespace('App/Controllers/Http/Api') .prefix('api'); diff --git a/start/rules/referenceValidation.ts b/start/rules/referenceValidation.ts index 531418c..dd4030a 100644 --- a/start/rules/referenceValidation.ts +++ b/start/rules/referenceValidation.ts @@ -19,12 +19,63 @@ async function checkDoiExists(doi: string): Promise { } // Function to check if ISBN exists using the Open Library API +// async function checkIsbnExists(isbn: string): Promise { +// try { +// const response = await axios.get(`https://isbnsearch.org/isbn/${isbn}`); +// return response.status === 200 && response.data.includes('ISBN'); // Check if response contains ISBN information +// } catch (error) { +// return false; // If request fails, ISBN does not exist +// } +// } + async function checkIsbnExists(isbn: string): Promise { + // Try Open Library first try { - const response = await axios.get(`https://isbnsearch.org/isbn/${isbn}`); - return response.status === 200 && response.data.includes('ISBN'); // Check if response contains ISBN information + const response = await axios.get(`https://openlibrary.org/api/books?bibkeys=ISBN:${isbn}&format=json&jscmd=data`); + const data = response.data; + if (Object.keys(data).length > 0) { + return true; + } } catch (error) { - return false; // If request fails, ISBN does not exist + // If an error occurs, continue to the next API + } + + // Fallback to Google Books API + try { + const response = await axios.get(`https://www.googleapis.com/books/v1/volumes?q=isbn:${isbn}`); + const data = response.data; + if (data.totalItems > 0) { + return true; + } + } catch (error) { + // If an error occurs, continue to the next API + } + + // Lastly use the Koha library by scraping HTML + try { + const response = await axios.get(`https://bibliothek.geosphere.at/cgi-bin/koha/opac-search.pl?idx=nb&q=${isbn}`); + const html = response.data; + // Check if zero results are explicitly indicated (German or English) + if (html.includes('Keine Treffer gefunden!') || html.includes('Your search returned 0 results')) { + return false; + } + // Try to extract the count from German message + let match = html.match(/Ihre Suche erzielte\s*(\d+)\s*Treffer/); + + // If not found, try the English equivalent + if (!match) { + match = html.match(/Your search returned\s*(\d+)\s*results/); + } + + if (match && match[1]) { + const count = parseInt(match[1], 10); + return count > 0; + } + + // Fallback: if no match is found, return false + return false; + } catch (error) { + return false; } } @@ -42,10 +93,10 @@ async function validateReference(value: unknown, options: Options, field: FieldC try { const exists = await checkDoiExists(value); if (!exists) { - field.report('The {{ field }} must be an existing DOI', 'validateReference', field); + field.report('The {{ field }} must be an existing URL', 'validateReference', field); } } catch (error) { - field.report('Error checking DOI existence: ' + error.message, 'validateReference', field); + field.report('Error checking URL existence: ' + error.message, 'validateReference', field); } } }