cinchdb 0.1.3__py3-none-any.whl → 0.1.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
cinchdb/config.py CHANGED
@@ -9,7 +9,7 @@ from pydantic import BaseModel, Field, ConfigDict
9
9
 
10
10
  class RemoteConfig(BaseModel):
11
11
  """Configuration for a remote CinchDB instance."""
12
-
12
+
13
13
  url: str = Field(description="Base URL of the remote CinchDB API")
14
14
  key: str = Field(description="API key for authentication")
15
15
 
@@ -50,7 +50,7 @@ class Config:
50
50
  env_dir = os.environ.get("CINCHDB_PROJECT_DIR")
51
51
  if env_dir:
52
52
  project_dir = Path(env_dir)
53
-
53
+
54
54
  self.project_dir = Path(project_dir) if project_dir else Path.cwd()
55
55
  self.config_dir = self.project_dir / ".cinchdb"
56
56
  self.config_path = self.config_dir / "config.toml"
@@ -86,24 +86,24 @@ class Config:
86
86
  # Override database and branch
87
87
  if env_db := os.environ.get("CINCHDB_DATABASE"):
88
88
  data["active_database"] = env_db
89
-
89
+
90
90
  if env_branch := os.environ.get("CINCHDB_BRANCH"):
91
91
  data["active_branch"] = env_branch
92
-
92
+
93
93
  # Override or create remote configuration
94
94
  env_url = os.environ.get("CINCHDB_REMOTE_URL")
95
95
  env_key = os.environ.get("CINCHDB_API_KEY")
96
-
96
+
97
97
  if env_url and env_key:
98
98
  # Create or update "env" remote
99
99
  if "remotes" not in data:
100
100
  data["remotes"] = {}
101
-
101
+
102
102
  data["remotes"]["env"] = {
103
103
  "url": env_url.rstrip("/"), # Remove trailing slash
104
- "key": env_key
104
+ "key": env_key,
105
105
  }
106
-
106
+
107
107
  # Make it active if no other remote is set
108
108
  if not data.get("active_remote"):
109
109
  data["active_remote"] = "env"
@@ -130,48 +130,21 @@ class Config:
130
130
  for alias, remote in config_dict["remotes"].items():
131
131
  if isinstance(remote, dict):
132
132
  config_dict["remotes"][alias] = remote
133
-
133
+
134
134
  with open(self.config_path, "w") as f:
135
135
  toml.dump(config_dict, f)
136
136
 
137
137
  def init_project(self) -> ProjectConfig:
138
- """Initialize a new CinchDB project with default configuration."""
139
- if self.exists:
140
- raise FileExistsError(f"Project already exists at {self.config_dir}")
138
+ """Initialize a new CinchDB project with default configuration.
141
139
 
142
- # Create default config
143
- config = ProjectConfig()
144
- self.save(config)
140
+ This method now delegates to the ProjectInitializer for the actual
141
+ initialization logic.
142
+ """
143
+ from cinchdb.core.initializer import ProjectInitializer
145
144
 
146
- # Create default database structure
147
- self._create_default_structure()
145
+ initializer = ProjectInitializer(self.project_dir)
146
+ config = initializer.init_project()
148
147
 
148
+ # Load the config into this instance
149
+ self._config = config
149
150
  return config
150
-
151
- def _create_default_structure(self) -> None:
152
- """Create default project structure."""
153
- # Create main database with main branch
154
- db_path = self.config_dir / "databases" / "main" / "branches" / "main"
155
- db_path.mkdir(parents=True, exist_ok=True)
156
-
157
- # Create metadata files
158
- from datetime import datetime, timezone
159
-
160
- metadata = {"created_at": datetime.now(timezone.utc).isoformat()}
161
-
162
- import json
163
-
164
- with open(db_path / "metadata.json", "w") as f:
165
- json.dump(metadata, f, indent=2)
166
-
167
- # Create empty changes file
168
- with open(db_path / "changes.json", "w") as f:
169
- json.dump([], f, indent=2)
170
-
171
- # Create main tenant directory and database
172
- tenant_dir = db_path / "tenants"
173
- tenant_dir.mkdir(exist_ok=True)
174
-
175
- # Create main tenant database file
176
- main_db = tenant_dir / "main.db"
177
- main_db.touch()
cinchdb/core/__init__.py CHANGED
@@ -1,5 +1,6 @@
1
1
  """Core CinchDB functionality."""
