🪢Added resolvers for lib, dashboard endpoints

This commit is contained in:
2026-03-04 21:49:38 -05:00
parent 8c224bad68
commit 22cbdcd468
9 changed files with 1846 additions and 10 deletions

View File

@@ -0,0 +1,139 @@
/**
* ComicDetailContainer - GraphQL Version
*
* This file should replace the existing ComicDetailContainer.tsx
* Location: src/client/components/ComicDetail/ComicDetailContainer.tsx
*
* Key changes from REST version:
* 1. Uses executeGraphQLQuery instead of axios directly
* 2. Parses JSON strings from sourcedMetadata
* 3. Maps GraphQL 'id' to REST '_id' for backward compatibility
* 4. Better error and loading states
*/
import React, { ReactElement } from "react";
import { useParams } from "react-router-dom";
import { ComicDetail } from "../ComicDetail/ComicDetail";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { executeGraphQLQuery, transformComicToRestFormat } from "../../services/api/GraphQLApi";
import {
GET_COMIC_DETAIL_QUERY,
ComicDetailQueryResponse
} from "../../graphql/queries/comicDetail";
export const ComicDetailContainer = (): ReactElement | null => {
const { comicObjectId } = useParams<{ comicObjectId: string }>();
const queryClient = useQueryClient();
const {
data: comicBookDetailData,
isLoading,
isError,
error,
} = useQuery({
queryKey: ["comicBookMetadata", comicObjectId],
queryFn: async () => {
// Execute GraphQL query
const result = await executeGraphQLQuery<ComicDetailQueryResponse>(
GET_COMIC_DETAIL_QUERY,
{ id: comicObjectId }
);
// Transform to REST format for backward compatibility
const transformedComic = transformComicToRestFormat(result.comic);
// Return in the format expected by ComicDetail component
return {
data: transformedComic,
};
},
enabled: !!comicObjectId, // Only run query if we have an ID
staleTime: 5 * 60 * 1000, // Consider data fresh for 5 minutes
retry: 2, // Retry failed requests twice
});
if (isError) {
return (
<div className="mx-auto max-w-screen-xl px-4 py-4">
<div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded">
<strong className="font-bold">Error loading comic: </strong>
<span className="block sm:inline">
{error instanceof Error ? error.message : 'Unknown error'}
</span>
</div>
</div>
);
}
if (isLoading) {
return (
<div className="mx-auto max-w-screen-xl px-4 py-4">
<div className="flex items-center justify-center">
<div className="text-gray-500 dark:text-gray-400">
Loading comic details...
</div>
</div>
</div>
);
}
return (
comicBookDetailData?.data && (
<ComicDetail
data={comicBookDetailData.data}
queryClient={queryClient}
comicObjectId={comicObjectId}
/>
)
);
};
/**
* Alternative implementation with feature flag for gradual rollout
* Uncomment this version if you want to toggle between REST and GraphQL
*/
/*
export const ComicDetailContainer = (): ReactElement | null => {
const { comicObjectId } = useParams<{ comicObjectId: string }>();
const queryClient = useQueryClient();
// Feature flag to toggle between REST and GraphQL
const USE_GRAPHQL = import.meta.env.VITE_USE_GRAPHQL === 'true';
const {
data: comicBookDetailData,
isLoading,
isError,
error,
} = useQuery({
queryKey: ["comicBookMetadata", comicObjectId],
queryFn: async () => {
if (USE_GRAPHQL) {
// GraphQL implementation
const result = await executeGraphQLQuery<ComicDetailQueryResponse>(
GET_COMIC_DETAIL_QUERY,
{ id: comicObjectId }
);
const transformedComic = transformComicToRestFormat(result.comic);
return {
data: transformedComic,
};
} else {
// REST implementation (fallback)
const response = await axios({
url: `${LIBRARY_SERVICE_BASE_URI}/getComicBookById`,
method: "POST",
data: { id: comicObjectId },
});
return response;
}
},
enabled: !!comicObjectId,
});
// ... rest of the component remains the same
};
*/

View File

