Files
deepseek-harness/vendor/cordis/src/service.ts
T
Tianyi Cui 07f4047ff0 Use explicit ts specifiers for declarations
Restore explicit .ts relative specifiers in source and enable rewriteRelativeImportExtensions so emitted JS uses .js while declarations keep explicit .ts specifiers. Add a NodeNext declaration-consumer gate to prevent extensionless declaration regressions.
2026-06-22 06:11:00 +08:00

89 lines
2.8 KiB
TypeScript

import { defineProperty } from 'cosmokit'
import { Context } from './context.ts'
import { createCallable, joinPrototype, symbols, Tracker } from './utils.ts'
/**
* Base class for services that expose a named API on `ctx`.
*
* Subclasses call `super(ctx, name)` from their constructor. The service is
* registered immediately and is automatically removed with the owning fiber.
*/
export abstract class Service<out T = never> {
static readonly init: unique symbol = symbols.init
static readonly check: unique symbol = symbols.check
static readonly config: unique symbol = symbols.config
static readonly invoke: unique symbol = symbols.invoke
static readonly extend: unique symbol = symbols.extend
static readonly tracker: unique symbol = symbols.tracker
static readonly resolveConfig: unique symbol = symbols.resolveConfig
declare [symbols.config]: T
public name!: string
/** Register this instance as `name` in the current context. */
constructor(protected ctx: Context, name: string) {
name ??= this.constructor['provide'] as string
let self = this
const tracker: Tracker = {
associate: name,
property: 'ctx',
}
if (self[symbols.invoke]) {
self = createCallable(name, joinPrototype(Object.getPrototypeOf(this), Function.prototype), tracker)
}
self.ctx = ctx
self.name = name
defineProperty(self, symbols.tracker, tracker)
self.ctx.reflect.provide(name, self, this[symbols.check])
return self
}
protected [symbols.filter](ctx: Context) {
return ctx[symbols.isolate][this.name] === this.ctx[symbols.isolate][this.name]
}
protected [symbols.extend](props?: any) {
let self: any
if (this[Service.invoke]) {
self = createCallable(this.name, this, this[symbols.tracker])
} else {
self = Object.create(this)
}
return Object.assign(self, props)
}
/** Merge intercept config from ancestors with optional base and head values. */
[symbols.resolveConfig](base?: T, head?: T): T {
let intercept = this.ctx[Context.intercept]
const configs: any[] = []
while (this.name in intercept) {
if (Object.hasOwn(intercept, this.name)) {
configs.unshift(intercept[this.name])
}
intercept = Object.getPrototypeOf(intercept)
}
if (base) configs.unshift(base)
if (head) configs.push(head)
if (this['Config']?.merge) {
return this['Config'].merge(...configs)
} else {
return Object.assign({}, ...configs)
}
}
static [Symbol.hasInstance](instance: any) {
if (!instance) return false
let constructor = instance.constructor
while (constructor) {
// constructor may be a proxy
constructor = constructor.prototype?.constructor
if (constructor === this) return true
constructor &&= Object.getPrototypeOf(constructor)
}
return false
}
}