Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2,073 changes: 1,146 additions & 927 deletions package-lock.json

Large diffs are not rendered by default.

17 changes: 9 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "tempo-monorepo",
"version": "3.1.1",
"version": "3.2.0",
"private": true,
"engines": {
"node": ">=20.0.0"
Expand Down Expand Up @@ -34,29 +34,30 @@
"@types/google.maps": "^3.65.0",
"@types/hammerjs": "^2.0.46",
"@types/jquery": "^4.0.0",
"@types/node": "^25.9.1",
"@types/node": "^25.9.3",
"@vitest/browser": "^4.1.8",
"@vitest/browser-playwright": "^4.1.8",
"@vitest/browser-webdriverio": "^4.1.8",
"@vitest/ui": "^4.1.7",
"cross-env": "^10.1.0",
"markdown-it-mathjax3": "^4.3.2",
"markdown-it-mathjax3": "^5.2.0",
"playwright": "^1.60.0",
"rollup": "^4.60.4",
"rollup": "^4.62.0",
"tslib": "^2.8.1",
"tsx": "^4.22.3",
"typescript": "^6.0.3",
"unplugin-swc": "^1.5.9",
"vitest": "^4.1.7",
"webdriverio": "^9.27.2"
"webdriverio": "^9.28.0"
},
"overrides": {
"esbuild": "^0.28.0"
},
"allowScripts": {
"esbuild": true,
"@swc/core": true,
"edgedriver@6.3.0": true,
"geckodriver@6.1.0": true
"geckodriver@6.1.0": true,
"@swc/core@1.15.41": true,
"esbuild@0.28.1": true,
"esbuild@0.21.5": true
}
}
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": "3.1.1",
"version": "3.1.2",
"description": "Shared utility library for Tempo",
"author": "Magma Computing Solutions",
"license": "MIT",
Expand Down
21 changes: 14 additions & 7 deletions packages/tempo/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@ import { defineConfig } from 'vitepress'
import { fileURLToPath } from 'node:url'
import { Temporal } from '@js-temporal/polyfill'