@@ -0,0 +1,248 @@
/**
* GraphQL query to fetch complete comic detail data
*
* This file should be placed in the frontend project at:
* src/client/graphql/queries/comicDetail.ts
*
* Matches the data structure expected by ComicDetail component
*/
export const GET_COMIC_DETAIL_QUERY = `
query GetComicDetail($id: ID!) {
comic(id: $id) {
id
# Raw file information
rawFileDetails {
name
filePath
fileSize
extension
mimeType
containedIn
pageCount
archive {
uncompressed
expandedPath
}
cover {
filePath
stats
}
}
# Inferred metadata from filename parsing
inferredMetadata {
issue {
name
number
year
subtitle
}
}
# Sourced metadata from various providers
sourcedMetadata {
comicInfo
comicvine
metron
gcd
locg {
name
publisher
url
cover
description
price
rating
pulls
potw
}
}
# Import status
importStatus {
isImported
tagged
matchedResult {
score
}
}
# Acquisition/download information
acquisition {
source {
wanted
name
}
directconnect {
downloads {
bundleId
name
size
}
}
torrent {
infoHash
name
announce
}
}
# Timestamps
createdAt
updatedAt
}
}
`;
/**
* TypeScript type for the query response
* Generated from GraphQL schema
*/
export interface ComicDetailQueryResponse {
comic: {
id: string;
rawFileDetails?: {
name?: string;
filePath?: string;
fileSize?: number;
extension?: string;
mimeType?: string;
containedIn?: string;
pageCount?: number;
archive?: {
uncompressed?: boolean;
expandedPath?: string;
};
cover?: {
filePath?: string;
stats?: any;
};
};
inferredMetadata?: {
issue?: {
name?: string;
number?: number;
year?: string;
subtitle?: string;
};
};
sourcedMetadata?: {
comicInfo?: string; // JSON string - needs parsing
comicvine?: string; // JSON string - needs parsing
metron?: string; // JSON string - needs parsing
gcd?: string; // JSON string - needs parsing
locg?: {
name?: string;
publisher?: string;
url?: string;
cover?: string;
description?: string;
price?: string;
rating?: number;
pulls?: number;
potw?: number;
};
};
importStatus?: {
isImported?: boolean;
tagged?: boolean;
matchedResult?: {
score?: string;
};
};
acquisition?: {
source?: {
wanted?: boolean;
name?: string;
};
directconnect?: {
downloads?: Array<{
bundleId?: number;
name?: string;
size?: string;
}>;
};
torrent?: Array<{
infoHash?: string;
name?: string;
announce?: string[];
}>;
};
createdAt?: string;
updatedAt?: string;
};
}
/**
* Minimal query for basic comic information
* Use this when you only need basic details
*/
export const GET_COMIC_BASIC_QUERY = `
query GetComicBasic($id: ID!) {
comic(id: $id) {
id
rawFileDetails {
name
filePath
fileSize
pageCount
}
inferredMetadata {
issue {
name
number
year
}
}
}
}
`;
/**
* Query for comic metadata only (no file details)
* Use this when you only need metadata
*/
export const GET_COMIC_METADATA_QUERY = `
query GetComicMetadata($id: ID!) {
comic(id: $id) {
id
sourcedMetadata {
comicInfo
comicvine
metron
gcd
locg {
name
publisher
description
rating
}
}
canonicalMetadata {
title {
value
provenance {
source
confidence
}
}
series {
value
provenance {
source
confidence
}
}
publisher {
value
provenance {
source
confidence
}
}
}
}
}
`;

View File

@@ -0,0 +1,260 @@
/**
* GraphQL queries for library operations
* Examples for getComicBooks, getComicBookGroups, getLibraryStatistics, and searchIssue
*/
/**
* Query to get comic books with pagination and filtering
*/
export const GET_COMIC_BOOKS = `
query GetComicBooks($paginationOptions: PaginationOptionsInput!, $predicate: PredicateInput) {
getComicBooks(paginationOptions: $paginationOptions, predicate: $predicate) {
docs {
id
canonicalMetadata {
title {
value
provenance {
source
confidence
}
}
series {
value
}
issueNumber {
value
}
publisher {
value
}
coverImage {
value
}
}
rawFileDetails {
name
filePath
fileSize
extension
}
createdAt
updatedAt
}
totalDocs
limit
page
totalPages
hasNextPage
hasPrevPage
nextPage
prevPage
pagingCounter
}
}
`;
/**
* Query to get comic book groups (volumes)
*/
export const GET_COMIC_BOOK_GROUPS = `
query GetComicBookGroups {
getComicBookGroups {
id
volumes {
id
name
count_of_issues
publisher {
id
name
}
start_year
image {
medium_url
thumb_url
}
description
site_detail_url
}
}
}
`;
/**
* Query to get library statistics
*/
export const GET_LIBRARY_STATISTICS = `
query GetLibraryStatistics {
getLibraryStatistics {
totalDocuments
comicDirectorySize {
totalSize
totalSizeInMB
totalSizeInGB
fileCount
}
statistics {
fileTypes {
id
data
}
publisherWithMostComicsInLibrary {
id
count
}
}
}
}
`;
/**
* Example usage with variables for getComicBooks
*/
export const exampleGetComicBooksVariables = {
paginationOptions: {
page: 1,
limit: 10,
sort: "-createdAt", // Sort by creation date, descending
lean: false,
pagination: true,
},
predicate: {
// Optional: Add filters here
// Example: { "canonicalMetadata.publisher.value": "Marvel" }
},
};
/**
* Example: Get first page of comics
*/
export const exampleGetFirstPage = {
query: GET_COMIC_BOOKS,
variables: {
paginationOptions: {
page: 1,
limit: 20,
sort: "-createdAt",
},
},
};
/**
* Example: Get comics with specific filters
*/
export const exampleGetFilteredComics = {
query: GET_COMIC_BOOKS,
variables: {
paginationOptions: {
page: 1,
limit: 10,
},
predicate: {
"importStatus.isImported": true,
},
},
};
/**
* Query to search issues using Elasticsearch
*/
export const SEARCH_ISSUE = `
query SearchIssue($query: SearchIssueQueryInput, $pagination: SearchPaginationInput, $type: SearchType!) {
searchIssue(query: $query, pagination: $pagination, type: $type) {
hits {
total {
value
relation
}
max_score
hits {
_index
_id
_score
_source {
id
canonicalMetadata {
title {
value
}
series {
value
}
issueNumber {
value
}
publisher {
value
}
}
rawFileDetails {
name
filePath
}
}
}
}
took
timed_out
}
}
`;
/**
* Example: Search all comics
*/
export const exampleSearchAll = {
query: SEARCH_ISSUE,
variables: {
type: "all",
pagination: {
size: 10,
from: 0,
},
},
};
/**
* Example: Search by volume name
*/
export const exampleSearchByVolumeName = {
query: SEARCH_ISSUE,
variables: {
query: {
volumeName: "Spider-Man",
},
type: "volumeName",
pagination: {
size: 20,
from: 0,
},
},
};
/**
* Example: Search wanted comics
*/
export const exampleSearchWanted = {
query: SEARCH_ISSUE,
variables: {
type: "wanted",
pagination: {
size: 50,
from: 0,
},
},
};
/**
* Example: Search volumes
*/
export const exampleSearchVolumes = {
query: SEARCH_ISSUE,
variables: {
type: "volumes",
pagination: {
size: 10,
from: 0,
},
},
};

