Skip to content

Migrate legacy localStorage entries to new 2048-prefixed keys#71

Merged
Coteh merged 7 commits into
mainfrom
copilot/migrate-local-storage-entries
Mar 1, 2026
Merged

Migrate legacy localStorage entries to new 2048-prefixed keys#71
Coteh merged 7 commits into
mainfrom
copilot/migrate-local-storage-entries

Conversation

Copilot AI commented Feb 23, 2026

Copy link
Copy Markdown
Contributor

When the 2048- prefix was added to localStorage keys, existing players lost access to their saved high scores, preferences, and unlockables. This adds a one-time migration that copies legacy values into the new keys on startup, without touching unrelated localStorage entries from other games on the same origin. A notification dialog is shown to the player the first (and only) time the migration runs.

Changes

  • src/storage/browser.ts

    • Exports legacy key constants (LEGACY_GAME_STATE_KEY, LEGACY_PERSISTENT_STATE_KEY, LEGACY_PREFERENCES_KEY)
    • Exports migrateLocalStorage_v1_3_1() — iterates the legacy→new key map and copies each value only when the legacy key exists and the new key is absent, preventing overwrites of existing new saves; returns true if any migration was performed, false otherwise. The function name is versioned to indicate which app version introduced this migration and to avoid conflicts with any future migrations.
  • index.html

    • Adds #migration-dialog-content template with a user-friendly message informing the player their save data was migrated, and a mailto link to contact the developer if anything looks off
  • src/index.ts

    • Calls migrateLocalStorage_v1_3_1() immediately after BrowserGameStorage is instantiated; if it returns true, shows the migration notification dialog before game initialization
    • Because migration only runs once (only when old keys exist and new keys don't), the dialog naturally appears only once
  • test/browser_storage_test.ts

    • Tests covering: full migration (returns true), no-op when new key already exists (returns false), no-op when no legacy keys present (returns false), partial migration when only some legacy keys exist (returns true)
export const migrateLocalStorage_v1_3_1 = (): boolean => {
    let migrated = false;
    for (const [legacyKey, newKey] of Object.entries(LEGACY_KEY_MAP)) {
        const legacyValue = window.localStorage.getItem(legacyKey);
        if (legacyValue != null && window.localStorage.getItem(newKey) == null) {
            window.localStorage.setItem(newKey, legacyValue);
            migrated = true;
        }
    }
    return migrated;
};
Original prompt

A change was made recently to update the local storage fields to contain a "2048-" prefix, to disambiguate the save data from other games on my website. Players who load this new version will not be able to see their high score, preferences, or unlockables, but they are still available under the old local storage entries. I'd like a function that detects if the player has the old save fields, and selectively brings over these field values to the equivalent field in the new save entry. It's key that we selectively bring these individual values over rather than bring everything over, in case there's something else being used in another game that we do not want.


🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.

…ration

Co-authored-by: Coteh <3276350+Coteh@users.noreply.github.com>
Copilot AI changed the title [WIP] Add function to migrate old local storage fields Migrate legacy localStorage entries to new 2048-prefixed keys Feb 23, 2026
@github-actions

github-actions Bot commented Feb 23, 2026

Copy link
Copy Markdown

Test Results

 14 files  + 1   45 suites  +7   2m 25s ⏱️ +20s
169 tests +19  169 ✅ +19  0 💤 ±0  0 ❌ ±0 
177 runs  +19  177 ✅ +19  0 💤 ±0  0 ❌ ±0 

Results for commit d6c45ca. ± Comparison against base commit 7bc4d6e.

♻️ This comment has been updated with latest results.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Feb 23, 2026

Copy link
Copy Markdown

Deploying 2048-clone with  Cloudflare Pages  Cloudflare Pages

Latest commit: d6c45ca
Status: ✅  Deploy successful!
Preview URL: https://55f23532.2048-clone-33h.pages.dev
Branch Preview URL: https://copilot-migrate-local-storag.2048-clone-33h.pages.dev

View logs

Comment thread src/index.ts Outdated
let animationManager = new AnimationManager();
let undoManager = new UndoManager();
let gameStorage = new BrowserGameStorage();
migrateLocalStorage();

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you make the function return whether a migration was performed, and if it was, then pop up a dialog box saying that a migration was done and if there's any issues then contact me at my email. (Take a look at the error dialog template for the email) Make the message user-friendly. Because of the return value, the dialog should only ever appear once, when the migration moves stuff from the old local storage entries to the new one. It should not appear again.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in f652bec. migrateLocalStorage now returns true when at least one entry was migrated, and false otherwise. When true is returned, a migration notification dialog is shown (only on that first load after migration, since subsequent loads won't trigger migration). The dialog uses a new #migration-dialog-content template with a user-friendly message and a mailto link to james.cote.730@gmail.com (same email as the error dialog).

Migration dialog

…alStorage

Co-authored-by: Coteh <3276350+Coteh@users.noreply.github.com>

Copilot AI commented Feb 26, 2026

Copy link
Copy Markdown
Contributor Author

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • o1.ingest.sentry.io
    • Triggering command: /home/REDACTED/work/_temp/ghcca-node/node/bin/node node /home/REDACTED/work/2048-clone/2048-clone/node_modules/.bin/vite build (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Comment thread src/storage/browser.ts Outdated
[LEGACY_PREFERENCES_KEY]: PREFERENCES_KEY,
};

export const migrateLocalStorage = (): boolean => {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To make this function more context-aware and to track migrations over time, can we update the migrateLocalStorage function name to include the current version of the application? For example, if the version in package.json is 1.2.3, the function should be named migrateLocalStorage_v1_2_3.

This will help:

• Indicate the version associated with the migration logic.

• Avoid confusion if additional migrations are implemented in future versions.

Let me know what you think!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in ad9d0fe. The function has been renamed to migrateLocalStorage_v1_3_1 (version read from package.json), with all references updated in src/storage/browser.ts, src/index.ts, and test/browser_storage_test.ts.

Co-authored-by: Coteh <3276350+Coteh@users.noreply.github.com>
…n dialogs using document fragments

Template elements have already been using document fragments under the hood. Explicitly allowing document fragments now allows any document fragment to be used, thereby allowing multiple elements to be rendered without needing them to be parented to another element. This was done to allow for a debug dialog with an OK button that is properly spaced, (the spacing isn't so great when the span and button are parented by a div) but is really something that should be allowed in general going forward.

Other Changes:
- Fix migration dialog replacing feather elements before initial setup replaces them, causing the game to not load and log an error
@Coteh Coteh marked this pull request as ready for review February 28, 2026 17:27
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@Coteh Coteh merged commit 591bee6 into main Mar 1, 2026
6 checks passed
@Coteh Coteh deleted the copilot/migrate-local-storage-entries branch March 1, 2026 17:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants