@tnid/core 0.0.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.
Files changed (65) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +509 -0
  3. package/esm/_dnt.shims.d.ts +5 -0
  4. package/esm/_dnt.shims.d.ts.map +1 -0
  5. package/esm/_dnt.shims.js +61 -0
  6. package/esm/bits.d.ts +18 -0
  7. package/esm/bits.d.ts.map +1 -0
  8. package/esm/bits.js +91 -0
  9. package/esm/data_encoding.d.ts +16 -0
  10. package/esm/data_encoding.d.ts.map +1 -0
  11. package/esm/data_encoding.js +103 -0
  12. package/esm/dynamic.d.ts +36 -0
  13. package/esm/dynamic.d.ts.map +1 -0
  14. package/esm/dynamic.js +173 -0
  15. package/esm/factory.d.ts +27 -0
  16. package/esm/factory.d.ts.map +1 -0
  17. package/esm/factory.js +107 -0
  18. package/esm/index.d.ts +5 -0
  19. package/esm/index.d.ts.map +1 -0
  20. package/esm/index.js +9 -0
  21. package/esm/name_encoding.d.ts +15 -0
  22. package/esm/name_encoding.d.ts.map +1 -0
  23. package/esm/name_encoding.js +92 -0
  24. package/esm/package.json +3 -0
  25. package/esm/types.d.ts +93 -0
  26. package/esm/types.d.ts.map +1 -0
  27. package/esm/types.js +4 -0
  28. package/esm/uuid.d.ts +17 -0
  29. package/esm/uuid.d.ts.map +1 -0
  30. package/esm/uuid.js +55 -0
  31. package/esm/uuidlike.d.ts +17 -0
  32. package/esm/uuidlike.d.ts.map +1 -0
  33. package/esm/uuidlike.js +38 -0
  34. package/package.json +37 -0
  35. package/script/_dnt.shims.d.ts +5 -0
  36. package/script/_dnt.shims.d.ts.map +1 -0
  37. package/script/_dnt.shims.js +65 -0
  38. package/script/bits.d.ts +18 -0
  39. package/script/bits.d.ts.map +1 -0
  40. package/script/bits.js +134 -0
  41. package/script/data_encoding.d.ts +16 -0
  42. package/script/data_encoding.d.ts.map +1 -0
  43. package/script/data_encoding.js +108 -0
  44. package/script/dynamic.d.ts +36 -0
  45. package/script/dynamic.d.ts.map +1 -0
  46. package/script/dynamic.js +176 -0
  47. package/script/factory.d.ts +27 -0
  48. package/script/factory.d.ts.map +1 -0
  49. package/script/factory.js +110 -0
  50. package/script/index.d.ts +5 -0
  51. package/script/index.d.ts.map +1 -0
  52. package/script/index.js +15 -0
  53. package/script/name_encoding.d.ts +15 -0
  54. package/script/name_encoding.d.ts.map +1 -0
  55. package/script/name_encoding.js +97 -0
  56. package/script/package.json +3 -0
  57. package/script/types.d.ts +93 -0
  58. package/script/types.d.ts.map +1 -0
  59. package/script/types.js +5 -0
  60. package/script/uuid.d.ts +17 -0
  61. package/script/uuid.d.ts.map +1 -0
  62. package/script/uuid.js +65 -0
  63. package/script/uuidlike.d.ts +17 -0
  64. package/script/uuidlike.d.ts.map +1 -0
  65. package/script/uuidlike.js +41 -0