View File

@@ -0,0 +1,165 @@
/**
* GraphQL API Client Utility
*
* This file should be placed in the frontend project at:
* src/client/services/api/GraphQLApi.ts
*
* Simple wrapper around axios for executing GraphQL queries and mutations
* No additional dependencies needed (no Apollo Client)
* Works seamlessly with React Query
*/
import axios from 'axios';
// Update this to match your frontend constants file
// import { LIBRARY_SERVICE_BASE_URI } from '../../constants/endpoints';
const LIBRARY_SERVICE_BASE_URI = process.env.REACT_APP_LIBRARY_SERVICE_BASE_URI || 'http://localhost:3000/api/library';
/**
* Execute a GraphQL query against the threetwo-core-service GraphQL endpoint
*
* @param query - GraphQL query string
* @param variables - Query variables
* @returns Promise with query result data
*
* @example
* ```typescript
* const result = await executeGraphQLQuery<ComicDetailQueryResponse>(
* GET_COMIC_DETAIL_QUERY,
* { id: 'comic-id-123' }
* );
* console.log(result.comic.rawFileDetails.name);
* ```
*/
export const executeGraphQLQuery = async <T = any>(
query: string,
variables?: Record<string, any>
): Promise<T> => {
try {
const response = await axios.post(
`${LIBRARY_SERVICE_BASE_URI}/graphql`,
{
query,
variables,
},
{
headers: {
'Content-Type': 'application/json',
},
}
);
// GraphQL can return partial data with errors
if (response.data.errors) {
console.error('GraphQL errors:', response.data.errors);
throw new Error(
`GraphQL errors: ${response.data.errors.map((e: any) => e.message).join(', ')}`
);
}
return response.data.data;
} catch (error) {
console.error('GraphQL query failed:', error);
throw error;
}
};
/**
* Execute a GraphQL mutation against the threetwo-core-service GraphQL endpoint
*
* @param mutation - GraphQL mutation string
* @param variables - Mutation variables
* @returns Promise with mutation result data
*
* @example
* ```typescript
* const result = await executeGraphQLMutation<{ setMetadataField: Comic }>(
* SET_METADATA_FIELD_MUTATION,
* { comicId: '123', field: 'title', value: 'New Title' }
* );
* console.log(result.setMetadataField.canonicalMetadata.title);
* ```
*/
export const executeGraphQLMutation = async <T = any>(
mutation: string,
variables?: Record<string, any>
): Promise<T> => {
// Mutations use the same endpoint as queries
return executeGraphQLQuery<T>(mutation, variables);
};
/**
* Helper function to parse JSON strings from sourcedMetadata
* GraphQL returns these fields as JSON strings that need parsing
*
* @param sourcedMetadata - The sourcedMetadata object from GraphQL response
* @returns Parsed sourcedMetadata with JSON fields converted to objects
*
* @example
* ```typescript
* const comic = result.comic;
* comic.sourcedMetadata = parseSourcedMetadata(comic.sourcedMetadata);
* // Now comic.sourcedMetadata.comicInfo is an object, not a string
* ```
*/
export const parseSourcedMetadata = (sourcedMetadata: any) => {
if (!sourcedMetadata) return sourcedMetadata;
const parsed = { ...sourcedMetadata };
// Parse JSON strings
if (parsed.comicInfo && typeof parsed.comicInfo === 'string') {
try {
parsed.comicInfo = JSON.parse(parsed.comicInfo);
} catch (e) {
console.warn('Failed to parse comicInfo:', e);
parsed.comicInfo = {};
}
}
if (parsed.comicvine && typeof parsed.comicvine === 'string') {
try {
parsed.comicvine = JSON.parse(parsed.comicvine);
} catch (e) {
console.warn('Failed to parse comicvine:', e);
parsed.comicvine = {};
}
}
if (parsed.metron && typeof parsed.metron === 'string') {
try {
parsed.metron = JSON.parse(parsed.metron);
} catch (e) {
console.warn('Failed to parse metron:', e);
parsed.metron = {};
}
}
if (parsed.gcd && typeof parsed.gcd === 'string') {
try {
parsed.gcd = JSON.parse(parsed.gcd);
} catch (e) {
console.warn('Failed to parse gcd:', e);
parsed.gcd = {};
}
}
return parsed;
};
/**
* Helper function to transform GraphQL comic response to REST format
* Ensures backward compatibility with existing components
*
* @param comic - Comic object from GraphQL response
* @returns Comic object in REST format with _id field
*/
export const transformComicToRestFormat = (comic: any) => {
if (!comic) return null;
return {
_id: comic.id,
...comic,
sourcedMetadata: parseSourcedMetadata(comic.sourcedMetadata),
};
};

View File

