Skip to content
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
a81b523
Add CI workflow for Tempo project
magmacomputing Apr 25, 2026
b4ba0e7
Refactor CI workflow for Node.js setup and tests
magmacomputing Apr 25, 2026
c6d7a15
bump vitest
magmacomputing Apr 26, 2026
12eceea
working on ci.yml
magmacomputing Apr 26, 2026
fdc351e
review 1st pass
magmacomputing Apr 26, 2026
64299a9
patch env in CI.yml
magmacomputing Apr 26, 2026
06aabcd
debug in compact.time
magmacomputing Apr 26, 2026
4fadfc7
debug in tempo.class
magmacomputing Apr 26, 2026
23d7c89
debug Intl.Locale
magmacomputing Apr 26, 2026
c4c6e39
test Intl.Local('en-us')
magmacomputing Apr 26, 2026
79d38a6
mdy Fallback list
magmacomputing Apr 26, 2026
e8ad9e9
clean-up timeZones fallback
magmacomputing Apr 26, 2026
ce08a9b
more debug fallback timeZones
magmacomputing Apr 26, 2026
0100a75
cut down on debug
magmacomputing Apr 26, 2026
725f66f
isMonthDay
magmacomputing Apr 26, 2026
987ee49
fix to $setEvents
magmacomputing Apr 26, 2026
fad7235
add bench/ for CI testing
magmacomputing Apr 26, 2026
f9d2997
Options grouping, isMonthDay
magmacomputing Apr 27, 2026
430563b
merge with main
magmacomputing Apr 27, 2026
7d2ae4a
complete merge
magmacomputing Apr 27, 2026
25e667f
post-merge baseline
magmacomputing Apr 27, 2026
a2ca7eb
rm spyOn from test-scripts
magmacomputing Apr 27, 2026
2f77c22
Tempo.config
magmacomputing Apr 27, 2026
a4f06f0
test-cases passing
magmacomputing Apr 28, 2026
69349bf
doc/tempo.month-day
magmacomputing Apr 28, 2026
bca222b
test sbox
magmacomputing Apr 28, 2026
40714f9
draw a line... layout ordering
magmacomputing Apr 28, 2026
980b249
new scripts in package.json
magmacomputing Apr 28, 2026
68dd00a
PR review #1 updates
magmacomputing Apr 28, 2026
dcaf82c
PR review #2 updates
magmacomputing Apr 28, 2026
e40dda0
PR review #3 updates
magmacomputing Apr 29, 2026
da96de0
#library
magmacomputing Apr 29, 2026
4537efa
resolve some final typings
magmacomputing Apr 29, 2026
c93f67f
realign test-scripts
magmacomputing Apr 29, 2026
a9db3f1
plan
magmacomputing Apr 30, 2026
6dbcfcf
PR review #4 updates
magmacomputing Apr 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 4 additions & 18 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,7 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Cache npm
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-npm-${{ hashFiles('packages/tempo/package-lock.json') }}
restore-keys: |
${{ runner.os }}-npm-
cache: 'npm'
- name: Install monorepo dependencies
run: npm ci
working-directory: ${{ github.workspace }}
Expand All @@ -51,28 +45,20 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Cache npm
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-npm-${{ hashFiles('packages/tempo/package-lock.json') }}
restore-keys: |
${{ runner.os }}-npm-
cache: 'npm'
- name: Install monorepo dependencies
run: npm ci
working-directory: ${{ github.workspace }}
- name: Write parsePrefilter setup file
run: |
echo "import { Tempo } from '../src/tempo.index.ts';\nTempo.init({ parsePrefilter: true });" > packages/tempo/test/ci.prefilter.setup.js
- name: Run all tests with parsePrefilter
run: npm test
working-directory: packages/tempo
env:
TEMPO_PREFILTER_CI: 'true'
- name: Run end-to-end benchmark
run: npx tsx --conditions=development bench/bench.parse.prefilter.e2e.ts > bench-output.json
run: npx tsx --conditions=development bench/bench.parse.prefilter.e2e.ts > bench-output.json 2>&1
Comment thread
magmacomputing marked this conversation as resolved.
Outdated
working-directory: packages/tempo
- name: Upload benchmark output
if: always()
uses: actions/upload-artifact@v4
with:
name: bench-parse-prefilter-e2e
Expand Down
39 changes: 35 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,50 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [2.4.0] - 2026-04-24
## [2.7.0] - 2026-04-27

### Added
- **Sandbox Factory Mode**: Introduced `Tempo.create()`, a static factory method for creating isolated `Tempo` subclasses with independent configurations and registries, preventing global state leakage.
- **Layout Controller Framework**: Added a classification-based layout controller to `engine.layout`, enabling future input-aware parsing optimizations.
- **Grouped Configuration Options**: Consolidated `monthDay` and `relativeTime` options into nested objects.
- **Internal layout detection**: Added `isMonthDay` detection for improved regional layout resolution.
- **CI Benchmarks**: Added performance benchmarking suite to CI.

