diff --git a/CHANGELOG.md b/CHANGELOG.md index a32dbe01..3ba36c13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/package.json b/package.json index 2d91086e..955787b2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tempo-monorepo", - "version": "3.9.3", + "version": "3.10.0", "private": true, "engines": { "node": ">=20.0.0" @@ -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", diff --git a/packages/library/package.json b/packages/library/package.json index 7a441e35..5cd0e264 100644 --- a/packages/library/package.json +++ b/packages/library/package.json @@ -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", diff --git a/packages/plugins/.bin/README.md b/packages/plugins/.bin/README.md index cf6ac9df..cd815ea0 100644 --- a/packages/plugins/.bin/README.md +++ b/packages/plugins/.bin/README.md @@ -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. diff --git a/packages/plugins/.bin/catalog-sync.mjs b/packages/plugins/.bin/catalog-sync.mjs new file mode 100644 index 00000000..1d624c70 --- /dev/null +++ b/packages/plugins/.bin/catalog-sync.mjs @@ -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}`); diff --git a/packages/plugins/.setup/catalog.json b/packages/plugins/.setup/catalog.json new file mode 100644 index 00000000..3b4938f7 --- /dev/null +++ b/packages/plugins/.setup/catalog.json @@ -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" + } +] diff --git a/packages/plugins/.setup/community-plugin-template.md b/packages/plugins/.setup/community-plugin-template.md index 88659ffb..863e26fe 100644 --- a/packages/plugins/.setup/community-plugin-template.md +++ b/packages/plugins/.setup/community-plugin-template.md @@ -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"`. diff --git a/packages/plugins/finance/README.md b/packages/plugins/finance/README.md index 5a236ed0..e084c42f 100644 --- a/packages/plugins/finance/README.md +++ b/packages/plugins/finance/README.md @@ -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'); diff --git a/packages/plugins/finance/doc/index.md b/packages/plugins/finance/doc/index.md index 067a7f14..c1c7f285 100644 --- a/packages/plugins/finance/doc/index.md +++ b/packages/plugins/finance/doc/index.md @@ -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'); diff --git a/packages/plugins/finance/src/index.ts b/packages/plugins/finance/src/index.ts index 59906f5d..93da7411 100644 --- a/packages/plugins/finance/src/index.ts +++ b/packages/plugins/finance/src/index.ts @@ -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, diff --git a/packages/plugins/finance/test/finance.test.ts b/packages/plugins/finance/test/finance.test.ts index 7a4686db..0a2f51af 100644 --- a/packages/plugins/finance/test/finance.test.ts +++ b/packages/plugins/finance/test/finance.test.ts @@ -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); diff --git a/packages/plugins/snap/README.md b/packages/plugins/snap/README.md index f21653a8..52d6d0aa 100644 --- a/packages/plugins/snap/README.md +++ b/packages/plugins/snap/README.md @@ -6,7 +6,7 @@ npm version npm peer dependency version License TypeScript Ready Documentation

-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)** diff --git a/packages/tempo/.vitepress/theme/components/CatalogList.vue b/packages/tempo/.vitepress/theme/components/CatalogList.vue index 5f519a1e..0016f77c 100644 --- a/packages/tempo/.vitepress/theme/components/CatalogList.vue +++ b/packages/tempo/.vitepress/theme/components/CatalogList.vue @@ -1,5 +1,5 @@