@nextera.one/tps-standard 0.5.2 → 0.5.33

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.
@@ -12,12 +12,12 @@
12
12
  * 3. Convert day-of-year to TPS month/day (each month = 28 days)
13
13
  * 4. Preserve millennium/century/year structure
14
14
  */
15
- import { CalendarDriver, CalendarMetadata, TPSComponents, TPS } from '../index';
16
- import { GregorianDriver } from './gregorian';
15
+ import { CalendarDriver, CalendarMetadata, TPSComponents, TPS } from "../index";
16
+ import { GregorianDriver } from "./gregorian";
17
17
 
18
18
  export class TpsDriver implements CalendarDriver {
19
- readonly code = 'tps';
20
- readonly name = 'TPS Canonical';
19
+ readonly code = "tps";
20
+ readonly name = "TPS Canonical";
21
21
 
22
22
  // TPS Epoch: August 11, 1999, 00:00 UTC
23
23
  private readonly TPS_EPOCH = new Date(Date.UTC(1999, 7, 11, 0, 0, 0, 0));
@@ -45,7 +45,7 @@ export class TpsDriver implements CalendarDriver {
45
45
  // Calculate day-of-year (0-indexed) for the offset date
46
46
  const yearStart = new Date(Date.UTC(offsetDate.getUTCFullYear(), 0, 1));
47
47
  const dayOfYear = Math.floor(
48
- (offsetDate.getTime() - yearStart.getTime()) / (24 * 60 * 60 * 1000)
48
+ (offsetDate.getTime() - yearStart.getTime()) / (24 * 60 * 60 * 1000),
49
49
  );
50
50
 
51
51
  // Convert day-of-year to TPS month/day (each month = 28 days)
@@ -72,14 +72,14 @@ export class TpsDriver implements CalendarDriver {
72
72
  */
73
73
  getDateFromComponents(components: Partial<TPSComponents>): Date {
74
74
  // Convert TPS month/day (28-day months) to day-of-year (0-indexed)
75
- const tpsMonth = components.month || 1;
76
- const tpsDay = components.day || 1;
75
+ const tpsMonth = components.month ?? 1;
76
+ const tpsDay = components.day ?? 1;
77
77
  const dayOfYear = (tpsMonth - 1) * this.TPS_DAYS_PER_MONTH + (tpsDay - 1);
78
78
 
79
79
  // Reconstruct full Gregorian year from millennium/century/year
80
- const m = components.millennium || 1;
81
- const c = components.century || 1;
82
- const y = components.year || 0;
80
+ const m = components.millennium ?? 0;
81
+ const c = components.century ?? 1;
82
+ const y = components.year ?? 0;
83
83
  const fullYear = (m - 1) * 1000 + (c - 1) * 100 + y;
84
84
 
85
85
  // Create date at start of year and add day-of-year offset
@@ -87,10 +87,10 @@ export class TpsDriver implements CalendarDriver {
87
87
  dateInYear.setUTCDate(dateInYear.getUTCDate() + dayOfYear);
88
88
 
89
89
  // Set time components
90
- dateInYear.setUTCHours(components.hour || 0);
91
- dateInYear.setUTCMinutes(components.minute || 0);
92
- dateInYear.setUTCSeconds(components.second || 0);
93
- dateInYear.setUTCMilliseconds(components.millisecond || 0);
90
+ dateInYear.setUTCHours(components.hour ?? 0);
91
+ dateInYear.setUTCMinutes(components.minute ?? 0);
92
+ dateInYear.setUTCSeconds(components.second ?? 0);
93
+ dateInYear.setUTCMilliseconds(components.millisecond ?? 0);
94
94
 
95
95
  // Remove 7-hour TPS offset to get back to Gregorian
96
96
  const offsetMillis = this.TPS_OFFSET_HOURS * 60 * 60 * 1000;
@@ -103,12 +103,14 @@ export class TpsDriver implements CalendarDriver {
103
103
  }
104
104
 
105
105
  /**
106
- * Parse a TPS date string: "YYYY-MM-DD" where MM is 01-13, DD is 01-28.
106
+ * Parse a TPS date string: "YYYY-MM-DD" where MM is 01-12, DD is 01-28.
107
107
  * Optional time: "YYYY-MM-DD HH:MM:SS.mmm"
108
108
  */
109
109
  parseDate(input: string, format?: string): Partial<TPSComponents> {
110
110
  const s = input.trim();
111
- const m = s.match(/^(\d{4})-(\d{2})-(\d{2})(?:[ T](\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?)?$/);
111
+ const m = s.match(
112
+ /^(\d{4})-(\d{2})-(\d{2})(?:[ T](\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?)?$/,
113
+ );
112
114
  if (!m) {
113
115
  throw new Error(`TpsDriver.parseDate: unsupported format "${input}"`);
114
116
  }
@@ -119,16 +121,21 @@ export class TpsDriver implements CalendarDriver {
119
121
 
120
122
  // Validate TPS month/day constraints
121
123
  if (month < 1 || month > this.TPS_MONTHS_PER_YEAR) {
122
- throw new Error(`TpsDriver.parseDate: invalid TPS month ${month} (expected 1-12)`);
124
+ throw new Error(
125
+ `TpsDriver.parseDate: invalid TPS month ${month} (expected 1-12)`,
126
+ );
123
127
  }
124
128
  if (day < 1 || day > this.TPS_DAYS_PER_MONTH) {
125
- throw new Error(`TpsDriver.parseDate: invalid TPS day ${day} (expected 1-${this.TPS_DAYS_PER_MONTH})`);
129
+ throw new Error(
130
+ `TpsDriver.parseDate: invalid TPS day ${day} (expected 1-${this.TPS_DAYS_PER_MONTH})`,
131
+ );
126
132
  }
127
133
 
128
134
  const hour = m[4] !== undefined ? parseInt(m[4], 10) : undefined;
129
135
  const minute = m[5] !== undefined ? parseInt(m[5], 10) : undefined;
130
136
  const second = m[6] !== undefined ? parseInt(m[6], 10) : undefined;
131
- const millisecond = m[7] !== undefined ? parseInt((m[7] + '000').slice(0, 3), 10) : undefined;
137
+ const millisecond =
138
+ m[7] !== undefined ? parseInt((m[7] + "000").slice(0, 3), 10) : undefined;
132
139
 
133
140
  const comp: Partial<TPSComponents> = {
134
141
  calendar: this.code,
@@ -144,13 +151,22 @@ export class TpsDriver implements CalendarDriver {
144
151
  }
145
152
 
146
153
  /**
147
- * Format TPS components to "YYYY-MM-DD" where MM is 01-13, DD is 01-28.
154
+ * Format TPS components to "YYYY-MM-DD" where MM is 01-12, DD is 01-28.
148
155
  * With time: "YYYY-MM-DD THH:MM:SS.mmm"
149
156
  */
150
157
  format(components: Partial<TPSComponents>, format?: string): string {
151
- const y = components.year !== undefined ? String(components.year).padStart(4, '0') : '0000';
152
- const mo = components.month !== undefined ? String(components.month).padStart(2, '0') : '01';
153
- const d = components.day !== undefined ? String(components.day).padStart(2, '0') : '01';
158
+ const y =
159
+ components.year !== undefined
160
+ ? String(components.year).padStart(4, "0")
161
+ : "0000";
162
+ const mo =
163
+ components.month !== undefined
164
+ ? String(components.month).padStart(2, "0")
165
+ : "01";
166
+ const d =
167
+ components.day !== undefined
168
+ ? String(components.day).padStart(2, "0")
169
+ : "01";
154
170
  let out = `${y}-${mo}-${d}`;
155
171
 
156
172
  if (
@@ -159,10 +175,22 @@ export class TpsDriver implements CalendarDriver {
159
175
  components.second !== undefined ||
160
176
  components.millisecond !== undefined
161
177
  ) {
162
- const h = components.hour !== undefined ? String(components.hour).padStart(2, '0') : '00';
163
- const mi = components.minute !== undefined ? String(components.minute).padStart(2, '0') : '00';
164
- const s = components.second !== undefined ? String(Math.floor(components.second)).padStart(2, '0') : '00';
165
- const ms = components.millisecond !== undefined ? String(components.millisecond).padStart(3, '0') : '000';
178
+ const h =
179
+ components.hour !== undefined
180
+ ? String(components.hour).padStart(2, "0")
181
+ : "00";
182
+ const mi =
183
+ components.minute !== undefined
184
+ ? String(components.minute).padStart(2, "0")
185
+ : "00";
186
+ const s =
187
+ components.second !== undefined
188
+ ? String(Math.floor(components.second)).padStart(2, "0")
189
+ : "00";
190
+ const ms =
191
+ components.millisecond !== undefined
192
+ ? String(components.millisecond).padStart(3, "0")
193
+ : "000";
166
194
  out += `T${h}:${mi}:${s}.${ms}`;
167
195
  }
168
196
  return out;
@@ -170,11 +198,14 @@ export class TpsDriver implements CalendarDriver {
170
198
 
171
199
  /**
172
200
  * Validate TPS date string or components.
173
- * TPS has months 1-13, each with 28 days.
201
+ * TPS has months 1-12, each with 28 days.
174
202
  */
175
203
  validate(input: string | Partial<TPSComponents>): boolean {
176
- if (typeof input === 'string') {
177
- const valid = /^\d{4}-\d{2}-\d{2}(?:[ T]\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?)?$/.test(input.trim());
204
+ if (typeof input === "string") {
205
+ const valid =
206
+ /^\d{4}-\d{2}-\d{2}(?:[ T]\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?)?$/.test(
207
+ input.trim(),
208
+ );
178
209
  if (!valid) return false;
179
210
 
180
211
  // Parse and validate constraints
@@ -186,7 +217,7 @@ export class TpsDriver implements CalendarDriver {
186
217
  }
187
218
  }
188
219
 
189
- if (typeof input === 'object') {
220
+ if (typeof input === "object") {
190
221
  return (
191
222
  input.year !== undefined &&
192
223
  input.month !== undefined &&
@@ -207,29 +238,29 @@ export class TpsDriver implements CalendarDriver {
207
238
  */
208
239
  getMetadata(): CalendarMetadata {
209
240
  return {
210
- name: 'TPS Canonical (28-day months)',
241
+ name: "TPS Canonical (28-day months)",
211
242
  monthNames: [
212
- 'Month 1',
213
- 'Month 2',
214
- 'Month 3',
215
- 'Month 4',
216
- 'Month 5',
217
- 'Month 6',
218
- 'Month 7',
219
- 'Month 8',
220
- 'Month 9',
221
- 'Month 10',
222
- 'Month 11',
223
- 'Month 12',
243
+ "Month 1",
244
+ "Month 2",
245
+ "Month 3",
246
+ "Month 4",
247
+ "Month 5",
248
+ "Month 6",
249
+ "Month 7",
250
+ "Month 8",
251
+ "Month 9",
252
+ "Month 10",
253
+ "Month 11",
254
+ "Month 12",
224
255
  ],
225
256
  dayNames: [
226
- 'Sunday',
227
- 'Monday',
228
- 'Tuesday',
229
- 'Wednesday',
230
- 'Thursday',
231
- 'Friday',
232
- 'Saturday',
257
+ "Sunday",
258
+ "Monday",
259
+ "Tuesday",
260
+ "Wednesday",
261
+ "Thursday",
262
+ "Friday",
263
+ "Saturday",
233
264
  ],
234
265
  monthsPerYear: this.TPS_MONTHS_PER_YEAR,
235
266
  epochYear: 1999,
package/src/index.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  * TPS: Temporal Positioning System
3
3
  * The Universal Protocol for Space-Time Coordinates.
4
4
  * @packageDocumentation
5
- * @version 0.5.0
5
+ * @version 0.5.2
6
6
  * @license Apache-2.0
7
7
  * @copyright 2026 TPS Standards Working Group
8
8
  *
@@ -18,6 +18,10 @@
18
18
  import { GregorianDriver } from "./drivers/gregorian";
19
19
  import { UnixDriver } from "./drivers/unix";
20
20
  import { TpsDriver } from "./drivers/tps";
21
+ import { PersianDriver } from "./drivers/persian";
22
+ import { HijriDriver } from "./drivers/hijri";
23
+ import { JulianDriver } from "./drivers/julian";
24
+ import { HoloceneDriver } from "./drivers/holocene";
21
25
 
22
26
  // Calendar codes are plain strings to allow arbitrary user-defined
23
27
  // calendars. The library still exports constants for the built-in values but
@@ -26,6 +30,7 @@ export const DefaultCalendars = {
26
30
  TPS: "tps",
27
31
  GREG: "greg",
28
32
  HIJ: "hij",
33
+ PER: "per",
29
34
  JUL: "jul",
30
35
  HOLO: "holo",
31
36
  UNIX: "unix",
@@ -402,10 +407,8 @@ export class TPS {
402
407
  }
403
408
 
404
409
  // No tokens at all — fill every slot with 0 and return
405
- // Use tps as the default calendar if none was specified
406
- const resolvedCal = cal || DefaultCalendars.TPS;
407
410
  if (!tokenStr) {
408
- return `${beforeT}T:${resolvedCal}.m0.c0.y0.m0.d0.h0.m0.s0.m0${timeSuffix}`;
411
+ return `${beforeT}T:${cal}.m0.c0.y0.m0.d0.h0.m0.s0.m0${timeSuffix}`;
409
412
  }
410
413
 
411
414
  // ── 5. Tokenise ──────────────────────────────────────────────────────────
@@ -464,7 +467,7 @@ export class TPS {
464
467
  .map(([p, r]) => p + (byRank.get(r) ?? "0"))
465
468
  .join(".");
466
469
 
467
- return `${beforeT}T:${resolvedCal}.${finalTokenStr}${timeSuffix}`;
470
+ return `${beforeT}T:${cal}.${finalTokenStr}${timeSuffix}`;
468
471
  }
469
472
 
470
473
  static validate(input: string): boolean {
@@ -1107,6 +1110,10 @@ export class TPS {
1107
1110
  TPS.registerDriver(new TpsDriver());
1108
1111
  TPS.registerDriver(new GregorianDriver());
1109
1112
  TPS.registerDriver(new UnixDriver());
1113
+ TPS.registerDriver(new PersianDriver());
1114
+ TPS.registerDriver(new HijriDriver());
1115
+ TPS.registerDriver(new JulianDriver());
1116
+ TPS.registerDriver(new HoloceneDriver());
1110
1117
 
1111
1118
  // --- TPS-UID v1 Types ---
1112
1119
 
@@ -1400,45 +1407,6 @@ export class TPSUID7RB {
1400
1407
  /**
1401
1408
  * Generate a TPS string from a Date and optional location.
1402
1409
  */
1403
- // NOTE: this helper is primarily used by `generate()`; drivers and
1404
- // callers should prefer `TPS.fromDate()` when order or calendars matter.
1405
- private static generateTPSString(
1406
- date: Date,
1407
- opts?: {
1408
- latitude?: number;
1409
- longitude?: number;
1410
- altitude?: number;
1411
- order?: TimeOrder;
1412
- },
1413
- ): string {
1414
- const fullYear = date.getUTCFullYear();
1415
- const comp: TPSComponents = {
1416
- calendar: DefaultCalendars.TPS,
1417
- millennium: Math.floor(fullYear / 1000) + 1,
1418
- century: Math.floor((fullYear % 1000) / 100) + 1,
1419
- year: fullYear % 100,
1420
- month: date.getUTCMonth() + 1,
1421
- day: date.getUTCDate(),
1422
- hour: date.getUTCHours(),
1423
- minute: date.getUTCMinutes(),
1424
- second: date.getUTCSeconds(),
1425
- millisecond: date.getUTCMilliseconds(),
1426
- };
1427
- if (opts?.order) comp.order = opts.order;
1428
-
1429
- // note: this method belongs to TPSUID7RB, but buildTimePart lives on TPS
1430
- const timePart = TPS.buildTimePart(comp);
1431
-
1432
- let spacePart = "unknown";
1433
- if (opts?.latitude !== undefined && opts?.longitude !== undefined) {
1434
- spacePart = `${opts.latitude},${opts.longitude}`;
1435
- if (opts.altitude !== undefined) {
1436
- spacePart += `,${opts.altitude}m`;
1437
- }
1438
- }
1439
-
1440
- return `tps://${spacePart}@${timePart}`;
1441
- }
1442
1410
 
1443
1411
  /**
1444
1412
  * Parse epoch milliseconds from a TPS string.
@@ -1785,46 +1753,38 @@ export class TPSUID7RB {
1785
1753
  try {
1786
1754
  // eslint-disable-next-line @typescript-eslint/no-require-imports
1787
1755
  const crypto = require("crypto");
1788
- // Node's crypto.sign uses PEM or KeyObject, but for raw Ed25519 keys we might need 'crypto.sign(null, data, key)'
1789
- // or ensure key is properly formatted.
1790
- // For simplicity in Node 20+, crypto.sign(null, data, privateKey) works if key is KeyObject.
1791
- // If raw bytes: establish KeyObject.
1792
-
1793
- let keyObj;
1794
- if (Buffer.isBuffer(privateKey) || privateKey instanceof Uint8Array) {
1795
- // Assuming raw 64-byte private key (or 32-byte seed properly expanded by crypto)
1796
- // Node < 16 is tricky with raw keys.
1797
- // Let's assume standard Ed25519 standard implementation pattern logic:
1798
- keyObj = crypto.createPrivateKey({
1799
- key: Buffer.from(privateKey),
1800
- format: "der", // or 'pem' - strict.
1756
+
1757
+ let key: any;
1758
+ if (typeof privateKey === "string") {
1759
+ if (privateKey.includes("PRIVATE KEY")) {
1760
+ // PEM format — use directly
1761
+ key = privateKey;
1762
+ } else {
1763
+ // Hex-encoded DER/PKCS8
1764
+ key = crypto.createPrivateKey({
1765
+ key: Buffer.from(privateKey, "hex"),
1766
+ format: "der",
1767
+ type: "pkcs8",
1768
+ });
1769
+ }
1770
+ } else if (
1771
+ typeof privateKey === "object" &&
1772
+ privateKey !== null &&
1773
+ "asymmetricKeyType" in privateKey
1774
+ ) {
1775
+ // Node.js KeyObject (e.g. from crypto.generateKeyPairSync)
1776
+ key = privateKey;
1777
+ } else {
1778
+ // Buffer or Uint8Array — assume DER/PKCS8 encoded
1779
+ key = crypto.createPrivateKey({
1780
+ key: Buffer.from(privateKey as Uint8Array),
1781
+ format: "der",
1801
1782
  type: "pkcs8",
1802
1783
  });
1803
- // Actually, simpler: construct key object from raw bytes if possible?
1804
- // Node's crypto is strict. Let's try the simplest:
1805
- // If hex string provided, convert to buffer.
1806
1784
  }
1807
1785
 
1808
- // Simpler fallback: If user passed a PEM string, great.
1809
- // If they passed raw bytes, we might need 'ed25519' key type.
1810
- // For this implementation, let's target Node's high-level sign/verify
1811
- // and assume the user provides a VALID key object or compatible format (PEM/DER).
1812
- // Handling RAW Ed25519 keys in Node requires specific 'crypto.createPrivateKey' with 'raw' format (Node 11.6+).
1813
-
1814
- const key =
1815
- typeof privateKey === "string" && !privateKey.includes("PRIVATE KEY")
1816
- ? crypto.createPrivateKey({
1817
- key: Buffer.from(privateKey, "hex"),
1818
- format: "pem",
1819
- type: "pkcs8",
1820
- }) // Fallback guess
1821
- : privateKey;
1822
-
1823
- // Note: Raw Ed25519 key support in Node.js 'crypto' acts via 'generateKeyPair' or KeyObject.
1824
- // Direct raw signing is via crypto.sign(null, data, key).
1825
1786
  return new Uint8Array(crypto.sign(null, data, key));
1826
1787
  } catch (e) {
1827
- // If standard crypto fails (e.g. key format issue), throw
1828
1788
  throw new Error("TPSUID7RB: signing failed (check key format)");
1829
1789
  }
1830
1790
  }