### Fixed
- **Event Overrides**: Fixed `$setEvents` logic to correctly handle custom event overrides.
- **TimeZone Fallbacks**: Improved and cleaned up the IANA TimeZone fallback list.
- **Intl.Locale Debugging**: Enhanced diagnostic logging for locale resolution.

## [2.6.0] - 2026-04-25

### Added
- **Standardized UTC Offsets**: Added `normalizeUtcOffset` utility for transforming informal UTC-offset strings.
- **Custom Layout Order**: Added `layoutOrder` option to customize parsing element precedence.

### Changed
- **Layout Order Resolver**: Extracted layout-ordering logic into a dedicated module to improve maintainability and testability.
- **Season Scope Simplification (Breaking)**: Removed Chinese-specific object from `term.season` scope.
- **Refined TimeZone Normalization**: Improved UTC offset handling during initialization.

### Fixed
- **Layout Pattern Resolution**: Fixed ordering to respect intended sequence.

## [2.5.0] - 2026-04-24

### Added
- **Sandbox Factory Mode**: Introduced `Tempo.create()`, a static factory method for creating isolated `Tempo` subclasses with independent configurations and registries.
- **Layout Order Resolver Module**: Extracted layout-ordering decision logic into a dedicated `engine.layout` module.
- **Layout Controller Framework**: Implemented minimal controller-map infrastructure for future input-class pre-filtering.
- **Debug Layout Order Visibility**: Added optional debug output in `Tempo.#swapLayout` to emit the resolved layout order for diagnostics.

### Changed
- **Internal Layout Resolution**: Refactored `Tempo.#swapLayout` to delegate ordering to the external resolver.
- **Alias Precedence**: User-defined `event` and `period` aliases now take precedence over built-in aliases.
- **Module Path Flattening**: Relocated core modules to `src/module/` for a flatter, more intuitive internal architecture.

### Fixed
- **Determinism Coverage**: Added comprehensive unit tests for layout resolution and multi-pair swap handling.

## [2.4.0] - (Skipped)

_Version 2.4.0 was not released; the project merged new functionality from 2.4.0 into 2.5.0._

## [2.3.0] - 2026-04-22

Expand Down
21 changes: 16 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "tempo-monorepo",
"version": "2.6.0",
"version": "2.7.0",
"private": true,
"description": "Magma Computing Monorepo",
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion packages/library/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@magmacomputing/library",
"version": "2.6.0",
"version": "2.7.0",
"description": "Shared utility library for Tempo",
"author": "Magma Computing Solutions",
"license": "MIT",
Expand Down
4 changes: 3 additions & 1 deletion packages/library/src/common/proxy.library.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,10 @@ function factory<T extends object>(target: T, options: ProxyOptions = {}): T {
pending.delete(k);
}
// silent mark to avoid redundant discovery
if (Reflect.isExtensible(t) && !Reflect.has(t, k))
// Only define if object is extensible and not frozen
if (Reflect.isExtensible(t) && !Object.isFrozen(t) && !Reflect.has(t, k)) {
Object.defineProperty(t, k, { value: undefined, writable: true, enumerable: false, configurable: true });
}
}

const val = Reflect.get(t, k, r);
Expand Down
9 changes: 6 additions & 3 deletions packages/tempo/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ if (!(globalThis as any).Temporal) {
(globalThis as any).Temporal = Temporal;
}

import typedocSidebar from '../doc/api/typedoc-sidebar.json'

