@nextera.one/tps-standard 0.4.3 → 0.5.1

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 ADDED
@@ -0,0 +1,192 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ Copyright 2026 TPS Standards Working Group
181
+
182
+ Licensed under the Apache License, Version 2.0 (the "License");
183
+ you may not use this file except in compliance with the License.
184
+ You may obtain a copy of the License at
185
+
186
+ http://www.apache.org/licenses/LICENSE-2.0
187
+
188
+ Unless required by applicable law or agreed to in writing, software
189
+ distributed under the License is distributed on an "AS IS" BASIS,
190
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
191
+ See the License for the specific language governing permissions and
192
+ limitations under the License.
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # TPS — Temporal Positioning Standard
2
2
 
3
+ ![TPS — Temporal Positioning Standard](assets/tps-banner.svg)
4
+
3
5
  > **A universal coordinate system for Space, Time, and Context.**
4
6
 
5
7
  TPS defines a deterministic way to represent **when** something happened, **where** it happened, and **under which calendar**, using a single, machine-readable identifier.
@@ -7,7 +9,7 @@ TPS defines a deterministic way to represent **when** something happened, **wher
7
9
  ## 📦 Installation
8
10
 
9
11
  ```bash
10
- npm install tps-standard
12
+ npm i @nextera.one/tps-standard
11
13
  ```
12
14
 
13
15
  ## 🚀 Quick Start
@@ -15,27 +17,27 @@ npm install tps-standard
15
17
  ### Basic Example
16
18
 
17
19
  ```ts
18
- import { TPS } from 'tps-standard';
20
+ import { TPS } from "@nextera.one/tps-standard";
19
21
 
20
22
  // Create a TPS time string from current date
21
- const nowTime = TPS.fromDate(new Date(), 'greg');
23
+ const nowTime = TPS.fromDate(new Date(), "greg");
22
24
  console.log(nowTime);
23
25
  // Output: "T:greg.m3.c1.y26.M01.d07.h13.n20.s45"
24
26
 
25
27
  // Parse a full TPS URI with location and extensions
26
- const uri = 'tps://31.95,35.91,800@T:greg.m3.c1.y26.M01.d07.h13.n20;f4;r7';
28
+ const uri = "tps://31.95,35.91,800m@T:greg.m3.c1.y26.M01.d07.h13.n20;f4.r7";
27
29
  const parsed = TPS.parse(uri);
28
30
  console.log(parsed);
29
31
  // { latitude: 31.95, longitude: 35.91, altitude: 800, calendar: 'greg', year: 26, ... }
30
32
 
31
33
  // Convert back to URI
32
34
  const components = {
33
- calendar: 'greg',
35
+ calendar: "greg",
34
36
  year: 26,
35
37
  month: 1,
36
38
  day: 7,
37
39
  latitude: 31.95,
38
- longitude: 35.91
40
+ longitude: 35.91,
39
41
  };
40
42
  const uriString = TPS.toURI(components);
41
43
  console.log(uriString);
@@ -52,34 +54,37 @@ TPS represents time as a coordinate using this hierarchy:
52
54
  T:greg.m3.c1.y26.M01.d07.h13.n20.s45
53
55
  ```
54
56
 
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 |
57
+ | Component | Meaning |
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 |
66
68
 
67
69
  Partial coordinates represent **time volumes** (entire year, century, etc.).
68
70
 
69
71
  ### TPS URI Format
70
72
 
71
73
  **Canonical form:**
74
+
72
75
  ```
73
76
  tps://[SPACE]@[TIME][;EXTENSIONS]
74
77
  ```
75
78
 
76
79
  #### Components
77
80
 
78
- | Component | Description |
79
- |-----------|-------------|
80
- | `SPACE` | `lat,lon[,alt]m` (WGS84) or `unknown`/`hidden`/`redacted` |
81
- | `TIME` | TPS Time format (`T:calendar.hierarchy`) |
82
- | `EXTENSIONS` | Optional context (key-value pairs separated by dots) |
81
+ | Component | Description |
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 `.` |
86
+
87
+ Extensions are encoded as compact pairs with no `=` (e.g. `;f4.r7` means `{ f: "4", r: "7" }`).
83
88
 
84
89
  #### Location Privacy
85
90
 
@@ -92,57 +97,156 @@ tps://[SPACE]@[TIME][;EXTENSIONS]
92
97
 
