@vyckr/ttid
Version:
A lightweight, time-based identifier generator that tracks creation, update, and deletion timestamps using a progressive format.
292 lines (215 loc) • 7.28 kB
Markdown
# TTID (Time-Tagged Identifier)
A lightweight, time-based identifier generator that tracks creation, update, and deletion timestamps using a progressive format.
## Overview
TTID creates unique identifiers with a progressive structure:
- **Created:** `[CREATION_TIMESTAMP]`
- **Updated:** `[CREATION_TIMESTAMP]-[UPDATE_TIMESTAMP]`
- **Deleted:** `[CREATION_TIMESTAMP]-[UPDATE_TIMESTAMP]-[DELETION_TIMESTAMP]`
Each TTID segment contains:
- High-resolution timestamps encoded in base-36
- Progressive expansion to track lifecycle states
- Compact 11-character timestamps for efficiency
- Immutable deletion state (cannot be modified once deleted)
## Installation
```bash
npm install ttid
```
## Usage
### Basic ID Generation
```typescript
import TTID from 'ttid';
// Generate a new TTID (creation only)
const newId = TTID.generate();
console.log(newId);
// Example output: "1A2B3C4D5E6"
// Verify if a string is a valid TTID
const isValid = TTID.isTTID(newId);
console.log(isValid); // Returns Date object if valid, null if invalid
```
### Progressive Updates
```typescript
import TTID from 'ttid';
// Start with a new ID
let id = TTID.generate();
console.log(id); // "1A2B3C4D5E6"
// Update the ID (adds update timestamp)
id = TTID.generate(id);
console.log(id); // "1A2B3C4D5E6-F7G8H9I0J1"
// Update again (replaces update timestamp)
id = TTID.generate(id);
console.log(id); // "1A2B3C4D5E6-K2L3M4N5O6"
// Mark as deleted (adds deletion timestamp - final state)
id = TTID.generate(id, true);
console.log(id); // "1A2B3C4D5E6-K2L3M4N5O6-P7Q8R9S0T1"
// Attempting to modify a deleted ID throws an error
try {
TTID.generate(id); // Throws: "This identifier can no longer be modified"
} catch (error) {
console.error(error.message);
}
```
### Decoding Timestamps
```typescript
import TTID from 'ttid';
// Create and update an ID
let id = TTID.generate();
setTimeout(() => {
id = TTID.generate(id);
setTimeout(() => {
id = TTID.generate(id, true); // Mark as deleted
// Decode all timestamps
const times = TTID.decodeTime(id);
console.log(times);
// Example output:
// {
// createdAt: 1651234567890,
// updatedAt: 1651234572345,
// deletedAt: 1651234578901
// }
// Convert to readable dates
console.log({
created: new Date(times.createdAt),
updated: times.updatedAt ? new Date(times.updatedAt) : null,
deleted: times.deletedAt ? new Date(times.deletedAt) : null
});
}, 2000);
}, 1000);
```
### Working with Different States
```typescript
import TTID from 'ttid';
// Check ID states
function analyzeId(id: string) {
const validation = TTID.isTTID(id);
if (!validation) {
console.log('Invalid TTID');
return;
}
const segments = id.split('-');
const times = TTID.decodeTime(id);
console.log(`ID State: ${getIdState(segments.length)}`);
console.log(`Created: ${new Date(times.createdAt)}`);
if (times.updatedAt) {
console.log(`Updated: ${new Date(times.updatedAt)}`);
}
if (times.deletedAt) {
console.log(`Deleted: ${new Date(times.deletedAt)}`);
}
}
function getIdState(segmentCount: number) {
switch (segmentCount) {
case 1: return 'Created';
case 2: return 'Updated';
case 3: return 'Deleted';
default: return 'Unknown';
}
}
// Examples
const createdId = TTID.generate();
analyzeId(createdId); // ID State: Created
const updatedId = TTID.generate(createdId);
analyzeId(updatedId); // ID State: Updated
const deletedId = TTID.generate(updatedId, true);
analyzeId(deletedId); // ID State: Deleted
```
### Error Handling
```typescript
import TTID from 'ttid';
// Invalid ID format
try {
TTID.generate('invalid-id');
} catch (error) {
console.error(error.message); // "Invalid TTID!"
}
// Attempting to modify deleted ID
const id = TTID.generate();
const updated = TTID.generate(id);
const deleted = TTID.generate(updated, true);
try {
TTID.generate(deleted);
} catch (error) {
console.error(error.message); // "This identifier can no longer be modified"
}
// Invalid format for decoding
try {
TTID.decodeTime('not-a-ttid');
} catch (error) {
console.error(error.message); // "Invalid Format!"
}
```
## API Reference
### `TTID.generate(id?: string, del?: boolean)`
Generates a new TTID or updates an existing one.
**Parameters:**
- `id` (optional) - An existing TTID to update
- `del` (optional) - Set to `true` to mark the ID as deleted
**Returns:** `_ttid` - A TTID string
**Behavior:**
- No parameters: Creates new ID `[TIMESTAMP]`
- Valid TTID provided: Updates to `[CREATED]-[NEW_TIMESTAMP]`
- Valid TTID + `del=true`: Marks as deleted `[CREATED]-[UPDATED]-[DELETED_TIMESTAMP]`
**Throws:**
- Error if provided ID is invalid
- Error if attempting to modify a deleted ID (3 segments)
### `TTID.decodeTime(id: string)`
Decodes timestamps from a TTID.
**Parameters:**
- `id` - A TTID string
**Returns:** `_timestamps` object with:
- `createdAt` - Creation timestamp in milliseconds
- `updatedAt` (optional) - Update timestamp in milliseconds
- `deletedAt` (optional) - Deletion timestamp in milliseconds
**Throws:** Error if the format is invalid
### `TTID.isTTID(id: string)`
Validates a TTID and returns creation date if valid.
**Parameters:**
- `id` - A string to validate
**Returns:**
- `Date` object (creation date) if valid
- `null` if invalid
### `TTID.isUUID(id: string)`
Checks if a string is a valid UUID.
**Parameters:**
- `id` - A string to check
**Returns:** `RegExpMatchArray | null` - Match result or null
## Format Specification
TTIDs follow a strict format:
- Base-36 encoding (0-9, A-Z)
- 11-character timestamps
- Hyphen-separated segments
- Progressive structure
**Valid Patterns:**
- `[A-Z0-9]{11}` - Created only
- `[A-Z0-9]{11}-[A-Z0-9]{1,11}` - Created + Updated
- `[A-Z0-9]{11}-[A-Z0-9]{1,11}-[A-Z0-9]{1,11}` - Created + Updated + Deleted
**Special Cases:**
- Placeholder 'X' may appear in update position for certain states
- Deleted IDs cannot be modified further
## Lifecycle States
| State | Format | Segments | Modifiable |
|-------|--------|----------|------------|
| Created | `TIMESTAMP` | 1 | ✅ |
| Updated | `CREATED-UPDATED` | 2 | ✅ |
| Deleted | `CREATED-UPDATED-DELETED` | 3 | ❌ |
## Comparison with Other Systems
| Feature | TTID | UUID | ULID |
|---------|------|------|------|
| Progressive states | ✅ | ❌ | ❌ |
| Soft delete tracking | ✅ | ❌ | ❌ |
| Immutable final state | ✅ | ❌ | ❌ |
| Compact encoding | ✅ | ❌ | ✅ |
| Time-based | ✅ | ⚠️ | ✅ |
| Fixed length | ❌ | ✅ | ✅ |
## Use Cases
- **Database Records**: Track entity lifecycle (created → updated → soft deleted)
- **Audit Systems**: Maintain chronological history in the ID itself
- **Document Management**: Version control with embedded timestamps
- **API Resources**: RESTful endpoints with state-aware identifiers
- **Event Sourcing**: Compact event identifiers with temporal information
## Performance Considerations
- Base-36 encoding provides compact representation
- Progressive format minimizes storage for simple states
- High-resolution timestamps ensure uniqueness in high-frequency scenarios
- Validation includes timestamp parsing for integrity checking
## License
MIT