export default defineConfig({
base: '/magma/',
title: "Tempo",
Expand All @@ -20,7 +22,7 @@ export default defineConfig({
},
nav: [
{ text: 'Guide', link: '/README' },
{ text: 'API', link: '/doc/tempo.api' },
{ text: 'API Reference', link: typedocSidebar[0].items[0].link },
{ text: 'Releases', link: '/doc/releases/' }
],
sidebar: [
Expand All @@ -39,6 +41,7 @@ export default defineConfig({
items: [
{ text: 'Configuration', link: '/doc/tempo.config' },
{ text: 'Smart Parsing', link: '/doc/tempo.parse' },
{ text: 'Regional Parsing (MDY)', link: '/doc/tempo.month-day' },
{ text: 'Smart Formatting', link: '/doc/tempo.format' },
{ text: 'Modularity', link: '/doc/tempo.modularity' },
{ text: 'Layout Patterns', link: '/doc/tempo.layout' },
Expand All @@ -49,8 +52,8 @@ export default defineConfig({
{
text: 'Advanced Reference',
items: [
{ text: 'API Reference', link: '/doc/tempo.api' },
{ text: 'Types System', link: '/doc/tempo.types' },
{ text: 'API Overview', link: '/doc/api/' },
{ text: 'Technical Reference', link: typedocSidebar[0].items[0].link },
{ text: 'Shorthand Engine', link: '/doc/tempo.shorthand' },
{ text: 'Weekday Engine', link: '/doc/tempo.weekday' },
{ text: 'Debugging', link: '/doc/tempo.debugging' }
Expand Down
25 changes: 25 additions & 0 deletions packages/tempo/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,31 @@ 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).

## [2.7.0] - 2026-04-27

### Added
- **Grouped Configuration Options**: Consolidated `monthDay` and `relativeTime` options into nested objects.
- **Internal layout detection**: Added `isMonthDay` detection for improved regional layout resolution.
- **CI Benchmarks**: Added performance benchmarking suite to CI.

### Fixed
- **Event Overrides**: Fixed `$setEvents` logic to correctly handle custom event overrides.
- **TimeZone Fallbacks**: Improved and cleaned up the IANA TimeZone fallback list.
- **Intl.Locale Debugging**: Enhanced diagnostic logging for locale resolution.

## [2.6.0] - 2026-04-25

### Added
- **Standardized UTC Offsets**: Added `normalizeUtcOffset` utility for transforming informal UTC-offset strings.
- **Custom Layout Order**: Added `layoutOrder` option to customize parsing element precedence.

### Changed
- **Season Scope Simplification (Breaking)**: Removed Chinese-specific object from `term.season` scope.
- **Refined TimeZone Normalization**: Improved UTC offset handling during initialization.

### Fixed
- **Layout Pattern Resolution**: Fixed ordering to respect intended sequence.

## [2.5.0] - 2026-04-24

### Added
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ interface Options {
catch?: boolean; // If true, invalid inputs return a Void instance
store?: string; // Key for persistent storage (e.g., localStorage)
sphere?: 'north' | 'south'; // Hemisphere for seasonal plugins
rtfFormat?: Intl.RelativeTimeFormat; // Pre-configured formatter
rtfStyle?: 'long' | 'short' | 'narrow'; // Default style (default: 'narrow')
relativeTime?: { format?: Intl.RelativeTimeFormat, style?: 'long' | 'short' | 'narrow' };
monthDay?: boolean | { active?: boolean, locales?: string[], layouts?: [string, string][], timezones?: Record<string, string[]> };
timeStamp?: 'ms' | 'ns'; // Precision for numeric timestamps
[key: string]: any; // Allows custom configurations shared with plugins
}
Expand Down
18 changes: 18 additions & 0 deletions packages/tempo/doc/migration-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,5 +94,23 @@ Season term scope output has been simplified.
1. If you previously relied on the Chinese-specific object attached to `term.season` scope output, remove that dependency.
2. Resolve Chinese season context by creating a dedicated `Tempo` instance with the appropriate Chinese `timeZone` for the interpretation you need.

## 🔁 Migrating from version 2.7.0 (Grouped Options)

Tempo has rationalized its configuration surface by grouping related options into nested objects. This improves discoverability and allows for easier additive merging across the prototype chain.

### Month-Day (Regional Parsing)
The individual `mdyLocales` and `mdyLayouts` options have been consolidated into a single `monthDay` object.
- **v2.6.x:** `new Tempo({ mdyLocales: ['en-US'] })`
- **v2.7.x:** `new Tempo({ monthDay: { locales: ['en-US'] } })`
- **Shortcut:** `new Tempo({ monthDay: true })` (enables forced MDY parsing using default locales).

### Relative Time
The individual `rtfFormat` and `rtfStyle` options have been consolidated into a single `relativeTime` object.
- **v2.6.x:** `new Tempo({ rtfStyle: 'long' })`
- **v2.7.x:** `new Tempo({ relativeTime: { style: 'long' } })`

### Action Required:
While the old top-level keys still work as fallbacks in the current release, you should update your configuration to use the new nested structure to ensure compatibility with future versions and the upcoming Release-C optimization engine.

Comment thread
magmacomputing marked this conversation as resolved.
## 🧪 Testing and Stability
v2.x has been hardened with a 100% pass rate on our regression suite. If you were relying on undocumented "quirks" or bugs in v1.x parsing, you may find that v2.x is more strict and deterministic.
17 changes: 17 additions & 0 deletions packages/tempo/doc/releases/v2.x.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
# 📜 Version 2.x History

## [v2.7.0] - 2026-04-27
### 🧩 Configuration Rationalization
- **Grouped Options**: Consolidated regional parsing and relative time options into nested objects for improved discoverability and additive merging support.
- `mdyLocales` and `mdyLayouts` → `monthDay: { locales, layouts }`
- `rtfFormat` and `rtfStyle` → `relativeTime: { format, style }`
- **Internal Hardening**: Added `isMonthDay` internal detection to more accurately resolve parse-order layouts in regional contexts.

### 🛡️ Resilience & Bug Fixes
- **Event Registry Overrides**: Fixed `$setEvents` logic to ensure user-defined event aliases correctly override or merge with built-in patterns without duplication or stale matches.
- **TimeZone Fallback Resilience**: Enhanced the IANA TimeZone fallback mechanism with a cleaned-up, prioritized list of regional aliases.
- **Intl.Locale Debugging**: Improved diagnostic logging for `Intl.Locale` resolution to assist in cross-environment consistency issues.
- **Ticker Stabilization**: Further refined async generator pulse counts and termination logic.

### ⚙️ CI/CD & Infrastructure
- **Performance Benchmarking**: Integrated a dedicated `bench/` suite into the CI pipeline to monitor performance impact on core parsing and mutation logic.
- **Workflow Optimization**: Refactored the GitHub Actions workflow for better test isolation and Node.js 24 compatibility.

## [v2.6.0] - 2026-04-25
### ⚠️ Migration Notes

Expand Down
6 changes: 2 additions & 4 deletions packages/tempo/doc/tempo.config.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,12 +133,10 @@ Tempo.init({
| `locale` | `string` | System Locale | Default BCP 47 language tag. used in .since() method |
| `calendar` | `string` | `'iso8601'` | Default calendar system. |
| `pivot` | `number` | `75` | Cutoff for parsing two-digit years. |
| `mdyLocales` | `string \| string[]` | `['en-US','en-AS']` | Locale list that prefers month-day-year parse ordering. Tempo uses `Intl.Locale.getTimeZones()` to detect if your `timeZone` belongs to these locales, with an automated fallback for CI environments. |
| `mdyLayouts` | `[string, string][]` | Built-in pairs | Layout swap pairs used when month-day ordering applies. |
| `monthDay` | `MonthDay \| boolean` | `undefined` | Regional date-parsing configuration (grouped). Includes `active`, `locales`, `layouts`, and `timezones`. |
| `timeStamp`| `'ms' \| 'ns'` | `'ms'` | Precision for timestamps. |
| `sphere` | `'north' \| 'south'`| Auto-inferred | Hemisphere for seasonal plugins. |
| `rtfFormat` | `Intl.RTF` | `undefined` | Pre-configured relative time formatter. |
| `rtfStyle` | `'long' \| 'short' \| 'narrow'` | `'narrow'` | Default style for relative time formatting. |
| `relativeTime` | `RelativeTime` | `undefined` | Relative time formatting configuration (grouped). |
| `event` | `Record<string, string \| Function>` | Built-in aliases | Custom date aliases merged into the event registry. |
| `period` | `Record<string, string \| Function>` | Built-in aliases | Custom time aliases merged into the period registry. |
| `snippet` | `Record<string, string \| RegExp>` | Built-in snippets | Custom snippet patterns used to compose parse layouts. |
Expand Down
34 changes: 34 additions & 0 deletions packages/tempo/doc/tempo.cookbook.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,17 @@ if (t.isValid) {
}
```

### Global Configuration & Initialization
You can initialize global defaults that apply to all future `Tempo` instances.
```typescript
Tempo.init({
timeZone: 'UTC',
locale: 'en-GB',
silent: true // Suppress console errors for expected parsing failures
});
```
Settings are inherited from library defaults, persistent storage, and your provided options.

---

## Parsing Challenges
Expand Down Expand Up @@ -120,6 +131,16 @@ const daysLeft = t.until('2025-01-01', 'days');
console.log(`${daysLeft} days remaining`);
```

### Precision Manipulation (`end` vs `mid`)
When using `.set()` with a term anchor (like `#qtr`), you can specify whether to land on the inclusive end or the exact center.
```typescript
// Lands on 30-Sep 23:59:59.999... (Inclusive End)
const qtrEnd = new Tempo().set({ end: '#qtr' });

// Lands on the arithmetic nanosecond midpoint of the period
const qtrMid = new Tempo().set({ mid: '#qtr' });
```

---

## Timezones & Locales
Expand All @@ -138,6 +159,19 @@ console.log(london.format('{hh}:{mi}')); // "15:00"
const utcNow = new Tempo({ timeZone: 'UTC' });
```

### High-Performance Relative Time (`since`)
Tempo memoizes `Intl.RelativeTimeFormat` objects internally for efficiency.
```typescript
const t = new Tempo('yesterday');
console.log(t.since()); // "1d ago" (narrow style)

// For maximum performance in tight loops, pass a pre-allocated formatter
const rtf = new Intl.RelativeTimeFormat('fr', { style: 'long' });
for (const entry of logEntries) {
console.log(new Tempo(entry.ts).since(null, { rtfFormat: rtf }));
}
Comment thread
magmacomputing marked this conversation as resolved.
```

---

## Business Logic & Terms
Expand Down
Loading
Loading