Skip to content
中文

Check result aggregation ​

The policy implementation follows.

ts
export function defineAnswerCheckCatalog<Value, App>(
  definitions: readonly AnswerChecker<Value, App>[],
) {
  const checks = readonlyCatalog(
    definitions.map((check) => {
      if (typeof check.check !== 'function') throw new TypeError('Invalid answer checker');
      return Object.freeze({ id: check.id, check: check.check.bind(check) });
    }),
    (id) => new TypeError(`Unknown answer checker: ${id}`),
  );
  return Object.freeze({
    ...checks,
    // Severity only escalates: `failed` wins over `unavailable`, which wins over `passed`.
    check(
      request: { checkIds: readonly string[]; value: Value },
      context: OperationContext<App>,
    ): AnswerCheck<Value> {
      context.signal.throwIfAborted();
      const issues: AnswerIssue[] = [];
      let status: AnswerCheck<Value>['status'] = 'passed',
        value = request.value;
      if (new Set(request.checkIds).size !== request.checkIds.length)
        throw new TypeError('Duplicate selected check id');
      for (const id of request.checkIds) {
        context.signal.throwIfAborted();
        const result = nativeSubFlowSync(context, 'check', id, () =>
          snapshotAnswerCheck(
            synchronous(
              checks.require(id).check(value, context),
              'Answer checkers must be synchronous',
            ),
            id,
          ),
        );
        context.signal.throwIfAborted();
        if (Object.hasOwn(result, 'value')) value = result.value!;
        issues.push(...result.issues);
        if (result.status === 'failed') status = 'failed';
        else if (status !== 'failed' && result.status !== 'passed') status = 'unavailable';
      }
      return { status, value, issues };
    },
  });
}

API reference, usage examples and developer guides.