2
2
 
3
3
  from cinchdb.core.database import CinchDB, connect, connect_api
4
+ from cinchdb.core.initializer import init_project, init_database
4
5
 
5
- __all__ = ["CinchDB", "connect", "connect_api"]
6
+ __all__ = ["CinchDB", "connect", "connect_api", "init_project", "init_database"]
cinchdb/core/database.py CHANGED
@@ -3,10 +3,9 @@
3
3
  from pathlib import Path
4
4
  from typing import List, Dict, Any, Optional, TYPE_CHECKING
5
5
 
6
- from cinchdb.config import Config
7
6
  from cinchdb.models import Column, Change
8
7
  from cinchdb.core.path_utils import get_project_root
9
- from cinchdb.utils import validate_query_safe, SQLValidationError
8
+ from cinchdb.utils import validate_query_safe
10
9
 
11
10
  if TYPE_CHECKING:
12
11
  from cinchdb.managers.table import TableManager
@@ -307,7 +306,10 @@ class CinchDB:
307
306
  # Convenience methods for common operations
308
307
 
309
308
  def query(
310
- self, sql: str, params: Optional[List[Any]] = None, skip_validation: bool = False
309
+ self,
310
+ sql: str,
311
+ params: Optional[List[Any]] = None,
312
+ skip_validation: bool = False,
311
313
  ) -> List[Dict[str, Any]]:
312
314
  """Execute a SQL query.
313
315
 
@@ -318,14 +320,14 @@ class CinchDB:
318
320
 
319
321
  Returns:
320
322
  List of result rows as dictionaries
321
-
323
+
322
324
  Raises:
323
325
  SQLValidationError: If the query contains restricted operations
324
326
  """
325
327
  # Validate query unless explicitly skipped
326
328
  if not skip_validation:
327
329
  validate_query_safe(sql)
328
-
330
+
329
331
  if self.is_local:
330
332
  if self._query_manager is None:
331
333
  from cinchdb.managers.query import QueryManager
