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
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ 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.10.0] - 2026-07-19

### Added
- **Format Token Modifiers**: Introduced new capabilities for chained formatting modifiers.
- **Custom Format Tokens**: Completed the Custom Format Tokens implementation, allowing developers to build custom zero-overhead logic evaluators (like native Intl bridges).

### Changed
- **Documentation Architecture**: Architectural deep-dives (Localized Parsing, Slick Mutations, Custom Tokens) have been extracted from the Cookbook into specialized Core Concepts guides (e.g. `tempo.parse.md`, `tempo.mutate.md`, `tempo.format.md`) to provide a punchier onboarding experience.

## [3.9.0] - 2026-07-14

### Added
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "tempo-monorepo",
"version": "3.9.3",
"version": "3.10.0",
"private": true,
"engines": {
"node": ">=20.0.0"
Expand All @@ -22,6 +22,7 @@
"clean": "node node_modules/typescript-7/bin/tsc -b --clean",
"version:bump": "node bin/version-bump.mjs",
"version:sync": "node bin/version-sync.mjs",
"catalog:sync": "node packages/plugins/.bin/catalog-sync.mjs",
"repl": "npm run repl --workspace=@magmacomputing/tempo",
"repl:plugins": "tsx --import ./packages/plugins/.bin/temporal-polyfill.mts ./packages/plugins/.bin/repl.mts",
"repl:dist": "npm run repl:dist --workspace=@magmacomputing/tempo",
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": "3.9.3",
"version": "3.10.0",
"description": "Shared utility library for Tempo",
"author": "Magma Computing Solutions",
"license": "MIT",
Expand Down
1 change: 1 addition & 0 deletions packages/plugins/.bin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ This directory (`packages/plugins/bin/`) contains internal support scripts and u
It includes:
- **REPL Environment (`repl.mts`)**: Scripts to initialize an interactive Node.js REPL session with Tempo and Temporal pre-loaded, making it easy to experiment with plugins from the CLI.
- **Polyfill Setup (`temporal-polyfill.mts`)**: Initialization scripts to ensure the `@js-temporal/polyfill` is correctly loaded into the global scope during testing or REPL sessions, allowing plugins to work with native `Temporal` APIs before they are officially adopted by all runtimes.
- **Catalog Synchronization (`catalog-sync.mjs`)**: A developer utility that scans all local and external plugin `package.json` files and extracts their metadata into a centralized `catalog.json` file. Run via `npm run catalog:sync`.
- **TypeScript Configuration (`tsconfig.json`)**: Specific compiler options for running these support scripts directly via tools like `tsx`.

These files are meant for local monorepo development and testing purposes only. They are not published or distributed with any NPM packages.
81 changes: 81 additions & 0 deletions packages/plugins/.bin/catalog-sync.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const catalogPath = path.resolve(__dirname, '../.setup/catalog.json');
const pluginsDir = path.resolve(__dirname, '../../plugins');
const nodeModulesDir = path.resolve(__dirname, '../../../node_modules/@magmacomputing');

let catalog = [];
if (fs.existsSync(catalogPath)) {
try {
catalog = JSON.parse(fs.readFileSync(catalogPath, 'utf8'));
} catch (e) {
console.error('Failed to parse catalog.json, starting fresh.', e);
}
}

const catalogMap = new Map();
catalog.forEach(p => catalogMap.set(p.id, p));

function processPlugin(pluginDir, isExternal) {
const pkgPath = path.join(pluginDir, 'package.json');
if (!fs.existsSync(pkgPath)) return;

const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));

// Extract id from directory name, replacing leading dots for VitePress safety
let id = path.basename(pluginDir).replace(/^\./, '_');
if (isExternal) {
id = id.replace('tempo-plugin-', '');
}

// Use the human-readable displayName if we have one, otherwise create a titleized version of the ID
const humanName = id.charAt(0).toUpperCase() + id.slice(1) + ' Plugin';

const entry = catalogMap.get(id) || { id };

// Update fields from package.json
entry.name = entry.name || humanName; // allow manual override of human name
entry.description = pkg.description || entry.description || '';
entry.packageName = pkg.name;
entry.plan = pkg.tempo?.plan || entry.plan || 'community';
entry.status = entry.status || 'active';
// Note: we do NOT store version here, as build-catalog.mjs injects it dynamically!

