From 3020c2c8bc582c474e124daa819c3d28247d7f65 Mon Sep 17 00:00:00 2001
From: Michael McRae
Date: Sun, 19 Jul 2026 11:01:56 +1000
Subject: [PATCH 1/2] Format modifiers, Custom Format tokens
---
CHANGELOG.md | 9 ++
package.json | 3 +-
packages/library/package.json | 2 +-
packages/plugins/.bin/README.md | 1 +
packages/plugins/.bin/catalog-sync.mjs | 81 ++++++++++++
packages/plugins/.setup/catalog.json | 50 ++++++++
.../.setup/community-plugin-template.md | 27 ++++
packages/plugins/finance/README.md | 4 +-
packages/plugins/finance/doc/index.md | 4 +-
packages/plugins/finance/src/index.ts | 2 +-
packages/plugins/finance/test/finance.test.ts | 4 +-
packages/plugins/snap/README.md | 2 +-
.../theme/components/CatalogList.vue | 96 +++++++++------
.../tempo/.vitepress/theme/data/catalog.json | 56 +++++++++
packages/tempo/CHANGELOG.md | 2 +-
packages/tempo/bin/build-catalog.mjs | 55 +++++++++
.../doc/1-getting-started/installation.md | 8 +-
.../doc/1-getting-started/tempo.cookbook.md | 116 +++---------------
.../tempo/doc/2-core-concepts/tempo.config.md | 8 +-
.../tempo/doc/2-core-concepts/tempo.format.md | 46 ++++++-
.../tempo/doc/2-core-concepts/tempo.mutate.md | 34 +++++
.../tempo/doc/2-core-concepts/tempo.parse.md | 20 +++
.../doc/3-extending-tempo/tempo.namespace.md | 12 +-
.../doc/3-extending-tempo/tempo.plugin.md | 16 ++-
.../doc/8-project-and-support/commercial.md | 2 +-
.../8-project-and-support/migration-guide.md | 6 +-
.../8-project-and-support/releases/v3.x.md | 25 ++--
packages/tempo/package.json | 6 +-
packages/tempo/src/module/module.format.ts | 32 +++++
packages/tempo/src/support/support.init.ts | 4 +
packages/tempo/src/tempo.version.ts | 2 +-
packages/tempo/test/discrete/format.test.ts | 35 ++++++
32 files changed, 582 insertions(+), 188 deletions(-)
create mode 100644 packages/plugins/.bin/catalog-sync.mjs
create mode 100644 packages/plugins/.setup/catalog.json
create mode 100644 packages/tempo/.vitepress/theme/data/catalog.json
create mode 100644 packages/tempo/bin/build-catalog.mjs
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 @@
-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 @@
-
Loading catalog...
-
-
+
Community Plugins
These plugins are free, open-source extensions that do not require a license token.
@@ -238,4 +225,35 @@ const copyInstall = (pkgName: string) => {
font-size: 1.2rem;
color: var(--vp-c-text-2);
}
+.copy-tooltip {
+ position: absolute;
+ bottom: calc(100% + 4px);
+ left: 50%;
+ transform: translateX(-50%);
+ background-color: var(--vp-c-text-1);
+ color: var(--vp-c-bg);
+ padding: 4px 8px;
+ border-radius: 4px;
+ font-size: 0.75rem;
+ font-weight: bold;
+ white-space: nowrap;
+ pointer-events: none;
+ animation: tooltipFadeIn 0.15s ease-out;
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
+ z-index: 10;
+}
+.copy-tooltip::after {
+ content: '';
+ position: absolute;
+ top: 100%;
+ left: 50%;
+ transform: translateX(-50%);
+ border-width: 4px;
+ border-style: solid;
+ border-color: var(--vp-c-text-1) transparent transparent transparent;
+}
+@keyframes tooltipFadeIn {
+ from { opacity: 0; transform: translateX(-50%) translateY(4px); }
+ to { opacity: 1; transform: translateX(-50%) translateY(0); }
+}
diff --git a/packages/tempo/.vitepress/theme/data/catalog.json b/packages/tempo/.vitepress/theme/data/catalog.json
new file mode 100644
index 00000000..b52b08a1
--- /dev/null
+++ b/packages/tempo/.vitepress/theme/data/catalog.json
@@ -0,0 +1,56 @@
+[
+ {
+ "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",
+ "version": "2.1.3"
+ },
+ {
+ "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",
+ "version": "1.0.1"
+ },
+ {
+ "id": "finance",
+ "name": "Finance Plugin",
+ "description": "Tempo Community Plugin: Finance namespace and fiscal year utilities",
+ "packageName": "@magmacomputing/tempo-plugin-finance",
+ "plan": "community",
+ "status": "active",
+ "version": "1.0.1"
+ },
+ {
+ "id": "snap",
+ "name": "Snap Plugin",
+ "description": "Snap time to blocks",
+ "packageName": "@magmacomputing/tempo-plugin-snap",
+ "plan": "community",
+ "status": "active",
+ "version": "1.3.2"
+ },
+ {
+ "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",
+ "version": "1.0.2"
+ },
+ {
+ "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",
+ "version": "2.2.3"
+ }
+]
diff --git a/packages/tempo/CHANGELOG.md b/packages/tempo/CHANGELOG.md
index 58a142d0..4a2fa792 100644
--- a/packages/tempo/CHANGELOG.md
+++ b/packages/tempo/CHANGELOG.md
@@ -40,7 +40,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **Auto-Meridiem Spacing**: Implemented the `:space` modifier for the `{h12}` formatting token (e.g., `{h12:space:dots}`). This enables typographically correct spacing before automatically injected meridiems (e.g., `"10:30 a.m."`).
### Changed
-- **Documentation Alignment**: Cleaned up `tempo.config.md` to remove deprecated module references, perfectly aligning examples with the `tempo-workspace` ecosystem (`FinancePlugin` and `AstroTerm`).
+- **Documentation Alignment**: Cleaned up `tempo.config.md` to remove deprecated module references, perfectly aligning examples with the `tempo-workspace` ecosystem (`FinanceNamespace` and `AstroTerm`).
- **Getter Documentation**: Created `tempo.getters.md` as the definitive, educational conceptual guide for utilizing Tempo's zero-cost evaluation getters.
### Fixed
diff --git a/packages/tempo/bin/build-catalog.mjs b/packages/tempo/bin/build-catalog.mjs
new file mode 100644
index 00000000..2c48c601
--- /dev/null
+++ b/packages/tempo/bin/build-catalog.mjs
@@ -0,0 +1,55 @@
+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 sourceCatalogPath = path.resolve(__dirname, '../../../packages/plugins/.setup/catalog.json');
+const targetDir = path.resolve(__dirname, '../.vitepress/theme/data');
+const targetCatalogPath = path.join(targetDir, 'catalog.json');
+
+const pluginsDir = path.resolve(__dirname, '../../../packages/plugins');
+const nodeModulesDir = path.resolve(__dirname, '../../../node_modules');
+
+if (!fs.existsSync(sourceCatalogPath)) {
+ console.error('build-catalog: source catalog.json not found!');
+ process.exit(1);
+}
+
+const catalog = JSON.parse(fs.readFileSync(sourceCatalogPath, 'utf8'));
+
+for (const entry of catalog) {
+ let version = '';
+
+ // 1. Try resolving from local monorepo source (Community Plugins)
+ // Note: Local plugins use the original un-mangled folder name, but our entry.id has leading dots replaced with underscores.
+ // We can just check the dir directly if we kept the original name, but let's just search the dirs.
+ // Actually, wait, community plugins match `entry.id`.
+ const localPkgPath = path.join(pluginsDir, entry.id, 'package.json');
+
+ if (fs.existsSync(localPkgPath)) {
+ const pkg = JSON.parse(fs.readFileSync(localPkgPath, 'utf8'));
+ version = pkg.version;
+ } else {
+ // 2. Try resolving from node_modules (Premium Plugins installed by Dependabot)
+ const externalPkgPath = path.join(nodeModulesDir, entry.packageName, 'package.json');
+ if (fs.existsSync(externalPkgPath)) {
+ const pkg = JSON.parse(fs.readFileSync(externalPkgPath, 'utf8'));
+ version = pkg.version;
+ }
+ }
+
+ if (!version) {
+ console.warn(`build-catalog: Could not resolve installed version for plugin ${entry.id}`);
+ }
+
+ entry.version = version;
+}
+
+if (!fs.existsSync(targetDir)) {
+ fs.mkdirSync(targetDir, { recursive: true });
+}
+
+fs.writeFileSync(targetCatalogPath, JSON.stringify(catalog, null, 2) + '\n');
+console.log(`Successfully generated VitePress catalog JSON with live local versions (${catalog.length} plugins).`);
diff --git a/packages/tempo/doc/1-getting-started/installation.md b/packages/tempo/doc/1-getting-started/installation.md
index 4740415e..677af8e4 100644
--- a/packages/tempo/doc/1-getting-started/installation.md
+++ b/packages/tempo/doc/1-getting-started/installation.md
@@ -119,7 +119,7 @@ The easiest way to use Tempo natively in the browser is via the pre-optimized ES
### 2. Smart CDNs (The "Best-of-Both-Worlds")
-If you want the absolute easiest setup for **Tempo Premium Plugins** natively in the browser, use an on-the-fly bundling CDN like [esm.sh](https://esm.sh). Smart CDNs act like a Node environment—they read the package resolution rules and resolve nested dependencies automatically, meaning you don't have to map any internal subpaths.
+If you want the absolute easiest setup for **Tempo Plugins** natively in the browser, use an on-the-fly bundling CDN like [esm.sh](https://esm.sh). Smart CDNs act like a Node environment—they read the package resolution rules and resolve nested dependencies automatically, meaning you don't have to map any internal subpaths.
While you *could* import directly from the URL everywhere, the best practice is to use a tiny import map for your top-level packages to keep your application code clean:
@@ -141,9 +141,9 @@ While you *could* import directly from the URL everywhere, the best practice is
// esm.sh handles all the complex internal plugin routing behind the scenes!
import '@js-temporal/polyfill';
import { Tempo } from '@magmacomputing/tempo';
- import { TickerModule } from '@magmacomputing/tempo-plugin-ticker';
+ import { TickerPlugin } from '@magmacomputing/tempo-plugin-ticker';
- Tempo.extend(TickerModule);
+ Tempo.extend(TickerPlugin);
```
@@ -164,7 +164,7 @@ While `esm.sh` is fantastic for prototyping and reducing import map complexity,
For production environments where uptime and load speeds are critical, you should use a static file CDN (like jsdelivr). Because static CDNs serve raw files without compiling them on the fly, they are significantly faster and more reliable than Smart CDNs.
-To use **Tempo Premium Plugins** via a static CDN, you simply need to explicitly map the unified `plugin-api` subpath so the browser knows how to resolve the internal connections:
+To use **Tempo Plugins** via a static CDN, you simply need to explicitly map the unified `plugin-api` subpath so the browser knows how to resolve the internal connections:
```html
```
diff --git a/packages/tempo/doc/3-extending-tempo/tempo.plugin.md b/packages/tempo/doc/3-extending-tempo/tempo.plugin.md
index 5be128c0..89d7fcfa 100644
--- a/packages/tempo/doc/3-extending-tempo/tempo.plugin.md
+++ b/packages/tempo/doc/3-extending-tempo/tempo.plugin.md
@@ -8,13 +8,13 @@ In the Tempo ecosystem, a **Plugin** is the universal overarching term for any f
2. **`defineTerm`**: A specialized factory exclusively for defining temporal vocabulary constraints (a "Term" is technically just a highly-opinionated "Plugin" focused on date ranges and schedules).
3. **`defineNamespace`**: A factory for creating lazily-evaluated property landing pads (e.g., `Tempo().finance.taxYear`).
-### Naming Convention Standard
+## Naming Convention Standard
To provide a consistent and intuitive developer experience, the exported symbol of your plugin should use a suffix that directly matches the factory used to construct it. This makes it instantly obvious to consumers how the extension will attach to the Tempo core:
- Built with `definePlugin` ➡️ **`[Name]Plugin`** (e.g., `TickerPlugin`)
- Built with `defineTerm` ➡️ **`[Name]Term`** (e.g., `AstroTerm`)
- Built with `defineNamespace` ➡️ **`[Name]Namespace`** (e.g., `FinanceNamespace`)
-*(Note: The `Module` suffix is strictly reserved for Tempo's core internal injection APIs (like `ParseModule`) and should not be used by external plugins.)*
+*(Note: The `Module` suffix and `defineModule` factory are strictly reserved for Tempo's core internal injection APIs like `ParseModule` and should not be used by external plugins.)*
To manually register a plugin, use the static `extend` method. This is typically used for "opt-in" features or when you need to provide specific configuration to a plugin factory.
@@ -166,10 +166,10 @@ If your plugin requires its own configuration, export a **factory function** tha
```typescript
// tempo-plugin-holiday/index.ts
-import { defineModule } from '@magmacomputing/tempo/plugin-api';
+import { definePlugin } from '@magmacomputing/tempo/plugin-api';
export const HolidayPlugin = (pluginOptions = {}) => {
- return defineModule((TempoClass, tempoOptions, factory) => {
+ return definePlugin((TempoClass, tempoOptions, factory) => {
// ... use pluginOptions here ...
});
};
@@ -180,11 +180,11 @@ If your plugin provides multiple related components, wrap them in an aggregator
```typescript
// index.ts
-import { defineModule } from '@magmacomputing/tempo/plugin-api';
+import { definePlugin } from '@magmacomputing/tempo/plugin-api';
import { PluginA } from './plugin.a.js';
import { PluginB } from './plugin.b.js';
-export const MyFeaturePlugin = defineModule((TempoClass, options) => {
+export const MyFeaturePlugin = definePlugin((TempoClass, options) => {
TempoClass.extend([PluginA, PluginB]);
});
```
diff --git a/packages/tempo/doc/8-project-and-support/releases/v3.x.md b/packages/tempo/doc/8-project-and-support/releases/v3.x.md
index 845ae944..a5727034 100644
--- a/packages/tempo/doc/8-project-and-support/releases/v3.x.md
+++ b/packages/tempo/doc/8-project-and-support/releases/v3.x.md
@@ -335,6 +335,9 @@ If you are upgrading from v2.x and your application relies on `Tempo.ticker()`,
import { Tempo } from '@magmacomputing/tempo';
import { TickerPlugin } from '@magmacomputing/tempo-plugin-ticker';
+ // Wire your license key
+ Tempo.init({ license: 'YOUR_JWT_KEY' });
+
// Register the extracted plugin
Tempo.extend(TickerPlugin);
```