if (!(globalThis as any).Temporal) {
(globalThis as any).Temporal = Temporal;
if (typeof (globalThis as any).Temporal === 'undefined') {
Object.defineProperty(globalThis, 'Temporal', {
value: Temporal,
enumerable: false,
configurable: true,
writable: true,
});
}

import typedocSidebar from '../doc/api/typedoc-sidebar.json'
Expand All @@ -14,11 +19,11 @@ export default defineConfig({
description: "The Professional Date-Time Library for Temporal",
srcDir: '.',
srcExclude: ['**/plan/**', '**/archive/**', '**/bench/**', '**/scratch/**', 'CHANGELOG.md'],
markdown: {
math: true
},
head: [
['link', { rel: 'icon', type: 'image/svg+xml', href: '/magma/tempo-logo.svg' }]
],
themeConfig: {
logo: '/logo.svg',
logo: '/tempo-logo.svg',
search: {
provider: 'local'
},
Expand Down Expand Up @@ -63,6 +68,7 @@ export default defineConfig({
items: [
{ text: 'API Overview', link: '/doc/api/' },
{ text: 'Technical Reference', link: typedocSidebar[0].items[0].link },
{ text: 'The Role of Locale', link: '/doc/tempo.locale' },
{ text: 'Shorthand Engine', link: '/doc/tempo.shorthand' },
{ text: 'Weekday Engine', link: '/doc/tempo.weekday' },
{ text: 'Debugging', link: '/doc/tempo.debugging' }
Expand Down Expand Up @@ -115,7 +121,8 @@ export default defineConfig({
},
vite: {
build: {
target: 'es2022'
target: 'es2022',
chunkSizeWarningLimit: 2000
},
esbuild: {
target: 'esnext'
Expand Down
24 changes: 24 additions & 0 deletions packages/tempo/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,30 @@ 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.2.0] - 2026-06-16

### 🔒 Security & Licensing
- **Domain-Locked Licensing**: Stabilized the premium plugin licensing engine with a robust domain-locked validation mechanism in the `@core` validator.

### 📚 Documentation & Ecosystem
- **SSR Hydration Fixes**: Fixed `Temporal is not defined` crashes during VitePress SSR builds by enforcing global polyfill initialization in `.vitepress/config.ts`.
- **Navigation Enhancements**: Integrated the new Internationalization (`tempo.locale.md`) guides into the primary VitePress sidebar.
- **MathPlugin Removal**: Removed the heavy `math: true` Markdown plugin from the documentation pipeline and migrated all computational complexity notations (e.g., `O(1)`) to standard inline code to prevent Vue compiler crashes and reduce client bundle sizes.

### Added
- **Multi-lingual Parsing**: The `locale` configuration property now officially accepts an array of strings (`string | string[]`). This enables the `ParseModule` to intelligently extract terminology from multiple languages simultaneously, generating a single, high-performance, deduplicated RegExp engine capable of parsing dates from any of the specified locales interchangeably.
- **O(1) Locale Traceability**: Upgraded the internal dictionary architecture so that `monthMap` and `weekdayMap` store strict objects `{ value, locale }` instead of raw numeric indices. This allows the Normalizer to resolve the winning language of a matched token in pure `O(1)` time without any expensive Regex sub-capture scanning, automatically injecting the originating `locale` into the `t.Parse.result` debugging array!
- **Intl.DateTimeFormatOptions Passthrough**: The `.format()` method now officially supports passing a native `Intl.DateTimeFormatOptions` object. This provides a highly flexible, "humanized" wrapper around the rigid `Temporal` API for complex cultural formatting (e.g. Arabic, Japanese Reiwa).
- **Native Intl Delegation Support**: The formatting engine now explicitly attempts to use the high-performance, memoized `getDTF` instance to support V8's upcoming native `Temporal` support in `Intl.DateTimeFormat`.
- **BigInt Overload for Epoch Nanoseconds**: Implemented a `BigIntPattern` registry and resolution logic to guarantee that the `{nano}` token (epoch nanoseconds) correctly coerces to and returns a precision-preserving `BigInt` instead of a string, as it vastly exceeds `Number.MAX_SAFE_INTEGER`.

### Changed
- **Parser Map Safety**: To support multi-lingual lexing securely, the internal reverse-lookup `localeMap` has been architecturally split into distinct `monthMap` and `weekdayMap` dictionaries. This eliminates the risk of cross-locale abbreviation collisions (e.g., if "mai" is a month in one language but a weekday in another), guaranteeing completely safe parsing determinism.

### Fixed
- **Polyfill RangeError Bypassing**: Implemented a defensive stripping mechanism that intercepts and deletes `timeZone` and `calendar` constraints from the `options` object when falling back to the `ZonedDateTime.prototype.toLocaleString()` polyfill. Additionally, wrapped `withTimeZone` and `withCalendar` shifts in `try/catch` blocks to gracefully bypass invalid configurations and safely fall through to the native Intl fallback without crashing the formatter.
- **Strict Numeric Output Evaluation**: Fixed a bug where `{nano}` was bypassing the precision-preserving `ifNumeric` coercion due to an evaluation gap in the generic numeric-pattern detector.

## [3.1.1] - 2026-06-14

### Added
Expand Down
5 changes: 3 additions & 2 deletions packages/tempo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<tbody>
<tr>
<td width="100" valign="top">
<img src="./img/logo.svg" width="90" height="90" alt="Tempo Logo">
<img src="./img/tempo-logo.svg" width="90" height="90" alt="Tempo Logo">
</td>
<td valign="middle">
<h1 style="border-bottom: none; margin-bottom: 0;">Tempo</h1>
Expand Down Expand Up @@ -73,7 +73,7 @@ For standard usage natively in the browser, use the pre-optimized **Global ESM B
{
"imports": {
"@js-temporal/polyfill": "https://cdn.jsdelivr.net/npm/@js-temporal/polyfill@0.5.1/dist/index.esm.js",
"@magmacomputing/tempo": "https://cdn.jsdelivr.net/npm/@magmacomputing/tempo@3/dist/tempo.bundle.esm.js"
"@magmacomputing/tempo": "https://cdn.jsdelivr.net/npm/@magmacomputing/tempo@4/dist/tempo.bundle.esm.js"
}
}
</script>
Expand All @@ -95,6 +95,7 @@ For standard usage natively in the browser, use the pre-optimized **Global ESM B

## ✨ Why Tempo?
* **🏗️ Future Standard**: Built natively on the TC39 `Temporal` proposal. Inherit the reliability of the future standard.
* **🌍 Zero-Bundle Localization**: Best-in-class multi-language parsing and formatting powered natively by the `Intl` API—no massive static locale dictionaries required.
* **🗣️ Natural Language**: Resolve complex terms like "two days ago" with zero configuration.
* **🧠 Functional Aliases**: Extend the parser with custom logic using a powerful resolution context for relative date math.
* **🔄 Cycle Persistence**: Shift by semantic terms (Quarters, Seasons) while preserving your relative day-of-period offset.
Expand Down
12 changes: 6 additions & 6 deletions packages/tempo/doc/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ The property descriptor is `enumerable: false, configurable: false, writable: fa

**Benefits:**
- **Reduced global footprint** — one slot instead of seven.
- **Centralised hardening** — input validation (`addTerm`, `addPlugin`) and hook management (`setRegisterHook`, `fireRegisterHook`) live in one place.
- **Centralised hardening** — input validation (`Tempo.extend`) and hook management (`setRegisterHook`, `fireRegisterHook`) live in one place.
- **Scoped runtimes (Experimental)** — `TempoRuntime.createScoped()` returns a fresh, isolated runtime for clean test isolation without `globalThis` manipulation. This remains an experimental internal feature and is not yet fully threaded through all core utilities. Unlike the primary runtime, a scoped runtime is not pinned to `globalThis`, does not receive the hardened `defineProperty` protections, and relies on the returned lexical reference instead of the shared `getRuntime()` / `globalThis[BRIDGE]` path. Implementation examples of this test-scoping pattern can be found in [plugin_registration.test.ts](../test/plugin_registration.test.ts) and [duration.core.test.ts](../test/duration.core.test.ts).
- **Multi-bundle / HMR safety** — `getRuntime()` checks `globalThis[BRIDGE]` before constructing, so two bundle copies of Tempo always share the same runtime object, preserving the original split-brain guarantee.

Expand Down Expand Up @@ -62,7 +62,7 @@ This objective is achieved through two primary architectural pillars:
1. **Lazy Evaluation ([Section 1](#1-lazy-evaluation-shadowing))**: Deferring the expensive work of string parsing and Term computation until the first property access.
2. **Master Guard ([Section 3](#3-master-guard-fast-fail-sync-point))**: Implementing a high-speed "fast-fail" gatekeeper to instantly reject invalid inputs when parsing *is* eventually triggered.

Together, these ensure that `new Tempo()` maintains an $O(1)$ constructor execution time by deferring $O(N)$ full-parse work until the first property access, regardless of how many plugins or custom Terms are registered in the global system.
Together, these ensure that `new Tempo()` maintains an `O(1)` constructor execution time by deferring `O(N)` full-parse work until the first property access, regardless of how many plugins or custom Terms are registered in the global system.

---

Expand All @@ -83,7 +83,7 @@ A delegator Proxy is a Proxy wrapper whose traps forward operations to an intern
### 🛡️ Iteration Notes
- **`Object.keys` / `for...in` / object spread**: Operate on enumerable keys exposed by the delegator target after discovery.
- **`[Symbol.iterator]`**: Still provides explicit iterator semantics where implemented.
- **`Tempo.formats` & `Tempo.terms`**: These static getters continue to provide a registry-wide view of available keys across the system, independent of per-instance memoization state.
- **`Tempo.registry.formats` & `Tempo.registry.terms`**: These static getters continue to provide a registry-wide view of available keys across the system, independent of per-instance memoization state.

---

Expand Down Expand Up @@ -129,9 +129,9 @@ The **Guarded-Lazy** strategy ensures that even with hundreds of custom plugins,
### How it works:
1. **Longest-Token Matching**: To prevent partial matching (e.g., matching `qtr` inside `quarter`), the guard uses a greedy "Scan-and-Consume" loop that prioritizes the longest available token.
2. **Unified Wordlist**: The guard automatically ingests all registered Terms, Timezones, Month names, and Custom Events into a single high-speed lookup Set.
3. **High-Speed Gatekeeper**: By avoiding complex backtracking regexes, the gatekeeper provides predictable $O(1)$ performance regardless of how many plugins are registered.
3. **High-Speed Gatekeeper**: By avoiding complex backtracking regexes, the gatekeeper provides predictable `O(1)` performance regardless of how many plugins are registered.
4. **Versioned Registry**: To avoid redundant wordlist rebuilding, the Guard monitors a version counter on the alias registry. The wordlist is only rebuilt when a mutation actually occurs.
5. **Auto-Lazy**: Valid inputs that pass the guard automatically switch the instance to `mode: 'defer'`, deferring the full $O(N)$ parse work until a property is actually read.
5. **Auto-Lazy**: Valid inputs that pass the guard automatically switch the instance to `mode: 'defer'`, deferring the full `O(N)` parse work until a property is actually read.

---

Expand Down Expand Up @@ -160,7 +160,7 @@ For detailed timing results and methodology, see [Performance Benchmarks](./temp
Tempo maintains system-wide synchronization through a private, Symbol-based hook system.

### Reactive Registration
When a plugin is imported via a side-effect (`import '@magmacomputing/tempo/ticker'`), it triggers a **`sym.$Register`** hook.
When a plugin is imported via a side-effect (`import '@magmacomputing/tempo/duration'`), it triggers a **`sym.$Register`** hook.
- **Auto-Sync**: The `Tempo` class listens for these hooks and automatically updates its internal registries.
- **Guard Rebuild**: Every time a new Term or layout is registered, the **Master Guard** is automatically rebuilt to include the new tokens, ensuring the "Zero-Cost Constructor" always stays up to date.

Expand Down
10 changes: 10 additions & 0 deletions packages/tempo/doc/commercial.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@ Need a specialized plugin for your industry? We design and implement high-perfor
- **Production Timelines**: Complex shift-based scheduling and resource allocation generators.
- **Custom Terms**: Domain-specific date ranges (e.g., academic years, retail seasons, or medical billing cycles).

<div style="display: flex; align-items: center; gap: 16px; margin: 16px 0;">
<a href="https://registry.magmacomputing.com.au" target="_blank" rel="noopener noreferrer" style="display: flex; flex-shrink: 0;">
<img src="https://registry.magmacomputing.com.au/registry-logo.svg" width="48" height="48" alt="Tempo License Registry" style="margin: 0;" />
</a>
<div>
<strong><a href="https://registry.magmacomputing.com.au" target="_blank" rel="noopener noreferrer">👉 Go to the Tempo License Registry 👈</a></strong><br>
Manage your subscriptions and retrieve your license key.
</div>
</div>

### 🏛️ Architecture & Migration Consulting
Transitioning from legacy libraries like **Moment.js** or **Luxon**? Our team can:
- Audit your existing date-time logic for `Temporal` compatibility.
Expand Down
18 changes: 9 additions & 9 deletions packages/tempo/doc/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

As of 13 January 2026, Chrome 144 has shipped `Temporal`, and Firefox 139 also includes native `Temporal` support. You can verify browser support at https://caniuse.com/temporal.

Node.js 26.0.0+ ships native `Temporal` fully enabled by default. Older Node versions may still require an external polyfill or experimental flags depending on the V8 version.
Node.js 26.0.0+ ships native `Temporal` fully enabled by default. Older Node versions may still require an external polyfill or experimental flags depending on the underlying engine version.

::: warning
Older Node.js releases that ship `Temporal` behind a feature flag may still have incomplete or experimental implementations. For mission-critical stability in those older environments, we strongly recommend using `@js-temporal/polyfill`.
Expand Down Expand Up @@ -136,7 +136,7 @@ While you *could* import directly from the URL everywhere, the best practice is
<script type="importmap">
{
"imports": {
"@magmacomputing/tempo": "https://esm.sh/@magmacomputing/tempo@3.0.1",
"@magmacomputing/tempo": "https://esm.sh/@magmacomputing/tempo@3.2.0",
"@magmacomputing/tempo-plugin-ticker": "https://esm.sh/@magmacomputing/tempo-plugin-ticker@1.0.4"
}
}
Expand Down Expand Up @@ -166,14 +166,14 @@ To use Premium Plugins via static CDN, you must map the core library, its subpat
"tslib": "https://cdn.jsdelivr.net/npm/tslib@2.8.1/tslib.es6.mjs",
"@js-temporal/polyfill": "https://cdn.jsdelivr.net/npm/@js-temporal/polyfill@0.5.1/dist/index.esm.js",

"@magmacomputing/tempo": "https://cdn.jsdelivr.net/npm/@magmacomputing/tempo@3.0.1/dist/tempo.index.js",
"@magmacomputing/tempo/core": "https://cdn.jsdelivr.net/npm/@magmacomputing/tempo@3.0.1/dist/core.index.js",
"@magmacomputing/tempo/library": "https://cdn.jsdelivr.net/npm/@magmacomputing/tempo@3.0.1/dist/library.index.js",
"@magmacomputing/tempo/plugin": "https://cdn.jsdelivr.net/npm/@magmacomputing/tempo@3.0.1/dist/plugin/plugin.index.js",
"@magmacomputing/tempo/enums": "https://cdn.jsdelivr.net/npm/@magmacomputing/tempo@3.0.1/dist/support/support.enum.js",
"@magmacomputing/tempo/term": "https://cdn.jsdelivr.net/npm/@magmacomputing/tempo@3.0.1/dist/plugin/term/term.index.js",
"@magmacomputing/tempo": "https://cdn.jsdelivr.net/npm/@magmacomputing/tempo@3.2.0/dist/tempo.index.js",
"@magmacomputing/tempo/core": "https://cdn.jsdelivr.net/npm/@magmacomputing/tempo@3.2.0/dist/core.index.js",
"@magmacomputing/tempo/library": "https://cdn.jsdelivr.net/npm/@magmacomputing/tempo@3.2.0/dist/library.index.js",
"@magmacomputing/tempo/plugin": "https://cdn.jsdelivr.net/npm/@magmacomputing/tempo@3.2.0/dist/plugin/plugin.index.js",
"@magmacomputing/tempo/enums": "https://cdn.jsdelivr.net/npm/@magmacomputing/tempo@3.2.0/dist/support/support.enum.js",
"@magmacomputing/tempo/term": "https://cdn.jsdelivr.net/npm/@magmacomputing/tempo@3.2.0/dist/plugin/term/term.index.js",

"#tempo/license": "https://cdn.jsdelivr.net/npm/@magmacomputing/tempo@3.0.1/dist/plugin/license/license.validator.js",
"#tempo/license": "https://cdn.jsdelivr.net/npm/@magmacomputing/tempo@3.2.0/dist/plugin/license/license.validator.js",

"@magmacomputing/tempo-plugin-astro": "https://cdn.jsdelivr.net/npm/@magmacomputing/tempo-plugin-astro@1.1.6/dist/index.js",
"@magmacomputing/tempo-plugin-ticker": "https://cdn.jsdelivr.net/npm/@magmacomputing/tempo-plugin-ticker@1.0.4/dist/index.js"
Expand Down
Loading
Loading