@qilin-agent/cosmokit 0.0.0-stage → 1.8.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.
@@ -0,0 +1,86 @@
1
+ /** Shared config references used by schema validators and plugin runtimes. */
2
+
3
+ const write = Symbol.for('cosmokit.volatile.write')
4
+
5
+ /** Recursively readonly data returned by a volatile config reference. */
6
+ export type VolatileSnapshot<T> = T extends object ? { readonly [K in keyof T]: VolatileSnapshot<T[K]> } : T
7
+
8
+ /** A stable reference; keep the reference, or capture its value for one operation only. */
9
+ export interface Volatile<T> {
10
+ /** @returns the current immutable snapshot, including undefined for an absent value. */
11
+ get(): VolatileSnapshot<T>
12
+ }
13
+
14
+ interface WritableVolatile extends Volatile<unknown> {
15
+ [write](value: unknown): void
16
+ }
17
+
18
+ function snapshot(value: unknown, ancestors = new Set<object>()): unknown {
19
+ if (typeof value === 'function') throw new TypeError('volatile config cannot contain functions')
20
+ if (value === null || typeof value !== 'object') return value
21
+ if (ancestors.has(value)) throw new TypeError('volatile config cannot contain cycles')
22
+ ancestors.add(value)
23
+ try {
24
+ if (Array.isArray(value)) return Object.freeze(value.map(item => snapshot(item, ancestors)))
25
+ if (Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) {
26
+ throw new TypeError('volatile config objects must be plain objects or arrays')
27
+ }
28
+ return Object.freeze(Object.fromEntries(Object.entries(value).map(([key, item]) => [key, snapshot(item, ancestors)])))
29
+ } finally {
30
+ ancestors.delete(value)
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Create a detached reference containing an immutable copy of the supplied data.
36
+ * @param value - validated config data; class instances and functions are unsupported.
37
+ * @returns a reference whose value is updated only by its owning runtime.
38
+ */
39
+ export function createVolatile<T>(value: T): Volatile<T> {
40
+ let current = snapshot(value)
41
+ return Object.freeze({
42
+ get: () => current,
43
+ [write]: (value: unknown) => { current = value },
44
+ }) as Volatile<T>
45
+ }
46
+
47
+ /**
48
+ * Identify references across ESM/CJS copies of the shared library.
49
+ * @param value - a parsed config value.
50
+ * @returns whether the value implements the shared reference protocol.
51
+ */
52
+ export function isVolatile(value: unknown): value is Volatile<unknown> {
53
+ return typeof value === 'object' && value !== null && write in value
54
+ }
55
+
56
+ /**
57
+ * Collect config references without descending into their snapshots or opaque objects.
58
+ * @internal
59
+ * @param value - parsed config; cyclic ordinary fields are visited once per path.
60
+ * @returns references and their object-key paths, including an empty path for a root reference.
61
+ */
62
+ export function volatileEntries(value: unknown): { path: string[]; ref: Volatile<unknown> }[] {
63
+ const ancestors = new Set<object>()
64
+ function visit(value: unknown, path: string[]): { path: string[]; ref: Volatile<unknown> }[] {
65
+ if (isVolatile(value)) return [{ path, ref: value }]
66
+ if (!value || typeof value !== 'object' || ancestors.has(value)) return []
67
+ if (!Array.isArray(value) && Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) return []
68
+ ancestors.add(value)
69
+ try {
70
+ return Object.entries(value).flatMap(([key, child]) => visit(child, [...path, key]))
71
+ } finally {
72
+ ancestors.delete(value)
73
+ }
74
+ }
75
+ return visit(value, [])
76
+ }
77
+
78
+ /**
79
+ * Commit an already validated immutable snapshot from another reference.
80
+ * @internal
81
+ * @param target - the owning plugin's stable reference.
82
+ * @param source - a newly parsed candidate reference.
83
+ */
84
+ export function updateVolatile(target: Volatile<unknown>, source: Volatile<unknown>): void {
85
+ ;(target as WritableVolatile)[write](source.get())
86
+ }