@ewanc26/tid 1.0.0
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 +106 -0
- package/dist/index.cjs +96 -0
- package/dist/index.d.cts +62 -0
- package/dist/index.d.ts +62 -0
- package/dist/index.js +66 -0
- package/package.json +37 -0
package/README.md
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# @malachite/tid
|
|
2
|
+
|
|
3
|
+
Zero-dependency [AT Protocol](https://atproto.com/) TID (Timestamp Identifier) generation for Node.js and browsers.
|
|
4
|
+
|
|
5
|
+
TIDs are the record keys used throughout ATProto / Bluesky — 13-character, lexicographically sortable, monotonic identifiers derived from a microsecond timestamp and a random clock ID.
|
|
6
|
+
|
|
7
|
+
## Why this package?
|
|
8
|
+
|
|
9
|
+
Other TID implementations require native bindings (`node-gyp`, Python) or pull in large dependency trees. This package is **pure JavaScript** with **no runtime dependencies**, and runs anywhere the [Web Crypto API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API) is available — Node.js 20+, Deno, Bun, and all modern browsers.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
npm install @ewanc26/tid
|
|
15
|
+
# or
|
|
16
|
+
pnpm add @ewanc26/tid
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
### Generate a TID for a historical timestamp
|
|
22
|
+
|
|
23
|
+
Pass an ISO 8601 string or a `Date` object. The clock is monotonic — if records arrive out of order, the timestamp is bumped forward so every call produces a strictly increasing TID within the same JS context.
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { generateTID } from '@malachite/tid';
|
|
27
|
+
|
|
28
|
+
// From an ISO string (e.g. a Last.fm scrobble timestamp)
|
|
29
|
+
const tid = generateTID('2023-11-01T12:00:00Z');
|
|
30
|
+
|
|
31
|
+
// From a Date object
|
|
32
|
+
const tid2 = generateTID(new Date('2024-03-15T09:30:00Z'));
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Generate a TID for right now
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
import { generateNextTID } from '@malachite/tid';
|
|
39
|
+
|
|
40
|
+
const tid = generateNextTID();
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Validate a TID
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
import { validateTid } from '@malachite/tid';
|
|
47
|
+
|
|
48
|
+
validateTid('3jzfcijpj2z2a'); // true
|
|
49
|
+
validateTid('not-a-tid'); // false
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Decode a TID
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import { decodeTid } from '@malachite/tid';
|
|
56
|
+
|
|
57
|
+
const { timestampUs, clockId, date } = decodeTid('3jzfcijpj2z2a');
|
|
58
|
+
// timestampUs — microseconds since Unix epoch
|
|
59
|
+
// clockId — random 0–31 clock identifier
|
|
60
|
+
// date — equivalent JavaScript Date (millisecond precision)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Compare / sort TIDs
|
|
64
|
+
|
|
65
|
+
Because the AT Protocol base-32 alphabet is ordered by timestamp, lexicographic string comparison is correct. `compareTids` returns `-1 | 0 | 1` for use as a `sort` comparator.
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
import { compareTids } from '@malachite/tid';
|
|
69
|
+
|
|
70
|
+
const tids = ['3jzfcijpj2z2a', '3jzfabc000022', '3jzfzzzzzzz2a'];
|
|
71
|
+
tids.sort(compareTids);
|
|
72
|
+
// → ['3jzfabc000022', '3jzfcijpj2z2a', '3jzfzzzzzzz2a'] (chronological)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## API
|
|
76
|
+
|
|
77
|
+
| Export | Signature | Description |
|
|
78
|
+
|--------|-----------|-------------|
|
|
79
|
+
| `generateTID` | `(source: string \| Date) => string` | Generate a TID for a historical timestamp |
|
|
80
|
+
| `generateNextTID` | `() => string` | Generate a TID for the current wall-clock time |
|
|
81
|
+
| `validateTid` | `(tid: string) => boolean` | Returns `true` if the string is a well-formed TID |
|
|
82
|
+
| `decodeTid` | `(tid: string) => DecodedTid` | Decode a TID into timestamp, clockId, and Date |
|
|
83
|
+
| `compareTids` | `(a: string, b: string) => -1 \| 0 \| 1` | Lexicographic comparator for sorting |
|
|
84
|
+
| `resetTidClock` | `() => void` | Reset the monotonic clock (**tests only**) |
|
|
85
|
+
|
|
86
|
+
### `DecodedTid`
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
interface DecodedTid {
|
|
90
|
+
timestampUs: number; // microseconds since Unix epoch
|
|
91
|
+
clockId: number; // 0–31
|
|
92
|
+
date: Date; // millisecond-precision equivalent
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Spec notes
|
|
97
|
+
|
|
98
|
+
- TIDs are 13 characters in the AT Protocol base-32 alphabet (`234567abcdefghijklmnopqrstuvwxyz`).
|
|
99
|
+
- The first 11 characters encode a microsecond-precision Unix timestamp.
|
|
100
|
+
- The last 2 characters encode a random clock ID (0–31) that disambiguates TIDs generated on different machines or in different processes within the same microsecond.
|
|
101
|
+
- The clock ID is randomised once at module load time (per JS context).
|
|
102
|
+
- The full specification is at <https://atproto.com/specs/tid>.
|
|
103
|
+
|
|
104
|
+
## License
|
|
105
|
+
|
|
106
|
+
AGPL-3.0-only — same as [Malachite](https://github.com/ewanc26/malachite).
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
compareTids: () => compareTids,
|
|
24
|
+
decodeTid: () => decodeTid,
|
|
25
|
+
generateNextTID: () => generateNextTID,
|
|
26
|
+
generateTID: () => generateTID,
|
|
27
|
+
resetTidClock: () => resetTidClock,
|
|
28
|
+
validateTid: () => validateTid
|
|
29
|
+
});
|
|
30
|
+
module.exports = __toCommonJS(index_exports);
|
|
31
|
+
var S32 = "234567abcdefghijklmnopqrstuvwxyz";
|
|
32
|
+
var S32_MAP = {};
|
|
33
|
+
for (let i = 0; i < S32.length; i++) S32_MAP[S32[i]] = i;
|
|
34
|
+
function s32encode(n) {
|
|
35
|
+
if (n === 0) return "2";
|
|
36
|
+
let s = "";
|
|
37
|
+
let v = n;
|
|
38
|
+
while (v > 0) {
|
|
39
|
+
s = S32[v % 32] + s;
|
|
40
|
+
v = Math.floor(v / 32);
|
|
41
|
+
}
|
|
42
|
+
return s;
|
|
43
|
+
}
|
|
44
|
+
function s32decode(s) {
|
|
45
|
+
let n = 0;
|
|
46
|
+
for (const ch of s) n = n * 32 + (S32_MAP[ch] ?? 0);
|
|
47
|
+
return n;
|
|
48
|
+
}
|
|
49
|
+
var TID_RE = /^[234567abcdefghij][234567abcdefghijklmnopqrstuvwxyz]{12}$/;
|
|
50
|
+
function validateTid(tid) {
|
|
51
|
+
return TID_RE.test(tid);
|
|
52
|
+
}
|
|
53
|
+
function decodeTid(tid) {
|
|
54
|
+
if (!validateTid(tid)) throw new TypeError(`Invalid TID: "${tid}"`);
|
|
55
|
+
const timestampUs = s32decode(tid.slice(0, 11));
|
|
56
|
+
const clockId = s32decode(tid.slice(11));
|
|
57
|
+
return { timestampUs, clockId, date: new Date(Math.floor(timestampUs / 1e3)) };
|
|
58
|
+
}
|
|
59
|
+
var lastUs = 0;
|
|
60
|
+
var CLOCK_ID = (() => {
|
|
61
|
+
const buf = new Uint8Array(1);
|
|
62
|
+
(globalThis.crypto ?? globalThis.webcrypto).getRandomValues(buf);
|
|
63
|
+
return buf[0] % 32;
|
|
64
|
+
})();
|
|
65
|
+
function nextUs(targetUs) {
|
|
66
|
+
const us = targetUs <= lastUs ? lastUs + 1 : targetUs;
|
|
67
|
+
lastUs = us;
|
|
68
|
+
return us;
|
|
69
|
+
}
|
|
70
|
+
function makeTid(us) {
|
|
71
|
+
return s32encode(us).padStart(11, "2") + s32encode(CLOCK_ID).padStart(2, "2");
|
|
72
|
+
}
|
|
73
|
+
function generateTID(source) {
|
|
74
|
+
const ms = typeof source === "string" ? new Date(source).getTime() : source.getTime();
|
|
75
|
+
return makeTid(nextUs(ms * 1e3));
|
|
76
|
+
}
|
|
77
|
+
function generateNextTID() {
|
|
78
|
+
return makeTid(nextUs(Date.now() * 1e3));
|
|
79
|
+
}
|
|
80
|
+
function compareTids(a, b) {
|
|
81
|
+
if (a < b) return -1;
|
|
82
|
+
if (a > b) return 1;
|
|
83
|
+
return 0;
|
|
84
|
+
}
|
|
85
|
+
function resetTidClock() {
|
|
86
|
+
lastUs = 0;
|
|
87
|
+
}
|
|
88
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
89
|
+
0 && (module.exports = {
|
|
90
|
+
compareTids,
|
|
91
|
+
decodeTid,
|
|
92
|
+
generateNextTID,
|
|
93
|
+
generateTID,
|
|
94
|
+
resetTidClock,
|
|
95
|
+
validateTid
|
|
96
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @malachite/tid — AT Protocol TID generation
|
|
3
|
+
*
|
|
4
|
+
* Zero-dependency, spec-compliant TID (Timestamp Identifier) generation for
|
|
5
|
+
* the AT Protocol. Works in Node.js 20+ and all modern browsers.
|
|
6
|
+
*
|
|
7
|
+
* Spec: https://atproto.com/specs/tid
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Returns `true` if `tid` is a well-formed AT Protocol TID.
|
|
11
|
+
*/
|
|
12
|
+
declare function validateTid(tid: string): boolean;
|
|
13
|
+
interface DecodedTid {
|
|
14
|
+
/** Microseconds since the Unix epoch. */
|
|
15
|
+
timestampUs: number;
|
|
16
|
+
/** Clock identifier (0–31). */
|
|
17
|
+
clockId: number;
|
|
18
|
+
/** Convenience: the timestamp as a `Date` (millisecond precision). */
|
|
19
|
+
date: Date;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Decode a TID into its constituent parts.
|
|
23
|
+
* Throws a `TypeError` if the TID is malformed.
|
|
24
|
+
*/
|
|
25
|
+
declare function decodeTid(tid: string): DecodedTid;
|
|
26
|
+
/**
|
|
27
|
+
* Generate a TID for a historical timestamp.
|
|
28
|
+
*
|
|
29
|
+
* Accepts either an ISO 8601 string or a `Date` object. Monotonicity is
|
|
30
|
+
* guaranteed — if records arrive out of order the clock is bumped forward so
|
|
31
|
+
* every call produces a strictly increasing TID within the same JS context.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* const tid = generateTID('2023-11-01T12:00:00Z');
|
|
35
|
+
* const tid = generateTID(new Date());
|
|
36
|
+
*/
|
|
37
|
+
declare function generateTID(source: string | Date): string;
|
|
38
|
+
/**
|
|
39
|
+
* Generate a TID for the current wall-clock time.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* const tid = generateNextTID();
|
|
43
|
+
*/
|
|
44
|
+
declare function generateNextTID(): string;
|
|
45
|
+
/**
|
|
46
|
+
* Compare two TIDs lexicographically.
|
|
47
|
+
*
|
|
48
|
+
* Because the AT Protocol base-32 alphabet is lexicographically ordered by
|
|
49
|
+
* timestamp, string comparison is sufficient and correct.
|
|
50
|
+
*
|
|
51
|
+
* Returns `-1`, `0`, or `1` (suitable as a `Array.prototype.sort` comparator).
|
|
52
|
+
*/
|
|
53
|
+
declare function compareTids(a: string, b: string): -1 | 0 | 1;
|
|
54
|
+
/**
|
|
55
|
+
* Reset the module-level monotonic clock to zero.
|
|
56
|
+
*
|
|
57
|
+
* **Only use this in tests.** Calling it in production risks generating
|
|
58
|
+
* duplicate or non-monotonic TIDs.
|
|
59
|
+
*/
|
|
60
|
+
declare function resetTidClock(): void;
|
|
61
|
+
|
|
62
|
+
export { type DecodedTid, compareTids, decodeTid, generateNextTID, generateTID, resetTidClock, validateTid };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @malachite/tid — AT Protocol TID generation
|
|
3
|
+
*
|
|
4
|
+
* Zero-dependency, spec-compliant TID (Timestamp Identifier) generation for
|
|
5
|
+
* the AT Protocol. Works in Node.js 20+ and all modern browsers.
|
|
6
|
+
*
|
|
7
|
+
* Spec: https://atproto.com/specs/tid
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Returns `true` if `tid` is a well-formed AT Protocol TID.
|
|
11
|
+
*/
|
|
12
|
+
declare function validateTid(tid: string): boolean;
|
|
13
|
+
interface DecodedTid {
|
|
14
|
+
/** Microseconds since the Unix epoch. */
|
|
15
|
+
timestampUs: number;
|
|
16
|
+
/** Clock identifier (0–31). */
|
|
17
|
+
clockId: number;
|
|
18
|
+
/** Convenience: the timestamp as a `Date` (millisecond precision). */
|
|
19
|
+
date: Date;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Decode a TID into its constituent parts.
|
|
23
|
+
* Throws a `TypeError` if the TID is malformed.
|
|
24
|
+
*/
|
|
25
|
+
declare function decodeTid(tid: string): DecodedTid;
|
|
26
|
+
/**
|
|
27
|
+
* Generate a TID for a historical timestamp.
|
|
28
|
+
*
|
|
29
|
+
* Accepts either an ISO 8601 string or a `Date` object. Monotonicity is
|
|
30
|
+
* guaranteed — if records arrive out of order the clock is bumped forward so
|
|
31
|
+
* every call produces a strictly increasing TID within the same JS context.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* const tid = generateTID('2023-11-01T12:00:00Z');
|
|
35
|
+
* const tid = generateTID(new Date());
|
|
36
|
+
*/
|
|
37
|
+
declare function generateTID(source: string | Date): string;
|
|
38
|
+
/**
|
|
39
|
+
* Generate a TID for the current wall-clock time.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* const tid = generateNextTID();
|
|
43
|
+
*/
|
|
44
|
+
declare function generateNextTID(): string;
|
|
45
|
+
/**
|
|
46
|
+
* Compare two TIDs lexicographically.
|
|
47
|
+
*
|
|
48
|
+
* Because the AT Protocol base-32 alphabet is lexicographically ordered by
|
|
49
|
+
* timestamp, string comparison is sufficient and correct.
|
|
50
|
+
*
|
|
51
|
+
* Returns `-1`, `0`, or `1` (suitable as a `Array.prototype.sort` comparator).
|
|
52
|
+
*/
|
|
53
|
+
declare function compareTids(a: string, b: string): -1 | 0 | 1;
|
|
54
|
+
/**
|
|
55
|
+
* Reset the module-level monotonic clock to zero.
|
|
56
|
+
*
|
|
57
|
+
* **Only use this in tests.** Calling it in production risks generating
|
|
58
|
+
* duplicate or non-monotonic TIDs.
|
|
59
|
+
*/
|
|
60
|
+
declare function resetTidClock(): void;
|
|
61
|
+
|
|
62
|
+
export { type DecodedTid, compareTids, decodeTid, generateNextTID, generateTID, resetTidClock, validateTid };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
var S32 = "234567abcdefghijklmnopqrstuvwxyz";
|
|
3
|
+
var S32_MAP = {};
|
|
4
|
+
for (let i = 0; i < S32.length; i++) S32_MAP[S32[i]] = i;
|
|
5
|
+
function s32encode(n) {
|
|
6
|
+
if (n === 0) return "2";
|
|
7
|
+
let s = "";
|
|
8
|
+
let v = n;
|
|
9
|
+
while (v > 0) {
|
|
10
|
+
s = S32[v % 32] + s;
|
|
11
|
+
v = Math.floor(v / 32);
|
|
12
|
+
}
|
|
13
|
+
return s;
|
|
14
|
+
}
|
|
15
|
+
function s32decode(s) {
|
|
16
|
+
let n = 0;
|
|
17
|
+
for (const ch of s) n = n * 32 + (S32_MAP[ch] ?? 0);
|
|
18
|
+
return n;
|
|
19
|
+
}
|
|
20
|
+
var TID_RE = /^[234567abcdefghij][234567abcdefghijklmnopqrstuvwxyz]{12}$/;
|
|
21
|
+
function validateTid(tid) {
|
|
22
|
+
return TID_RE.test(tid);
|
|
23
|
+
}
|
|
24
|
+
function decodeTid(tid) {
|
|
25
|
+
if (!validateTid(tid)) throw new TypeError(`Invalid TID: "${tid}"`);
|
|
26
|
+
const timestampUs = s32decode(tid.slice(0, 11));
|
|
27
|
+
const clockId = s32decode(tid.slice(11));
|
|
28
|
+
return { timestampUs, clockId, date: new Date(Math.floor(timestampUs / 1e3)) };
|
|
29
|
+
}
|
|
30
|
+
var lastUs = 0;
|
|
31
|
+
var CLOCK_ID = (() => {
|
|
32
|
+
const buf = new Uint8Array(1);
|
|
33
|
+
(globalThis.crypto ?? globalThis.webcrypto).getRandomValues(buf);
|
|
34
|
+
return buf[0] % 32;
|
|
35
|
+
})();
|
|
36
|
+
function nextUs(targetUs) {
|
|
37
|
+
const us = targetUs <= lastUs ? lastUs + 1 : targetUs;
|
|
38
|
+
lastUs = us;
|
|
39
|
+
return us;
|
|
40
|
+
}
|
|
41
|
+
function makeTid(us) {
|
|
42
|
+
return s32encode(us).padStart(11, "2") + s32encode(CLOCK_ID).padStart(2, "2");
|
|
43
|
+
}
|
|
44
|
+
function generateTID(source) {
|
|
45
|
+
const ms = typeof source === "string" ? new Date(source).getTime() : source.getTime();
|
|
46
|
+
return makeTid(nextUs(ms * 1e3));
|
|
47
|
+
}
|
|
48
|
+
function generateNextTID() {
|
|
49
|
+
return makeTid(nextUs(Date.now() * 1e3));
|
|
50
|
+
}
|
|
51
|
+
function compareTids(a, b) {
|
|
52
|
+
if (a < b) return -1;
|
|
53
|
+
if (a > b) return 1;
|
|
54
|
+
return 0;
|
|
55
|
+
}
|
|
56
|
+
function resetTidClock() {
|
|
57
|
+
lastUs = 0;
|
|
58
|
+
}
|
|
59
|
+
export {
|
|
60
|
+
compareTids,
|
|
61
|
+
decodeTid,
|
|
62
|
+
generateNextTID,
|
|
63
|
+
generateTID,
|
|
64
|
+
resetTidClock,
|
|
65
|
+
validateTid
|
|
66
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ewanc26/tid",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Zero-dependency AT Protocol TID generation for Node.js and browsers",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"import": "./dist/index.js",
|
|
10
|
+
"require": "./dist/index.cjs"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"main": "./dist/index.cjs",
|
|
14
|
+
"module": "./dist/index.js",
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"files": ["dist", "README.md", "LICENCE"],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsup src/index.ts --format esm,cjs --dts --clean",
|
|
19
|
+
"dev": "tsup src/index.ts --format esm,cjs --dts --watch",
|
|
20
|
+
"type-check": "tsc --noEmit",
|
|
21
|
+
"test": "node --test dist/index.test.js"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"atproto",
|
|
25
|
+
"bluesky",
|
|
26
|
+
"tid",
|
|
27
|
+
"timestamp",
|
|
28
|
+
"identifier",
|
|
29
|
+
"lexicon"
|
|
30
|
+
],
|
|
31
|
+
"author": "",
|
|
32
|
+
"license": "AGPL-3.0-only",
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"tsup": "^8.5.0",
|
|
35
|
+
"typescript": "^5.9.3"
|
|
36
|
+
}
|
|
37
|
+
}
|