@nextera.one/tps-standard 0.5.0 → 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/LICENSE +192 -0
- package/README.md +92 -464
- 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 +200 -41
- package/dist/index.js +880 -258
- package/dist/index.js.map +1 -0
- package/package.json +5 -4
- 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 +1087 -310
- package/dist/src/index.js +0 -693
- package/dist/test/src/index.js +0 -960
- 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
|
@@ -1,528 +1,156 @@
|
|
|
1
1
|
# TPS — Temporal Positioning Standard
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+

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