93
98
  - `greg` — Gregorian calendar (default)
94
99
  - `unix` — Unix epoch seconds
95
- - `hij` — Hijri (Islamic) — *requires driver*
96
- - `jul` — Julian — *requires driver*
97
- - `holo` — Holocene — *requires driver*
100
+ - `hij` — Hijri (Islamic) — _requires driver_
101
+ - `jul` — Julian — _requires driver_
102
+ - `holo` — Holocene — _requires driver_
98
103
 
99
104
  ## 🔌 Plugin Architecture
100
105
 
101
- TPS supports custom calendar drivers for non-Gregorian systems.
106
+ TPS supports custom calendar drivers for non-Gregorian systems. Drivers can wrap external date libraries (like `moment-hijri`, `@js-joda/extra`, etc.).
102
107
 
103
108
  ### CalendarDriver Interface
104
109
 
105
110
  ```ts
106
111
  export interface CalendarDriver {
107
112
  readonly code: CalendarCode;
113
+ readonly name?: string; // Optional human-readable name
114
+
115
+ // Required methods
108
116
  fromGregorian(date: Date): Partial<TPSComponents>;
109
117
  toGregorian(components: Partial<TPSComponents>): Date;
110
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;
111
125
  }
112
126
  ```
113
127
 
114
128
  ### Register a Custom Driver
115
129
 
116
130
  ```ts
117
- import { TPS, CalendarDriver, TPSComponents } from 'tps-standard';
131
+ import { TPS, CalendarDriver, TPSComponents } from "@nextera.one/tps-standard";
118
132
 
119
133
  class HijriDriver implements CalendarDriver {
120
- readonly code = 'hij';
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
+ }
121
149
 
122
150
  fromGregorian(date: Date): Partial<TPSComponents> {
123
- // Convert Gregorian date to Hijri components
124
- // ... conversion logic ...
125
- return { year: 1445, month: 7, day: 15 };
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 };
126
156
  }
127
157
 
128
158
  toGregorian(components: Partial<TPSComponents>): Date {
129
- // Convert Hijri components to Gregorian Date
130
- // ... conversion logic ...
159
+ // Reverse conversion using external library
131
160
  return new Date();
132
161
  }
133
162
 
134
163
  fromDate(date: Date): string {
135
164
  const comp = this.fromGregorian(date);
136
- return `T:hij.y${comp.year}.M${comp.month}.d${comp.day}`;
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)}`;
137
167
  }
138
168
  }
139
169
 
140
170
  // Register the driver
141
171
  TPS.registerDriver(new HijriDriver());
172
+ ```
173
+
174
+ ### Using Calendar Drivers
142
175
 
143
- // Now use it
144
- const hijriTime = TPS.fromDate(new Date(), 'hij');
145
- console.log(hijriTime);
176
+ ```ts
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 }
180
+
181
+ // Convert to TPS URI with location
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
+ ```
200
+
201
+ ### Wrapping External Libraries
202
+
203
+ Example with `moment-hijri`:
204
+
205
+ ```ts
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
+ }
146
250
  ```
147
251
 
148
252
  ## 📚 API Reference
@@ -152,8 +256,8 @@ console.log(hijriTime);
152
256
  Validates whether a string is properly formatted TPS.
153
257
 
154
258
  ```ts
155
- TPS.validate('tps://31.95,35.91@T:greg.m3.c1.y26'); // true
156
- TPS.validate('invalid'); // false
259
+ TPS.validate("tps://31.95,35.91@T:greg.m3.c1.y26"); // true
260
+ TPS.validate("invalid"); // false
157
261
  ```
158
262
 
159
263
  ### `TPS.parse(input: string): TPSComponents | null`
@@ -161,7 +265,9 @@ TPS.validate('invalid'); // false
161
265
  Parses a TPS string into components. Returns `null` if invalid.
162
266
 
163
267
  ```ts
164
- const parsed = TPS.parse('tps://31.95,35.91,800@T:greg.m3.c1.y26.M01.d07.h13.n20;f4;r7');
268
+ const parsed = TPS.parse(
269
+ "tps://31.95,35.91,800m@T:greg.m3.c1.y26.M01.d07.h13.n20;f4.r7"
270
+ );
165
271
  // {
166
272
  // latitude: 31.95,
167
273
  // longitude: 35.91,