@@ -427,6 +429,7 @@ class CinchDB:
427
429
  """
428
430
  if self.is_local:
429
431
  from cinchdb.managers.change_tracker import ChangeTracker
432
+
430
433
  tracker = ChangeTracker(self.project_dir, self.database, self.branch)
431
434
  return tracker.get_changes()
432
435
  else:
@@ -435,6 +438,7 @@ class CinchDB:
435
438
  # Convert API response to Change objects
436
439
  from cinchdb.models import Change
437
440
  from datetime import datetime
441
+
438
442
  changes = []
439
443
  for data in result.get("changes", []):
440
444
  # Convert string dates back to datetime if present
@@ -0,0 +1,214 @@
1
+ """Project initialization for CinchDB."""
2
+
3
+ import json
4
+ from datetime import datetime, timezone
5
+ from pathlib import Path
6
+ from typing import Optional
7
+
8
+ from cinchdb.core.connection import DatabaseConnection
9
+ from cinchdb.config import ProjectConfig
10
+
11
+
12
+ class ProjectInitializer:
13
+ """Handles initialization of CinchDB projects."""
14
+
15
+ def __init__(self, project_dir: Optional[Path] = None):
16
+ """Initialize the project initializer.
17
+
18
+ Args:
19
+ project_dir: Path to project directory. If None, uses current directory.
20
+ """
21
+ self.project_dir = Path(project_dir) if project_dir else Path.cwd()
22
+ self.config_dir = self.project_dir / ".cinchdb"
23
+ self.config_path = self.config_dir / "config.toml"
24
+
25
+ def init_project(
26
+ self, database_name: str = "main", branch_name: str = "main"
27
+ ) -> ProjectConfig:
28
+ """Initialize a new CinchDB project with default configuration.
29
+
30
+ Args:
31
+ database_name: Name for the initial database (default: "main")
32
+ branch_name: Name for the initial branch (default: "main")
33
+
34
+ Returns:
35
+ The created ProjectConfig
36
+
37
+ Raises:
38
+ FileExistsError: If project already exists at the location
39
+ """
40
+ if self.config_path.exists():
41
+ raise FileExistsError(f"Project already exists at {self.config_dir}")
42
+
43
+ # Create config directory
44
+ self.config_dir.mkdir(parents=True, exist_ok=True)
45
+
46
+ # Create default config
47
+ config = ProjectConfig(active_database=database_name, active_branch=branch_name)
48
+
49
+ # Save config
50
+ self._save_config(config)
51
+
52
+ # Create default database structure
53
+ self._create_database_structure(database_name, branch_name)
54
+
55
+ return config
56
+
57
+ def init_database(
58
+ self,
59
+ database_name: str,
60
+ branch_name: str = "main",
61
+ description: Optional[str] = None,
62
+ ) -> None:
63
+ """Initialize a new database within an existing project.
64
+
65
+ Args:
66
+ database_name: Name for the database
67
+ branch_name: Initial branch name (default: "main")
68
+ description: Optional description for the database
69
+
70
+ Raises:
71
+ FileNotFoundError: If project doesn't exist
72
+ FileExistsError: If database already exists
73
+ """
74
+ if not self.config_path.exists():
75
+ raise FileNotFoundError(f"No CinchDB project found at {self.config_dir}")
76
+
77
+ db_path = self.config_dir / "databases" / database_name
78
+ if db_path.exists():
79
+ raise FileExistsError(f"Database '{database_name}' already exists")
80
+
81
+ # Create database structure
82
+ self._create_database_structure(database_name, branch_name, description)
83
+
84
+ def _create_database_structure(
85
+ self,
86
+ database_name: str,
87
+ branch_name: str = "main",
88
+ description: Optional[str] = None,
89
+ ) -> None:
90
+ """Create the directory structure for a database.
91
+
92
+ Args:
93
+ database_name: Name of the database
94
+ branch_name: Name of the initial branch
95
+ description: Optional description
96
+ """
97
+ # Create database branch path
98
+ branch_path = (
99
+ self.config_dir / "databases" / database_name / "branches" / branch_name
100
+ )
101
+ branch_path.mkdir(parents=True, exist_ok=True)
102
+
103
+ # Create metadata file
104
+ metadata = {
105
+ "created_at": datetime.now(timezone.utc).isoformat(),
106
+ "name": branch_name,
107
+ "parent": None,
108
+ "description": description,
109
+ }
110
+
111
+ with open(branch_path / "metadata.json", "w") as f:
112
+ json.dump(metadata, f, indent=2)
113
+
114
+ # Create empty changes file
115
+ with open(branch_path / "changes.json", "w") as f:
116
+ json.dump([], f, indent=2)
117
+
118
+ # Create tenants directory
119
+ tenant_dir = branch_path / "tenants"
120
+ tenant_dir.mkdir(exist_ok=True)
121
+
122
+ # Create and initialize main tenant database
123
+ self._init_tenant_database(tenant_dir / "main.db")
124
+
125
+ def _init_tenant_database(self, db_path: Path) -> None:
126
+ """Initialize a tenant database with proper PRAGMAs.
127
+
128
+ Args:
129
+ db_path: Path to the database file
130
+ """
131
+ # Create the database file
132
+ db_path.touch()
133
+
134
+ # Initialize with proper PRAGMAs
135
+ with DatabaseConnection(db_path):
136
+ # The connection automatically sets up PRAGMAs in _connect():
137
+ # - journal_mode = WAL
138
+ # - synchronous = NORMAL
139
+ # - wal_autocheckpoint = 0
140
+ # - foreign_keys = ON
141
+ pass
142
+
143
+ def _save_config(self, config: ProjectConfig) -> None:
144
+ """Save configuration to disk.
145
+
146
+ Args:
147
+ config: Configuration to save
148
+ """
149
+ import toml
150
+
151
+ # Ensure config directory exists
152
+ self.config_dir.mkdir(parents=True, exist_ok=True)
153
+
154
+ # Convert to dict for TOML serialization
155
+ config_dict = config.model_dump()
156
+
157
+ # Convert RemoteConfig objects to dicts if present
158
+ if "remotes" in config_dict:
159
+ for alias, remote in config_dict["remotes"].items():
160
+ if isinstance(remote, dict):
161
+ config_dict["remotes"][alias] = remote
162
+
163
+ with open(self.config_path, "w") as f:
164
+ toml.dump(config_dict, f)
165
+
166
+
167
+ def init_project(
168
+ project_dir: Optional[Path] = None,
169
+ database_name: str = "main",
170
+ branch_name: str = "main",
171
+ ) -> ProjectConfig:
172
+ """Initialize a new CinchDB project.
173
+
174
+ This is a convenience function that creates a ProjectInitializer
175
+ and initializes a project.
176
+
177
+ Args:
178
+ project_dir: Directory to initialize in (default: current directory)
179
+ database_name: Name for initial database (default: "main")
180
+ branch_name: Name for initial branch (default: "main")
181
+
182
+ Returns:
183
+ The created ProjectConfig
184
+
185
+ Raises:
186
+ FileExistsError: If project already exists
187
+ """
188
+ initializer = ProjectInitializer(project_dir)
189
+ return initializer.init_project(database_name, branch_name)
190
+
191
+
192
+ def init_database(
193
+ project_dir: Optional[Path] = None,
194
+ database_name: str = "main",
195
+ branch_name: str = "main",
196
+ description: Optional[str] = None,
197
+ ) -> None:
198
+ """Initialize a new database within an existing project.
199
+
200
+ This is a convenience function that creates a ProjectInitializer
201
+ and initializes a database.
202
+
203
+ Args:
204
+ project_dir: Project directory (default: current directory)
205
+ database_name: Name for the database
206
+ branch_name: Initial branch name (default: "main")
207
+ description: Optional description
208
+
209
+ Raises:
210
+ FileNotFoundError: If project doesn't exist
211
+ FileExistsError: If database already exists
212
+ """
213
+ initializer = ProjectInitializer(project_dir)
214
+ initializer.init_database(database_name, branch_name, description)
@@ -6,7 +6,6 @@ from pathlib import Path
6
6
  from typing import List, Dict, Any
7
7
  from datetime import datetime, timezone
8
8
 
9
- from cinchdb.config import Config
10
9
  from cinchdb.models import Branch
11
10
  from cinchdb.core.path_utils import (
12
11
  get_database_path,
@@ -68,7 +67,7 @@ class BranchManager:
68
67
  """
