Files
AFFiNE/packages/backend/server/src/models/feature.ts
2025-12-30 05:22:54 +08:00

79 lines
1.7 KiB
TypeScript

import { Injectable } from '@nestjs/common';
import { z } from 'zod';
import { BaseModel } from './base';
import {
type FeatureConfig,
FeatureConfigs,
type FeatureName,
FeaturesShapes,
FeatureType,
} from './common';
@Injectable()
export class FeatureModel extends BaseModel {
async get<T extends FeatureName>(name: T) {
const feature = await this.get_unchecked(name);
return {
...feature,
configs: this.check(name, feature.configs),
};
}
/**
* Get the latest feature from code definitions.
*
* @internal
*/
async try_get_unchecked<T extends FeatureName>(name: T) {
const config = FeatureConfigs[name];
if (!config) {
return null;
}
return {
name,
configs: config.configs,
type: config.type,
};
}
/**
* Get the latest feature from code definitions.
*
* @throws {Error} If the feature is not found in code.
* @internal
*/
async get_unchecked<T extends FeatureName>(name: T) {
const feature = await this.try_get_unchecked(name);
if (!feature) {
throw new Error(`Feature ${name} not found`);
}
return feature;
}
check<T extends FeatureName>(name: T, config: any) {
const shape = this.getConfigShape(name);
const parseResult = shape.safeParse(config);
if (!parseResult.success) {
throw new Error(`Invalid feature config for ${name}`, {
cause: parseResult.error,
});
}
return parseResult.data as FeatureConfig<T>;
}
getConfigShape(name: FeatureName): z.ZodObject<any> {
return FeaturesShapes[name] ?? z.object({});
}
getFeatureType(name: FeatureName): FeatureType {
return FeatureConfigs[name].type;
}
}