Skip to content
中文

Candidate envelope, count, and shallow constraints ​

The policy implementation follows.

ts
export function defineCandidateSelection(definition: CandidateSelectionDefinition) {
  const { field, error: configuredError, maxCandidates: max } = definition;
  const error =
    typeof configuredError === 'function' ? configuredError.bind(definition) : configuredError;
  const min = definition.minCandidates ?? 1;
  const tags = [...(definition.tags ?? [])];
  breakWhile([
    { occur: () => !field.trim(), error: 'Missing candidate field' },
    {
      occur: [
        () => !Number.isSafeInteger(min) || !Number.isSafeInteger(max),
        () => min < 1 || max < min,
      ],
      error: 'Invalid candidate bounds',
    },
  ]);
  /**
   * Unpack the candidates from one parsed reply value.
   *
   * @param request - The raw value plus whether proposals are still allowed at this point.
   * @returns The candidate list, or `[value]` when no envelope is present.
   * @throws TypeError When `allowed` is not boolean, or when the envelope is disallowed,
   *   malformed, out of bounds or contains nested candidate envelopes.
   */
  function parse({ value, allowed }: { value: unknown; allowed: boolean }): unknown[] {
    if (typeof allowed !== 'boolean') throw new TypeError('Candidate availability must be boolean');

    if (!value || typeof value !== 'object' || !Object.hasOwn(value, field)) return [value];
    const candidates = (value as Record<string, unknown>)[field] as unknown[];
    breakWhile(
      [
        { occur: () => !allowed, error, tags: ['availability'] },
        {
          occur: () => Array.isArray(value) || Object.keys(value).length !== 1,
          error,
          tags: ['envelope'],
        },
        {
          occur: [
            () => !Array.isArray(candidates),
            () => candidates.length < min || candidates.length > max,
          ],
          error,
          tags: ['selection'],
        },
        {
          occur: () => candidates.some((candidate) => invalidCandidate(candidate, field)),
          error,
          tags: ['members'],
        },
      ],
      tags,
    );
    return candidates;
  }
  return Object.freeze({ field, minCandidates: min, maxCandidates: max, parse });
}

API reference, usage examples and developer guides.