-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathutils.ts
More file actions
99 lines (95 loc) · 3.3 KB
/
Copy pathutils.ts
File metadata and controls
99 lines (95 loc) · 3.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
import Globals from '@app/globals.ts'
import { Immutable } from '@neabyte/utils-core'
/**
* Internal helper utilities for typebox.
* @description Provides freezing, cloning, and type checks.
*/
export default class Utils {
/**
* Recursively freeze a value.
* @description Deep freezes objects, maps, and sets.
* @param value - Value to deeply freeze
* @returns Deeply frozen value
* @throws TypeError when nesting or a string value exceeds its limit
* @template ValueType - Type of the value
*/
static deepFreeze<ValueType>(value: ValueType): ValueType {
return Utils.freezeNode(value, new WeakSet<object>(), 0)
}
/**
* Check if value is object.
* @description Returns true for non-null object values.
* @param value - Value to inspect
* @returns True when value is an object
*/
static isObject(value: unknown): value is Record<string, unknown> {
return typeof value === 'object' && value !== null
}
/**
* Clone a value structurally.
* @description Uses structuredClone with a typed failure.
* @param value - Value to clone
* @returns Structured clone of the value
* @throws TypeError when value is not cloneable
* @template ValueType - Type of the value
*/
static safeClone<ValueType>(value: ValueType): ValueType {
try {
return structuredClone(value)
} catch {
throw new TypeError('stateful initial state must be structured cloneable')
}
}
/**
* Freeze a node guarded against cycles.
* @description Skips frozen nodes, views, and seen references.
* @param value - Value to freeze in place
* @param seen - Set of already visited references
* @param depth - Current recursion depth
* @returns Deeply frozen value
* @throws TypeError when nesting or a string value exceeds its limit
* @template ValueType - Type of the value
*/
private static freezeNode<ValueType>(
value: ValueType,
seen: WeakSet<object>,
depth: number
): ValueType {
if (typeof value === 'string' && value.length > Globals.maxGuardInputLength) {
const reason = `input exceeds ${Globals.maxGuardInputLength} characters`
throw new TypeError(reason, { cause: [reason] })
}
if (!Utils.isObject(value) || Object.isFrozen(value) || ArrayBuffer.isView(value)) {
return value
}
if (seen.has(value)) {
return value
}
if (depth >= Globals.maxFreezeDepth) {
const reason = `input nesting exceeds ${Globals.maxFreezeDepth} levels`
throw new TypeError(reason, { cause: [reason] })
}
seen.add(value)
if (value instanceof Map) {
const frozen = new Map<unknown, unknown>()
for (const [key, item] of value) {
frozen.set(key, Utils.freezeNode(item, seen, depth + 1))
}
return Immutable.freezeMap(frozen) as ValueType
}
if (value instanceof Set) {
const frozen = new Set<unknown>()
for (const item of value) {
frozen.add(Utils.freezeNode(item, seen, depth + 1))
}
return Immutable.freezeSet(frozen) as ValueType
}
for (const key of Object.keys(value)) {
const descriptor = Object.getOwnPropertyDescriptor(value, key)
if (descriptor !== undefined && 'value' in descriptor) {
Utils.freezeNode(descriptor.value, seen, depth + 1)
}
}
return Object.freeze(value) as ValueType
}
}