Skip to main content
Project updaters provide automated fixes for issues found during validation. They modify project files to resolve specific problems.

IProjectUpdater Interface

interface IProjectUpdater {
  update(project: Project, updateId: number): Promise<ProjectUpdateResult[]>;

  id: string;
  title: string;

  getUpdateIds(): number[];
  getUpdaterData(updaterId: number): IProjectUpdaterData;
}

Properties

id

Unique identifier for the updater (e.g., “FORMATVER”, “MANIF”).
id: string;

title

Human-readable name describing what the updater fixes.
title: string;

Methods

update()

Performs the update operation on a project.
update(
  project: Project,
  updateId: number
): Promise<ProjectUpdateResult[]>
Parameters:
  • project - The project to update
  • updateId - Specific update to perform (from getUpdateIds())
Returns: Array of update results describing what changed Example:
async update(project: Project, updateId: number): Promise<ProjectUpdateResult[]> {
  const results: ProjectUpdateResult[] = [];

  switch (updateId) {
    case 100:
      // Perform specific update
      const result = await this.fixManifestVersion(project);
      results.push(result);
      break;
  }

  return results;
}

getUpdateIds()

Returns all update IDs supported by this updater.
getUpdateIds(): number[]
Returns: Array of numeric update identifiers Example:
getUpdateIds(): number[] {
  return [100, 101, 102];  // Three different updates
}

getUpdaterData()

Returns metadata for a specific update.
getUpdaterData(updaterId: number): IProjectUpdaterData
Parameters:
  • updaterId - The update ID to get data for
Returns: Metadata describing the update
interface IProjectUpdaterData {
  title: string;  // Description of what this specific update does
}
Example:
getUpdaterData(updaterId: number): IProjectUpdaterData {
  switch (updaterId) {
    case 100:
      return { title: "Update manifest format version" };
    case 101:
      return { title: "Fix UUID format" };
    default:
      return { title: "Unknown update" };
  }
}

Update Results

Updaters return ProjectUpdateResult objects describing changes:
const result = new ProjectUpdateResult(
  UpdateResultType.updatedFile,     // Result type
  "MYUPDATER",                      // Updater ID
  100,                              // Update index
  "Fixed manifest version",         // Description
  projectItem,                      // Optional: affected item
  "1.20.0",                         // Optional: additional data
  "manifest",                       // Optional: item identifier
  "{\"format_version\": ...}"       // Optional: content before update
);

Update Result Types

enum UpdateResultType {
  updatedFile = 1,              // File was modified
  noChange = 0,                 // No changes needed
  internalProcessingError = 99  // Error occurred during update
}

Example Updater Implementation

export default class CustomUpdater implements IProjectUpdater {
  id = "CUSTOM";
  title = "Custom Fixes";

  getUpdateIds(): number[] {
    return [100, 101];
  }

  getUpdaterData(updaterId: number): IProjectUpdaterData {
    switch (updaterId) {
      case 100:
        return { title: "Fix component formatting" };
      case 101:
        return { title: "Update deprecated fields" };
      default:
        return { title: "Unknown" };
    }
  }

  async update(
    project: Project,
    updateId: number
  ): Promise<ProjectUpdateResult[]> {
    const results: ProjectUpdateResult[] = [];

    switch (updateId) {
      case 100:
        results.push(...await this.fixComponentFormatting(project));
        break;
      case 101:
        results.push(...await this.updateDeprecatedFields(project));
        break;
    }

    return results;
  }

  private async fixComponentFormatting(
    project: Project
  ): Promise<ProjectUpdateResult[]> {
    const results: ProjectUpdateResult[] = [];

    // Find items that need fixing
    const items = project.getItemsCopy();

    for (const item of items) {
      if (item.itemType === ProjectItemType.entityTypeBehavior) {
        await item.ensureStorage();
        const file = item.file;

        if (file) {
          // Read current content
          const content = await file.loadContent();
          const json = JSON.parse(content);

          // Make changes
          let modified = false;
          if (json["minecraft:entity"]?.components) {
            // Fix something
            modified = true;
          }

          if (modified) {
            // Write back
            await file.setContent(JSON.stringify(json, null, 2));

            results.push(
              new ProjectUpdateResult(
                UpdateResultType.updatedFile,
                this.id,
                100,
                `Fixed formatting in ${item.name}`,
                item
              )
            );
          } else {
            results.push(
              new ProjectUpdateResult(
                UpdateResultType.noChange,
                this.id,
                100,
                `No changes needed for ${item.name}`,
                item
              )
            );
          }
        }
      }
    }

    return results;
  }

  private async updateDeprecatedFields(
    project: Project
  ): Promise<ProjectUpdateResult[]> {
    // Similar implementation
    return [];
  }
}

Running Updaters

Updaters are executed through the ProjectUpdateRunner:
const runner = new ProjectUpdateRunner(project);

// Run a specific update
const results = await runner.update("CUSTOM", 100);

// Run all updates from specific updaters
const allResults = await runner.updateProject(
  ["CUSTOM", "FORMATVER"],  // Include these
  ["DEPRECATED"]            // Exclude these
);

Linking Updaters to Validation

Connect updaters to validation issues through topic data:
// In your info generator
getTopicData(topicId: number): IProjectInfoTopicData {
  return {
    title: "Component Formatting Issue",
    updaters: [
      {
        updaterId: "CUSTOM",    // Updater ID
        updaterIndex: 100,      // Update ID
        action: "Fix formatting" // User-facing action
      }
    ]
  };
}
This allows the UI to offer “Fix” buttons for validation errors.

Registering Updaters

Add your updater to the registration list:
// In GeneratorRegistrations.ts
static updaters = [
  new FormatVersionUpdater(),
  new CustomUpdater(),  // Your updater
  // ...
];

Best Practices

  1. Always backup: Consider project state before modifications
  2. Return meaningful results: Describe exactly what changed
  3. Handle errors gracefully: Use internalProcessingError for failures
  4. Test thoroughly: Updaters modify user projects
  5. Be idempotent: Running twice should be safe
  6. Validate changes: Ensure updates actually fix the issue
  7. Provide clear titles: Help users understand what will happen
  8. Log operations: Track what was changed for debugging

Safety Considerations

Updaters modify project files. Always:
  • Test with sample projects first
  • Validate JSON before writing
  • Check file permissions
  • Handle concurrent access
  • Preserve file formatting when possible

Example: Format Version Updater

A typical updater might fix format versions in manifests:
async update(project: Project, updateId: number): Promise<ProjectUpdateResult[]> {
  const results: ProjectUpdateResult[] = [];
  
  const manifests = project.getItemsByType(ProjectItemType.manifest);
  
  for (const manifest of manifests) {
    const file = manifest.file;
    if (!file) continue;
    
    const content = await file.loadContent();
    const json = JSON.parse(content);
    
    const oldVersion = json.format_version;
    const newVersion = 2; // Current format
    
    if (oldVersion !== newVersion) {
      json.format_version = newVersion;
      await file.setContent(JSON.stringify(json, null, 2));
      
      results.push(
        new ProjectUpdateResult(
          UpdateResultType.updatedFile,
          this.id,
          updateId,
          `Updated format version from ${oldVersion} to ${newVersion}`,
          manifest,
          `${oldVersion}${newVersion}`
        )
      );
    }
  }
  
  return results;
}

Build docs developers (and LLMs) love