import { type ContentTypeMimeType, type Maybe, type SlashPath, type SlashPathFile, type SlashPathFolder, type SlashPathTypedFileExtension, type SlashPathUntypedFile } from '@dereekb/util'; import { type FirebaseAuthUserId } from '../../common/auth/auth'; import { type StorageFilePurpose } from '../storagefile/storagefile.id'; import { type StorageFilePurposeUploadPolicy, type UploadedFileTypeIdentifier } from '../storagefile/storagefile.upload'; import { type FormSpaceFileSlot, type FormSpaceId } from './formspace.id'; /** * @module formspace.upload * * Where a FormSpace's uploads land, and how a landed file is read back into `{ formSpaceId, slot }`. * * ONE purpose for every FormSpace file, across every type. The per-type rules live in the * {@link FormSpaceTypeConfig} registry and are enforced by the initializer against the loaded FormSpace, so * a new form type needs no new purpose, no new storage-rules block, and no new initializer. */ /** * {@link UploadedFileTypeIdentifier} for a file uploaded into a FormSpace. */ export declare const FORM_SPACE_UPLOADED_FILE_TYPE_IDENTIFIER: UploadedFileTypeIdentifier; /** * The single {@link StorageFilePurpose} carried by every FormSpace upload. */ export declare const FORM_SPACE_PURPOSE: StorageFilePurpose; /** * The folder under a user's uploads folder that FormSpace uploads land in. */ export declare const FORM_SPACE_UPLOADS_FOLDER_NAME = "formSpace"; /** * Returns the uploads folder path for one FormSpace slot. * * @param uid - The uploading Firebase Auth user id. * @param formSpaceId - The FormSpace being uploaded into. * @param slot - The slot being filled. * @returns The SlashPathFolder the slot's uploads land in. * * @example * ```ts * formSpaceUploadsFolderPath('user123', 'fsp1', 'resume'); * // 'uploads/u/user123/formSpace/fsp1/resume/' * ``` */ export declare function formSpaceUploadsFolderPath(uid: FirebaseAuthUserId, formSpaceId: FormSpaceId, slot: FormSpaceFileSlot): SlashPathFolder; /** * Input for {@link formSpaceUploadsFilePath}. */ export interface FormSpaceUploadsFilePathInput { readonly uid: FirebaseAuthUserId; readonly formSpaceId: FormSpaceId; readonly slot: FormSpaceFileSlot; readonly filename: SlashPathFile; } /** * Returns the full uploads path for one file in one FormSpace slot. * * The FormSpace id and the slot are IN THE PATH rather than in custom metadata because the initializer runs * from a storage-triggered sweep that only ever sees the path — and because the storage rules can then keep * the write inside the uploader's own namespace with no Firestore read. * * @param input - The uploader, the target space and slot, and the file name. * @returns The full upload path. * * @example * ```ts * formSpaceUploadsFilePath({ uid: 'user123', formSpaceId: 'fsp1', slot: 'resume', filename: 'resume.pdf' }); * // 'uploads/u/user123/formSpace/fsp1/resume/resume.pdf' * ``` * * @__NO_SIDE_EFFECTS__ */ export declare function formSpaceUploadsFilePath(input: FormSpaceUploadsFilePathInput): SlashPath; /** * Root folder every FormSpace's accepted files are moved to, out of the transient uploads folder. */ export declare const FORM_SPACE_FILES_ROOT_FOLDER_PATH: SlashPathFolder; /** * Input for {@link formSpaceFileStoragePath}. */ export interface FormSpaceFileStoragePathInput { readonly formSpaceId: FormSpaceId; readonly slot: FormSpaceFileSlot; /** * The index claimed from the space's `fi` counter. */ readonly index: number; /** * The extension, without its leading separator. Absent when neither the uploaded name nor its mime type * named one. */ readonly extension?: Maybe; } /** * Returns the permanent storage path an accepted FormSpace file is moved to. * * Keyed by the space, the slot, and a monotonic INDEX rather than by the uploaded name. A name-keyed * destination is not unique: a file removed from the space's `f` keeps its object until the delete sweep * runs, so re-uploading the same name overwrote it — leaving two StorageFiles on one object, where * deleting the first destroyed the second's bytes. * * The leaf carries at most one separator, which is also the only shape {@link slashPathDetails} can read * ({@link slashPathType} calls two or more `invalid`), so the destination always parses back into a name * and an extension. * * @param input - The space, the slot, the claimed index, and the file's extension. * @returns The permanent storage path. * * @example * ```ts * formSpaceFileStoragePath({ formSpaceId: 'fsp1', slot: 'resume', index: 0, extension: 'pdf' }); * // '/fsp/fsp1/resume/0.pdf' * ``` * * @__NO_SIDE_EFFECTS__ */ export declare function formSpaceFileStoragePath(input: FormSpaceFileStoragePathInput): SlashPath; /** * Input for {@link formSpaceUploadFileNameDetails}. */ export interface FormSpaceUploadFileNameDetailsInput { readonly filename: SlashPathFile; /** * The uploaded file's content type, used to name the extension when the filename does not. */ readonly mimeType?: Maybe; } /** * The uploaded name, split into the parts each layer stores. */ export interface FormSpaceUploadFileNameDetails { /** * The name without its extension, for the StorageFile's `n` — which is UNTYPED by contract (see * {@link StorageFileDisplayName}) because the zip builder merges it with the path's extension. * * Absent for a name that is nothing but an extension, such as `.gitignore`. */ readonly displayName?: Maybe; /** * The extension for the destination leaf. */ readonly extension?: Maybe; /** * The two recomposed — what the FormSpace's `f` entry records as the file's name, and what a download * of it is named. */ readonly fileName: SlashPathFile; } /** * Splits an uploaded filename into the display name and extension the rest of the pipeline stores. * * Normalizes first: an uploaded name may carry any number of separators, and {@link slashPathDetails} * reads a path with two or more as `invalid` and yields neither a name nor an extension for it. The * canonical {@link replaceInvalidFilePathTypeSeparatorsInSlashPath} collapses it to at most one, so * `my.report.pdf` becomes `my_report.pdf` rather than losing its extension entirely. * * Falls back to the mime type for a name that has no extension, which keeps the stored object * self-describing and keeps `fileName` in step with what a download or a zip entry is actually called. * * @param input - The uploaded filename and its content type. * @returns The display name, the extension, and the two recomposed. * * @example * ```ts * formSpaceUploadFileNameDetails({ filename: 'resume.pdf' }); * // { displayName: 'resume', extension: 'pdf', fileName: 'resume.pdf' } * ``` * * @__NO_SIDE_EFFECTS__ */ export declare function formSpaceUploadFileNameDetails(input: FormSpaceUploadFileNameDetailsInput): FormSpaceUploadFileNameDetails; /** * The pieces {@link parseFormSpaceUploadPath} recovers from a FormSpace upload path. */ export interface ParsedFormSpaceUploadPath { readonly uid: FirebaseAuthUserId; readonly formSpaceId: FormSpaceId; readonly slot: FormSpaceFileSlot; readonly filename: SlashPathFile; } /** * Reads a FormSpace upload path back into its parts. * * Returns null for anything that is not exactly a FormSpace upload path, including a path with extra * segments: a nested path would otherwise resolve to a slot name that no config declares, and silently * widening the parse is how an upload lands somewhere nobody validated. * * @param path - The path to parse. * @returns The parsed parts, or null when the path is not a FormSpace upload path. * * @example * ```ts * parseFormSpaceUploadPath('uploads/u/user123/formSpace/fsp1/resume/resume.pdf'); * // { uid: 'user123', formSpaceId: 'fsp1', slot: 'resume', filename: 'resume.pdf' } * ``` * * @__NO_SIDE_EFFECTS__ */ export declare function parseFormSpaceUploadPath(path: SlashPath): Maybe; /** * Upload policy for {@link FORM_SPACE_PURPOSE}. * * The caps here are the OUTER bound — the widest a FormSpace upload may ever be — and they are what * `storage.rules` mirrors. The per-type and per-slot rules in the {@link FormSpaceTypeConfig} registry * narrow it further, and are enforced by the initializer, which is the only layer that can read the * FormSpace to learn which type it even is. */ export declare const FORM_SPACE_UPLOAD_POLICY: StorageFilePurposeUploadPolicy;