vectra 0.12.2 → 0.14.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/LICENSE +1 -1
- package/README.md +92 -100
- package/bin/vectra.js +3 -0
- package/lib/BrowserWebFetcher.d.ts +75 -0
- package/lib/BrowserWebFetcher.d.ts.map +1 -0
- package/lib/BrowserWebFetcher.js +290 -0
- package/lib/BrowserWebFetcher.js.map +1 -0
- package/lib/FileFetcher.d.ts +5 -0
- package/lib/FileFetcher.d.ts.map +1 -0
- package/lib/FileFetcher.js +89 -0
- package/lib/FileFetcher.js.map +1 -0
- package/lib/FileFetcher.spec.d.ts +2 -0
- package/lib/FileFetcher.spec.d.ts.map +1 -0
- package/lib/FileFetcher.spec.js +244 -0
- package/lib/FileFetcher.spec.js.map +1 -0
- package/lib/FolderWatcher.d.ts +91 -0
- package/lib/FolderWatcher.d.ts.map +1 -0
- package/lib/FolderWatcher.js +304 -0
- package/lib/FolderWatcher.js.map +1 -0
- package/lib/FolderWatcher.spec.d.ts +2 -0
- package/lib/FolderWatcher.spec.d.ts.map +1 -0
- package/lib/FolderWatcher.spec.js +308 -0
- package/lib/FolderWatcher.spec.js.map +1 -0
- package/lib/GPT3Tokenizer.d.ts +9 -0
- package/lib/GPT3Tokenizer.spec.d.ts +2 -0
- package/lib/GPT3Tokenizer.spec.d.ts.map +1 -0
- package/lib/GPT3Tokenizer.spec.js +45 -0
- package/lib/GPT3Tokenizer.spec.js.map +1 -0
- package/lib/ItemSelector.d.ts +41 -0
- package/lib/ItemSelector.d.ts.map +1 -0
- package/lib/ItemSelector.js +179 -0
- package/lib/ItemSelector.js.map +1 -0
- package/lib/ItemSelector.spec.d.ts +2 -0
- package/lib/ItemSelector.spec.d.ts.map +1 -0
- package/lib/ItemSelector.spec.js +204 -0
- package/lib/ItemSelector.spec.js.map +1 -0
- package/lib/LocalDocument.d.ts +54 -0
- package/lib/LocalDocument.d.ts.map +1 -1
- package/lib/LocalDocument.js +116 -0
- package/lib/LocalDocument.js.map +1 -0
- package/lib/LocalDocument.spec.d.ts +2 -0
- package/lib/LocalDocument.spec.d.ts.map +1 -0
- package/lib/LocalDocument.spec.js +214 -0
- package/lib/LocalDocument.spec.js.map +1 -0
- package/lib/LocalDocumentIndex.d.ts +152 -0
- package/lib/LocalDocumentIndex.d.ts.map +1 -1
- package/lib/LocalDocumentIndex.js +420 -0
- package/lib/LocalDocumentIndex.js.map +1 -0
- package/lib/LocalDocumentIndex.spec.d.ts +2 -0
- package/lib/LocalDocumentIndex.spec.d.ts.map +1 -0
- package/lib/LocalDocumentIndex.spec.js +494 -0
- package/lib/LocalDocumentIndex.spec.js.map +1 -0
- package/lib/LocalDocumentResult.d.ts +66 -0
- package/lib/LocalDocumentResult.d.ts.map +1 -1
- package/lib/LocalDocumentResult.js +376 -0
- package/lib/LocalDocumentResult.js.map +1 -0
- package/lib/LocalDocumentResult.spec.d.ts +2 -0
- package/lib/LocalDocumentResult.spec.d.ts.map +1 -0
- package/lib/LocalDocumentResult.spec.js +373 -0
- package/lib/LocalDocumentResult.spec.js.map +1 -0
- package/lib/LocalEmbeddings.d.ts +59 -0
- package/lib/LocalEmbeddings.d.ts.map +1 -0
- package/lib/LocalEmbeddings.js +101 -0
- package/lib/LocalEmbeddings.js.map +1 -0
- package/lib/LocalEmbeddings.spec.d.ts +2 -0
- package/lib/LocalEmbeddings.spec.d.ts.map +1 -0
- package/lib/LocalEmbeddings.spec.js +155 -0
- package/lib/LocalEmbeddings.spec.js.map +1 -0
- package/lib/LocalIndex.d.ts +159 -0
- package/lib/LocalIndex.d.ts.map +1 -1
- package/lib/LocalIndex.js +519 -0
- package/lib/LocalIndex.js.map +1 -0
- package/lib/LocalIndex.spec.d.ts +2 -0
- package/lib/LocalIndex.spec.js +611 -9
- package/lib/LocalIndex.spec.js.map +1 -1
- package/lib/OpenAIEmbeddings.d.ts +124 -0
- package/lib/OpenAIEmbeddings.d.ts.map +1 -0
- package/lib/OpenAIEmbeddings.js +166 -0
- package/lib/OpenAIEmbeddings.js.map +1 -0
- package/lib/OpenAIEmbeddings.spec.d.ts +2 -0
- package/lib/OpenAIEmbeddings.spec.d.ts.map +1 -0
- package/lib/OpenAIEmbeddings.spec.js +298 -0
- package/lib/OpenAIEmbeddings.spec.js.map +1 -0
- package/lib/TextSplitter.d.ts +21 -0
- package/lib/TextSplitter.d.ts.map +1 -1
- package/lib/TextSplitter.js +500 -0
- package/lib/TextSplitter.js.map +1 -0
- package/lib/TextSplitter.spec.d.ts +2 -0
- package/lib/TextSplitter.spec.d.ts.map +1 -0
- package/lib/TextSplitter.spec.js +337 -0
- package/lib/TextSplitter.spec.js.map +1 -0
- package/lib/TransformersEmbeddings.d.ts +121 -0
- package/lib/TransformersEmbeddings.d.ts.map +1 -0
- package/lib/TransformersEmbeddings.js +176 -0
- package/lib/TransformersEmbeddings.js.map +1 -0
- package/lib/TransformersEmbeddings.spec.d.ts +2 -0
- package/lib/TransformersEmbeddings.spec.d.ts.map +1 -0
- package/lib/TransformersEmbeddings.spec.js +198 -0
- package/lib/TransformersEmbeddings.spec.js.map +1 -0
- package/lib/TransformersTokenizer.d.ts +33 -0
- package/lib/TransformersTokenizer.d.ts.map +1 -0
- package/lib/TransformersTokenizer.js +44 -0
- package/lib/TransformersTokenizer.js.map +1 -0
- package/lib/TransformersTokenizer.spec.d.ts +2 -0
- package/lib/TransformersTokenizer.spec.d.ts.map +1 -0
- package/lib/TransformersTokenizer.spec.js +112 -0
- package/lib/TransformersTokenizer.spec.js.map +1 -0
- package/lib/WebFetcher.d.ts +14 -0
- package/lib/WebFetcher.d.ts.map +1 -0
- package/lib/WebFetcher.js +238 -0
- package/lib/WebFetcher.js.map +1 -0
- package/lib/WebFetcher.spec.d.ts +2 -0
- package/lib/WebFetcher.spec.d.ts.map +1 -0
- package/lib/WebFetcher.spec.js +263 -0
- package/lib/WebFetcher.spec.js.map +1 -0
- package/lib/browser.d.ts +30 -0
- package/lib/browser.d.ts.map +1 -0
- package/lib/browser.js +52 -0
- package/lib/browser.js.map +1 -0
- package/lib/codecs/IndexCodec.d.ts +37 -0
- package/lib/codecs/IndexCodec.d.ts.map +1 -0
- package/lib/codecs/IndexCodec.js +3 -0
- package/lib/codecs/IndexCodec.js.map +1 -0
- package/lib/codecs/JsonCodec.d.ts +19 -0
- package/lib/codecs/JsonCodec.d.ts.map +1 -0
- package/lib/codecs/JsonCodec.js +35 -0
- package/lib/codecs/JsonCodec.js.map +1 -0
- package/lib/codecs/JsonCodec.spec.d.ts +2 -0
- package/lib/codecs/JsonCodec.spec.d.ts.map +1 -0
- package/lib/codecs/JsonCodec.spec.js +66 -0
- package/lib/codecs/JsonCodec.spec.js.map +1 -0
- package/lib/codecs/LocalIndex.protobuf.spec.d.ts +2 -0
- package/lib/codecs/LocalIndex.protobuf.spec.d.ts.map +1 -0
- package/lib/codecs/LocalIndex.protobuf.spec.js +108 -0
- package/lib/codecs/LocalIndex.protobuf.spec.js.map +1 -0
- package/lib/codecs/ProtobufCodec.d.ts +20 -0
- package/lib/codecs/ProtobufCodec.d.ts.map +1 -0
- package/lib/codecs/ProtobufCodec.js +225 -0
- package/lib/codecs/ProtobufCodec.js.map +1 -0
- package/lib/codecs/ProtobufCodec.spec.d.ts +2 -0
- package/lib/codecs/ProtobufCodec.spec.d.ts.map +1 -0
- package/lib/codecs/ProtobufCodec.spec.js +155 -0
- package/lib/codecs/ProtobufCodec.spec.js.map +1 -0
- package/lib/codecs/index.d.ts +5 -0
- package/lib/codecs/index.d.ts.map +1 -0
- package/lib/codecs/index.js +21 -0
- package/lib/codecs/index.js.map +1 -0
- package/lib/codecs/migrateIndex.d.ts +24 -0
- package/lib/codecs/migrateIndex.d.ts.map +1 -0
- package/lib/codecs/migrateIndex.js +119 -0
- package/lib/codecs/migrateIndex.js.map +1 -0
- package/lib/codecs/migrateIndex.spec.d.ts +2 -0
- package/lib/codecs/migrateIndex.spec.d.ts.map +1 -0
- package/lib/codecs/migrateIndex.spec.js +151 -0
- package/lib/codecs/migrateIndex.spec.js.map +1 -0
- package/lib/codecs/schemas/index.proto +34 -0
- package/lib/index.d.ts +20 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +36 -0
- package/lib/index.js.map +1 -0
- package/lib/internals/Colorize.d.ts +14 -0
- package/lib/internals/Colorize.d.ts.map +1 -0
- package/lib/internals/Colorize.js +69 -0
- package/lib/internals/Colorize.js.map +1 -0
- package/lib/internals/index.d.ts +3 -0
- package/lib/internals/index.d.ts.map +1 -0
- package/lib/internals/index.js +19 -0
- package/lib/internals/index.js.map +1 -0
- package/lib/internals/types.d.ts +43 -0
- package/lib/internals/types.d.ts.map +1 -0
- package/lib/internals/types.js +3 -0
- package/lib/internals/types.js.map +1 -0
- package/lib/server/IndexManager.d.ts +78 -0
- package/lib/server/IndexManager.d.ts.map +1 -0
- package/lib/server/IndexManager.js +259 -0
- package/lib/server/IndexManager.js.map +1 -0
- package/lib/server/VectraServer.d.ts +40 -0
- package/lib/server/VectraServer.d.ts.map +1 -0
- package/lib/server/VectraServer.js +151 -0
- package/lib/server/VectraServer.js.map +1 -0
- package/lib/server/VectraServer.spec.d.ts +2 -0
- package/lib/server/VectraServer.spec.d.ts.map +1 -0
- package/lib/server/VectraServer.spec.js +322 -0
- package/lib/server/VectraServer.spec.js.map +1 -0
- package/lib/server/handlers/documentHandlers.d.ts +15 -0
- package/lib/server/handlers/documentHandlers.d.ts.map +1 -0
- package/lib/server/handlers/documentHandlers.js +95 -0
- package/lib/server/handlers/documentHandlers.js.map +1 -0
- package/lib/server/handlers/helpers.d.ts +23 -0
- package/lib/server/handlers/helpers.d.ts.map +1 -0
- package/lib/server/handlers/helpers.js +138 -0
- package/lib/server/handlers/helpers.js.map +1 -0
- package/lib/server/handlers/index.d.ts +8 -0
- package/lib/server/handlers/index.d.ts.map +1 -0
- package/lib/server/handlers/index.js +22 -0
- package/lib/server/handlers/index.js.map +1 -0
- package/lib/server/handlers/indexHandlers.d.ts +14 -0
- package/lib/server/handlers/indexHandlers.d.ts.map +1 -0
- package/lib/server/handlers/indexHandlers.js +85 -0
- package/lib/server/handlers/indexHandlers.js.map +1 -0
- package/lib/server/handlers/itemHandlers.d.ts +34 -0
- package/lib/server/handlers/itemHandlers.d.ts.map +1 -0
- package/lib/server/handlers/itemHandlers.js +166 -0
- package/lib/server/handlers/itemHandlers.js.map +1 -0
- package/lib/server/handlers/lifecycleHandlers.d.ts +11 -0
- package/lib/server/handlers/lifecycleHandlers.d.ts.map +1 -0
- package/lib/server/handlers/lifecycleHandlers.js +31 -0
- package/lib/server/handlers/lifecycleHandlers.js.map +1 -0
- package/lib/server/handlers/queryHandlers.d.ts +27 -0
- package/lib/server/handlers/queryHandlers.d.ts.map +1 -0
- package/lib/server/handlers/queryHandlers.js +135 -0
- package/lib/server/handlers/queryHandlers.js.map +1 -0
- package/lib/server/handlers/statsHandlers.d.ts +17 -0
- package/lib/server/handlers/statsHandlers.d.ts.map +1 -0
- package/lib/server/handlers/statsHandlers.js +81 -0
- package/lib/server/handlers/statsHandlers.js.map +1 -0
- package/lib/server/index.d.ts +4 -0
- package/lib/server/index.d.ts.map +1 -0
- package/lib/server/index.js +23 -0
- package/lib/server/index.js.map +1 -0
- package/lib/storage/FileStorage.d.ts +92 -0
- package/lib/storage/FileStorage.d.ts.map +1 -0
- package/lib/storage/FileStorage.js +3 -0
- package/lib/storage/FileStorage.js.map +1 -0
- package/lib/storage/FileStorageUtilities.d.ts +36 -0
- package/lib/storage/FileStorageUtilities.d.ts.map +1 -0
- package/lib/storage/FileStorageUtilities.js +91 -0
- package/lib/storage/FileStorageUtilities.js.map +1 -0
- package/lib/storage/FileStorageUtilities.spec.d.ts +2 -0
- package/lib/storage/FileStorageUtilities.spec.d.ts.map +1 -0
- package/lib/storage/FileStorageUtilities.spec.js +98 -0
- package/lib/storage/FileStorageUtilities.spec.js.map +1 -0
- package/lib/storage/FileType.d.ts +29 -0
- package/lib/storage/FileType.d.ts.map +1 -0
- package/lib/storage/FileType.js +38 -0
- package/lib/storage/FileType.js.map +1 -0
- package/lib/storage/IndexedDBStorage.d.ts +47 -0
- package/lib/storage/IndexedDBStorage.d.ts.map +1 -0
- package/lib/storage/IndexedDBStorage.js +347 -0
- package/lib/storage/IndexedDBStorage.js.map +1 -0
- package/lib/storage/LocalFileStorage.browser.d.ts +19 -0
- package/lib/storage/LocalFileStorage.browser.d.ts.map +1 -0
- package/lib/storage/LocalFileStorage.browser.js +43 -0
- package/lib/storage/LocalFileStorage.browser.js.map +1 -0
- package/lib/storage/LocalFileStorage.d.ts +23 -0
- package/lib/storage/LocalFileStorage.d.ts.map +1 -0
- package/lib/storage/LocalFileStorage.js +152 -0
- package/lib/storage/LocalFileStorage.js.map +1 -0
- package/lib/storage/LocalFileStorage.spec.d.ts +2 -0
- package/lib/storage/LocalFileStorage.spec.d.ts.map +1 -0
- package/lib/storage/LocalFileStorage.spec.js +249 -0
- package/lib/storage/LocalFileStorage.spec.js.map +1 -0
- package/lib/storage/VirtualFileStorage.d.ts +18 -0
- package/lib/storage/VirtualFileStorage.d.ts.map +1 -0
- package/lib/storage/VirtualFileStorage.js +178 -0
- package/lib/storage/VirtualFileStorage.js.map +1 -0
- package/lib/storage/VirtualFileStorage.spec.d.ts +2 -0
- package/lib/storage/VirtualFileStorage.spec.d.ts.map +1 -0
- package/lib/storage/VirtualFileStorage.spec.js +302 -0
- package/lib/storage/VirtualFileStorage.spec.js.map +1 -0
- package/lib/storage/index.d.ts +6 -0
- package/lib/storage/index.d.ts.map +1 -0
- package/lib/storage/index.js +22 -0
- package/lib/storage/index.js.map +1 -0
- package/lib/templates/templates/csharp/README.md +48 -0
- package/lib/templates/templates/csharp/VectraClient.cs +234 -0
- package/lib/templates/templates/go/README.md +71 -0
- package/lib/templates/templates/go/vectra_client.go +322 -0
- package/lib/templates/templates/java/README.md +81 -0
- package/lib/templates/templates/java/VectraClient.java +232 -0
- package/lib/templates/templates/python/README.md +37 -0
- package/lib/templates/templates/python/vectra_client.py +279 -0
- package/lib/templates/templates/rust/Cargo.toml +14 -0
- package/lib/templates/templates/rust/README.md +39 -0
- package/lib/templates/templates/rust/build.rs +4 -0
- package/lib/templates/templates/rust/lib.rs +284 -0
- package/lib/templates/templates/typescript/README.md +96 -0
- package/lib/templates/templates/typescript/VectraClient.ts +374 -0
- package/lib/templates/typescript/VectraClient.d.ts +114 -0
- package/lib/templates/typescript/VectraClient.d.ts.map +1 -0
- package/lib/templates/typescript/VectraClient.js +328 -0
- package/lib/templates/typescript/VectraClient.js.map +1 -0
- package/lib/types.d.ts +153 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +3 -0
- package/lib/types.js.map +1 -0
- package/lib/utils/index.d.ts +2 -0
- package/lib/utils/index.d.ts.map +1 -0
- package/lib/utils/index.js +18 -0
- package/lib/utils/index.js.map +1 -0
- package/lib/utils/pathUtils.d.ts +40 -0
- package/lib/utils/pathUtils.d.ts.map +1 -0
- package/lib/utils/pathUtils.js +98 -0
- package/lib/utils/pathUtils.js.map +1 -0
- package/lib/vectra-cli.d.ts +2 -0
- package/lib/vectra-cli.d.ts.map +1 -1
- package/lib/vectra-cli.generate.spec.d.ts +2 -0
- package/lib/vectra-cli.generate.spec.d.ts.map +1 -0
- package/lib/vectra-cli.generate.spec.js +112 -0
- package/lib/vectra-cli.generate.spec.js.map +1 -0
- package/lib/vectra-cli.js +760 -0
- package/lib/vectra-cli.js.map +1 -0
- package/lib/vectra-cli.spec.d.ts +1 -0
- package/lib/vectra-cli.spec.d.ts.map +1 -0
- package/lib/vectra-cli.spec.js +2 -0
- package/lib/vectra-cli.spec.js.map +1 -0
- package/package.json +91 -16
- package/proto/vectra_service.proto +276 -0
- package/src/BrowserWebFetcher.ts +345 -0
- package/src/FileFetcher.spec.ts +234 -0
- package/src/FileFetcher.ts +37 -25
- package/src/FolderWatcher.spec.ts +288 -0
- package/src/FolderWatcher.ts +304 -0
- package/src/GPT3Tokenizer.spec.ts +50 -0
- package/src/ItemSelector.spec.ts +252 -0
- package/src/ItemSelector.ts +163 -150
- package/src/LocalDocument.spec.ts +211 -0
- package/src/LocalDocument.ts +88 -94
- package/src/LocalDocumentIndex.spec.ts +481 -0
- package/src/LocalDocumentIndex.ts +39 -40
- package/src/LocalDocumentResult.spec.ts +373 -0
- package/src/LocalDocumentResult.ts +489 -319
- package/src/LocalEmbeddings.spec.ts +138 -0
- package/src/LocalEmbeddings.ts +120 -0
- package/src/LocalIndex.spec.ts +808 -66
- package/src/LocalIndex.ts +479 -429
- package/src/OpenAIEmbeddings.spec.ts +354 -0
- package/src/OpenAIEmbeddings.ts +26 -27
- package/src/TextSplitter.spec.ts +342 -0
- package/src/TextSplitter.ts +517 -532
- package/src/TransformersEmbeddings.spec.ts +188 -0
- package/src/TransformersEmbeddings.ts +232 -0
- package/src/TransformersTokenizer.spec.ts +143 -0
- package/src/TransformersTokenizer.ts +45 -0
- package/src/WebFetcher.spec.ts +288 -0
- package/src/WebFetcher.ts +184 -186
- package/src/browser.ts +69 -0
- package/src/codecs/IndexCodec.ts +40 -0
- package/src/codecs/JsonCodec.spec.ts +70 -0
- package/src/codecs/JsonCodec.ts +37 -0
- package/src/codecs/LocalIndex.protobuf.spec.ts +115 -0
- package/src/codecs/ProtobufCodec.spec.ts +166 -0
- package/src/codecs/ProtobufCodec.ts +193 -0
- package/src/codecs/index.ts +4 -0
- package/src/codecs/migrateIndex.spec.ts +176 -0
- package/src/codecs/migrateIndex.ts +125 -0
- package/src/codecs/schemas/index.proto +34 -0
- package/src/index.ts +9 -1
- package/src/internals/Colorize.ts +19 -16
- package/src/server/IndexManager.ts +243 -0
- package/src/server/VectraServer.spec.ts +303 -0
- package/src/server/VectraServer.ts +156 -0
- package/src/server/handlers/documentHandlers.ts +59 -0
- package/src/server/handlers/helpers.ts +93 -0
- package/src/server/handlers/index.ts +7 -0
- package/src/server/handlers/indexHandlers.ts +44 -0
- package/src/server/handlers/itemHandlers.ts +140 -0
- package/src/server/handlers/lifecycleHandlers.ts +26 -0
- package/src/server/handlers/queryHandlers.ts +96 -0
- package/src/server/handlers/statsHandlers.ts +38 -0
- package/src/server/index.ts +3 -0
- package/src/storage/FileStorage.ts +105 -0
- package/src/storage/FileStorageUtilities.spec.ts +106 -0
- package/src/storage/FileStorageUtilities.ts +77 -0
- package/src/storage/FileType.ts +61 -0
- package/src/storage/IndexedDBStorage.ts +365 -0
- package/src/storage/LocalFileStorage.browser.ts +52 -0
- package/src/storage/LocalFileStorage.spec.ts +292 -0
- package/src/storage/LocalFileStorage.ts +98 -0
- package/src/storage/VirtualFileStorage.spec.ts +307 -0
- package/src/storage/VirtualFileStorage.ts +169 -0
- package/src/storage/index.ts +5 -0
- package/src/templates/csharp/README.md +48 -0
- package/src/templates/csharp/VectraClient.cs +234 -0
- package/src/templates/go/README.md +71 -0
- package/src/templates/go/vectra_client.go +322 -0
- package/src/templates/java/README.md +81 -0
- package/src/templates/java/VectraClient.java +232 -0
- package/src/templates/python/README.md +37 -0
- package/src/templates/python/vectra_client.py +279 -0
- package/src/templates/rust/Cargo.toml +14 -0
- package/src/templates/rust/README.md +39 -0
- package/src/templates/rust/build.rs +4 -0
- package/src/templates/rust/lib.rs +284 -0
- package/src/templates/typescript/README.md +96 -0
- package/src/templates/typescript/VectraClient.ts +374 -0
- package/src/types.ts +131 -123
- package/src/utils/index.ts +1 -0
- package/src/utils/pathUtils.ts +106 -0
- package/src/vectra-cli.generate.spec.ts +72 -0
- package/src/vectra-cli.spec.ts +0 -0
- package/src/vectra-cli.ts +687 -246
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import * as grpc from '@grpc/grpc-js';
|
|
2
|
+
import { v4 } from 'uuid';
|
|
3
|
+
import { LocalIndex } from '../../LocalIndex';
|
|
4
|
+
import { IndexManager } from '../IndexManager';
|
|
5
|
+
import { EmbeddingsModel, IndexItem } from '../../types';
|
|
6
|
+
import { wrapHandler, grpcError, fromProtoMetadata, toProtoMetadata, parseFilterJson } from './helpers';
|
|
7
|
+
|
|
8
|
+
async function resolveVector(
|
|
9
|
+
text: string | undefined,
|
|
10
|
+
vector: number[] | undefined,
|
|
11
|
+
embeddings: EmbeddingsModel | undefined
|
|
12
|
+
): Promise<number[]> {
|
|
13
|
+
if (vector && vector.length > 0) {
|
|
14
|
+
return vector;
|
|
15
|
+
}
|
|
16
|
+
if (text && text.length > 0) {
|
|
17
|
+
if (!embeddings) {
|
|
18
|
+
throw grpcError(grpc.status.FAILED_PRECONDITION, 'No embeddings model configured on the server');
|
|
19
|
+
}
|
|
20
|
+
const response = await embeddings.createEmbeddings(text);
|
|
21
|
+
if (response.status !== 'success' || !response.output) {
|
|
22
|
+
throw grpcError(grpc.status.INTERNAL, `Embeddings error: ${response.message || 'unknown'}`);
|
|
23
|
+
}
|
|
24
|
+
return response.output[0];
|
|
25
|
+
}
|
|
26
|
+
throw grpcError(grpc.status.INVALID_ARGUMENT, 'Either text or vector must be provided');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function asLocalIndex(managed: { index: LocalIndex | any }): LocalIndex {
|
|
30
|
+
return managed.index as LocalIndex;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function itemToProto(item: IndexItem) {
|
|
34
|
+
return {
|
|
35
|
+
id: item.id,
|
|
36
|
+
metadata: toProtoMetadata(item.metadata as Record<string, any>),
|
|
37
|
+
vector: Array.from(item.vector),
|
|
38
|
+
norm: item.norm,
|
|
39
|
+
score: 0,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function createItemHandlers(manager: IndexManager, embeddings?: EmbeddingsModel) {
|
|
44
|
+
return {
|
|
45
|
+
InsertItem: wrapHandler(async (call: grpc.ServerUnaryCall<any, any>) => {
|
|
46
|
+
const req = call.request;
|
|
47
|
+
if (!req.index_name) {
|
|
48
|
+
throw grpcError(grpc.status.INVALID_ARGUMENT, 'index_name is required');
|
|
49
|
+
}
|
|
50
|
+
const idx = asLocalIndex(manager.requireIndex(req.index_name));
|
|
51
|
+
const vector = await resolveVector(req.text, req.vector, embeddings);
|
|
52
|
+
const metadata = fromProtoMetadata(req.metadata);
|
|
53
|
+
const id = req.id || v4();
|
|
54
|
+
|
|
55
|
+
const item = await idx.insertItem({ id, vector, metadata });
|
|
56
|
+
return { id: item.id };
|
|
57
|
+
}),
|
|
58
|
+
|
|
59
|
+
UpsertItem: wrapHandler(async (call: grpc.ServerUnaryCall<any, any>) => {
|
|
60
|
+
const req = call.request;
|
|
61
|
+
if (!req.index_name) {
|
|
62
|
+
throw grpcError(grpc.status.INVALID_ARGUMENT, 'index_name is required');
|
|
63
|
+
}
|
|
64
|
+
if (!req.id) {
|
|
65
|
+
throw grpcError(grpc.status.INVALID_ARGUMENT, 'id is required for upsert');
|
|
66
|
+
}
|
|
67
|
+
const idx = asLocalIndex(manager.requireIndex(req.index_name));
|
|
68
|
+
const vector = await resolveVector(req.text, req.vector, embeddings);
|
|
69
|
+
const metadata = fromProtoMetadata(req.metadata);
|
|
70
|
+
|
|
71
|
+
const item = await idx.upsertItem({ id: req.id, vector, metadata });
|
|
72
|
+
return { id: item.id };
|
|
73
|
+
}),
|
|
74
|
+
|
|
75
|
+
BatchInsertItems: wrapHandler(async (call: grpc.ServerUnaryCall<any, any>) => {
|
|
76
|
+
const req = call.request;
|
|
77
|
+
if (!req.index_name) {
|
|
78
|
+
throw grpcError(grpc.status.INVALID_ARGUMENT, 'index_name is required');
|
|
79
|
+
}
|
|
80
|
+
const idx = asLocalIndex(manager.requireIndex(req.index_name));
|
|
81
|
+
const items = req.items || [];
|
|
82
|
+
const toInsert: Array<Partial<IndexItem>> = [];
|
|
83
|
+
|
|
84
|
+
for (const item of items) {
|
|
85
|
+
const vector = await resolveVector(item.text, item.vector, embeddings);
|
|
86
|
+
const metadata = fromProtoMetadata(item.metadata);
|
|
87
|
+
toInsert.push({ id: item.id || v4(), vector, metadata });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const inserted = await idx.batchInsertItems(toInsert);
|
|
91
|
+
return { ids: inserted.map((i: IndexItem) => i.id) };
|
|
92
|
+
}),
|
|
93
|
+
|
|
94
|
+
GetItem: wrapHandler(async (call: grpc.ServerUnaryCall<any, any>) => {
|
|
95
|
+
const req = call.request;
|
|
96
|
+
if (!req.index_name) {
|
|
97
|
+
throw grpcError(grpc.status.INVALID_ARGUMENT, 'index_name is required');
|
|
98
|
+
}
|
|
99
|
+
if (!req.id) {
|
|
100
|
+
throw grpcError(grpc.status.INVALID_ARGUMENT, 'id is required');
|
|
101
|
+
}
|
|
102
|
+
const idx = asLocalIndex(manager.requireIndex(req.index_name));
|
|
103
|
+
const item = await idx.getItem(req.id);
|
|
104
|
+
if (!item) {
|
|
105
|
+
throw grpcError(grpc.status.NOT_FOUND, `Item not found: ${req.id}`);
|
|
106
|
+
}
|
|
107
|
+
return { item: itemToProto(item) };
|
|
108
|
+
}),
|
|
109
|
+
|
|
110
|
+
DeleteItem: wrapHandler(async (call: grpc.ServerUnaryCall<any, any>) => {
|
|
111
|
+
const req = call.request;
|
|
112
|
+
if (!req.index_name) {
|
|
113
|
+
throw grpcError(grpc.status.INVALID_ARGUMENT, 'index_name is required');
|
|
114
|
+
}
|
|
115
|
+
if (!req.id) {
|
|
116
|
+
throw grpcError(grpc.status.INVALID_ARGUMENT, 'id is required');
|
|
117
|
+
}
|
|
118
|
+
const idx = asLocalIndex(manager.requireIndex(req.index_name));
|
|
119
|
+
await idx.deleteItem(req.id);
|
|
120
|
+
return {};
|
|
121
|
+
}),
|
|
122
|
+
|
|
123
|
+
ListItems: wrapHandler(async (call: grpc.ServerUnaryCall<any, any>) => {
|
|
124
|
+
const req = call.request;
|
|
125
|
+
if (!req.index_name) {
|
|
126
|
+
throw grpcError(grpc.status.INVALID_ARGUMENT, 'index_name is required');
|
|
127
|
+
}
|
|
128
|
+
const idx = asLocalIndex(manager.requireIndex(req.index_name));
|
|
129
|
+
const filter = parseFilterJson(req.filter);
|
|
130
|
+
|
|
131
|
+
const items = filter
|
|
132
|
+
? await idx.listItemsByMetadata(filter)
|
|
133
|
+
: await idx.listItems();
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
items: items.map((item: IndexItem) => itemToProto(item)),
|
|
137
|
+
};
|
|
138
|
+
}),
|
|
139
|
+
};
|
|
140
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as grpc from '@grpc/grpc-js';
|
|
2
|
+
import { IndexManager } from '../IndexManager';
|
|
3
|
+
import { wrapHandler } from './helpers';
|
|
4
|
+
|
|
5
|
+
export function createLifecycleHandlers(
|
|
6
|
+
manager: IndexManager,
|
|
7
|
+
startTime: number,
|
|
8
|
+
onShutdown: () => void
|
|
9
|
+
) {
|
|
10
|
+
return {
|
|
11
|
+
Healthcheck: wrapHandler(async (_call: grpc.ServerUnaryCall<any, any>) => {
|
|
12
|
+
const uptimeSeconds = Math.floor((Date.now() - startTime) / 1000);
|
|
13
|
+
return {
|
|
14
|
+
status: 'ok',
|
|
15
|
+
uptime_seconds: uptimeSeconds,
|
|
16
|
+
loaded_indexes: manager.indexes.size,
|
|
17
|
+
};
|
|
18
|
+
}),
|
|
19
|
+
|
|
20
|
+
Shutdown: wrapHandler(async (_call: grpc.ServerUnaryCall<any, any>) => {
|
|
21
|
+
// Trigger graceful shutdown asynchronously
|
|
22
|
+
process.nextTick(onShutdown);
|
|
23
|
+
return {};
|
|
24
|
+
}),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import * as grpc from '@grpc/grpc-js';
|
|
2
|
+
import { LocalIndex } from '../../LocalIndex';
|
|
3
|
+
import { IndexManager } from '../IndexManager';
|
|
4
|
+
import { EmbeddingsModel, QueryResult } from '../../types';
|
|
5
|
+
import { wrapHandler, grpcError, toProtoMetadata, parseFilterJson } from './helpers';
|
|
6
|
+
|
|
7
|
+
export function createQueryHandlers(manager: IndexManager, embeddings?: EmbeddingsModel) {
|
|
8
|
+
return {
|
|
9
|
+
QueryItems: wrapHandler(async (call: grpc.ServerUnaryCall<any, any>) => {
|
|
10
|
+
const req = call.request;
|
|
11
|
+
if (!req.index_name) {
|
|
12
|
+
throw grpcError(grpc.status.INVALID_ARGUMENT, 'index_name is required');
|
|
13
|
+
}
|
|
14
|
+
const idx = manager.requireIndex(req.index_name).index as LocalIndex;
|
|
15
|
+
const topK = req.top_k || 10;
|
|
16
|
+
const filter = parseFilterJson(req.filter);
|
|
17
|
+
|
|
18
|
+
let vector: number[];
|
|
19
|
+
if (req.vector && req.vector.length > 0) {
|
|
20
|
+
vector = req.vector;
|
|
21
|
+
} else if (req.text && req.text.length > 0) {
|
|
22
|
+
if (!embeddings) {
|
|
23
|
+
throw grpcError(grpc.status.FAILED_PRECONDITION, 'No embeddings model configured on the server');
|
|
24
|
+
}
|
|
25
|
+
const response = await embeddings.createEmbeddings(req.text);
|
|
26
|
+
if (response.status !== 'success' || !response.output) {
|
|
27
|
+
throw grpcError(grpc.status.INTERNAL, `Embeddings error: ${response.message || 'unknown'}`);
|
|
28
|
+
}
|
|
29
|
+
vector = response.output[0];
|
|
30
|
+
} else {
|
|
31
|
+
throw grpcError(grpc.status.INVALID_ARGUMENT, 'Either text or vector must be provided');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const results = await idx.queryItems(vector, req.text || '', topK, filter);
|
|
35
|
+
return {
|
|
36
|
+
results: results.map((r: QueryResult) => ({
|
|
37
|
+
id: r.item.id,
|
|
38
|
+
metadata: toProtoMetadata(r.item.metadata as Record<string, any>),
|
|
39
|
+
vector: Array.from(r.item.vector),
|
|
40
|
+
norm: r.item.norm,
|
|
41
|
+
score: r.score,
|
|
42
|
+
})),
|
|
43
|
+
};
|
|
44
|
+
}),
|
|
45
|
+
|
|
46
|
+
QueryDocuments: wrapHandler(async (call: grpc.ServerUnaryCall<any, any>) => {
|
|
47
|
+
const req = call.request;
|
|
48
|
+
if (!req.index_name) {
|
|
49
|
+
throw grpcError(grpc.status.INVALID_ARGUMENT, 'index_name is required');
|
|
50
|
+
}
|
|
51
|
+
const { docIndex } = manager.requireDocumentIndex(req.index_name);
|
|
52
|
+
if (!req.query) {
|
|
53
|
+
throw grpcError(grpc.status.INVALID_ARGUMENT, 'query is required');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const filter = parseFilterJson(req.filter);
|
|
57
|
+
const results = await docIndex.queryDocuments(req.query, {
|
|
58
|
+
maxDocuments: req.max_documents || 10,
|
|
59
|
+
maxChunks: req.max_chunks || 50,
|
|
60
|
+
filter,
|
|
61
|
+
isBm25: req.use_bm25 || false,
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const protoResults = [];
|
|
65
|
+
for (const result of results) {
|
|
66
|
+
const chunks = [];
|
|
67
|
+
for (const chunk of result.chunks) {
|
|
68
|
+
const text = await (async () => {
|
|
69
|
+
try {
|
|
70
|
+
const doc = result;
|
|
71
|
+
const startPos = (chunk.item.metadata as any).startPos || 0;
|
|
72
|
+
const endPos = (chunk.item.metadata as any).endPos || 0;
|
|
73
|
+
const fullText = await doc.loadText();
|
|
74
|
+
return fullText.substring(startPos, endPos + 1);
|
|
75
|
+
} catch {
|
|
76
|
+
return '';
|
|
77
|
+
}
|
|
78
|
+
})();
|
|
79
|
+
chunks.push({
|
|
80
|
+
text,
|
|
81
|
+
score: chunk.score,
|
|
82
|
+
token_count: 0,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
protoResults.push({
|
|
86
|
+
uri: result.uri,
|
|
87
|
+
document_id: result.id,
|
|
88
|
+
chunks,
|
|
89
|
+
score: result.score,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return { results: protoResults };
|
|
94
|
+
}),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import * as grpc from '@grpc/grpc-js';
|
|
2
|
+
import { IndexManager } from '../IndexManager';
|
|
3
|
+
import { LocalDocumentIndex } from '../../LocalDocumentIndex';
|
|
4
|
+
import { wrapHandler, grpcError } from './helpers';
|
|
5
|
+
|
|
6
|
+
export function createStatsHandlers(manager: IndexManager) {
|
|
7
|
+
return {
|
|
8
|
+
GetIndexStats: wrapHandler(async (call: grpc.ServerUnaryCall<any, any>) => {
|
|
9
|
+
const req = call.request;
|
|
10
|
+
if (!req.index_name) {
|
|
11
|
+
throw grpcError(grpc.status.INVALID_ARGUMENT, 'index_name is required');
|
|
12
|
+
}
|
|
13
|
+
const managed = manager.requireIndex(req.index_name);
|
|
14
|
+
const stats = await managed.index.getIndexStats();
|
|
15
|
+
return {
|
|
16
|
+
version: stats.version,
|
|
17
|
+
format: managed.format,
|
|
18
|
+
item_count: stats.items,
|
|
19
|
+
metadata_config_count: stats.metadata_config?.indexed?.length || 0,
|
|
20
|
+
};
|
|
21
|
+
}),
|
|
22
|
+
|
|
23
|
+
GetCatalogStats: wrapHandler(async (call: grpc.ServerUnaryCall<any, any>) => {
|
|
24
|
+
const req = call.request;
|
|
25
|
+
if (!req.index_name) {
|
|
26
|
+
throw grpcError(grpc.status.INVALID_ARGUMENT, 'index_name is required');
|
|
27
|
+
}
|
|
28
|
+
const { docIndex } = manager.requireDocumentIndex(req.index_name);
|
|
29
|
+
const stats = await docIndex.getCatalogStats();
|
|
30
|
+
return {
|
|
31
|
+
version: stats.version,
|
|
32
|
+
document_count: stats.documents,
|
|
33
|
+
chunk_count: stats.chunks,
|
|
34
|
+
metadata_counts: {},
|
|
35
|
+
};
|
|
36
|
+
}),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { FileType } from "./FileType";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Filter to apply when listing files.
|
|
5
|
+
*/
|
|
6
|
+
export type ListFilesFilter = 'files' | 'folders' | 'all';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Interface implemented by storage plugins.
|
|
10
|
+
*/
|
|
11
|
+
export interface FileStorage {
|
|
12
|
+
/**
|
|
13
|
+
* Creates a new file with the given path and content.
|
|
14
|
+
* @remarks
|
|
15
|
+
* Throws an error if the file already exists.
|
|
16
|
+
* @param filePath Path to file to create.
|
|
17
|
+
* @param content Content to write to the file.
|
|
18
|
+
*/
|
|
19
|
+
createFile(filePath: string, content: Buffer|string): Promise<void>;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Creates a folder with the given path.
|
|
23
|
+
* @remarks
|
|
24
|
+
* Any missing parent folders will also be created.
|
|
25
|
+
* @param folderPath Path to folder to create.
|
|
26
|
+
*/
|
|
27
|
+
createFolder(folderPath: string): Promise<void>;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Deletes the file at the given path if it exists.
|
|
31
|
+
* @param filePath Path to file to delete.
|
|
32
|
+
*/
|
|
33
|
+
deleteFile(filePath: string): Promise<void>;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Deletes the folder at the given path if it exists.
|
|
37
|
+
* @remarks
|
|
38
|
+
* All files and folders within the folder will also be deleted.
|
|
39
|
+
* @param folderPath Path to folder to delete.
|
|
40
|
+
*/
|
|
41
|
+
deleteFolder(folderPath: string): Promise<void>;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Returns the details of an existing file or folder.
|
|
45
|
+
* @remarks
|
|
46
|
+
* Throws an error if the file or folder does not exist.
|
|
47
|
+
* @param fileOrFolderPath File or folder path to get details for.
|
|
48
|
+
* @returns Details of the file or folder.
|
|
49
|
+
*/
|
|
50
|
+
getDetails(fileOrFolderPath: string): Promise<FileDetails>;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Lists all files in the given folder.
|
|
54
|
+
* @param folderPath Folder to list files in.
|
|
55
|
+
* @param filter Optional. Type of entries to return. Defaults to 'all'.
|
|
56
|
+
*/
|
|
57
|
+
listFiles(folderPath: string, filter?: ListFilesFilter): Promise<FileDetails[]>;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Returns true if a file or folder exists at the given path.
|
|
61
|
+
* @param fileOrFolderPath File or folder path to check.
|
|
62
|
+
*/
|
|
63
|
+
pathExists(fileOrFolderPath: string): Promise<boolean>;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Reads the file at the given path.
|
|
67
|
+
* @param filePath Path to file to read.
|
|
68
|
+
*/
|
|
69
|
+
readFile(filePath: string): Promise<Buffer>;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Creates or replaces the file at the given path with the given content.
|
|
73
|
+
* @param filePath Path to file to write.
|
|
74
|
+
* @param content Content to write to the file.
|
|
75
|
+
*/
|
|
76
|
+
upsertFile(filePath: string, content: Buffer|string): Promise<void>;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Details of a file or folder.
|
|
81
|
+
*/
|
|
82
|
+
export interface FileDetails {
|
|
83
|
+
/**
|
|
84
|
+
* Name of the file or folder.
|
|
85
|
+
*/
|
|
86
|
+
name: string;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Path to the file or folder.
|
|
90
|
+
*/
|
|
91
|
+
path: string;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* True if the entry is a folder.
|
|
95
|
+
*/
|
|
96
|
+
isFolder: boolean;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* The files type if known.
|
|
100
|
+
* @remarks
|
|
101
|
+
* Based off the file extension. This will be undefined for folders and for extensions that are not
|
|
102
|
+
* in the `FileExt[]` array.
|
|
103
|
+
*/
|
|
104
|
+
fileType?: FileType;
|
|
105
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import sinon from 'sinon';
|
|
3
|
+
import { FileStorageUtilities } from './FileStorageUtilities';
|
|
4
|
+
import type { FileStorage } from './FileStorage';
|
|
5
|
+
|
|
6
|
+
describe('FileStorageUtilities', () => {
|
|
7
|
+
afterEach(() => {
|
|
8
|
+
sinon.restore();
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
describe('ensureFolderExists', () => {
|
|
12
|
+
it('does not create the folder when it already exists', async () => {
|
|
13
|
+
const folderPath = '/already/there';
|
|
14
|
+
const storage = {
|
|
15
|
+
pathExists: sinon.stub().resolves(true),
|
|
16
|
+
createFolder: sinon.stub(),
|
|
17
|
+
} as unknown as FileStorage;
|
|
18
|
+
|
|
19
|
+
await FileStorageUtilities.ensureFolderExists(storage, folderPath);
|
|
20
|
+
|
|
21
|
+
sinon.assert.calledOnceWithExactly((storage as any).pathExists, folderPath);
|
|
22
|
+
sinon.assert.notCalled((storage as any).createFolder);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('creates the folder when it does not exist', async () => {
|
|
26
|
+
const folderPath = '/needs/creation';
|
|
27
|
+
const storage = {
|
|
28
|
+
pathExists: sinon.stub().resolves(false),
|
|
29
|
+
createFolder: sinon.stub().resolves(),
|
|
30
|
+
} as unknown as FileStorage;
|
|
31
|
+
|
|
32
|
+
await FileStorageUtilities.ensureFolderExists(storage, folderPath);
|
|
33
|
+
|
|
34
|
+
sinon.assert.calledOnceWithExactly((storage as any).pathExists, folderPath);
|
|
35
|
+
sinon.assert.calledOnceWithExactly((storage as any).createFolder, folderPath);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe('getFileType', () => {
|
|
40
|
+
it('returns type for known extension (lowercase)', () => {
|
|
41
|
+
assert.strictEqual(FileStorageUtilities.getFileType('/any/path/file.txt'), 'txt');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('returns type for known extension (case-insensitive)', () => {
|
|
45
|
+
assert.strictEqual(FileStorageUtilities.getFileType('/any/path/FILE.TXT'), 'txt');
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('returns undefined for unknown extension', () => {
|
|
49
|
+
assert.strictEqual(FileStorageUtilities.getFileType('/any/path/file.unknown'), undefined);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('returns undefined when no extension', () => {
|
|
53
|
+
assert.strictEqual(FileStorageUtilities.getFileType('/any/path/filename'), undefined);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('returns undefined for trailing dot', () => {
|
|
57
|
+
assert.strictEqual(FileStorageUtilities.getFileType('/any/path/file.'), undefined);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('getFileTypeFromContentType', () => {
|
|
62
|
+
it('returns mapped type when content type is directly mapped', () => {
|
|
63
|
+
assert.strictEqual(FileStorageUtilities.getFileTypeFromContentType('text/plain'), 'txt');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('falls back to subtype when not directly mapped', () => {
|
|
67
|
+
// Not directly mapped but subtype is a known extension
|
|
68
|
+
assert.strictEqual(FileStorageUtilities.getFileTypeFromContentType('application/png'), 'png');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('handles "+" in subtype by using part before plus (may be unknown)', () => {
|
|
72
|
+
assert.strictEqual(FileStorageUtilities.getFileTypeFromContentType('application/ld+json'), undefined);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('returns undefined for invalid content type format (no slash)', () => {
|
|
76
|
+
assert.strictEqual(FileStorageUtilities.getFileTypeFromContentType('invalid'), undefined);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
describe('tryDeleteFile', () => {
|
|
81
|
+
it('returns undefined on successful delete', async () => {
|
|
82
|
+
const filePath = '/tmp/success.bin';
|
|
83
|
+
const storage = {
|
|
84
|
+
deleteFile: sinon.stub().resolves(),
|
|
85
|
+
} as unknown as FileStorage;
|
|
86
|
+
|
|
87
|
+
const result = await FileStorageUtilities.tryDeleteFile(storage, filePath);
|
|
88
|
+
|
|
89
|
+
sinon.assert.calledOnceWithExactly((storage as any).deleteFile, filePath);
|
|
90
|
+
assert.strictEqual(result, undefined);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('returns the error on failed delete', async () => {
|
|
94
|
+
const filePath = '/tmp/fail.bin';
|
|
95
|
+
const error = new Error('boom');
|
|
96
|
+
const storage = {
|
|
97
|
+
deleteFile: sinon.stub().rejects(error),
|
|
98
|
+
} as unknown as FileStorage;
|
|
99
|
+
|
|
100
|
+
const result = await FileStorageUtilities.tryDeleteFile(storage, filePath);
|
|
101
|
+
|
|
102
|
+
sinon.assert.calledOnceWithExactly((storage as any).deleteFile, filePath);
|
|
103
|
+
assert.strictEqual(result, error);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
});
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { FileStorage } from "./FileStorage";
|
|
2
|
+
import { FileExt, FileType, ContentTypeMap } from "./FileType";
|
|
3
|
+
import { pathUtils as path } from '../utils/pathUtils';
|
|
4
|
+
/**
|
|
5
|
+
* Utility functions for working with FileStorage abstractions.
|
|
6
|
+
*/
|
|
7
|
+
export class FileStorageUtilities {
|
|
8
|
+
/**
|
|
9
|
+
* Ensures that a folder exists in the given storage.
|
|
10
|
+
* @param storage Storage to create the folder in.
|
|
11
|
+
* @param folderPath Path to folder to ensure is created.
|
|
12
|
+
*/
|
|
13
|
+
public static async ensureFolderExists(storage: FileStorage, folderPath: string): Promise<void> {
|
|
14
|
+
if (!await storage.pathExists(folderPath)) {
|
|
15
|
+
await storage.createFolder(folderPath);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Returns the file type of a file based on its extension.
|
|
21
|
+
* @remarks
|
|
22
|
+
* The file type is determined by the file extension. Only extensions found in the
|
|
23
|
+
* `FileExt[]` array are returned.
|
|
24
|
+
* @param filePath Path to file to get type for.
|
|
25
|
+
* @returns The file type, or undefined if the file type is unknown.
|
|
26
|
+
*/
|
|
27
|
+
public static getFileType(filePath: string): FileType | undefined {
|
|
28
|
+
// Get extension from file
|
|
29
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
30
|
+
if (ext.length > 1) {
|
|
31
|
+
// Ensure the extension is valid
|
|
32
|
+
const fileType = ext.substring(1);
|
|
33
|
+
if (FileExt.includes(fileType)) {
|
|
34
|
+
return fileType as FileType;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Maps a content type to a file type.
|
|
43
|
+
* @param contentType Content type to map.
|
|
44
|
+
* @returns File type, or undefined if the content type is unknown.
|
|
45
|
+
*/
|
|
46
|
+
public static getFileTypeFromContentType(contentType: string): FileType | undefined {
|
|
47
|
+
if (Object.prototype.hasOwnProperty.call(ContentTypeMap, contentType)) {
|
|
48
|
+
return ContentTypeMap[contentType];
|
|
49
|
+
} else {
|
|
50
|
+
// Try to find a matching file type
|
|
51
|
+
const parts = contentType.split('/');
|
|
52
|
+
if (parts.length == 2) {
|
|
53
|
+
const fileType = parts[1].includes('+') ? parts[1].split('+')[0] : parts[1];
|
|
54
|
+
if (FileExt.includes(fileType)) {
|
|
55
|
+
return fileType as FileType;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Deletes a file from storage if it exists.
|
|
65
|
+
* @param storage Storage to delete the file from.
|
|
66
|
+
* @param filePath Path to the file to delete.
|
|
67
|
+
* @returns An Error if the file could not be deleted, otherwise undefined.
|
|
68
|
+
*/
|
|
69
|
+
public static async tryDeleteFile(storage: FileStorage, filePath: string): Promise<Error | undefined> {
|
|
70
|
+
try {
|
|
71
|
+
await storage.deleteFile(filePath);
|
|
72
|
+
return undefined;
|
|
73
|
+
} catch (err: unknown) {
|
|
74
|
+
return err as Error;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export const ImageFileExt = ['png', 'jpg', 'jpeg', 'gif', 'bmp', 'tiff', 'tif', 'webp', 'svg', 'heic', 'heif'];
|
|
2
|
+
export type ImageFileType = typeof ImageFileExt[number];
|
|
3
|
+
|
|
4
|
+
export const VideoFileExt = ['mp4', 'avi', 'mov', 'wmv', 'flv', 'mkv', 'webm', 'mpg', 'mpeg', '3gp'];
|
|
5
|
+
export type VideoFileType = typeof VideoFileExt[number];
|
|
6
|
+
|
|
7
|
+
export const AudioFileExt = ['mp3', 'wav', 'flac', 'm4a', 'aac', 'ogg', 'wma', 'aiff', 'alac'];
|
|
8
|
+
export type AudioFileType = typeof AudioFileExt[number];
|
|
9
|
+
|
|
10
|
+
export const MediaFileExt = [...ImageFileExt, ...VideoFileExt, ...AudioFileExt];
|
|
11
|
+
export type MediaFileType = ImageFileType | VideoFileType | AudioFileType;
|
|
12
|
+
|
|
13
|
+
export const ModelFileExt = ['obj', 'fbx', 'stl', 'dae', 'ply', '3ds', 'gltf', 'glb'];
|
|
14
|
+
export type ModelFileType = typeof ModelFileExt[number];
|
|
15
|
+
|
|
16
|
+
export const ArchiveFileExt = ['zip', 'tar', 'gz', '7z', 'rar', 'tgz'];
|
|
17
|
+
export type ArchiveFileType = typeof ArchiveFileExt[number];
|
|
18
|
+
|
|
19
|
+
export const SystemFileExt = ['exe', 'dll', 'bin', 'iso', 'dmg', 'msi', 'deb', 'rpm', 'apk', 'appimage', 'rom', 'efi'];
|
|
20
|
+
export type SystemFileType = typeof SystemFileExt[number];
|
|
21
|
+
|
|
22
|
+
export const DatabaseFileExt = ['sqlite', 'sql', 'mdb', 'accdb'];
|
|
23
|
+
export type DatabaseFileType = typeof DatabaseFileExt[number];
|
|
24
|
+
|
|
25
|
+
export const BinaryFileExt = [...MediaFileExt, ...ModelFileExt, ...ArchiveFileExt, ...SystemFileExt, ...DatabaseFileExt];
|
|
26
|
+
|
|
27
|
+
export const DocumentFileExt = ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'rtf', 'tex'];
|
|
28
|
+
export type DocumentFileType = typeof DocumentFileExt[number];
|
|
29
|
+
|
|
30
|
+
export const TextDocumentFileExt = ['txt', 'csv', 'log', 'md', 'rst'];
|
|
31
|
+
export type TextDocumentFileType = typeof TextDocumentFileExt[number];
|
|
32
|
+
|
|
33
|
+
export const CodeFileExt = ['js', 'ts', 'jsx', 'tsx', 'py', 'java', 'c', 'cpp', 'cs', 'php', 'go', 'rb', 'rs', 'swift', 'kt', 'dart', 'sh', 'bash', 'ps1', 'bat', 'cmd', 'html', 'css', 'scss', 'sass', 'less', 'xml', 'json', 'yaml', 'yml', 'ini', 'conf', 'cfg', 'env'];
|
|
34
|
+
export type CodeFileType = typeof CodeFileExt[number];
|
|
35
|
+
|
|
36
|
+
export const PlainTextFileExt = [...TextDocumentFileExt, ...CodeFileExt];
|
|
37
|
+
export type PlainTextFileType = TextDocumentFileType | CodeFileType;
|
|
38
|
+
|
|
39
|
+
export const FileExt = [...BinaryFileExt, ...DocumentFileExt, ...PlainTextFileExt];
|
|
40
|
+
export type FileType = typeof FileExt[number] | 'folder';
|
|
41
|
+
|
|
42
|
+
export const ContentTypeMap: Record<string, FileType> = {
|
|
43
|
+
"text/html": "html",
|
|
44
|
+
"text/plain": "txt",
|
|
45
|
+
"text/css": "css",
|
|
46
|
+
"text/javascript": "js",
|
|
47
|
+
"application/json": "json",
|
|
48
|
+
"application/xml": "xml",
|
|
49
|
+
"application/javascript": "js",
|
|
50
|
+
"application/pdf": "pdf",
|
|
51
|
+
"image/jpeg": "jpg",
|
|
52
|
+
"image/png": "png",
|
|
53
|
+
"image/gif": "gif",
|
|
54
|
+
"image/svg+xml": "svg",
|
|
55
|
+
"application/zip": "zip",
|
|
56
|
+
"application/octet-stream": "bin",
|
|
57
|
+
"audio/mpeg": "mp3",
|
|
58
|
+
"video/mp4": "mp4",
|
|
59
|
+
"video/webm": "webm",
|
|
60
|
+
"text/csv": "csv",
|
|
61
|
+
};
|