From ad2749957cb353aa38ca108ad9a5e88f541de352 Mon Sep 17 00:00:00 2001 From: Michael McRae Date: Sun, 28 Jun 2026 13:20:29 +1000 Subject: [PATCH 1/4] Harden de-duplication of Terms --- packages/tempo/src/tempo.class.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/tempo/src/tempo.class.ts b/packages/tempo/src/tempo.class.ts index dda8535..f826872 100644 --- a/packages/tempo/src/tempo.class.ts +++ b/packages/tempo/src/tempo.class.ts @@ -572,13 +572,23 @@ export class Tempo { if (Tempo._termMap.get(config.key) === config) return; if (Tempo._termMap.has(config.key)) { + const existing = Tempo._termMap.get(config.key); + if (existing?.scope === config.scope && existing?.description === config.description) { + logDebug(`[Tempo#extend] Duplicate term registration ignored for key: "${config.key}"`, state.config); + return; + } logError(`[Tempo#extend] Term collision on key: "${config.key}". Registration aborted.`, state.config); return; } if (config.scope && Tempo._termMap.get(config.scope) === config) { /* continue */ } else if (config.scope && Tempo._termMap.has(config.scope)) { - logError(`[Tempo#extend] Term collision on scope: "${config.scope}". Registration aborted.`, state.config); - return; + const existingScope = Tempo._termMap.get(config.scope); + if (existingScope?.key === config.key && existingScope?.description === config.description) { + /* continue */ + } else { + logError(`[Tempo#extend] Term collision on scope: "${config.scope}". Registration aborted.`, state.config); + return; + } } Tempo._termMap.set(config.key, config); From 71be72edebd5a91019cf9e5c303783725b3e55f9 Mon Sep 17 00:00:00 2001 From: Michael McRae Date: Sun, 28 Jun 2026 13:26:25 +1000 Subject: [PATCH 2/4] set version and CHANGELOG --- package-lock.json | 8 ++++---- package.json | 2 +- packages/library/package.json | 2 +- packages/tempo/CHANGELOG.md | 5 +++++ packages/tempo/package.json | 2 +- packages/tempo/src/tempo.version.ts | 2 +- 6 files changed, 13 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index fc620d2..62433a0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "tempo-monorepo", - "version": "3.4.0", + "version": "3.5.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "tempo-monorepo", - "version": "3.4.0", + "version": "3.5.1", "workspaces": [ "packages/*" ], @@ -10149,7 +10149,7 @@ }, "packages/library": { "name": "@magmacomputing/library", - "version": "3.4.0", + "version": "3.5.1", "license": "MIT", "dependencies": { "tslib": "^2.8.1" @@ -10160,7 +10160,7 @@ }, "packages/tempo": { "name": "@magmacomputing/tempo", - "version": "3.4.0", + "version": "3.5.1", "license": "MIT", "dependencies": { "tslib": "^2.8.1" diff --git a/package.json b/package.json index 483a4aa..e532b8a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tempo-monorepo", - "version": "3.5.0", + "version": "3.5.1", "private": true, "engines": { "node": ">=20.0.0" diff --git a/packages/library/package.json b/packages/library/package.json index a7e24fe..e18e5c4 100644 --- a/packages/library/package.json +++ b/packages/library/package.json @@ -1,6 +1,6 @@ { "name": "@magmacomputing/library", - "version": "3.5.0", + "version": "3.5.1", "description": "Shared utility library for Tempo", "author": "Magma Computing Solutions", "license": "MIT", diff --git a/packages/tempo/CHANGELOG.md b/packages/tempo/CHANGELOG.md index 205a545..be609ae 100644 --- a/packages/tempo/CHANGELOG.md +++ b/packages/tempo/CHANGELOG.md @@ -6,6 +6,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [3.5.1] - 2026-06-28 + +### Fixed +- **Term Registration Resilience**: Hardened `Tempo.extend` with structural deduplication to safely ignore redundant registrations of identical core terms. This prevents edge-case term collisions (e.g., `qtr`) in mixed module-loader environments like Vitest where Node ESM and Vite may evaluate plugins multiple times. + ## [3.5.0] - 2026-06-28 ### Added diff --git a/packages/tempo/package.json b/packages/tempo/package.json index a01dffe..70b7e1c 100644 --- a/packages/tempo/package.json +++ b/packages/tempo/package.json @@ -1,6 +1,6 @@ { "name": "@magmacomputing/tempo", - "version": "3.5.0", + "version": "3.5.1", "engines": { "node": ">=20.0.0" }, diff --git a/packages/tempo/src/tempo.version.ts b/packages/tempo/src/tempo.version.ts index f3af59d..674c584 100644 --- a/packages/tempo/src/tempo.version.ts +++ b/packages/tempo/src/tempo.version.ts @@ -5,4 +5,4 @@ * ⚠️ This file is auto-updated by `npm run build:version` (see `bin/update-version.mjs`). * Do NOT edit manually — your changes will be overwritten on the next build. */ -export const TEMPO_VERSION = '3.5.0'; +export const TEMPO_VERSION = '3.5.1'; From ea3e25a595b0c94099d1bcdc6002f2c71d4c2007 Mon Sep 17 00:00:00 2001 From: Michael McRae Date: Sun, 28 Jun 2026 13:37:11 +1000 Subject: [PATCH 3/4] package-lock.json --- package-lock.json | 2 +- packages/tempo/src/tempo.class.ts | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 62433a0..385ff7e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10160,7 +10160,7 @@ }, "packages/tempo": { "name": "@magmacomputing/tempo", - "version": "3.5.1", + "version": "3.5.2", "license": "MIT", "dependencies": { "tslib": "^2.8.1" diff --git a/packages/tempo/src/tempo.class.ts b/packages/tempo/src/tempo.class.ts index f826872..027dd77 100644 --- a/packages/tempo/src/tempo.class.ts +++ b/packages/tempo/src/tempo.class.ts @@ -606,13 +606,11 @@ export class Tempo { if (r.key) { let val: string | undefined; if (isDefined(r.hour)) { - if (Number.isInteger(r.hour) && r.hour >= 0 && r.hour <= 23) { + if (Number.isInteger(r.hour) && r.hour >= 0 && r.hour <= 23) val = `${r.hour}:${pad(r.minute ?? 0)}`; - } } else if (r.month) { - if (Number.isInteger(r.month) && r.month >= 1 && r.month <= 12) { + if (Number.isInteger(r.month) && r.month >= 1 && r.month <= 12) val = `${pad(r.day ?? 1)} ${monthKeys[r.month - 1]}`; - } } if (val) aliases.push([r.key, val]); From f77af220317ec76fca01730938120f02289d7309 Mon Sep 17 00:00:00 2001 From: Michael McRae Date: Sun, 28 Jun 2026 13:47:36 +1000 Subject: [PATCH 4/4] add Ranges to Term de-dup check --- packages/tempo/src/tempo.class.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/tempo/src/tempo.class.ts b/packages/tempo/src/tempo.class.ts index 027dd77..16349a6 100644 --- a/packages/tempo/src/tempo.class.ts +++ b/packages/tempo/src/tempo.class.ts @@ -573,7 +573,8 @@ export class Tempo { if (Tempo._termMap.get(config.key) === config) return; if (Tempo._termMap.has(config.key)) { const existing = Tempo._termMap.get(config.key); - if (existing?.scope === config.scope && existing?.description === config.description) { + const rangesMatch = JSON.stringify(existing?.ranges) === JSON.stringify(config.ranges); + if (existing?.scope === config.scope && existing?.description === config.description && rangesMatch) { logDebug(`[Tempo#extend] Duplicate term registration ignored for key: "${config.key}"`, state.config); return; } @@ -583,7 +584,8 @@ export class Tempo { if (config.scope && Tempo._termMap.get(config.scope) === config) { /* continue */ } else if (config.scope && Tempo._termMap.has(config.scope)) { const existingScope = Tempo._termMap.get(config.scope); - if (existingScope?.key === config.key && existingScope?.description === config.description) { + const rangesMatch = JSON.stringify(existingScope?.ranges) === JSON.stringify(config.ranges); + if (existingScope?.key === config.key && existingScope?.description === config.description && rangesMatch) { /* continue */ } else { logError(`[Tempo#extend] Term collision on scope: "${config.scope}". Registration aborted.`, state.config);