@@ -0,0 +1,87 @@
#!/bin/bash
# Test GraphQL Endpoint Script
# This script tests the GraphQL endpoint with various queries
GRAPHQL_URL="http://localhost:3000/graphql"
echo "🧪 Testing GraphQL Endpoint: $GRAPHQL_URL"
echo "================================================"
echo ""
# Test 1: List Comics
echo "📚 Test 1: List Comics (first 5)"
echo "--------------------------------"
curl -s -X POST $GRAPHQL_URL \
-H "Content-Type: application/json" \
-d '{
"query": "query { comics(limit: 5) { comics { id rawFileDetails { name pageCount } } totalCount } }"
}' | jq '.'
echo ""
echo ""
# Test 2: Get Single Comic (you need to replace COMIC_ID)
echo "📖 Test 2: Get Single Comic"
echo "--------------------------------"
echo "⚠️ Replace COMIC_ID with an actual comic ID from your database"
read -p "Enter Comic ID (or press Enter to skip): " COMIC_ID
if [ ! -z "$COMIC_ID" ]; then
curl -s -X POST $GRAPHQL_URL \
-H "Content-Type: application/json" \
-d "{
\"query\": \"query GetComic(\$id: ID!) { comic(id: \$id) { id rawFileDetails { name filePath fileSize pageCount } sourcedMetadata { locg { name publisher rating } } } }\",
\"variables\": { \"id\": \"$COMIC_ID\" }
}" | jq '.'
else
echo "Skipped"
fi
echo ""
echo ""
# Test 3: Get User Preferences
echo "⚙️ Test 3: Get User Preferences"
echo "--------------------------------"
curl -s -X POST $GRAPHQL_URL \
-H "Content-Type: application/json" \
-d '{
"query": "query { userPreferences(userId: \"default\") { id userId conflictResolution minConfidenceThreshold autoMerge { enabled onImport onMetadataUpdate } } }"
}' | jq '.'
echo ""
echo ""
# Test 4: Search Comics
echo "🔍 Test 4: Search Comics"
echo "--------------------------------"
read -p "Enter search term (or press Enter to skip): " SEARCH_TERM
if [ ! -z "$SEARCH_TERM" ]; then
curl -s -X POST $GRAPHQL_URL \
-H "Content-Type: application/json" \
-d "{
\"query\": \"query SearchComics(\$search: String) { comics(search: \$search, limit: 10) { comics { id rawFileDetails { name } } totalCount } }\",
\"variables\": { \"search\": \"$SEARCH_TERM\" }
}" | jq '.'
else
echo "Skipped"
fi
echo ""
echo ""
# Test 5: GraphQL Introspection (get schema info)
echo "🔬 Test 5: Introspection - Available Queries"
echo "--------------------------------"
curl -s -X POST $GRAPHQL_URL \
-H "Content-Type: application/json" \
-d '{
"query": "{ __schema { queryType { fields { name description } } } }"
}' | jq '.data.__schema.queryType.fields[] | {name, description}'
echo ""
echo ""
echo "✅ GraphQL endpoint tests complete!"
echo ""
echo "💡 Tips:"
echo " - Open http://localhost:3000/graphql in your browser for GraphQL Playground"
echo " - Use 'jq' for better JSON formatting (install with: apt-get install jq)"
echo " - Check the docs at: docs/FRONTEND_GRAPHQL_INTEGRATION.md"

View File

@@ -27,6 +27,202 @@ export const resolvers = {
}
},
/**
* Get comic books with advanced pagination and filtering
*/
getComicBooks: async (
_: any,
{
paginationOptions,
predicate = {},
}: {
paginationOptions: any;
predicate?: any;
}
) => {
try {
const result = await Comic.paginate(predicate, paginationOptions);
return result;
} catch (error) {
console.error("Error fetching comic books:", error);
throw new Error("Failed to fetch comic books");
}
},
/**
* Get comic book groups (volumes with multiple issues)
*/
getComicBookGroups: async () => {
try {
const volumes = await Comic.aggregate([
{
$project: {
volumeInfo:
"$sourcedMetadata.comicvine.volumeInformation",
},
},
{
$unwind: "$volumeInfo",
},
{
$group: {
_id: "$_id",
volumes: {
$addToSet: "$volumeInfo",
},
},
},
{
$unwind: "$volumes",
},
{ $sort: { updatedAt: -1 } },
{ $skip: 0 },
{ $limit: 5 },
]);
return volumes.map((vol) => ({
id: vol._id.toString(),
volumes: vol.volumes,
}));
} catch (error) {
console.error("Error fetching comic book groups:", error);
throw new Error("Failed to fetch comic book groups");
}
},
/**
* Get library statistics
*/
getLibraryStatistics: async () => {
try {
const { getSizeOfDirectory } = require("../../utils/file.utils");
const { COMICS_DIRECTORY } = require("../../constants/directories");
const comicDirectorySize = await getSizeOfDirectory(
COMICS_DIRECTORY,
[".cbz", ".cbr", ".cb7"]
);
const totalCount = await Comic.countDocuments({});
const statistics = await Comic.aggregate([
{
$facet: {
fileTypes: [
{
$match: {
"rawFileDetails.extension": {
$in: [".cbr", ".cbz", ".cb7"],
},
},
},
{
$group: {
_id: "$rawFileDetails.extension",
data: { $push: "$$ROOT._id" },
},
},
],
issues: [
{
$match: {
"sourcedMetadata.comicvine.volumeInformation":
{
$gt: {},
},
},
},
{
$group: {
_id: "$sourcedMetadata.comicvine.volumeInformation",
data: { $push: "$$ROOT._id" },
},
},
],
fileLessComics: [
{
$match: {
rawFileDetails: {
$exists: false,
},
},
},
],
issuesWithComicInfoXML: [
{
$match: {
"sourcedMetadata.comicInfo": {
$exists: true,
$gt: { $size: 0 },
},
},
},
],
publisherWithMostComicsInLibrary: [
{
$unwind:
"$sourcedMetadata.comicvine.volumeInformation.publisher",
},
{
$group: {
_id: "$sourcedMetadata.comicvine.volumeInformation.publisher.name",
count: { $sum: 1 },
},
},
{ $sort: { count: -1 } },
{ $limit: 1 },
],
},
},
]);
return {
totalDocuments: totalCount,
comicDirectorySize,
statistics,
};
} catch (error) {
console.error("Error fetching library statistics:", error);
throw new Error("Failed to fetch library statistics");
}
},
/**
* Search issues using Elasticsearch
*/
searchIssue: async (
_: any,
{
query,
pagination = { size: 10, from: 0 },
type,
}: {
query?: { volumeName?: string; issueNumber?: string };
pagination?: { size?: number; from?: number };
type: string;
},
context: any
) => {
try {
// Get broker from context (set up in GraphQL service)
const broker = context?.broker;
if (!broker) {
throw new Error("Broker not available in context");
}
// Call the search service through the broker
const result = await broker.call("search.issue", {
query: query || {},
pagination,
type,
});
return result;
} catch (error) {
console.error("Error searching issues:", error);
throw new Error(`Failed to search issues: ${error.message}`);
}
},
/**
* List comics with pagination and filtering
*/

