New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@koishijs/core

Package Overview
Dependencies
Maintainers
1
Versions
167
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@koishijs/core - npm Package Compare versions

Comparing version 4.16.2 to 4.16.3

163

lib/index.d.ts

@@ -8,4 +8,4 @@ /// <reference types="node" />

import { Fragment, Schema, h, Universal, Bot, Quester } from '@satorijs/core';
import { LocaleTree } from '@koishijs/i18n-utils';
import { Disposable, GetEvents, Parameters, ReturnType, ThisType } from 'cordis';
import { LocaleTree } from '@koishijs/i18n-utils';
import { version } from '../package.json';

@@ -158,2 +158,57 @@ export interface Events {

}
declare const kTemplate: unique symbol;
export interface Context {
i18n: I18n;
}
export interface Events {
'internal/i18n'(): void;
}
type GroupNames<P extends string, K extends string = never> = P extends `${string}(${infer R})${infer S}` ? GroupNames<S, K | R> : K;
export type MatchResult<P extends string = never> = Record<GroupNames<P>, string>;
export function createMatch<P extends string>(pattern: P): (string: string) => undefined | MatchResult<P>;
export interface CompareOptions {
minSimilarity?: number;
}
export namespace I18n {
type Node = string | Store;
interface Store {
[kTemplate]?: string;
[K: string]: Node;
}
type Formatter = (value: any, args: string[], locale: string) => string;
type Renderer = (dict: Dict, params: any, locale: string) => string;
interface FindOptions extends CompareOptions {
}
interface FindResult<P extends string> {
locale: string;
data: MatchResult<P>;
similarity: number;
}
}
export class I18n {
ctx: Context;
_data: Dict<Dict<string>>;
_presets: Dict<I18n.Renderer>;
locales: LocaleTree;
constructor(ctx: Context, config: I18n.Config);
fallback(locales: string[]): string[];
compare(expect: string, actual: string, options?: CompareOptions): number;
get(key: string, locales?: string[]): Dict<string>;
private set;
define(locale: string, dict: I18n.Store): () => void;
define(locale: string, key: string, value: I18n.Node): () => void;
find<P extends string>(pattern: P, actual: string, options?: I18n.FindOptions): I18n.FindResult<P>[];
_render(value: I18n.Node, params: any, locale: string): h[];
/** @deprecated */
text(locales: string[], paths: string[], params: object): string;
render(locales: string[], paths: string[], params: object): h[];
}
export namespace I18n {
interface Config {
locales?: string[];
output?: 'prefer-user' | 'prefer-channel';
match?: 'strict' | 'prefer-input' | 'prefer-output';
}
const Config: Schema<Config>;
}
declare module '@satorijs/core' {

@@ -167,32 +222,32 @@ interface Context {

}
export interface PermissionConfig {
authority?: number;
permissions?: string[];
dependencies?: string[];
}
declare class DAG {
store: Map<string, Map<string, Computed<boolean>[]>>;
define(name: string): void;
delete(name: string): void;
link(source: string, target: string, condition: Computed<boolean>): void;
unlink(source: string, target: string, condition: Computed<boolean>): void;
subgraph(parents: Iterable<string>, session: Partial<Session>, result?: Set<string>): Set<string>;
}
export namespace Permissions {
type ProvideCallback = (name: string, session: Partial<Session>) => Awaitable<boolean>;
type Links<P extends string> = undefined | string[] | ((data: MatchResult<P>) => undefined | string[]);
type Check<P extends string> = (data: MatchResult<P>, session: Partial<Session>) => Awaitable<boolean>;
interface Options<P extends string = string> {
list?: () => string[];
check?: Check<P>;
depends?: Links<P>;
inherits?: Links<P>;
}
interface Entry extends Options {
match: (string: string) => undefined | MatchResult;
}
interface Config {
authority?: number;
permissions?: string[];
dependencies?: string[];
}
}
export class Permissions {
ctx: Context;
_inherits: DAG;
_depends: DAG;
_providers: Dict<Permissions.ProvideCallback>;
store: Permissions.Entry[];
constructor(ctx: Context);
private get caller();
provide(name: string, callback: Permissions.ProvideCallback): () => void;
define<P extends string>(pattern: P, options: Permissions.Options<P>): () => void;
provide<P extends string>(pattern: P, check: Permissions.Check<P>): () => void;
inherit<P extends string>(pattern: P, inherits: Permissions.Links<P>): () => void;
depend<P extends string>(pattern: P, depends: Permissions.Links<P>): () => void;
list(result?: Set<string>): string[];
check(name: string, session: Partial<Session>): Promise<boolean>;
config(name: string, config?: PermissionConfig, defaultAuthority?: number): () => void;
define(name: string, inherits: string[]): () => void;
inherit(child: string, parent: string, condition?: Computed<boolean>): () => void;
depend(dependent: string, dependency: string, condition?: Computed<boolean>): () => void;
list(): string[];
subgraph(type: 'inherits' | 'depends', parents: Iterable<string>, result?: Set<string>): Set<string>;
test(names: Iterable<string>, session?: Partial<Session>, cache?: Map<string, Promise<boolean>>): Promise<boolean>;

@@ -283,3 +338,3 @@ }

export function parseValue(source: string, kind: string, argv: Argv, decl?: Declaration): any;
export interface OptionConfig<T extends Type = Type> extends PermissionConfig {
export interface OptionConfig<T extends Type = Type> extends Permissions.Config {
aliases?: string[];

@@ -359,4 +414,2 @@ symbols?: string[];

private _checkers;
static defaultConfig: Command.Config;
static defaultOptionConfig: Argv.OptionConfig;
private static _userFields;

@@ -368,3 +421,3 @@ private static _channelFields;

static channelFields(fields: FieldCollector<'channel'>): typeof Command;
constructor(name: string, decl: string, ctx: Context);
constructor(name: string, decl: string, ctx: Context, config: Command.Config);
get caller(): Context;

@@ -406,3 +459,3 @@ get displayName(): string;

export namespace Command {
interface Config extends Argv.CommandBase.Config, PermissionConfig {
interface Config extends Argv.CommandBase.Config, Permissions.Config {
/** disallow unknown options */

@@ -461,54 +514,2 @@ checkUnknown?: boolean;

}
declare const kTemplate: unique symbol;
export interface Context {
i18n: I18n;
}
export interface Events {
'internal/i18n'(): void;
}
export interface CompareOptions {
minSimilarity?: number;
}
export namespace I18n {
type Node = string | Store;
interface Store {
[kTemplate]?: string;
[K: string]: Node;
}
type Formatter = (value: any, args: string[], locale: string) => string;
type Renderer = (dict: Dict, params: any, locale: string) => string;
interface FindOptions extends CompareOptions {
}
interface FindResult {
locale: string;
data: Dict;
similarity: number;
}
}
export class I18n {
ctx: Context;
_data: Dict<Dict<string>>;
_presets: Dict<I18n.Renderer>;
locales: LocaleTree;
constructor(ctx: Context, config: I18n.Config);
fallback(locales: string[]): string[];
compare(expect: string, actual: string, options?: CompareOptions): number;
get(key: string, locales?: string[]): Dict<string>;
private set;
define(locale: string, dict: I18n.Store): () => void;
define(locale: string, key: string, value: I18n.Node): () => void;
find(path: string, actual: string, options?: I18n.FindOptions): I18n.FindResult[];
_render(value: I18n.Node, params: any, locale: string): h[];
/** @deprecated */
text(locales: string[], paths: string[], params: object): string;
render(locales: string[], paths: string[], params: object): h[];
}
export namespace I18n {
interface Config {
locales?: string[];
output?: 'prefer-user' | 'prefer-channel';
match?: 'strict' | 'prefer-input' | 'prefer-output';
}
const Config: Schema<Config>;
}
export interface PromptOptions {

@@ -515,0 +516,0 @@ timeout?: number;

{
"name": "@koishijs/core",
"description": "Core Features for Koishi",
"version": "4.16.2",
"version": "4.16.3",
"main": "lib/index.cjs",

@@ -35,9 +35,9 @@ "module": "lib/index.mjs",

"@koishijs/i18n-utils": "^1.0.0",
"@koishijs/utils": "^7.1.1",
"@koishijs/utils": "^7.1.2",
"@minatojs/core": "^2.8.1",
"@satorijs/core": "^3.3.2",
"@satorijs/core": "^3.3.4",
"cordis": "^3.4.1",
"cosmokit": "^1.5.1",
"cosmokit": "^1.5.2",
"fastest-levenshtein": "^1.0.16"
}
}

@@ -8,3 +8,3 @@ import { Awaitable, camelize, Dict, isNullable, remove } from 'cosmokit'

import { FieldCollector, Session } from '../session'
import { PermissionConfig } from '../permission'
import { Permissions } from '../permission'
import { Context } from '../context'

@@ -61,10 +61,2 @@

static defaultConfig: Command.Config = {
showWarning: true,
handleError: true,
slash: true,
}
static defaultOptionConfig: Argv.OptionConfig = {}
private static _userFields: FieldCollector<'user'>[] = []

@@ -85,4 +77,10 @@ private static _channelFields: FieldCollector<'channel'>[] = []

constructor(name: string, decl: string, ctx: Context) {
super(name, decl, ctx, { ...Command.defaultConfig })
constructor(name: string, decl: string, ctx: Context, config: Command.Config) {
super(name, decl, ctx, {
showWarning: true,
handleError: true,
slash: true,
...config,
})
this.config.permissions ??= [`authority:${config?.authority ?? 1}`]
this._registerAlias(name)

@@ -109,8 +107,5 @@ ctx.$commander._commandList.push(this)

set parent(parent: Command) {
// We do not use `ctx.permissions.depend()` here
// because the permission `command.${name}` itself is disposable.
if (this._parent === parent) return
if (this._parent) {
remove(this._parent.children, this)
this.ctx.permissions._depends.unlink(`command.${this.name}`, `command.${this._parent.name}`, true)
}

@@ -120,3 +115,2 @@ this._parent = parent

parent.children.push(this)
this.ctx.permissions._depends.link(`command.${this.name}`, `command.${parent.name}`, true)
}

@@ -250,9 +244,6 @@ }

}
const config = {
...Command.defaultOptionConfig,
...args[0] as Argv.OptionConfig,
}
const config = { ...args[0] as Argv.OptionConfig }
config.permissions ??= [`authority:${config.authority ?? 0}`]
this._createOption(name, desc, config)
this.caller.collect('command.option', () => this.removeOption(name))
this.caller.permissions.config(`command.${this.name}.option.${name}`, config, 0)
this.caller.collect('option', () => this.removeOption(name))
return this

@@ -392,3 +383,3 @@ }

export namespace Command {
export interface Config extends Argv.CommandBase.Config, PermissionConfig {
export interface Config extends Argv.CommandBase.Config, Permissions.Config {
/** disallow unknown options */

@@ -407,3 +398,4 @@ checkUnknown?: boolean

export const Config: Schema<Config> = Schema.object({
authority: Schema.natural().description('指令的权限等级。').default(1).hidden(),
permissions: Schema.array(String).role('perms').default(['authority:1']).description('权限继承。'),
dependencies: Schema.array(String).role('perms').description('权限依赖。'),
slash: Schema.boolean().description('启用斜线指令功能。').default(true),

@@ -410,0 +402,0 @@ checkUnknown: Schema.boolean().description('是否检查未知选项。').default(false).hidden(),

@@ -125,3 +125,3 @@ import { Awaitable, defineProperty } from 'cosmokit'

name = this.resolve(name)!.name
return ctx.permissions.test(`command.${name}`, session, cache)
return ctx.permissions.test(`command:${name}`, session, cache)
},

@@ -137,3 +137,4 @@ })

ctx.schema.extend('command-option', Schema.object({
authority: Schema.computed(Schema.natural()).description('选项的权限等级。').default(0).hidden(),
permissions: Schema.array(String).role('perms').default(['authority:0']).description('权限继承。'),
dependencies: Schema.array(String).role('perms').description('权限依赖。'),
}), 1000)

@@ -251,3 +252,3 @@

const isLast = index === segments.length - 1
command = new Command(name, isLast ? decl : '', caller)
command = new Command(name, isLast ? decl : '', caller, isLast ? config : {})
command._disposables.push(caller.i18n.define('', {

@@ -257,3 +258,2 @@ [`commands.${command.name}.$`]: '',

}))
command._disposables.push(caller.permissions.config(`command.${name}`, isLast ? config : {}, 1))
created.push(command)

@@ -260,0 +260,0 @@ root ||= command

@@ -7,3 +7,3 @@ import { camelCase, Dict, paramCase, Time } from 'cosmokit'

import { Next } from '../middleware'
import { PermissionConfig } from '../permission'
import { Permissions } from '../permission'
import { Disposable } from 'cordis'

@@ -373,3 +373,3 @@ import { Session } from '../session'

export interface OptionConfig<T extends Type = Type> extends PermissionConfig {
export interface OptionConfig<T extends Type = Type> extends Permissions.Config {
aliases?: string[]

@@ -376,0 +376,0 @@ symbols?: string[]

@@ -6,2 +6,32 @@ import { isNullable } from 'cosmokit'

export default function validate(ctx: Context) {
ctx.permissions.define('command:(name)', {
depends: ({ name }) => {
const command = ctx.$commander.get(name)
if (!command) return
const depends = [...command.config.dependencies ?? []]
if (command.parent) depends.push(`command:${command.parent.name}`)
return depends
},
inherits: ({ name }) => {
return ctx.$commander.get(name)?.config.permissions
},
list: () => {
return ctx.$commander._commandList.map(command => `command:${command.name}`)
},
})
ctx.permissions.define('command:(name):option:(name2)', {
depends: ({ name, name2 }) => {
return ctx.$commander.get(name)?._options[name2]?.dependencies
},
inherits: ({ name, name2 }) => {
return ctx.$commander.get(name)?._options[name2]?.permissions
},
list: () => {
return ctx.$commander._commandList.flatMap(command => {
return Object.keys(command._options).map(name => `command:${command.name}:option:${name}`)
})
},
})
// check user

@@ -17,9 +47,9 @@ ctx.before('command/execute', async (argv: Argv<'authority'>) => {

// check permissions
const permissions = [`command.${command.name}`]
const permissions = [`command:${command.name}`]
for (const option of Object.values(command._options)) {
if (option.name in options) {
permissions.push(`command.${command.name}.option.${option.name}`)
permissions.push(`command:${command.name}:option:${option.name}`)
}
}
if (!await ctx.permissions.test(permissions, session as any)) {
if (!await ctx.permissions.test(permissions, session)) {
return sendHint('internal.low-authority')

@@ -26,0 +56,0 @@ }

@@ -22,2 +22,27 @@ import { distance } from 'fastest-levenshtein'

type GroupNames<P extends string, K extends string = never> =
| P extends `${string}(${infer R})${infer S}`
? GroupNames<S, K | R>
: K
export type MatchResult<P extends string = never> = Record<GroupNames<P>, string>
export function createMatch<P extends string>(pattern: P): (string: string) => undefined | MatchResult<P> {
const groups: string[] = []
const source = pattern.replace(/\(([^)]+)\)/g, (_, name) => {
groups.push(name)
return '(.+)'
})
const regexp = new RegExp(`^${source}$`)
return (string: string) => {
const capture = regexp.exec(string)
if (!capture) return
const data: any = {}
for (let i = 0; i < groups.length; i++) {
data[groups[i]] = capture[i + 1]
}
return data
}
}
export interface CompareOptions {

@@ -40,5 +65,5 @@ minSimilarity?: number

export interface FindResult {
export interface FindResult<P extends string> {
locale: string
data: Dict
data: MatchResult<P>
similarity: number

@@ -119,15 +144,10 @@ }

find(path: string, actual: string, options: I18n.FindOptions = {}): I18n.FindResult[] {
find<P extends string>(pattern: P, actual: string, options: I18n.FindOptions = {}): I18n.FindResult<P>[] {
if (!actual) return []
const groups: string[] = []
path = path.replace(/\(([^)]+)\)/g, (_, name) => {
groups.push(name)
return '([^.]+)'
})
const pattern = new RegExp(`^${path}$`)
const results: I18n.FindResult[] = []
const match = createMatch(pattern)
const results: I18n.FindResult<P>[] = []
for (const locale in this._data) {
for (const path in this._data[locale]) {
const capture = pattern.exec(path)
if (!capture) continue
const data = match(path)
if (!data) continue
const expect = this._data[locale][path]

@@ -137,6 +157,2 @@ if (typeof expect !== 'string') continue

if (!similarity) continue
const data = {}
for (let i = 0; i < groups.length; i++) {
data[groups[i]] = capture[i + 1]
}
results.push({ locale, data, similarity })

@@ -143,0 +159,0 @@ }

import { Logger } from '@satorijs/core'
import { Awaitable, Dict, remove } from 'cosmokit'
import { Computed } from './filter'
import { Awaitable, defineProperty, remove } from 'cosmokit'
import { Session } from './session'
import { Context } from './context'
import { createMatch, MatchResult } from './i18n'

@@ -19,72 +19,43 @@ const logger = new Logger('app')

export interface PermissionConfig {
authority?: number
permissions?: string[]
dependencies?: string[]
}
export namespace Permissions {
export type Links<P extends string> = undefined | string[] | ((data: MatchResult<P>) => undefined | string[])
export type Check<P extends string> = (data: MatchResult<P>, session: Partial<Session>) => Awaitable<boolean>
class DAG {
store: Map<string, Map<string, Computed<boolean>[]>> = new Map()
define(name: string) {
this.delete(name)
this.store.set(name, new Map())
export interface Options<P extends string = string> {
list?: () => string[]
check?: Check<P>
depends?: Links<P>
inherits?: Links<P>
}
delete(name: string) {
this.store.delete(name)
for (const map of this.store.values()) {
map.delete(name)
}
export interface Entry extends Options {
match: (string: string) => undefined | MatchResult
}
link(source: string, target: string, condition: Computed<boolean>) {
if (!this.store.has(source)) this.store.set(source, new Map())
const map = this.store.get(source)
if (!map.has(target)) map.set(target, [])
map.get(target).push(condition)
export interface Config {
authority?: number
permissions?: string[]
dependencies?: string[]
}
unlink(source: string, target: string, condition: Computed<boolean>) {
const list = this.store.get(source)?.get(target)
if (list) remove(list, condition)
}
subgraph(parents: Iterable<string>, session: Partial<Session>, result = new Set<string>()): Set<string> {
let node: string
const queue = [...parents]
while ((node = queue.shift())) {
if (result.has(node)) continue
result.add(node)
const map = this.store.get(node)
if (!map) continue
for (const [key, conditions] of map) {
if (conditions.every(value => !session.resolve(value))) continue
queue.push(key)
}
}
return result
}
}
export namespace Permissions {
export type ProvideCallback = (name: string, session: Partial<Session>) => Awaitable<boolean>
}
export class Permissions {
_inherits = new DAG()
_depends = new DAG()
_providers: Dict<Permissions.ProvideCallback> = Object.create(null)
public store: Permissions.Entry[] = []
constructor(public ctx: Context) {
this.provide('authority.*', (name, { user }: Partial<Session<'authority'>>) => {
const value = +name.slice(10)
return !user || user.authority >= value
defineProperty(this, Context.current, ctx)
ctx.alias('permissions', ['perms'])
this.define('authority:(value)', {
check: ({ value }, { user }: Partial<Session<'authority'>>) => {
return !user || user.authority >= +value
},
list: () => Array(5).fill(0).map((_, i) => `authority:${i}`),
})
this.provide('*', (name, session) => {
this.provide('(name)', ({ name }, session) => {
return session.bot?.checkPermission(name, session)
})
this.provide('*', (name, session: Partial<Session<'permissions', 'permissions'>>) => {
this.provide('(name)', ({ name }, session: Partial<Session<'permissions', 'permissions'>>) => {
return session.permissions?.includes(name)

@@ -100,71 +71,66 @@ || session.user?.permissions?.includes(name)

provide(name: string, callback: Permissions.ProvideCallback) {
this._providers[name] = callback
return this.caller?.collect('permission-provide', () => {
return delete this._providers[name]
define<P extends string>(pattern: P, options: Permissions.Options<P>) {
const entry: Permissions.Entry = {
...options,
match: createMatch(pattern),
}
if (!pattern.includes('(')) entry.list ||= () => [pattern]
return this.caller.effect(() => {
this.store.push(entry)
return () => remove(this.store, entry)
})
}
async check(name: string, session: Partial<Session>) {
try {
const callbacks = Object.entries(this._providers)
.filter(([key]) => name === key || key.endsWith('*') && name.startsWith(key.slice(0, -1)))
.map(([key, value]) => value)
if (!callbacks.length) return false
for (const callback of callbacks) {
if (await callback(name, session)) return true
}
return false
} catch (error) {
logger.warn(error)
return false
}
provide<P extends string>(pattern: P, check: Permissions.Check<P>) {
return this.define(pattern, { check })
}
config(name: string, config: PermissionConfig = {}, defaultAuthority = 0) {
for (const dep of config.dependencies || []) {
this._depends.link(name, dep, true)
}
const children = config.permissions || []
if (!config.permissions || typeof config.authority === 'number') {
children.push(`authority.${config.authority ?? defaultAuthority}`)
}
for (const child of children) {
this._inherits.link(name, child, true)
}
return this.caller?.collect('permission-config', () => {
this._depends.delete(name)
this._inherits.delete(name)
this.ctx.emit('internal/permission')
})
inherit<P extends string>(pattern: P, inherits: Permissions.Links<P>) {
return this.define(pattern, { inherits })
}
define(name: string, inherits: string[]) {
this._inherits.define(name)
this.ctx.emit('internal/permission')
for (const permission of inherits) {
this.inherit(name, permission)
}
return this.caller?.collect('permission-define', () => {
this._inherits.delete(name)
this.ctx.emit('internal/permission')
})
depend<P extends string>(pattern: P, depends: Permissions.Links<P>) {
return this.define(pattern, { depends })
}
inherit(child: string, parent: string, condition: Computed<boolean> = true) {
this._inherits.link(parent, child, condition)
return this.caller?.collect('permission-inherit', () => {
this._inherits.unlink(parent, child, condition)
})
list(result = new Set<string>()) {
for (const { list } of this.store) {
if (!list) continue
for (const name of list()) {
result.add(name)
}
}
return [...result]
}
depend(dependent: string, dependency: string, condition: Computed<boolean> = true) {
this._depends.link(dependent, dependency, condition)
return this.caller?.collect('permission-depend', () => {
this._depends.unlink(dependent, dependency, condition)
})
async check(name: string, session: Partial<Session>) {
const results = await Promise.all(this.store.map(async ({ match, check }) => {
if (!check) return false
const data = match(name)
if (!data) return false
try {
return await check(data, session)
} catch (error) {
logger.warn(error)
return false
}
}))
return results.some(Boolean)
}
list() {
return [...this._inherits.store.keys()]
subgraph(type: 'inherits' | 'depends', parents: Iterable<string>, result = new Set<string>()): Set<string> {
let name: string
const queue = [...parents]
while ((name = queue.shift())) {
if (result.has(name)) continue
result.add(name)
for (const entry of this.store) {
const data = entry.match(name)
if (!data) continue
let links = entry[type]
if (typeof links === 'function') links = links(data)
if (Array.isArray(links)) queue.push(...links)
}
}
return result
}

@@ -175,4 +141,4 @@

if (typeof names === 'string') names = [names]
for (const name of this._depends.subgraph(names, session)) {
const parents = [...this._inherits.subgraph([name], session)]
for (const name of this.subgraph('depends', names)) {
const parents = [...this.subgraph('inherits', [name])]
const results = await Promise.all(parents.map(parent => {

@@ -179,0 +145,0 @@ let result = cache.get(parent)

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc