步骤执行、同步约束和停止顺序
策略的具体实现如下。
ts
export function defineNormalization<Value, Context = void>(
definitions: readonly NormalizationStep<Value, Context>[],
options: NormalizationOptions<Value, Context> = {},
) {
const isComplete = options.isComplete?.bind(options);
const predicate = (value: unknown) =>
booleanResult(
value,
'Normalization predicates must be synchronous',
'Normalization predicates must return boolean',
);
const ids = new Set<string>();
const steps = Object.freeze(
definitions.map((step) => {
breakWhile([
{
occur: () => !step.id.trim() || ids.has(step.id),
error: 'Invalid or duplicate normalization step',
tags: ['id'],
},
{
occur: () => typeof step.normalize !== 'function',
error: 'Missing normalization handler',
tags: ['handler'],
},
]);
ids.add(step.id);
return Object.freeze({
id: step.id,
when: step.when?.bind(step),
normalize: step.normalize.bind(step),
});
}),
);
return Object.freeze({
steps,
normalize(value: Value, context: Context): Value {
for (const step of steps) {
if (step.when && !predicate(step.when(value, context))) continue;
value = synchronous(
step.normalize(value, context),
'Normalization handlers must be synchronous',
);
if (isComplete && predicate(isComplete(value, context))) break;
}
return value;
},
});
}
export function uniqueBy<Value, Key>(
values: readonly Value[],
key: (value: Value) => Key,
initial: Iterable<Key> = [],
): Value[] {
const seen = new Set(initial);
return values.filter((value) => {
const id = key(value);
if (seen.has(id)) return false;
seen.add(id);
return true;
});
}