69
68
  # Validate new branch name
70
69
  validate_name(new_branch_name, "branch")
71
-
70
+
72
71
  # Validate source branch exists
73
72
  if source_branch not in list_branches(self.project_root, self.database):
74
73
  raise ValueError(f"Source branch '{source_branch}' does not exist")
@@ -122,7 +121,6 @@ class BranchManager:
122
121
  branch_path = get_branch_path(self.project_root, self.database, branch_name)
123
122
  shutil.rmtree(branch_path)
124
123
 
125
-
126
124
  def get_branch_metadata(self, branch_name: str) -> Dict[str, Any]:
127
125
  """Get metadata for a branch.
128
126
 
@@ -439,16 +439,20 @@ class ColumnManager:
439
439
  conn.commit()
440
440
 
441
441
  def alter_column_nullable(
442
- self, table_name: str, column_name: str, nullable: bool, fill_value: Optional[Any] = None
442
+ self,
443
+ table_name: str,
444
+ column_name: str,
445
+ nullable: bool,
446
+ fill_value: Optional[Any] = None,
443
447
  ) -> None:
444
448
  """Change the nullable constraint on a column.
445
-
449
+
446
450
  Args:
447
451
  table_name: Name of the table
448
452
  column_name: Name of the column to modify
449
453
  nullable: Whether the column should allow NULL values
450
454
  fill_value: Value to use for existing NULL values when making column NOT NULL
451
-
455
+
452
456
  Raises:
453
457
  ValueError: If table/column doesn't exist or column is protected
454
458
  ValueError: If making NOT NULL and column has NULL values without fill_value
@@ -469,13 +473,13 @@ class ColumnManager:
469
473
  existing_columns = self.list_columns(table_name)
470
474
  column_found = False
471
475
  old_column = None
472
-
476
+
473
477
  for col in existing_columns:
474
478
  if col.name == column_name:
475
479
  column_found = True
476
480
  old_column = col
477
481
  break
478
-
482
+
479
483
  if not column_found:
480
484
  raise ValueError(
481
485
  f"Column '{column_name}' does not exist in table '{table_name}'"
@@ -494,7 +498,7 @@ class ColumnManager:
494
498
  f"SELECT COUNT(*) FROM {table_name} WHERE {column_name} IS NULL"
495
499
  )
496
500
  null_count = cursor.fetchone()[0]
497
-
501
+
498
502
  if null_count > 0 and fill_value is None:
