@nextera.one/tps-standard 0.5.0 → 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 +192 -0
- package/README.md +9 -5
- package/dist/index.d.ts +28 -2
- package/dist/index.js +137 -23
- package/dist/src/index.js +621 -633
- package/dist/test/src/index.js +908 -905
- package/package.json +2 -2
- package/src/index.ts +191 -47
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nextera.one/tps-standard",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "The Universal Protocol for Space-Time Coordinates. A standard URI scheme (tps://) combining WGS84 spatial data with hierarchical, multi-calendar temporal coordinates. Includes TPS-UID: time-first, reversible event identifiers.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"tps",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"type": "git",
|
|
25
25
|
"url": "git+https://github.com/nextera-one/tps.git"
|
|
26
26
|
},
|
|
27
|
-
"license": "
|
|
27
|
+
"license": "Apache-2.0",
|
|
28
28
|
"author": "Mohammed Ayesh",
|
|
29
29
|
"types": "dist/index.d.ts",
|
|
30
30
|
"main": "dist/index.js",
|
package/src/index.ts
CHANGED
|
@@ -2,9 +2,15 @@
|
|
|
2
2
|
* TPS: Temporal Positioning System
|
|
3
3
|
* The Universal Protocol for Space-Time Coordinates.
|
|
4
4
|
* @packageDocumentation
|
|
5
|
-
* @version 0.
|
|
6
|
-
* @license
|
|
5
|
+
* @version 0.5.0
|
|
6
|
+
* @license Apache-2.0
|
|
7
7
|
* @copyright 2026 TPS Standards Working Group
|
|
8
|
+
*
|
|
9
|
+
* v0.5.0 Changes:
|
|
10
|
+
* - Added Actor anchor (A:) for provenance tracking
|
|
11
|
+
* - Added Signature (!) for cryptographic verification
|
|
12
|
+
* - Added structural anchors (bldg, floor, room, zone)
|
|
13
|
+
* - Added geospatial cell systems (S2, H3, Plus Code, what3words)
|
|
8
14
|
*/
|
|
9
15
|
|
|
10
16
|
export type CalendarCode = 'greg' | 'hij' | 'jul' | 'holo' | 'unix';
|
|
@@ -22,11 +28,32 @@ export interface TPSComponents {
|
|
|
22
28
|
second?: number;
|
|
23
29
|
unixSeconds?: number;
|
|
24
30
|
|
|
25
|
-
// --- SPATIAL ---
|
|
31
|
+
// --- SPATIAL: GPS Coordinates ---
|
|
26
32
|
latitude?: number;
|
|
27
33
|
longitude?: number;
|
|
28
34
|
altitude?: number;
|
|
29
35
|
|
|
36
|
+
// --- SPATIAL: Geospatial Cells ---
|
|
37
|
+
/** Google S2 cell ID (hierarchical, prefix-searchable) */
|
|
38
|
+
s2Cell?: string;
|
|
39
|
+
/** Uber H3 cell ID (hexagonal grid) */
|
|
40
|
+
h3Cell?: string;
|
|
41
|
+
/** Open Location Code / Plus Code */
|
|
42
|
+
plusCode?: string;
|
|
43
|
+
/** what3words address (e.g. "filled.count.soap") */
|
|
44
|
+
what3words?: string;
|
|
45
|
+
|
|
46
|
+
// --- SPATIAL: Structural Anchors ---
|
|
47
|
+
/** Physical building identifier */
|
|
48
|
+
building?: string;
|
|
49
|
+
/** Vertical division (level) */
|
|
50
|
+
floor?: string;
|
|
51
|
+
/** Enclosed space identifier */
|
|
52
|
+
room?: string;
|
|
53
|
+
/** Logical area within building */
|
|
54
|
+
zone?: string;
|
|
55
|
+
|
|
56
|
+
// --- SPATIAL: Privacy Markers ---
|
|
30
57
|
/** Technical missing data (e.g. server log without GPS) */
|
|
31
58
|
isUnknownLocation?: boolean;
|
|
32
59
|
/** Removed for legal/security reasons (e.g. GDPR) */
|
|
@@ -34,6 +61,12 @@ export interface TPSComponents {
|
|
|
34
61
|
/** Masked by user preference (e.g. "Don't show my location") */
|
|
35
62
|
isHiddenLocation?: boolean;
|
|
36
63
|
|
|
64
|
+
// --- PROVENANCE ---
|
|
65
|
+
/** Actor anchor - identifies observer/witness (e.g. "did:web:sensor.example.com", "node:gateway-01") */
|
|
66
|
+
actor?: string;
|
|
67
|
+
/** Verification hash appended to time (e.g. "sha256:8f3e2a...") */
|
|
68
|
+
signature?: string;
|
|
69
|
+
|
|
37
70
|
// --- CONTEXT ---
|
|
38
71
|
extensions?: Record<string, string>;
|
|
39
72
|
}
|
|
@@ -229,12 +262,40 @@ export class TPS {
|
|
|
229
262
|
}
|
|
230
263
|
|
|
231
264
|
// --- REGEX ---
|
|
265
|
+
// Updated for v0.5.0: supports L: anchors, A: actor, ! signature, structural & geospatial anchors
|
|
266
|
+
// Note: Complex regex - carefully balanced parentheses
|
|
232
267
|
private static readonly REGEX_URI = new RegExp(
|
|
233
|
-
'^tps://
|
|
268
|
+
'^tps://' +
|
|
269
|
+
// Location part (L: prefix optional for backward compat)
|
|
270
|
+
'(?:L:)?(?<space>' +
|
|
271
|
+
'~|-|unknown|redacted|hidden|' + // Privacy markers
|
|
272
|
+
's2=(?<s2>[a-fA-F0-9]+)|' + // S2 cell
|
|
273
|
+
'h3=(?<h3>[a-fA-F0-9]+)|' + // H3 cell
|
|
274
|
+
'plus=(?<plus>[A-Z0-9+]+)|' + // Plus Code
|
|
275
|
+
'w3w=(?<w3w>[a-z]+\\.[a-z]+\\.[a-z]+)|' + // what3words
|
|
276
|
+
'bldg=(?<bldg>[\\w-]+)(?:\\.floor=(?<floor>[\\w-]+))?(?:\\.room=(?<room>[\\w-]+))?(?:\\.zone=(?<zone>[\\w-]+))?|' + // Structural
|
|
277
|
+
'(?<lat>-?\\d+(?:\\.\\d+)?),(?<lon>-?\\d+(?:\\.\\d+)?)(?:,(?<alt>-?\\d+(?:\\.\\d+)?)m?)?' + // GPS
|
|
278
|
+
')' +
|
|
279
|
+
// Optional Actor anchor
|
|
280
|
+
'(?:/A:(?<actor>[^/@]+))?' +
|
|
281
|
+
// Time part separator
|
|
282
|
+
'[/@]T:(?<calendar>[a-z]{3,4})\\.' +
|
|
283
|
+
// Time components
|
|
284
|
+
'(?:(?<unix>s\\d+(?:\\.\\d+)?)|m(?<millennium>-?\\d+)(?:\\.c(?<century>\\d+)(?:\\.y(?<year>\\d+)(?:\\.M(?<month>\\d{1,2})(?:\\.d(?<day>\\d{1,2})(?:\\.h(?<hour>\\d{1,2})(?:\\.n(?<minute>\\d{1,2})(?:\\.s(?<second>\\d{1,2}(?:\\.\\d+)?))?)?)?)?)?)?)?)' +
|
|
285
|
+
// Optional signature
|
|
286
|
+
'(?:!(?<signature>[^;?#]+))?' +
|
|
287
|
+
// Optional extensions
|
|
288
|
+
'(?:;(?<extensions>[a-z0-9.\\-_=]+))?' +
|
|
289
|
+
// Optional query params
|
|
290
|
+
'(?:\\?(?<params>[^#]+))?' +
|
|
291
|
+
// Optional context
|
|
292
|
+
'(?:#(?<context>.+))?$',
|
|
234
293
|
);
|
|
235
294
|
|
|
236
295
|
private static readonly REGEX_TIME = new RegExp(
|
|
237
|
-
'^T:(?<calendar>[a-z]{3,4})\\.
|
|
296
|
+
'^T:(?<calendar>[a-z]{3,4})\\.' +
|
|
297
|
+
'(?:(?<unix>s\\d+(?:\\.\\d+)?)|m(?<millennium>-?\\d+)(?:\\.c(?<century>\\d+)(?:\\.y(?<year>\\d+)(?:\\.M(?<month>\\d{1,2})(?:\\.d(?<day>\\d{1,2})(?:\\.h(?<hour>\\d{1,2})(?:\\.n(?<minute>\\d{1,2})(?:\\.s(?<second>\\d{1,2}(?:\\.\\d+)?))?)?)?)?)?)?)?)' +
|
|
298
|
+
'(?:!(?<signature>[^;?#]+))?$',
|
|
238
299
|
);
|
|
239
300
|
|
|
240
301
|
// --- CORE METHODS ---
|
|
@@ -261,23 +322,42 @@ export class TPS {
|
|
|
261
322
|
* @returns Full URI string (e.g. "tps://...").
|
|
262
323
|
*/
|
|
263
324
|
static toURI(comp: TPSComponents): string {
|
|
264
|
-
// 1. Build Space Part
|
|
265
|
-
let spacePart = '
|
|
325
|
+
// 1. Build Space Part (L: anchor)
|
|
326
|
+
let spacePart = 'L:-'; // Default: unknown
|
|
266
327
|
|
|
267
328
|
if (comp.isHiddenLocation) {
|
|
268
|
-
spacePart = '
|
|
329
|
+
spacePart = 'L:~';
|
|
269
330
|
} else if (comp.isRedactedLocation) {
|
|
270
|
-
spacePart = 'redacted';
|
|
331
|
+
spacePart = 'L:redacted';
|
|
271
332
|
} else if (comp.isUnknownLocation) {
|
|
272
|
-
spacePart = '
|
|
333
|
+
spacePart = 'L:-';
|
|
334
|
+
} else if (comp.s2Cell) {
|
|
335
|
+
spacePart = `L:s2=${comp.s2Cell}`;
|
|
336
|
+
} else if (comp.h3Cell) {
|
|
337
|
+
spacePart = `L:h3=${comp.h3Cell}`;
|
|
338
|
+
} else if (comp.plusCode) {
|
|
339
|
+
spacePart = `L:plus=${comp.plusCode}`;
|
|
340
|
+
} else if (comp.what3words) {
|
|
341
|
+
spacePart = `L:w3w=${comp.what3words}`;
|
|
342
|
+
} else if (comp.building) {
|
|
343
|
+
spacePart = `L:bldg=${comp.building}`;
|
|
344
|
+
if (comp.floor) spacePart += `.floor=${comp.floor}`;
|
|
345
|
+
if (comp.room) spacePart += `.room=${comp.room}`;
|
|
346
|
+
if (comp.zone) spacePart += `.zone=${comp.zone}`;
|
|
273
347
|
} else if (comp.latitude !== undefined && comp.longitude !== undefined) {
|
|
274
|
-
spacePart =
|
|
348
|
+
spacePart = `L:${comp.latitude},${comp.longitude}`;
|
|
275
349
|
if (comp.altitude !== undefined) {
|
|
276
350
|
spacePart += `,${comp.altitude}m`;
|
|
277
351
|
}
|
|
278
352
|
}
|
|
279
353
|
|
|
280
|
-
// 2. Build
|
|
354
|
+
// 2. Build Actor Part (A: anchor) - optional
|
|
355
|
+
let actorPart = '';
|
|
356
|
+
if (comp.actor) {
|
|
357
|
+
actorPart = `/A:${comp.actor}`;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// 3. Build Time Part
|
|
281
361
|
let timePart = `T:${comp.calendar}`;
|
|
282
362
|
|
|
283
363
|
if (comp.calendar === 'unix' && comp.unixSeconds !== undefined) {
|
|
@@ -293,16 +373,21 @@ export class TPS {
|
|
|
293
373
|
if (comp.second !== undefined) timePart += `.s${this.pad(comp.second)}`;
|
|
294
374
|
}
|
|
295
375
|
|
|
296
|
-
//
|
|
376
|
+
// 4. Add Signature (!) - optional
|
|
377
|
+
if (comp.signature) {
|
|
378
|
+
timePart += `!${comp.signature}`;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// 5. Build Extensions
|
|
297
382
|
let extPart = '';
|
|
298
383
|
if (comp.extensions && Object.keys(comp.extensions).length > 0) {
|
|
299
384
|
const extStrings = Object.entries(comp.extensions).map(
|
|
300
|
-
([k, v]) => `${k}
|
|
385
|
+
([k, v]) => `${k}=${v}`,
|
|
301
386
|
);
|
|
302
387
|
extPart = `;${extStrings.join('.')}`;
|
|
303
388
|
}
|
|
304
389
|
|
|
305
|
-
return `tps://${spacePart}
|
|
390
|
+
return `tps://${spacePart}${actorPart}/${timePart}${extPart}`;
|
|
306
391
|
}
|
|
307
392
|
|
|
308
393
|
/**
|
|
@@ -339,7 +424,9 @@ export class TPS {
|
|
|
339
424
|
const n = date.getUTCMinutes();
|
|
340
425
|
const s = date.getUTCSeconds();
|
|
341
426
|
|
|
342
|
-
return `T:greg.m${m}.c${c}.y${y}.M${this.pad(M)}.d${this.pad(
|
|
427
|
+
return `T:greg.m${m}.c${c}.y${y}.M${this.pad(M)}.d${this.pad(
|
|
428
|
+
d,
|
|
429
|
+
)}.h${this.pad(h)}.n${this.pad(n)}.s${this.pad(s)}`;
|
|
343
430
|
}
|
|
344
431
|
|
|
345
432
|
throw new Error(
|
|
@@ -547,11 +634,44 @@ export class TPS {
|
|
|
547
634
|
if (g.second) components.second = parseFloat(g.second);
|
|
548
635
|
}
|
|
549
636
|
|
|
637
|
+
// Signature Mapping
|
|
638
|
+
if (g.signature) {
|
|
639
|
+
components.signature = g.signature;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// Actor Mapping
|
|
643
|
+
if (g.actor) {
|
|
644
|
+
components.actor = g.actor;
|
|
645
|
+
}
|
|
646
|
+
|
|
550
647
|
// Space Mapping
|
|
551
648
|
if (g.space) {
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
649
|
+
// Privacy markers
|
|
650
|
+
if (g.space === 'unknown' || g.space === '-') {
|
|
651
|
+
components.isUnknownLocation = true;
|
|
652
|
+
} else if (g.space === 'redacted') {
|
|
653
|
+
components.isRedactedLocation = true;
|
|
654
|
+
} else if (g.space === 'hidden' || g.space === '~') {
|
|
655
|
+
components.isHiddenLocation = true;
|
|
656
|
+
}
|
|
657
|
+
// Geospatial cells
|
|
658
|
+
else if (g.s2) {
|
|
659
|
+
components.s2Cell = g.s2;
|
|
660
|
+
} else if (g.h3) {
|
|
661
|
+
components.h3Cell = g.h3;
|
|
662
|
+
} else if (g.plus) {
|
|
663
|
+
components.plusCode = g.plus;
|
|
664
|
+
} else if (g.w3w) {
|
|
665
|
+
components.what3words = g.w3w;
|
|
666
|
+
}
|
|
667
|
+
// Structural anchors
|
|
668
|
+
else if (g.bldg) {
|
|
669
|
+
components.building = g.bldg;
|
|
670
|
+
if (g.floor) components.floor = g.floor;
|
|
671
|
+
if (g.room) components.room = g.room;
|
|
672
|
+
if (g.zone) components.zone = g.zone;
|
|
673
|
+
}
|
|
674
|
+
// GPS coordinates
|
|
555
675
|
else {
|
|
556
676
|
if (g.lat) components.latitude = parseFloat(g.lat);
|
|
557
677
|
if (g.lon) components.longitude = parseFloat(g.lon);
|
|
@@ -564,9 +684,17 @@ export class TPS {
|
|
|
564
684
|
const extObj: any = {};
|
|
565
685
|
const parts = g.extensions.split('.');
|
|
566
686
|
parts.forEach((p: string) => {
|
|
567
|
-
const
|
|
568
|
-
|
|
569
|
-
|
|
687
|
+
const eqIdx = p.indexOf('=');
|
|
688
|
+
if (eqIdx > 0) {
|
|
689
|
+
const key = p.substring(0, eqIdx);
|
|
690
|
+
const val = p.substring(eqIdx + 1);
|
|
691
|
+
if (key && val) extObj[key] = val;
|
|
692
|
+
} else {
|
|
693
|
+
// Legacy format: first char is key
|
|
694
|
+
const key = p.charAt(0);
|
|
695
|
+
const val = p.substring(1);
|
|
696
|
+
if (key && val) extObj[key] = val;
|
|
697
|
+
}
|
|
570
698
|
});
|
|
571
699
|
components.extensions = extObj;
|
|
572
700
|
}
|
|
@@ -871,7 +999,9 @@ export class TPSUID7RB {
|
|
|
871
999
|
|
|
872
1000
|
const pad = (num: number) => num.toString().padStart(2, '0');
|
|
873
1001
|
|
|
874
|
-
const timePart = `T:greg.m${m}.c${c}.y${y}.M${pad(M)}.d${pad(d)}.h${pad(
|
|
1002
|
+
const timePart = `T:greg.m${m}.c${c}.y${y}.M${pad(M)}.d${pad(d)}.h${pad(
|
|
1003
|
+
h,
|
|
1004
|
+
)}.n${pad(n)}.s${pad(s)}`;
|
|
875
1005
|
|
|
876
1006
|
let spacePart = 'unknown';
|
|
877
1007
|
if (opts?.latitude !== undefined && opts?.longitude !== undefined) {
|
|
@@ -1160,12 +1290,16 @@ export class TPSUID7RB {
|
|
|
1160
1290
|
const content = new Uint8Array(contentLen);
|
|
1161
1291
|
let offset = 0;
|
|
1162
1292
|
|
|
1163
|
-
content.set(this.MAGIC, offset);
|
|
1293
|
+
content.set(this.MAGIC, offset);
|
|
1294
|
+
offset += 4;
|
|
1164
1295
|
content[offset++] = this.VER;
|
|
1165
1296
|
content[offset++] = flags;
|
|
1166
|
-
content.set(this.writeU48(epochMs), offset);
|
|
1167
|
-
|
|
1168
|
-
content.set(
|
|
1297
|
+
content.set(this.writeU48(epochMs), offset);
|
|
1298
|
+
offset += 6;
|
|
1299
|
+
content.set(nonceBuf, offset);
|
|
1300
|
+
offset += 4;
|
|
1301
|
+
content.set(lenVar, offset);
|
|
1302
|
+
offset += lenVar.length;
|
|
1169
1303
|
content.set(payload, offset);
|
|
1170
1304
|
|
|
1171
1305
|
// Sign the content
|
|
@@ -1215,7 +1349,10 @@ export class TPSUID7RB {
|
|
|
1215
1349
|
// We need to parse LEN and Payload to find the split point
|
|
1216
1350
|
let offset = 16; // Start of LEN
|
|
1217
1351
|
// Decode LEN
|
|
1218
|
-
const { value: tpsLen, bytesRead } = this.uvarintDecode(
|
|
1352
|
+
const { value: tpsLen, bytesRead } = this.uvarintDecode(
|
|
1353
|
+
sealedBytes,
|
|
1354
|
+
offset,
|
|
1355
|
+
);
|
|
1219
1356
|
offset += bytesRead;
|
|
1220
1357
|
const payloadEnd = offset + tpsLen;
|
|
1221
1358
|
|
|
@@ -1233,12 +1370,16 @@ export class TPSUID7RB {
|
|
|
1233
1370
|
|
|
1234
1371
|
const sealType = sealedBytes[payloadEnd];
|
|
1235
1372
|
if (sealType !== 0x01) {
|
|
1236
|
-
throw new Error(
|
|
1373
|
+
throw new Error(
|
|
1374
|
+
`TPSUID7RB: unsupported seal type 0x${sealType.toString(16)}`,
|
|
1375
|
+
);
|
|
1237
1376
|
}
|
|
1238
1377
|
|
|
1239
1378
|
const signature = sealedBytes.slice(payloadEnd + 1);
|
|
1240
1379
|
if (signature.length !== 64) {
|
|
1241
|
-
throw new Error(
|
|
1380
|
+
throw new Error(
|
|
1381
|
+
`TPSUID7RB: invalid Ed25519 signature length ${signature.length}`,
|
|
1382
|
+
);
|
|
1242
1383
|
}
|
|
1243
1384
|
|
|
1244
1385
|
// Verify
|
|
@@ -1270,20 +1411,20 @@ export class TPSUID7RB {
|
|
|
1270
1411
|
// or ensure key is properly formatted.
|
|
1271
1412
|
// For simplicity in Node 20+, crypto.sign(null, data, privateKey) works if key is KeyObject.
|
|
1272
1413
|
// If raw bytes: establish KeyObject.
|
|
1273
|
-
|
|
1414
|
+
|
|
1274
1415
|
let keyObj;
|
|
1275
1416
|
if (Buffer.isBuffer(privateKey) || privateKey instanceof Uint8Array) {
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1417
|
+
// Assuming raw 64-byte private key (or 32-byte seed properly expanded by crypto)
|
|
1418
|
+
// Node < 16 is tricky with raw keys.
|
|
1419
|
+
// Let's assume standard Ed25519 standard implementation pattern logic:
|
|
1420
|
+
keyObj = crypto.createPrivateKey({
|
|
1421
|
+
key: Buffer.from(privateKey),
|
|
1422
|
+
format: 'der', // or 'pem' - strict.
|
|
1423
|
+
type: 'pkcs8',
|
|
1424
|
+
});
|
|
1425
|
+
// Actually, simpler: construct key object from raw bytes if possible?
|
|
1426
|
+
// Node's crypto is strict. Let's try the simplest:
|
|
1427
|
+
// If hex string provided, convert to buffer.
|
|
1287
1428
|
}
|
|
1288
1429
|
|
|
1289
1430
|
// Simpler fallback: If user passed a PEM string, great.
|
|
@@ -1292,14 +1433,18 @@ export class TPSUID7RB {
|
|
|
1292
1433
|
// and assume the user provides a VALID key object or compatible format (PEM/DER).
|
|
1293
1434
|
// Handling RAW Ed25519 keys in Node requires specific 'crypto.createPrivateKey' with 'raw' format (Node 11.6+).
|
|
1294
1435
|
|
|
1295
|
-
const key =
|
|
1296
|
-
|
|
1297
|
-
|
|
1436
|
+
const key =
|
|
1437
|
+
typeof privateKey === 'string' && !privateKey.includes('PRIVATE KEY')
|
|
1438
|
+
? crypto.createPrivateKey({
|
|
1439
|
+
key: Buffer.from(privateKey, 'hex'),
|
|
1440
|
+
format: 'pem',
|
|
1441
|
+
type: 'pkcs8',
|
|
1442
|
+
}) // Fallback guess
|
|
1443
|
+
: privateKey;
|
|
1298
1444
|
|
|
1299
1445
|
// Note: Raw Ed25519 key support in Node.js 'crypto' acts via 'generateKeyPair' or KeyObject.
|
|
1300
1446
|
// Direct raw signing is via crypto.sign(null, data, key).
|
|
1301
1447
|
return new Uint8Array(crypto.sign(null, data, key));
|
|
1302
|
-
|
|
1303
1448
|
} catch (e) {
|
|
1304
1449
|
// If standard crypto fails (e.g. key format issue), throw
|
|
1305
1450
|
throw new Error('TPSUID7RB: signing failed (check key format)');
|
|
@@ -1350,4 +1495,3 @@ export class TPSUID7RB {
|
|
|
1350
1495
|
throw new Error('TPSUID7RB: no crypto available');
|
|
1351
1496
|
}
|
|
1352
1497
|
}
|
|
1353
|
-
|