View File

@@ -239,6 +239,25 @@ export const typeDefs = gql`
series: String
): ComicConnection!
# Get comic books with advanced pagination and filtering
getComicBooks(
paginationOptions: PaginationOptionsInput!
predicate: PredicateInput
): ComicBooksResult!
# Get comic book groups (volumes with multiple issues)
getComicBookGroups: [ComicBookGroup!]!
# Get library statistics
getLibraryStatistics: LibraryStatistics!
# Search issues using Elasticsearch
searchIssue(
query: SearchIssueQueryInput
pagination: SearchPaginationInput
type: SearchType!
): SearchIssueResult!
# Get user preferences
userPreferences(userId: String = "default"): UserPreferences
@@ -439,4 +458,162 @@ export const typeDefs = gql`
message: String
canonicalMetadataResolved: Boolean!
}
# Pagination options input
input PaginationOptionsInput {
page: Int
limit: Int
sort: String
lean: Boolean
leanWithId: Boolean
offset: Int
pagination: Boolean
}
# Predicate input for filtering
# Note: This is a placeholder. In practice, predicates are passed as JSON objects
# and handled dynamically in the resolver
scalar PredicateInput
# Comic books result with pagination
type ComicBooksResult {
docs: [Comic!]!
totalDocs: Int!
limit: Int!
page: Int
totalPages: Int!
hasNextPage: Boolean!
hasPrevPage: Boolean!
nextPage: Int
prevPage: Int
pagingCounter: Int!
}
# Comic book group (volume with issues)
type ComicBookGroup {
id: ID!
volumes: VolumeInfo
}
# Volume information
type VolumeInfo {
id: Int
name: String
count_of_issues: Int
publisher: Publisher
start_year: String
image: VolumeImage
description: String
site_detail_url: String
}
# Publisher information
type Publisher {
id: Int
name: String
api_detail_url: String
}
# Volume image
type VolumeImage {
icon_url: String
medium_url: String
screen_url: String
screen_large_url: String
small_url: String
super_url: String
thumb_url: String
tiny_url: String
original_url: String
image_tags: String
}
# Library statistics
type LibraryStatistics {
totalDocuments: Int!
comicDirectorySize: DirectorySize!
statistics: [StatisticsFacet!]!
}
# Directory size information
type DirectorySize {
totalSize: Float!
totalSizeInMB: Float!
totalSizeInGB: Float!
fileCount: Int!
}
# Statistics facet
type StatisticsFacet {
fileTypes: [FileTypeStats!]
issues: [IssueStats!]
fileLessComics: [Comic!]
issuesWithComicInfoXML: [Comic!]
publisherWithMostComicsInLibrary: [PublisherStats!]
}
# File type statistics
type FileTypeStats {
id: String!
data: [ID!]!
}
# Issue statistics
type IssueStats {
id: VolumeInfo
data: [ID!]!
}
# Publisher statistics
type PublisherStats {
id: String!
count: Int!
}
# Search issue query input
input SearchIssueQueryInput {
volumeName: String
issueNumber: String
}
# Search pagination input
input SearchPaginationInput {
size: Int
from: Int
}
# Search type enum
enum SearchType {
all
volumeName
wanted
volumes
}
# Search issue result
type SearchIssueResult {
hits: SearchHits!
took: Int
timed_out: Boolean
}
# Search hits
type SearchHits {
total: SearchTotal!
max_score: Float
hits: [SearchHit!]!
}
# Search total
type SearchTotal {
value: Int!
relation: String!
}
# Search hit
type SearchHit {
_index: String!
_id: String!
_score: Float
_source: Comic!
}
`;

583
package-lock.json generated
View File

@@ -39,11 +39,12 @@
"leven": "^3.1.0",
"lodash": "^4.17.21",
"mkdirp": "^0.5.5",
"moleculer-apollo-server": "^0.4.0",
"moleculer-bullmq": "^3.0.0",
"moleculer-db": "^0.8.23",
"moleculer-db-adapter-mongoose": "^0.9.2",
"moleculer-io": "^2.2.0",
"moleculer-web": "^0.10.8",
"moleculer-web": "^0.10.5",
"mongoosastic-ts": "^6.0.3",
"mongoose": "^6.10.4",
"mongoose-paginate-v2": "^1.3.18",
@@ -4265,6 +4266,18 @@
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@whatwg-node/promise-helpers": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@whatwg-node/promise-helpers/-/promise-helpers-1.3.2.tgz",
"integrity": "sha512-Nst5JdK47VIl9UcGwtv2Rcgyn5lWtZ0/mhRQ4G8NN2isxpq2TO30iqHzmwoJycjWuyUfg3GFXqP/gFHXeV57IA==",
"license": "MIT",
"dependencies": {
"tslib": "^2.6.3"
},
"engines": {
"node": ">=16.0.0"
}
},
"node_modules/@xmldom/xmldom": {
"version": "0.9.8",
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.8.tgz",
@@ -5718,6 +5731,18 @@
"yup": "0.32.9"
}
},
"node_modules/cross-inspect": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/cross-inspect/-/cross-inspect-1.0.1.tgz",
"integrity": "sha512-Pcw1JTvZLSJH83iiGWt6fRcT+BjZlCDRVwYLbUcHzv/CRpB7r0MlSrGbIyQvVSNyGnbt7G4AXuyCiDR3POvZ1A==",
"license": "MIT",
"dependencies": {
"tslib": "^2.4.0"
},
"engines": {
"node": ">=16.0.0"
}
},
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@@ -5782,6 +5807,12 @@
"node": ">=14"
}
},
"node_modules/dataloader": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.2.3.tgz",
"integrity": "sha512-y2krtASINtPFS1rSDjacrFgn1dcUuoREVabwlOGOe4SdxenREqwjwjElAdwvbGM7kgZz9a3KVicWR7vcz8rnzA==",
"license": "MIT"
},
"node_modules/debug": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
@@ -8052,6 +8083,15 @@
"node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0"
}
},
"node_modules/graphql-subscriptions": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/graphql-subscriptions/-/graphql-subscriptions-3.0.0.tgz",
"integrity": "sha512-kZCdevgmzDjGAOqH7GlDmQXYAkuHoKpMlJrqF40HMPhUhM5ZWSFSxCwD/nSi6AkaijmMfsFhoJRGJ27UseCvRA==",
"license": "MIT",
"peerDependencies": {
"graphql": "^15.7.2 || ^16.0.0"
}
},
"node_modules/graphql-tag": {
"version": "2.12.6",
"resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz",
@@ -8067,6 +8107,32 @@
"graphql": "^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0"
}
},
"node_modules/graphql-ws": {
"version": "6.0.7",
"resolved": "https://registry.npmjs.org/graphql-ws/-/graphql-ws-6.0.7.tgz",
"integrity": "sha512-yoLRW+KRlDmnnROdAu7sX77VNLC0bsFoZyGQJLy1cF+X/SkLg/fWkRGrEEYQK8o2cafJ2wmEaMqMEZB3U3DYDg==",
"license": "MIT",
"engines": {
"node": ">=20"
},
"peerDependencies": {
"@fastify/websocket": "^10 || ^11",
"crossws": "~0.3",
"graphql": "^15.10.1 || ^16",
"ws": "^8"
},
"peerDependenciesMeta": {
"@fastify/websocket": {
"optional": true
},
"crossws": {
"optional": true
},
"ws": {
"optional": true
}
}
},
"node_modules/har-schema": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
@@ -10946,6 +11012,438 @@
}
}
},
"node_modules/moleculer-apollo-server": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/moleculer-apollo-server/-/moleculer-apollo-server-0.4.0.tgz",
"integrity": "sha512-uHI8XgYyczOMAhS4OdGgq/5jO+JmqRsuJPkcQqXRMXGJk1WjAy35kHkRsJBImFGfLlR4YKlsBVAPqEIs22QAFg==",
"license": "MIT",
"dependencies": {
"@apollo/server": "^5.0.0",
"@graphql-tools/schema": "^10.0.25",
"dataloader": "^2.2.3",
"graphql-subscriptions": "^3.0.0",
"graphql-ws": "^6.0.6",
"lodash": "^4.17.21",
"object-hash": "^3.0.0",
"ws": "^8.18.3"
},
"engines": {
"node": ">= 20.x.x"
},
"peerDependencies": {
"graphql": "^16.0.0",
"moleculer": "^0.14.0 || ^0.15.0-0"
}
},
"node_modules/moleculer-apollo-server/node_modules/@apollo/server": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@apollo/server/-/server-5.4.0.tgz",
"integrity": "sha512-E0/2C5Rqp7bWCjaDh4NzYuEPDZ+dltTf2c0FI6GCKJA6GBetVferX3h1//1rS4+NxD36wrJsGGJK+xyT/M3ysg==",
"license": "MIT",
"dependencies": {
"@apollo/cache-control-types": "^1.0.3",
"@apollo/server-gateway-interface": "^2.0.0",
"@apollo/usage-reporting-protobuf": "^4.1.1",
"@apollo/utils.createhash": "^3.0.0",
"@apollo/utils.fetcher": "^3.0.0",
"@apollo/utils.isnodelike": "^3.0.0",
"@apollo/utils.keyvaluecache": "^4.0.0",
"@apollo/utils.logger": "^3.0.0",
"@apollo/utils.usagereporting": "^2.1.0",
"@apollo/utils.withrequired": "^3.0.0",
"@graphql-tools/schema": "^10.0.0",
"async-retry": "^1.2.1",
"body-parser": "^2.2.2",
"content-type": "^1.0.5",
"cors": "^2.8.5",
"finalhandler": "^2.1.0",
"loglevel": "^1.6.8",
"lru-cache": "^11.1.0",
"negotiator": "^1.0.0",
"uuid": "^11.1.0",
"whatwg-mimetype": "^4.0.0"
},
"engines": {
"node": ">=20"
},
"peerDependencies": {
"graphql": "^16.11.0"
}
},
"node_modules/moleculer-apollo-server/node_modules/@apollo/server-gateway-interface": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@apollo/server-gateway-interface/-/server-gateway-interface-2.0.0.tgz",
"integrity": "sha512-3HEMD6fSantG2My3jWkb9dvfkF9vJ4BDLRjMgsnD790VINtuPaEp+h3Hg9HOHiWkML6QsOhnaRqZ+gvhp3y8Nw==",
"license": "MIT",
"dependencies": {
"@apollo/usage-reporting-protobuf": "^4.1.1",
"@apollo/utils.fetcher": "^3.0.0",
"@apollo/utils.keyvaluecache": "^4.0.0",
"@apollo/utils.logger": "^3.0.0"
},
"peerDependencies": {
"graphql": "14.x || 15.x || 16.x"
}
},
"node_modules/moleculer-apollo-server/node_modules/@apollo/utils.createhash": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@apollo/utils.createhash/-/utils.createhash-3.0.1.tgz",
"integrity": "sha512-CKrlySj4eQYftBE5MJ8IzKwIibQnftDT7yGfsJy5KSEEnLlPASX0UTpbKqkjlVEwPPd4mEwI7WOM7XNxEuO05A==",
"license": "MIT",
"dependencies": {
"@apollo/utils.isnodelike": "^3.0.0",
"sha.js": "^2.4.11"
},
"engines": {
"node": ">=16"
}
},
"node_modules/moleculer-apollo-server/node_modules/@apollo/utils.fetcher": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@apollo/utils.fetcher/-/utils.fetcher-3.1.0.tgz",
"integrity": "sha512-Z3QAyrsQkvrdTuHAFwWDNd+0l50guwoQUoaDQssLOjkmnmVuvXlJykqlEJolio+4rFwBnWdoY1ByFdKaQEcm7A==",
"license": "MIT",
"engines": {
"node": ">=16"
}
},
"node_modules/moleculer-apollo-server/node_modules/@apollo/utils.isnodelike": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@apollo/utils.isnodelike/-/utils.isnodelike-3.0.0.tgz",
"integrity": "sha512-xrjyjfkzunZ0DeF6xkHaK5IKR8F1FBq6qV+uZ+h9worIF/2YSzA0uoBxGv6tbTeo9QoIQnRW4PVFzGix5E7n/g==",
"license": "MIT",
"engines": {
"node": ">=16"
}
},
"node_modules/moleculer-apollo-server/node_modules/@apollo/utils.keyvaluecache": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@apollo/utils.keyvaluecache/-/utils.keyvaluecache-4.0.0.tgz",
"integrity": "sha512-mKw1myRUkQsGPNB+9bglAuhviodJ2L2MRYLTafCMw5BIo7nbvCPNCkLnIHjZ1NOzH7SnMAr5c9LmXiqsgYqLZw==",
"license": "MIT",
"dependencies": {
"@apollo/utils.logger": "^3.0.0",
"lru-cache": "^11.0.0"
},
"engines": {
"node": ">=20"
}
},
"node_modules/moleculer-apollo-server/node_modules/@apollo/utils.logger": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@apollo/utils.logger/-/utils.logger-3.0.0.tgz",
"integrity": "sha512-M8V8JOTH0F2qEi+ktPfw4RL7MvUycDfKp7aEap2eWXfL5SqWHN6jTLbj5f5fj1cceHpyaUSOZlvlaaryaxZAmg==",
"license": "MIT",
"engines": {
"node": ">=16"
}
},
"node_modules/moleculer-apollo-server/node_modules/@apollo/utils.withrequired": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@apollo/utils.withrequired/-/utils.withrequired-3.0.0.tgz",
"integrity": "sha512-aaxeavfJ+RHboh7c2ofO5HHtQobGX4AgUujXP4CXpREHp9fQ9jPi6K9T1jrAKe7HIipoP0OJ1gd6JamSkFIpvA==",
"license": "MIT",
"engines": {
"node": ">=16"
}
},
"node_modules/moleculer-apollo-server/node_modules/@graphql-tools/merge": {
"version": "9.1.7",
"resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-9.1.7.tgz",
"integrity": "sha512-Y5E1vTbTabvcXbkakdFUt4zUIzB1fyaEnVmIWN0l0GMed2gdD01TpZWLUm4RNAxpturvolrb24oGLQrBbPLSoQ==",
"license": "MIT",
"dependencies": {
"@graphql-tools/utils": "^11.0.0",
"tslib": "^2.4.0"
},
"engines": {
"node": ">=16.0.0"
},
"peerDependencies": {
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
}
},
"node_modules/moleculer-apollo-server/node_modules/@graphql-tools/schema": {
"version": "10.0.31",
"resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.31.tgz",
"integrity": "sha512-ZewRgWhXef6weZ0WiP7/MV47HXiuFbFpiDUVLQl6mgXsWSsGELKFxQsyUCBos60Qqy1JEFAIu3Ns6GGYjGkqkQ==",
"license": "MIT",
"dependencies": {
"@graphql-tools/merge": "^9.1.7",
"@graphql-tools/utils": "^11.0.0",
"tslib": "^2.4.0"
},
"engines": {
"node": ">=16.0.0"
},
"peerDependencies": {
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
}
},
"node_modules/moleculer-apollo-server/node_modules/@graphql-tools/utils": {
"version": "11.0.0",
"resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-11.0.0.tgz",
"integrity": "sha512-bM1HeZdXA2C3LSIeLOnH/bcqSgbQgKEDrjxODjqi3y58xai2TkNrtYcQSoWzGbt9VMN1dORGjR7Vem8SPnUFQA==",
"license": "MIT",
"dependencies": {
"@graphql-typed-document-node/core": "^3.1.1",
"@whatwg-node/promise-helpers": "^1.0.0",
"cross-inspect": "1.0.1",
"tslib": "^2.4.0"
},
"engines": {
"node": ">=16.0.0"
},
"peerDependencies": {
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
}
},
"node_modules/moleculer-apollo-server/node_modules/body-parser": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz",
"integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==",
"license": "MIT",
"dependencies": {
"bytes": "^3.1.2",
"content-type": "^1.0.5",
"debug": "^4.4.3",
"http-errors": "^2.0.0",
"iconv-lite": "^0.7.0",
"on-finished": "^2.4.1",
"qs": "^6.14.1",
"raw-body": "^3.0.1",
"type-is": "^2.0.1"
},
"engines": {
"node": ">=18"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/moleculer-apollo-server/node_modules/debug": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/moleculer-apollo-server/node_modules/finalhandler": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz",
"integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==",
"license": "MIT",
"dependencies": {
"debug": "^4.4.0",
"encodeurl": "^2.0.0",
"escape-html": "^1.0.3",
"on-finished": "^2.4.1",
"parseurl": "^1.3.3",
"statuses": "^2.0.1"
},
"engines": {
"node": ">= 18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/moleculer-apollo-server/node_modules/http-errors": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
"integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
"license": "MIT",
"dependencies": {
"depd": "~2.0.0",
"inherits": "~2.0.4",
"setprototypeof": "~1.2.0",
"statuses": "~2.0.2",
"toidentifier": "~1.0.1"
},
"engines": {
"node": ">= 0.8"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/moleculer-apollo-server/node_modules/iconv-lite": {
"version": "0.7.2",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz",
"integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==",
"license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
"engines": {
"node": ">=0.10.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/moleculer-apollo-server/node_modules/lru-cache": {
"version": "11.2.6",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz",
"integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==",
"license": "BlueOak-1.0.0",
"engines": {
"node": "20 || >=22"
}
},
"node_modules/moleculer-apollo-server/node_modules/media-typer": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
"integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/moleculer-apollo-server/node_modules/mime-db": {
"version": "1.54.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
"integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/moleculer-apollo-server/node_modules/mime-types": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz",
"integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==",
"license": "MIT",
"dependencies": {
"mime-db": "^1.54.0"
},
"engines": {
"node": ">=18"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/moleculer-apollo-server/node_modules/negotiator": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
"integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/moleculer-apollo-server/node_modules/qs": {
"version": "6.15.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz",
"integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==",
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.1.0"
},
"engines": {
"node": ">=0.6"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/moleculer-apollo-server/node_modules/raw-body": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz",
"integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==",
"license": "MIT",
"dependencies": {
"bytes": "~3.1.2",
"http-errors": "~2.0.1",
"iconv-lite": "~0.7.0",
"unpipe": "~1.0.0"
},
"engines": {
"node": ">= 0.10"
}
},
"node_modules/moleculer-apollo-server/node_modules/statuses": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
"integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/moleculer-apollo-server/node_modules/type-is": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
"integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
"license": "MIT",
"dependencies": {
"content-type": "^1.0.5",
"media-typer": "^1.1.0",
"mime-types": "^3.0.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/moleculer-apollo-server/node_modules/uuid": {
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
"integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
],
"license": "MIT",
"bin": {
"uuid": "dist/esm/bin/uuid"
}
},
"node_modules/moleculer-apollo-server/node_modules/whatwg-mimetype": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
"integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==",
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/moleculer-apollo-server/node_modules/ws": {
"version": "8.19.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
"integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/moleculer-bullmq": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/moleculer-bullmq/-/moleculer-bullmq-3.0.0.tgz",
@@ -14084,10 +14582,20 @@
"node": ">=0.10.0"
}
},
"node_modules/object-hash": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
"license": "MIT",
"engines": {
"node": ">= 6"
}
},
"node_modules/object-inspect": {
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz",
"integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==",
"version": "1.13.4",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
"integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
@@ -15787,14 +16295,69 @@
}
},
"node_modules/side-channel": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
"integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
"integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
"license": "MIT",
"dependencies": {
"call-bind": "^1.0.7",
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.4",
"object-inspect": "^1.13.1"
"object-inspect": "^1.13.3",
"side-channel-list": "^1.0.0",
"side-channel-map": "^1.0.1",
"side-channel-weakmap": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/side-channel-list": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
"integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"object-inspect": "^1.13.3"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/side-channel-map": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
"integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
"license": "MIT",
"dependencies": {
"call-bound": "^1.0.2",
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.5",
"object-inspect": "^1.13.3"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/side-channel-weakmap": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
"integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
"license": "MIT",
"dependencies": {
"call-bound": "^1.0.2",
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.5",
"object-inspect": "^1.13.3",
"side-channel-map": "^1.0.1"
},
"engines": {
"node": ">= 0.4"

View File

@@ -40,6 +40,7 @@
},
"dependencies": {
"@apollo/server": "^4.12.2",
"moleculer-apollo-server": "^0.4.0",
"@bluelovers/fast-glob": "https://github.com/rishighan/fast-glob-v2-api.git",
"@elastic/elasticsearch": "^8.13.1",
"@jorgeferrero/stream-to-buffer": "^2.0.6",