Client State
Managing state across template executions
Client State
Client state manages data persistence across template executions, enabling updates and reproducibility.
What is Client State?
Client state is stored in the generated project at .cyan/generation.json:
.cyan/generation.json
{"version": "1.0","template": {"name": "myorg/my-template","version": "1.0.0"},"pin": "abc123-def456-ghi789","answers": {"project.name": "my-project","project.typescript": true,"project.docker": false},"generated": "2024-01-15T10:30:00Z"}
Purpose
Reproducibility
Regenerate with the same configuration:
# Original generation stored answerscyanprint create myorg/template:1.0.0 ./my-project# Regenerate uses stored answerscyanprint regenerate ./my-project
Updates
Update to new template versions:
# Update to latest versioncyanprint update ./my-project# Update to specific versioncyanprint update ./my-project myorg/template:2.0.0
Debugging
Inspect what configuration was used:
cat ./my-project/.cyan/generation.json
State Structure
Version
Schema version for future compatibility:
"version": "1.0"
Template
Template identification:
"template": {"name": "myorg/my-template","version": "1.0.0","registry": "https://registry.example.com"}
Pin
Determinism seed:
"pin": "abc123-def456-ghi789"
Answers
User responses to questions (by key):
"answers": {"project.name": "my-project","project.typescript": true,"project.features": ["ESLint", "Prettier"]}
Generated
Timestamp of generation:
"generated": "2024-01-15T10:30:00Z"
State Lifecycle
Accessing State
In Templates
State is automatically managed. Access via keys:
const name = await i.text('Name?', 'project.name', '...');// This answer is stored in state
Programmatically
Read state directly:
# Read statecat .cyan/generation.json | jq '.answers'# Extract specific answercat .cyan/generation.json | jq '.answers["project.name"]'
State and Keys
Keys are the bridge between questions and state:
// Question with keyconst name = await i.text('Name?', 'project.name', '...');// Stored in state as:// { "project.name": "user-entered-value" }
When updating:
- Read state to get previous answers
- Skip questions that have answers in state
- Use stored pin for deterministic values
Best Practices
Namespace Keys
Prevent collisions with namespaced keys:
// Good: Namespacedconst name = await i.text('Name?', 'my-template.project.name', '...');// Bad: Genericconst name = await i.text('Name?', 'name', '...');
Don't Modify State
Never manually edit .cyan/generation.json:
- Breaks determinism
- Breaks update functionality
- May corrupt template data
Version Control
Decide whether to commit .cyan/:
# Option 1: Commit for reproducibility# (No entry in .gitignore)# Option 2: Ignore for cleaner history.cyan/
Related
- Determinism - How reproducibility works
- 3-Way Merge - Update mechanics
- Use Keys - Keys and namespacing