@@ -184,14 +290,14 @@ Converts a components object into a canonical TPS URI string.
184
290
 
185
291
  ```ts
186
292
  const components = {
187
- calendar: 'greg',
293
+ calendar: "greg",
188
294
  year: 26,
189
295
  month: 1,
190
296
  day: 7,
191
297
  latitude: 31.95,
192
298
  longitude: 35.91,
193
299
  altitude: 800,
194
- extensions: { f: "4", r: "7" }
300
+ extensions: { f: "4", r: "7" },
195
301
  };
196
302
  const uri = TPS.toURI(components);
197
303
  // "tps://31.95,35.91,800m@T:greg.y26.M01.d07;f4.r7"
@@ -202,10 +308,10 @@ const uri = TPS.toURI(components);
202
308
  Generates a TPS time string from a JavaScript Date. Supports registered drivers.
203
309
 
204
310
  ```ts
205
- const timeString = TPS.fromDate(new Date(), 'greg');
311
+ const timeString = TPS.fromDate(new Date(), "greg");
206
312
  // "T:greg.m3.c1.y26.M01.d07.h13.n20.s45"
207
313
 
208
- const unixTime = TPS.fromDate(new Date(), 'unix');
314
+ const unixTime = TPS.fromDate(new Date(), "unix");
209
315
  // "T:unix.s1704729645.123"
210
316
  ```
211
317
 
@@ -214,7 +320,7 @@ const unixTime = TPS.fromDate(new Date(), 'unix');
214
320
  Converts a TPS string back to a JavaScript Date object.
215
321
 
216
322
  ```ts
217
- const date = TPS.toDate('T:greg.m3.c1.y26.M01.d07.h13.n20.s45');
323
+ const date = TPS.toDate("T:greg.m3.c1.y26.M01.d07.h13.n20.s45");
218
324
  console.log(date); // Date object for 2026-01-07 13:20:45 UTC
219
325
  ```
220
326
 
@@ -223,8 +329,8 @@ console.log(date); // Date object for 2026-01-07 13:20:45 UTC
223
329
  Converts a TPS string from one calendar to another using registered drivers.
224
330
 
225
331
  ```ts
226
- const gregTime = 'T:greg.m3.c1.y26.M01.d07';
227
- const hijriTime = TPS.to('hij', gregTime);
332
+ const gregTime = "T:greg.m3.c1.y26.M01.d07";
333
+ const hijriTime = TPS.to("hij", gregTime);
228
334
  // Requires registered Hijri driver
229
335
  ```
230
336
 
@@ -242,7 +348,7 @@ TPS.registerDriver(hijriDriver);
242
348
  Retrieves a registered calendar driver.
243
349
 
244
350
  ```ts
245
- const driver = TPS.getDriver('hij');
351
+ const driver = TPS.getDriver("hij");
246
352
  if (driver) {
247
353
  const components = driver.fromGregorian(new Date());
248
354
  }
@@ -284,7 +390,124 @@ interface TPSComponents {
284
390
  ### `CalendarCode`
285
391
 
286
392
  ```ts
287
- type CalendarCode = 'greg' | 'hij' | 'jul' | 'holo' | 'unix';
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
+ });
442
+ ```
443
+
444
+ ### API Reference
445
+
446
+ #### `TPSUID7RB.encodeBinary(tps, opts?): Uint8Array`
447
+
448
+ Encode TPS string to binary bytes (canonical form).
449
+
450
+ ```ts
451
+ const bytes = TPSUID7RB.encodeBinary(tps, { compress: true });
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`
464
+
465
+ Encode to base64url string with prefix (transport form).
466
+
467
+ ```ts
468
+ const id = TPSUID7RB.encodeBinaryB64(tps, { compress: true });
469
+ // "tpsuid7rb_..."
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
479
+ ```
480
+
481
+ #### `TPSUID7RB.validateBinaryB64(id): boolean`
482
+
483
+ Validate base64url encoded TPS-UID format.
484
+
485
+ ```ts
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
+ );
288
511
  ```
289
512
 
290
513
  ## 🎯 Use Cases
@@ -306,4 +529,4 @@ type CalendarCode = 'greg' | 'hij' | 'jul' | 'holo' | 'unix';
306
529
 
307
530
  ## 📄 License
308
531
 
309
- MIT © 2026 TPS Standards Working Group
532
+ Apache-2.0 see [LICENSE](LICENSE).