catalogMap.set(id, entry);
console.log(`Synced plugin metadata for: ${id} (${isExternal ? 'external' : 'local'})`);
}

// 1. Process Local Plugins
if (fs.existsSync(pluginsDir)) {
const plugins = fs.readdirSync(pluginsDir);
for (const plugin of plugins) {
// Skip hidden files/dirs like .setup or .bin unless we want to document them
// But we know .setup isn't a plugin, so let's skip it if it doesn't have a package.json
const fullPath = path.join(pluginsDir, plugin);
if (fs.statSync(fullPath).isDirectory()) {
processPlugin(fullPath, false);
}
}
}

// 2. Process Node Modules Plugins
if (fs.existsSync(nodeModulesDir)) {
const modules = fs.readdirSync(nodeModulesDir);
for (const mod of modules) {
if (mod.startsWith('tempo-plugin-')) {
const fullPath = path.join(nodeModulesDir, mod);
if (fs.statSync(fullPath).isDirectory()) {
processPlugin(fullPath, true);
}
}
}
}

// Write back to catalog.json
fs.writeFileSync(catalogPath, JSON.stringify(Array.from(catalogMap.values()), null, 2) + '\n');
console.log(`\nSuccessfully updated ${catalogPath}`);
50 changes: 50 additions & 0 deletions packages/plugins/.setup/catalog.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
[
{
"id": "astro",
"name": "Astro Plugin",
"description": "Tempo plugin that calculates precise astronomical seasons (solstices & equinoxes) using the Jean Meeus algorithm — hemisphere-aware, sub-minute accuracy",
"packageName": "@magmacomputing/tempo-plugin-astro",
"plan": "community",
"status": "active"
},
{
"id": "batch",
"name": "Batch Plugin",
"description": "Tempo community plugin bringing C-level parallelization to massive date arrays via SharedArrayBuffer and Worker Threads.",
"packageName": "@magmacomputing/tempo-plugin-batch",
"plan": "community",
"status": "active"
},
{
"id": "finance",
"name": "Finance Plugin",
"description": "Tempo Community Plugin: Finance namespace and fiscal year utilities",
"packageName": "@magmacomputing/tempo-plugin-finance",
"plan": "community",
"status": "active"
},
{
"id": "snap",
"name": "Snap Plugin",
"description": "Snap time to blocks",
"packageName": "@magmacomputing/tempo-plugin-snap",
"plan": "community",
"status": "active"
},
{
"id": "sync",
"name": "Sync Plugin",
"description": "Tempo community plugin providing lock-free, highly precise cross-thread synchronization via SharedArrayBuffer and Atomics.",
"packageName": "@magmacomputing/tempo-plugin-sync",
"plan": "community",
"status": "active"
},
{
"id": "ticker",
"name": "Ticker Plugin",
"description": "Tempo plugin that provides a high-performance continuous execution loop (Ticker) based on temporal mathematics.",
"packageName": "@magmacomputing/tempo-plugin-ticker",
"plan": "pro",
"status": "active"
}
]
27 changes: 27 additions & 0 deletions packages/plugins/.setup/community-plugin-template.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,33 @@ Ensure the plugin's `package.json` contains the correct community configuration:

- **Version**: Set to a fresh semantic version (e.g., `"1.0.0"` for the first release).
- **License**: Must strictly be `"MIT"`.
- **Type**: Set `"type": "module"`.
- **Files**: Include the published files array:
```json
"files": [
"dist",
"src",
"README.md",
"CHANGELOG.md",
"LICENSE"
]
```
- **PublishConfig**: Configure public npm publishing:
```json
"publishConfig": {
"registry": "https://registry.npmjs.org/",
"access": "public"
}
```
- **Exports**: Define exports with types and import entrypoints:
```json
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
}
```
- **Scripts**:
- Ensure `"build": "tsup && tsc"` and `"postbuild": "rm -rf dist/src"` are present.
- Include the prepublish safeguard: `"prepublishOnly": "if [ $(git rev-parse --abbrev-ref HEAD) != main ]; then echo 'ERROR: Must be on main branch to publish.'; exit 1; fi && npm run build"`.
Expand Down
4 changes: 2 additions & 2 deletions packages/plugins/finance/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ npm install @magmacomputing/tempo-plugin-finance

