@tnid/core 0.0.1 → 0.0.4
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 +80 -298
- package/esm/_dnt.shims.d.ts +1 -4
- package/esm/_dnt.shims.d.ts.map +1 -1
- package/esm/_dnt.shims.js +1 -5
- package/esm/bits.js +3 -3
- package/esm/crypto.d.ts +4 -0
- package/esm/crypto.d.ts.map +1 -0
- package/esm/crypto.js +8 -0
- package/esm/dynamic.d.ts +18 -16
- package/esm/dynamic.d.ts.map +1 -1
- package/esm/dynamic.js +0 -3
- package/esm/uuidlike.d.ts +9 -7
- package/esm/uuidlike.d.ts.map +1 -1
- package/package.json +8 -3
- package/script/_dnt.shims.d.ts +1 -4
- package/script/_dnt.shims.d.ts.map +1 -1
- package/script/_dnt.shims.js +2 -7
- package/script/bits.js +3 -36
- package/script/crypto.d.ts +4 -0
- package/script/crypto.d.ts.map +1 -0
- package/script/crypto.js +44 -0
- package/script/dynamic.d.ts +18 -16
- package/script/dynamic.d.ts.map +1 -1
- package/script/dynamic.js +0 -3
- package/script/uuidlike.d.ts +9 -7
- package/script/uuidlike.d.ts.map +1 -1
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @tnid/core
|
|
2
2
|
|
|
3
3
|
Type-safe, named, unique identifiers (TNIDs) for TypeScript.
|
|
4
4
|
|
|
@@ -21,6 +21,15 @@ bun add @tnid/core
|
|
|
21
21
|
deno add npm:@tnid/core
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
+
## Platform Support
|
|
25
|
+
|
|
26
|
+
Requires `globalThis.crypto` (Web Crypto API):
|
|
27
|
+
|
|
28
|
+
- Node.js 20+
|
|
29
|
+
- Deno 1.0+
|
|
30
|
+
- Bun 1.0+
|
|
31
|
+
- Modern browsers (ES2020+)
|
|
32
|
+
|
|
24
33
|
## Quick Start
|
|
25
34
|
|
|
26
35
|
```typescript
|
|
@@ -54,8 +63,7 @@ const fromUuid: UserId = UserId.parseUuidString(uuid);
|
|
|
54
63
|
- **UUID compatible**: Store in any UUID column, use with existing UUID tooling
|
|
55
64
|
- **Time-ordered (V0)**: Sortable by creation time, like UUIDv7
|
|
56
65
|
- **High-entropy (V1)**: Maximum randomness, like UUIDv4
|
|
57
|
-
- **String-backed**: At runtime, TNIDs are their string representation
|
|
58
|
-
`user.Br2flcNDfF6LYICnT`). Use as Map keys, in JSON, or compare with `===`
|
|
66
|
+
- **String-backed**: At runtime, TNIDs are their string representation
|
|
59
67
|
|
|
60
68
|
```typescript
|
|
61
69
|
const id = UserId.new_v0();
|
|
@@ -71,8 +79,6 @@ JSON.stringify({ userId: id }); // {"userId":"user.Br2flcNDfF6LYICnT"}
|
|
|
71
79
|
id === otherUserId; // true/false
|
|
72
80
|
```
|
|
73
81
|
|
|
74
|
-
---
|
|
75
|
-
|
|
76
82
|
## API Reference
|
|
77
83
|
|
|
78
84
|
### Exports
|
|
@@ -81,9 +87,9 @@ id === otherUserId; // true/false
|
|
|
81
87
|
import {
|
|
82
88
|
Case, // "lower" | "upper"
|
|
83
89
|
DynamicTnid, // Runtime TNID operations (type + namespace)
|
|
84
|
-
Tnid, //
|
|
85
|
-
NamedTnid, //
|
|
86
|
-
TnidType, // Type helper to extract ID type
|
|
90
|
+
Tnid, // NamedTnid creator function
|
|
91
|
+
NamedTnid, // NamedTnid interface
|
|
92
|
+
TnidType, // Type helper to extract ID type
|
|
87
93
|
// Types only:
|
|
88
94
|
TnidValue, // Branded string type
|
|
89
95
|
TnidVariant, // "v0" | "v1" | "v2" | "v3"
|
|
@@ -91,11 +97,9 @@ import {
|
|
|
91
97
|
} from "@tnid/core";
|
|
92
98
|
```
|
|
93
99
|
|
|
94
|
-
---
|
|
95
|
-
|
|
96
100
|
### `Tnid(name)`
|
|
97
101
|
|
|
98
|
-
Creates a
|
|
102
|
+
Creates a `NamedTnid` for a specific name. The name is validated at
|
|
99
103
|
**compile time**.
|
|
100
104
|
|
|
101
105
|
```typescript
|
|
@@ -106,90 +110,57 @@ const ItemId = Tnid("item");
|
|
|
106
110
|
|
|
107
111
|
#### Name Rules
|
|
108
112
|
|
|
109
|
-
-
|
|
110
|
-
-
|
|
111
|
-
-
|
|
113
|
+
- 1-4 characters
|
|
114
|
+
- Only `0-4` and `a-z` (31 characters total)
|
|
115
|
+
- Lowercase only
|
|
112
116
|
|
|
113
117
|
```typescript
|
|
114
118
|
// Valid names
|
|
115
|
-
Tnid("user"); //
|
|
116
|
-
Tnid("a"); //
|
|
117
|
-
Tnid("1234"); //
|
|
118
|
-
Tnid("a1b2"); //
|
|
119
|
+
Tnid("user"); // OK
|
|
120
|
+
Tnid("a"); // OK
|
|
121
|
+
Tnid("1234"); // OK
|
|
122
|
+
Tnid("a1b2"); // OK
|
|
119
123
|
|
|
120
124
|
// Compile errors
|
|
121
|
-
Tnid("users"); //
|
|
122
|
-
Tnid("User"); //
|
|
123
|
-
Tnid("a-b"); //
|
|
124
|
-
Tnid("5"); //
|
|
125
|
-
Tnid(""); //
|
|
125
|
+
Tnid("users"); // too long (max 4)
|
|
126
|
+
Tnid("User"); // uppercase not allowed
|
|
127
|
+
Tnid("a-b"); // hyphen not allowed
|
|
128
|
+
Tnid("5"); // only digits 0-4
|
|
129
|
+
Tnid(""); // empty not allowed
|
|
126
130
|
```
|
|
127
131
|
|
|
128
|
-
---
|
|
129
|
-
|
|
130
132
|
### `NamedTnid<Name>` (returned by `Tnid()`)
|
|
131
133
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
#### Properties
|
|
135
|
-
|
|
136
|
-
| Property | Type | Description |
|
|
137
|
-
| -------- | ------ | ------------------------------------------ |
|
|
138
|
-
| `name` | `Name` | The TNID name this creates IDs for |
|
|
139
|
-
|
|
140
|
-
#### Generation Methods
|
|
141
|
-
|
|
142
|
-
| Method | Returns | Description |
|
|
143
|
-
| ---------------------------------------- | ----------------- | ------------------------------------------------- |
|
|
144
|
-
| `new_v0()` | `TnidValue<Name>` | Generate a time-ordered ID (like UUIDv7) |
|
|
145
|
-
| `new_v1()` | `TnidValue<Name>` | Generate a high-entropy random ID (like UUIDv4) |
|
|
146
|
-
| `v0_from_parts(timestampMs, randomBits)` | `TnidValue<Name>` | Create V0 from explicit parts (for testing) |
|
|
147
|
-
| `v1_from_parts(randomBits)` | `TnidValue<Name>` | Create V1 from explicit random bits (for testing) |
|
|
148
|
-
|
|
149
|
-
#### Parsing Methods
|
|
150
|
-
|
|
151
|
-
| Method | Returns | Throws | Description |
|
|
152
|
-
| ----------------------- | ----------------- | -------------------------------- | ----------------------- |
|
|
153
|
-
| `parse(s)` | `TnidValue<Name>` | If invalid or name mismatch | Parse a TNID string |
|
|
154
|
-
| `parseUuidString(uuid)` | `TnidValue<Name>` | If invalid UUID or name mismatch | Parse a UUID hex string |
|
|
155
|
-
|
|
156
|
-
#### Utility Methods
|
|
157
|
-
|
|
158
|
-
| Method | Returns | Description |
|
|
159
|
-
| ------------------------- | ------------- | -------------------------------------- |
|
|
160
|
-
| `variant(id)` | `TnidVariant` | Get the variant (`"v0"`, `"v1"`, etc.) |
|
|
161
|
-
| `toUuidString(id, case?)` | `string` | Convert to UUID hex format |
|
|
162
|
-
| `nameHex()` | `string` | Get the name as a 5-char hex string |
|
|
163
|
-
|
|
164
|
-
#### Example
|
|
134
|
+
#### Generation
|
|
165
135
|
|
|
166
136
|
```typescript
|
|
167
137
|
const UserId = Tnid("user");
|
|
168
138
|
type UserId = TnidType<typeof UserId>;
|
|
169
139
|
|
|
170
|
-
//
|
|
171
|
-
|
|
172
|
-
|
|
140
|
+
UserId.new_v0(); // time-ordered ID
|
|
141
|
+
UserId.new_v1(); // high-entropy random ID
|
|
142
|
+
UserId.v0_from_parts(1234567890n, 0n); // V0 with explicit timestamp/random
|
|
143
|
+
UserId.v1_from_parts(0n); // V1 with explicit random bits
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
#### Parsing
|
|
173
147
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
"d6157329-4640-8e30-8012-345678901234",
|
|
178
|
-
);
|
|
179
|
-
|
|
180
|
-
// Utilities
|
|
181
|
-
UserId.variant(id); // "v0"
|
|
182
|
-
UserId.toUuidString(id); // "d6157329-4640-8e30-8012-..."
|
|
183
|
-
UserId.toUuidString(id, "upper"); // "D6157329-4640-8E30-8012-..."
|
|
184
|
-
UserId.nameHex(); // "d6157"
|
|
185
|
-
|
|
186
|
-
// Property
|
|
187
|
-
UserId.name; // "user"
|
|
148
|
+
```typescript
|
|
149
|
+
UserId.parse("user.Br2flcNDfF6LYICnT"); // parse TNID string
|
|
150
|
+
UserId.parseUuidString("d6157329-4640-8e30-..."); // parse UUID hex string
|
|
188
151
|
```
|
|
189
152
|
|
|
190
|
-
|
|
153
|
+
#### Inspection and Conversion
|
|
191
154
|
|
|
192
|
-
|
|
155
|
+
```typescript
|
|
156
|
+
UserId.name; // "user" - the TNID name
|
|
157
|
+
UserId.variant(id); // "v0" or "v1" - get the variant
|
|
158
|
+
UserId.toUuidString(id); // "d6157329-4640-8e30-..." - convert to UUID
|
|
159
|
+
UserId.toUuidString(id, "upper"); // "D6157329-4640-8E30-..." - uppercase UUID
|
|
160
|
+
UserId.nameHex(); // "d6157" - name as 5-char hex
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### `TnidType<T>`
|
|
193
164
|
|
|
194
165
|
Type helper to extract the `TnidValue` type from a `NamedTnid`.
|
|
195
166
|
|
|
@@ -205,131 +176,47 @@ function getUser(id: UserId): User { ... }
|
|
|
205
176
|
function getPost(id: PostId): Post { ... }
|
|
206
177
|
```
|
|
207
178
|
|
|
208
|
-
---
|
|
209
|
-
|
|
210
179
|
### `DynamicTnid`
|
|
211
180
|
|
|
212
|
-
For working with TNIDs when the name isn't known at compile time.
|
|
213
|
-
**type** and a **namespace**.
|
|
214
|
-
|
|
215
|
-
#### As a Type
|
|
216
|
-
|
|
217
|
-
`DynamicTnid` accepts any TNID regardless of name:
|
|
181
|
+
For working with TNIDs when the name isn't known at compile time.
|
|
218
182
|
|
|
219
183
|
```typescript
|
|
184
|
+
// As a type - accepts any TNID
|
|
220
185
|
function logAnyId(id: DynamicTnid) {
|
|
221
186
|
console.log(DynamicTnid.getName(id), id);
|
|
222
187
|
}
|
|
223
188
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
logAnyId(userId); // works
|
|
228
|
-
logAnyId(postId); // works
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
#### Namespace Methods
|
|
232
|
-
|
|
233
|
-
##### Generation
|
|
234
|
-
|
|
235
|
-
| Method | Returns | Description |
|
|
236
|
-
| ---------------------------------------------- | ------------- | ------------------------------------------ |
|
|
237
|
-
| `new_v0(name)` | `DynamicTnid` | Generate time-ordered ID with runtime name |
|
|
238
|
-
| `new_v1(name)` | `DynamicTnid` | Generate high-entropy ID with runtime name |
|
|
239
|
-
| `new_time_ordered(name)` | `DynamicTnid` | Alias for `new_v0` |
|
|
240
|
-
| `new_high_entropy(name)` | `DynamicTnid` | Alias for `new_v1` |
|
|
241
|
-
| `new_v0_with_time(name, date)` | `DynamicTnid` | V0 with specific timestamp |
|
|
242
|
-
| `new_v0_with_parts(name, epochMillis, random)` | `DynamicTnid` | V0 with explicit parts |
|
|
243
|
-
| `new_v1_with_random(name, randomBits)` | `DynamicTnid` | V1 with explicit random bits |
|
|
189
|
+
// Generation with runtime names
|
|
190
|
+
DynamicTnid.new_v0("user"); // time-ordered (alias: new_time_ordered)
|
|
191
|
+
DynamicTnid.new_v1("user"); // high-entropy (alias: new_high_entropy)
|
|
244
192
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
| `parse(s)` | `DynamicTnid` | If invalid | Parse any TNID string |
|
|
250
|
-
| `parse_uuid_string(uuid)` | `DynamicTnid` | If invalid | Parse UUID to TNID |
|
|
251
|
-
|
|
252
|
-
##### Inspection
|
|
253
|
-
|
|
254
|
-
| Method | Returns | Description |
|
|
255
|
-
| ------------------------- | ------------- | ------------------------ |
|
|
256
|
-
| `getName(id)` | `string` | Extract the name portion |
|
|
257
|
-
| `getNameHex(id)` | `string` | Get name as 5-char hex |
|
|
258
|
-
| `getVariant(id)` | `TnidVariant` | Get the variant |
|
|
259
|
-
| `toUuidString(id, case?)` | `string` | Convert to UUID hex |
|
|
260
|
-
|
|
261
|
-
#### Example
|
|
262
|
-
|
|
263
|
-
```typescript
|
|
264
|
-
// Runtime name - useful for dynamic/generic code
|
|
265
|
-
const entityType = "user"; // from config, API, etc.
|
|
266
|
-
const id = DynamicTnid.new_v0(entityType);
|
|
193
|
+
// Generation with explicit values (useful for testing/migrations)
|
|
194
|
+
DynamicTnid.new_v0_with_time("user", new Date("2024-01-15"));
|
|
195
|
+
DynamicTnid.new_v0_with_parts("user", 1705312800000n, 123n);
|
|
196
|
+
DynamicTnid.new_v1_with_random("user", 0x123456789abcdef0123456789n);
|
|
267
197
|
|
|
268
|
-
//
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
console.log(DynamicTnid.getVariant(unknown)); // "v1"
|
|
198
|
+
// Parsing any TNID
|
|
199
|
+
DynamicTnid.parse("post.EUBcUw4T9x3KNOll-");
|
|
200
|
+
DynamicTnid.parse_uuid_string("d6157329-4640-...");
|
|
272
201
|
|
|
273
|
-
//
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
//
|
|
277
|
-
|
|
202
|
+
// Inspection
|
|
203
|
+
DynamicTnid.getName(id); // "user"
|
|
204
|
+
DynamicTnid.getNameHex(id); // "d6157"
|
|
205
|
+
DynamicTnid.getVariant(id); // "v0" or "v1"
|
|
206
|
+
DynamicTnid.toUuidString(id); // UUID hex string
|
|
278
207
|
```
|
|
279
208
|
|
|
280
|
-
---
|
|
281
|
-
|
|
282
209
|
### `UuidLike`
|
|
283
210
|
|
|
284
|
-
For working with UUID hex strings that may or may not be valid TNIDs.
|
|
285
|
-
both a **type** and a **namespace**.
|
|
286
|
-
|
|
287
|
-
#### As a Type
|
|
288
|
-
|
|
289
|
-
`UuidLike` represents any valid UUID hex string (format:
|
|
290
|
-
`xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`):
|
|
291
|
-
|
|
292
|
-
```typescript
|
|
293
|
-
function storeInDatabase(uuid: UuidLike) { ... }
|
|
294
|
-
```
|
|
295
|
-
|
|
296
|
-
#### Namespace Methods
|
|
297
|
-
|
|
298
|
-
| Method | Returns | Throws | Description |
|
|
299
|
-
| ------------------- | ------------- | ------------------- | ----------------------------------- |
|
|
300
|
-
| `fromTnid(id)` | `UuidLike` | - | Convert TNID to UUID string |
|
|
301
|
-
| `parse(s)` | `UuidLike` | If invalid format | Parse any UUID string (format only) |
|
|
302
|
-
| `toTnid(uuid)` | `DynamicTnid` | If not a valid TNID | Convert UUID to TNID |
|
|
303
|
-
| `toUpperCase(uuid)` | `UuidLike` | - | Convert to uppercase |
|
|
304
|
-
|
|
305
|
-
#### Example
|
|
211
|
+
For working with UUID hex strings that may or may not be valid TNIDs.
|
|
306
212
|
|
|
307
213
|
```typescript
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
//
|
|
312
|
-
const uuid: UuidLike = UuidLike.fromTnid(id);
|
|
313
|
-
// "d6157329-4640-8e30-8012-345678901234"
|
|
314
|
-
|
|
315
|
-
// Parse any UUID (doesn't validate TNID structure)
|
|
316
|
-
const anyUuid = UuidLike.parse("550e8400-e29b-41d4-a716-446655440000");
|
|
317
|
-
|
|
318
|
-
// Convert back to TNID (validates it's a valid TNID)
|
|
319
|
-
try {
|
|
320
|
-
const tnid = UuidLike.toTnid(uuid); // works
|
|
321
|
-
const fail = UuidLike.toTnid(anyUuid); // throws - not a TNID
|
|
322
|
-
} catch (e) {
|
|
323
|
-
console.log("Not a valid TNID");
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
// Case conversion
|
|
327
|
-
const upper = UuidLike.toUpperCase(uuid);
|
|
328
|
-
// "D6157329-4640-8E30-8012-345678901234"
|
|
214
|
+
UuidLike.fromTnid(id); // convert TNID to UUID string
|
|
215
|
+
UuidLike.parse(s); // parse any UUID (validates format only)
|
|
216
|
+
UuidLike.toTnid(uuid); // convert UUID to TNID (throws if invalid)
|
|
217
|
+
UuidLike.toUpperCase(uuid); // convert to uppercase
|
|
329
218
|
```
|
|
330
219
|
|
|
331
|
-
---
|
|
332
|
-
|
|
333
220
|
## Variants
|
|
334
221
|
|
|
335
222
|
### V0 (Time-Ordered)
|
|
@@ -338,23 +225,11 @@ const upper = UuidLike.toUpperCase(uuid);
|
|
|
338
225
|
- 57 bits: random
|
|
339
226
|
- **Use case**: When you need chronological sorting (logs, events, feeds)
|
|
340
227
|
|
|
341
|
-
```typescript
|
|
342
|
-
const id = UserId.new_v0();
|
|
343
|
-
// IDs created later sort after earlier ones
|
|
344
|
-
```
|
|
345
|
-
|
|
346
228
|
### V1 (High-Entropy)
|
|
347
229
|
|
|
348
230
|
- 100 bits: random
|
|
349
231
|
- **Use case**: When you need maximum uniqueness/unpredictability
|
|
350
232
|
|
|
351
|
-
```typescript
|
|
352
|
-
const id = UserId.new_v1();
|
|
353
|
-
// Maximum entropy, no time component
|
|
354
|
-
```
|
|
355
|
-
|
|
356
|
-
---
|
|
357
|
-
|
|
358
233
|
## Type Safety
|
|
359
234
|
|
|
360
235
|
Different TNID types are completely incompatible at compile time:
|
|
@@ -369,44 +244,31 @@ const userId: UserId = UserId.new_v0();
|
|
|
369
244
|
const postId: PostId = PostId.new_v0();
|
|
370
245
|
|
|
371
246
|
// Compile errors - types don't match
|
|
372
|
-
const wrong1: UserId = postId; //
|
|
373
|
-
const wrong2: PostId = userId; //
|
|
247
|
+
const wrong1: UserId = postId; // Error
|
|
248
|
+
const wrong2: PostId = userId; // Error
|
|
374
249
|
function getUser(id: UserId) { ... }
|
|
375
|
-
getUser(postId); //
|
|
250
|
+
getUser(postId); // Error
|
|
376
251
|
|
|
377
252
|
// DynamicTnid accepts any TNID
|
|
378
|
-
const dynamic: DynamicTnid = userId; //
|
|
379
|
-
const dynamic2: DynamicTnid = postId; //
|
|
253
|
+
const dynamic: DynamicTnid = userId; // OK
|
|
254
|
+
const dynamic2: DynamicTnid = postId; // OK
|
|
380
255
|
```
|
|
381
256
|
|
|
382
|
-
### Preventing Forgery
|
|
383
|
-
|
|
384
257
|
Plain strings cannot be assigned to TNID types:
|
|
385
258
|
|
|
386
259
|
```typescript
|
|
387
|
-
const UserId = Tnid("user");
|
|
388
|
-
type UserId = TnidType<typeof UserId>;
|
|
389
|
-
|
|
390
260
|
// Compile errors - plain strings not allowed
|
|
391
|
-
const
|
|
392
|
-
const fake2: UserId = someString; // ✗
|
|
261
|
+
const fake: UserId = "user.Br2flcNDfF6LYICnT"; // Error
|
|
393
262
|
|
|
394
263
|
// Must use parse() or new_*() to get a valid TNID
|
|
395
|
-
const valid: UserId = UserId.parse("user.Br2flcNDfF6LYICnT"); //
|
|
264
|
+
const valid: UserId = UserId.parse("user.Br2flcNDfF6LYICnT"); // OK
|
|
396
265
|
```
|
|
397
266
|
|
|
398
|
-
---
|
|
399
|
-
|
|
400
267
|
## UUID Compatibility
|
|
401
268
|
|
|
402
|
-
TNIDs are valid UUIDv8 identifiers
|
|
403
|
-
|
|
404
|
-
- Store in UUID database columns
|
|
405
|
-
- Use with UUID-aware tools and libraries
|
|
406
|
-
- Convert freely between TNID and UUID formats
|
|
269
|
+
TNIDs are valid UUIDv8 identifiers:
|
|
407
270
|
|
|
408
271
|
```typescript
|
|
409
|
-
const UserId = Tnid("user");
|
|
410
272
|
const id = UserId.new_v0();
|
|
411
273
|
|
|
412
274
|
// To UUID
|
|
@@ -415,94 +277,14 @@ const uuid = UserId.toUuidString(id);
|
|
|
415
277
|
|
|
416
278
|
// From UUID
|
|
417
279
|
const back = UserId.parseUuidString(uuid);
|
|
418
|
-
// back === id
|
|
419
280
|
|
|
420
281
|
// Store in database as UUID
|
|
421
282
|
await db.query("INSERT INTO users (id) VALUES ($1)", [uuid]);
|
|
422
|
-
|
|
423
|
-
// Retrieve and convert back
|
|
424
|
-
const row = await db.query("SELECT id FROM users WHERE ...");
|
|
425
|
-
const userId = UserId.parseUuidString(row.id);
|
|
426
|
-
```
|
|
427
|
-
|
|
428
|
-
---
|
|
429
|
-
|
|
430
|
-
## Common Patterns
|
|
431
|
-
|
|
432
|
-
### Define ID types for your domain
|
|
433
|
-
|
|
434
|
-
```typescript
|
|
435
|
-
// ids.ts
|
|
436
|
-
import { Tnid, TnidType } from "@tnid/core";
|
|
437
|
-
|
|
438
|
-
export const UserId = Tnid("user");
|
|
439
|
-
export type UserId = TnidType<typeof UserId>;
|
|
440
|
-
|
|
441
|
-
export const PostId = Tnid("post");
|
|
442
|
-
export type PostId = TnidType<typeof PostId>;
|
|
443
|
-
|
|
444
|
-
export const OrgId = Tnid("org");
|
|
445
|
-
export type OrgId = TnidType<typeof OrgId>;
|
|
446
283
|
```
|
|
447
284
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
```typescript
|
|
451
|
-
import { PostId, UserId } from "./ids";
|
|
452
|
-
|
|
453
|
-
interface Post {
|
|
454
|
-
id: PostId;
|
|
455
|
-
authorId: UserId;
|
|
456
|
-
title: string;
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
function createPost(authorId: UserId, title: string): Post {
|
|
460
|
-
return {
|
|
461
|
-
id: PostId.new_v0(),
|
|
462
|
-
authorId,
|
|
463
|
-
title,
|
|
464
|
-
};
|
|
465
|
-
}
|
|
466
|
-
```
|
|
467
|
-
|
|
468
|
-
### Generic functions with DynamicTnid
|
|
469
|
-
|
|
470
|
-
```typescript
|
|
471
|
-
import { DynamicTnid } from "@tnid/core";
|
|
472
|
-
|
|
473
|
-
function logEntity(id: DynamicTnid) {
|
|
474
|
-
const name = DynamicTnid.getName(id);
|
|
475
|
-
const variant = DynamicTnid.getVariant(id);
|
|
476
|
-
console.log(`[${name}:${variant}] ${id}`);
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
// Works with any TNID
|
|
480
|
-
logEntity(userId); // "[user:v0] user.Br2..."
|
|
481
|
-
logEntity(postId); // "[post:v1] post.EU..."
|
|
482
|
-
```
|
|
483
|
-
|
|
484
|
-
### Parsing from external sources
|
|
485
|
-
|
|
486
|
-
```typescript
|
|
487
|
-
import { UserId } from "./ids";
|
|
488
|
-
|
|
489
|
-
// From API request
|
|
490
|
-
app.get("/users/:id", (req, res) => {
|
|
491
|
-
try {
|
|
492
|
-
const id = UserId.parse(req.params.id);
|
|
493
|
-
const user = await getUser(id);
|
|
494
|
-
res.json(user);
|
|
495
|
-
} catch {
|
|
496
|
-
res.status(400).json({ error: "Invalid user ID" });
|
|
497
|
-
}
|
|
498
|
-
});
|
|
499
|
-
|
|
500
|
-
// From database UUID column
|
|
501
|
-
const row = await db.query("SELECT id FROM users WHERE email = $1", [email]);
|
|
502
|
-
const userId = UserId.parseUuidString(row.id);
|
|
503
|
-
```
|
|
285
|
+
## Related Packages
|
|
504
286
|
|
|
505
|
-
|
|
287
|
+
- **@tnid/encryption** - Format-preserving encryption to convert V0 (time-ordered) to V1 (random-looking) IDs
|
|
506
288
|
|
|
507
289
|
## License
|
|
508
290
|
|
package/esm/_dnt.shims.d.ts
CHANGED
|
@@ -1,5 +1,2 @@
|
|
|
1
|
-
export
|
|
2
|
-
export declare const dntGlobalThis: Omit<typeof globalThis, "crypto"> & {
|
|
3
|
-
crypto: import("@deno/shim-crypto").Crypto;
|
|
4
|
-
};
|
|
1
|
+
export declare const dntGlobalThis: Omit<typeof globalThis, never>;
|
|
5
2
|
//# sourceMappingURL=_dnt.shims.d.ts.map
|
package/esm/_dnt.shims.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_dnt.shims.d.ts","sourceRoot":"","sources":["../src/_dnt.shims.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"_dnt.shims.d.ts","sourceRoot":"","sources":["../src/_dnt.shims.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,aAAa,gCAA2C,CAAC"}
|
package/esm/_dnt.shims.js
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
export { crypto } from "@deno/shim-crypto";
|
|
3
|
-
const dntGlobals = {
|
|
4
|
-
crypto,
|
|
5
|
-
};
|
|
1
|
+
const dntGlobals = {};
|
|
6
2
|
export const dntGlobalThis = createMergeProxy(globalThis, dntGlobals);
|
|
7
3
|
function createMergeProxy(baseObj, extObj) {
|
|
8
4
|
return new Proxy(baseObj, {
|
package/esm/bits.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
// Bit Manipulation and TNID Generation
|
|
3
3
|
// Mask-based approach matching Rust implementation
|
|
4
4
|
// =============================================================================
|
|
5
|
+
import { crypto } from "./crypto.js";
|
|
5
6
|
// Masks for UUID version 8 and variant bits
|
|
6
|
-
import * as dntShim from "./_dnt.shims.js";
|
|
7
7
|
export const UUID_V8_MASK = 0x00000000000080008000000000000000n;
|
|
8
8
|
// V1: 100 random bits scattered across the UUID
|
|
9
9
|
export const V1_RANDOM_MASK = 0x00000fffffff0fff0fffffffffffffffn;
|
|
@@ -62,7 +62,7 @@ export function generateV0(nameBits, timestampMs, randomBits) {
|
|
|
62
62
|
random = randomBits;
|
|
63
63
|
}
|
|
64
64
|
else {
|
|
65
|
-
const randomBytes =
|
|
65
|
+
const randomBytes = crypto.getRandomValues(new Uint8Array(8));
|
|
66
66
|
random = 0n;
|
|
67
67
|
for (const byte of randomBytes) {
|
|
68
68
|
random = (random << 8n) | BigInt(byte);
|
|
@@ -79,7 +79,7 @@ export function generateV1(nameBits, randomBits) {
|
|
|
79
79
|
random = randomBits;
|
|
80
80
|
}
|
|
81
81
|
else {
|
|
82
|
-
const randomBytes =
|
|
82
|
+
const randomBytes = crypto.getRandomValues(new Uint8Array(16));
|
|
83
83
|
random = 0n;
|
|
84
84
|
for (const byte of randomBytes) {
|
|
85
85
|
random = (random << 8n) | BigInt(byte);
|
package/esm/crypto.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,MAAM,EAA4C;IAC7D,eAAe,CAAC,CAAC,SAAS,eAAe,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC;CACzD,CAAC"}
|
package/esm/crypto.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Web Crypto API accessor
|
|
2
|
+
// Isolates the type assertion to one place. Works in:
|
|
3
|
+
// - Browsers (globalThis.crypto)
|
|
4
|
+
// - Deno (globalThis.crypto)
|
|
5
|
+
// - Node.js 20+ (globalThis.crypto)
|
|
6
|
+
// deno-lint-ignore no-explicit-any
|
|
7
|
+
import * as dntShim from "./_dnt.shims.js";
|
|
8
|
+
export const crypto = dntShim.dntGlobalThis.crypto;
|
package/esm/dynamic.d.ts
CHANGED
|
@@ -3,34 +3,36 @@ import type { Case, TnidValue, TnidVariant } from "./types.js";
|
|
|
3
3
|
export type DynamicTnid = TnidValue<string>;
|
|
4
4
|
declare function getTnidVariantImpl(id: DynamicTnid): TnidVariant;
|
|
5
5
|
declare function toUuidStringImpl(id: DynamicTnid, upperCase?: boolean): string;
|
|
6
|
-
/**
|
|
7
|
-
export
|
|
6
|
+
/** Interface for DynamicTnid static methods. */
|
|
7
|
+
export interface DynamicTnidNamespace {
|
|
8
8
|
/** Generate a new time-sortable TNID (variant 0) with runtime name validation. */
|
|
9
|
-
|
|
9
|
+
new_v0(name: string): DynamicTnid;
|
|
10
10
|
/** Alias for new_v0. */
|
|
11
|
-
|
|
11
|
+
new_time_ordered(name: string): DynamicTnid;
|
|
12
12
|
/** Generate a new time-sortable TNID with a specific timestamp. */
|
|
13
|
-
|
|
13
|
+
new_v0_with_time(name: string, time: Date): DynamicTnid;
|
|
14
14
|
/** Generate a new time-sortable TNID with explicit timestamp and random components. */
|
|
15
|
-
|
|
15
|
+
new_v0_with_parts(name: string, epochMillis: bigint, random: bigint): DynamicTnid;
|
|
16
16
|
/** Generate a new high-entropy TNID (variant 1) with runtime name validation. */
|
|
17
|
-
|
|
17
|
+
new_v1(name: string): DynamicTnid;
|
|
18
18
|
/** Alias for new_v1. */
|
|
19
|
-
|
|
19
|
+
new_high_entropy(name: string): DynamicTnid;
|
|
20
20
|
/** Generate a new high-entropy TNID with explicit random bits. */
|
|
21
|
-
|
|
21
|
+
new_v1_with_random(name: string, randomBits: bigint): DynamicTnid;
|
|
22
22
|
/** Parse any valid TNID string. */
|
|
23
|
-
|
|
23
|
+
parse(s: string): DynamicTnid;
|
|
24
24
|
/** Parse a UUID hex string into a DynamicTnid (validates TNID structure). */
|
|
25
|
-
|
|
25
|
+
parse_uuid_string(uuid: string): DynamicTnid;
|
|
26
26
|
/** Get the name from a TNID. */
|
|
27
|
-
|
|
27
|
+
getName(id: DynamicTnid): string;
|
|
28
28
|
/** Get the name encoded as a 5-character hex string. */
|
|
29
|
-
|
|
29
|
+
getNameHex(id: DynamicTnid): string;
|
|
30
30
|
/** Get the variant of a TNID. */
|
|
31
|
-
|
|
31
|
+
getVariant(id: DynamicTnid): TnidVariant;
|
|
32
32
|
/** Convert to UUID hex string format. */
|
|
33
|
-
|
|
34
|
-
}
|
|
33
|
+
toUuidString(id: DynamicTnid, caseFormat?: Case): string;
|
|
34
|
+
}
|
|
35
|
+
/** Static methods for working with any TNID regardless of name. */
|
|
36
|
+
export declare const DynamicTnid: DynamicTnidNamespace;
|
|
35
37
|
export { getTnidVariantImpl, toUuidStringImpl, };
|
|
36
38
|
//# sourceMappingURL=dynamic.d.ts.map
|
package/esm/dynamic.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dynamic.d.ts","sourceRoot":"","sources":["../src/dynamic.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE/D,gEAAgE;AAChE,MAAM,MAAM,WAAW,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;AAgB5C,iBAAS,kBAAkB,CAAC,EAAE,EAAE,WAAW,GAAG,WAAW,CASxD;AAiBD,iBAAS,gBAAgB,CAAC,EAAE,EAAE,WAAW,EAAE,SAAS,GAAE,OAAe,GAAG,MAAM,CAmB7E;AAmDD,
|
|
1
|
+
{"version":3,"file":"dynamic.d.ts","sourceRoot":"","sources":["../src/dynamic.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE/D,gEAAgE;AAChE,MAAM,MAAM,WAAW,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;AAgB5C,iBAAS,kBAAkB,CAAC,EAAE,EAAE,WAAW,GAAG,WAAW,CASxD;AAiBD,iBAAS,gBAAgB,CAAC,EAAE,EAAE,WAAW,EAAE,SAAS,GAAE,OAAe,GAAG,MAAM,CAmB7E;AAmDD,gDAAgD;AAChD,MAAM,WAAW,oBAAoB;IACnC,kFAAkF;IAClF,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAClC,wBAAwB;IACxB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC5C,mEAAmE;IACnE,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,WAAW,CAAC;IACxD,uFAAuF;IACvF,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,CAAC;IAClF,iFAAiF;IACjF,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAClC,wBAAwB;IACxB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC5C,kEAAkE;IAClE,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,WAAW,CAAC;IAClE,mCAAmC;IACnC,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;IAC9B,6EAA6E;IAC7E,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC7C,gCAAgC;IAChC,OAAO,CAAC,EAAE,EAAE,WAAW,GAAG,MAAM,CAAC;IACjC,wDAAwD;IACxD,UAAU,CAAC,EAAE,EAAE,WAAW,GAAG,MAAM,CAAC;IACpC,iCAAiC;IACjC,UAAU,CAAC,EAAE,EAAE,WAAW,GAAG,WAAW,CAAC;IACzC,yCAAyC;IACzC,YAAY,CAAC,EAAE,EAAE,WAAW,EAAE,UAAU,CAAC,EAAE,IAAI,GAAG,MAAM,CAAC;CAC1D;AAED,mEAAmE;AACnE,eAAO,MAAM,WAAW,EAAE,oBA8GzB,CAAC;AAGF,OAAO,EACL,kBAAkB,EAClB,gBAAgB,GACjB,CAAC"}
|
package/esm/dynamic.js
CHANGED
|
@@ -80,9 +80,6 @@ function parseDynamicUuidStringImpl(uuid) {
|
|
|
80
80
|
}
|
|
81
81
|
return valueToTnidString(value);
|
|
82
82
|
}
|
|
83
|
-
// -----------------------------------------------------------------------------
|
|
84
|
-
// DynamicTnid Namespace
|
|
85
|
-
// -----------------------------------------------------------------------------
|
|
86
83
|
/** Static methods for working with any TNID regardless of name. */
|
|
87
84
|
export const DynamicTnid = {
|
|
88
85
|
/** Generate a new time-sortable TNID (variant 0) with runtime name validation. */
|
package/esm/uuidlike.d.ts
CHANGED
|
@@ -3,15 +3,17 @@ import type { DynamicTnid } from "./dynamic.js";
|
|
|
3
3
|
export type UuidLike = string & {
|
|
4
4
|
__uuidlike: true;
|
|
5
5
|
};
|
|
6
|
-
/**
|
|
7
|
-
export
|
|
6
|
+
/** Interface for UuidLike static methods. */
|
|
7
|
+
export interface UuidLikeNamespace {
|
|
8
8
|
/** Create from a TNID (always valid). */
|
|
9
|
-
|
|
9
|
+
fromTnid(id: DynamicTnid): UuidLike;
|
|
10
10
|
/** Parse UUID hex string (format validation only, not TNID validation). */
|
|
11
|
-
|
|
11
|
+
parse(s: string): UuidLike;
|
|
12
12
|
/** Try to convert to DynamicTnid (validates TNID structure). */
|
|
13
|
-
|
|
13
|
+
toTnid(uuid: UuidLike): DynamicTnid;
|
|
14
14
|
/** Format as uppercase UUID hex string. */
|
|
15
|
-
|
|
16
|
-
}
|
|
15
|
+
toUpperCase(uuid: UuidLike): UuidLike;
|
|
16
|
+
}
|
|
17
|
+
/** Wrapper for UUID hex strings that may or may not be valid TNIDs. */
|
|
18
|
+
export declare const UuidLike: UuidLikeNamespace;
|
|
17
19
|
//# sourceMappingURL=uuidlike.d.ts.map
|
package/esm/uuidlike.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uuidlike.d.ts","sourceRoot":"","sources":["../src/uuidlike.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,oGAAoG;AACpG,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG;IAAE,UAAU,EAAE,IAAI,CAAA;CAAE,CAAC;AAWrD,
|
|
1
|
+
{"version":3,"file":"uuidlike.d.ts","sourceRoot":"","sources":["../src/uuidlike.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,oGAAoG;AACpG,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG;IAAE,UAAU,EAAE,IAAI,CAAA;CAAE,CAAC;AAWrD,6CAA6C;AAC7C,MAAM,WAAW,iBAAiB;IAChC,yCAAyC;IACzC,QAAQ,CAAC,EAAE,EAAE,WAAW,GAAG,QAAQ,CAAC;IACpC,2EAA2E;IAC3E,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC3B,gEAAgE;IAChE,MAAM,CAAC,IAAI,EAAE,QAAQ,GAAG,WAAW,CAAC;IACpC,2CAA2C;IAC3C,WAAW,CAAC,IAAI,EAAE,QAAQ,GAAG,QAAQ,CAAC;CACvC;AAED,uEAAuE;AACvE,eAAO,MAAM,QAAQ,EAAE,iBAiCtB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tnid/core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "Type-safe, named, unique identifiers (TNIDs) - UUID-compatible IDs with embedded type names",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"uuid",
|
|
@@ -24,14 +24,19 @@
|
|
|
24
24
|
".": {
|
|
25
25
|
"import": "./esm/index.js",
|
|
26
26
|
"require": "./script/index.js"
|
|
27
|
+
},
|
|
28
|
+
"./uuid": {
|
|
29
|
+
"import": "./esm/uuid.js",
|
|
30
|
+
"require": "./script/uuid.js"
|
|
27
31
|
}
|
|
28
32
|
},
|
|
29
33
|
"scripts": {},
|
|
30
34
|
"publishConfig": {
|
|
31
35
|
"access": "public"
|
|
32
36
|
},
|
|
33
|
-
"
|
|
34
|
-
"
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=20"
|
|
35
39
|
},
|
|
40
|
+
"dependencies": {},
|
|
36
41
|
"_generatedBy": "dnt@dev"
|
|
37
42
|
}
|
package/script/_dnt.shims.d.ts
CHANGED
|
@@ -1,5 +1,2 @@
|
|
|
1
|
-
export
|
|
2
|
-
export declare const dntGlobalThis: Omit<typeof globalThis, "crypto"> & {
|
|
3
|
-
crypto: import("@deno/shim-crypto").Crypto;
|
|
4
|
-
};
|
|
1
|
+
export declare const dntGlobalThis: Omit<typeof globalThis, never>;
|
|
5
2
|
//# sourceMappingURL=_dnt.shims.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_dnt.shims.d.ts","sourceRoot":"","sources":["../src/_dnt.shims.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"_dnt.shims.d.ts","sourceRoot":"","sources":["../src/_dnt.shims.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,aAAa,gCAA2C,CAAC"}
|
package/script/_dnt.shims.js
CHANGED
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.dntGlobalThis =
|
|
4
|
-
const
|
|
5
|
-
var shim_crypto_2 = require("@deno/shim-crypto");
|
|
6
|
-
Object.defineProperty(exports, "crypto", { enumerable: true, get: function () { return shim_crypto_2.crypto; } });
|
|
7
|
-
const dntGlobals = {
|
|
8
|
-
crypto: shim_crypto_1.crypto,
|
|
9
|
-
};
|
|
3
|
+
exports.dntGlobalThis = void 0;
|
|
4
|
+
const dntGlobals = {};
|
|
10
5
|
exports.dntGlobalThis = createMergeProxy(globalThis, dntGlobals);
|
|
11
6
|
function createMergeProxy(baseObj, extObj) {
|
|
12
7
|
return new Proxy(baseObj, {
|
package/script/bits.js
CHANGED
|
@@ -3,39 +3,6 @@
|
|
|
3
3
|
// Bit Manipulation and TNID Generation
|
|
4
4
|
// Mask-based approach matching Rust implementation
|
|
5
5
|
// =============================================================================
|
|
6
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
-
if (k2 === undefined) k2 = k;
|
|
8
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
-
}
|
|
12
|
-
Object.defineProperty(o, k2, desc);
|
|
13
|
-
}) : (function(o, m, k, k2) {
|
|
14
|
-
if (k2 === undefined) k2 = k;
|
|
15
|
-
o[k2] = m[k];
|
|
16
|
-
}));
|
|
17
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
-
}) : function(o, v) {
|
|
20
|
-
o["default"] = v;
|
|
21
|
-
});
|
|
22
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
-
var ownKeys = function(o) {
|
|
24
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
-
var ar = [];
|
|
26
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
-
return ar;
|
|
28
|
-
};
|
|
29
|
-
return ownKeys(o);
|
|
30
|
-
};
|
|
31
|
-
return function (mod) {
|
|
32
|
-
if (mod && mod.__esModule) return mod;
|
|
33
|
-
var result = {};
|
|
34
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
-
__setModuleDefault(result, mod);
|
|
36
|
-
return result;
|
|
37
|
-
};
|
|
38
|
-
})();
|
|
39
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
7
|
exports.V0_RANDOM_MASK = exports.V1_RANDOM_MASK = exports.UUID_V8_MASK = void 0;
|
|
41
8
|
exports.nameMask = nameMask;
|
|
@@ -45,8 +12,8 @@ exports.buildTnidValue = buildTnidValue;
|
|
|
45
12
|
exports.valueToBytes = valueToBytes;
|
|
46
13
|
exports.generateV0 = generateV0;
|
|
47
14
|
exports.generateV1 = generateV1;
|
|
15
|
+
const crypto_js_1 = require("./crypto.js");
|
|
48
16
|
// Masks for UUID version 8 and variant bits
|
|
49
|
-
const dntShim = __importStar(require("./_dnt.shims.js"));
|
|
50
17
|
exports.UUID_V8_MASK = 0x00000000000080008000000000000000n;
|
|
51
18
|
// V1: 100 random bits scattered across the UUID
|
|
52
19
|
exports.V1_RANDOM_MASK = 0x00000fffffff0fff0fffffffffffffffn;
|
|
@@ -105,7 +72,7 @@ function generateV0(nameBits, timestampMs, randomBits) {
|
|
|
105
72
|
random = randomBits;
|
|
106
73
|
}
|
|
107
74
|
else {
|
|
108
|
-
const randomBytes =
|
|
75
|
+
const randomBytes = crypto_js_1.crypto.getRandomValues(new Uint8Array(8));
|
|
109
76
|
random = 0n;
|
|
110
77
|
for (const byte of randomBytes) {
|
|
111
78
|
random = (random << 8n) | BigInt(byte);
|
|
@@ -122,7 +89,7 @@ function generateV1(nameBits, randomBits) {
|
|
|
122
89
|
random = randomBits;
|
|
123
90
|
}
|
|
124
91
|
else {
|
|
125
|
-
const randomBytes =
|
|
92
|
+
const randomBytes = crypto_js_1.crypto.getRandomValues(new Uint8Array(16));
|
|
126
93
|
random = 0n;
|
|
127
94
|
for (const byte of randomBytes) {
|
|
128
95
|
random = (random << 8n) | BigInt(byte);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,MAAM,EAA4C;IAC7D,eAAe,CAAC,CAAC,SAAS,eAAe,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC;CACzD,CAAC"}
|
package/script/crypto.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Web Crypto API accessor
|
|
3
|
+
// Isolates the type assertion to one place. Works in:
|
|
4
|
+
// - Browsers (globalThis.crypto)
|
|
5
|
+
// - Deno (globalThis.crypto)
|
|
6
|
+
// - Node.js 20+ (globalThis.crypto)
|
|
7
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
|
+
if (k2 === undefined) k2 = k;
|
|
9
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
10
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
11
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
+
}
|
|
13
|
+
Object.defineProperty(o, k2, desc);
|
|
14
|
+
}) : (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
o[k2] = m[k];
|
|
17
|
+
}));
|
|
18
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
19
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
20
|
+
}) : function(o, v) {
|
|
21
|
+
o["default"] = v;
|
|
22
|
+
});
|
|
23
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
24
|
+
var ownKeys = function(o) {
|
|
25
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
26
|
+
var ar = [];
|
|
27
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
28
|
+
return ar;
|
|
29
|
+
};
|
|
30
|
+
return ownKeys(o);
|
|
31
|
+
};
|
|
32
|
+
return function (mod) {
|
|
33
|
+
if (mod && mod.__esModule) return mod;
|
|
34
|
+
var result = {};
|
|
35
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
36
|
+
__setModuleDefault(result, mod);
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
39
|
+
})();
|
|
40
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
+
exports.crypto = void 0;
|
|
42
|
+
// deno-lint-ignore no-explicit-any
|
|
43
|
+
const dntShim = __importStar(require("./_dnt.shims.js"));
|
|
44
|
+
exports.crypto = dntShim.dntGlobalThis.crypto;
|
package/script/dynamic.d.ts
CHANGED
|
@@ -3,34 +3,36 @@ import type { Case, TnidValue, TnidVariant } from "./types.js";
|
|
|
3
3
|
export type DynamicTnid = TnidValue<string>;
|
|
4
4
|
declare function getTnidVariantImpl(id: DynamicTnid): TnidVariant;
|
|
5
5
|
declare function toUuidStringImpl(id: DynamicTnid, upperCase?: boolean): string;
|
|
6
|
-
/**
|
|
7
|
-
export
|
|
6
|
+
/** Interface for DynamicTnid static methods. */
|
|
7
|
+
export interface DynamicTnidNamespace {
|
|
8
8
|
/** Generate a new time-sortable TNID (variant 0) with runtime name validation. */
|
|
9
|
-
|
|
9
|
+
new_v0(name: string): DynamicTnid;
|
|
10
10
|
/** Alias for new_v0. */
|
|
11
|
-
|
|
11
|
+
new_time_ordered(name: string): DynamicTnid;
|
|
12
12
|
/** Generate a new time-sortable TNID with a specific timestamp. */
|
|
13
|
-
|
|
13
|
+
new_v0_with_time(name: string, time: Date): DynamicTnid;
|
|
14
14
|
/** Generate a new time-sortable TNID with explicit timestamp and random components. */
|
|
15
|
-
|
|
15
|
+
new_v0_with_parts(name: string, epochMillis: bigint, random: bigint): DynamicTnid;
|
|
16
16
|
/** Generate a new high-entropy TNID (variant 1) with runtime name validation. */
|
|
17
|
-
|
|
17
|
+
new_v1(name: string): DynamicTnid;
|
|
18
18
|
/** Alias for new_v1. */
|
|
19
|
-
|
|
19
|
+
new_high_entropy(name: string): DynamicTnid;
|
|
20
20
|
/** Generate a new high-entropy TNID with explicit random bits. */
|
|
21
|
-
|
|
21
|
+
new_v1_with_random(name: string, randomBits: bigint): DynamicTnid;
|
|
22
22
|
/** Parse any valid TNID string. */
|
|
23
|
-
|
|
23
|
+
parse(s: string): DynamicTnid;
|
|
24
24
|
/** Parse a UUID hex string into a DynamicTnid (validates TNID structure). */
|
|
25
|
-
|
|
25
|
+
parse_uuid_string(uuid: string): DynamicTnid;
|
|
26
26
|
/** Get the name from a TNID. */
|
|
27
|
-
|
|
27
|
+
getName(id: DynamicTnid): string;
|
|
28
28
|
/** Get the name encoded as a 5-character hex string. */
|
|
29
|
-
|
|
29
|
+
getNameHex(id: DynamicTnid): string;
|
|
30
30
|
/** Get the variant of a TNID. */
|
|
31
|
-
|
|
31
|
+
getVariant(id: DynamicTnid): TnidVariant;
|
|
32
32
|
/** Convert to UUID hex string format. */
|
|
33
|
-
|
|
34
|
-
}
|
|
33
|
+
toUuidString(id: DynamicTnid, caseFormat?: Case): string;
|
|
34
|
+
}
|
|
35
|
+
/** Static methods for working with any TNID regardless of name. */
|
|
36
|
+
export declare const DynamicTnid: DynamicTnidNamespace;
|
|
35
37
|
export { getTnidVariantImpl, toUuidStringImpl, };
|
|
36
38
|
//# sourceMappingURL=dynamic.d.ts.map
|
package/script/dynamic.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dynamic.d.ts","sourceRoot":"","sources":["../src/dynamic.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE/D,gEAAgE;AAChE,MAAM,MAAM,WAAW,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;AAgB5C,iBAAS,kBAAkB,CAAC,EAAE,EAAE,WAAW,GAAG,WAAW,CASxD;AAiBD,iBAAS,gBAAgB,CAAC,EAAE,EAAE,WAAW,EAAE,SAAS,GAAE,OAAe,GAAG,MAAM,CAmB7E;AAmDD,
|
|
1
|
+
{"version":3,"file":"dynamic.d.ts","sourceRoot":"","sources":["../src/dynamic.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE/D,gEAAgE;AAChE,MAAM,MAAM,WAAW,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;AAgB5C,iBAAS,kBAAkB,CAAC,EAAE,EAAE,WAAW,GAAG,WAAW,CASxD;AAiBD,iBAAS,gBAAgB,CAAC,EAAE,EAAE,WAAW,EAAE,SAAS,GAAE,OAAe,GAAG,MAAM,CAmB7E;AAmDD,gDAAgD;AAChD,MAAM,WAAW,oBAAoB;IACnC,kFAAkF;IAClF,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAClC,wBAAwB;IACxB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC5C,mEAAmE;IACnE,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,WAAW,CAAC;IACxD,uFAAuF;IACvF,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,CAAC;IAClF,iFAAiF;IACjF,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAClC,wBAAwB;IACxB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC5C,kEAAkE;IAClE,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,WAAW,CAAC;IAClE,mCAAmC;IACnC,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;IAC9B,6EAA6E;IAC7E,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC7C,gCAAgC;IAChC,OAAO,CAAC,EAAE,EAAE,WAAW,GAAG,MAAM,CAAC;IACjC,wDAAwD;IACxD,UAAU,CAAC,EAAE,EAAE,WAAW,GAAG,MAAM,CAAC;IACpC,iCAAiC;IACjC,UAAU,CAAC,EAAE,EAAE,WAAW,GAAG,WAAW,CAAC;IACzC,yCAAyC;IACzC,YAAY,CAAC,EAAE,EAAE,WAAW,EAAE,UAAU,CAAC,EAAE,IAAI,GAAG,MAAM,CAAC;CAC1D;AAED,mEAAmE;AACnE,eAAO,MAAM,WAAW,EAAE,oBA8GzB,CAAC;AAGF,OAAO,EACL,kBAAkB,EAClB,gBAAgB,GACjB,CAAC"}
|
package/script/dynamic.js
CHANGED
|
@@ -85,9 +85,6 @@ function parseDynamicUuidStringImpl(uuid) {
|
|
|
85
85
|
}
|
|
86
86
|
return (0, uuid_js_1.valueToTnidString)(value);
|
|
87
87
|
}
|
|
88
|
-
// -----------------------------------------------------------------------------
|
|
89
|
-
// DynamicTnid Namespace
|
|
90
|
-
// -----------------------------------------------------------------------------
|
|
91
88
|
/** Static methods for working with any TNID regardless of name. */
|
|
92
89
|
exports.DynamicTnid = {
|
|
93
90
|
/** Generate a new time-sortable TNID (variant 0) with runtime name validation. */
|
package/script/uuidlike.d.ts
CHANGED
|
@@ -3,15 +3,17 @@ import type { DynamicTnid } from "./dynamic.js";
|
|
|
3
3
|
export type UuidLike = string & {
|
|
4
4
|
__uuidlike: true;
|
|
5
5
|
};
|
|
6
|
-
/**
|
|
7
|
-
export
|
|
6
|
+
/** Interface for UuidLike static methods. */
|
|
7
|
+
export interface UuidLikeNamespace {
|
|
8
8
|
/** Create from a TNID (always valid). */
|
|
9
|
-
|
|
9
|
+
fromTnid(id: DynamicTnid): UuidLike;
|
|
10
10
|
/** Parse UUID hex string (format validation only, not TNID validation). */
|
|
11
|
-
|
|
11
|
+
parse(s: string): UuidLike;
|
|
12
12
|
/** Try to convert to DynamicTnid (validates TNID structure). */
|
|
13
|
-
|
|
13
|
+
toTnid(uuid: UuidLike): DynamicTnid;
|
|
14
14
|
/** Format as uppercase UUID hex string. */
|
|
15
|
-
|
|
16
|
-
}
|
|
15
|
+
toUpperCase(uuid: UuidLike): UuidLike;
|
|
16
|
+
}
|
|
17
|
+
/** Wrapper for UUID hex strings that may or may not be valid TNIDs. */
|
|
18
|
+
export declare const UuidLike: UuidLikeNamespace;
|
|
17
19
|
//# sourceMappingURL=uuidlike.d.ts.map
|
package/script/uuidlike.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uuidlike.d.ts","sourceRoot":"","sources":["../src/uuidlike.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,oGAAoG;AACpG,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG;IAAE,UAAU,EAAE,IAAI,CAAA;CAAE,CAAC;AAWrD,
|
|
1
|
+
{"version":3,"file":"uuidlike.d.ts","sourceRoot":"","sources":["../src/uuidlike.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,oGAAoG;AACpG,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG;IAAE,UAAU,EAAE,IAAI,CAAA;CAAE,CAAC;AAWrD,6CAA6C;AAC7C,MAAM,WAAW,iBAAiB;IAChC,yCAAyC;IACzC,QAAQ,CAAC,EAAE,EAAE,WAAW,GAAG,QAAQ,CAAC;IACpC,2EAA2E;IAC3E,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC3B,gEAAgE;IAChE,MAAM,CAAC,IAAI,EAAE,QAAQ,GAAG,WAAW,CAAC;IACpC,2CAA2C;IAC3C,WAAW,CAAC,IAAI,EAAE,QAAQ,GAAG,QAAQ,CAAC;CACvC;AAED,uEAAuE;AACvE,eAAO,MAAM,QAAQ,EAAE,iBAiCtB,CAAC"}
|