Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
4 changes: 2 additions & 2 deletions .coderabbit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ reviews:
# Use "! " at the start of a pattern to EXCLUDE it from the review
- "!**/doc/api/**" # Ignore ONLY the generated TypeDoc API documentation
- "!**/docs/**" # Ignore other generated documentation artifacts
- "!**/.vitepress/cache/**" # Ignore VitePress cache
- "!**/.vitepress/dist/**" # Ignore VitePress build output
- "!**/.vitepress/cache/**" # Ignore VitePress cache
- "!**/.vitepress/dist/**" # Ignore VitePress build output
- "!**/dist/**" # Ignore all build artifacts
- "!**/vendor/**" # Ignore third-party code
2 changes: 1 addition & 1 deletion packages/library/src/common/pledge.class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ declare module '#library/type.library.js' {
export class Pledge<T> {
#pledge: PromiseWithResolvers<T>;
#status = {} as Pledge.Status<T>;
static #dbg = new Logify('Pledge: ');
static #dbg = new Logify('Pledge');
static #static = {} as Pledge.Constructor;

static STATE = secure({
Expand Down
2 changes: 2 additions & 0 deletions packages/tempo/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export default defineConfig({
text: 'Getting Started',
items: [
{ text: 'Introduction', link: '/README' },
{ text: 'Installation', link: '/doc/installation' },
{ text: 'Cookbook', link: '/doc/tempo.cookbook' },
{ text: 'Migration Guide', link: '/doc/migration-guide' },
{ text: 'Release Notes', link: '/doc/releases/' }
Expand All @@ -34,6 +35,7 @@ export default defineConfig({
text: 'Core Concepts',
items: [
{ text: 'Configuration', link: '/doc/tempo.config' },
{ text: 'Smart Parsing', link: '/doc/tempo.parse' },
{ text: 'Modularity', link: '/doc/tempo.modularity' },
{ text: 'Layout Patterns', link: '/doc/tempo.layout' },
{ text: 'Terms System', link: '/doc/tempo.term' },
Expand Down
217 changes: 62 additions & 155 deletions packages/tempo/README.md
Original file line number Diff line number Diff line change
@@ -1,148 +1,91 @@
<table width="100%">
<tbody>
<tr>
<td align="left" width="120">
<img src="./img/logo.svg" width="120" alt="Tempo logo">
</td>
<td align="left" valign="middle">
<h1><font color="#3498db">Tempo</font></h1>
<p><strong>The Professional Date-Time Library for the Temporal API</strong></p>
</td>
</tr>
</tbody>
</table>

<br>

**Tempo** is a premium, high-performance wrapper around the JavaScript `Temporal` API. It provides a modern, **immutable**, and **fluent** interface for date-time manipulation, and flexible parsing. It's designed as a better-performing, type-safe alternative to legacy libraries like **Moment.js**, **Day.js**, and **Luxon**.

<div align="center">
<table>
<tbody>
<tr>
<td align="center"><a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a></td>
<td align="center"><a href="https://tc39.es/proposal-temporal/"><img src="https://img.shields.io/badge/Temporal-Stage%204-green" alt="Temporal"></a></td>
<td align="center"><a href="https://www.typescriptlang.org/"><img src="https://img.shields.io/badge/TypeScript-Ready-blue?logo=typescript" alt="TypeScript Ready"></a></td>
<td align="center"><a href="https://nodejs.org/api/esm.html"><img src="https://img.shields.io/badge/Native-ESM-green" alt="Native ESM"></a></td>
<td align="center"><a href="https://magmacomputing.github.io/magma/"><img src="https://img.shields.io/badge/Docs-VitePress-brightgreen?logo=vitepress" alt="Documentation"></a></td>
</tr>
</tbody>
</table>
<div style="display: flex; align-items: stretch; gap: 24px; margin-bottom: 24px;">
<img src="./img/logo.svg" width="90" height="90" alt="Tempo Logo">
<div style="display: flex; flex-direction: column; height: 90px;">
<div style="height: 45px; display: flex; align-items: flex-start;">
<h1 style="margin: 0; color: #3498db; border-bottom: none; padding: 0; line-height: 1;">Tempo</h1>
</div>
<div style="height: 45px; display: flex; align-items: center;">
<div style="font-weight: 600; font-size: 1.1rem; color: #2c3e50; line-height: 1.2;">The Professional Date-Time Library for the Temporal API</div>
</div>
</div>
</div>

## 🚀 Overview
Working with `Date` in JavaScript has historically been painful. The new `Temporal` standard (Stage 4) fixes this, but it can be verbose and strict when parsing strings.

**Tempo** bridges that gap by providing:
- **Flexible Parsing**: Interprets almost any date string, including relative ones like "next Friday".
- **Natural Language**: Supports word-based numbers (0-10) in relative parsing (e.g., "two days ago").
- **Fluent API**: Chainable methods for adding, subtracting, and setting date-times (similar to Moment.js).
- **Formatting**: Use custom tokens to format date-times in a way that is both intuitive and flexible.
- **Plugin**: Extend core functionality safely; all extensions (including the Ticker) are opted-into via side-effect imports or explicit registration, ensuring a lean footprint even in the full package.
- **Terms**: Access complex date ranges (Quarters, Seasons, Zodiacs) easily.
- **Immutable**: Operations (like `set` and `add`) return a new `Tempo` instance, ensuring thread safety and predictability.
## 🤔 Why Tempo?
If you're looking for a **modern date library** that leverages the native power of the browser's `Temporal` API, Tempo is for you.

- **Type Safety**: Built from the ground up with TypeScript.
- **Performance**: High-performance wrapper with minimal overhead.
- **Familiarity**: If you like the fluent syntax of **Moment** or **Day.js**, you'll feel right at home.
- **Future-Proof**: Built on the TC39 `Temporal` standard.
## 🎯 Target Audience

Tempo is built for **modern JavaScript developers** who require a premium, type-safe, and developer-friendly interface over the native Temporal API. It is ideal for those migrating from legacy libraries like **Moment.js**, **Day.js**, or **Luxon**, as well as teams building complex, time-sensitive applications that demand reliability, immutability, and high-performance parsing.
Tempo is designed for a broad spectrum of developers and teams who interact with date and time data in JavaScript:

### 1. Modern JavaScript Developers
For those who want to leverage the power of the native `Temporal` API today but find its raw implementation too verbose or strict for rapid development.

### 2. Teams Migrating from Legacy Libraries
Ideal for organizations looking to move away from **Moment.js**, **Day.js**, or **Luxon** without sacrificing the fluent, chainable API and flexible parsing on which they've come to rely.

### 3. Enterprise Application Architects
For those building complex, time-sensitive systems (such as financial platforms, scheduling engines, or global logistics trackers) that demand the precision of Temporal combined with a premium, type-safe developer experience.
## 📦 Installation

### 💻 on the Server (or Bundler)
For Node.js, Bun, Deno, or projects using a bundler (Vite, Webpack, etc.), install via npm:
**Tempo** is a premium, high-performance wrapper for the ECMAScript `Temporal` API. Designed for professionals, it combines **immutable** state-management with a **fluent**, natural-language engine. It is the modern, type-safe successor to legacy libraries like Moment.js and Luxon.

```bash
npm install @magmacomputing/tempo
```

Tempo is a native ESM package. In Node.js (20+), simply import the class.
Node.js, Bun, and Deno support native ESM out of the box.

<div style="display: flex; justify-content: center; flex-wrap: wrap; gap: 12px; margin: 32px 0;">
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
<a href="https://tc39.es/proposal-temporal/"><img src="https://img.shields.io/badge/Temporal-Stage%204-green" alt="Temporal"></a>
<a href="https://www.typescriptlang.org/"><img src="https://img.shields.io/badge/TypeScript-Ready-blue?logo=typescript" alt="TypeScript Ready"></a>
<a href="https://nodejs.org/api/esm.html"><img src="https://img.shields.io/badge/Native-ESM-green" alt="Native ESM"></a>
<a href="https://magmacomputing.github.io/magma/"><img src="https://img.shields.io/badge/Docs-VitePress-brightgreen?logo=vitepress" alt="Documentation"></a>
</div>



---

## ⚡ Quick Start
```javascript
import { Tempo } from '@magmacomputing/tempo';

const t = new Tempo('next Friday');
console.log(t.format('{dd} {mon} {yyyy}'));
// 🎯 Natural Language Parsing
const event = new Tempo('next Friday 3pm');

// 🔄 Fluent Mutations (Immutable)
const reminder = event.add({ hours: 2 }).set({ minute: 0 });

// ⏱️ High-Precision Tickers
Tempo.ticker({ seconds: 1 }, (t) => {
console.log(t.format('{hh}:{mi}:{ss} {tz}'));
});
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
```

---

## 📦 Installation

```bash
npm install @magmacomputing/tempo # npm
yarn add @magmacomputing/tempo # yarn
pnpm add @magmacomputing/tempo # pnpm
bun add @magmacomputing/tempo # bun
deno add npm:@magmacomputing/tempo # deno
```
Comment thread
coderabbitai[bot] marked this conversation as resolved.

### 🌐 in the Browser (Import Maps)
Since Tempo is a native ESM package, you can use it directly in modern browsers using `importmap`. The **bundle** entrypoint includes all standard modules pre-registered, but requires a separate `Temporal` polyfill for current browser environments.
<details>
<summary><b>🌐 Browser & Lite Environments</b></summary>

For modern browsers using **Import Maps**:
```html
<script type="importmap">
{
"imports": {
"jsbi": "https://cdn.jsdelivr.net/npm/jsbi@4.3.0/dist/jsbi.mjs",
"@js-temporal/polyfill": "https://cdn.jsdelivr.net/npm/@js-temporal/polyfill@0.5/dist/index.esm.js",
"@magmacomputing/tempo": "https://cdn.jsdelivr.net/npm/@magmacomputing/tempo@2/dist/tempo.bundle.esm.js"
}
}
</script>
<script type="module">
import '@js-temporal/polyfill';
import { Tempo } from '@magmacomputing/tempo';
const t = new Tempo('next friday');
console.log(t.format('{mon} {day}'));
</script>
```

### 📦 in the Browser (Script Tag)
For environments without `importmap` support or simple prototypes, use the global bundle. This automatically attaches the `Tempo` class to the `window` object.

For rapid prototyping without a package manager (UMD):
```html
<script src="https://cdn.jsdelivr.net/npm/@js-temporal/polyfill@0.5/dist/index.umd.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@magmacomputing/tempo@2/dist/tempo.bundle.js"></script>
<script>
const t = new Tempo('tomorrow');
console.log(t.toString());
</script>
```

### 🧪 Advanced: Granular ESM (Lite Build)
For maximum performance, you can use the lean **Core** engine and opt-in to specific modules. This prevents loading unused logic and keeps your production bundle minimal.
For granular "Lite" builds, see the [Full Installation Guide](https://magmacomputing.github.io/magma/doc/installation).
</details>

```html
<script type="importmap">
{
"imports": {
"jsbi": "https://cdn.jsdelivr.net/npm/jsbi@4.3.0/dist/jsbi.mjs",
"@js-temporal/polyfill": "https://cdn.jsdelivr.net/npm/@js-temporal/polyfill@0.5/dist/index.esm.js",
"@magmacomputing/tempo/core": "https://cdn.jsdelivr.net/npm/@magmacomputing/tempo@2/dist/core.index.js",
"@magmacomputing/tempo/mutate": "https://cdn.jsdelivr.net/npm/@magmacomputing/tempo@2/dist/plugin/module/module.mutate.js",
"@magmacomputing/library": "https://cdn.jsdelivr.net/npm/@magmacomputing/library@2/dist/common.index.js"
}
}
</script>
<script type="module">
import '@js-temporal/polyfill';
import { Tempo } from '@magmacomputing/tempo/core';
import { MutateModule } from '@magmacomputing/tempo/mutate';

// Opt-in to mutation logic
Tempo.extend(MutateModule);

const t = new Tempo().add({ days: 1 });
console.log(t.toString());
</script>
```
---

> [!TIP]
> **CDN Versioning**: The examples above use pinned versions (`@magmacomputing/tempo@2`, `@magmacomputing/library@2`, `@js-temporal/polyfill@0.5`) for production stability. To use the latest releases, you can omit the version string from every URL (e.g., remove `@2` from all Magma entries and `@0.5` from the polyfill). Ensure all `@magmacomputing/...` entries resolve to the same release to avoid mixed-version loading.
## ✨ Why Tempo?
* **🏗️ Future Standard**: Built natively on the TC39 `Temporal` proposal. Inherit the reliability of the future standard.
* **🗣️ Natural Language**: Resolve complex terms like `#friday.last` or "two days ago" with zero configuration.
* **🔄 Cycle Persistence**: Shift by semantic terms (Quarters, Seasons) while preserving your relative day-of-period offset.
* **⚡ Zero-Cost Parsing**: Lazy evaluation and smart matching ensure instantiation overhead is near-zero.
* **🛡️ Monorepo Resilient**: Built for stability in complex environments with proxy-protected registries.
* **📦 Tree-Shakable**: Keep your bundle light. Only load what you need—from Fiscal calendars to high-performance Tickers.

---

Expand All @@ -154,59 +97,23 @@ For a deeper dive into the API, architecture, and advanced features:
* **[Full API Reference Guide](https://magmacomputing.github.io/magma/doc/tempo.api)** — Detailed technical documentation for every class and method.

---
## 🛠️ Quick Start

```javascript
import { Tempo } from '@magmacomputing/tempo';

// Instantiate
const now = new Tempo();
const birthday = new Tempo('20-May-1990');
const nextWeek = new Tempo('next Monday');

// Manipulate
const later = now.add({ days: 3, hours: 2 });
const startOfMonth = now.set({ start: 'month' });

// Format
console.log(now.format('{dd} {mmm} {yyyy}')); // using custom format with tokens: "24 Jan 2026"
console.log(now.fmt.date); // using pre-built formats: "2026-01-24"
```


## 💬 Contact & Support

If you have a question, find a bug, or want to suggest a new feature:

1. **Bug Reports & Features**: Please open an [Issue](https://github.com/magmacomputing/magma/issues).
2. **Questions & Ideas**: Start a thread in [Discussions](https://github.com/magmacomputing/magma/discussions).
3. **Direct Contact**: You can reach me at `hello@magmacomputing.com.au`.

## 🛡️ Privacy & Transparency

We value your privacy. **Tempo** does not include any runtime telemetry or "phone-home" features.
Tempo will never make network requests from your application.
---

## 🗳️ Feedback & Reactions

How are we doing? Let us know with a simple reaction!
*(This will open a pre-filled GitHub Issue)*

[🚀 Premium!](https://github.com/magmacomputing/magma/issues/new?title=Feedback:%20🚀%20Premium!) &nbsp; | &nbsp;
[⭐ Loving it!](https://github.com/magmacomputing/magma/issues/new?title=Feedback:%20⭐%20Loving%20it!) &nbsp; | &nbsp;
[💡 Needs work](https://github.com/magmacomputing/magma/issues/new?title=Feedback:%20💡%20Needs%20work) &nbsp; | &nbsp;
[🐞 Found a bug](https://github.com/magmacomputing/magma/issues/new?title=Feedback:%20🐞%20Found%20a%20bug)

### ⚡ Quick Reactions
*(Native reactions available in [Discussions](https://github.com/magmacomputing/magma/discussions/categories/feedback))*

[👍 Like](https://github.com/magmacomputing/magma/discussions/categories/feedback) &nbsp; | &nbsp;
[❤️ Love](https://github.com/magmacomputing/magma/discussions/categories/feedback) &nbsp; | &nbsp;
[😄 Haha](https://github.com/magmacomputing/magma/discussions/categories/feedback) &nbsp; | &nbsp;
[😮 Wow](https://github.com/magmacomputing/magma/discussions/categories/feedback) &nbsp; | &nbsp;
[😢 Sad](https://github.com/magmacomputing/magma/discussions/categories/feedback) &nbsp; | &nbsp;
[😡 Angry](https://github.com/magmacomputing/magma/discussions/categories/feedback) &nbsp; | &nbsp;
[💩 Poop](https://github.com/magmacomputing/magma/discussions/categories/feedback)
---

## ⚖️ License

Expand Down
22 changes: 22 additions & 0 deletions packages/tempo/bin/parse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { parse } from '#tempo/parse';

// Pre-load core symbols (parse) to the global scope
Object.assign(globalThis, { parse });

console.log(`\n\x1b[38;2;252;194;1m\x1b[1m ⏳ Tempo (parse) \x1b[0m\x1b[38;2;45;212;191mREPL initialized (parse only).\x1b[0m\n`);

/**
* 💡 SMART IDLE: Auto-exit after 1 hour of keyboard inactivity
* Monitors 'stdin' so background Tickers won't keep the session alive if you walk away.
*/
let idleTimer: NodeJS.Timeout;
const resetIdle = () => {
clearTimeout(idleTimer);
idleTimer = setTimeout(() => {
console.warn('\n\x1b[33m[Tempo] REPL idle for 1 hour. Safety shutdown triggered.\x1b[0m');
process.exit(0);
}, 3600 * 1000);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
};

process.stdin.on('data', resetIdle);
resetIdle();
2 changes: 1 addition & 1 deletion packages/tempo/demo/3-modular-granular.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ <h1>⏳ Demo 3: Modular (Granular)</h1>
<script type="importmap">
{
"imports": {
"@magmacomputing/tempo/core": "../dist/tempo.class.js",
"@magmacomputing/tempo/core": "../dist/core.index.js",
"@magmacomputing/tempo/format": "../dist/plugin/module/module.format.js"
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/tempo/demo/4-error-no-plugin.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ <h1>⏳ Demo 4: Expected Error (No Plugin)</h1>
<script type="importmap">
{
"imports": {
"@magmacomputing/tempo/core": "../dist/tempo.class.js"
"@magmacomputing/tempo/core": "../dist/core.index.js"
}
}
</script>
Expand Down
Loading