```typescript
import { Tempo } from '@magmacomputing/tempo';
import { FinancePlugin } from '@magmacomputing/tempo-plugin-finance';
import { FinanceNamespace } from '@magmacomputing/tempo-plugin-finance';

// Register the namespace
Tempo.extend(FinancePlugin);
Tempo.extend(FinanceNamespace);

const t = new Tempo('2024-07-01');

Expand Down
4 changes: 2 additions & 2 deletions packages/plugins/finance/doc/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ npm install @magmacomputing/tempo-plugin-finance

```typescript
import { Tempo } from '@magmacomputing/tempo';
import { FinancePlugin } from '@magmacomputing/tempo-plugin-finance';
import { FinanceNamespace } from '@magmacomputing/tempo-plugin-finance';

// Register the namespace
Tempo.extend(FinancePlugin);
Tempo.extend(FinanceNamespace);

const t = new Tempo('2024-07-01');

Expand Down
2 changes: 1 addition & 1 deletion packages/plugins/finance/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const isFiscalYearStart = (tempo: Tempo) => () => tempo.mm === 1 && tempo
// Wrap the functions in a Namespace Plugin so they can be injected directly
// onto the Tempo instance (e.g., `t.finance.taxYear`) for a fluent experience.
// -----------------------------------------------------------------------------
export const FinancePlugin: TempoPlugin = defineNamespace({
export const FinanceNamespace: TempoPlugin = defineNamespace({
name: 'finance',
resolvers: {
fiscalQuarter,
Expand Down
4 changes: 2 additions & 2 deletions packages/plugins/finance/test/finance.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Tempo } from '@magmacomputing/tempo';
import { FinancePlugin } from '../src/index.js';
import { FinanceNamespace } from '../src/index.js';

describe('Finance Namespace', () => {
it('should lazy load the finance properties', () => {
Tempo.extend(FinancePlugin);
Tempo.extend(FinanceNamespace);

const t1 = new Tempo('2024-02-15');
expect(t1.finance.fiscalQuarter).toBe(1);
Expand Down
2 changes: 1 addition & 1 deletion packages/plugins/snap/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<a href="https://www.npmjs.com/package/@magmacomputing/tempo-plugin-snap"><img src="https://img.shields.io/npm/v/@magmacomputing/tempo-plugin-snap?style=flat-square" alt="npm version" style="display: inline-block; margin: 0 4px;"></a> <a href="https://www.npmjs.com/package/@magmacomputing/tempo"><img src="https://img.shields.io/npm/dependency-version/@magmacomputing/tempo-plugin-snap/peer/@magmacomputing/tempo?style=flat-square" alt="npm peer dependency version" style="display: inline-block; margin: 0 4px;"></a> <a href="https://www.npmjs.com/package/@magmacomputing/tempo-plugin-snap"><img src="https://img.shields.io/npm/l/@magmacomputing/tempo-plugin-snap?style=flat-square" alt="License" style="display: inline-block; margin: 0 4px;"></a> <a href="https://www.typescriptlang.org/"><img src="https://img.shields.io/badge/TypeScript-Ready-blue?logo=typescript&style=flat-square" alt="TypeScript Ready" style="display: inline-block; margin: 0 4px;"></a> <a href="https://magmacomputing.github.io/magma/doc/9-plugins/snap.index.html"><img src="https://img.shields.io/badge/Docs-VitePress-brightgreen?logo=vitepress&style=flat-square" alt="Documentation" style="display: inline-block; margin: 0 4px;"></a>
</p>

A Community plugin for the [Tempo](https://github.com/magmacomputing/magma) library that provides robust time rounding and snapping functionality (e.g. snapping to the nearest 15 minutes or 1 hour block) for calendar and scheduling applications.
A Community plugin for the [Tempo](https://github.com/magmacomputing/magma) library that provides robust time rounding and snapping functionality (e.g. snapping to the nearest 15-minute or 1-hour block) for calendar and scheduling applications.

👉 **[View the full documentation on our GitHub Pages](https://magmacomputing.github.io/magma/doc/9-plugins/snap.index.html)**

Expand Down
Loading
Loading