@qilin-agent/kylin 0.0.0-stage → 4.0.3
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/LICENSE +21 -0
- package/README.md +104 -2
- package/bin.js +16 -0
- package/lib/index.js +1829 -0
- package/lib/types/context.d.ts +101 -0
- package/lib/types/context.d.ts.map +1 -0
- package/lib/types/events.d.ts +240 -0
- package/lib/types/events.d.ts.map +1 -0
- package/lib/types/fiber.d.ts +202 -0
- package/lib/types/fiber.d.ts.map +1 -0
- package/lib/types/index.d.ts +17 -0
- package/lib/types/index.d.ts.map +1 -0
- package/lib/types/logger.d.ts +105 -0
- package/lib/types/logger.d.ts.map +1 -0
- package/lib/types/reflect.d.ts +186 -0
- package/lib/types/reflect.d.ts.map +1 -0
- package/lib/types/registry.d.ts +201 -0
- package/lib/types/registry.d.ts.map +1 -0
- package/lib/types/service.d.ts +55 -0
- package/lib/types/service.d.ts.map +1 -0
- package/lib/types/utils.d.ts +62 -0
- package/lib/types/utils.d.ts.map +1 -0
- package/package.json +53 -3
- package/src/context.ts +146 -0
- package/src/events.ts +352 -0
- package/src/fiber.ts +754 -0
- package/src/index.ts +16 -0
- package/src/logger.ts +271 -0
- package/src/reflect.ts +418 -0
- package/src/registry.ts +337 -0
- package/src/service.ts +115 -0
- package/src/utils.ts +287 -0
package/src/utils.ts
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import { defineProperty } from '@qilin-agent/cosmokit'
|
|
2
|
+
import type { Context, Service } from './index.ts'
|
|
3
|
+
|
|
4
|
+
/** Ordered collection of disposable values with O(1) deletion by value. */
|
|
5
|
+
export class DisposableList<T extends WeakKey> {
|
|
6
|
+
private sn = 0
|
|
7
|
+
private map = new Map<number, T>()
|
|
8
|
+
private weak = new WeakMap<T, number>()
|
|
9
|
+
|
|
10
|
+
get length() {
|
|
11
|
+
return this.map.size
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
push(value: T) {
|
|
15
|
+
const sn = ++this.sn
|
|
16
|
+
this.map.set(sn, value)
|
|
17
|
+
this.weak.set(value, sn)
|
|
18
|
+
return () => this.map.delete(sn)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
delete(value: T) {
|
|
22
|
+
const sn = this.weak.get(value)
|
|
23
|
+
if (!sn) return false
|
|
24
|
+
return this.map.delete(sn)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
clear() {
|
|
28
|
+
const values = [...this.map.values()]
|
|
29
|
+
this.map.clear()
|
|
30
|
+
return values.reverse()
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
[Symbol.iterator]() {
|
|
34
|
+
return this.map.values()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
[Symbol.for('nodejs.util.inspect.custom')]() {
|
|
38
|
+
return [...this]
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Metadata used by traceable proxies to rebind `ctx` and associated services. */
|
|
43
|
+
export interface Tracker {
|
|
44
|
+
associate?: string
|
|
45
|
+
property?: string
|
|
46
|
+
noShadow?: boolean
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Shared symbols used to avoid public property-name collisions. */
|
|
50
|
+
export const symbols = {
|
|
51
|
+
// internal symbols
|
|
52
|
+
shadow: Symbol.for('cordis.shadow'),
|
|
53
|
+
receiver: Symbol.for('cordis.receiver'),
|
|
54
|
+
original: Symbol.for('cordis.original'),
|
|
55
|
+
metadata: Symbol.for('cordis.metadata'),
|
|
56
|
+
initHooks: Symbol.for('cordis.initHooks'),
|
|
57
|
+
checkProto: Symbol.for('cordis.checkProto'),
|
|
58
|
+
|
|
59
|
+
// context symbols
|
|
60
|
+
effect: Symbol.for('cordis.effect') as typeof Context.effect,
|
|
61
|
+
filter: Symbol.for('cordis.filter') as typeof Context.filter,
|
|
62
|
+
isolate: Symbol.for('cordis.isolate') as typeof Context.isolate,
|
|
63
|
+
intercept: Symbol.for('cordis.intercept') as typeof Context.intercept,
|
|
64
|
+
|
|
65
|
+
// service symbols
|
|
66
|
+
init: Symbol.for('cordis.init') as typeof Service.init,
|
|
67
|
+
check: Symbol.for('cordis.check') as typeof Service.check,
|
|
68
|
+
config: Symbol.for('cordis.config') as typeof Service.config,
|
|
69
|
+
invoke: Symbol.for('cordis.invoke') as typeof Service.invoke,
|
|
70
|
+
extend: Symbol.for('cordis.extend') as typeof Service.extend,
|
|
71
|
+
tracker: Symbol.for('cordis.tracker') as typeof Service.tracker,
|
|
72
|
+
resolveConfig: Symbol.for('cordis.resolveConfig') as typeof Service.resolveConfig,
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const GeneratorFunction = function* () {}.constructor
|
|
76
|
+
const AsyncGeneratorFunction = async function* () {}.constructor
|
|
77
|
+
|
|
78
|
+
/** Return true when a plugin callback should be constructed with `new`. */
|
|
79
|
+
export function isConstructor(func: any): func is new (...args: any) => any {
|
|
80
|
+
// async function or arrow function
|
|
81
|
+
if (!func.prototype) return false
|
|
82
|
+
// generator function or malformed definition
|
|
83
|
+
// we cannot use below check because `mock.fn()` is proxied
|
|
84
|
+
// if (func.prototype.constructor !== func) return false
|
|
85
|
+
if (func instanceof GeneratorFunction) return false
|
|
86
|
+
// polyfilled AsyncGeneratorFunction === Function
|
|
87
|
+
if (AsyncGeneratorFunction !== Function && func instanceof AsyncGeneratorFunction) return false
|
|
88
|
+
return true
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Merge two prototype chains while preserving descriptors from `proto1`. */
|
|
92
|
+
export function joinPrototype(proto1: {}, proto2: {}) {
|
|
93
|
+
if (proto1 === Object.prototype) return proto2
|
|
94
|
+
const result = Object.create(joinPrototype(Object.getPrototypeOf(proto1), proto2))
|
|
95
|
+
for (const key of Reflect.ownKeys(proto1)) {
|
|
96
|
+
Object.defineProperty(result, key, Object.getOwnPropertyDescriptor(proto1, key)!)
|
|
97
|
+
}
|
|
98
|
+
return result
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Return true for non-null objects and functions. */
|
|
102
|
+
export function isObject(value: any): value is {} {
|
|
103
|
+
return value && (typeof value === 'object' || typeof value === 'function')
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Find a property descriptor by walking an object's prototype chain. */
|
|
107
|
+
export function getPropertyDescriptor(target: any, prop: string | symbol) {
|
|
108
|
+
let proto = target
|
|
109
|
+
while (proto) {
|
|
110
|
+
const desc = Reflect.getOwnPropertyDescriptor(proto, prop)
|
|
111
|
+
if (desc) return desc
|
|
112
|
+
proto = Object.getPrototypeOf(proto)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Wrap services/functions so method calls see the caller's active context. */
|
|
117
|
+
export function getTraceable<T>(ctx: Context, value: T): T {
|
|
118
|
+
if (!isObject(value)) return value
|
|
119
|
+
if (Object.hasOwn(value, symbols.shadow)) {
|
|
120
|
+
return Object.getPrototypeOf(value)
|
|
121
|
+
}
|
|
122
|
+
const tracker = value[symbols.tracker]
|
|
123
|
+
if (!tracker) return value
|
|
124
|
+
return createTraceable(ctx, value, tracker)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** Return a proxy that overlays readonly or writable properties onto a target. */
|
|
128
|
+
export function withProps(target: any, props?: {}) {
|
|
129
|
+
if (!props) return target
|
|
130
|
+
return new Proxy(target, {
|
|
131
|
+
get: (target, prop, receiver) => {
|
|
132
|
+
if (prop in props && prop !== 'constructor') return Reflect.get(props, prop, receiver)
|
|
133
|
+
return Reflect.get(target, prop, receiver)
|
|
134
|
+
},
|
|
135
|
+
set: (target, prop, value, receiver) => {
|
|
136
|
+
if (prop in props && prop !== 'constructor') return Reflect.set(props, prop, value, receiver)
|
|
137
|
+
return Reflect.set(target, prop, value, receiver)
|
|
138
|
+
},
|
|
139
|
+
})
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function withProp(target: any, prop: string | symbol, value: any) {
|
|
143
|
+
return withProps(target, Object.defineProperty(Object.create(null), prop, {
|
|
144
|
+
value,
|
|
145
|
+
writable: false,
|
|
146
|
+
}))
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function createShadow(ctx: Context, target: any, property: string | undefined, receiver: any) {
|
|
150
|
+
if (!property) return receiver
|
|
151
|
+
const origin = Reflect.getOwnPropertyDescriptor(target, property)?.value
|
|
152
|
+
if (!origin) return receiver
|
|
153
|
+
return withProp(receiver, property, ctx.extend({ [symbols.shadow]: origin }))
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function createShadowMethod(ctx: Context, value: any, outer: any, shadow: {}) {
|
|
157
|
+
return new Proxy(value, {
|
|
158
|
+
apply: (target, thisArg, args) => {
|
|
159
|
+
if (thisArg === outer) thisArg = shadow
|
|
160
|
+
return getTraceable(ctx, Reflect.apply(target, thisArg, args))
|
|
161
|
+
},
|
|
162
|
+
})
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function createTraceable(ctx: Context, value: any, tracker: Tracker) {
|
|
166
|
+
// noShadow services are identity-aware (e.g. logger uses the origin fiber to
|
|
167
|
+
// derive its name): keep the shadow ctx so they can read [symbols.shadow]
|
|
168
|
+
// and resolve the origin. Non-noShadow services strip — their side effects
|
|
169
|
+
// bind to caller, not origin.
|
|
170
|
+
if (ctx[symbols.shadow] && !tracker.noShadow) {
|
|
171
|
+
ctx = Object.getPrototypeOf(ctx)
|
|
172
|
+
}
|
|
173
|
+
const proxy = new Proxy(value, {
|
|
174
|
+
get: (target, prop, receiver) => {
|
|
175
|
+
if (prop === symbols.original) return target
|
|
176
|
+
if (prop === tracker.property) return ctx
|
|
177
|
+
if (typeof prop === 'symbol') {
|
|
178
|
+
return Reflect.get(target, prop, receiver)
|
|
179
|
+
}
|
|
180
|
+
if (tracker.associate && ctx.reflect.props[`${tracker.associate}.${prop}`]) {
|
|
181
|
+
return Reflect.get(ctx, `${tracker.associate}.${prop}`, withProp(ctx, symbols.receiver, receiver))
|
|
182
|
+
}
|
|
183
|
+
let shadow: any, innerValue: any
|
|
184
|
+
const desc = getPropertyDescriptor(target, prop)
|
|
185
|
+
if (desc && 'value' in desc) {
|
|
186
|
+
innerValue = desc.value
|
|
187
|
+
} else {
|
|
188
|
+
shadow = createShadow(ctx, target, tracker.property, receiver)
|
|
189
|
+
innerValue = Reflect.get(target, prop, shadow)
|
|
190
|
+
}
|
|
191
|
+
const innerTracker = innerValue?.[symbols.tracker]
|
|
192
|
+
if (innerTracker) {
|
|
193
|
+
return createTraceable(ctx, innerValue, innerTracker)
|
|
194
|
+
} else if (!tracker.noShadow && typeof innerValue === 'function') {
|
|
195
|
+
shadow ??= createShadow(ctx, target, tracker.property, receiver)
|
|
196
|
+
return createShadowMethod(ctx, innerValue, receiver, shadow)
|
|
197
|
+
} else {
|
|
198
|
+
return innerValue
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
set: (target, prop, value, receiver) => {
|
|
202
|
+
if (prop === symbols.original) return false
|
|
203
|
+
if (prop === tracker.property) return false
|
|
204
|
+
if (typeof prop === 'symbol') {
|
|
205
|
+
return Reflect.set(target, prop, value, receiver)
|
|
206
|
+
}
|
|
207
|
+
if (tracker.associate && ctx.reflect.props[`${tracker.associate}.${prop}`]) {
|
|
208
|
+
return Reflect.set(ctx, `${tracker.associate}.${prop}`, value, withProp(ctx, symbols.receiver, receiver))
|
|
209
|
+
}
|
|
210
|
+
const shadow = createShadow(ctx, target, tracker.property, receiver)
|
|
211
|
+
return Reflect.set(target, prop, value, shadow)
|
|
212
|
+
},
|
|
213
|
+
apply: (target, thisArg, args) => {
|
|
214
|
+
return applyTraceable(proxy, target, thisArg, args)
|
|
215
|
+
},
|
|
216
|
+
})
|
|
217
|
+
return proxy
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function applyTraceable(proxy: any, value: any, thisArg: any, args: any[]) {
|
|
221
|
+
if (!value[symbols.invoke]) return Reflect.apply(value, thisArg, args)
|
|
222
|
+
return value[symbols.invoke].apply(proxy, args)
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/** Create a callable service object that dispatches through `symbols.invoke`. */
|
|
226
|
+
export function createCallable(name: string, proto: {}, tracker: Tracker) {
|
|
227
|
+
const self = function (...args: any[]) {
|
|
228
|
+
const proxy = createTraceable(self['ctx'], self, tracker)
|
|
229
|
+
return applyTraceable(proxy, self, this, args)
|
|
230
|
+
}
|
|
231
|
+
defineProperty(self, 'name', name)
|
|
232
|
+
return Object.setPrototypeOf(self, proto)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
interface StackInfo {
|
|
236
|
+
offset: number
|
|
237
|
+
error: Error
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function handleError(info: StackInfo, reason: any, getOuterStack: () => string[]): never {
|
|
241
|
+
const innerLines = info.error.stack!.split('\n')
|
|
242
|
+
|
|
243
|
+
// malformed error
|
|
244
|
+
if (typeof reason?.stack !== 'string') {
|
|
245
|
+
const outerError = new Error(reason)
|
|
246
|
+
const lines = outerError.stack!.split('\n')
|
|
247
|
+
lines.splice(1, Infinity, ...getOuterStack())
|
|
248
|
+
outerError.stack = lines.join('\n')
|
|
249
|
+
throw outerError
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// long stack trace
|
|
253
|
+
const lines: string[] = reason.stack.split('\n')
|
|
254
|
+
let index = lines.indexOf(innerLines[2])
|
|
255
|
+
if (index === -1) throw reason
|
|
256
|
+
|
|
257
|
+
index -= info.offset
|
|
258
|
+
while (index > 0) {
|
|
259
|
+
if (!lines[index - 1].endsWith(' (<anonymous>)')) break
|
|
260
|
+
index -= 1
|
|
261
|
+
}
|
|
262
|
+
lines.splice(index, Infinity, ...getOuterStack())
|
|
263
|
+
reason.stack = lines.join('\n')
|
|
264
|
+
throw reason
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/** Run a callback and splice outer call-site frames into thrown async errors. */
|
|
268
|
+
export function composeError<T>(callback: (info: StackInfo) => T, getOuterStack = buildOuterStack()): T {
|
|
269
|
+
const info: StackInfo = { offset: 1, error: new Error() }
|
|
270
|
+
|
|
271
|
+
try {
|
|
272
|
+
const result: any = callback(info)
|
|
273
|
+
if (isObject(result) && 'then' in result) {
|
|
274
|
+
return (result as any).then(undefined, (reason) => handleError(info, reason, getOuterStack)) as T
|
|
275
|
+
} else {
|
|
276
|
+
return result
|
|
277
|
+
}
|
|
278
|
+
} catch (reason: any) {
|
|
279
|
+
handleError(info, reason, getOuterStack)
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/** Capture a lazy stack-frame supplier for later error composition. */
|
|
284
|
+
export function buildOuterStack(offset = 0) {
|
|
285
|
+
const outerError = new Error()
|
|
286
|
+
return () => outerError.stack!.split('\n').slice(3 + offset)
|
|
287
|
+
}
|