@ewanc26/tid 1.0.1 → 1.0.3

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.
Files changed (2) hide show
  1. package/README.md +96 -61
  2. package/package.json +2 -2
package/README.md CHANGED
@@ -2,105 +2,140 @@
2
2
 
3
3
  Zero-dependency [AT Protocol](https://atproto.com/) TID (Timestamp Identifier) generation for Node.js and browsers.
4
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.
5
+ This package is **written in TypeScript** and compiled to **plain JavaScript**, so it ships with type definitions for TypeScript users and runs anywhere the Web Crypto API is available — Node.js 20+, Deno, Bun, and modern browsers.
6
+
7
+ TIDs are 13-character, lexicographically sortable record keys used across the AT Protocol and Bluesky. They’re monotonic identifiers derived from a microsecond timestamp and a 5-bit clock ID. When multiple TIDs would otherwise share the same microsecond, this package avoids collisions by nudging the clock ID (initialised per JS context) so each generated TID stays unique and strictly increasing within that runtime.
8
+
9
+ ---
6
10
 
7
11
  ## Why this package?
8
12
 
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.
13
+ Other TID implementations either require native bindings (e.g. `node-gyp`) or pull in large dependency trees. This package is **pure JavaScript**, has **no runtime dependencies**, ships with `.d.ts` typings, and is intentionally tinyideal for libraries, servers and client code where bundle size and portability matter.
14
+
15
+ ---
10
16
 
11
17
  ## Install
12
18
 
13
- ```sh
19
+ ```bash
14
20
  npm install @ewanc26/tid
15
21
  # or
16
22
  pnpm add @ewanc26/tid
17
23
  ```
18
24
 
19
- ## Usage
25
+ ---
20
26
 
21
- ### Generate a TID for a historical timestamp
27
+ ## Usage
22
28
 
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.
29
+ ### TypeScript (recommended)
24
30
 
25
31
  ```ts
26
- import { generateTID } from '@malachite/tid';
32
+ import {
33
+ generateTID,
34
+ generateNextTID,
35
+ validateTid,
36
+ decodeTid,
37
+ compareTids,
38
+ } from '@ewanc26/tid';
39
+
40
+ // From ISO string or Date
41
+ const tid: string = generateTID('2023-11-01T12:00:00Z');
42
+ const tid2: string = generateTID(new Date('2024-03-15T09:30:00Z'));
43
+
44
+ // Now
45
+ const currentTid: string = generateNextTID();
46
+
47
+ // Validate
48
+ const ok: boolean = validateTid('3jzfcijpj2z2a');
49
+
50
+ // Decode
51
+ const decoded = decodeTid('3jzfcijpj2z2a');
52
+ console.log(decoded.timestampUs, decoded.clockId, decoded.date);
53
+
54
+ // Sort
55
+ const tids: string[] = ['3jzfcijpj2z2a', '3jzfabc000022', '3jzfzzzzzzz2a'];
56
+ tids.sort(compareTids);
57
+ ```
27
58
 
28
- // From an ISO string (e.g. a Last.fm scrobble timestamp)
29
- const tid = generateTID('2023-11-01T12:00:00Z');
59
+ ### JavaScript (ESM)
30
60
 
31
- // From a Date object
32
- const tid2 = generateTID(new Date('2024-03-15T09:30:00Z'));
61
+ ```js
62
+ import {
63
+ generateTID,
64
+ generateNextTID,
65
+ validateTid,
66
+ decodeTid,
67
+ compareTids,
68
+ } from '@ewanc26/tid';
69
+
70
+ const tid = generateTID('2023-11-01T12:00:00Z');
71
+ const currentTid = generateNextTID();
72
+ console.log(validateTid(tid));
33
73
  ```
34
74
 
35
- ### Generate a TID for right now
75
+ ### JavaScript (CommonJS)
36
76
 
37
- ```ts
38
- import { generateNextTID } from '@malachite/tid';
77
+ ```js
78
+ const {
79
+ generateTID,
80
+ generateNextTID,
81
+ validateTid,
82
+ decodeTid,
83
+ compareTids,
84
+ } = require('@ewanc26/tid');
39
85
 
40
- const tid = generateNextTID();
86
+ const tid = generateTID('2023-11-01T12:00:00Z');
41
87
  ```
42
88
 
43
- ### Validate a TID
89
+ ---
44
90
 
45
- ```ts
46
- import { validateTid } from '@malachite/tid';
91
+ ## API
47
92
 
48
- validateTid('3jzfcijpj2z2a'); // true
49
- validateTid('not-a-tid'); // false
50
- ```
93
+ | Export | Signature | Description | | |
94
+ | ----------------- | ----------------------------- | ------------------------------------------------- | ----------------------------------------- | ------------------------------------ |
95
+ | `generateTID` | `(source: string | Date) => string` | Generate a TID for a historical timestamp | |
96
+ | `generateNextTID` | `() => string` | Generate a TID for the current wall-clock time | | |
97
+ | `validateTid` | `(tid: string) => boolean` | Returns `true` if the string is a well-formed TID | | |
98
+ | `decodeTid` | `(tid: string) => DecodedTid` | Decode a TID into timestamp, clockId, and Date | | |
99
+ | `compareTids` | `(a: string, b: string) => -1 | 0 | 1` | Lexicographic comparator for sorting |
100
+ | `resetTidClock` | `() => void` | Reset the monotonic clock (**tests only**) | | |
51
101
 
52
- ### Decode a TID
102
+ ### `DecodedTid`
53
103
 
54
104
  ```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)
105
+ interface DecodedTid {
106
+ timestampUs: number; // microseconds since Unix epoch
107
+ clockId: number; // 0–31
108
+ date: Date; // millisecond-precision equivalent
109
+ }
61
110
  ```
62
111
 
63
- ### Compare / sort TIDs
112
+ ---
64
113
 
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.
114
+ ## Spec notes
66
115
 
67
- ```ts
68
- import { compareTids } from '@malachite/tid';
116
+ * TIDs are 13 characters in the AT Protocol base-32 alphabet: `234567abcdefghijklmnopqrstuvwxyz`.
117
+ * The first 11 characters encode a microsecond-precision Unix timestamp.
118
+ * The last 2 characters encode a 5-bit clock ID (0–31) which disambiguates TIDs generated on different machines or processes within the same microsecond.
119
+ * The clock ID is randomised once at module load time (per JS context). When multiple TIDs would collide at the same microsecond, the implementation adjusts (nudges) the clock ID so collisions are avoided while preserving lexicographic ordering and monotonicity within that runtime.
120
+ * Full specification: [https://atproto.com/specs/tid](https://atproto.com/specs/tid)
69
121
 
70
- const tids = ['3jzfcijpj2z2a', '3jzfabc000022', '3jzfzzzzzzz2a'];
71
- tids.sort(compareTids);
72
- // → ['3jzfabc000022', '3jzfcijpj2z2a', '3jzfzzzzzzz2a'] (chronological)
73
- ```
122
+ ---
74
123
 
75
- ## API
124
+ ## Behavioural notes
76
125
 
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**) |
126
+ * Monotonicity is maintained per JS context. If records arrive out of chronological order in the same runtime, the package bumps the timestamp forward to ensure every generated TID is strictly increasing.
127
+ * Clock ID collisions across processes or machines are extremely unlikely due to the randomised 5-bit clock ID; the clock-nudging only applies inside a JS context to disambiguate simultaneous generations.
128
+ * The package does not attempt cross-process coordination if you need globally unique sequencing beyond the TID spec, consider a server-side sequencer or combining TIDs with per-host identifiers.
85
129
 
86
- ### `DecodedTid`
130
+ ---
87
131
 
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
- ```
132
+ ## Testing & development
95
133
 
96
- ## Spec notes
134
+ * `resetTidClock()` is exported for tests to make deterministic TID generation possible.
135
+ * The library has zero runtime deps and uses the Web Crypto API for secure randomness. When running in older environments, provide a compatible Web Crypto polyfill if necessary.
97
136
 
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>.
137
+ ---
103
138
 
104
- ## License
139
+ ## Licence
105
140
 
106
- AGPL-3.0-only — same as [Malachite](https://github.com/ewanc26/malachite).
141
+ AGPL-3.0-only — same as [Malachite](https://github.com/ewanc26/malachite/tree/main).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ewanc26/tid",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Zero-dependency AT Protocol TID generation for Node.js and browsers",
5
5
  "type": "module",
6
6
  "exports": {
@@ -32,7 +32,7 @@
32
32
  "identifier",
33
33
  "lexicon"
34
34
  ],
35
- "author": "",
35
+ "author": "Ewan Croft",
36
36
  "license": "AGPL-3.0-only",
37
37
  "devDependencies": {
38
38
  "tsup": "^8.5.0",