updateDurableObjectBindings function

String updateDurableObjectBindings(
  1. String source, {
  2. required String binding,
  3. required String className,
  4. required String coordinatorBinding,
  5. required String coordinatorClass,
  6. required String tag,
})

DO bindingとclass migrationを環境内に追加する。既存設定は保持する。

Implementation

String updateDurableObjectBindings(String source,
    {required String binding,
    required String className,
    required String coordinatorBinding,
    required String coordinatorClass,
    required String tag}) {
  for (final name in [
    binding,
    className,
    coordinatorBinding,
    coordinatorClass
  ]) {
    if (!RegExp(r"^[A-Za-z_][A-Za-z0-9_]*$").hasMatch(name)) {
      throw ArgumentError("DO識別子が不正です。");
    }
  }
  final objectPattern = RegExp(
      r'"durable_objects"\s*:\s*\{\s*"bindings"\s*:\s*(\[[\s\S]*?\])\s*\}');
  final existing = objectPattern.firstMatch(source);
  if (source.contains('"durable_objects"') && existing == null) {
    throw StateError("既存DO設定の構造を解釈できません。");
  }
  final entries =
      existing == null ? <dynamic>[] : jsonDecode(existing.group(1)!) as List;
  for (final pair in [
    [binding, className],
    [coordinatorBinding, coordinatorClass]
  ]) {
    final old = entries.where((e) => e["name"] == pair[0]);
    if (old.isNotEmpty &&
        old.any(
            (e) => e["class_name"] != pair[1] || e["script_name"] != null)) {
      throw StateError("既存DO bindingの接続先が異なります。");
    }
    if (old.isEmpty) {
      entries.add({"name": pair[0], "class_name": pair[1]});
    }
  }
  final property = '"durable_objects":{"bindings":${jsonEncode(entries)}}';
  source = existing == null
      ? source.replaceFirst("{", "{$property,")
      : source.replaceRange(existing.start, existing.end, property);
  final found = _doArrayProperty(source, "migrations");
  final migrations = found == null ? <dynamic>[] : found.value;
  final desired = {
    "tag": tag,
    "new_sqlite_classes": [className, coordinatorClass]
  };
  final same = migrations.where((m) => m["tag"] == tag);
  if (same.isNotEmpty &&
      same.any((m) => jsonEncode(m) != jsonEncode(desired))) {
    throw StateError("migration tagが競合しています。");
  }
  if (same.isEmpty) {
    if (migrations.any((m) => (m["new_sqlite_classes"] as List? ?? [])
        .any([className, coordinatorClass].contains))) {
      throw StateError("classは別tagで登録済みです。");
    }
    migrations.add(desired);
  }
  final migrationProperty = '"migrations":${jsonEncode(migrations)}';
  return found == null
      ? source.replaceFirst("{", "{$migrationProperty,")
      : source.replaceRange(found.start, found.end, migrationProperty);
}