import { type MaybeNot, type ValuesTypesAsArray, type CommaSeparatedKeysOfObject } from '@dereekb/util'; import { type Functions } from 'firebase/functions'; import { type NonNever } from 'ts-essentials'; import { type FirestoreModelIdentity, type FirestoreModelTypes } from '../../common/firestore/collection'; import { type FirebaseFunctionTypeMap, type FirebaseFunctionMap, type FirebaseFunction } from './function'; import { type FirebaseFunctionTypeConfigMap } from './function.factory'; import { type OnCallCreateModelResult } from '../../common/model/function'; /** * String identifier that routes a CRUD request to a specific sub-handler within a model's * CRUD function. For example, a model's `update` might have specifiers like `'status'` or `'config'` * to target different update behaviors. * * @semanticType * @semanticTopic identifier * @semanticTopic string * @semanticTopic dereekb-firebase:functions */ export type ModelFirebaseCrudFunctionSpecifier = string; /** * Optional reference to a {@link ModelFirebaseCrudFunctionSpecifier}, used to route CRUD requests * to a specific sub-handler. */ export type ModelFirebaseCrudFunctionSpecifierRef = { readonly specifier?: ModelFirebaseCrudFunctionSpecifier; }; /** * Generic CRUD function for a Firestore model. Wraps {@link FirebaseFunction} with model-specific input/output types. */ export type ModelFirebaseCrudFunction = FirebaseFunction; /** * Create function for a model. Returns {@link OnCallCreateModelResult} containing the created document's key by default. */ export type ModelFirebaseCreateFunction = ModelFirebaseCrudFunction; /** * Read function for a model, returning the read data. */ export type ModelFirebaseReadFunction = ModelFirebaseCrudFunction; /** * Update function for a model. Returns void by default. */ export type ModelFirebaseUpdateFunction = ModelFirebaseCrudFunction; /** * Delete function for a model. Returns void by default. */ export type ModelFirebaseDeleteFunction = ModelFirebaseCrudFunction; /** * Maps each model type (from a {@link FirestoreModelIdentity}) to its CRUD function type configuration. * * Each entry defines which CRUD operations (create, read, update, delete) are available for * that model type, along with optional specifiers for sub-operations. */ export type ModelFirebaseCrudFunctionTypeMap = { [K in FirestoreModelTypes]: ModelFirebaseCrudFunctionTypeMapEntry; }; /** * Entry for a single model type in a {@link ModelFirebaseCrudFunctionTypeMap}. * * Can be `null`/`undefined` (no CRUD functions for this model) or a partial record of * create/read/update/delete configurations, each optionally with specifiers. */ export type ModelFirebaseCrudFunctionTypeMapEntry = MaybeNot | Partial; export type ModelFirebaseCrudFunctionTypeMapEntryWithReturnType = [I, O]; export type ModelFirebaseCrudFunctionTypeSpecifierConfig = Record; export type ModelFirebaseCrudFunctionCreateTypeConfig = { readonly create: unknown | ModelFirebaseCrudFunctionTypeSpecifierConfig; }; export type ModelFirebaseCrudFunctionReadTypeConfig = { readonly read: unknown | ModelFirebaseCrudFunctionTypeSpecifierConfig; }; export type ModelFirebaseCrudFunctionUpdateTypeConfig = { readonly update: unknown | ModelFirebaseCrudFunctionTypeSpecifierConfig; }; export type ModelFirebaseCrudFunctionDeleteTypeConfig = { readonly delete: unknown | ModelFirebaseCrudFunctionTypeSpecifierConfig; }; /** * Default specifier string (`'_'`) used when a CRUD operation has specifiers but one * should map to the base function name without a specifier suffix. */ export declare const MODEL_FUNCTION_FIREBASE_CRUD_FUNCTION_SPECIFIER_DEFAULT = "_"; export type ModelFirebaseCrudFunctionSpecifierDefault = typeof MODEL_FUNCTION_FIREBASE_CRUD_FUNCTION_SPECIFIER_DEFAULT; /** * Separator character (`','`) used to split multiple specifier keys in a config string. * For example, `'update:status,config'` defines two update specifiers: `status` and `config`. */ export declare const MODEL_FUNCTION_FIREBASE_CRUD_FUNCTION_SPECIFIER_SPLITTER = ","; export type ModelFirebaseCrudFunctionSpecifierSplitter = typeof MODEL_FUNCTION_FIREBASE_CRUD_FUNCTION_SPECIFIER_SPLITTER; /** * Configuration map that defines which CRUD operations and specifiers to generate for each model type. * * The {@link FirestoreModelIdentity} type parameter `T` constrains which model type keys are * valid, providing compile-time safety. Entries are string arrays like `['create', 'update:status,config']`. */ export type ModelFirebaseCrudFunctionConfigMap, T extends FirestoreModelIdentity> = NonNever<{ [K in FirestoreModelTypes]: C[K] extends null ? never : ModelFirebaseCrudFunctionConfigMapEntry; }>; export type ModelFirebaseCrudFunctionConfigMapEntry = ValuesTypesAsArray<{ [K in keyof T]: K extends string ? (T[K] extends Record ? ModelFirebaseCrudFunctionConfigMapEntrySpecifier : K) : never; }>; export type ModelFirebaseCrudFunctionConfigMapEntrySpecifier = `${K}:${CommaSeparatedKeysOfObject}`; export type ModelFirebaseCrudFunctionMap = ModelFirebaseCrudFunctionRawMap; export type ModelFirebaseCrudFunctionRawMap = NonNever<{ [K in keyof C]: K extends string ? ModelFirebaseCrudFunctionMapEntry : never; }>; export type ModelFirebaseCrudFunctionName = `${CRUD}${Capitalize}`; export type ModelFirebaseCrudFunctionWithSpecifierName = `${CRUD}${Capitalize}${Capitalize}`; export type ModelFirebaseCrudFunctionMapEntry = E extends null ? never : { [CRUD in keyof E as CRUD extends string ? (E[CRUD] extends Record ? never : ModelFirebaseCrudFunctionName) : never]: CRUD extends string ? ModelFirebaseCrudFunctionMapEntryFunction : never; } & { [CRUD in keyof E as CRUD extends string ? (E[CRUD] extends Record ? ModelFirebaseCrudFunctionName : never) : never]: CRUD extends string ? ModelFirebaseCrudFunctionMapEntrySpecifier | ModelFirebaseCrudFunctionMapEntrySpecifierShort : never; }; export type ModelFirebaseCrudFunctionMapEntrySpecifier = { [SPECIFIER in keyof M as SPECIFIER extends string ? (CRUD extends string ? (SPECIFIER extends ModelFirebaseCrudFunctionSpecifierDefault ? ModelFirebaseCrudFunctionName : ModelFirebaseCrudFunctionWithSpecifierName) : never) : never]: ModelFirebaseCrudFunctionMapEntryFunction; }; export type ModelFirebaseCrudFunctionMapEntrySpecifierShort<_MODEL extends string, CRUD extends string, M extends unknown | ModelFirebaseCrudFunctionTypeSpecifierConfig> = { [SPECIFIER in keyof M as SPECIFIER extends string ? (CRUD extends string ? (SPECIFIER extends ModelFirebaseCrudFunctionSpecifierDefault ? CRUD : SPECIFIER) : never) : never]: ModelFirebaseCrudFunctionMapEntryFunction; }; export declare type ModelFirebaseCrudFunctionMapEntryFunction = E[K] extends ModelFirebaseCrudFunctionTypeMapEntryWithReturnType ? ModelFirebaseCrudFunction : CRUD extends 'create' ? ModelFirebaseCreateFunction : ModelFirebaseCrudFunction; /** * Combined function map providing both custom functions ({@link FirebaseFunctionMap}) and * auto-generated model CRUD functions ({@link ModelFirebaseCrudFunctionMap}). */ export type ModelFirebaseFunctionMap = FirebaseFunctionMap & ModelFirebaseCrudFunctionMap; /** * Factory that creates a {@link ModelFirebaseFunctionMap} for a given `Functions` instance. */ export type ModelFirebaseFunctionMapFactory = (functionsInstance: Functions) => ModelFirebaseFunctionMap; /** * Creates a {@link ModelFirebaseFunctionMapFactory} that routes model CRUD operations through the * single `callModel` Cloud Function endpoint. * * Rather than creating separate `HttpsCallable` instances for each CRUD operation, all model * operations are multiplexed through a single Firebase function (identified by {@link CALL_MODEL_APP_FUNCTION_KEY}). * The model type, CRUD operation, and optional specifier are encoded into the request parameters * via {@link onCallTypedModelParamsFunction}. * * Custom (non-CRUD) functions from `configMap` get their own individual `HttpsCallable` instances. * * @param configMap - configuration for custom (non-CRUD) functions * @param crudConfigMap - configuration for model CRUD functions with optional specifiers * @returns a {@link ModelFirebaseFunctionMapFactory} that creates a combined custom and CRUD function map for a given `Functions` instance * * @example * ```ts * const factory = callModelFirebaseFunctionMapFactory( * { customFn: null }, * { notification: ['create', 'update:status,config', 'delete'] } * ); * const fns = factory(getFunctions()); * await fns.createNotification(data); * await fns.updateNotificationStatus(data); * ``` */ export declare function callModelFirebaseFunctionMapFactory(configMap: FirebaseFunctionTypeConfigMap, crudConfigMap: ModelFirebaseCrudFunctionConfigMap): ModelFirebaseFunctionMapFactory;