import { type ArrayOrValue, type DecisionFunction, type Maybe, type PromiseOrValue, type SlashPathFile, type SlashPathFolder, type SlashPathPart, type SlashPathPathMatcherConfigInput, type SlashPathSubPathMatcherConfig } from '@dereekb/util'; import { type UploadedFileTypeIdentifier } from './storagefile.upload'; import { type FirebaseAuthUserId, type StorageBucketId } from '../../common'; import { type StoredFileReader } from './storagefile.file'; /** * @module storagefile.upload.determiner * * Provides the upload type determination system for classifying uploaded files. * * Determiners inspect a file's path, name, metadata, or content to assign an * {@link UploadedFileTypeIdentifier} with a confidence level. Multiple determiners * can be combined via {@link combineUploadFileTypeDeterminers}, and user detection * can be added via {@link determineUserByFolderWrapperFunction}. * * Built-in determiners: * - {@link determineByFileName} — match by file name or prefix * - {@link determineByFolderName} — match by parent folder name * - {@link determineByFilePath} — match by full path pattern with optional bucket filtering */ /** * Confidence level of a file type determination. Higher values indicate higher confidence. * * When multiple determiners match, the one with the highest level wins. * * Higher values indicate higher confidence. * * For example, a match by a file name would potentially have a higher confidence than a match by folder name * in the case where we have two determiners: * - One looking for 'avatar.png' exactly * - One looking for the folder 'photos' exactly * * In this case, the folder match should return a lower determination level than the file name match, so that * it is processed properly. * * In the ideal case there generally shouldn't be two determiners that could potentially match the same file. * * @semanticType * @semanticTopic numeric * @semanticTopic dereekb-firebase:storage-file */ export type UploadedFileTypeDeterminationLevel = number; /** * Lower determination level. */ export declare const LOW_UPLOADED_FILE_TYPE_DETERMINATION_LEVEL = 1; /** * Default determination level. */ export declare const DEFAULT_UPLOADED_FILE_TYPE_DETERMINATION_LEVEL = 2; /** * High determination level. */ export declare const HIGH_UPLOADED_FILE_TYPE_DETERMINATION_LEVEL = 3; /** * Exact match determination level. * * The default highest determination level. */ export declare const EXACT_UPLOADED_FILE_TYPE_DETERMINATION_LEVEL = 10; /** * Result of a StorageFileUploadTypeDeterminer. */ export interface UploadedFileTypeDeterminerResult { /** * The input file details accessor. */ readonly input: StoredFileReader; /** * The determined type identifier. */ readonly type: UploadedFileTypeIdentifier; /** * The level of confidence in the determined type. */ readonly level: UploadedFileTypeDeterminationLevel; /** * The user this file appears to be associated with. * * Unset if no user determination could be made. */ readonly user?: Maybe; } /** * Async function that determines the upload type of a StorageFile. * * Returns a determination result or undefined if the function cannot determine the upload type. */ export type UploadedFileTypeDeterminationFunction = (input: StoredFileReader) => PromiseOrValue>; /** * Strategy interface for determining the upload type of a file in Firebase Storage. * * Implement custom determiners for application-specific file type classification, * or use the built-in factory functions ({@link determineByFileName}, {@link determineByFolderName}, * {@link determineByFilePath}). */ export interface UploadedFileTypeDeterminer { /** * Determine function */ readonly determine: UploadedFileTypeDeterminationFunction; /** * Returns a list of possible file types that this determiner can handle. */ getPossibleFileTypes(): UploadedFileTypeIdentifier[]; } export interface DetermineByFileNameConfig { /** * The file type identifier to match on. */ readonly fileType: UploadedFileTypeIdentifier; /** * Case-sensitive file name to try and match on. * * If this value contains a file type/extension then will match on the file name exactly. * * Otherwise, will match on file names that begin with this value (e.g. "image" will match "image.png", "image.jpg", "image-test.png", etc.). */ readonly match: SlashPathFile; /** * The determination level to use if the file name exactly matches the match value. * * Defaults to EXACT_UPLOADED_FILE_TYPE_DETERMINATION_LEVEL. */ readonly exactMatchDeterminationLevel?: Maybe; /** * The determination level to use if the file name matches the match value. * * Defaults to HIGH_UPLOADED_FILE_TYPE_DETERMINATION_LEVEL. */ readonly nameMatchDeterminationLevel?: Maybe; } /** * Creates a determiner that classifies files by their file name. * * If the match string includes a file extension (e.g., `avatar.png`), only exact matches succeed * at {@link EXACT_UPLOADED_FILE_TYPE_DETERMINATION_LEVEL}. Otherwise, prefix matching is used * (e.g., `image` matches `image.png`, `image-test.jpg`) at {@link HIGH_UPLOADED_FILE_TYPE_DETERMINATION_LEVEL}. * * @param config - file type, match string, and optional determination levels * @returns an UploadedFileTypeDeterminer that classifies by file name * * @example * ```ts * const avatarDeterminer = determineByFileName({ * fileType: 'avatar', * match: 'avatar.png' * }); * ``` */ export declare function determineByFileName(config: DetermineByFileNameConfig): UploadedFileTypeDeterminer; export interface DetermineByFolderNameConfig { /** * The file type identifier to determine. */ readonly fileType: UploadedFileTypeIdentifier; /** * The case-sensitive folder name to match on. */ readonly match: SlashPathPart; } /** * Creates a determiner that classifies files by their parent folder name. * * Matches at {@link EXACT_UPLOADED_FILE_TYPE_DETERMINATION_LEVEL} when the folder path * exactly equals the configured match string. * * @param config - file type and folder name to match * @returns an UploadedFileTypeDeterminer that classifies by folder name * * @example * ```ts * const photoDeterminer = determineByFolderName({ * fileType: 'photo', * match: 'photos' * }); * ``` */ export declare function determineByFolderName(config: DetermineByFolderNameConfig): UploadedFileTypeDeterminer; export interface DetermineByFilePathConfig { /** * The file type identifier to return. */ readonly fileType: UploadedFileTypeIdentifier; /** * Optional decision function to filter/match the bucket. */ readonly matchBucket?: Maybe>; /** * Match path configuration to use. */ readonly match: SlashPathPathMatcherConfigInput; /** * Optional decision function to further filter/match the input. */ readonly matchFileDetails?: Maybe>; /** * The determination level to use if the file name matches the match value. * * Defaults to HIGH_UPLOADED_FILE_TYPE_DETERMINATION_LEVEL. */ readonly matchDeterminationLevel?: Maybe; } /** * Creates a determiner that classifies files by their full storage path, with optional * bucket filtering and additional file details matching. * * Uses {@link slashPathPathMatcher} for path pattern matching. Most flexible of the * built-in determiners. * * @param config - file type, path match config, optional bucket/file filters * @returns an UploadedFileTypeDeterminer that classifies by storage path * * @example * ```ts * const reportDeterminer = determineByFilePath({ * fileType: 'report', * match: { targetPath: 'reports', matchType: 'startsWith' }, * matchBucket: (bucket) => bucket === 'my-bucket' * }); * ``` */ export declare function determineByFilePath(config: DetermineByFilePathConfig): UploadedFileTypeDeterminer; export interface DetermineUserByFolderWrapperFunctionConfig { /** * Requires the detection of a user. * * If no user can be detected, the determination will return null, even if the wrapped determiner would have otherwise returned a result. * * Defaults to false. */ readonly requireUser?: boolean; /** * The root folder/path to filter on. * * Defaults to the value of UPLOADS_FOLDER_PATH. */ readonly rootFolder: SlashPathPart; /** * Specific user folder/path to filter on. This path must match the path that comes after the rootFolder exactly. * * For example, if the rootFolder is "uploads" and the userFolderPrefix is "u", then the user at the path "uploads/u/123/avatar.png" would be "123". */ readonly userFolderPrefix: SlashPathPart | SlashPathFolder; /** * Sub path matcher configuration. * * If provided, the rootFolder and userFolderPrefix are ignored. */ readonly matchSubPath?: Maybe; /** * Whether to allow sub-paths after the user folder. * * Defaults to true. */ readonly allowSubPaths?: boolean; } /** * Wraps the input determiner with determineUserByFolder. */ export type DetermineUserByFolderDeterminerWrapperFunction = (determiner: UploadedFileTypeDeterminer) => UploadedFileTypeDeterminer; /** * Creates a wrapper function that adds user detection to any {@link UploadedFileTypeDeterminer} * by inspecting the folder path structure. * * Extracts the user ID from a path like `{rootFolder}/{userFolderPrefix}/{userId}/{file}`. * If `requireUser` is true, the determination fails when no user can be detected. * * @param config - root folder, user prefix, and matching options * @returns a wrapper function that adds user detection to any UploadedFileTypeDeterminer * * @example * ```ts * const addUser = determineUserByFolderWrapperFunction({ * rootFolder: 'uploads', * userFolderPrefix: 'u' * }); * const withUser = addUser(myDeterminer); * ``` */ export declare function determineUserByFolderWrapperFunction(config: DetermineUserByFolderWrapperFunctionConfig): DetermineUserByFolderDeterminerWrapperFunction; /** * Convenience wrapper pre-configured for the standard uploads folder structure (`uploads/u/{userId}/...`). * * @param config - optional matching options (rootFolder and userFolderPrefix are pre-configured) * @returns a wrapper function that adds user detection for the standard uploads folder structure * * @example * ```ts * const addUser = determineUserByUserUploadsFolderWrapperFunction(); * const withUser = addUser(myDeterminer); * ``` */ export declare function determineUserByUserUploadsFolderWrapperFunction(config?: Omit): DetermineUserByFolderDeterminerWrapperFunction; /** * Configuration for determineUserByFolder(). */ export interface DetermineUserByFolderFunctionConfig extends DetermineUserByFolderWrapperFunctionConfig { /** * The determiner to wrap. */ readonly determiner: UploadedFileTypeDeterminer; } /** * Convenience function for using determineUserByFolderWrapperFunction directly on a pre-set determiner. * * @param config Configuration. * @returns The wrapped UploadedFileTypeDeterminer. */ export declare function determineUserByFolder(config: DetermineUserByFolderFunctionConfig): UploadedFileTypeDeterminer; /** * Configuration for limitUploadFileTypeDeterminer. */ export interface LimitUploadFileTypeDeterminerConfig { /** * The file type(s) to allow. */ readonly allowedType: ArrayOrValue; /** * The determiner to wrap. */ readonly determiner: UploadedFileTypeDeterminer; } /** * Wraps an {@link UploadedFileTypeDeterminer} to only return results for the specified file types. * * Useful for scoping a broad determiner to a subset of types in a specific context. * * @param determiner - the determiner to filter * @param types - allowed file type identifier(s) * @returns an UploadedFileTypeDeterminer that only returns results for the specified file types * * @example * ```ts * const limited = limitUploadFileTypeDeterminer(broadDeterminer, ['avatar', 'photo']); * ``` */ export declare function limitUploadFileTypeDeterminer(determiner: UploadedFileTypeDeterminer, types: ArrayOrValue): UploadedFileTypeDeterminer; export interface CombineUploadFileTypeDeterminerConfig { /** * The determiners to combine/try. * * Determiners are tried in order and sequentially. */ readonly determiners: UploadedFileTypeDeterminer[]; /** * If true, will complete the search early if any determiner returns a LOW_UPLOADED_FILE_TYPE_DETERMINATION_LEVEL or higher result level. * * Defaults to false. */ readonly completeSearchOnFirstMatch?: Maybe; /** * The level at which to complete the search early if any determiner returns a result at or above that level. * * Ignored if `completeSearchOnFirstMatch` is true. */ readonly completeSearchAtLevel?: Maybe; } /** * Combines multiple {@link UploadedFileTypeDeterminer} instances into a single determiner * that tries each in order and returns the highest-confidence match. * * If only one determiner is provided, it is returned unwrapped. The search can be * short-circuited via `completeSearchOnFirstMatch` or `completeSearchAtLevel`. * * @param config - determiners to combine and optional early-exit settings * @returns a combined UploadedFileTypeDeterminer that returns the highest-confidence match * * @example * ```ts * const combined = combineUploadFileTypeDeterminers({ * determiners: [avatarDeterminer, photoDeterminer, documentDeterminer], * completeSearchOnFirstMatch: true * }); * ``` */ export declare function combineUploadFileTypeDeterminers(config: CombineUploadFileTypeDeterminerConfig): UploadedFileTypeDeterminer;