@@ -0,0 +1,92 @@
1
+ // =============================================================================
2
+ // Name Encoding - 5-bit encoding for TNID names
3
+ // Valid characters: 0-4, a-z (31 chars + null terminator = 32 = 2^5)
4
+ // =============================================================================
5
+ const NAME_CHAR_TO_VALUE = {
6
+ "0": 1,
7
+ "1": 2,
8
+ "2": 3,
9
+ "3": 4,
10
+ "4": 5,
11
+ a: 6,
12
+ b: 7,
13
+ c: 8,
14
+ d: 9,
15
+ e: 10,
16
+ f: 11,
17
+ g: 12,
18
+ h: 13,
19
+ i: 14,
20
+ j: 15,
21
+ k: 16,
22
+ l: 17,
23
+ m: 18,
24
+ n: 19,
25
+ o: 20,
26
+ p: 21,
27
+ q: 22,
28
+ r: 23,
29
+ s: 24,
30
+ t: 25,
31
+ u: 26,
32
+ v: 27,
33
+ w: 28,
34
+ x: 29,
35
+ y: 30,
36
+ z: 31,
37
+ };
38
+ const NAME_VALUE_TO_CHAR = {};
39
+ for (const [char, value] of Object.entries(NAME_CHAR_TO_VALUE)) {
40
+ NAME_VALUE_TO_CHAR[value] = char;
41
+ }
42
+ const VALID_NAME_CHARS = new Set("01234abcdefghijklmnopqrstuvwxyz".split(""));
43
+ /**
44
+ * Encode a name into 20 bits (4 chars * 5 bits each).
45
+ * Null-pads on the right (least significant bits).
46
+ */
47
+ export function encodeName(name) {
48
+ let result = 0;
49
+ for (let i = 0; i < 4; i++) {
50
+ result <<= 5;
51
+ if (i < name.length) {
52
+ const value = NAME_CHAR_TO_VALUE[name[i]];
53
+ if (value === undefined) {
54
+ throw new Error(`Invalid name character: ${name[i]}`);
55
+ }
56
+ result |= value;
57
+ }
58
+ // else: null (0) is already the default
59
+ }
60
+ return result;
61
+ }
62
+ /**
63
+ * Decode 20 bits back to a name string.
64
+ */
65
+ export function decodeName(encoded) {
66
+ let result = "";
67
+ for (let i = 0; i < 4; i++) {
68
+ const shift = (3 - i) * 5;
69
+ const value = (encoded >> shift) & 0x1f;
70
+ if (value === 0)
71
+ break; // null terminator
72
+ const char = NAME_VALUE_TO_CHAR[value];
73
+ if (!char) {
74
+ throw new Error(`Invalid encoded name value: ${value}`);
75
+ }
76
+ result += char;
77
+ }
78
+ return result;
79
+ }
80
+ /**
81
+ * Validate a name at runtime.
82
+ * Must be 1-4 characters, each being 0-4 or a-z.
83
+ */
84
+ export function isValidNameRuntime(name) {
85
+ if (name.length < 1 || name.length > 4)
86
+ return false;
87
+ for (const char of name) {
88
+ if (!VALID_NAME_CHARS.has(char))
89
+ return false;
90
+ }
91
+ return true;
92
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "module"
3
+ }
package/esm/types.d.ts ADDED
@@ -0,0 +1,93 @@
1
+ /** Valid characters for TNID names (5-bit encoding): 0-4, a-z */
2
+ export type NameChar = "0" | "1" | "2" | "3" | "4" | "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z";
3
+ /** Recursive validation - check one character at a time to avoid type explosion */
4
+ type ValidateNameChars<S extends string> = S extends "" ? true : S extends `${infer First}${infer Rest}` ? First extends NameChar ? ValidateNameChars<Rest> : false : false;
5
+ /** Check string length is 1-4 using tuple length trick */
6
+ type StringLength<S extends string, Acc extends unknown[] = []> = S extends "" ? Acc["length"] : S extends `${infer _}${infer Rest}` ? StringLength<Rest, [...Acc, unknown]> : never;
7
+ type ValidLength = 1 | 2 | 3 | 4;
8
+ /**
9
+ * Validate a TNID name at compile time.
10
+ * Must be 1-4 characters, each being a valid NameChar.
11
+ */
12
+ export type ValidateName<S extends string> = ValidateNameChars<S> extends true ? StringLength<S> extends ValidLength ? S : never : never;
13
+ /**
14
+ * A TNID value branded with its name. At runtime this is just a string.
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * // Define a NamedTnid and matching type
19
+ * const UserId = Tnid("user");
20
+ * type UserId = TnidType<typeof UserId>;
21
+ *
22
+ * // Generate IDs
23
+ * const id: UserId = UserId.new_v0(); // time-sortable
24
+ * const id2: UserId = UserId.new_v1(); // high-entropy random
25
+ *
26
+ * // Parse from strings
27
+ * const parsed = UserId.parse("user.abc..."); // validates name matches
28
+ * const fromUuid = UserId.parseUuidString("d6157329-4640-8e30-...");
29
+ *
30
+ * // DynamicTnid - runtime name validation
31
+ * DynamicTnid.new_v0("item"); // create with runtime name
32
+ * DynamicTnid.new_v1("item"); // create with runtime name
33
+ * DynamicTnid.parse("post.xyz..."); // parse any TNID
34
+ * DynamicTnid.getName(id); // "user"
35
+ * DynamicTnid.getVariant(id); // "v0" | "v1" | "v2" | "v3"
36
+ *
37
+ * // UuidLike - wrapper for any UUID hex string
38
+ * UuidLike.fromTnid(id); // TNID to UUID string
39
+ * UuidLike.parse("d6157329-..."); // parse any UUID (format only)
40
+ * UuidLike.toTnid(uuid); // UUID to DynamicTnid (validates)
41
+ *
42
+ * // Type safety: different names are incompatible
43
+ * const PostId = Tnid("post");
44
+ * type PostId = TnidType<typeof PostId>;
45
+ * const postId: PostId = PostId.new_v0();
46
+ * // id = postId; // Compile error!
47
+ *
48
+ * // DynamicTnid type accepts any TNID
49
+ * function log(id: DynamicTnid) { console.log(DynamicTnid.getName(id)); }
50
+ * log(id); // works
51
+ * log(postId); // works
52
+ * ```
53
+ */
54
+ export type TnidValue<Name extends string> = string & {
55
+ tnid: Name;
56
+ };
57
+ /** TNID variant: v0=time-ordered, v1=random, v2/v3=reserved */
58
+ export type TnidVariant = "v0" | "v1" | "v2" | "v3";
59
+ /** Case for UUID hex string formatting. */
60
+ export type Case = "lower" | "upper";
61
+ export interface NamedTnid<Name extends string> {
62
+ /** The TNID name */
63
+ readonly name: Name;
64
+ /** Generate a new time-sortable TNID (variant 0) */
65
+ new_v0(): TnidValue<Name>;
66
+ /** Generate a new random TNID (variant 1) */
67
+ new_v1(): TnidValue<Name>;
68
+ /** Construct a V0 TNID from specific parts (for deterministic testing) */
69
+ v0_from_parts(timestampMs: bigint, randomBits: bigint): TnidValue<Name>;
70
+ /** Construct a V1 TNID from specific parts (for deterministic testing) */
71
+ v1_from_parts(randomBits: bigint): TnidValue<Name>;
72
+ /**
73
+ * Parse and validate a TNID string.
74
+ * @throws Error if the string is invalid or the name doesn't match
75
+ */
76
+ parse(s: string): TnidValue<Name>;
77
+ /**
78
+ * Parse a UUID hex string into a TNID.
79
+ * Validates that it's a valid UUIDv8 TNID and the name matches.
80
+ * @throws Error if the UUID is invalid or the name doesn't match
81
+ */
82
+ parseUuidString(uuid: string): TnidValue<Name>;
83
+ /** Get the name encoded as a 5-character hex string. */
84
+ nameHex(): string;
85
+ /** Get the variant of a TNID. */
86
+ variant(id: TnidValue<Name>): TnidVariant;
87
+ /** Convert a TNID to UUID hex string format. */
88
+ toUuidString(id: TnidValue<Name>, caseFormat?: Case): string;
89
+ }
90
+ /** Extract the `TnidValue` type from a NamedTnid. */
91
+ export type TnidType<T> = T extends NamedTnid<infer N> ? TnidValue<N> : never;
92
+ export {};
93
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/esm/types.js ADDED
@@ -0,0 +1,4 @@
1
+ // =============================================================================
2
+ // TNID Type Definitions
3
+ // =============================================================================
4
+ export {};
package/esm/uuid.d.ts ADDED
@@ -0,0 +1,17 @@
1
+ import type { TnidVariant } from "./types.js";
2
+ export declare const UUID_REGEX: RegExp;
3
+ /** Convert a 128-bit value to UUID hex string format */
4
+ export declare function valueToUuidString(value: bigint, upperCase?: boolean): string;
5
+ /** Parse a UUID hex string to a 128-bit value */
6
+ export declare function parseUuidStringToValue(uuid: string): bigint;
7
+ /** Extract name bits from a 128-bit TNID value */
8
+ export declare function extractNameBitsFromValue(value: bigint): number;
9
+ /** Extract TNID variant from a 128-bit value */
10
+ export declare function extractVariantFromValue(value: bigint): TnidVariant;
11
+ /** Validate that a 128-bit value has correct UUIDv8 version and variant bits */
12
+ export declare function validateUuidBits(value: bigint): boolean;
13
+ /** Convert bytes to TNID string */
14
+ export declare function bytesToTnidString(bytes: Uint8Array, name: string): string;
15
+ /** Convert 128-bit value to TNID string */
16
+ export declare function valueToTnidString(value: bigint): string;
17
+ //# sourceMappingURL=uuid.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uuid.d.ts","sourceRoot":"","sources":["../src/uuid.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAK9C,eAAO,MAAM,UAAU,QAC4C,CAAC;AAEpE,wDAAwD;AACxD,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,EACb,SAAS,GAAE,OAAe,GACzB,MAAM,CAMR;AAED,iDAAiD;AACjD,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAM3D;AAED,kDAAkD;AAClD,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED,gDAAgD;AAChD,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,CAIlE;AAED,gFAAgF;AAChF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAUvD;AAED,mCAAmC;AACnC,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAGzE;AAED,2CAA2C;AAC3C,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKvD"}
package/esm/uuid.js ADDED
@@ -0,0 +1,55 @@
1
+ // =============================================================================
2
+ // UUID String Utilities
3
+ // =============================================================================
4
+ import { decodeName } from "./name_encoding.js";
5
+ import { encodeData } from "./data_encoding.js";
6
+ import { valueToBytes } from "./bits.js";
7
+ export const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
8
+ /** Convert a 128-bit value to UUID hex string format */
9
+ export function valueToUuidString(value, upperCase = false) {
10
+ const hex = value.toString(16).padStart(32, "0");
11
+ const uuid = `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
12
+ return upperCase ? uuid.toUpperCase() : uuid.toLowerCase();
13
+ }
14
+ /** Parse a UUID hex string to a 128-bit value */
15
+ export function parseUuidStringToValue(uuid) {
16
+ if (!UUID_REGEX.test(uuid)) {
17
+ throw new Error(`Invalid UUID format: ${uuid}`);
18
+ }
19
+ const hex = uuid.replace(/-/g, "");
20
+ return BigInt("0x" + hex);
21
+ }
22
+ /** Extract name bits from a 128-bit TNID value */
23
+ export function extractNameBitsFromValue(value) {
24
+ return Number((value >> 108n) & 0xfffffn);
25
+ }
26
+ /** Extract TNID variant from a 128-bit value */
27
+ export function extractVariantFromValue(value) {
28
+ const variantBits = Number((value >> 60n) & 3n);
29
+ const variants = ["v0", "v1", "v2", "v3"];
30
+ return variants[variantBits];
31
+ }
32
+ /** Validate that a 128-bit value has correct UUIDv8 version and variant bits */
33
+ export function validateUuidBits(value) {
34
+ // Check UUID version (bits 76-79 should be 0x8)
35
+ const version = Number((value >> 76n) & 0xfn);
36
+ if (version !== 8)
37
+ return false;
38
+ // Check UUID variant (bits 62-63 should be 0b10)
39
+ const variant = Number((value >> 62n) & 3n);
40
+ if (variant !== 0b10)
41
+ return false;
42
+ return true;
43
+ }
44
+ /** Convert bytes to TNID string */
45
+ export function bytesToTnidString(bytes, name) {
46
+ const dataEncoded = encodeData(bytes);
47
+ return `${name}.${dataEncoded}`;
48
+ }
49
+ /** Convert 128-bit value to TNID string */
50
+ export function valueToTnidString(value) {
51
+ const nameBits = extractNameBitsFromValue(value);
52
+ const name = decodeName(nameBits);
53
+ const bytes = valueToBytes(value);
54
+ return bytesToTnidString(bytes, name);
55
+ }
@@ -0,0 +1,17 @@
1
+ import type { DynamicTnid } from "./dynamic.js";
2
+ /** A UUID hex string (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) that may or may not be a valid TNID. */
3
+ export type UuidLike = string & {
4
+ __uuidlike: true;
5
+ };
6
+ /** Wrapper for UUID hex strings that may or may not be valid TNIDs. */
7
+ export declare const UuidLike: {
8
+ /** Create from a TNID (always valid). */
9
+ readonly fromTnid: (id: DynamicTnid) => UuidLike;
10
+ /** Parse UUID hex string (format validation only, not TNID validation). */
11
+ readonly parse: (s: string) => UuidLike;
12
+ /** Try to convert to DynamicTnid (validates TNID structure). */
13
+ readonly toTnid: (uuid: UuidLike) => DynamicTnid;
14
+ /** Format as uppercase UUID hex string. */
15
+ readonly toUpperCase: (uuid: UuidLike) => UuidLike;
16
+ };
17
+ //# sourceMappingURL=uuidlike.d.ts.map
@@ -0,0 +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,uEAAuE;AACvE,eAAO,MAAM,QAAQ;IACnB,yCAAyC;4BAC5B,WAAW,KAAG,QAAQ;IAInC,2EAA2E;wBAClE,MAAM,KAAG,QAAQ;IAQ1B,gEAAgE;4BACnD,QAAQ,KAAG,WAAW;IAanC,2CAA2C;iCACzB,QAAQ,KAAG,QAAQ;CAG7B,CAAC"}
@@ -0,0 +1,38 @@
1
+ // =============================================================================
2
+ // UuidLike - Wrapper for UUID hex strings
3
+ // =============================================================================
4
+ import { decodeName, isValidNameRuntime } from "./name_encoding.js";
5
+ import { UUID_REGEX, parseUuidStringToValue, validateUuidBits, extractNameBitsFromValue, valueToTnidString, } from "./uuid.js";
6
+ import { toUuidStringImpl } from "./dynamic.js";
7
+ /** Wrapper for UUID hex strings that may or may not be valid TNIDs. */
8
+ export const UuidLike = {
9
+ /** Create from a TNID (always valid). */
10
+ fromTnid(id) {
11
+ return toUuidStringImpl(id, false);
12
+ },
13
+ /** Parse UUID hex string (format validation only, not TNID validation). */
14
+ parse(s) {
15
+ // Validate format only
16
+ if (!UUID_REGEX.test(s)) {
17
+ throw new Error(`Invalid UUID format: ${s}`);
18
+ }
19
+ return s.toLowerCase();
20
+ },
21
+ /** Try to convert to DynamicTnid (validates TNID structure). */
22
+ toTnid(uuid) {
23
+ const value = parseUuidStringToValue(uuid);
24
+ if (!validateUuidBits(value)) {
25
+ throw new Error("Invalid TNID: not a valid UUIDv8");
26
+ }
27
+ const nameBits = extractNameBitsFromValue(value);
28
+ const name = decodeName(nameBits);
29
+ if (!isValidNameRuntime(name)) {
30
+ throw new Error("Invalid TNID: invalid name encoding");
31
+ }
32
+ return valueToTnidString(value);
33
+ },
34
+ /** Format as uppercase UUID hex string. */
35
+ toUpperCase(uuid) {
36
+ return uuid.toUpperCase();
37
+ },
38
+ };
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@tnid/core",
3
+ "version": "0.0.1",
4
+ "description": "Type-safe, named, unique identifiers (TNIDs) - UUID-compatible IDs with embedded type names",
5
+ "keywords": [
6
+ "uuid",
7
+ "id",
8
+ "identifier",
9
+ "tnid",
10
+ "typed",
11
+ "type-safe"
12
+ ],
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/tnid/tnid-typescript.git"
16
+ },
17
+ "license": "MIT",
18
+ "bugs": {
19
+ "url": "https://github.com/tnid/tnid-typescript/issues"
20
+ },
21
+ "main": "./script/index.js",
22
+ "module": "./esm/index.js",
23
+ "exports": {
24
+ ".": {
25
+ "import": "./esm/index.js",
26
+ "require": "./script/index.js"
27
+ }
28
+ },
29
+ "scripts": {},
30
+ "publishConfig": {
31
+ "access": "public"
32
+ },
33
+ "dependencies": {
34
+ "@deno/shim-crypto": "~0.3.1"
35
+ },
36
+ "_generatedBy": "dnt@dev"
37
+ }
@@ -0,0 +1,5 @@
1
+ export { crypto, type Crypto, type SubtleCrypto, type AlgorithmIdentifier, type Algorithm, type RsaOaepParams, type BufferSource, type AesCtrParams, type AesCbcParams, type AesGcmParams, type CryptoKey, type KeyAlgorithm, type KeyType, type KeyUsage, type EcdhKeyDeriveParams, type HkdfParams, type HashAlgorithmIdentifier, type Pbkdf2Params, type AesDerivedKeyParams, type HmacImportParams, type JsonWebKey, type RsaOtherPrimesInfo, type KeyFormat, type RsaHashedKeyGenParams, type RsaKeyGenParams, type BigInteger, type EcKeyGenParams, type NamedCurve, type CryptoKeyPair, type AesKeyGenParams, type HmacKeyGenParams, type RsaHashedImportParams, type EcKeyImportParams, type AesKeyAlgorithm, type RsaPssParams, type EcdsaParams } from "@deno/shim-crypto";
2
+ export declare const dntGlobalThis: Omit<typeof globalThis, "crypto"> & {
3
+ crypto: import("@deno/shim-crypto").Crypto;
4
+ };
5
+ //# sourceMappingURL=_dnt.shims.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_dnt.shims.d.ts","sourceRoot":"","sources":["../src/_dnt.shims.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,KAAK,MAAM,EAAE,KAAK,YAAY,EAAE,KAAK,mBAAmB,EAAE,KAAK,SAAS,EAAE,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,KAAK,YAAY,EAAE,KAAK,YAAY,EAAE,KAAK,YAAY,EAAE,KAAK,SAAS,EAAE,KAAK,YAAY,EAAE,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,mBAAmB,EAAE,KAAK,UAAU,EAAE,KAAK,uBAAuB,EAAE,KAAK,YAAY,EAAE,KAAK,mBAAmB,EAAE,KAAK,gBAAgB,EAAE,KAAK,UAAU,EAAE,KAAK,kBAAkB,EAAE,KAAK,SAAS,EAAE,KAAK,qBAAqB,EAAE,KAAK,eAAe,EAAE,KAAK,UAAU,EAAE,KAAK,cAAc,EAAE,KAAK,UAAU,EAAE,KAAK,aAAa,EAAE,KAAK,eAAe,EAAE,KAAK,gBAAgB,EAAE,KAAK,qBAAqB,EAAE,KAAK,iBAAiB,EAAE,KAAK,eAAe,EAAE,KAAK,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAKrvB,eAAO,MAAM,aAAa;;CAA2C,CAAC"}
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.dntGlobalThis = exports.crypto = void 0;
4
+ const shim_crypto_1 = require("@deno/shim-crypto");
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
+ };
10
+ exports.dntGlobalThis = createMergeProxy(globalThis, dntGlobals);
11
+ function createMergeProxy(baseObj, extObj) {
12
+ return new Proxy(baseObj, {
13
+ get(_target, prop, _receiver) {
14
+ if (prop in extObj) {
15
+ return extObj[prop];
16
+ }
17
+ else {
18
+ return baseObj[prop];
19
+ }
20
+ },
21
+ set(_target, prop, value) {
22
+ if (prop in extObj) {
23
+ delete extObj[prop];
24
+ }
25
+ baseObj[prop] = value;
26
+ return true;
27
+ },
28
+ deleteProperty(_target, prop) {
29
+ let success = false;
30
+ if (prop in extObj) {
31
+ delete extObj[prop];
32
+ success = true;
33
+ }
34
+ if (prop in baseObj) {
35
+ delete baseObj[prop];
36
+ success = true;
37
+ }
38
+ return success;
39
+ },
40
+ ownKeys(_target) {
41
+ const baseKeys = Reflect.ownKeys(baseObj);
42
+ const extKeys = Reflect.ownKeys(extObj);
43
+ const extKeysSet = new Set(extKeys);
44
+ return [...baseKeys.filter((k) => !extKeysSet.has(k)), ...extKeys];
45
+ },
46
+ defineProperty(_target, prop, desc) {
47
+ if (prop in extObj) {
48
+ delete extObj[prop];
49
+ }
50
+ Reflect.defineProperty(baseObj, prop, desc);
51
+ return true;
52
+ },
53
+ getOwnPropertyDescriptor(_target, prop) {
54
+ if (prop in extObj) {
55
+ return Reflect.getOwnPropertyDescriptor(extObj, prop);
56
+ }
57
+ else {
58
+ return Reflect.getOwnPropertyDescriptor(baseObj, prop);
59
+ }
60
+ },
61
+ has(_target, prop) {
62
+ return prop in extObj || prop in baseObj;
63
+ },
64
+ });
65
+ }
@@ -0,0 +1,18 @@
1
+ export declare const UUID_V8_MASK = 604472133179351442128896n;
2
+ export declare const V1_RANDOM_MASK = 324518552525041477071872066584575n;
3
+ export declare const V0_RANDOM_MASK = 144115188075855871n;
4
+ /** Place name bits in their correct position (bits 108-127) */
5
+ export declare function nameMask(nameBits: number): bigint;
6
+ /** Place UUID version/variant and TNID variant in their correct positions */
7
+ export declare function uuidAndVariantMask(tnidVariant: bigint): bigint;
8
+ /** Scatter 43-bit timestamp into its three positions within the 128-bit ID */
9
+ export declare function millisMask(millisSinceEpoch: bigint): bigint;
10
+ /** Build a 128-bit TNID value using mask-based OR operations */
11
+ export declare function buildTnidValue(nameBits: number, payloadMask: bigint, tnidVariant: bigint): bigint;
12
+ /** Convert 128-bit value to byte array */
13
+ export declare function valueToBytes(value: bigint): Uint8Array;
14
+ /** Generate a V0 (time-ordered) TNID as bytes */
15
+ export declare function generateV0(nameBits: number, timestampMs?: bigint, randomBits?: bigint): Uint8Array;
16
+ /** Generate a V1 (high-entropy random) TNID as bytes */
17
+ export declare function generateV1(nameBits: number, randomBits?: bigint): Uint8Array;
18
+ //# sourceMappingURL=bits.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bits.d.ts","sourceRoot":"","sources":["../src/bits.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,YAAY,4BAA0C,CAAC;AAGpE,eAAO,MAAM,cAAc,qCAA0C,CAAC;AAGtE,eAAO,MAAM,cAAc,sBAA0C,CAAC;AAOtE,+DAA+D;AAC/D,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED,6EAA6E;AAC7E,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED,8EAA8E;AAC9E,wBAAgB,UAAU,CAAC,gBAAgB,EAAE,MAAM,GAAG,MAAM,CAmB3D;AAED,gEAAgE;AAChE,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,GAClB,MAAM,CAER;AAED,0CAA0C;AAC1C,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAQtD;AAED,iDAAiD;AACjD,wBAAgB,UAAU,CACxB,QAAQ,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,MAAM,EACpB,UAAU,CAAC,EAAE,MAAM,GAClB,UAAU,CAmBZ;AAED,wDAAwD;AACxD,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,UAAU,CAe5E"}
package/script/bits.js ADDED
@@ -0,0 +1,134 @@
1
+ "use strict";
2
+ // =============================================================================
3
+ // Bit Manipulation and TNID Generation
4
+ // Mask-based approach matching Rust implementation
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
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ exports.V0_RANDOM_MASK = exports.V1_RANDOM_MASK = exports.UUID_V8_MASK = void 0;
41
+ exports.nameMask = nameMask;
42
+ exports.uuidAndVariantMask = uuidAndVariantMask;
43
+ exports.millisMask = millisMask;
44
+ exports.buildTnidValue = buildTnidValue;
45
+ exports.valueToBytes = valueToBytes;
46
+ exports.generateV0 = generateV0;
47
+ exports.generateV1 = generateV1;
48
+ // Masks for UUID version 8 and variant bits
49
+ const dntShim = __importStar(require("./_dnt.shims.js"));
50
+ exports.UUID_V8_MASK = 0x00000000000080008000000000000000n;
51
+ // V1: 100 random bits scattered across the UUID
52
+ exports.V1_RANDOM_MASK = 0x00000fffffff0fff0fffffffffffffffn;
53
+ // V0: 57 random bits in the lower portion
54
+ exports.V0_RANDOM_MASK = 0x000000000000000001ffffffffffffffn;
55
+ // V0 timestamp masks (for extracting and placing 43-bit timestamp)
56
+ const TIMESTAMP_FIRST_28_MASK = 0x000007ffffff8000n;
57
+ const TIMESTAMP_SECOND_12_MASK = 0x0000000000007ff8n;
58
+ const TIMESTAMP_LAST_3_MASK = 0x0000000000000007n;
59
+ /** Place name bits in their correct position (bits 108-127) */
60
+ function nameMask(nameBits) {
61
+ return BigInt(nameBits) << 108n;
62
+ }
63
+ /** Place UUID version/variant and TNID variant in their correct positions */
64
+ function uuidAndVariantMask(tnidVariant) {
65
+ return exports.UUID_V8_MASK | ((tnidVariant & 3n) << 60n);
66
+ }
67
+ /** Scatter 43-bit timestamp into its three positions within the 128-bit ID */
68
+ function millisMask(millisSinceEpoch) {
69
+ let mask = 0n;
70
+ // First 28 bits of timestamp → bits 80-107 of UUID
71
+ const first28LeadingZeros = 64n - 43n; // = 21, so mask starts at bit 42
72
+ mask |= (millisSinceEpoch & TIMESTAMP_FIRST_28_MASK) <<
73
+ (first28LeadingZeros + 64n - 20n);
74
+ // Middle 12 bits of timestamp → bits 64-75 of UUID (after version nibble)
75
+ const second12LeadingZeros = 64n - 15n; // = 49
76
+ mask |= (millisSinceEpoch & TIMESTAMP_SECOND_12_MASK) <<
77
+ (second12LeadingZeros + 64n - 52n);
78
+ // Last 3 bits of timestamp → bits 57-59 of UUID (after variant bits)
79
+ const last3LeadingZeros = 64n - 3n; // = 61
80
+ mask |= (millisSinceEpoch & TIMESTAMP_LAST_3_MASK) <<
81
+ (last3LeadingZeros + 64n - 68n);
82
+ return mask;
83
+ }
84
+ /** Build a 128-bit TNID value using mask-based OR operations */
85
+ function buildTnidValue(nameBits, payloadMask, tnidVariant) {
86
+ return nameMask(nameBits) | uuidAndVariantMask(tnidVariant) | payloadMask;
87
+ }
88
+ /** Convert 128-bit value to byte array */
89
+ function valueToBytes(value) {
90
+ const bytes = new Uint8Array(16);
91
+ let v = value;
92
+ for (let i = 15; i >= 0; i--) {
93
+ bytes[i] = Number(v & 0xffn);
94
+ v >>= 8n;
95
+ }
96
+ return bytes;
97
+ }
98
+ /** Generate a V0 (time-ordered) TNID as bytes */
99
+ function generateV0(nameBits, timestampMs, randomBits) {
100
+ const timestamp = timestampMs !== undefined
101
+ ? timestampMs & ((1n << 43n) - 1n)
102
+ : BigInt(Date.now()) & ((1n << 43n) - 1n);
103
+ let random;
104
+ if (randomBits !== undefined) {
105
+ random = randomBits;
106
+ }
107
+ else {
108
+ const randomBytes = dntShim.crypto.getRandomValues(new Uint8Array(8));
109
+ random = 0n;
110
+ for (const byte of randomBytes) {
111
+ random = (random << 8n) | BigInt(byte);
112
+ }
113
+ }
114
+ const payloadMask = millisMask(timestamp) | (random & exports.V0_RANDOM_MASK);
115
+ const value = buildTnidValue(nameBits, payloadMask, 0n);
116
+ return valueToBytes(value);
117
+ }
118
+ /** Generate a V1 (high-entropy random) TNID as bytes */
119
+ function generateV1(nameBits, randomBits) {
120
+ let random;
121
+ if (randomBits !== undefined) {
122
+ random = randomBits;
123
+ }
124
+ else {
125
+ const randomBytes = dntShim.crypto.getRandomValues(new Uint8Array(16));
126
+ random = 0n;
127
+ for (const byte of randomBytes) {
128
+ random = (random << 8n) | BigInt(byte);
129
+ }
130
+ }
131
+ const payloadMask = random & exports.V1_RANDOM_MASK;
132
+ const value = buildTnidValue(nameBits, payloadMask, 1n);
133
+ return valueToBytes(value);
134
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Encode a 16-byte TNID value as a 17-character data string.
3
+ */
4
+ export declare function encodeData(bytes: Uint8Array): string;
5
+ /**
6
+ * Decode a 17-character data string to data bits and TNID variant.
7
+ */
8
+ export declare function decodeData(encoded: string): {
9
+ dataBits: bigint;
10
+ tnidVariant: number;
11
+ };
12
+ /**
13
+ * Convert data bits back to a 16-byte array.
14
+ */
15
+ export declare function dataBitsToBytes(dataBits: bigint, nameBits: number): Uint8Array;
16
+ //# sourceMappingURL=data_encoding.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data_encoding.d.ts","sourceRoot":"","sources":["../src/data_encoding.ts"],"names":[],"mappings":"AA6BA;;GAEG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAgCpD;AAED;;GAEG;AACH,wBAAgB,UAAU,CACxB,OAAO,EAAE,MAAM,GACd;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAmB3C;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU,CA8B9E"}