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
30 changes: 25 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "tempo-monorepo",
"version": "2.2.1",
"version": "2.2.2",
"private": true,
"description": "Magma Computing Monorepo",
"repository": {
Expand All @@ -21,10 +21,12 @@
"core": "npm run core --workspace=@magmacomputing/tempo",
"docs:dev": "npm run docs:dev --workspace=@magmacomputing/tempo",
"docs:build": "npm run docs:build --workspace=@magmacomputing/tempo",
"docs:preview": "npm run docs:preview --workspace=@magmacomputing/tempo"
"docs:preview": "npm run docs:preview --workspace=@magmacomputing/tempo",
"test:dist": "npm run build:library && npm run build:tempo && cross-env TEST_DIST=true vitest run"
},
"devDependencies": {
"@js-temporal/polyfill": "^0.5.1",
"cross-env": "^7.0.3",
"@release-it/keep-a-changelog": "^5.0.0",
"@rollup/plugin-node-resolve": "^16.0.3",
"@types/google.maps": "^3.58.1",
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": "2.2.1",
"version": "2.2.2",
"description": "Shared utility library for Tempo",
"author": "Magma Computing Solutions",
"license": "MIT",
Expand Down
4 changes: 2 additions & 2 deletions packages/library/src/common/pledge.class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ declare module '#library/type.library.js' {
* Wrap a Promise's resolve/reject/finally methods for later fulfilment.
* with useful methods for tracking the state of the Promise, chaining fulfilment, etc.
```
new Pledge<T>({tag: string, onResolve?: () => void, onReject?: () => void, onSettle?: () => void})
new Pledge<T>(tag?: string)
new Pledge<T>({tag: string, onResolve?: () => void, onReject?: () => void, onSettle?: () => void})
new Pledge<T>(tag?: string)
```
*/
@Immutable
Expand Down
31 changes: 27 additions & 4 deletions packages/library/src/common/reflection.library.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,34 @@ export const setAccessors = (obj: any = {}) => {
}

const ownAccessors = (obj: any = {}, type: 'get' | 'set') => {
const accessors = Object.getOwnPropertyDescriptors(obj.prototype || Object.getPrototypeOf(obj));
const keys: PropertyKey[] = [];
const limit = 50;
let depth = 0;

// 1. Walk the Instance Prototype chain (for instance accessors)
let proto = obj.prototype || Object.getPrototypeOf(obj);
while (proto && proto !== Object.prototype && ++depth < limit) {
const descriptors = Object.getOwnPropertyDescriptors(proto);
Reflect.ownKeys(descriptors).forEach(key => {
if (isFunction((descriptors as any)[key][type]))
keys.push(key);
});
proto = Object.getPrototypeOf(proto);
}

// 2. Walk the Constructor chain (for static accessors)
let constructor = isFunction(obj) ? obj : (obj as any).constructor;
depth = 0;
while (constructor && constructor !== Function.prototype && constructor !== Object.prototype && ++depth < limit) {
const descriptors = Object.getOwnPropertyDescriptors(constructor);
Reflect.ownKeys(descriptors).forEach(key => {
if (isFunction((descriptors as any)[key][type]))
keys.push(key);
});
constructor = Object.getPrototypeOf(constructor);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

return ownEntries(accessors)
.filter(([_, descriptor]) => isFunction(descriptor[type]))
.map(([key, _]) => key)
return distinct(keys as string[]);
}

/**
Expand Down
4 changes: 2 additions & 2 deletions packages/library/src/common/serialize.library.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { curry } from '#library/function.library.js';
import { ownKeys, ownValues, ownEntries } from '#library/primitive.library.js';

import { isType, asType, isEmpty, isDefined, isUndefined, isNullish, isString, isObject, isArray, isFunction, isSymbolFor, isSymbol } from '#library/type.library.js';
import sym from '#library/symbol.library.js';
import type { Obj, Type } from '#library/type.library.js';


export const Registry = new Map<string, Function>();
export const Registry = (globalThis as any)[sym.$SerializerRegistry] ??= new Map<string, Function>();

/** register a Class for serialization */
export const registerSerializable = (name: string, cls: Function) => {
Expand Down
2 changes: 2 additions & 0 deletions packages/library/src/common/symbol.library.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ const sym = {
$Registry: Symbol.for('$LibraryRegistry'),
/** key to identify the global registration hook */
$Register: Symbol.for('$LibraryRegister'),
/** key to identify the global serialization registry */
$SerializerRegistry: Symbol.for('$LibrarySerializerRegistry'),
} as const;

/** identify and mark a Logify configuration object */
Expand Down
13 changes: 11 additions & 2 deletions packages/library/vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,25 @@ import { dirname, resolve } from 'node:path';
import { defineConfig } from 'vitest/config';

const __dirname = dirname(fileURLToPath(import.meta.url));
const isDist = process.env.TEST_DIST === 'true';

export default defineConfig({
test: {
name: 'Library: Full',
globals: true,
environment: 'node',
include: ['test/**/*.{test,spec}.ts'],
setupFiles: [resolve(__dirname, '../tempo/bin/setup.polyfill.ts')],
},
resolve: {
alias: [
alias: isDist ? [
{ find: /^#library\/(.*)\.js$/, replacement: resolve(__dirname, './dist/common/$1.js') },
{ find: /^#library$/, replacement: resolve(__dirname, './dist/common.index.js') },
{ find: /^#browser\/(.*)\.js$/, replacement: resolve(__dirname, './dist/browser/$1.js') },
{ find: /^#server\/(.*)\.js$/, replacement: resolve(__dirname, './dist/server/$1.js') },
] : [
{ find: /^#library\/(.*)\.js$/, replacement: resolve(__dirname, './src/common/$1.ts') },
{ find: /^#library$/, replacement: resolve(__dirname, './src/common/index.ts') },
{ find: /^#library$/, replacement: resolve(__dirname, './src/common.index.ts') },
{ find: /^#browser\/(.*)\.js$/, replacement: resolve(__dirname, './src/browser/$1.ts') },
{ find: /^#server\/(.*)\.js$/, replacement: resolve(__dirname, './src/server/$1.ts') },
{ find: /^#server\/(.*)$/, replacement: resolve(__dirname, './src/server/$1.ts') },
Expand Down
13 changes: 9 additions & 4 deletions packages/tempo/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,21 +67,26 @@ export default defineConfig({
},
vite: {
build: {
target: 'es2022'
},
esbuild: {
target: 'esnext'
},
resolve: {
// Include 'development' so workspace packages resolve from TypeScript source
// (no pre-built dist required when running docs:dev or docs:build).
conditions: ['development', 'module', 'browser', 'import', 'default'],
alias: [
{
find: /^#library\/(.*)\.js$/,
replacement: fileURLToPath(new URL('../../library/dist/common/$1.js', import.meta.url))
},
// More-specific path must come first so it is matched before the bare package.
{
find: /^@magmacomputing\/tempo\/ticker$/,
replacement: fileURLToPath(new URL('../src/plugin/extend/extend.ticker.ts', import.meta.url))
replacement: fileURLToPath(new URL('../dist/plugin/extend/extend.ticker.js', import.meta.url))
},
{
find: /^@magmacomputing\/tempo$/,
replacement: fileURLToPath(new URL('../src/tempo.index.ts', import.meta.url))
replacement: fileURLToPath(new URL('../dist/tempo.index.js', import.meta.url))
},
]
Comment thread
coderabbitai[bot] marked this conversation as resolved.
},
Expand Down
8 changes: 8 additions & 0 deletions packages/tempo/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ 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).

## [2.2.2] - 2026-04-18

### Fixed
- **Plugin Infrastructure Preservation**: Refactored the Rollup configuration to treat all library files as public entry points. This prevents critical utilities (like `defineExtension`) from being tree-shaken during the build process, ensuring that modular plugins can register correctly.
- **API Surface Hardening**: Explicitly exported all registration and utility helpers (`defineModule`, `defineTerm`, etc.) from the main entry point to guarantee their availability for third-party extensions.
- **Documentation Build Stability**: Updated the documentation configuration to utilize pre-compiled `dist/` assets. This resolves runtime `SyntaxError` issues in the browser caused by the presence of modern TC39 decorators in the raw TypeScript source files.
- **Decorator Transpilation**: Refactored utility functions to ensure standard function declarations are used where appropriate, improving the reliability of the transpilation phase.

## [2.1.2] - 2026-04-14

### Added
Expand Down
50 changes: 25 additions & 25 deletions packages/tempo/README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,12 @@
<a href="https://magmacomputing.github.io/magma/">
<img src="./img/docs-banner.png" width="100%" alt="Visit Tempo Documentation">
</a>

<br>

<table width="100%" border="0" style="border: none;">
<table width="100%">
<tbody>
<tr style="border: none;">
<td align="left" width="100" style="border: none;">
<img src="./img/logo.svg" width="100" style="display: block;" alt="Tempo logo">
<tr>
<td align="left" width="120">
<img src="./img/logo.svg" width="120" alt="Tempo logo">
</td>
<td align="left" style="border: none;" valign="middle">
<span style="font-size: 4em; font-weight: bold; line-height: 1;">Tempo</span>
</td>
</tr>
<tr style="border: none;">
<td colspan="2" align="center" style="border: none; padding-top: 20px;">
<strong>The Professional Date-Time Library for Temporal</strong>
<td align="left" valign="middle">
<h1>Tempo</h1>
<p><strong>The Professional Date-Time Library for the Temporal API</strong></p>
</td>
</tr>
</tbody>
Expand All @@ -27,14 +17,14 @@
**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 border="0" style="border: none;">
<table>
<tbody>
<tr style="border: none;">
<td style="border: none; padding: 0 5px;"><a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a></td>
<td style="border: none; padding: 0 5px;"><a href="https://tc39.es/proposal-temporal/"><img src="https://img.shields.io/badge/Temporal-Stage%204-green" alt="Temporal"></a></td>
<td style="border: none; padding: 0 5px;"><a href="https://www.typescriptlang.org/"><img src="https://img.shields.io/badge/TypeScript-Ready-blue?logo=typescript" alt="TypeScript Ready"></a></td>
<td style="border: none; padding: 0 5px;"><a href="https://nodejs.org/api/esm.html"><img src="https://img.shields.io/badge/Native-ESM-green" alt="Native ESM"></a></td>
<td style="border: none; padding: 0 5px;"><a href="https://magmacomputing.github.io/magma/"><img src="https://img.shields.io/badge/Docs-VitePress-brightgreen?logo=vitepress" alt="Documentation"></a></td>
<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>
Expand Down Expand Up @@ -116,6 +106,17 @@ For environments without `importmap` support or simple prototypes, use the bundl
console.log(t.toString());
</script>
```

---

## 📚 Documentation

For a deeper dive into the API, architecture, and advanced features:

* **[Official Documentation Website](https://magmacomputing.github.io/magma/)** — Tutorials, interactive demos, and "Getting Started" guides.
* **[Full API Reference Guide](https://magmacomputing.github.io/magma/doc/tempo.api)** — Detailed technical documentation for every class and method.

---
## 🛠️ Quick Start

```javascript
Expand All @@ -134,7 +135,6 @@ const startOfMonth = now.set({ start: 'month' });
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"
```
Looking for the full API reference? Check out the **[Tempo API Guide](https://magmacomputing.github.io/magma/doc/tempo.api)**.


## 💬 Contact & Support
Expand Down
10 changes: 5 additions & 5 deletions packages/tempo/doc/tempo.ticker.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Tempo Ticker

`Tempo.ticker` is an optional plugin (provided in the project) that creates a reactive stream of `Tempo` instances at regular intervals. It is designed to be high-performance and lightweight, providing a simple way to build clocks, countdowns, or scheduled updates.
`Tempo.ticker` is an optional plugin (provided in the @magmacomputing/tempo/ticker module) that creates a reactive stream of `Tempo` instances at regular intervals. It is designed to be high-performance and lightweight, providing a simple way to build clocks, countdowns, or scheduled updates.

## Installation

Expand Down Expand Up @@ -142,11 +142,11 @@ All listeners use the same callback signature: `(t, stop) => {}`.

```typescript
const ticker = Tempo.ticker(1);
ticker.on('pulse', (t) => console.log('Listener A:', t));
ticker.on('pulse', (t) => console.log('Listener B:', t));
ticker.on('stop', (t, stop) => console.log('Ticker stopped at:', t, stop));
ticker.on('pulse', (t) => console.log('Listener A:', t.fmt.weekTime));
ticker.on('pulse', (t) => console.log('Listener B:', t.fmt.weekTime));
ticker.on('stop', (t) => console.log('Ticker stopped at:', t.fmt.weekTime));
```
For `'stop'` listeners, `stop` is included for callback signature consistency; invoking it after stop has already occurred is a no-op.
For `'stop'` listeners, the `stop` callback argument is included for signature consistency; however, invoking it after stop has already occurred is a no-op.

### 4. Manual Pulsing (.pulse)
In some scenarios, you may want to drive a ticker manually (e.g., from a UI event or a WebSocket message) while still benefiting from the ticker's internal state management and listeners.
Expand Down
Loading