From f4f881e839392396caf81d0c20d7144c40d9b7f8 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sun, 5 Apr 2026 19:58:48 +0300 Subject: [PATCH 1/8] fix(ocr): not working in server prod --- apps/server/scripts/build.ts | 2 +- scripts/build-utils.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/server/scripts/build.ts b/apps/server/scripts/build.ts index 9fa1f9cb83..e8a27f66c3 100644 --- a/apps/server/scripts/build.ts +++ b/apps/server/scripts/build.ts @@ -11,7 +11,7 @@ async function main() { build.copy("/packages/share-theme/src/templates", "share-theme/templates/"); // Copy node modules dependencies - build.copyNodeModules([ "better-sqlite3", "bindings", "file-uri-to-path" ]); + build.copyNodeModules([ "better-sqlite3", "bindings", "file-uri-to-path", "tesseract.js" ]); build.copy("/node_modules/ckeditor5/dist/ckeditor5-content.css", "ckeditor5-content.css"); build.buildFrontend(); diff --git a/scripts/build-utils.ts b/scripts/build-utils.ts index 07a93a256e..7349cecb2b 100644 --- a/scripts/build-utils.ts +++ b/scripts/build-utils.ts @@ -53,7 +53,8 @@ export default class BuildHelper { "better-sqlite3", "pdfjs-dist", "./xhr-sync-worker.js", - "vite" + "vite", + "tesseract.js" ], metafile: true, splitting: false, From 94987314b8d6588a320bb127ced9981d7214aec6 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sun, 5 Apr 2026 20:03:12 +0300 Subject: [PATCH 2/8] feat(ocr): warn about OCR confidence too low --- .../src/translations/en/translation.json | 2 ++ .../ReadOnlyTextRepresentation.tsx | 25 ++++++++++++++++--- apps/server/src/routes/api/ocr.ts | 24 ++++++++++++++---- packages/commons/src/lib/server_api.ts | 14 +++++++++++ 4 files changed, 56 insertions(+), 9 deletions(-) diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index ce49038fb5..f7ff29ae0c 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -2093,7 +2093,9 @@ "process_now": "Process OCR", "processing": "Processing...", "processing_started": "OCR processing has been started. Please wait a moment and refresh.", + "processing_complete": "OCR processing complete.", "processing_failed": "Failed to start OCR processing", + "text_filtered_low_confidence": "OCR detected text with {{confidence}}% confidence, but it was discarded because your minimum threshold is {{threshold}}%.\n\nYou can adjust the threshold in Options → Media.", "view_extracted_text": "View extracted text (OCR)" }, "command_palette": { diff --git a/apps/client/src/widgets/type_widgets/ReadOnlyTextRepresentation.tsx b/apps/client/src/widgets/type_widgets/ReadOnlyTextRepresentation.tsx index 9ee376f977..c8f4340c32 100644 --- a/apps/client/src/widgets/type_widgets/ReadOnlyTextRepresentation.tsx +++ b/apps/client/src/widgets/type_widgets/ReadOnlyTextRepresentation.tsx @@ -1,6 +1,6 @@ import "./ReadOnlyTextRepresentation.css"; -import type { TextRepresentationResponse } from "@triliumnext/commons"; +import type { OCRProcessResponse, TextRepresentationResponse } from "@triliumnext/commons"; import { useEffect, useState } from "preact/hooks"; import { t } from "../../services/i18n"; @@ -62,10 +62,27 @@ export function TextRepresentation({ textUrl, processUrl }: TextRepresentationPr async function processOCR() { setProcessing(true); try { - const response = await server.post<{ success: boolean; message?: string }>(processUrl, { forceReprocess: true }); + const response = await server.post(processUrl, { forceReprocess: true }); if (response.success) { - toast.showMessage(t("ocr.processing_started")); - setTimeout(fetchText, 2000); + const result = response.result; + const minConfidence = response.minConfidence ?? 0; + + // Check if text was filtered due to low confidence + if (result && !result.text && result.confidence > 0 && minConfidence > 0) { + const confidencePercent = Math.round(result.confidence * 100); + const thresholdPercent = Math.round(minConfidence * 100); + toast.showMessage( + t("ocr.text_filtered_low_confidence", { + confidence: confidencePercent, + threshold: thresholdPercent + }), + 10000, // Show for 10 seconds since this is important info + "bx bx-info-circle" + ); + } else { + toast.showMessage(t("ocr.processing_complete")); + } + setTimeout(fetchText, 500); } else { toast.showError(response.message || t("ocr.processing_failed")); } diff --git a/apps/server/src/routes/api/ocr.ts b/apps/server/src/routes/api/ocr.ts index 65618b7db8..1177634d00 100644 --- a/apps/server/src/routes/api/ocr.ts +++ b/apps/server/src/routes/api/ocr.ts @@ -1,10 +1,16 @@ -import { TextRepresentationResponse } from "@triliumnext/commons"; +import type { OCRProcessResponse, TextRepresentationResponse } from "@triliumnext/commons"; import type { Request } from "express"; import becca from "../../becca/becca.js"; import ocrService from "../../services/ocr/ocr_service.js"; +import options from "../../services/options.js"; import sql from "../../services/sql.js"; +function getMinConfidenceThreshold(): number { + const minConfidence = options.getOption('ocrMinConfidence') ?? 0; + return parseFloat(minConfidence); +} + /** * @swagger * /api/ocr/process-note/{noteId}: @@ -48,7 +54,7 @@ import sql from "../../services/sql.js"; * - session: [] * tags: ["ocr"] */ -async function processNoteOCR(req: Request<{ noteId: string }>) { +async function processNoteOCR(req: Request<{ noteId: string }>): Promise { const { noteId } = req.params; const { language, forceReprocess = false } = req.body || {}; @@ -62,7 +68,11 @@ async function processNoteOCR(req: Request<{ noteId: string }>) { return [400, { success: false, message: 'Note is not an image or has unsupported format' }]; } - return { success: true, result }; + return { + success: true, + result, + minConfidence: getMinConfidenceThreshold() + }; } /** @@ -108,7 +118,7 @@ async function processNoteOCR(req: Request<{ noteId: string }>) { * - session: [] * tags: ["ocr"] */ -async function processAttachmentOCR(req: Request<{ attachmentId: string }>) { +async function processAttachmentOCR(req: Request<{ attachmentId: string }>): Promise { const { attachmentId } = req.params; const { language, forceReprocess = false } = req.body || {}; @@ -122,7 +132,11 @@ async function processAttachmentOCR(req: Request<{ attachmentId: string }>) { return [400, { success: false, message: 'Attachment is not an image or has unsupported format' }]; } - return { success: true, result }; + return { + success: true, + result, + minConfidence: getMinConfidenceThreshold() + }; } /** diff --git a/packages/commons/src/lib/server_api.ts b/packages/commons/src/lib/server_api.ts index 48abb76404..84b16482bb 100644 --- a/packages/commons/src/lib/server_api.ts +++ b/packages/commons/src/lib/server_api.ts @@ -295,6 +295,20 @@ export interface TextRepresentationResponse { message?: string; } +export interface OCRProcessResponse { + success: boolean; + message?: string; + result?: { + text: string; + confidence: number; + extractedAt: string; + language?: string; + pageCount?: number; + }; + /** The minimum confidence threshold that was applied (0-1 scale). */ + minConfidence?: number; +} + export interface IconRegistry { sources: { prefix: string; From e5c67b16acda18ba79ad8039ed67431c1bdecc24 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sun, 5 Apr 2026 20:12:59 +0300 Subject: [PATCH 3/8] fix(flake): failing due to symlinks to /build --- flake.nix | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/flake.nix b/flake.nix index 5c206301d4..55f299b854 100644 --- a/flake.nix +++ b/flake.nix @@ -151,9 +151,10 @@ runHook postInstall ''; - # This file is a symlink into /build which is not allowed. + # Symlinks pointing to /build directory are not allowed in the Nix store. + # This removes all dangling symlinks that point to the temporary build directory. postFixup = '' - find $out/opt -name prebuild-install -path "*/better-sqlite3/node_modules/.bin/*" -delete || true + find $out/opt -type l -lname '/build/*' -delete || true ''; components = [ From 1437fdc4e39b2fdf64c43a791eeb98e02f91fa1b Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sun, 5 Apr 2026 20:14:38 +0300 Subject: [PATCH 4/8] feat(ocr): warn if text wasn't retrieved on manual to due low confidence --- .../src/stylesheets/theme-next/base.css | 7 ++----- .../src/stylesheets/theme-next/dialogs.css | 21 ++++++++++++------- .../src/translations/en/translation.json | 3 ++- apps/client/src/widgets/Toast.css | 21 ++++++++++++------- .../ReadOnlyTextRepresentation.tsx | 20 +++++++++++++----- 5 files changed, 46 insertions(+), 26 deletions(-) diff --git a/apps/client/src/stylesheets/theme-next/base.css b/apps/client/src/stylesheets/theme-next/base.css index 7bf9423138..c1d3cc3094 100644 --- a/apps/client/src/stylesheets/theme-next/base.css +++ b/apps/client/src/stylesheets/theme-next/base.css @@ -544,14 +544,11 @@ li.dropdown-item a.dropdown-item-button:focus-visible { vertical-align: middle; } -#toast-container .toast .toast-header .btn-close { +#toast-container .toast .toast-header .btn-close, +#toast-container .toast .toast-close .btn-close { margin: 0 0 0 12px; } -#toast-container .toast.no-title { - flex-direction: row; -} - #toast-container .toast .toast-body { flex-grow: 1; overflow: hidden; diff --git a/apps/client/src/stylesheets/theme-next/dialogs.css b/apps/client/src/stylesheets/theme-next/dialogs.css index 613fb94f36..94b6ff21a1 100644 --- a/apps/client/src/stylesheets/theme-next/dialogs.css +++ b/apps/client/src/stylesheets/theme-next/dialogs.css @@ -26,7 +26,8 @@ .modal .modal-header .btn-close, .modal .modal-header .help-button, .modal .modal-header .custom-title-bar-button, -#toast-container .toast .toast-header .btn-close { +#toast-container .toast .toast-header .btn-close, +#toast-container .toast .toast-close .btn-close { display: flex; justify-content: center; align-items: center; @@ -46,12 +47,14 @@ } .modal .modal-header .btn-close, -#toast-container .toast .toast-header .btn-close { +#toast-container .toast .toast-header .btn-close, +#toast-container .toast .toast-close .btn-close { --modal-control-button-hover-background: var(--modal-close-button-hover-background); } .modal .modal-header .btn-close::after, -#toast-container .toast .toast-header .btn-close::after { +#toast-container .toast .toast-header .btn-close::after, +#toast-container .toast .toast-close .btn-close::after { content: "\ec8d"; font-family: boxicons; } @@ -67,7 +70,8 @@ .modal .modal-header .btn-close:hover, .modal .modal-header .help-button:hover, .modal .modal-header .custom-title-bar-button:hover, -#toast-container .toast .toast-header .btn-close:hover { +#toast-container .toast .toast-header .btn-close:hover, +#toast-container .toast .toast-close .btn-close:hover { background: var(--modal-control-button-hover-background); color: var(--modal-control-button-hover-color); } @@ -75,19 +79,22 @@ .modal .modal-header .btn-close:active, .modal .modal-header .help-button:active, .modal .modal-header .custom-title-bar-button:active, -#toast-container .toast .toast-header .btn-close:active { +#toast-container .toast .toast-header .btn-close:active, +#toast-container .toast .toast-close .btn-close:active { transform: scale(.85); } .modal .modal-header .btn-close:focus, .modal .modal-header .help-button:focus, -#toast-container .toast .toast-header .btn-close:focus { +#toast-container .toast .toast-header .btn-close:focus, +#toast-container .toast .toast-close .btn-close:focus { box-shadow: none !important; } .modal .modal-header .btn-close:focus-visible, .modal .modal-header .help-button:focus-visible, -#toast-container .toast .toast-header .btn-close:focus-visible { +#toast-container .toast .toast-header .btn-close:focus-visible, +#toast-container .toast .toast-close .btn-close:focus-visible { outline: 2px solid var(--input-focus-outline-color); outline-offset: 2px; } diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index f7ff29ae0c..e051f8f720 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -2095,7 +2095,8 @@ "processing_started": "OCR processing has been started. Please wait a moment and refresh.", "processing_complete": "OCR processing complete.", "processing_failed": "Failed to start OCR processing", - "text_filtered_low_confidence": "OCR detected text with {{confidence}}% confidence, but it was discarded because your minimum threshold is {{threshold}}%.\n\nYou can adjust the threshold in Options → Media.", + "text_filtered_low_confidence": "OCR detected text with {{confidence}}% confidence, but it was discarded because your minimum threshold is {{threshold}}%.", + "open_media_settings": "Open Settings", "view_extracted_text": "View extracted text (OCR)" }, "command_palette": { diff --git a/apps/client/src/widgets/Toast.css b/apps/client/src/widgets/Toast.css index b58e6a13ab..95b9f69157 100644 --- a/apps/client/src/widgets/Toast.css +++ b/apps/client/src/widgets/Toast.css @@ -28,9 +28,10 @@ overflow: hidden; } -.toast.no-title { +.toast.no-title .toast-main-row { display: flex; flex-direction: row; + align-items: center; } .toast.no-title .toast-icon { @@ -40,22 +41,26 @@ } .toast.no-title .toast-body { - padding-inline-start: 0; - padding-inline-end: 0; + flex: 1; + padding-block: var(--bs-toast-padding-y); + padding-inline: 0; } -.toast.no-title .toast-header { - background-color: unset !important; +.toast.no-title .toast-close { + display: flex; + align-items: center; + padding: var(--bs-toast-padding-y) var(--bs-toast-padding-x); } .toast { .toast-buttons { - padding: 0 1em 1em 1em; + padding: 0 var(--bs-toast-padding-x) var(--bs-toast-padding-y) var(--bs-toast-padding-x); display: flex; - gap: 1em; - justify-content: space-between; + flex-direction: column; + gap: 0.5em; .btn { + width: 100%; color: var(--bs-toast-color); background: var(--modal-control-button-background); diff --git a/apps/client/src/widgets/type_widgets/ReadOnlyTextRepresentation.tsx b/apps/client/src/widgets/type_widgets/ReadOnlyTextRepresentation.tsx index c8f4340c32..a5bada9f6b 100644 --- a/apps/client/src/widgets/type_widgets/ReadOnlyTextRepresentation.tsx +++ b/apps/client/src/widgets/type_widgets/ReadOnlyTextRepresentation.tsx @@ -3,9 +3,11 @@ import "./ReadOnlyTextRepresentation.css"; import type { OCRProcessResponse, TextRepresentationResponse } from "@triliumnext/commons"; import { useEffect, useState } from "preact/hooks"; +import appContext from "../../components/app_context"; import { t } from "../../services/i18n"; import server from "../../services/server"; import toast from "../../services/toast"; +import { randomString } from "../../services/utils"; import { TypeWidgetProps } from "./type_widget"; type State = @@ -71,14 +73,22 @@ export function TextRepresentation({ textUrl, processUrl }: TextRepresentationPr if (result && !result.text && result.confidence > 0 && minConfidence > 0) { const confidencePercent = Math.round(result.confidence * 100); const thresholdPercent = Math.round(minConfidence * 100); - toast.showMessage( - t("ocr.text_filtered_low_confidence", { + toast.showPersistent({ + id: `ocr-low-confidence-${randomString(8)}`, + icon: "bx bx-info-circle", + message: t("ocr.text_filtered_low_confidence", { confidence: confidencePercent, threshold: thresholdPercent }), - 10000, // Show for 10 seconds since this is important info - "bx bx-info-circle" - ); + timeout: 15000, + buttons: [{ + text: t("ocr.open_media_settings"), + onClick: ({ dismissToast }) => { + appContext.tabManager.openInNewTab("_optionsMedia", null, true); + dismissToast(); + } + }] + }); } else { toast.showMessage(t("ocr.processing_complete")); } From 055dd9cd01eb4d197a1f3914a937a05c6ed1ab21 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sun, 5 Apr 2026 20:14:54 +0300 Subject: [PATCH 5/8] chore(toast): fix button alignment if no title & make buttons full-width --- apps/client/src/widgets/Toast.tsx | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/apps/client/src/widgets/Toast.tsx b/apps/client/src/widgets/Toast.tsx index f0321345a2..c251b900a3 100644 --- a/apps/client/src/widgets/Toast.tsx +++ b/apps/client/src/widgets/Toast.tsx @@ -42,21 +42,24 @@ function Toast({ id, title, timeout, progress, message, icon, buttons }: ToastOp id={`toast-${id}`} > {title ? ( -
- - {toastIcon} - {title} - - {closeButton} -
+ <> +
+ + {toastIcon} + {title} + + {closeButton} +
+
{message}
+ ) : ( -
{toastIcon}
+
+
{toastIcon}
+
{message}
+
{closeButton}
+
)} -
{message}
- - {!title &&
{closeButton}
} - {buttons && (
{buttons.map(({ text, onClick }) => ( From 9e34fcb8a804d5f2f6f9f4aca5206f270b047cb0 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sun, 5 Apr 2026 20:15:08 +0300 Subject: [PATCH 6/8] fix(desktop): failing in prod due to tesseract --- apps/desktop/scripts/build.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/desktop/scripts/build.ts b/apps/desktop/scripts/build.ts index 2e5f82c506..d080036b11 100644 --- a/apps/desktop/scripts/build.ts +++ b/apps/desktop/scripts/build.ts @@ -16,7 +16,7 @@ async function main() { build.copy("/packages/share-theme/src/templates", "share-theme/templates/"); // Copy node modules dependencies - build.copyNodeModules([ "better-sqlite3", "bindings", "file-uri-to-path", "@electron/remote" ]); + build.copyNodeModules([ "better-sqlite3", "bindings", "file-uri-to-path", "@electron/remote", "tesseract.js" ]); build.copy("/node_modules/ckeditor5/dist/ckeditor5-content.css", "ckeditor5-content.css"); build.buildFrontend(); From aba5ff75af84c04533392b299ef560b5d06040f0 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sun, 5 Apr 2026 20:22:49 +0300 Subject: [PATCH 7/8] fix(server): sync version not increased after breaking changes --- apps/server/src/services/app_info.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/server/src/services/app_info.ts b/apps/server/src/services/app_info.ts index ee59c1b595..bf9a7c6c1e 100644 --- a/apps/server/src/services/app_info.ts +++ b/apps/server/src/services/app_info.ts @@ -6,7 +6,7 @@ import build from "./build.js"; import dataDir from "./data_dir.js"; const APP_DB_VERSION = 236; -const SYNC_VERSION = 37; +const SYNC_VERSION = 38; const CLIPPER_PROTOCOL_VERSION = "1.0"; export default { From 530d193734508f61f74b059de60bee732158decf Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sun, 5 Apr 2026 20:37:33 +0300 Subject: [PATCH 8/8] fix(forge): build no longer working due to audit --- package.json | 1 + pnpm-lock.yaml | 21 ++++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 76c682174e..5bf4b146e6 100644 --- a/package.json +++ b/package.json @@ -158,6 +158,7 @@ "handlebars@<4.7.9": ">=4.7.9", "qs@<6.14.2": ">=6.14.2", "minimatch@<3.1.4": "^3.1.4", + "minimatch@3>brace-expansion": "^1.1.13", "serialize-javascript@<7.0.5": ">=7.0.5", "webpack@<5.104.1": ">=5.104.1" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 305a045d8d..3d75802949 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -61,6 +61,7 @@ overrides: handlebars@<4.7.9: '>=4.7.9' qs@<6.14.2: '>=6.14.2' minimatch@<3.1.4: ^3.1.4 + minimatch@3>brace-expansion: ^1.1.13 serialize-javascript@<7.0.5: '>=7.0.5' webpack@<5.104.1: '>=5.104.1' @@ -7147,6 +7148,9 @@ packages: bail@2.0.2: resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + balanced-match@4.0.3: resolution: {integrity: sha512-1pHv8LX9CpKut1Zp4EXey7Z8OfH11ONNH6Dhi2WDUt31VVZFXZzKwXcysBgqSumFCmR+0dqjMK5v5JiFHzi0+g==} engines: {node: 20 || >=22} @@ -7275,6 +7279,9 @@ packages: bplist-creator@0.0.8: resolution: {integrity: sha512-Za9JKzD6fjLC16oX2wsXfc+qBEhJBJB1YPInoAQpMLhDuj5aVOv1baGeIQSq1Fr3OCqzvsoQcSBSwGId/Ja2PA==} + brace-expansion@1.1.13: + resolution: {integrity: sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==} + brace-expansion@5.0.2: resolution: {integrity: sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw==} engines: {node: 20 || >=22} @@ -7761,6 +7768,9 @@ packages: resolution: {integrity: sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==} engines: {node: '>= 0.8.0'} + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + concat-stream@1.6.2: resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==} engines: {'0': node >= 0.8} @@ -22478,6 +22488,8 @@ snapshots: bail@2.0.2: {} + balanced-match@1.0.2: {} + balanced-match@4.0.3: {} bare-events@2.7.0: {} @@ -22618,6 +22630,11 @@ snapshots: stream-buffers: 2.2.0 optional: true + brace-expansion@1.1.13: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + brace-expansion@5.0.2: dependencies: balanced-match: 4.0.3 @@ -23272,6 +23289,8 @@ snapshots: transitivePeerDependencies: - supports-color + concat-map@0.0.1: {} + concat-stream@1.6.2: dependencies: buffer-from: 1.1.2 @@ -27261,7 +27280,7 @@ snapshots: minimatch@3.1.5: dependencies: - brace-expansion: 5.0.5 + brace-expansion: 1.1.13 minimatch@5.1.9: dependencies: