@nextera.one/tps-standard 0.5.1 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +91 -467
- package/dist/drivers/gregorian.d.ts +18 -0
- package/dist/drivers/gregorian.js +142 -0
- package/dist/drivers/gregorian.js.map +1 -0
- package/dist/drivers/tps.d.ts +55 -0
- package/dist/drivers/tps.js +221 -0
- package/dist/drivers/tps.js.map +1 -0
- package/dist/drivers/unix.d.ts +16 -0
- package/dist/drivers/unix.js +76 -0
- package/dist/drivers/unix.js.map +1 -0
- package/dist/index.d.ts +172 -39
- package/dist/index.js +794 -286
- package/dist/index.js.map +1 -0
- package/package.json +4 -3
- package/src/drivers/gregorian.ts +158 -0
- package/src/drivers/tps.ts +239 -0
- package/src/drivers/unix.ts +79 -0
- package/src/index.ts +956 -323
- package/dist/src/index.js +0 -681
- package/dist/test/src/index.js +0 -963
- package/dist/test/test/persian-calendar.test.js +0 -488
- package/dist/test/test/tps-uid.test.js +0 -295
- package/dist/test/tps-uid.test.js +0 -240
package/README.md
CHANGED
|
@@ -2,531 +2,155 @@
|
|
|
2
2
|
|
|
3
3
|

|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
A universal protocol for representing **time + location + context** in a single coordinate.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
## 📦 Installation
|
|
7
|
+
## Installation
|
|
10
8
|
|
|
11
9
|
```bash
|
|
12
10
|
npm i @nextera.one/tps-standard
|
|
13
11
|
```
|
|
14
12
|
|
|
15
|
-
##
|
|
16
|
-
|
|
17
|
-
### Basic Example
|
|
13
|
+
## Quick Start
|
|
18
14
|
|
|
19
15
|
```ts
|
|
20
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
TPS,
|
|
18
|
+
TpsDate,
|
|
19
|
+
DefaultCalendars,
|
|
20
|
+
TimeOrder,
|
|
21
|
+
TPSUID7RB,
|
|
22
|
+
} from "@nextera.one/tps-standard";
|
|
21
23
|
|
|
22
|
-
//
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
//
|
|
28
|
-
const uri = "tps://31.95,35.91,800m@T:greg.m3.c1.y26.M01.d07.h13.n20;f4.r7";
|
|
29
|
-
const parsed = TPS.parse(uri);
|
|
30
|
-
console.log(parsed);
|
|
31
|
-
// { latitude: 31.95, longitude: 35.91, altitude: 800, calendar: 'greg', year: 26, ... }
|
|
24
|
+
// 1) Date -> TPS time string
|
|
25
|
+
const tpsTime = TPS.fromDate(
|
|
26
|
+
new Date("2026-01-09T14:30:25Z"),
|
|
27
|
+
DefaultCalendars.GREG,
|
|
28
|
+
);
|
|
29
|
+
// T:greg.m3.c1.y26.m1.d9.h14.m30.s25.m0
|
|
32
30
|
|
|
33
|
-
//
|
|
34
|
-
const
|
|
31
|
+
// 2) Build full TPS URI
|
|
32
|
+
const uri = TPS.toURI({
|
|
35
33
|
calendar: "greg",
|
|
34
|
+
millennium: 3,
|
|
35
|
+
century: 1,
|
|
36
36
|
year: 26,
|
|
37
37
|
month: 1,
|
|
38
|
-
day:
|
|
38
|
+
day: 9,
|
|
39
|
+
hour: 14,
|
|
40
|
+
minute: 30,
|
|
41
|
+
second: 25,
|
|
42
|
+
millisecond: 0,
|
|
39
43
|
latitude: 31.95,
|
|
40
44
|
longitude: 35.91,
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
// Output: "tps://31.95,35.91@T:greg.y26.M01.d07"
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
## 📖 Core Concepts
|
|
48
|
-
|
|
49
|
-
### Time Hierarchy
|
|
45
|
+
altitude: 800,
|
|
46
|
+
});
|
|
47
|
+
// tps://L:31.95,35.91,800m@T:greg.m3.c1.y26.m1.d9.h14.m30.s25.m0
|
|
50
48
|
|
|
51
|
-
|
|
49
|
+
// 3) Parse back
|
|
50
|
+
const parsed = TPS.parse(uri);
|
|
52
51
|
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
// 4) Generate reversible TPS UID (binary base64url form)
|
|
53
|
+
const uid = TPSUID7RB.encodeBinaryB64(uri);
|
|
54
|
+
const decoded = TPSUID7RB.decodeBinaryB64(uid);
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
| --------- | ------------------------- |
|
|
59
|
-
| `greg` | Calendar code (gregorian) |
|
|
60
|
-
| `m3` | Millennium 3 (2000-2999) |
|
|
61
|
-
| `c1` | Century 1 (2000-2099) |
|
|
62
|
-
| `y26` | Year 26 (2026) |
|
|
63
|
-
| `M01` | Month 01 (January) |
|
|
64
|
-
| `d07` | Day 07 |
|
|
65
|
-
| `h13` | Hour 13 (1:00 PM) |
|
|
66
|
-
| `n20` | Minute 20 |
|
|
67
|
-
| `s45` | Second 45 |
|
|
57
|
+
## Time Format
|
|
68
58
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
### TPS URI Format
|
|
72
|
-
|
|
73
|
-
**Canonical form:**
|
|
59
|
+
Canonical time token order is descending hierarchy:
|
|
74
60
|
|
|
61
|
+
```txt
|
|
62
|
+
T:greg.m3.c1.y26.m01.d13.h09.m30.s12.m0
|
|
75
63
|
```
|
|
76
|
-
tps://[SPACE]@[TIME][;EXTENSIONS]
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
#### Components
|
|
80
64
|
|
|
81
|
-
|
|
82
|
-
| ------------ | --------------------------------------------------------- |
|
|
83
|
-
| `SPACE` | `lat,lon[,alt]m` (WGS84) or `unknown`/`hidden`/`redacted` |
|
|
84
|
-
| `TIME` | TPS Time format (`T:calendar.hierarchy`) |
|
|
85
|
-
| `EXTENSIONS` | Optional context after `;`, pairs separated by `.` |
|
|
65
|
+
Where:
|
|
86
66
|
|
|
87
|
-
|
|
67
|
+
- `m` (rank 8) = millennium
|
|
68
|
+
- `c` = century
|
|
69
|
+
- `y` = year in century
|
|
70
|
+
- `m` (rank 5) = month
|
|
71
|
+
- `d` = day
|
|
72
|
+
- `h` = hour
|
|
73
|
+
- `m` (rank 2) = minute
|
|
74
|
+
- `s` = second
|
|
75
|
+
- `m` (rank 0) = millisecond
|
|
88
76
|
|
|
89
|
-
|
|
77
|
+
Ascending order is supported via `TimeOrder.ASC` and is auto-detected during parsing.
|
|
90
78
|
|
|
91
|
-
|
|
92
|
-
- **Unknown:** `unknown` (technical failure, no GPS)
|
|
93
|
-
- **Hidden:** `hidden` (user masked their location)
|
|
94
|
-
- **Redacted:** `redacted` (censored for legal/security)
|
|
79
|
+
## URI Format
|
|
95
80
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
- `greg` — Gregorian calendar (default)
|
|
99
|
-
- `unix` — Unix epoch seconds
|
|
100
|
-
- `hij` — Hijri (Islamic) — _requires driver_
|
|
101
|
-
- `jul` — Julian — _requires driver_
|
|
102
|
-
- `holo` — Holocene — _requires driver_
|
|
103
|
-
|
|
104
|
-
## 🔌 Plugin Architecture
|
|
105
|
-
|
|
106
|
-
TPS supports custom calendar drivers for non-Gregorian systems. Drivers can wrap external date libraries (like `moment-hijri`, `@js-joda/extra`, etc.).
|
|
107
|
-
|
|
108
|
-
### CalendarDriver Interface
|
|
109
|
-
|
|
110
|
-
```ts
|
|
111
|
-
export interface CalendarDriver {
|
|
112
|
-
readonly code: CalendarCode;
|
|
113
|
-
readonly name?: string; // Optional human-readable name
|
|
114
|
-
|
|
115
|
-
// Required methods
|
|
116
|
-
fromGregorian(date: Date): Partial<TPSComponents>;
|
|
117
|
-
toGregorian(components: Partial<TPSComponents>): Date;
|
|
118
|
-
fromDate(date: Date): string;
|
|
119
|
-
|
|
120
|
-
// Optional enhanced methods
|
|
121
|
-
parseDate?(input: string, format?: string): Partial<TPSComponents>;
|
|
122
|
-
format?(components: Partial<TPSComponents>, format?: string): string;
|
|
123
|
-
validate?(input: string | Partial<TPSComponents>): boolean;
|
|
124
|
-
getMetadata?(): CalendarMetadata;
|
|
125
|
-
}
|
|
81
|
+
```txt
|
|
82
|
+
tps://[SPACE][ /A:actor ]@T:[calendar].[tokens][!signature][;extensions][?query][#fragment]
|
|
126
83
|
```
|
|
127
84
|
|
|
128
|
-
###
|
|
129
|
-
|
|
130
|
-
```ts
|
|
131
|
-
import { TPS, CalendarDriver, TPSComponents } from "@nextera.one/tps-standard";
|
|
132
|
-
|
|
133
|
-
class HijriDriver implements CalendarDriver {
|
|
134
|
-
readonly code = "hij";
|
|
135
|
-
readonly name = "Hijri (Islamic)";
|
|
136
|
-
|
|
137
|
-
// Parse a Hijri date string like '1447-07-21'
|
|
138
|
-
parseDate(input: string): Partial<TPSComponents> {
|
|
139
|
-
const [year, month, day] = input.split("-").map(Number);
|
|
140
|
-
return { calendar: "hij", year, month, day };
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Format components to Hijri date string
|
|
144
|
-
format(comp: Partial<TPSComponents>): string {
|
|
145
|
-
return `${comp.year}-${String(comp.month).padStart(2, "0")}-${String(
|
|
146
|
-
comp.day
|
|
147
|
-
).padStart(2, "0")}`;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
fromGregorian(date: Date): Partial<TPSComponents> {
|
|
151
|
-
// Use external library for accurate conversion
|
|
152
|
-
// Example with moment-hijri:
|
|
153
|
-
// const m = moment(date);
|
|
154
|
-
// return { year: m.iYear(), month: m.iMonth() + 1, day: m.iDate() };
|
|
155
|
-
return { year: 1447, month: 7, day: 21 };
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
toGregorian(components: Partial<TPSComponents>): Date {
|
|
159
|
-
// Reverse conversion using external library
|
|
160
|
-
return new Date();
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
fromDate(date: Date): string {
|
|
164
|
-
const comp = this.fromGregorian(date);
|
|
165
|
-
const pad = (n?: number) => String(n || 0).padStart(2, "0");
|
|
166
|
-
return `T:hij.y${comp.year}.M${pad(comp.month)}.d${pad(comp.day)}`;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// Register the driver
|
|
171
|
-
TPS.registerDriver(new HijriDriver());
|
|
172
|
-
```
|
|
85
|
+
### Supported space anchors
|
|
173
86
|
|
|
174
|
-
|
|
87
|
+
- Privacy: `L:-`, `L:~`, `L:redacted`, `unknown`, `hidden`, `redacted`
|
|
88
|
+
- Coordinates: `L:lat,lon[,alt]m`
|
|
89
|
+
- Cells: `L:s2=...`, `L:h3=...`, `L:plus=...`, `L:w3w=...`
|
|
90
|
+
- Structural: `L:bldg=...(.floor=...)(.room=...)(.zone=...)`
|
|
91
|
+
- Generic pre-`@` anchors: `adm:...`, `node:...`, `net:ip4:...`, `net:ip6:...`, `planet:...`, `P:...`
|
|
175
92
|
|
|
176
|
-
|
|
177
|
-
// Parse a Hijri date string directly
|
|
178
|
-
const components = TPS.parseCalendarDate("hij", "1447-07-21");
|
|
179
|
-
// { calendar: 'hij', year: 1447, month: 7, day: 21 }
|
|
93
|
+
## Calendars
|
|
180
94
|
|
|
181
|
-
|
|
182
|
-
const uri = TPS.fromCalendarDate("hij", "1447-07-21", {
|
|
183
|
-
latitude: 31.95,
|
|
184
|
-
longitude: 35.91,
|
|
185
|
-
});
|
|
186
|
-
// "tps://31.95,35.91@T:hij.y1447.M07.d21"
|
|
187
|
-
|
|
188
|
-
// Format TPS components back to calendar-native string
|
|
189
|
-
const parsed = TPS.parse("tps://unknown@T:hij.y1447.M07.d21");
|
|
190
|
-
const formatted = TPS.formatCalendarDate("hij", parsed);
|
|
191
|
-
// "1447-07-21"
|
|
192
|
-
|
|
193
|
-
// Using the driver directly
|
|
194
|
-
const driver = TPS.getDriver("hij");
|
|
195
|
-
if (driver?.parseDate) {
|
|
196
|
-
const comp = driver.parseDate("1447-07-21");
|
|
197
|
-
const gregDate = driver.toGregorian(comp);
|
|
198
|
-
}
|
|
199
|
-
```
|
|
95
|
+
Built-in drivers:
|
|
200
96
|
|
|
201
|
-
|
|
97
|
+
- `tps`
|
|
98
|
+
- `greg`
|
|
99
|
+
- `unix`
|
|
202
100
|
|
|
203
|
-
|
|
101
|
+
Additional calendars can be plugged in via `TPS.registerDriver(driver)`.
|
|
204
102
|
|
|
205
|
-
|
|
206
|
-
import moment from "moment-hijri";
|
|
207
|
-
|
|
208
|
-
class MomentHijriDriver implements CalendarDriver {
|
|
209
|
-
readonly code = "hij";
|
|
210
|
-
|
|
211
|
-
parseDate(input: string, format = "iYYYY-iMM-iDD"): Partial<TPSComponents> {
|
|
212
|
-
const m = moment(input, format);
|
|
213
|
-
return {
|
|
214
|
-
calendar: "hij",
|
|
215
|
-
year: m.iYear(),
|
|
216
|
-
month: m.iMonth() + 1,
|
|
217
|
-
day: m.iDate(),
|
|
218
|
-
hour: m.hour(),
|
|
219
|
-
minute: m.minute(),
|
|
220
|
-
second: m.second(),
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
fromGregorian(date: Date): Partial<TPSComponents> {
|
|
225
|
-
const m = moment(date);
|
|
226
|
-
return {
|
|
227
|
-
calendar: "hij",
|
|
228
|
-
year: m.iYear(),
|
|
229
|
-
month: m.iMonth() + 1,
|
|
230
|
-
day: m.iDate(),
|
|
231
|
-
hour: m.hour(),
|
|
232
|
-
minute: m.minute(),
|
|
233
|
-
second: m.second(),
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
toGregorian(comp: Partial<TPSComponents>): Date {
|
|
238
|
-
const m = moment(`${comp.year}-${comp.month}-${comp.day}`, "iYYYY-iM-iD");
|
|
239
|
-
return m.toDate();
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
fromDate(date: Date): string {
|
|
243
|
-
const c = this.fromGregorian(date);
|
|
244
|
-
const p = (n?: number) => String(n || 0).padStart(2, "0");
|
|
245
|
-
return `T:hij.y${c.year}.M${p(c.month)}.d${p(c.day)}.h${p(c.hour)}.n${p(
|
|
246
|
-
c.minute
|
|
247
|
-
)}.s${p(c.second)}`;
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
```
|
|
103
|
+
Calendar code width is enforced to **3–4 lowercase letters** when generating TPS time strings.
|
|
251
104
|
|
|
252
|
-
##
|
|
105
|
+
## TpsDate
|
|
253
106
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
Validates whether a string is properly formatted TPS.
|
|
107
|
+
`TpsDate` is a Date-like wrapper that works directly with TPS.
|
|
257
108
|
|
|
258
109
|
```ts
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
110
|
+
import {
|
|
111
|
+
TpsDate,
|
|
112
|
+
DefaultCalendars,
|
|
113
|
+
TimeOrder,
|
|
114
|
+
} from "@nextera.one/tps-standard";
|
|
262
115
|
|
|
263
|
-
|
|
116
|
+
const td = new TpsDate("tps://unknown@T:greg.m3.c1.y26.m01.d09.h14.m30.s25.m0");
|
|
264
117
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
```ts
|
|
268
|
-
const parsed = TPS.parse(
|
|
269
|
-
"tps://31.95,35.91,800m@T:greg.m3.c1.y26.M01.d07.h13.n20;f4.r7"
|
|
270
|
-
);
|
|
271
|
-
// {
|
|
272
|
-
// latitude: 31.95,
|
|
273
|
-
// longitude: 35.91,
|
|
274
|
-
// altitude: 800,
|
|
275
|
-
// calendar: 'greg',
|
|
276
|
-
// millennium: 3,
|
|
277
|
-
// century: 1,
|
|
278
|
-
// year: 26,
|
|
279
|
-
// month: 1,
|
|
280
|
-
// day: 7,
|
|
281
|
-
// hour: 13,
|
|
282
|
-
// minute: 20,
|
|
283
|
-
// extensions: { f: "4", r: "7" }
|
|
284
|
-
// }
|
|
285
|
-
```
|
|
118
|
+
// Native Gregorian Date clone
|
|
119
|
+
const gregorian = td.toGregorianDate();
|
|
286
120
|
|
|
287
|
-
|
|
121
|
+
// Alias (same value)
|
|
122
|
+
const same = td.toDate();
|
|
288
123
|
|
|
289
|
-
|
|
124
|
+
// TPS time in desired calendar
|
|
125
|
+
const asTps = td.toTPS(DefaultCalendars.TPS, { order: TimeOrder.DESC });
|
|
290
126
|
|
|
291
|
-
|
|
292
|
-
const
|
|
293
|
-
calendar: "greg",
|
|
294
|
-
year: 26,
|
|
295
|
-
month: 1,
|
|
296
|
-
day: 7,
|
|
127
|
+
// Full URI
|
|
128
|
+
const uri = td.toTPSURI(DefaultCalendars.GREG, {
|
|
297
129
|
latitude: 31.95,
|
|
298
130
|
longitude: 35.91,
|
|
299
131
|
altitude: 800,
|
|
300
|
-
extensions: { f: "4", r: "7" },
|
|
301
|
-
};
|
|
302
|
-
const uri = TPS.toURI(components);
|
|
303
|
-
// "tps://31.95,35.91,800m@T:greg.y26.M01.d07;f4.r7"
|
|
304
|
-
```
|
|
305
|
-
|
|
306
|
-
### `TPS.fromDate(date: Date, calendar: CalendarCode): string`
|
|
307
|
-
|
|
308
|
-
Generates a TPS time string from a JavaScript Date. Supports registered drivers.
|
|
309
|
-
|
|
310
|
-
```ts
|
|
311
|
-
const timeString = TPS.fromDate(new Date(), "greg");
|
|
312
|
-
// "T:greg.m3.c1.y26.M01.d07.h13.n20.s45"
|
|
313
|
-
|
|
314
|
-
const unixTime = TPS.fromDate(new Date(), "unix");
|
|
315
|
-
// "T:unix.s1704729645.123"
|
|
316
|
-
```
|
|
317
|
-
|
|
318
|
-
### `TPS.toDate(tpsString: string): Date | null`
|
|
319
|
-
|
|
320
|
-
Converts a TPS string back to a JavaScript Date object.
|
|
321
|
-
|
|
322
|
-
```ts
|
|
323
|
-
const date = TPS.toDate("T:greg.m3.c1.y26.M01.d07.h13.n20.s45");
|
|
324
|
-
console.log(date); // Date object for 2026-01-07 13:20:45 UTC
|
|
325
|
-
```
|
|
326
|
-
|
|
327
|
-
### `TPS.to(targetCalendar: CalendarCode, tpsString: string): string | null`
|
|
328
|
-
|
|
329
|
-
Converts a TPS string from one calendar to another using registered drivers.
|
|
330
|
-
|
|
331
|
-
```ts
|
|
332
|
-
const gregTime = "T:greg.m3.c1.y26.M01.d07";
|
|
333
|
-
const hijriTime = TPS.to("hij", gregTime);
|
|
334
|
-
// Requires registered Hijri driver
|
|
335
|
-
```
|
|
336
|
-
|
|
337
|
-
### `TPS.registerDriver(driver: CalendarDriver): void`
|
|
338
|
-
|
|
339
|
-
Registers a custom calendar driver plugin.
|
|
340
|
-
|
|
341
|
-
```ts
|
|
342
|
-
const hijriDriver = new HijriDriver();
|
|
343
|
-
TPS.registerDriver(hijriDriver);
|
|
344
|
-
```
|
|
345
|
-
|
|
346
|
-
### `TPS.getDriver(code: CalendarCode): CalendarDriver | undefined`
|
|
347
|
-
|
|
348
|
-
Retrieves a registered calendar driver.
|
|
349
|
-
|
|
350
|
-
```ts
|
|
351
|
-
const driver = TPS.getDriver("hij");
|
|
352
|
-
if (driver) {
|
|
353
|
-
const components = driver.fromGregorian(new Date());
|
|
354
|
-
}
|
|
355
|
-
```
|
|
356
|
-
|
|
357
|
-
## 📋 Types Reference
|
|
358
|
-
|
|
359
|
-
### `TPSComponents`
|
|
360
|
-
|
|
361
|
-
```ts
|
|
362
|
-
interface TPSComponents {
|
|
363
|
-
// Temporal
|
|
364
|
-
calendar: CalendarCode;
|
|
365
|
-
millennium?: number;
|
|
366
|
-
century?: number;
|
|
367
|
-
year?: number;
|
|
368
|
-
month?: number;
|
|
369
|
-
day?: number;
|
|
370
|
-
hour?: number;
|
|
371
|
-
minute?: number;
|
|
372
|
-
second?: number;
|
|
373
|
-
unixSeconds?: number;
|
|
374
|
-
|
|
375
|
-
// Spatial
|
|
376
|
-
latitude?: number;
|
|
377
|
-
longitude?: number;
|
|
378
|
-
altitude?: number;
|
|
379
|
-
|
|
380
|
-
// Location Privacy Flags
|
|
381
|
-
isUnknownLocation?: boolean;
|
|
382
|
-
isRedactedLocation?: boolean;
|
|
383
|
-
isHiddenLocation?: boolean;
|
|
384
|
-
|
|
385
|
-
// Context
|
|
386
|
-
extensions?: Record<string, string>;
|
|
387
|
-
}
|
|
388
|
-
```
|
|
389
|
-
|
|
390
|
-
### `CalendarCode`
|
|
391
|
-
|
|
392
|
-
```ts
|
|
393
|
-
type CalendarCode = "greg" | "hij" | "jul" | "holo" | "unix";
|
|
394
|
-
```
|
|
395
|
-
|
|
396
|
-
## 🆔 TPS-UID — Temporal Positioning Identifier
|
|
397
|
-
|
|
398
|
-
TPS-UID is a **time-first, reversible identifier** format that binds an event to a TPS coordinate. Unlike UUIDs which identify records, TPS-UID identifies **events in spacetime** and allows exact reconstruction of the original TPS string.
|
|
399
|
-
|
|
400
|
-
### Why TPS-UID?
|
|
401
|
-
|
|
402
|
-
| Feature | UUID v4/v7 | TPS-UID |
|
|
403
|
-
| ----------------- | ---------------- | --------------- |
|
|
404
|
-
| **Purpose** | Identify objects | Identify events |
|
|
405
|
-
| **Time** | Optional/weak | Mandatory |
|
|
406
|
-
| **Reversible** | ❌ No | ✅ Yes |
|
|
407
|
-
| **Time-sortable** | v7 only | ✅ Always |
|
|
408
|
-
| **Audit-grade** | ❌ No | ✅ Yes |
|
|
409
|
-
|
|
410
|
-
### Binary Schema
|
|
411
|
-
|
|
412
|
-
```
|
|
413
|
-
MAGIC 4 bytes "TPU7"
|
|
414
|
-
VER 1 byte 0x01
|
|
415
|
-
FLAGS 1 byte bit0 = compression
|
|
416
|
-
TIME 6 bytes 48-bit epoch ms
|
|
417
|
-
NONCE 4 bytes collision guard
|
|
418
|
-
LEN varint payload length
|
|
419
|
-
TPS bytes UTF-8 TPS string
|
|
420
|
-
```
|
|
421
|
-
|
|
422
|
-
### Quick Start
|
|
423
|
-
|
|
424
|
-
```ts
|
|
425
|
-
import { TPSUID7RB } from "@nextera.one/tps-standard";
|
|
426
|
-
|
|
427
|
-
// Create TPS-UID from a TPS string
|
|
428
|
-
const tps = "tps://31.95,35.91@T:greg.m3.c1.y26.M01.d09.h14.n30.s25";
|
|
429
|
-
const id = TPSUID7RB.encodeBinaryB64(tps);
|
|
430
|
-
// → "tpsuid7rb_VFBVNwEAAZujKmvo..."
|
|
431
|
-
|
|
432
|
-
// Decode back to original TPS (exact reconstruction)
|
|
433
|
-
const decoded = TPSUID7RB.decodeBinaryB64(id);
|
|
434
|
-
console.log(decoded.tps); // exact original TPS
|
|
435
|
-
console.log(decoded.epochMs); // 1767969025000
|
|
436
|
-
|
|
437
|
-
// Generate from current time
|
|
438
|
-
const generated = TPSUID7RB.generate({
|
|
439
|
-
latitude: 32.0,
|
|
440
|
-
longitude: 35.0,
|
|
441
132
|
});
|
|
442
133
|
```
|
|
443
134
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
#### `TPSUID7RB.encodeBinary(tps, opts?): Uint8Array`
|
|
447
|
-
|
|
448
|
-
Encode TPS string to binary bytes (canonical form).
|
|
135
|
+
## TPSUID7RB (Reversible Binary ID)
|
|
449
136
|
|
|
450
137
|
```ts
|
|
451
|
-
|
|
452
|
-
```
|
|
453
|
-
|
|
454
|
-
#### `TPSUID7RB.decodeBinary(bytes): TPSUID7RBDecodeResult`
|
|
455
|
-
|
|
456
|
-
Decode binary bytes back to original TPS.
|
|
457
|
-
|
|
458
|
-
```ts
|
|
459
|
-
const decoded = TPSUID7RB.decodeBinary(bytes);
|
|
460
|
-
// { version: 'tpsuid7rb', epochMs, compressed, nonce, tps }
|
|
461
|
-
```
|
|
462
|
-
|
|
463
|
-
#### `TPSUID7RB.encodeBinaryB64(tps, opts?): string`
|
|
138
|
+
import { TPSUID7RB } from "@nextera.one/tps-standard";
|
|
464
139
|
|
|
465
|
-
|
|
140
|
+
const tps = "tps://node:api-1@T:greg.m3.c1.y26.m01.d09.h14.m30.s25.m0";
|
|
466
141
|
|
|
467
|
-
```ts
|
|
468
142
|
const id = TPSUID7RB.encodeBinaryB64(tps, { compress: true });
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
#### `TPSUID7RB.decodeBinaryB64(id): TPSUID7RBDecodeResult`
|
|
473
|
-
|
|
474
|
-
Decode base64url string back to original TPS.
|
|
475
|
-
|
|
476
|
-
```ts
|
|
477
|
-
const decoded = TPSUID7RB.decodeBinaryB64(id);
|
|
478
|
-
console.log(decoded.tps); // exact original
|
|
143
|
+
const out = TPSUID7RB.decodeBinaryB64(id);
|
|
144
|
+
// out.tps === tps
|
|
479
145
|
```
|
|
480
146
|
|
|
481
|
-
|
|
147
|
+
## Scripts
|
|
482
148
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
TPSUID7RB.validateBinaryB64("tpsuid7rb_VFB..."); // true
|
|
487
|
-
TPSUID7RB.validateBinaryB64("invalid"); // false
|
|
488
|
-
```
|
|
489
|
-
|
|
490
|
-
#### `TPSUID7RB.generate(opts?): string`
|
|
491
|
-
|
|
492
|
-
Generate TPS-UID from current time.
|
|
493
|
-
|
|
494
|
-
```ts
|
|
495
|
-
const id = TPSUID7RB.generate({
|
|
496
|
-
latitude: 32.0,
|
|
497
|
-
longitude: 35.0,
|
|
498
|
-
compress: true,
|
|
499
|
-
});
|
|
500
|
-
```
|
|
501
|
-
|
|
502
|
-
### Database Schema (Recommended)
|
|
503
|
-
|
|
504
|
-
```sql
|
|
505
|
-
CREATE TABLE events (
|
|
506
|
-
epoch_ms BIGINT NOT NULL,
|
|
507
|
-
tps_uid VARBINARY(96) NOT NULL,
|
|
508
|
-
tps TEXT NOT NULL,
|
|
509
|
-
PRIMARY KEY (epoch_ms, tps_uid)
|
|
510
|
-
);
|
|
149
|
+
```bash
|
|
150
|
+
npm run build
|
|
151
|
+
npm run tests
|
|
511
152
|
```
|
|
512
153
|
|
|
513
|
-
##
|
|
514
|
-
|
|
515
|
-
- **Audit Trails:** Immutable timestamp logs with explicit calendar
|
|
516
|
-
- **Distributed Systems:** Coordinate events across timezones
|
|
517
|
-
- **Security & Forensics:** Evidence timestamps with location binding
|
|
518
|
-
- **AI Agents:** Machine-readable event metadata
|
|
519
|
-
- **Long-term Archives:** Deterministic time encoding for historical records
|
|
520
|
-
|
|
521
|
-
## 🏗️ Design Principles
|
|
522
|
-
|
|
523
|
-
- **No Assumptions:** Explicit calendar, timezone, precision
|
|
524
|
-
- **Deterministic Parsing:** Same input always produces same output
|
|
525
|
-
- **Human Auditable:** Readable hierarchical structure
|
|
526
|
-
- **Machine Native:** Easy regex and parsing
|
|
527
|
-
- **Backward Compatible:** Partial coordinates are valid
|
|
528
|
-
- **Privacy First:** Built-in location masking
|
|
529
|
-
|
|
530
|
-
## 📄 License
|
|
154
|
+
## License
|
|
531
155
|
|
|
532
|
-
Apache-2.0
|
|
156
|
+
Apache-2.0
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { CalendarDriver, TPSComponents, CalendarMetadata } from '../index';
|
|
2
|
+
/**
|
|
3
|
+
* Gregorian calendar driver.
|
|
4
|
+
* This mirrors the built-in logic that used to live in `TPS.fromDate`/`toDate`
|
|
5
|
+
* and provides implementations for the full `CalendarDriver` interface.
|
|
6
|
+
* The driver also implements the optional helpers, enabling unit tests to
|
|
7
|
+
* exercise `parseDate`, `format`, `validate`, and `getMetadata`.
|
|
8
|
+
*/
|
|
9
|
+
export declare class GregorianDriver implements CalendarDriver {
|
|
10
|
+
readonly code: string;
|
|
11
|
+
getComponentsFromDate(date: Date): Partial<TPSComponents>;
|
|
12
|
+
getDateFromComponents(components: Partial<TPSComponents>): Date;
|
|
13
|
+
getFromDate(date: Date): string;
|
|
14
|
+
parseDate(input: string, format?: string): Partial<TPSComponents>;
|
|
15
|
+
format(components: Partial<TPSComponents>, format?: string): string;
|
|
16
|
+
validate(input: string | Partial<TPSComponents>): boolean;
|
|
17
|
+
getMetadata(): CalendarMetadata;
|
|
18
|
+
}
|