499
503
  raise ValueError(
500
504
  f"Column '{column_name}' has {null_count} NULL values. "
@@ -503,7 +507,7 @@ class ColumnManager:
503
507
 
504
508
  # Build SQL statements for table recreation
505
509
  temp_table = f"{table_name}_temp"
506
-
510
+
507
511
  # Create new table with modified column
508
512
  col_defs = []
509
513
  for col in existing_columns:
@@ -527,7 +531,7 @@ class ColumnManager:
527
531
  # Column names for copying
528
532
  col_names = [col.name for col in existing_columns]
529
533
  col_list = ", ".join(col_names)
530
-
534
+
531
535
  # Build copy SQL with COALESCE if needed
532
536
  if not nullable and fill_value is not None:
533
537
  # Build select list with COALESCE for the target column
@@ -545,7 +549,7 @@ class ColumnManager:
545
549
  copy_sql = f"INSERT INTO {temp_table} ({col_list}) SELECT {select_list} FROM {table_name}"
546
550
  else:
547
551
  copy_sql = f"INSERT INTO {temp_table} ({col_list}) SELECT {col_list} FROM {table_name}"
548
-
552
+
549
553
  drop_sql = f"DROP TABLE {table_name}"
550
554
  rename_sql = f"ALTER TABLE {temp_table} RENAME TO {table_name}"
551
555
 
cinchdb/managers/query.py CHANGED
@@ -7,7 +7,7 @@ from pydantic import BaseModel, ValidationError
7
7
 
8
8
  from cinchdb.core.connection import DatabaseConnection
9
9
  from cinchdb.core.path_utils import get_tenant_db_path
10
- from cinchdb.utils import validate_query_safe, SQLValidationError
10
+ from cinchdb.utils import validate_query_safe
11
11
 
12
12
  T = TypeVar("T", bound=BaseModel)
13
13
 
@@ -33,7 +33,10 @@ class QueryManager:
33
33
  self.db_path = get_tenant_db_path(project_root, database, branch, tenant)
34
34
 
35
35
  def execute(
36
- self, sql: str, params: Optional[Union[tuple, dict]] = None, skip_validation: bool = False
36
+ self,
37
+ sql: str,
38
+ params: Optional[Union[tuple, dict]] = None,
39
+ skip_validation: bool = False,
37
40
  ) -> List[Dict[str, Any]]:
38
41
  """Execute a SQL query and return results as dictionaries.
39
42
 
@@ -52,7 +55,7 @@ class QueryManager:
52
55
  # Validate query unless explicitly skipped
53
56
  if not skip_validation:
54
57
  validate_query_safe(sql)
55
-
58
+
56
59
  # Note: The original code had SELECT-only validation, but we're now more permissive
57
60
  if not sql.strip().upper().startswith("SELECT"):
58
61
  raise ValueError(
@@ -161,7 +164,10 @@ class QueryManager:
161
164
  return results[0] if results else None
162
165
 
163
166
  def execute_non_query(
164
- self, sql: str, params: Optional[Union[tuple, dict]] = None, skip_validation: bool = False
167
+ self,
168
+ sql: str,
169
+ params: Optional[Union[tuple, dict]] = None,
170
+ skip_validation: bool = False,
165
171
  ) -> int:
166
172
  """Execute a non-SELECT SQL query (INSERT, UPDATE, DELETE, etc.).
167
173
 
@@ -172,7 +178,7 @@ class QueryManager:
172
178
 
173
179
  Returns:
174
180
  Number of rows affected
175
-
181
+
176
182
  Raises:
177
183
  SQLValidationError: If query contains restricted operations
178
184
  Exception: If query execution fails
@@ -180,7 +186,7 @@ class QueryManager:
180
186
  # Validate query unless explicitly skipped
181
187
  if not skip_validation:
182
188
  validate_query_safe(sql)
183
-
189
+
184
190
  with DatabaseConnection(self.db_path) as conn:
185
191
  cursor = conn.execute(sql, params)
186
192
  affected_rows = cursor.rowcount
cinchdb/managers/table.py CHANGED
@@ -95,13 +95,13 @@ class TableManager:
95
95
  for column in columns:
96
96
  if column.foreign_key:
97
97
  fk = column.foreign_key
98
-
98
+
99
99
  # Validate referenced table exists
100
100
  if not self._table_exists(fk.table):
101
101
  raise ValueError(
102
102
  f"Foreign key reference to non-existent table: '{fk.table}'"
103
103
  )
104
-
104
+
105
105
  # Validate referenced column exists
106
106
  ref_table = self.get_table(fk.table)
107
107
  ref_col_names = [col.name for col in ref_table.columns]
@@ -109,9 +109,11 @@ class TableManager:
109
109
  raise ValueError(
110
110
  f"Foreign key reference to non-existent column: '{fk.table}.{fk.column}'"
111
111
  )
112
-
112
+
113
113
  # Build foreign key constraint
114
- fk_constraint = f"FOREIGN KEY ({column.name}) REFERENCES {fk.table}({fk.column})"
114
+ fk_constraint = (
115
+ f"FOREIGN KEY ({column.name}) REFERENCES {fk.table}({fk.column})"
116
+ )
115
117
  if fk.on_delete != "RESTRICT":
116
118
  fk_constraint += f" ON DELETE {fk.on_delete}"
117
119
  if fk.on_update != "RESTRICT":
@@ -143,7 +145,7 @@ class TableManager:
143
145
  col_def += " UNIQUE"
144
146
 
145
147
  sql_parts.append(col_def)
146
-
148
+
147
149
  # Add foreign key constraints
148
150
  sql_parts.extend(foreign_key_constraints)
149
151
 
@@ -201,16 +203,17 @@ class TableManager:
201
203
  to_col = fk_row["to"]
202
204
  on_update = fk_row["on_update"]
203
205
  on_delete = fk_row["on_delete"]
204
-
206
+
205
207
  # Create ForeignKeyRef
206
208
  from cinchdb.models import ForeignKeyRef
209
+
207
210
  foreign_keys[from_col] = ForeignKeyRef(
208
211
  table=to_table,
209
212
  column=to_col,
210
213
  on_update=on_update,
211
- on_delete=on_delete
214
+ on_delete=on_delete,
212
215
  )
213
-
216
+
214
217
  # Get column information
215
218
  cursor = conn.execute(f"PRAGMA table_info({table_name})")
216
219
 
@@ -241,7 +244,7 @@ class TableManager:
241
244
  nullable=(row["notnull"] == 0),
242
245
  default=row["dflt_value"],
243
246
  primary_key=(row["pk"] == 1),
244
- foreign_key=foreign_key
247
+ foreign_key=foreign_key,
245
248
  )
