@ncukondo/reference-manager 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +267 -109
- package/dist/chunks/file-watcher-B-SiUw5f.js +1557 -0
- package/dist/chunks/file-watcher-B-SiUw5f.js.map +1 -0
- package/dist/chunks/{search-Be9vzUIH.js → index-DLIGxQaB.js} +399 -89
- package/dist/chunks/index-DLIGxQaB.js.map +1 -0
- package/dist/chunks/loader-DuzyKV70.js +394 -0
- package/dist/chunks/loader-DuzyKV70.js.map +1 -0
- package/dist/cli/commands/add.d.ts +3 -3
- package/dist/cli/commands/add.d.ts.map +1 -1
- package/dist/cli/commands/cite.d.ts +3 -3
- package/dist/cli/commands/cite.d.ts.map +1 -1
- package/dist/cli/commands/fulltext.d.ts +5 -34
- package/dist/cli/commands/fulltext.d.ts.map +1 -1
- package/dist/cli/commands/list.d.ts +3 -3
- package/dist/cli/commands/list.d.ts.map +1 -1
- package/dist/cli/commands/mcp.d.ts +16 -0
- package/dist/cli/commands/mcp.d.ts.map +1 -0
- package/dist/cli/commands/remove.d.ts +3 -3
- package/dist/cli/commands/remove.d.ts.map +1 -1
- package/dist/cli/commands/search.d.ts +3 -3
- package/dist/cli/commands/search.d.ts.map +1 -1
- package/dist/cli/commands/server.d.ts +2 -0
- package/dist/cli/commands/server.d.ts.map +1 -1
- package/dist/cli/commands/update.d.ts +3 -3
- package/dist/cli/commands/update.d.ts.map +1 -1
- package/dist/cli/execution-context.d.ts +23 -36
- package/dist/cli/execution-context.d.ts.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/server-client.d.ts +24 -40
- package/dist/cli/server-client.d.ts.map +1 -1
- package/dist/cli/server-detection.d.ts +1 -0
- package/dist/cli/server-detection.d.ts.map +1 -1
- package/dist/cli.js +21060 -317
- package/dist/cli.js.map +1 -1
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/schema.d.ts +2 -3
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/core/csl-json/types.d.ts +3 -0
- package/dist/core/csl-json/types.d.ts.map +1 -1
- package/dist/core/library-interface.d.ts +100 -0
- package/dist/core/library-interface.d.ts.map +1 -0
- package/dist/core/library.d.ts +29 -46
- package/dist/core/library.d.ts.map +1 -1
- package/dist/features/operations/add.d.ts +2 -2
- package/dist/features/operations/add.d.ts.map +1 -1
- package/dist/features/operations/cite.d.ts +2 -2
- package/dist/features/operations/cite.d.ts.map +1 -1
- package/dist/features/operations/fulltext/attach.d.ts +47 -0
- package/dist/features/operations/fulltext/attach.d.ts.map +1 -0
- package/dist/features/operations/fulltext/detach.d.ts +38 -0
- package/dist/features/operations/fulltext/detach.d.ts.map +1 -0
- package/dist/features/operations/fulltext/get.d.ts +41 -0
- package/dist/features/operations/fulltext/get.d.ts.map +1 -0
- package/dist/features/operations/fulltext/index.d.ts +9 -0
- package/dist/features/operations/fulltext/index.d.ts.map +1 -0
- package/dist/features/operations/index.d.ts +15 -0
- package/dist/features/operations/index.d.ts.map +1 -0
- package/dist/features/operations/library-operations.d.ts +64 -0
- package/dist/features/operations/library-operations.d.ts.map +1 -0
- package/dist/features/operations/list.d.ts +2 -2
- package/dist/features/operations/list.d.ts.map +1 -1
- package/dist/features/operations/operations-library.d.ts +36 -0
- package/dist/features/operations/operations-library.d.ts.map +1 -0
- package/dist/features/operations/remove.d.ts +4 -4
- package/dist/features/operations/remove.d.ts.map +1 -1
- package/dist/features/operations/search.d.ts +2 -2
- package/dist/features/operations/search.d.ts.map +1 -1
- package/dist/features/operations/update.d.ts +2 -2
- package/dist/features/operations/update.d.ts.map +1 -1
- package/dist/features/search/matcher.d.ts.map +1 -1
- package/dist/features/search/normalizer.d.ts +12 -0
- package/dist/features/search/normalizer.d.ts.map +1 -1
- package/dist/features/search/tokenizer.d.ts.map +1 -1
- package/dist/features/search/types.d.ts +1 -1
- package/dist/features/search/types.d.ts.map +1 -1
- package/dist/features/search/uppercase.d.ts +41 -0
- package/dist/features/search/uppercase.d.ts.map +1 -0
- package/dist/index.js +24 -192
- package/dist/index.js.map +1 -1
- package/dist/mcp/context.d.ts +19 -0
- package/dist/mcp/context.d.ts.map +1 -0
- package/dist/mcp/index.d.ts +20 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/resources/index.d.ts +10 -0
- package/dist/mcp/resources/index.d.ts.map +1 -0
- package/dist/mcp/resources/library.d.ts +26 -0
- package/dist/mcp/resources/library.d.ts.map +1 -0
- package/dist/mcp/tools/add.d.ts +17 -0
- package/dist/mcp/tools/add.d.ts.map +1 -0
- package/dist/mcp/tools/cite.d.ts +15 -0
- package/dist/mcp/tools/cite.d.ts.map +1 -0
- package/dist/mcp/tools/fulltext.d.ts +51 -0
- package/dist/mcp/tools/fulltext.d.ts.map +1 -0
- package/dist/mcp/tools/index.d.ts +12 -0
- package/dist/mcp/tools/index.d.ts.map +1 -0
- package/dist/mcp/tools/list.d.ts +13 -0
- package/dist/mcp/tools/list.d.ts.map +1 -0
- package/dist/mcp/tools/remove.d.ts +19 -0
- package/dist/mcp/tools/remove.d.ts.map +1 -0
- package/dist/mcp/tools/search.d.ts +13 -0
- package/dist/mcp/tools/search.d.ts.map +1 -0
- package/dist/server/index.d.ts +23 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/routes/references.d.ts.map +1 -1
- package/dist/server.js +5 -271
- package/dist/server.js.map +1 -1
- package/package.json +2 -1
- package/dist/chunks/detector-DHztTaFY.js +0 -619
- package/dist/chunks/detector-DHztTaFY.js.map +0 -1
- package/dist/chunks/loader-mQ25o6cV.js +0 -1054
- package/dist/chunks/loader-mQ25o6cV.js.map +0 -1
- package/dist/chunks/search-Be9vzUIH.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"detector-DHztTaFY.js","sources":["../../src/core/csl-json/types.ts","../../src/features/search/tokenizer.ts","../../src/features/search/normalizer.ts","../../src/features/search/matcher.ts","../../src/features/search/sorter.ts","../../src/features/duplicate/detector.ts"],"sourcesContent":["import { z } from \"zod\";\n\n// CSL-JSON Name (Person)\nconst CslNameSchema = z.object({\n family: z.string().optional(),\n given: z.string().optional(),\n literal: z.string().optional(),\n \"dropping-particle\": z.string().optional(),\n \"non-dropping-particle\": z.string().optional(),\n suffix: z.string().optional(),\n});\n\n// CSL-JSON Date\nconst CslDateSchema = z.object({\n \"date-parts\": z.array(z.array(z.number())).optional(),\n raw: z.string().optional(),\n season: z.string().optional(),\n circa: z.boolean().optional(),\n literal: z.string().optional(),\n});\n\n// CSL-JSON Fulltext Metadata\nconst CslFulltextSchema = z.object({\n pdf: z.string().optional(),\n markdown: z.string().optional(),\n});\n\n// CSL-JSON Custom Metadata\nconst CslCustomSchema = z\n .object({\n uuid: z.string(),\n created_at: z.string(),\n timestamp: z.string(),\n additional_urls: z.array(z.string()).optional(),\n fulltext: CslFulltextSchema.optional(),\n })\n .passthrough();\n\n// CSL-JSON Item\nexport const CslItemSchema = z\n .object({\n id: z.string(),\n type: z.string(),\n title: z.string().optional(),\n author: z.array(CslNameSchema).optional(),\n editor: z.array(CslNameSchema).optional(),\n issued: CslDateSchema.optional(),\n accessed: CslDateSchema.optional(),\n \"container-title\": z.string().optional(),\n volume: z.string().optional(),\n issue: z.string().optional(),\n page: z.string().optional(),\n DOI: z.string().optional(),\n PMID: z.string().optional(),\n PMCID: z.string().optional(),\n ISBN: z.string().optional(),\n ISSN: z.string().optional(),\n URL: z.string().optional(),\n abstract: z.string().optional(),\n publisher: z.string().optional(),\n \"publisher-place\": z.string().optional(),\n note: z.string().optional(),\n keyword: z.array(z.string()).optional(),\n custom: CslCustomSchema.optional(),\n // Allow additional fields\n })\n .passthrough();\n\n// CSL-JSON Library (array of items)\nexport const CslLibrarySchema = z.array(CslItemSchema);\n\nexport type CslCustom = z.infer<typeof CslCustomSchema>;\nexport type CslItem = z.infer<typeof CslItemSchema>;\nexport type CslLibrary = z.infer<typeof CslLibrarySchema>;\n","import type { FieldSpecifier, SearchQuery, SearchToken } from \"./types.js\";\n\nconst VALID_FIELDS: Set<FieldSpecifier> = new Set([\n \"author\",\n \"title\",\n \"year\",\n \"doi\",\n \"pmid\",\n \"pmcid\",\n \"url\",\n \"keyword\",\n]);\n\n/**\n * Check if character at index is whitespace\n */\nfunction isWhitespace(query: string, index: number): boolean {\n return /\\s/.test(query.charAt(index));\n}\n\n/**\n * Check if character at index is a quote\n */\nfunction isQuote(query: string, index: number): boolean {\n return query.charAt(index) === '\"';\n}\n\n/**\n * Tokenize a search query string\n */\nexport function tokenize(query: string): SearchQuery {\n const tokens: SearchToken[] = [];\n let i = 0;\n\n while (i < query.length) {\n // Skip whitespace\n if (isWhitespace(query, i)) {\n i++;\n continue;\n }\n\n // Parse next token\n const result = parseNextToken(query, i);\n if (result.token) {\n tokens.push(result.token);\n }\n i = result.nextIndex;\n }\n\n return {\n original: query,\n tokens,\n };\n}\n\ntype TokenResult = { token: SearchToken | null; nextIndex: number };\n\n/**\n * Check if there's whitespace between two indices\n */\nfunction hasWhitespaceBetween(query: string, start: number, end: number): boolean {\n for (let j = start; j < end; j++) {\n if (isWhitespace(query, j)) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Try to parse a field:value pattern starting at the given index\n * Returns null if not a valid field:value pattern\n */\nfunction tryParseFieldValue(query: string, startIndex: number): TokenResult | null {\n const colonIndex = query.indexOf(\":\", startIndex);\n if (colonIndex === -1) {\n return null;\n }\n\n // Check if there's whitespace before colon (invalid field pattern)\n if (hasWhitespaceBetween(query, startIndex, colonIndex)) {\n return null;\n }\n\n const fieldName = query.substring(startIndex, colonIndex);\n if (!VALID_FIELDS.has(fieldName as FieldSpecifier)) {\n return null;\n }\n\n // Valid field specifier found\n const afterColon = colonIndex + 1;\n\n // Check if value is empty\n if (afterColon >= query.length || isWhitespace(query, afterColon)) {\n return { token: null, nextIndex: afterColon };\n }\n\n // Check if value is a quoted phrase\n if (isQuote(query, afterColon)) {\n const quoteResult = parseQuotedValue(query, afterColon);\n if (quoteResult.value !== null) {\n return {\n token: {\n raw: query.substring(startIndex, quoteResult.nextIndex),\n value: quoteResult.value,\n field: fieldName as FieldSpecifier,\n isPhrase: true,\n },\n nextIndex: quoteResult.nextIndex,\n };\n }\n // If quote parsing failed, return null to try other parsing\n return null;\n }\n\n // Regular unquoted value\n const valueResult = parseUnquotedValue(query, afterColon);\n return {\n token: {\n raw: query.substring(startIndex, valueResult.nextIndex),\n value: valueResult.value,\n field: fieldName as FieldSpecifier,\n isPhrase: false,\n },\n nextIndex: valueResult.nextIndex,\n };\n}\n\n/**\n * Parse a quoted token (phrase without field specifier)\n */\nfunction parseQuotedToken(query: string, startIndex: number): TokenResult {\n const quoteResult = parseQuotedValue(query, startIndex);\n if (quoteResult.value !== null) {\n return {\n token: {\n raw: query.substring(startIndex, quoteResult.nextIndex),\n value: quoteResult.value,\n isPhrase: true,\n },\n nextIndex: quoteResult.nextIndex,\n };\n }\n\n // If quote parsing failed (empty or unclosed), skip it\n if (quoteResult.nextIndex > startIndex) {\n // Empty quote - skip it\n return { token: null, nextIndex: quoteResult.nextIndex };\n }\n\n // Unclosed quote - treat as regular text including the quote character\n const valueResult = parseUnquotedValue(query, startIndex, true);\n return {\n token: {\n raw: valueResult.value,\n value: valueResult.value,\n isPhrase: false,\n },\n nextIndex: valueResult.nextIndex,\n };\n}\n\n/**\n * Parse a regular unquoted token\n */\nfunction parseRegularToken(query: string, startIndex: number): TokenResult {\n const valueResult = parseUnquotedValue(query, startIndex);\n return {\n token: {\n raw: valueResult.value,\n value: valueResult.value,\n isPhrase: false,\n },\n nextIndex: valueResult.nextIndex,\n };\n}\n\n/**\n * Parse the next token starting at the given index\n */\nfunction parseNextToken(query: string, startIndex: number): TokenResult {\n // Try to parse field:value pattern first\n const fieldResult = tryParseFieldValue(query, startIndex);\n if (fieldResult !== null) {\n return fieldResult;\n }\n\n // Check if it's a quoted phrase\n if (isQuote(query, startIndex)) {\n return parseQuotedToken(query, startIndex);\n }\n\n // Regular unquoted token\n return parseRegularToken(query, startIndex);\n}\n\n/**\n * Parse a quoted value starting at a quote character\n */\nfunction parseQuotedValue(\n query: string,\n startIndex: number\n): { value: string | null; nextIndex: number } {\n if (!isQuote(query, startIndex)) {\n return { value: null, nextIndex: startIndex };\n }\n\n let i = startIndex + 1; // Skip opening quote\n const valueStart = i;\n\n // Find closing quote\n while (i < query.length && !isQuote(query, i)) {\n i++;\n }\n\n // No closing quote found\n if (i >= query.length) {\n return { value: null, nextIndex: startIndex };\n }\n\n const value = query.substring(valueStart, i);\n i++; // Skip closing quote\n\n // Return null for empty quotes\n if (value.trim() === \"\") {\n return { value: null, nextIndex: i };\n }\n\n return { value, nextIndex: i };\n}\n\n/**\n * Parse an unquoted value\n * @param includeQuotes - If true, don't stop at quote characters (for unclosed quotes)\n */\nfunction parseUnquotedValue(\n query: string,\n startIndex: number,\n includeQuotes = false\n): { value: string; nextIndex: number } {\n let i = startIndex;\n\n // Read until whitespace (and optionally until quote)\n while (i < query.length && !isWhitespace(query, i)) {\n if (!includeQuotes && isQuote(query, i)) {\n break;\n }\n i++;\n }\n\n return {\n value: query.substring(startIndex, i),\n nextIndex: i,\n };\n}\n","/**\n * Normalize text for search matching\n *\n * Applies the following transformations:\n * 1. Unicode NFKC normalization\n * 2. Lowercase conversion\n * 3. Remove diacritics (accents)\n * 4. Punctuation removal\n * 5. Whitespace normalization\n */\nexport function normalize(text: string): string {\n // Step 1: Unicode NFKC normalization (compatibility normalization)\n let normalized = text.normalize(\"NFKC\");\n\n // Step 2: Lowercase\n normalized = normalized.toLowerCase();\n\n // Step 3: Remove diacritics\n // Use NFD to decompose, then remove combining diacritical marks\n normalized = normalized.normalize(\"NFD\").replace(/\\p{M}/gu, \"\");\n\n // Step 4: Remove punctuation\n // Replace all punctuation and special characters with spaces\n // Keep: letters (including Unicode), numbers, slashes, and whitespace\n normalized = normalized.replace(/[^\\p{L}\\p{N}/\\s]/gu, \" \");\n\n // Step 5: Normalize whitespace\n // - Replace all whitespace sequences (spaces, tabs, newlines) with a single space\n // - Trim leading and trailing whitespace\n normalized = normalized.replace(/\\s+/g, \" \").trim();\n\n return normalized;\n}\n","import type { CslItem } from \"../../core/csl-json/types.js\";\nimport { normalize } from \"./normalizer.js\";\nimport type { FieldMatch, MatchStrength, SearchResult, SearchToken } from \"./types.js\";\n\n/**\n * ID fields require exact match (case-sensitive)\n */\nconst ID_FIELDS = new Set([\"DOI\", \"PMID\", \"PMCID\", \"URL\"]);\n\n/**\n * Extract year from CSL-JSON issued field\n */\nfunction extractYear(reference: CslItem): string {\n if (reference.issued?.[\"date-parts\"]?.[0]?.[0]) {\n return String(reference.issued[\"date-parts\"][0][0]);\n }\n return \"0000\";\n}\n\n/**\n * Extract and format author names\n * Returns \"family given-initial\" format for all authors\n */\nfunction extractAuthors(reference: CslItem): string {\n if (!reference.author || reference.author.length === 0) {\n return \"\";\n }\n\n return reference.author\n .map((author) => {\n const family = author.family || \"\";\n const givenInitial = author.given ? author.given[0] : \"\";\n return givenInitial ? `${family} ${givenInitial}` : family;\n })\n .join(\" \");\n}\n\n/**\n * Get field value from reference\n */\nfunction getFieldValue(reference: CslItem, field: string): string | null {\n // Handle special fields\n if (field === \"year\") {\n return extractYear(reference);\n }\n\n if (field === \"author\") {\n return extractAuthors(reference);\n }\n\n // Handle direct field access\n const value = reference[field as keyof CslItem];\n if (typeof value === \"string\") {\n return value;\n }\n\n // Handle nested custom fields\n if (field.startsWith(\"custom.\")) {\n const customField = field.substring(7); // Remove \"custom.\" prefix\n const customValue = (reference.custom as Record<string, unknown>)?.[customField];\n if (typeof customValue === \"string\") {\n return customValue;\n }\n }\n\n return null;\n}\n\n/**\n * Check if URL matches in primary URL or additional_urls array\n */\nfunction matchUrl(queryValue: string, reference: CslItem): FieldMatch | null {\n // Check primary URL field\n if (reference.URL === queryValue) {\n return {\n field: \"URL\",\n strength: \"exact\",\n value: reference.URL,\n };\n }\n\n // Check additional_urls in custom field\n const additionalUrls = (reference.custom as Record<string, unknown>)?.additional_urls;\n if (Array.isArray(additionalUrls)) {\n for (const url of additionalUrls) {\n if (typeof url === \"string\" && url === queryValue) {\n return {\n field: \"custom.additional_urls\",\n strength: \"exact\",\n value: url,\n };\n }\n }\n }\n\n return null;\n}\n\n/**\n * Check if query matches any keyword in the keyword array\n * Performs partial match with normalization on each keyword element\n */\nfunction matchKeyword(queryValue: string, reference: CslItem): FieldMatch | null {\n // Check if keyword field exists and is an array\n if (!reference.keyword || !Array.isArray(reference.keyword)) {\n return null;\n }\n\n // Normalize query value\n const normalizedQuery = normalize(queryValue);\n\n // Search through each keyword element\n for (const keyword of reference.keyword) {\n if (typeof keyword === \"string\") {\n const normalizedKeyword = normalize(keyword);\n if (normalizedKeyword.includes(normalizedQuery)) {\n return {\n field: \"keyword\",\n strength: \"partial\",\n value: keyword,\n };\n }\n }\n }\n\n return null;\n}\n\n/**\n * Map field specifier to actual CSL-JSON field name\n */\nconst FIELD_MAP: Record<string, string> = {\n author: \"author\",\n title: \"title\",\n doi: \"DOI\",\n pmid: \"PMID\",\n pmcid: \"PMCID\",\n};\n\n/**\n * Match a year field against a reference\n */\nfunction matchYearField(tokenValue: string, reference: CslItem): FieldMatch | null {\n const year = extractYear(reference);\n if (year === tokenValue) {\n return {\n field: \"year\",\n strength: \"exact\",\n value: year,\n };\n }\n return null;\n}\n\n/**\n * Match a content or ID field against a reference\n */\nfunction matchFieldValue(field: string, tokenValue: string, reference: CslItem): FieldMatch | null {\n const fieldValue = getFieldValue(reference, field);\n if (fieldValue === null) {\n return null;\n }\n\n // Check if this is an ID field (exact match, case-sensitive)\n if (ID_FIELDS.has(field)) {\n if (fieldValue === tokenValue) {\n return {\n field,\n strength: \"exact\",\n value: fieldValue,\n };\n }\n return null;\n }\n\n // Content field: partial match, case-insensitive with normalization\n const normalizedFieldValue = normalize(fieldValue);\n const normalizedQuery = normalize(tokenValue);\n\n if (normalizedFieldValue.includes(normalizedQuery)) {\n return {\n field,\n strength: \"partial\",\n value: fieldValue,\n };\n }\n return null;\n}\n\n/**\n * Match token against a specific field\n */\nfunction matchSpecificField(token: SearchToken, reference: CslItem): FieldMatch[] {\n const matches: FieldMatch[] = [];\n const fieldToSearch = token.field as string;\n\n // Handle URL field specially (search both URL and additional_urls)\n if (fieldToSearch === \"url\") {\n const urlMatch = matchUrl(token.value, reference);\n if (urlMatch) matches.push(urlMatch);\n return matches;\n }\n\n // Handle year field\n if (fieldToSearch === \"year\") {\n const yearMatch = matchYearField(token.value, reference);\n if (yearMatch) matches.push(yearMatch);\n return matches;\n }\n\n // Handle keyword field specially (search array elements)\n if (fieldToSearch === \"keyword\") {\n const keywordMatch = matchKeyword(token.value, reference);\n if (keywordMatch) matches.push(keywordMatch);\n return matches;\n }\n\n // Standard field matching\n const actualField = FIELD_MAP[fieldToSearch] || fieldToSearch;\n const match = matchFieldValue(actualField, token.value, reference);\n if (match) matches.push(match);\n\n return matches;\n}\n\n/**\n * Standard fields to search (not special-cased)\n */\nconst STANDARD_SEARCH_FIELDS = [\n \"title\",\n \"author\",\n \"container-title\",\n \"publisher\",\n \"DOI\",\n \"PMID\",\n \"PMCID\",\n \"abstract\",\n];\n\n/**\n * Match token against a single field (used for all-fields search)\n */\nfunction matchSingleField(\n field: string,\n tokenValue: string,\n reference: CslItem\n): FieldMatch | null {\n if (field === \"year\") {\n return matchYearField(tokenValue, reference);\n }\n if (field === \"URL\") {\n return matchUrl(tokenValue, reference);\n }\n if (field === \"keyword\") {\n return matchKeyword(tokenValue, reference);\n }\n return matchFieldValue(field, tokenValue, reference);\n}\n\n/**\n * Match token against all searchable fields\n */\nfunction matchAllFields(token: SearchToken, reference: CslItem): FieldMatch[] {\n const matches: FieldMatch[] = [];\n\n // Match special fields\n const specialFields = [\"year\", \"URL\", \"keyword\"];\n for (const field of specialFields) {\n const match = matchSingleField(field, token.value, reference);\n if (match) matches.push(match);\n }\n\n // Match standard fields\n for (const field of STANDARD_SEARCH_FIELDS) {\n const match = matchFieldValue(field, token.value, reference);\n if (match) matches.push(match);\n }\n\n return matches;\n}\n\n/**\n * Match a single token against a reference\n * Returns an array of field matches\n */\nexport function matchToken(token: SearchToken, reference: CslItem): FieldMatch[] {\n // If field is specified, only search that field\n if (token.field) {\n return matchSpecificField(token, reference);\n }\n\n // No field specified: search all fields\n return matchAllFields(token, reference);\n}\n\n/**\n * Match a reference against all search tokens\n * Returns a SearchResult if all tokens match (AND logic), null otherwise\n */\nexport function matchReference(reference: CslItem, tokens: SearchToken[]): SearchResult | null {\n // Empty token array means no match\n if (tokens.length === 0) {\n return null;\n }\n\n const tokenMatches: SearchResult[\"tokenMatches\"] = [];\n let overallStrength: MatchStrength = \"none\";\n\n // Check if all tokens match (AND logic)\n for (const token of tokens) {\n const matches = matchToken(token, reference);\n\n // If any token doesn't match at least one field, no match\n if (matches.length === 0) {\n return null;\n }\n\n // Determine highest match strength for this token\n const tokenStrength = matches.some((m) => m.strength === \"exact\") ? \"exact\" : \"partial\";\n\n // Update overall strength (exact > partial > none)\n if (tokenStrength === \"exact\") {\n overallStrength = \"exact\";\n } else if (tokenStrength === \"partial\" && overallStrength === \"none\") {\n overallStrength = \"partial\";\n }\n\n tokenMatches.push({\n token,\n matches,\n });\n }\n\n // Calculate score (higher is better)\n // Exact matches get higher score than partial matches\n const score = overallStrength === \"exact\" ? 100 + tokenMatches.length : 50 + tokenMatches.length;\n\n return {\n reference,\n tokenMatches,\n overallStrength,\n score,\n };\n}\n\n/**\n * Search references against search tokens\n * Returns array of SearchResult for all matching references\n */\nexport function search(references: CslItem[], tokens: SearchToken[]): SearchResult[] {\n const results: SearchResult[] = [];\n\n for (const reference of references) {\n const match = matchReference(reference, tokens);\n if (match) {\n results.push(match);\n }\n }\n\n return results;\n}\n","import type { CslItem } from \"../../core/csl-json/types.js\";\nimport type { MatchStrength, SearchResult } from \"./types.js\";\n\n/**\n * Extract year from CSL-JSON reference\n * Returns \"0000\" for missing year (sorted last)\n */\nfunction extractYear(reference: CslItem): string {\n if (reference.issued?.[\"date-parts\"]?.[0]?.[0]) {\n return String(reference.issued[\"date-parts\"][0][0]);\n }\n return \"0000\";\n}\n\n/**\n * Extract first author's family name for sorting\n * Returns empty string for missing author (sorted last)\n */\nfunction extractFirstAuthorFamily(reference: CslItem): string {\n if (!reference.author || reference.author.length === 0) {\n return \"\";\n }\n return reference.author[0]?.family || \"\";\n}\n\n/**\n * Extract title for sorting\n * Returns empty string for missing title (sorted last)\n */\nfunction extractTitle(reference: CslItem): string {\n return reference.title || \"\";\n}\n\n/**\n * Compare match strength (exact > partial > none)\n * Returns negative if a < b, positive if a > b, 0 if equal\n */\nfunction compareStrength(a: MatchStrength, b: MatchStrength): number {\n const strengthOrder = { exact: 2, partial: 1, none: 0 };\n return strengthOrder[b] - strengthOrder[a];\n}\n\n/**\n * Compare years (descending - newer first)\n * Returns negative if a < b, positive if a > b, 0 if equal\n */\nfunction compareYear(a: CslItem, b: CslItem): number {\n const yearA = extractYear(a);\n const yearB = extractYear(b);\n return Number(yearB) - Number(yearA);\n}\n\n/**\n * Compare authors alphabetically (empty comes last)\n * Returns negative if a < b, positive if a > b, 0 if equal\n */\nfunction compareAuthor(a: CslItem, b: CslItem): number {\n const authorA = extractFirstAuthorFamily(a).toLowerCase();\n const authorB = extractFirstAuthorFamily(b).toLowerCase();\n // Empty string (no author) should come after authors\n if (authorA === \"\" && authorB !== \"\") return 1;\n if (authorA !== \"\" && authorB === \"\") return -1;\n return authorA.localeCompare(authorB);\n}\n\n/**\n * Compare titles alphabetically (empty comes last)\n * Returns negative if a < b, positive if a > b, 0 if equal\n */\nfunction compareTitle(a: CslItem, b: CslItem): number {\n const titleA = extractTitle(a).toLowerCase();\n const titleB = extractTitle(b).toLowerCase();\n // Empty string (no title) should come after titles\n if (titleA === \"\" && titleB !== \"\") return 1;\n if (titleA !== \"\" && titleB === \"\") return -1;\n return titleA.localeCompare(titleB);\n}\n\n/**\n * Sort search results according to the specification:\n * 1. Match strength (exact > partial)\n * 2. Year (descending)\n * 3. Author (alphabetical)\n * 4. Title (alphabetical)\n * 5. Registration order (original array order)\n */\nexport function sortResults(results: SearchResult[]): SearchResult[] {\n // Create a copy with original indices for stable sort\n const indexed = results.map((result, index) => ({ result, index }));\n\n // Sort according to the criteria\n const sorted = indexed.sort((a, b) => {\n // 1. Match strength (exact > partial)\n const strengthDiff = compareStrength(a.result.overallStrength, b.result.overallStrength);\n if (strengthDiff !== 0) return strengthDiff;\n\n // 2. Year (descending - newer first)\n const yearDiff = compareYear(a.result.reference, b.result.reference);\n if (yearDiff !== 0) return yearDiff;\n\n // 3. Author (alphabetical)\n const authorDiff = compareAuthor(a.result.reference, b.result.reference);\n if (authorDiff !== 0) return authorDiff;\n\n // 4. Title (alphabetical, case-insensitive)\n const titleDiff = compareTitle(a.result.reference, b.result.reference);\n if (titleDiff !== 0) return titleDiff;\n\n // 5. Registration order (original array order)\n return a.index - b.index;\n });\n\n // Return only the results (without indices)\n return sorted.map((item) => item.result);\n}\n","/**\n * Duplicate detection logic\n */\n\nimport type { CslItem } from \"../../core/csl-json/types.js\";\nimport { normalize } from \"../search/normalizer.js\";\nimport type { DuplicateMatch, DuplicateResult } from \"./types.js\";\n\n/**\n * Normalize DOI by removing common URL prefixes\n * Returns the DOI in format: 10.xxxx/...\n */\nfunction normalizeDoi(doi: string): string {\n // Remove common DOI URL prefixes\n const normalized = doi\n .replace(/^https?:\\/\\/doi\\.org\\//i, \"\")\n .replace(/^https?:\\/\\/dx\\.doi\\.org\\//i, \"\")\n .replace(/^doi:/i, \"\");\n\n return normalized;\n}\n\n/**\n * Extract year from CSL-JSON issued field\n */\nfunction extractYear(item: CslItem): string | null {\n const dateParts = item.issued?.[\"date-parts\"]?.[0];\n if (!dateParts || dateParts.length === 0) {\n return null;\n }\n return String(dateParts[0]);\n}\n\n/**\n * Normalize author names to \"family given-initial\" format\n */\nfunction normalizeAuthors(item: CslItem): string | null {\n if (!item.author || item.author.length === 0) {\n return null;\n }\n\n // Combine all authors: \"family given-initial\"\n const authorStrings = item.author.map((author) => {\n const family = author.family || \"\";\n const givenInitial = author.given ? author.given.charAt(0) : \"\";\n return `${family} ${givenInitial}`.trim();\n });\n\n // Join and normalize\n return normalize(authorStrings.join(\" \"));\n}\n\n/**\n * Check if two items match by DOI\n */\nfunction checkDoiMatch(item: CslItem, existing: CslItem): DuplicateMatch | null {\n if (!item.DOI || !existing.DOI) {\n return null;\n }\n\n const normalizedItemDoi = normalizeDoi(item.DOI);\n const normalizedExistingDoi = normalizeDoi(existing.DOI);\n\n // DOI comparison is case-sensitive\n if (normalizedItemDoi === normalizedExistingDoi) {\n return {\n type: \"doi\",\n existing,\n details: {\n doi: normalizedExistingDoi,\n },\n };\n }\n\n return null;\n}\n\n/**\n * Check if two items match by PMID\n */\nfunction checkPmidMatch(item: CslItem, existing: CslItem): DuplicateMatch | null {\n if (!item.PMID || !existing.PMID) {\n return null;\n }\n\n // PMID comparison is exact string match\n if (item.PMID === existing.PMID) {\n return {\n type: \"pmid\",\n existing,\n details: {\n pmid: existing.PMID,\n },\n };\n }\n\n return null;\n}\n\n/**\n * Check if two items match by Title + Author + Year\n */\nfunction checkTitleAuthorYearMatch(item: CslItem, existing: CslItem): DuplicateMatch | null {\n const itemTitle = item.title ? normalize(item.title) : null;\n const existingTitle = existing.title ? normalize(existing.title) : null;\n const itemAuthors = normalizeAuthors(item);\n const existingAuthors = normalizeAuthors(existing);\n const itemYear = extractYear(item);\n const existingYear = extractYear(existing);\n\n // All three must be present and match\n if (\n !itemTitle ||\n !existingTitle ||\n !itemAuthors ||\n !existingAuthors ||\n !itemYear ||\n !existingYear\n ) {\n return null;\n }\n\n if (itemTitle === existingTitle && itemAuthors === existingAuthors && itemYear === existingYear) {\n return {\n type: \"title-author-year\",\n existing,\n details: {\n normalizedTitle: existingTitle,\n normalizedAuthors: existingAuthors,\n year: existingYear,\n },\n };\n }\n\n return null;\n}\n\n/**\n * Check if an item is a duplicate of an existing item\n * Returns the first match found (highest priority)\n */\nfunction checkSingleDuplicate(item: CslItem, existing: CslItem): DuplicateMatch | null {\n // Priority 1: DOI matching (highest priority)\n const doiMatch = checkDoiMatch(item, existing);\n if (doiMatch) {\n return doiMatch;\n }\n\n // Priority 2: PMID matching\n const pmidMatch = checkPmidMatch(item, existing);\n if (pmidMatch) {\n return pmidMatch;\n }\n\n // Priority 3: Title + Author + Year matching (lowest priority)\n return checkTitleAuthorYearMatch(item, existing);\n}\n\n/**\n * Detects if a reference is a duplicate of any existing references\n *\n * Priority order:\n * 1. DOI (highest priority)\n * 2. PMID\n * 3. Title + Author + Year (lowest priority)\n *\n * @param item - The reference to check for duplicates\n * @param existingReferences - Array of existing references to check against\n * @returns DuplicateResult indicating if duplicate found and match details\n */\nexport function detectDuplicate(item: CslItem, existingReferences: CslItem[]): DuplicateResult {\n const matches: DuplicateMatch[] = [];\n const itemUuid = item.custom?.uuid;\n\n for (const existing of existingReferences) {\n // Skip if same UUID (same item)\n if (itemUuid && existing.custom?.uuid === itemUuid) {\n continue;\n }\n\n const match = checkSingleDuplicate(item, existing);\n if (match) {\n matches.push(match);\n }\n }\n\n return {\n isDuplicate: matches.length > 0,\n matches,\n };\n}\n"],"names":["extractYear"],"mappings":"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"}
|