@planet-matrix/mobius-model 0.3.0 → 0.5.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/CHANGELOG.md +15 -0
- package/README.md +30 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +22 -4
- package/package.json +3 -3
- package/scripts/build.ts +4 -4
- package/src/basic/README.md +144 -0
- package/src/basic/array.ts +872 -0
- package/src/basic/bigint.ts +114 -0
- package/src/basic/boolean.ts +180 -0
- package/src/basic/enhance.ts +10 -0
- package/src/basic/error.ts +51 -0
- package/src/basic/function.ts +453 -0
- package/src/basic/helper.ts +276 -0
- package/src/basic/index.ts +17 -0
- package/src/basic/is.ts +320 -0
- package/src/basic/number.ts +178 -0
- package/src/basic/object.ts +140 -0
- package/src/basic/promise.ts +464 -0
- package/src/basic/regexp.ts +7 -0
- package/src/basic/stream.ts +140 -0
- package/src/basic/string.ts +308 -0
- package/src/basic/symbol.ts +164 -0
- package/src/basic/temporal.ts +224 -0
- package/src/encoding/README.md +105 -0
- package/src/encoding/base64.ts +98 -0
- package/src/encoding/index.ts +1 -0
- package/src/index.ts +4 -0
- package/src/random/README.md +109 -0
- package/src/random/index.ts +1 -0
- package/src/random/uuid.ts +103 -0
- package/src/type/README.md +330 -0
- package/src/type/array.ts +5 -0
- package/src/type/boolean.ts +471 -0
- package/src/type/class.ts +419 -0
- package/src/type/function.ts +1519 -0
- package/src/type/helper.ts +135 -0
- package/src/type/index.ts +14 -0
- package/src/type/intersection.ts +93 -0
- package/src/type/is.ts +247 -0
- package/src/type/iteration.ts +233 -0
- package/src/type/number.ts +732 -0
- package/src/type/object.ts +788 -0
- package/src/type/path.ts +73 -0
- package/src/type/string.ts +1004 -0
- package/src/type/tuple.ts +2424 -0
- package/src/type/union.ts +108 -0
- package/tests/unit/basic/array.spec.ts +290 -0
- package/tests/unit/basic/bigint.spec.ts +50 -0
- package/tests/unit/basic/boolean.spec.ts +74 -0
- package/tests/unit/basic/error.spec.ts +32 -0
- package/tests/unit/basic/function.spec.ts +175 -0
- package/tests/unit/basic/helper.spec.ts +118 -0
- package/tests/unit/basic/number.spec.ts +74 -0
- package/tests/unit/basic/object.spec.ts +46 -0
- package/tests/unit/basic/promise.spec.ts +232 -0
- package/tests/unit/basic/regexp.spec.ts +11 -0
- package/tests/unit/basic/stream.spec.ts +120 -0
- package/tests/unit/basic/string.spec.ts +74 -0
- package/tests/unit/basic/symbol.spec.ts +72 -0
- package/tests/unit/basic/temporal.spec.ts +78 -0
- package/tests/unit/encoding/base64.spec.ts +40 -0
- package/tests/unit/random/uuid.spec.ts +37 -0
- package/dist/index.d.ts +0 -2
- package/dist/index.d.ts.map +0 -1
- package/dist/reactor/index.d.ts +0 -3
- package/dist/reactor/index.d.ts.map +0 -1
- package/dist/reactor/reactor-core/flags.d.ts +0 -99
- package/dist/reactor/reactor-core/flags.d.ts.map +0 -1
- package/dist/reactor/reactor-core/index.d.ts +0 -4
- package/dist/reactor/reactor-core/index.d.ts.map +0 -1
- package/dist/reactor/reactor-core/primitive.d.ts +0 -276
- package/dist/reactor/reactor-core/primitive.d.ts.map +0 -1
- package/dist/reactor/reactor-core/reactive-system.d.ts +0 -241
- package/dist/reactor/reactor-core/reactive-system.d.ts.map +0 -1
- package/dist/reactor/reactor-operators/branch.d.ts +0 -19
- package/dist/reactor/reactor-operators/branch.d.ts.map +0 -1
- package/dist/reactor/reactor-operators/convert.d.ts +0 -30
- package/dist/reactor/reactor-operators/convert.d.ts.map +0 -1
- package/dist/reactor/reactor-operators/create.d.ts +0 -26
- package/dist/reactor/reactor-operators/create.d.ts.map +0 -1
- package/dist/reactor/reactor-operators/filter.d.ts +0 -269
- package/dist/reactor/reactor-operators/filter.d.ts.map +0 -1
- package/dist/reactor/reactor-operators/index.d.ts +0 -8
- package/dist/reactor/reactor-operators/index.d.ts.map +0 -1
- package/dist/reactor/reactor-operators/join.d.ts +0 -48
- package/dist/reactor/reactor-operators/join.d.ts.map +0 -1
- package/dist/reactor/reactor-operators/map.d.ts +0 -165
- package/dist/reactor/reactor-operators/map.d.ts.map +0 -1
- package/dist/reactor/reactor-operators/utility.d.ts +0 -48
- package/dist/reactor/reactor-operators/utility.d.ts.map +0 -1
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Encoding
|
|
2
|
+
|
|
3
|
+
Runtime utilities for encoding and decoding text. This module currently focuses on Base64 conversions with predictable UTF-8 behavior.
|
|
4
|
+
|
|
5
|
+
## For Users
|
|
6
|
+
|
|
7
|
+
This module provides lightweight encoding helpers for common text <-> Base64 scenarios.
|
|
8
|
+
|
|
9
|
+
### 1. Domain Areas
|
|
10
|
+
|
|
11
|
+
1. Base64: Convert UTF-8 strings to Base64, decode Base64 back to UTF-8 strings, and validate Base64 input.
|
|
12
|
+
|
|
13
|
+
Current public exports:
|
|
14
|
+
|
|
15
|
+
- `isBase64(input: string): boolean`
|
|
16
|
+
- `assertBase64(input: string): void`
|
|
17
|
+
- `stringToBase64(input: string): string`
|
|
18
|
+
- `base64ToString(input: string): string`
|
|
19
|
+
|
|
20
|
+
## For Contributors
|
|
21
|
+
|
|
22
|
+
This guide documents conventions and best practices for implementing encoding utilities in this module.
|
|
23
|
+
|
|
24
|
+
### 1. Documentation and Comments
|
|
25
|
+
|
|
26
|
+
#### 1.1 JSDoc Comment Format
|
|
27
|
+
|
|
28
|
+
Every exported function should include JSDoc in this form:
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
/**
|
|
32
|
+
* Brief one-line description of what the function does.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```
|
|
36
|
+
* // Expect: "aGVsbG8="
|
|
37
|
+
* const example1 = stringToBase64("hello")
|
|
38
|
+
* // Expect: "hello"
|
|
39
|
+
* const example2 = base64ToString("aGVsbG8=")
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export const stringToBase64 = (input: string): string => {
|
|
43
|
+
...
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**Documentation Rules:**
|
|
48
|
+
- First line: Clear, concise description starting with a verb (Check, Get, Convert, etc.)
|
|
49
|
+
- Add a blank line after the description
|
|
50
|
+
- Use `@example` tag followed by triple backticks
|
|
51
|
+
- Include multiple cases showing different scenarios
|
|
52
|
+
- Use comment format: `// Expect: <result>`
|
|
53
|
+
- Assign example results to variables like `example1`, `example2` to keep examples readable
|
|
54
|
+
- Place `@see`(if has) after the `@example` block, separated by a blank line
|
|
55
|
+
- Prefer deterministic examples; avoid randomness or time-dependent output in docs
|
|
56
|
+
- If a function returns a non-scalar, show the expected shape or key properties
|
|
57
|
+
|
|
58
|
+
### 2. Runtime Implementation Patterns
|
|
59
|
+
|
|
60
|
+
#### 2.1 Input and Output Rules
|
|
61
|
+
|
|
62
|
+
- Text conversion utilities should accept explicit `string` input.
|
|
63
|
+
- Behavior should be deterministic and side-effect free.
|
|
64
|
+
- Keep UTF-8 semantics explicit in implementation.
|
|
65
|
+
|
|
66
|
+
#### 2.2 Helper Placement
|
|
67
|
+
|
|
68
|
+
- Place local helper constants/functions immediately before the utility they support.
|
|
69
|
+
- Prefix non-exported helpers with `internal`.
|
|
70
|
+
- Never export internal helpers.
|
|
71
|
+
|
|
72
|
+
#### 2.3 Spacing
|
|
73
|
+
|
|
74
|
+
- Separate different utility functions with a single blank line.
|
|
75
|
+
|
|
76
|
+
### 3. Naming Conventions
|
|
77
|
+
|
|
78
|
+
#### 3.1 Function Name Format
|
|
79
|
+
|
|
80
|
+
Use clear operation-oriented names in the `encoding` domain:
|
|
81
|
+
|
|
82
|
+
- `isBase64` for Base64 validation checks returning boolean
|
|
83
|
+
- `assertBase64` for Base64 validation that throws on malformed input
|
|
84
|
+
- `stringToBase64` for UTF-8 string to Base64 conversion
|
|
85
|
+
- `base64ToString` for Base64 to UTF-8 string conversion
|
|
86
|
+
|
|
87
|
+
Prefer names that encode both source and target representations.
|
|
88
|
+
|
|
89
|
+
### 4. Export Strategy
|
|
90
|
+
|
|
91
|
+
- Export all public utilities from domain files.
|
|
92
|
+
- Re-export them through `index.ts`.
|
|
93
|
+
- Do not export internal helpers.
|
|
94
|
+
|
|
95
|
+
### 5. Common Pitfalls to Avoid
|
|
96
|
+
|
|
97
|
+
1. Do not assume all Base64 input is valid without documenting behavior.
|
|
98
|
+
2. Do not mix encodings implicitly (keep UTF-8 explicit).
|
|
99
|
+
3. Do not add environment-specific APIs unless compatibility is documented.
|
|
100
|
+
4. Do not mutate shared state.
|
|
101
|
+
|
|
102
|
+
### 6. Testing Requirements
|
|
103
|
+
|
|
104
|
+
- Write one test per function.
|
|
105
|
+
- If multiple cases are needed, include them within the same test.
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
const internalBase64Pattern = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/
|
|
2
|
+
const internalNormalizeBase64 = (input: string): string => {
|
|
3
|
+
return input.replaceAll(/\s+/g, "")
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Check whether input is a valid Base64 string.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```
|
|
10
|
+
* // Expect: true
|
|
11
|
+
* const example1 = isBase64("aGVsbG8=")
|
|
12
|
+
* // Expect: false
|
|
13
|
+
* const example2 = isBase64("abc")
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export const isBase64 = (input: string): boolean => {
|
|
17
|
+
const normalizedInput = internalNormalizeBase64(input)
|
|
18
|
+
return internalBase64Pattern.test(normalizedInput)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const internalAssertValidBase64 = (input: string): void => {
|
|
22
|
+
if (internalBase64Pattern.test(input) === false) {
|
|
23
|
+
throw new TypeError("Invalid Base64 input")
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Assert input is a valid Base64 string.
|
|
28
|
+
*
|
|
29
|
+
* @throws {TypeError} when input is not valid Base64
|
|
30
|
+
*/
|
|
31
|
+
export const assertBase64 = (input: string): void => {
|
|
32
|
+
const normalizedInput = internalNormalizeBase64(input)
|
|
33
|
+
internalAssertValidBase64(normalizedInput)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const internalStringToBase64ByBrowserApi = (input: string): string => {
|
|
37
|
+
const bytes = new TextEncoder().encode(input)
|
|
38
|
+
let binaryString = ""
|
|
39
|
+
|
|
40
|
+
bytes.forEach((byte) => {
|
|
41
|
+
binaryString = binaryString + String.fromCodePoint(byte)
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
return btoa(binaryString)
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Convert a UTF-8 string into a Base64 string.
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```
|
|
51
|
+
* // Expect: "aGVsbG8="
|
|
52
|
+
* const example1 = stringToBase64("hello")
|
|
53
|
+
* // Expect: "5L2g5aW9"
|
|
54
|
+
* const example2 = stringToBase64("你好")
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export const stringToBase64 = (input: string): string => {
|
|
58
|
+
if (typeof Buffer !== "undefined") {
|
|
59
|
+
return Buffer.from(input, "utf8").toString("base64")
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (typeof btoa !== "undefined") {
|
|
63
|
+
return internalStringToBase64ByBrowserApi(input)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
throw new Error("No Base64 runtime support found")
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const internalBase64ToStringByBrowserApi = (input: string): string => {
|
|
70
|
+
const binaryString = atob(input)
|
|
71
|
+
const bytes = Uint8Array.from(binaryString, char => char.codePointAt(0) ?? 0)
|
|
72
|
+
return new TextDecoder().decode(bytes)
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Convert a valid Base64 string into a UTF-8 string.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```
|
|
79
|
+
* // Expect: "hello"
|
|
80
|
+
* const example1 = base64ToString("aGVsbG8=")
|
|
81
|
+
* // Expect: "你好"
|
|
82
|
+
* const example2 = base64ToString("5L2g5aW9")
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
export const base64ToString = (input: string): string => {
|
|
86
|
+
const normalizedInput = internalNormalizeBase64(input)
|
|
87
|
+
internalAssertValidBase64(normalizedInput)
|
|
88
|
+
|
|
89
|
+
if (typeof Buffer !== "undefined") {
|
|
90
|
+
return Buffer.from(normalizedInput, "base64").toString("utf8")
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (typeof atob !== "undefined") {
|
|
94
|
+
return internalBase64ToStringByBrowserApi(normalizedInput)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
throw new Error("No Base64 runtime support found")
|
|
98
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./base64.ts"
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Random
|
|
2
|
+
|
|
3
|
+
Runtime utilities for generating random-oriented values with cross-runtime compatibility. This module currently focuses on UUID generation.
|
|
4
|
+
|
|
5
|
+
## For Users
|
|
6
|
+
|
|
7
|
+
This module provides lightweight helpers for random value generation scenarios.
|
|
8
|
+
|
|
9
|
+
### 1. Domain Areas
|
|
10
|
+
|
|
11
|
+
1. UUID: Generate RFC 4122 version-4 UUID strings with runtime-aware fallback behavior.
|
|
12
|
+
|
|
13
|
+
Current public exports:
|
|
14
|
+
|
|
15
|
+
- `isUuid(input: string): boolean`
|
|
16
|
+
- `assertUuid(input: string): void`
|
|
17
|
+
- `getUuidVersion(input: string): number`
|
|
18
|
+
- `generateUuid(): string`
|
|
19
|
+
|
|
20
|
+
## For Contributors
|
|
21
|
+
|
|
22
|
+
This guide documents conventions and best practices for implementing random utilities in this module.
|
|
23
|
+
|
|
24
|
+
### 1. Documentation and Comments
|
|
25
|
+
|
|
26
|
+
#### 1.1 JSDoc Comment Format
|
|
27
|
+
|
|
28
|
+
Every exported function should include JSDoc in this form:
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
/**
|
|
32
|
+
* Brief one-line description of what the function does.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```
|
|
36
|
+
* const example1 = generateUuid()
|
|
37
|
+
* // Expect: true
|
|
38
|
+
* const example2 = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(example1)
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export const generateUuid = (): string => {
|
|
42
|
+
...
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Documentation Rules:**
|
|
47
|
+
- First line: Clear, concise description starting with a verb (Generate, Check, Convert, etc.)
|
|
48
|
+
- Add a blank line after the description
|
|
49
|
+
- Use `@example` tag followed by triple backticks
|
|
50
|
+
- Include multiple cases showing different scenarios
|
|
51
|
+
- Use comment format: `// Expect: <result>`
|
|
52
|
+
- Assign example results to variables like `example1`, `example2` to keep examples readable
|
|
53
|
+
- Place `@see`(if has) after the `@example` block, separated by a blank line
|
|
54
|
+
- Prefer deterministic examples; avoid asserting exact random outputs
|
|
55
|
+
- If a function returns a structured string, show expected format characteristics
|
|
56
|
+
|
|
57
|
+
### 2. Runtime Implementation Patterns
|
|
58
|
+
|
|
59
|
+
#### 2.1 Compatibility First
|
|
60
|
+
|
|
61
|
+
- Prefer standard runtime APIs when available (for example, `crypto.randomUUID`).
|
|
62
|
+
- Keep fallback logic for environments where modern APIs are unavailable.
|
|
63
|
+
|
|
64
|
+
#### 2.2 Deterministic Interface
|
|
65
|
+
|
|
66
|
+
- Public API shape should remain deterministic even if output values are random.
|
|
67
|
+
- Return values should always conform to the documented output format.
|
|
68
|
+
|
|
69
|
+
#### 2.3 Helper Placement
|
|
70
|
+
|
|
71
|
+
- Place local helper constants/functions immediately before the utility they support.
|
|
72
|
+
- Prefix non-exported helpers with `internal`.
|
|
73
|
+
- Never export internal helpers.
|
|
74
|
+
|
|
75
|
+
#### 2.4 Spacing
|
|
76
|
+
|
|
77
|
+
- Separate different utility functions with a single blank line.
|
|
78
|
+
|
|
79
|
+
### 3. Naming Conventions
|
|
80
|
+
|
|
81
|
+
#### 3.1 Function Name Format
|
|
82
|
+
|
|
83
|
+
Use clear operation-oriented names in the `random` domain:
|
|
84
|
+
|
|
85
|
+
- `isUuid` for UUID format validation checks
|
|
86
|
+
- `assertUuid` for UUID validation that throws on malformed input
|
|
87
|
+
- `getUuidVersion` for extracting UUID version numbers
|
|
88
|
+
- `generateUuid` for UUID creation helpers
|
|
89
|
+
|
|
90
|
+
Prefer names that clearly indicate output format and intent.
|
|
91
|
+
|
|
92
|
+
### 4. Export Strategy
|
|
93
|
+
|
|
94
|
+
- Export all public utilities from domain files.
|
|
95
|
+
- Re-export them through `index.ts`.
|
|
96
|
+
- Do not export internal helpers.
|
|
97
|
+
|
|
98
|
+
### 5. Common Pitfalls to Avoid
|
|
99
|
+
|
|
100
|
+
1. Do not assume browser-only APIs are always available.
|
|
101
|
+
2. Do not couple random utilities to a specific runtime unless documented.
|
|
102
|
+
3. Do not assert exact random results in tests.
|
|
103
|
+
4. Do not mutate shared/global state permanently.
|
|
104
|
+
|
|
105
|
+
### 6. Testing Requirements
|
|
106
|
+
|
|
107
|
+
- Write one test per function.
|
|
108
|
+
- If multiple cases are needed, include them within the same test.
|
|
109
|
+
- For random outputs, validate shape/constraints instead of exact value.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./uuid.ts"
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
|
|
2
|
+
const internalUUID_REGEXP = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
|
|
3
|
+
/**
|
|
4
|
+
* Check whether input is a valid UUID string.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```
|
|
8
|
+
* // Expect: true
|
|
9
|
+
* const example1 = isUuid("550e8400-e29b-41d4-a716-446655440000")
|
|
10
|
+
* // Expect: false
|
|
11
|
+
* const example2 = isUuid("not-a-uuid")
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export const isUuid = (input: string): boolean => {
|
|
15
|
+
return internalUUID_REGEXP.test(input)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Assert input is a valid UUID string.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```
|
|
23
|
+
* // Expect: no throw
|
|
24
|
+
* const example1 = assertUuid("550e8400-e29b-41d4-a716-446655440000")
|
|
25
|
+
* // Expect: throws TypeError
|
|
26
|
+
* const example2 = () => assertUuid("not-a-uuid")
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* @throws {TypeError} when input is not a valid UUID
|
|
30
|
+
*/
|
|
31
|
+
export const assertUuid = (input: string): void => {
|
|
32
|
+
if (isUuid(input) === false) {
|
|
33
|
+
throw new TypeError(`Expected a valid UUID string, got: ${input}`)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Get the version number from a valid UUID string.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```
|
|
42
|
+
* // Expect: 4
|
|
43
|
+
* const example1 = getUuidVersion("550e8400-e29b-41d4-a716-446655440000")
|
|
44
|
+
* // Expect: 1
|
|
45
|
+
* const example2 = getUuidVersion("123e4567-e89b-12d3-a456-426614174000")
|
|
46
|
+
* ```
|
|
47
|
+
*
|
|
48
|
+
* @throws {TypeError} when input is not a valid UUID
|
|
49
|
+
*/
|
|
50
|
+
export const getUuidVersion = (input: string): number => {
|
|
51
|
+
// 1) Ensure the input is a syntactically valid UUID string.
|
|
52
|
+
// If invalid, assertUuid throws TypeError and prevents unsafe parsing.
|
|
53
|
+
assertUuid(input)
|
|
54
|
+
|
|
55
|
+
// 2) Per UUID canonical format xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx,
|
|
56
|
+
// the version nibble is the first hex digit of the 3rd group.
|
|
57
|
+
// In a 36-char UUID string, that position is index 14.
|
|
58
|
+
|
|
59
|
+
// 3) Convert the single hex character (for example "4") to a base-10 number.
|
|
60
|
+
// parseInt("4", 16) -> 4, parseInt("a", 16) -> 10.
|
|
61
|
+
return Number.parseInt(input[14]!, 16)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Generate a RFC 4122 version-4 UUID string.
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```
|
|
69
|
+
* const example1 = generateUuid()
|
|
70
|
+
* // Expect: true
|
|
71
|
+
* const example2 = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(example1)
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
export const generateUuid = (): string => {
|
|
75
|
+
if (typeof crypto === "object") {
|
|
76
|
+
if (typeof crypto.randomUUID === "function") {
|
|
77
|
+
return crypto.randomUUID()
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (typeof crypto.getRandomValues === "function" && typeof Uint8Array === "function") {
|
|
81
|
+
const buffer = new Uint8Array(16)
|
|
82
|
+
crypto.getRandomValues(buffer)
|
|
83
|
+
|
|
84
|
+
// Per RFC 4122, set bits for version and `clock_seq_hi_and_reserved`
|
|
85
|
+
buffer[6] = (buffer[6]! & 0x0F) | 0x40 // version 4
|
|
86
|
+
buffer[8] = (buffer[8]! & 0x3F) | 0x80 // variant 1
|
|
87
|
+
|
|
88
|
+
// Convert buffer to UUID string format
|
|
89
|
+
const hex = [...buffer].map(b => b.toString(16).padStart(2, "0")).join("")
|
|
90
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Fallback for environments without crypto support
|
|
95
|
+
const fallbackUUID = (): string => {
|
|
96
|
+
const random = (a: number): string => {
|
|
97
|
+
return ((a ^ ((Math.random() * 16) >> (a / 4))) & 15).toString(16)
|
|
98
|
+
}
|
|
99
|
+
return "10000000-1000-4000-8000-100000000000".replaceAll(/[018]/g, (char: string) => random(Number(char)))
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return fallbackUUID()
|
|
103
|
+
}
|