normalizeIncludes method

Map<String, RelationScope> normalizeIncludes(
  1. dynamic includesInput
)

Normalize inputs

Implementation

Map<String, RelationScope> normalizeIncludes(dynamic includesInput)
{
  // Accept both List and Map for root
  final List includes = includesInput is Map
      ? [includesInput]
      : (includesInput ?? []) as List;

  final Map<String, RelationScope> result = {};

  // Helper: merge two scopes together
  RelationScope mergeScopes(RelationScope? oldScope, RelationScope newScope) {
    if (oldScope == null) return newScope;

    return (q) {
      final q1 = oldScope(q);
      final q2 = newScope(q1);
      return q2;
    };
  }

  // Helper: create a scope from nested include list
  RelationScope scopeFromNested(List nested) {
    return (q) => q.include(nested);
  }

  for (final inc in includes) {
    // CASE 1: Simple string (dot path)
    if (inc is String) {
      final parts = inc.split('.');
      final rel = parts.first;

      if (parts.length == 1) {
        // simple relation
        result[rel] = mergeScopes(result[rel], (q) => q);
      } else {
        final nested = [parts.sublist(1).join('.')];
        result[rel] =
            mergeScopes(result[rel], scopeFromNested(nested));
      }

      continue;
    }

    // CASE 2: Map { relation: config }
    if (inc is Map) {
      for (final entry in inc.entries) {
        final relName = entry.key.toString();
        final cfg = entry.value;

        RelationScope newScope;

        if (cfg is Function) {
          newScope = (q) {
            final out = cfg(q);
            return out is Query ? out : q;
          };
        } else if (cfg is List) {
          newScope = scopeFromNested(cfg);
        } else if (cfg is Map) {
          newScope = scopeFromNested([cfg]);
        } else if (cfg == true) {
          newScope = (q) => q;
        } else {
          throw Exception("Invalid include config for '$relName': $cfg");
        }

        result[relName] = mergeScopes(result[relName], newScope);
      }

      continue;
    }

    // CASE 3: List inside list → flatten / merge
    if (inc is List) {
      if (inc.isEmpty) continue;

      final nested = normalizeIncludes(inc); // recursion
      for (final e in nested.entries) {
        result[e.key] = mergeScopes(result[e.key], e.value);
      }
      continue;
    }

    // print("normalizeIncludes() invalid inc type: ${inc.runtimeType} value=$inc");

    throw Exception("Invalid include type: $inc");
  }

  return result;
}