An open API service indexing awesome lists of open source software.

https://github.com/edjcase/motoko_tid

A Motoko library for handling TIDs (Time Identifiers) for atproto
https://github.com/edjcase/motoko_tid

Last synced: 5 months ago
JSON representation

A Motoko library for handling TIDs (Time Identifiers) for atproto

Awesome Lists containing this project

README

          

# Motoko TID Library

[![MOPS](https://img.shields.io/badge/MOPS-tid-blue)](https://mops.one/tid)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/edjcase/motoko_tid/blob/main/LICENSE)

A Motoko library for working with Time Identifiers (TIDs) - compact, sortable identifiers based on timestamps with microsecond precision and clock identifiers. TIDs are designed to be lexicographically sortable and maintain chronological ordering.

## Package

### MOPS

```bash
mops add tid
```

To set up MOPS package manager, follow the instructions from the [MOPS Site](https://mops.one)

## Quick Start

### Import

```motoko
import TID "mo:tid"
```

### Example 1: Creating and Converting TIDs

```motoko
// Create a TID manually
let tid : TID.TID = {
timestamp = 1640995200000000; // Microseconds since UNIX epoch
clockId = 42; // Clock identifier (0-1023)
};

// Convert to text representation
let tidText = TID.toText(tid);
Debug.print("TID: " # tidText); // "3jzfcijpj2z2a" (example)

// Parse TID from text
switch (TID.fromText(tidText)) {
case (#ok(parsedTid)) {
Debug.print("Parsed successfully: " # debug_show(parsedTid));
};
case (#err(error)) {
Debug.print("Parse error: " # error);
};
};
```

### Example 2: Using the TID Generator

```motoko
// Create a generator
let generator = TID.Generator();

// Generate sequential TIDs
let tid1 = generator.next(); // clockId = 0
let tid2 = generator.next(); // clockId = 1
let tid3 = generator.next(); // clockId = 2

Debug.print("TID 1: " # TID.toText(tid1));
Debug.print("TID 2: " # TID.toText(tid2));
Debug.print("TID 3: " # TID.toText(tid3));
```

### Example 3: Comparing and Sorting TIDs

```motoko
let tid1 = { timestamp = 1000; clockId = 1 };
let tid2 = { timestamp = 2000; clockId = 2 };
let tid3 = { timestamp = 2000; clockId = 3 }; // Same timestamp, different clock

// Numeric comparison
switch (TID.compare(tid1, tid2)) {
case (#less) Debug.print("tid1 < tid2");
case (#equal) Debug.print("tid1 == tid2");
case (#greater) Debug.print("tid1 > tid2");
};

// Equality check
let isEqual = TID.equal(tid1, tid2); // false

// String sorting matches numeric sorting
let text1 = TID.toText(tid1);
let text2 = TID.toText(tid2);
assert(text1 < text2); // Lexicographic ordering matches temporal ordering
```

### Example 4: Working with Nat64 Representation

```motoko
// Convert TID to 64-bit integer
let tid = { timestamp = 1640995200000000; clockId = 42 };
let nat64Value = TID.toNat64(tid);

// Convert back from 64-bit integer
switch (TID.fromNat64(nat64Value)) {
case (#ok(reconstructedTid)) {
assert(TID.equal(tid, reconstructedTid)); // Round-trip successful
};
case (#err(error)) {
Debug.print("Conversion error: " # error);
};
};
```

## API Reference

### Types

```motoko
// TID structure
public type TID = {
timestamp : Nat; // Microseconds since UNIX epoch (max 53 bits)
clockId : Nat; // Clock identifier (max 10 bits: 0-1023)
};
```

### Core Functions

```motoko
// Convert TID to base32-sortable text representation
public func toText(tid : TID) : Text;

// Parse TID from base32-sortable text
public func fromText(text : Text) : Result.Result;

// Convert TID to 64-bit integer representation
public func toNat64(tid : TID) : Nat64;

// Convert 64-bit integer to TID
public func fromNat64(value : Nat64) : Result.Result;

// Compare two TIDs for sorting
public func compare(tid1 : TID, tid2 : TID) : Order.Order;

// Check if two TIDs are equal
public func equal(tid1 : TID, tid2 : TID) : Bool;
```

### TID Generator

```motoko
// TID generator class
public class Generator() {
// Generate the next TID with current timestamp and incremented clock ID
public func next() : TID;
}
```

## TID Format

### Structure

A TID is composed of:

- **1 bit**: Always 0 (reserved for future use)
- **53 bits**: Timestamp in microseconds since UNIX epoch
- **10 bits**: Clock identifier (0-1023)

### Text Representation

- **Length**: Exactly 13 characters
- **Encoding**: Base32-sortable (using AT Protocol's sortable alphabet)
- **Character set**: `234567abcdefghijklmnopqrstuvwxyz` (no 0, 1, or uppercase)
- **Ordering**: Lexicographic string sorting matches temporal ordering

### Constraints

- **Maximum timestamp**: 2^53 - 1 (9,007,199,254,740,991) for JavaScript compatibility
- **Maximum clock ID**: 1,023 (10 bits)
- **Top bit**: Must always be 0

## Use Cases

- **Distributed systems**: Sortable identifiers across multiple nodes
- **Database keys**: Time-ordered primary keys with collision resistance
- **Event logging**: Chronologically sortable event identifiers
- **AT Protocol**: Compatible with ATProto TID specification

## Error Handling

The library provides detailed error messages for invalid inputs:

- Invalid TID length (must be exactly 13 characters)
- Invalid characters (only base32-sortable alphabet allowed)
- Invalid first character (high bit would be set)
- Timestamp or clock ID exceeding maximum values
- Invalid byte representations

## Dependencies

- `core`: Core Motoko libraries
- `base-x-encoder`: Base32 encoding/decoding
- `xtended-numbers`: Extended number utilities
- `buffer`: Buffer utilities

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.