-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
42 changed files
with
1,631 additions
and
799 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
<!doctype html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
<title>Document</title> | ||
</head> | ||
<body> | ||
<script> | ||
Function.prototype.before = function (fn) { | ||
const self = this; | ||
return function () { | ||
fn(); | ||
self.apply(this, arguments); | ||
}; | ||
}; | ||
Function.prototype.after = function (fn) { | ||
const self = this; | ||
return function () { | ||
self.apply(this, arguments); | ||
fn(); | ||
}; | ||
}; | ||
|
||
class Symbols { | ||
static container = Symbol.for('kotori.context.container'); | ||
static containerKey = (prop) => Symbol.for(`kotori.context.container.${prop}`); | ||
} | ||
|
||
class Context { | ||
static handler() { | ||
return { | ||
get(target, prop, receiver) { | ||
if (prop === 'identity') return Reflect.get(target, Symbols.set).get(target); | ||
return Reflect.get(target, prop, receiver); | ||
} | ||
}; | ||
} | ||
|
||
[Symbols.container] = new Map(); | ||
[Symbols.set] = new WeakMap(); | ||
|
||
root; | ||
|
||
constructor(config = {}) { | ||
this.root = config.root || this; | ||
} | ||
|
||
get(prop) { | ||
return this[Symbols.container].get(prop); | ||
} | ||
|
||
inject(prop, value) { | ||
const key = value ? Symbols.containerKey(prop) : prop; | ||
if (value) { | ||
if (this[Symbols.container].has(key)) return; | ||
this.provide(key, value); | ||
} else { | ||
if (!this[Symbols.container].has(key)) return; | ||
} | ||
if (key in this) return; | ||
this[prop] = this.get(key); | ||
} | ||
|
||
provide(prop, value) { | ||
if (this[Symbols.container].has(prop)) return; | ||
this[Symbols.container].set(prop, value); | ||
} | ||
|
||
mixin(prop, keys) { | ||
if (!this[Symbols.container].has(prop)) return; | ||
keys.forEach((key) => { | ||
if (key in this) return; | ||
const instance = this.get(prop); | ||
if (!instance) return; | ||
if (!(key in instance)) return; | ||
if (typeof this.get(prop)[key] === 'function') { | ||
this[key] = this.get(prop)[key].bind(this.get(prop)); | ||
return; | ||
} | ||
this[key] = this.get(prop)[key]; | ||
}); | ||
} | ||
|
||
extends(meta = {}, identity = 'sub') { | ||
const metaHandle = meta; | ||
Object.keys(metaHandle).forEach((key) => { | ||
if (typeof this[key] === 'function') delete metaHandle[key]; | ||
}); | ||
const ctx = Object.assign(new Context(this.root), this, meta, { identity }); | ||
ctx[Symbols.container].forEach((value, key) => { | ||
if (!('ctx' in value)) { | ||
ctx[Symbols.container].set(key, value); | ||
return; | ||
} | ||
const instance = Object.assign(value, { ctx }); | ||
ctx[Symbols.container].set(key, instance); | ||
}); | ||
return ctx; | ||
} | ||
|
||
proxy() { | ||
return new Proxy(this, Context.handler); | ||
} | ||
} | ||
class Event { | ||
#stack = new Set(); | ||
heart = 114514; | ||
ctx; | ||
constructor(ctx) { | ||
this.ctx = ctx; | ||
} | ||
on(callback) { | ||
this.#stack.add(callback); | ||
} | ||
emit() { | ||
// console.log(this.heart); | ||
console.log(`identity ->`, this.ctx.identity); | ||
this.#stack.forEach((callback) => callback()); | ||
} | ||
} | ||
class Kotori extends Context { | ||
options; | ||
constructor(options) { | ||
super(); | ||
const event = new Event(this); | ||
this.provide('event', event); | ||
this.mixin('event', ['emit', 'on']); | ||
this.options = options; | ||
} | ||
} | ||
const ctx = new Kotori({ v: 1 }); | ||
// event.emit(); | ||
// ctx.inject('event').emit(); | ||
ctx.emit(); | ||
// console.log(ctx.options); | ||
ctx.on(() => console.log('')); | ||
// ctx.emit(); | ||
const ctx2 = ctx.extends({ options: 2 }); | ||
// console.log(ctx.options, ctx2.options); | ||
// ctx2.inject('event').on(() => console.log('nb from ctx2 with inject')); | ||
ctx2.emit(); | ||
ctx2.extends({}, 23).on(() => console.log('by ctx2')); | ||
ctx2.extends({}, 233).get('event').emit(); | ||
ctx2.extends({}, 2333).emit(); | ||
// const ctx3 = ctx2.extends(); | ||
// const ctx4 = ctx.extends(); | ||
// ctx2.inject('event').emit(); | ||
// console.log(ctx3.options, ctx4.options, ctx4); | ||
console.log(ctx.identity, ctx2.get('event'), ctx2[Symbols.container]); | ||
|
||
/* ctx2.provide('cc', { f: () => console.log('cc') }); */ | ||
const ctx3 = ctx2.extends({ options: 3, emit: 1 }, 'ccc'); | ||
ctx2.inject('cc', () => console.log('cc')); | ||
console.log(ctx2, ctx, ctx3, ctx2.extends()); | ||
</script> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { join } from 'path'; | ||
import { loadConfig } from '@kotori-bot/tools'; | ||
import { KotoriConfig as KotoriConfigType, PackageInfo, kotoriConfigSchema, packageInfoSchema } from '../types'; | ||
import { CoreError } from '../utils/errror'; | ||
|
||
type ConfigType = KotoriConfigType & { pkg: PackageInfo }; | ||
|
||
export class KotoriConfig implements ConfigType { | ||
readonly pkg: PackageInfo; | ||
|
||
readonly baseDir: KotoriConfigType['baseDir']; | ||
|
||
readonly config: KotoriConfigType['config']; | ||
|
||
readonly options: KotoriConfigType['options']; | ||
|
||
constructor(config?: KotoriConfigType) { | ||
const info = loadConfig(join(__dirname, '../../package.json')) as unknown; | ||
if (!info || Object.values(info).length === 0) throw new CoreError('Cannot find kotori-bot package.json'); | ||
const result = packageInfoSchema.parseSafe(info); | ||
if (!result.value) throw new CoreError(`File package.json format error: ${result.error.message}`); | ||
this.pkg = result.data; | ||
const handle = kotoriConfigSchema.parseSafe(config); | ||
if (!handle.value) throw new CoreError('Unexpected error in parsing config (baseDir,kotori,options)'); | ||
this.baseDir = handle.data.baseDir; | ||
this.config = handle.data.config; | ||
this.options = handle.data.options; | ||
} | ||
} | ||
|
||
export default KotoriConfig; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,63 +1,20 @@ | ||
import { loadConfig, obj } from '@kotori-bot/tools'; | ||
import path from 'path'; | ||
import { Parser } from 'tsukiko'; | ||
import { DEFAULT_LANG } from '@kotori-bot/i18n'; | ||
import { | ||
type BaseDir, | ||
type GlobalConfig, | ||
type GlobalOptions, | ||
type KotoriConfig, | ||
type PackageInfo, | ||
packageInfoSchema, | ||
kotoriConfigSchema, | ||
type ServiceConstructor | ||
} from '../types'; | ||
import type Api from '../components/api'; | ||
import { CoreError } from '../utils/errror'; | ||
import { DEFAULT_COMMAND_PREFIX, DEFAULT_ENV, DEFAULT_MODULES_DIR, DEFAULT_ROOT_DIR } from '../consts'; | ||
import { Context } from '../context'; | ||
import KotoriConfig from './config'; | ||
import Events from './events'; | ||
import Modules from './modules'; | ||
|
||
export const defaultConfig = { | ||
baseDir: { | ||
root: path.resolve(DEFAULT_ROOT_DIR), | ||
modules: path.resolve(DEFAULT_MODULES_DIR) | ||
}, | ||
config: { | ||
global: { | ||
lang: DEFAULT_LANG, | ||
'command-prefix': DEFAULT_COMMAND_PREFIX | ||
}, | ||
adapter: {} | ||
}, | ||
options: { | ||
env: DEFAULT_ENV | ||
} | ||
}; | ||
|
||
export class Core { | ||
protected readonly serviceStack: obj<[ServiceConstructor, Parser<unknown>?]> = {}; | ||
|
||
protected readonly botStack: obj<Api[]> = {}; | ||
|
||
readonly baseDir: BaseDir; | ||
|
||
readonly config: GlobalConfig; | ||
|
||
readonly options: GlobalOptions; | ||
|
||
readonly package: PackageInfo; | ||
declare module '../context' { | ||
interface Context extends KotoriConfig {} | ||
} | ||
|
||
export default class Kotori extends Context { | ||
constructor(config?: KotoriConfig) { | ||
const info = loadConfig(path.join(__dirname, '../../package.json')) as unknown; | ||
if (!info || Object.values(info).length === 0) throw new CoreError('Cannot find kotori-bot package.json'); | ||
const result = packageInfoSchema.parseSafe(info); | ||
if (!result.value) throw new CoreError(`File package.json format error: ${result.error.message}`); | ||
this.package = result.data; | ||
const handle = kotoriConfigSchema.parseSafe(config); | ||
if (!handle.value) throw new CoreError('Unexpected error in parsing config (basedir,kotori,options)'); | ||
this.baseDir = handle.data!.baseDir; | ||
this.config = handle.data!.config; | ||
this.options = handle.data!.options; | ||
super(); | ||
this.provide('config', new KotoriConfig(config)); | ||
this.mixin('config', ['pkg', 'baseDir', 'config', 'options']); | ||
this.provide('events', new Events()); | ||
this.mixin('events', ['emit', 'on', 'once', 'off', 'offAll']); | ||
this.provide('modules', new Modules(this)); | ||
this.mixin('modules', ['load', 'dispose']); | ||
} | ||
} | ||
|
||
export default Core; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { Events as EventsOrigin } from '@kotori-bot/tools'; | ||
import { EventsList } from '../types'; | ||
|
||
declare module '../context' { | ||
interface Context extends EventsOrigin<EventsList> {} | ||
} | ||
|
||
export const Events = EventsOrigin<EventsList>; | ||
export default Events; |
Oops, something went wrong.