From b7f38abae92ba5bc0315398d72da28c93bce097a Mon Sep 17 00:00:00 2001 From: Immick <12298761+Immick@users.noreply.github.com> Date: Tue, 15 Sep 2026 22:03:51 +0300 Subject: [PATCH] fix(customisation-sync): stop truncating entry mtimes to 32 bits PluginDataExDisplayV2.mtime applied `~~` to the sum of millisecond timestamps before dividing, which keeps only the low 32 bits. The freshness chip and "Select All Shiny" then compared wrapped values and could offer an older copy as newer. Fixes #1194 --- .../CmdConfigSync.command.unit.spec.ts | 24 ++++++++++++++++++- src/features/ConfigSync/CmdConfigSync.ts | 3 ++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/features/ConfigSync/CmdConfigSync.command.unit.spec.ts b/src/features/ConfigSync/CmdConfigSync.command.unit.spec.ts index 0237360c..5f524f8a 100644 --- a/src/features/ConfigSync/CmdConfigSync.command.unit.spec.ts +++ b/src/features/ConfigSync/CmdConfigSync.command.unit.spec.ts @@ -52,7 +52,7 @@ vi.mock("@/common/obsidianCommunityPlugins.ts", () => ({ })); import { cancelTask } from "@/common/utils.ts"; -import { ConfigSync } from "./CmdConfigSync"; +import { ConfigSync, PluginDataExDisplayV2, type IPluginDataExDisplay } from "./CmdConfigSync"; describe("ConfigSync commands", () => { it("shows the Customisation Sync command only whilst the feature is enabled", () => { @@ -111,3 +111,25 @@ describe("ConfigSync commands", () => { expect(periodicPluginSweepProcessor.disable).toHaveBeenCalledOnce(); }); }); + +describe("PluginDataExDisplayV2", () => { + const entryWithModifiedTimes = (mtimes: number[]) => + new PluginDataExDisplayV2({ + documentPath: "ix:device/plugin_main/example", + category: "PLUGIN_MAIN", + name: "example", + term: "device", + files: mtimes.map((mtime, index) => ({ filename: `file-${index}`, mtime, data: [] })), + } as unknown as IPluginDataExDisplay); + + it("averages millisecond modification times without truncating them to 32 bits", () => { + const mtime = Date.UTC(2026, 8, 15); + expect(entryWithModifiedTimes([mtime, mtime, mtime]).mtime).toBe(mtime); + }); + + it("keeps a newer copy newer than an older one", () => { + const newer = entryWithModifiedTimes([Date.UTC(2026, 8, 15)]); + const older = entryWithModifiedTimes([Date.UTC(2026, 8, 2)]); + expect(newer.mtime).toBeGreaterThan(older.mtime); + }); +}); diff --git a/src/features/ConfigSync/CmdConfigSync.ts b/src/features/ConfigSync/CmdConfigSync.ts index 4201385f..b560dba6 100644 --- a/src/features/ConfigSync/CmdConfigSync.ts +++ b/src/features/ConfigSync/CmdConfigSync.ts @@ -368,7 +368,8 @@ export class PluginDataExDisplayV2 { return this._version; } get mtime(): number { - return ~~this.files.reduce((a, b) => a + b.mtime, 0) / this.files.length; + // Millisecond timestamps exceed 32 bits, so `~~` would wrap them. + return Math.floor(this.files.reduce((a, b) => a + b.mtime, 0) / this.files.length); } } export type PluginDataEx = {