246
249
  columns.append(column)
247
250
 
@@ -70,7 +70,7 @@ class TenantManager:
70
70
  """
71
71
  # Validate tenant name
72
72
  validate_name(tenant_name, "tenant")
73
-
73
+
74
74
  # Check maintenance mode
75
75
  check_maintenance_mode(self.project_root, self.database, self.branch)
76
76
 
@@ -168,7 +168,7 @@ class TenantManager:
168
168
  """
169
169
  # Validate target tenant name
170
170
  validate_name(target_tenant, "tenant")
171
-
171
+
172
172
  # Check maintenance mode
173
173
  check_maintenance_mode(self.project_root, self.database, self.branch)
174
174
 
@@ -213,7 +213,7 @@ class TenantManager:
213
213
  """
214
214
  # Validate new tenant name
215
215
  validate_name(new_name, "tenant")
216
-
216
+
217
217
  # Can't rename main tenant
218
218
  if old_name == "main":
219
219
  raise ValueError("Cannot rename the main tenant")
cinchdb/models/branch.py CHANGED
@@ -22,7 +22,7 @@ class Branch(CinchDBBaseModel):
22
22
  )
23
23
  is_main: bool = Field(default=False, description="Whether this is the main branch")
24
24
 
25
- @field_validator('name')
25
+ @field_validator("name")
26
26
  @classmethod
27
27
  def validate_name_field(cls, v: str) -> str:
28
28
  """Validate branch name meets naming requirements."""
@@ -16,7 +16,7 @@ class Database(CinchDBBaseModel):
16
16
  active_branch: str = Field(default="main", description="Currently active branch")
17
17
  description: Optional[str] = Field(default=None, description="Database description")
18
18
 
19
- @field_validator('name')
19
+ @field_validator("name")
20
20
  @classmethod
21
21
  def validate_name_field(cls, v: str) -> str:
22
22
  """Validate database name meets naming requirements."""