import { type ContentTypeMimeType, type FactoryWithRequiredInput, type Maybe, type SlashPath, type SlashPathFile } from '@dereekb/util'; import { type StoragePath } from '../../common/storage/storage'; import { type FirebaseAuthUserId } from '../../common'; import { type StorageFilePurpose } from './storagefile.id'; /** * Root path for all uploaded files in Firebase Storage. * * Files uploaded here are transient and are processed/cleared by the upload initialization * service. The folder structure is: `uploads/u/{userId}/{fileName}`. * * See {@link userUploadsFolderSlashPathFactory} for building user-specific upload paths. */ export declare const UPLOADS_FOLDER_PATH = "uploads"; /** * The folder name that contains the uploads for each user "u". */ export declare const ALL_USER_UPLOADS_FOLDER_NAME = "u"; /** * All users uploads folder path. * * For example, user 12345 will upload their files to folder "uploads/u/12345/". */ export declare const ALL_USER_UPLOADS_FOLDER_PATH = "uploads/u"; /** * Factory that generates the uploads folder SlashPath for a given user ID. */ export type UserUploadsFolderSlashPathFactory = FactoryWithRequiredInput; /** * Creates a {@link UserUploadsFolderSlashPathFactory} that generates per-user upload folder paths. * * @param inputBasePath - Optional custom base path; defaults to {@link ALL_USER_UPLOADS_FOLDER_PATH} * @returns A factory function that generates per-user upload folder slash paths. * * @example * ```ts * const factory = userUploadsFolderSlashPathFactory(); * const path = factory('user123'); * // path === '/uploads/u/user123' * ``` * * @__NO_SIDE_EFFECTS__ */ export declare function userUploadsFolderSlashPathFactory(inputBasePath?: Maybe): UserUploadsFolderSlashPathFactory; /** * Factory that generates a full {@link StoragePath} (with bucket) for a given user's uploads folder. */ export type UserUploadsFolderStoragePathFactory = FactoryWithRequiredInput; /** * Configuration for {@link userUploadsFolderStoragePathFactory}. */ export interface UserUploadsFolderStoragePathFactoryConfig { readonly bucketId: string; readonly basePath?: Maybe; } /** * Creates a {@link UserUploadsFolderStoragePathFactory} that includes the storage bucket ID. * * @param root0 - The configuration object. * @param root0.bucketId - The storage bucket ID to include in each generated path. * @param root0.basePath - Optional custom base path; defaults to {@link ALL_USER_UPLOADS_FOLDER_PATH} * @returns A factory function that generates per-user StoragePath values including the bucket ID. * * @example * ```ts * const factory = userUploadsFolderStoragePathFactory({ bucketId: 'my-bucket' }); * const storagePath = factory('user123'); * // storagePath === { pathString: '/uploads/u/user123', bucketId: 'my-bucket' } * ``` * * @__NO_SIDE_EFFECTS__ */ export declare function userUploadsFolderStoragePathFactory({ bucketId, basePath: inputBasePath }: UserUploadsFolderStoragePathFactoryConfig): UserUploadsFolderStoragePathFactory; /** * StorageFile uploaded-file type identifier. * * Used as a descriminator for choosing the appropriate upload processor. * * The upload type is generally determined by one of a few ways: * - file name: A specific file name (e.g. 'avatar.png' or 'photos/avatar.png') * - folder name: A specific folder name (e.g. 'photos' in 'photos/12345.png') * - metadata: specific metadata value in the uploaded file's custom metadata * - data: specific data in the uploaded file * * @semanticType * @semanticTopic identifier * @semanticTopic string * @semanticTopic dereekb-firebase:storage-file */ export type UploadedFileTypeIdentifier = string; /** * Result of a `StorageFileInitializeFromUploadService.handleNotificationTask()` call, * indicating how the upload initialization concluded. * * - `success` — file was used/processed successfully and a StorageFile was created * - `no_determiner_match` — no {@link UploadedFileTypeDeterminer} could identify this file * - `no_initializer_configured` — the file type was identified but no initializer is registered for it * - `initializer_error` — the initializer threw an error during processing * - `permanent_initializer_failure` — the initializer failed permanently; the file should be deleted */ export type StorageFileInitializeFromUploadResultType = 'success' | 'no_determiner_match' | 'no_initializer_configured' | 'initializer_error' | 'permanent_initializer_failure'; /** * Input passed to {@link StorageFilePurposeUploadPolicy.buildUploadPath} when * computing the upload destination for a signed-upload-url. */ export interface StorageFilePurposeUploadPolicyBuildPathInput { readonly uid: FirebaseAuthUserId; readonly filename?: Maybe; /** * The model the upload is scoped to, for a purpose whose destination is not derivable from the uid alone. * * Required when the policy sets {@link StorageFilePurposeUploadPolicy.requiresScopeInput}. */ readonly scope?: Maybe; } /** * Names the specific model, and optionally the slot within it, that an upload belongs to. * * This is what lets ONE purpose serve many destinations: a FormSpace upload is `{ id: formSpaceId, * subgroup: slot }` under a single `form_space` purpose, rather than a purpose per form type. */ export interface StorageFileUploadScope { /** * Id of the model the upload is scoped to. */ readonly id: string; /** * Slot/subgroup within the scoped model, when the model has more than one. */ readonly subgroup?: Maybe; } /** * Per-purpose constraints for generating short-lived signed upload URLs. * * Implementations are kept in app-level registries (e.g. * `STORAGE_FILE_PURPOSE_UPLOAD_POLICIES` in demo-firebase) and wired into the * server-action context so the `createStorageFileSignedUploadUrl` action can * resolve the policy by purpose at request time. * * The policy ensures the URL it signs targets a path and content-type that * both `storage.rules` and the matching `StorageFileInitializeFromUploadService` * initializer accept. */ export interface StorageFilePurposeUploadPolicy { readonly purpose: StorageFilePurpose; readonly allowedMimeTypes: readonly ContentTypeMimeType[]; readonly maxFileSizeBytes: number; readonly buildUploadPath: (input: StorageFilePurposeUploadPolicyBuildPathInput) => SlashPath; /** * When true, the caller MUST provide a filename for `buildUploadPath`. * When false (e.g. avatar), the path is derived solely from the uid. */ readonly requiresFilenameInput: boolean; /** * When true, the caller MUST provide a {@link StorageFileUploadScope} for `buildUploadPath`. * * Set by a purpose whose destination folder is keyed by a model rather than by the uid alone. The scope * is only a PATH input — it is not itself authorization; the purpose's initializer is what loads the * scoped model and decides whether this uploader may write into it. */ readonly requiresScopeInput?: boolean; }