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

koishi-core

Package Overview
Dependencies
Maintainers
1
Versions
182
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

koishi-core - npm Package Compare versions

Comparing version 2.4.2 to 3.0.0-alpha.0

20

dist/app.d.ts

@@ -0,1 +1,2 @@

/// <reference types="node" />
import { Observed } from 'koishi-utils';

@@ -5,8 +6,9 @@ import { Command } from './command';

import { Group, User, Database } from './database';
import { BotOptions, Server } from './server';
import { BotOptions, Server, Bot } from './server';
import { Session } from './session';
import LruCache from 'lru-cache';
import * as http from 'http';
import type Router from 'koa-router';
export interface AppOptions extends BotOptions {
port?: number;
type?: string;
bots?: BotOptions[];

@@ -37,4 +39,7 @@ prefix?: string | string[];

options: AppOptions;
server: Server;
bots: Record<string, Bot>;
servers: Record<string, Server>;
status: AppStatus;
router?: Router;
_httpServer?: http.Server;
_database: Database;

@@ -45,13 +50,14 @@ _commands: Command[];

_hooks: Record<keyof any, [Context, (...args: any[]) => any][]>;
_userCache: LruCache<number, Observed<Partial<User>, Promise<void>>>;
_groupCache: LruCache<number, Observed<Partial<Group>, Promise<void>>>;
_userCache: Record<string, LruCache<string, Observed<Partial<User>, Promise<void>>>>;
_groupCache: LruCache<string, Observed<Partial<Group>, Promise<void>>>;
private _nameRE;
private _prefixRE;
private _getSelfIdsPromise;
static defaultConfig: AppOptions;
constructor(options?: AppOptions);
createServer(): void;
prepare(): void;
getSelfIds(): Promise<number[]>;
start(): Promise<void>;
private _listen;
stop(): Promise<void>;
private _close;
private _preprocess;

@@ -58,0 +64,0 @@ private _receive;

@@ -110,5 +110,5 @@ /// <reference types="node" />

stringify(args: readonly string[], options: any): string;
execute(argv: ParsedArgv<U, G, O>): Promise<void>;
execute(argv: ParsedArgv<U, G, O>): any;
dispose(): void;
}
export {};

@@ -1,6 +0,5 @@

/// <reference types="koa-router" />
import { Logger } from 'koishi-utils';
import { Command, CommandConfig, ParsedArgv, ExecuteArgv } from './command';
import { PostType, Session } from './session';
import { User, Group } from './database';
import { EventType, Session } from './session';
import { User, Group, PlatformKind } from './database';
import { App } from './app';

@@ -16,3 +15,3 @@ export declare type NextFunction = (next?: NextFunction) => Promise<void>;

export declare type Disposable = () => void;
interface ScopeSet extends Array<number> {
interface ScopeSet extends Array<string> {
positive?: boolean;

@@ -31,10 +30,8 @@ }

constructor(scope: Scope, app?: App);
get router(): import("koa-router")<any, {}>;
get database(): import("./database").Database;
set database(database: import("./database").Database);
logger(name: string): Logger;
get bots(): import("./server").Bot[];
group(...ids: number[]): Context;
user(...ids: number[]): Context;
private(...ids: number[]): Context;
group(...ids: string[]): Context;
user(...ids: string[]): Context;
private(...ids: string[]): Context;
match(session: Session): boolean;

@@ -64,45 +61,44 @@ plugin<T extends PluginFunction<this>>(plugin: T, options?: T extends PluginFunction<this, infer U> ? U : never): this;

command(rawName: string, description: string, config?: CommandConfig): Command;
broadcast(message: string, forced?: boolean): Promise<number[]>;
broadcast(groups: readonly number[], message: string, forced?: boolean): Promise<number[]>;
broadcast(message: string, forced?: boolean): Promise<string[]>;
broadcast(groups: readonly string[], message: string, forced?: boolean): Promise<string[]>;
dispose(): void;
}
export declare type RawSession<P extends PostType = PostType> = Session<never, never, never, P>;
export declare type RawSession<E extends EventType = EventType> = Session<never, never, never, PlatformKind, E>;
export interface EventMap {
[Context.MIDDLEWARE_EVENT]: Middleware;
'message'(session: RawSession<'message'>): void;
'message/normal'(session: RawSession<'message'>): void;
'message/notice'(session: RawSession<'message'>): void;
'message/anonymous'(session: RawSession<'message'>): void;
'message/friend'(session: RawSession<'message'>): void;
'message/group'(session: RawSession<'message'>): void;
'message/other'(session: RawSession<'message'>): void;
'friend-add'(session: RawSession<'notice'>): void;
'group-increase'(session: RawSession<'notice'>): void;
'group-increase/invite'(session: RawSession<'notice'>): void;
'group-increase/approve'(session: RawSession<'notice'>): void;
'group-decrease'(session: RawSession<'notice'>): void;
'group-decrease/leave'(session: RawSession<'notice'>): void;
'group-decrease/kick'(session: RawSession<'notice'>): void;
'group-decrease/kick-me'(session: RawSession<'notice'>): void;
'group-upload'(session: RawSession<'notice'>): void;
'group-admin'(session: RawSession<'notice'>): void;
'group-admin/set'(session: RawSession<'notice'>): void;
'group-admin/unset'(session: RawSession<'notice'>): void;
'group-ban'(session: RawSession<'notice'>): void;
'group-ban/ban'(session: RawSession<'notice'>): void;
'group-ban/lift-ban'(session: RawSession<'notice'>): void;
'group_recall'(session: RawSession<'notice'>): void;
'friend_recall'(session: RawSession<'notice'>): void;
'notify'(session: RawSession<'notice'>): void;
'notify/poke'(session: RawSession<'notice'>): void;
'notify/lucky_king'(session: RawSession<'notice'>): void;
'notify/honor'(session: RawSession<'notice'>): void;
'request/friend'(session: RawSession<'request'>): void;
'request/group/add'(session: RawSession<'request'>): void;
'request/group/invite'(session: RawSession<'request'>): void;
'heartbeat'(session: RawSession<'meta_event'>): void;
'lifecycle'(session: RawSession<'meta_event'>): void;
'lifecycle/enable'(session: RawSession<'meta_event'>): void;
'lifecycle/disable'(session: RawSession<'meta_event'>): void;
'lifecycle/connect'(session: RawSession<'meta_event'>): void;
'message-edited'(session: RawSession<'message'>): void;
'message-edited/friend'(session: RawSession<'message'>): void;
'message-edited/group'(session: RawSession<'message'>): void;
'message-deleted'(session: RawSession<'message'>): void;
'message-deleted/friend'(session: RawSession<'message'>): void;
'message-deleted/group'(session: RawSession<'message'>): void;
'friend-added'(session: Session): void;
'friend-deleted'(session: Session): void;
'group'(session: Session): void;
'group-added'(session: Session): void;
'group-deleted'(session: Session): void;
'group-member'(session: Session): void;
'group-member-added'(session: Session): void;
'group-member-deleted'(session: Session): void;
'group-upload'(session: Session): void;
'group-admin'(session: Session): void;
'group-admin/set'(session: Session): void;
'group-admin/unset'(session: Session): void;
'group-ban'(session: Session): void;
'group-ban/ban'(session: Session): void;
'group-ban/lift-ban'(session: Session): void;
'notify'(session: Session): void;
'notify/poke'(session: Session): void;
'notify/lucky_king'(session: Session): void;
'notify/honor'(session: Session): void;
'request/friend'(session: Session): void;
'request/group/add'(session: Session): void;
'request/group/invite'(session: Session): void;
'lifecycle/enable'(session: RawSession<'lifecycle'>): void;
'lifecycle/disable'(session: RawSession<'lifecycle'>): void;
'lifecycle/connect'(session: RawSession<'lifecycle'>): void;
'lifecycle/heartbeat'(session: RawSession<'lifecycle'>): void;
'parse'(message: string, session: Session, builtin: boolean, terminator: string): void | ExecuteArgv;

@@ -109,0 +105,0 @@ 'before-attach-user'(session: Session, fields: Set<User.Field>): void;

@@ -7,3 +7,3 @@ import * as utils from 'koishi-utils';

}
export interface User {
export interface User extends Record<PlatformKind, string> {
id: number;

@@ -23,11 +23,15 @@ flag: number;

export type Observed<K extends Field = Field> = utils.Observed<Pick<User, K>, Promise<void>>;
type Getter = (id: number, authority: number) => Partial<User>;
type Getter = (type: PlatformKind, id: string, authority: number) => Partial<User>;
export function extend(getter: Getter): void;
export function create(id: number, authority: number): User;
export function create(type: PlatformKind, id: string, authority: number): User;
export {};
}
export interface Platforms {
}
export declare type PlatformKind = keyof Platforms;
export interface Group {
id: number;
id: string;
type: PlatformKind;
flag: number;
assignee: number;
assignee: string;
}

@@ -37,3 +41,2 @@ export declare namespace Group {

ignore = 1,
noImage = 2,
silent = 4

@@ -44,18 +47,14 @@ }

export type Observed<K extends Field = Field> = utils.Observed<Pick<Group, K>, Promise<void>>;
type Getter = (id: number, assignee: number) => Partial<Group>;
type Getter = (type: PlatformKind, id: string, assignee: string) => Partial<Group>;
export function extend(getter: Getter): void;
export function create(id: number, assignee: number): Group;
export function create(type: PlatformKind, id: string, assignee: string): Group;
export {};
}
export interface Database {
getUser<K extends User.Field>(userId: number, fields?: readonly K[]): Promise<Pick<User, K | 'id'>>;
getUser<K extends User.Field>(userId: number, defaultAuthority?: number, fields?: readonly K[]): Promise<Pick<User, K | 'id'>>;
getUsers<K extends User.Field>(fields?: readonly K[]): Promise<Pick<User, K>[]>;
getUsers<K extends User.Field>(ids: readonly number[], fields?: readonly K[]): Promise<Pick<User, K>[]>;
setUser(userId: number, data: Partial<User>): Promise<void>;
getGroup<K extends Group.Field>(groupId: number, fields?: readonly K[]): Promise<Pick<Group, K | 'id'>>;
getGroup<K extends Group.Field>(groupId: number, selfId?: number, fields?: readonly K[]): Promise<Pick<Group, K | 'id'>>;
getAllGroups<K extends Group.Field>(assignees?: readonly number[]): Promise<Pick<Group, K>[]>;
getAllGroups<K extends Group.Field>(fields?: readonly K[], assignees?: readonly number[]): Promise<Pick<Group, K>[]>;
setGroup(groupId: number, data: Partial<Group>): Promise<void>;
getUser<K extends User.Field>(type: PlatformKind, id: string, fields?: readonly K[]): Promise<Pick<User, K>>;
getUsers<K extends User.Field>(type: PlatformKind, ids?: readonly string[], fields?: readonly K[]): Promise<Pick<User, K>[]>;
setUser(type: PlatformKind, id: string, data: Partial<User>): Promise<void>;
getGroup<K extends Group.Field>(type: PlatformKind, id: string, fields?: readonly K[]): Promise<Pick<Group, K | 'id' | 'type'>>;
getAllGroups<K extends Group.Field>(fields?: readonly K[], assignees?: readonly string[]): Promise<Pick<Group, K>[]>;
setGroup(type: PlatformKind, id: string, data: Partial<Group>): Promise<void>;
}

@@ -62,0 +61,0 @@ declare type DatabaseExtensionMethods<I> = {

@@ -1,24 +0,1710 @@

"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __markAsModule = (target) => __defProp(target, "__esModule", {value: true});
var __export = (target, all) => {
__markAsModule(target);
for (var name in all)
__defProp(target, name, {get: all[name], enumerable: true});
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./app"), exports);
__exportStar(require("./command"), exports);
__exportStar(require("./context"), exports);
__exportStar(require("./database"), exports);
__exportStar(require("./session"), exports);
__exportStar(require("./server"), exports);
__exportStar(require("./plugins/help"), exports);
__exportStar(require("./plugins/message"), exports);
__exportStar(require("./plugins/shortcut"), exports);
__exportStar(require("./plugins/suggest"), exports);
__exportStar(require("./plugins/validate"), exports);
//# sourceMappingURL=index.js.map
var __exportStar = (target, module2, desc) => {
__markAsModule(target);
if (module2 && typeof module2 === "object" || typeof module2 === "function") {
for (let key of __getOwnPropNames(module2))
if (!__hasOwnProp.call(target, key) && key !== "default")
__defProp(target, key, {get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable});
}
return target;
};
var __toModule = (module2) => {
if (module2 && module2.__esModule)
return module2;
return __exportStar(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", {value: module2, enumerable: true}), module2);
};
// packages/koishi-core/src/index.ts
__export(exports, {
App: () => App2,
AppStatus: () => AppStatus,
Bot: () => Bot,
BotStatusCode: () => BotStatusCode,
Command: () => Command,
Context: () => Context,
Group: () => Group,
Message: () => Message,
Server: () => Server,
Session: () => Session,
User: () => User,
checkTimer: () => checkTimer,
checkUsage: () => checkUsage,
extendDatabase: () => extendDatabase,
getCommands: () => getCommands,
getSessionId: () => getSessionId,
getTargetId: () => getTargetId,
getUsage: () => getUsage,
getUsageName: () => getUsageName,
parseArguments: () => parseArguments
});
// packages/koishi-core/src/app.ts
const koishi_utils7 = __toModule(require("koishi-utils"));
// packages/koishi-core/src/context.ts
const koishi_utils2 = __toModule(require("koishi-utils"));
// packages/koishi-core/src/command.ts
const koishi_utils = __toModule(require("koishi-utils"));
const util = __toModule(require("util"));
const logger = new koishi_utils.Logger("command");
const ANGLED_BRACKET_REGEXP = /<([^>]+)>/g;
const SQUARE_BRACKET_REGEXP = /\[([^\]]+)\]/g;
function parseBracket(name, required) {
let variadic = false, greedy = false;
if (name.startsWith("...")) {
name = name.slice(3);
variadic = true;
} else if (name.endsWith("...")) {
name = name.slice(0, -3);
greedy = true;
}
return {
name,
required,
variadic,
greedy
};
}
function parseArguments(source) {
let capture;
const result = [];
while (capture = ANGLED_BRACKET_REGEXP.exec(source)) {
result.push(parseBracket(capture[1], true));
}
while (capture = SQUARE_BRACKET_REGEXP.exec(source)) {
result.push(parseBracket(capture[1], false));
}
return result;
}
const supportedType = ["string", "number", "boolean"];
const quoteStart = `"'“‘`;
const quoteEnd = `"'”’`;
class Command {
constructor(name, declaration, context2, config = {}) {
this.name = name;
this.declaration = declaration;
this.context = context2;
this.children = [];
this.parent = null;
this._aliases = [];
this._options = {};
this._optionNameMap = {};
this._optionSymbolMap = {};
this._userFields = [];
this._groupFields = [];
if (!name)
throw new Error("expect a command name");
this._arguments = parseArguments(declaration);
this.config = {...Command.defaultConfig, ...config};
this._registerAlias(this.name);
context2.app._commands.push(this);
context2.app.emit("new-command", this);
}
static userFields(fields) {
this._userFields.push(fields);
return this;
}
static groupFields(fields) {
this._groupFields.push(fields);
return this;
}
static collect(argv, key, fields = new Set()) {
if (!argv)
return;
const values = [
...this[`_${key}Fields`],
...argv.command[`_${key}Fields`]
];
for (const value of values) {
if (typeof value === "function") {
value(argv, fields);
continue;
}
for (const field of value) {
fields.add(field);
}
}
return fields;
}
get app() {
return this.context.app;
}
_registerAlias(name) {
name = name.toLowerCase();
this._aliases.push(name);
const previous = this.app._commandMap[name];
if (!previous) {
this.app._commandMap[name] = this;
} else if (previous !== this) {
throw new Error(util.format('duplicate command names: "%s"', name));
}
}
[util.inspect.custom]() {
return `Command <${this.name}>`;
}
userFields(fields) {
this._userFields.push(fields);
return this;
}
groupFields(fields) {
this._groupFields.push(fields);
return this;
}
alias(...names) {
for (const name of names) {
this._registerAlias(name);
}
return this;
}
subcommand(rawName, ...args) {
rawName = this.name + (rawName.charCodeAt(0) === 46 ? "" : "/") + rawName;
return this.context.command(rawName, ...args);
}
_registerOption(name, def, config) {
const param = koishi_utils.paramCase(name);
const decl = def.replace(/(?<=^|\s)[\w\x80-\uffff].*/, "");
const desc = def.slice(decl.length);
let syntax = decl.replace(/(?<=^|\s)(<[^<]+>|\[[^[]+\]).*/, "");
const bracket = decl.slice(syntax.length);
syntax = syntax.trim() || "--" + param;
const names = [];
const symbols = [];
for (let param2 of syntax.trim().split(",")) {
param2 = param2.trimStart();
const name2 = param2.replace(/^-+/, "");
if (!name2 || !param2.startsWith("-")) {
symbols.push(param2);
} else {
names.push(name2);
}
}
if (!config.value && !names.includes(param)) {
syntax += ", --" + param;
}
const option = this._options[name] || (this._options[name] = {
...Command.defaultOptionConfig,
...config,
name,
values: {},
description: syntax + " " + bracket + desc,
greedy: bracket.includes("...")
});
if ("value" in config) {
names.forEach((name2) => option.values[name2] = config.value);
} else if (!bracket.trim()) {
option.type = "boolean";
}
this._assignOption(option, names, this._optionNameMap);
this._assignOption(option, symbols, this._optionSymbolMap);
if (!this._optionNameMap[param]) {
this._optionNameMap[param] = option;
}
return this;
}
_assignOption(option, names, optionMap) {
for (const name of names) {
if (name in optionMap) {
throw new Error(util.format('duplicate option names: "%s"', name));
}
optionMap[name] = option;
}
}
option(name, desc, config = {}) {
const fallbackType = typeof config.fallback;
const type = config["type"] || supportedType.includes(fallbackType) && fallbackType;
return this._registerOption(name, desc, {...config, type});
}
removeOption(name) {
if (!this._options[name])
return false;
const option = this._options[name];
delete this._options[name];
for (const key in this._optionNameMap) {
if (this._optionNameMap[key] === option) {
delete this._optionNameMap[key];
}
}
for (const key in this._optionSymbolMap) {
if (this._optionSymbolMap[key] === option) {
delete this._optionSymbolMap[key];
}
}
return true;
}
action(callback) {
this._action = callback;
return this;
}
parseArg(source, terminator) {
const index = quoteStart.indexOf(source[0]);
if (index >= 0) {
const capture = new RegExp(`${quoteEnd[index]}(?=[\\s${terminator}]|$)`).exec(source.slice(1));
if (capture) {
return {
quoted: true,
content: source.slice(1, 1 + capture.index),
rest: source.slice(2 + capture.index).trimLeft()
};
}
}
const [content] = source.split(new RegExp(`[\\s${terminator}]`), 1);
return {
content,
quoted: false,
rest: source.slice(content.length).trimLeft()
};
}
parseRest(source, terminator) {
const index = quoteStart.indexOf(source[0]);
if (index >= 0) {
const capture = terminator ? new RegExp(`${quoteEnd[index]}(?=[${terminator}]|$)`).exec(source.slice(1)) : new RegExp(`${quoteEnd[index]}(?=$)`).exec(source.slice(1));
if (capture) {
return {
quoted: true,
content: source.slice(1, 1 + capture.index),
rest: source.slice(2 + capture.index).trimLeft()
};
}
}
const [content] = terminator ? source.split(new RegExp(`[${terminator}]`), 1) : [source];
return {
content,
quoted: false,
rest: source.slice(content.length).trimLeft()
};
}
parseValue(source, quoted, {type, fallback} = {}) {
if (source === "" && quoted)
return "";
if (source === true || source === "") {
if (fallback !== void 0)
return fallback;
if (type === "string")
return "";
return true;
}
if (type === "number")
return +source;
if (type === "string")
return source;
const n = +source;
return n * 0 === 0 ? n : source;
}
parse(message4, terminator = "") {
var _a;
let rest = "";
terminator = koishi_utils.escapeRegExp(terminator);
const source = `${this.name} ${message4}`;
const args = [];
const options = {};
const handleOption = (name, value) => {
const config = this._optionNameMap[name];
if (config) {
options[config.name] = name in config.values ? config.values[name] : value;
} else {
options[koishi_utils.camelCase(name)] = value;
}
};
while (message4) {
if (terminator.includes(message4[0])) {
rest = message4;
break;
}
if (message4[0] !== "-" && ((_a = this._arguments[args.length]) == null ? void 0 : _a.greedy)) {
const arg02 = this.parseRest(message4, terminator);
args.push(arg02.content);
rest = arg02.rest;
break;
}
let arg0 = this.parseArg(message4, terminator);
const arg = arg0.content;
message4 = arg0.rest;
let option;
let names;
let param;
if (!arg0.quoted && (option = this._optionSymbolMap[arg])) {
names = [koishi_utils.paramCase(option.name)];
} else {
if (arg[0] !== "-" || arg0.quoted) {
args.push(arg);
continue;
}
let i = 0;
let name;
for (; i < arg.length; ++i) {
if (arg.charCodeAt(i) !== 45)
break;
}
if (arg.slice(i, i + 3) === "no-" && !this._optionNameMap[arg.slice(i)]) {
name = arg.slice(i + 3);
handleOption(name, false);
continue;
}
let j = i + 1;
for (; j < arg.length; j++) {
if (arg.charCodeAt(j) === 61)
break;
}
name = arg.slice(i, j);
names = i > 1 ? [name] : name;
param = arg.slice(++j);
option = this._optionNameMap[names[names.length - 1]];
}
let quoted = false;
if (!param) {
const {greedy, type} = option || {};
if (greedy) {
arg0 = this.parseRest(arg0.rest, terminator);
param = arg0.content;
quoted = arg0.quoted;
rest = arg0.rest;
message4 = "";
} else if (type !== "boolean" && (type || message4[0] !== "-")) {
arg0 = this.parseArg(message4, terminator);
param = arg0.content;
quoted = arg0.quoted;
message4 = arg0.rest;
}
}
for (let j = 0; j < names.length; j++) {
const name = names[j];
const config = this._optionNameMap[name];
const value = this.parseValue(j + 1 < names.length || param, quoted, config);
handleOption(name, value);
}
}
for (const {name, fallback} of Object.values(this._options)) {
if (fallback !== void 0 && !(name in options)) {
options[name] = fallback;
}
}
return {rest, options, args, source};
}
stringifyArg(value) {
value = "" + value;
return value.includes(" ") ? `"${value}"` : value;
}
stringify(args, options) {
let output = this.name;
for (const key in options) {
const value = options[key];
if (value === true) {
output += ` --${key}`;
} else if (value === false) {
output += ` --no-${key}`;
} else {
output += ` --${key} ${this.stringifyArg(value)}`;
}
}
for (const arg of args) {
output += " " + this.stringifyArg(arg);
}
return output;
}
async execute(argv) {
argv.command = this;
if (!argv.options)
argv.options = {};
if (!argv.args)
argv.args = [];
if (!argv.rest)
argv.rest = "";
let state = "before command";
const {next = koishi_utils.noop} = argv;
argv.next = async (fallback) => {
const oldState = state;
state = "";
await next(fallback);
state = oldState;
};
let {args, options, session: session3, source} = argv;
const getSource = () => source || (source = this.stringify(args, options));
if (logger.level >= 3)
logger.debug(getSource());
const lastCall = this.app.options.prettyErrors && new Error().stack.split("\n", 4)[3];
try {
const result = await this.app.serial(session3, "before-command", argv);
if (typeof result === "string")
return session3.$send(result);
state = "executing command";
const message4 = await this._action(argv, ...args);
if (message4)
await session3.$send(message4);
state = "after command";
await this.app.serial(session3, "command", argv);
} catch (error) {
if (!state)
throw error;
let stack = koishi_utils.coerce(error);
if (lastCall) {
const index = error.stack.indexOf(lastCall);
stack = stack.slice(0, index - 1);
}
logger.warn(`${state}: ${getSource()}
${stack}`);
}
}
dispose() {
for (const cmd of this.children) {
cmd.dispose();
}
this.context.emit("remove-command", this);
this._aliases.forEach((name) => delete this.app._commandMap[name]);
const index = this.app._commands.indexOf(this);
this.app._commands.splice(index, 1);
if (this.parent) {
const index2 = this.parent.children.indexOf(this);
this.parent.children.splice(index2, 1);
}
}
}
Command.defaultConfig = {};
Command.defaultOptionConfig = {};
Command._userFields = [];
Command._groupFields = [];
// packages/koishi-core/src/database.ts
var User;
(function(User3) {
let Flag;
(function(Flag2) {
Flag2[Flag2["ignore"] = 1] = "ignore";
})(Flag = User3.Flag || (User3.Flag = {}));
User3.fields = [];
const getters = [];
function extend(getter) {
getters.push(getter);
User3.fields.push(...Object.keys(getter(null, "0", 0)));
}
User3.extend = extend;
extend((type, id, authority) => ({
[type]: id,
authority,
flag: 0,
usage: {},
timers: {}
}));
function create(type, id, authority) {
const result = {};
for (const getter of getters) {
Object.assign(result, getter(type, id, authority));
}
return result;
}
User3.create = create;
})(User || (User = {}));
var Group;
(function(Group2) {
let Flag;
(function(Flag2) {
Flag2[Flag2["ignore"] = 1] = "ignore";
Flag2[Flag2["silent"] = 4] = "silent";
})(Flag = Group2.Flag || (Group2.Flag = {}));
Group2.fields = [];
const getters = [];
function extend(getter) {
getters.push(getter);
Group2.fields.push(...Object.keys(getter(null, "0", "0")));
}
Group2.extend = extend;
function create(type, id, assignee) {
const result = {};
for (const getter of getters) {
Object.assign(result, getter(type, id, assignee));
}
return result;
}
Group2.create = create;
extend((id, assignee) => ({
id,
assignee,
flag: 0
}));
})(Group || (Group = {}));
function extendDatabase(module2, extension) {
let Database2;
try {
Database2 = typeof module2 === "string" ? require(module2).default : module2;
} catch (error) {
}
if (typeof extension === "function") {
extension(Database2);
} else {
Object.assign(Database2.prototype, extension);
}
}
// packages/koishi-core/src/context.ts
function joinScope(base, ids) {
const result = !ids.length ? [...base] : base.positive ? koishi_utils2.intersection(ids, base) : koishi_utils2.difference(ids, base);
result.positive = !ids.length ? base.positive : true;
return result;
}
function matchScope(base, id) {
return !id || !(base.positive ^ base.includes(id));
}
function isBailed(value) {
return value !== null && value !== false && value !== void 0;
}
class Context {
constructor(scope, app2) {
this.scope = scope;
this.app = app2;
koishi_utils2.defineProperty(this, "_disposables", []);
}
get database() {
return this.app._database;
}
set database(database4) {
if (this.app._database && this.app._database !== database4) {
this.logger("app").warn("ctx.database is overwritten");
}
this.app._database = database4;
}
logger(name) {
return new koishi_utils2.Logger(name);
}
group(...ids) {
const scope = {...this.scope};
scope.groups = joinScope(scope.groups, ids);
scope.private = false;
return new Context(scope, this.app);
}
user(...ids) {
const scope = {...this.scope};
scope.users = joinScope(scope.users, ids);
return new Context(scope, this.app);
}
private(...ids) {
const scope = {...this.scope};
scope.users = joinScope(scope.users, ids);
scope.groups = [];
scope.groups.positive = true;
return new Context(scope, this.app);
}
match(session3) {
if (!session3)
return true;
return matchScope(this.scope.groups, session3.groupId) && matchScope(this.scope.users, session3.userId) && (this.scope.private || session3.subType !== "private");
}
plugin(plugin, options) {
if (options === false)
return;
if (options === true)
options = void 0;
const ctx = Object.create(this);
koishi_utils2.defineProperty(ctx, "_disposables", []);
if (typeof plugin === "function") {
plugin(ctx, options);
} else if (plugin && typeof plugin === "object" && typeof plugin.apply === "function") {
plugin.apply(ctx, options);
} else {
throw new Error('invalid plugin, expect function or object with an "apply" method');
}
this._disposables.push(() => ctx.dispose());
return this;
}
async parallel(...args) {
const tasks = [];
const session3 = typeof args[0] === "object" ? args.shift() : null;
const name = args.shift();
this.logger("dispatch").debug(name);
for (const [context2, callback] of this.app._hooks[name] || []) {
if (!context2.match(session3))
continue;
tasks.push(callback.apply(this, args));
}
await Promise.all(tasks);
}
emit(...args) {
this.parallel(...args);
}
async serial(...args) {
const session3 = typeof args[0] === "object" ? args.shift() : null;
const name = args.shift();
this.logger("dispatch").debug(name);
for (const [context2, callback] of this.app._hooks[name] || []) {
if (!context2.match(session3))
continue;
const result = await callback.apply(this, args);
if (isBailed(result))
return result;
}
}
bail(...args) {
const session3 = typeof args[0] === "object" ? args.shift() : null;
const name = args.shift();
this.logger("dispatch").debug(name);
for (const [context2, callback] of this.app._hooks[name] || []) {
if (!context2.match(session3))
continue;
const result = callback.apply(this, args);
if (isBailed(result))
return result;
}
}
getHooks(name) {
const hooks = this.app._hooks[name] || (this.app._hooks[name] = []);
if (hooks.length >= this.app.options.maxListeners) {
this.logger("app").warn('max listener count (%d) for event "%s" exceeded, which may be caused by a memory leak', this.app.options.maxListeners, name);
}
return hooks;
}
on(name, listener) {
return this.addListener(name, listener);
}
addListener(name, listener) {
this.getHooks(name).push([this, listener]);
const dispose = () => this.removeListener(name, listener);
this._disposables.push(name === "dispose" ? listener : dispose);
return dispose;
}
prependListener(name, listener) {
this.getHooks(name).unshift([this, listener]);
const dispose = () => this.removeListener(name, listener);
this._disposables.push(name === "dispose" ? listener : dispose);
return dispose;
}
once(name, listener) {
const dispose = this.addListener(name, (...args) => {
dispose();
return listener.apply(this, args);
});
return dispose;
}
off(name, listener) {
return this.removeListener(name, listener);
}
removeListener(name, listener) {
const index = (this.app._hooks[name] || []).findIndex(([context2, callback]) => context2 === this && callback === listener);
if (index >= 0) {
this.app._hooks[name].splice(index, 1);
return true;
}
}
middleware(middleware) {
return this.addListener(Context.MIDDLEWARE_EVENT, middleware);
}
addMiddleware(middleware) {
return this.addListener(Context.MIDDLEWARE_EVENT, middleware);
}
prependMiddleware(middleware) {
return this.prependListener(Context.MIDDLEWARE_EVENT, middleware);
}
removeMiddleware(middleware) {
return this.removeListener(Context.MIDDLEWARE_EVENT, middleware);
}
command(rawName, ...args) {
const description = typeof args[0] === "string" ? args.shift() : void 0;
const config = args[0] || {};
if (description !== void 0)
config.description = description;
const [path] = rawName.split(" ", 1);
const declaration = rawName.slice(path.length);
const segments = path.toLowerCase().split(/(?=[\\./])/);
let parent = null;
segments.forEach((segment) => {
const code = segment.charCodeAt(0);
const name = code === 46 ? parent.name + segment : code === 47 ? segment.slice(1) : segment;
let command6 = this.app._commandMap[name];
if (command6) {
if (parent) {
if (command6 === parent) {
throw new Error("cannot set a command as its own subcommand");
}
if (command6.parent) {
if (command6.parent !== parent) {
throw new Error("already has subcommand");
}
} else {
command6.parent = parent;
parent.children.push(command6);
}
}
return parent = command6;
}
command6 = new Command(name, declaration, this);
if (parent) {
command6.parent = parent;
parent.children.push(command6);
}
parent = command6;
});
Object.assign(parent.config, config);
this._disposables.push(() => parent.dispose());
return parent;
}
async broadcast(...args) {
let groups;
if (Array.isArray(args[0]))
groups = args.shift();
const [message4, forced] = args;
if (!message4)
return [];
const data = await this.database.getAllGroups(["id", "type", "assignee", "flag"]);
const assignMap = {};
for (const {id, assignee, flag} of data) {
if (groups && !groups.includes(id))
continue;
if (!forced && flag & Group.Flag.silent)
continue;
if (assignMap[assignee]) {
assignMap[assignee].push(id);
} else {
assignMap[assignee] = [id];
}
}
return (await Promise.all(Object.entries(assignMap).map(async ([id, groups2]) => {
return await this.app.bots[+id].broadcast(groups2, message4);
}))).flat(1);
}
dispose() {
this._disposables.forEach((dispose) => dispose());
this._disposables = [];
}
}
Context.MIDDLEWARE_EVENT = Symbol("mid");
// packages/koishi-core/src/server.ts
const koishi_utils4 = __toModule(require("koishi-utils"));
// packages/koishi-core/src/session.ts
const koishi_utils3 = __toModule(require("koishi-utils"));
const lru_cache = __toModule(require("lru-cache"));
const logger2 = new koishi_utils3.Logger("session");
class Session {
constructor(app2, session3) {
this.$prefix = null;
this.$uuid = koishi_utils3.Random.uuid();
koishi_utils3.defineProperty(this, "$app", app2);
koishi_utils3.defineProperty(this, "_queued", Promise.resolve());
koishi_utils3.defineProperty(this, "_hooks", []);
Object.assign(this, session3);
}
toJSON() {
return Object.fromEntries(Object.entries(this).filter(([key]) => {
return !key.startsWith("_") && !key.startsWith("$");
}));
}
get $bot() {
return this.$app.bots[this.selfId];
}
get $username() {
const idString = "" + this.userId;
return this.$user && this.$user["name"] && idString !== this.$user["name"] ? this.$user["name"] : this.anonymous ? this.anonymous.name : this.sender ? this.sender.card || this.sender.nickname : idString;
}
async $send(message4) {
if (this.$bot[Bot.$send]) {
return this.$bot[Bot.$send](this, message4);
}
if (!message4)
return;
await this.$bot.sendMessage(this.channelId, message4);
}
$cancelQueued(delay = 0) {
this._hooks.forEach(Reflect.apply);
this._delay = delay;
}
async $sendQueued(message4, delay) {
if (!message4)
return;
if (typeof delay === "undefined") {
const {queueDelay = 100} = this.$app.options;
delay = typeof queueDelay === "function" ? queueDelay(message4, this) : queueDelay;
}
return this._queued = this._queued.then(() => new Promise((resolve) => {
const hook = () => {
resolve();
clearTimeout(timer);
const index = this._hooks.indexOf(hook);
if (index >= 0)
this._hooks.splice(index, 1);
};
this._hooks.push(hook);
const timer = setTimeout(async () => {
await this.$send(message4);
this._delay = delay;
hook();
}, this._delay || 0);
}));
}
async $getGroup(id = this.groupId, fields = [], assignee) {
const group = await this.$app.database.getGroup(this.kind, id, fields);
if (group)
return group;
const fallback = Group.create(this.kind, id, assignee);
if (assignee) {
await this.$app.database.setGroup(this.kind, id, fallback);
}
return fallback;
}
async $observeGroup(fields = []) {
const fieldSet = new Set(fields);
const {kind, groupId, $argv, $group} = this;
if ($argv)
Command.collect($argv, "group", fieldSet);
const identifier = `${kind}:${groupId}`;
if ($group) {
for (const key in $group) {
fieldSet.delete(key);
}
if (fieldSet.size) {
const data2 = await this.$getGroup(groupId, [...fieldSet]);
this.$app._groupCache.set(identifier, $group._merge(data2));
}
return $group;
}
const cache = this.$app._groupCache.get(identifier);
const fieldArray = [...fieldSet];
const hasActiveCache = cache && koishi_utils3.contain(Object.keys(cache), fieldArray);
if (hasActiveCache)
return this.$group = cache;
const data = await this.$getGroup(groupId, fieldArray);
const group = koishi_utils3.observe(data, (diff) => this.$app.database.setGroup(kind, groupId, diff), `group ${groupId}`);
this.$app._groupCache.set(identifier, group);
return this.$group = group;
}
async $getUser(id = this.userId, fields = [], authority = 0) {
const user = await this.$app.database.getUser(this.kind, id, fields);
if (user)
return user;
const fallback = User.create(this.kind, id, authority);
if (authority) {
await this.$app.database.setUser(this.kind, id, fallback);
}
return fallback;
}
async $observeUser(fields = []) {
const fieldSet = new Set(fields);
const {userId, $argv, $user} = this;
if ($argv)
Command.collect($argv, "user", fieldSet);
let userCache = this.$app._userCache[this.kind];
if (!userCache) {
userCache = this.$app._userCache[this.kind] = new lru_cache.default({
max: this.$app.options.userCacheLength,
maxAge: this.$app.options.userCacheAge
});
}
if ($user && !this.anonymous) {
for (const key in $user) {
fieldSet.delete(key);
}
if (fieldSet.size) {
const data2 = await this.$getUser(userId, [...fieldSet]);
userCache.set(userId, $user._merge(data2));
}
}
if ($user)
return $user;
const defaultAuthority = typeof this.$app.options.defaultAuthority === "function" ? this.$app.options.defaultAuthority(this) : this.$app.options.defaultAuthority || 0;
if (this.anonymous) {
const user2 = koishi_utils3.observe(User.create(this.kind, userId, defaultAuthority), () => Promise.resolve());
return this.$user = user2;
}
const cache = userCache.get(userId);
const fieldArray = [...fieldSet];
const hasActiveCache = cache && koishi_utils3.contain(Object.keys(cache), fieldArray);
if (hasActiveCache)
return this.$user = cache;
const data = await this.$getUser(userId, fieldArray, defaultAuthority);
const user = koishi_utils3.observe(data, (diff) => this.$app.database.setUser(this.kind, userId, diff), `user ${userId}`);
userCache.set(userId, user);
return this.$user = user;
}
$resolve(argv) {
if (typeof argv.command === "string") {
argv.command = this.$app._commandMap[argv.command];
}
if (!argv.command) {
logger2.warn(new Error(`cannot find command ${argv}`));
return;
}
if (!argv.command.context.match(this))
return;
return {session: this, ...argv};
}
$parse(message4, terminator = "", builtin = false) {
if (!message4)
return;
const argv = this.$app.bail(this, "parse", message4, this, builtin, terminator);
return argv && this.$resolve(argv);
}
async $execute(...args) {
let argv, next;
if (typeof args[0] === "string") {
next = args[1] || koishi_utils3.noop;
argv = this.$parse(args[0]);
} else {
next = args[0].next || koishi_utils3.noop;
argv = this.$resolve(args[0]);
}
if (!argv)
return next();
argv.next = next;
argv.session = this;
this.$argv = argv;
if (this.$app.database) {
if (this.subType === "group") {
await this.$observeGroup();
}
await this.$observeUser();
}
return argv.command.execute(argv);
}
}
function getTargetId(target) {
if (typeof target !== "string" && typeof target !== "number")
return;
let qq = +target;
if (!qq) {
const capture = /\[CQ:at,qq=(\d+)\]/.exec(target);
if (capture)
qq = +capture[1];
}
if (!koishi_utils3.isInteger(qq))
return;
return qq;
}
// packages/koishi-core/src/server.ts
class Server {
constructor(app2, BotStatic) {
this.app = app2;
this.BotStatic = BotStatic;
this.bots = [];
}
create(options) {
const bot = new this.BotStatic(this.app, options);
this.bots.push(bot);
this.app.bots[bot.selfId] = bot;
}
dispatch(session3) {
if (this.app.status !== AppStatus.open)
return;
const events = [];
events.push(session3.eventType);
if (session3.subType) {
events.unshift(events[0] + "/" + session3.subType);
}
for (const event of events) {
this.app.emit(session3, koishi_utils4.paramCase(event), session3);
}
}
}
Server.types = {};
var BotStatusCode;
(function(BotStatusCode2) {
BotStatusCode2[BotStatusCode2["GOOD"] = 0] = "GOOD";
BotStatusCode2[BotStatusCode2["BOT_IDLE"] = 1] = "BOT_IDLE";
BotStatusCode2[BotStatusCode2["BOT_OFFLINE"] = 2] = "BOT_OFFLINE";
BotStatusCode2[BotStatusCode2["NET_ERROR"] = 3] = "NET_ERROR";
BotStatusCode2[BotStatusCode2["SERVER_ERROR"] = 4] = "SERVER_ERROR";
})(BotStatusCode || (BotStatusCode = {}));
class Bot {
constructor(app2, options) {
this.app = app2;
Object.assign(this, options);
}
createSession(subType, ctxType, ctxId, message4) {
return new Session(this.app, {
message: message4,
subType,
eventType: "send",
selfId: this.selfId,
[ctxType + "Id"]: ctxId,
time: Math.round(Date.now() / 1e3)
});
}
async getGroupMemberMap(groupId) {
const list = await this.getGroupMemberList(groupId);
return Object.fromEntries(list.map((info) => [info.id, info.nick || info.name]));
}
async broadcast(channels, message4, delay = this.app.options.broadcastDelay) {
const messageIds = [];
for (let index = 0; index < channels.length; index++) {
if (index && delay)
await koishi_utils4.sleep(delay);
try {
messageIds.push(await this.sendMessage(channels[index], message4));
} catch (error) {
this.app.logger("bot").warn(error);
}
}
return messageIds;
}
}
Bot.$send = Symbol.for("koishi.send");
// packages/koishi-core/src/plugins/validate.ts
const util2 = __toModule(require("util"));
const koishi_utils5 = __toModule(require("koishi-utils"));
// packages/koishi-core/src/plugins/message.ts
var Message;
(function(Message2) {
Message2.LOW_AUTHORITY = "权限不足。";
Message2.TOO_FREQUENT = "调用过于频繁,请稍后再试。";
Message2.INSUFFICIENT_ARGUMENTS = "缺少参数,请检查指令语法。";
Message2.REDUNANT_ARGUMENTS = "存在多余参数,请检查指令语法。";
Message2.INVALID_OPTION = "选项 %s 输入无效,%s";
Message2.UNKNOWN_OPTIONS = "存在未知选项 %s,请检查指令语法。";
Message2.CHECK_SYNTAX = "请检查指令语法。";
Message2.SHOW_THIS_MESSAGE = "显示本信息";
Message2.USAGE_EXHAUSTED = "调用次数已达上限。";
Message2.SUGGESTION = "你要找的是不是%s?";
Message2.COMMAND_SUGGEST_PREFIX = "";
Message2.COMMAND_SUGGEST_SUFFIX = "发送空行或句号以调用推测的指令。";
Message2.HELP_SUGGEST_PREFIX = "指令未找到。";
Message2.HELP_SUGGEST_SUFFIX = "发送空行或句号以调用推测的指令。";
Message2.GLOBAL_HELP_EPILOG = [
"群聊普通指令可以通过“@我+指令名”的方式进行触发。",
"私聊或全局指令则不需要添加上述前缀,直接输入指令名即可触发。",
"输入“帮助+指令名”查看特定指令的语法和使用示例。"
].join("\n");
})(Message || (Message = {}));
// packages/koishi-core/src/plugins/validate.ts
function getUsageName(command6) {
return command6.config.usageName || command6.name;
}
Object.assign(Command.defaultConfig, {
authority: 1,
showWarning: true,
maxUsage: Infinity,
minInterval: 0
});
Object.assign(Command.defaultOptionConfig, {
authority: 0
});
Command.userFields(({command: command6, options = {}}, fields) => {
const {maxUsage, minInterval, authority} = command6.config;
let shouldFetchAuthority = !fields.has("authority") && authority > 0;
let shouldFetchUsage = !!(maxUsage || minInterval);
for (const {name, authority: authority2, notUsage} of Object.values(command6._options)) {
if (name in options) {
if (authority2 > 0)
shouldFetchAuthority = true;
if (notUsage)
shouldFetchUsage = false;
}
}
if (shouldFetchAuthority)
fields.add("authority");
if (shouldFetchUsage) {
if (maxUsage)
fields.add("usage");
if (minInterval)
fields.add("timers");
}
});
Command.prototype.getConfig = function(key, session3) {
const value = this.config[key];
return typeof value === "function" ? value(session3.$user) : value;
};
Command.prototype.before = function(checker) {
this._checkers.push(checker);
return this;
};
function apply(app2) {
app2.on("new-command", (cmd) => {
cmd._checkers = [];
});
app2.on("before-command", ({session: session3, args, options, command: command6}) => {
function sendHint(message4, ...param) {
return command6.config.showWarning ? util2.format(message4, ...param) : "";
}
for (const checker of command6._checkers) {
const result = checker(session3);
if (result)
return sendHint(result === true ? "" : result);
}
if (command6.config.checkArgCount) {
const nextArg = command6._arguments[args.length] || {};
if (nextArg.required) {
return sendHint(Message.INSUFFICIENT_ARGUMENTS);
}
const finalArg = command6._arguments[command6._arguments.length - 1] || {};
if (args.length > command6._arguments.length && !finalArg.greedy && !finalArg.variadic) {
return sendHint(Message.REDUNANT_ARGUMENTS);
}
}
if (command6.config.checkUnknown) {
const unknown = Object.keys(options).filter((key) => !command6._options[key]);
if (unknown.length) {
return sendHint(Message.UNKNOWN_OPTIONS, unknown.join(", "));
}
}
for (const {validate: validate3, name} of Object.values(command6._options)) {
if (!validate3 || !(name in options))
continue;
const result = typeof validate3 !== "function" ? !validate3.test(options[name]) : validate3(options[name]);
if (result) {
return sendHint(Message.INVALID_OPTION, name, result === true ? Message.CHECK_SYNTAX : result);
}
}
if (!session3.$user)
return;
let isUsage = true;
if (command6.config.authority > session3.$user.authority) {
return sendHint(Message.LOW_AUTHORITY);
}
for (const option of Object.values(command6._options)) {
if (option.name in options) {
if (option.authority > session3.$user.authority) {
return sendHint(Message.LOW_AUTHORITY);
}
if (option.notUsage)
isUsage = false;
}
}
if (isUsage) {
const name = getUsageName(command6);
const minInterval = command6.getConfig("minInterval", session3);
const maxUsage = command6.getConfig("maxUsage", session3);
if (maxUsage < Infinity && checkUsage(name, session3.$user, maxUsage)) {
return sendHint(Message.USAGE_EXHAUSTED);
}
if (minInterval > 0 && checkTimer(name, session3.$user, minInterval)) {
return sendHint(Message.TOO_FREQUENT);
}
}
});
}
function getUsage(name, user) {
const $date = koishi_utils5.Time.getDateNumber();
if (user.usage.$date !== $date) {
user.usage = {$date};
}
return user.usage[name] || 0;
}
function checkUsage(name, user, maxUsage) {
const count = getUsage(name, user);
if (count >= maxUsage)
return true;
if (maxUsage) {
user.usage[name] = count + 1;
}
}
function checkTimer(name, {timers}, offset) {
const now = Date.now();
if (!(now <= timers.$date)) {
for (const key in timers) {
if (now > timers[key])
delete timers[key];
}
timers.$date = now + koishi_utils5.Time.day;
}
if (now <= timers[name])
return true;
if (offset !== void 0) {
timers[name] = now + offset;
}
}
// packages/koishi-core/src/plugins/help.ts
Command.prototype.usage = function(text) {
this._usage = text;
return this;
};
Command.prototype.example = function(example) {
this._examples.push(example);
return this;
};
function apply2(app2) {
app2.on("new-command", (cmd) => {
cmd._examples = [];
cmd.option("help", "-h 显示此信息", {hidden: true});
});
app2.prependListener("before-command", async ({command: command6, session: session3, options}) => {
if (command6._action && !options["help"])
return;
await session3.$execute({
command: "help",
args: [command6.name]
});
return "";
});
const createCollector = (key) => (argv, fields) => {
const {args: [name]} = argv;
const command6 = app2._commandMap[name] || app2._shortcutMap[name];
if (!command6)
return;
Command.collect({...argv, command: command6, args: [], options: {help: true}}, key, fields);
};
app2.command("help [command]", "显示帮助信息", {authority: 0}).userFields(["authority"]).userFields(createCollector("user")).groupFields(createCollector("group")).shortcut("帮助", {fuzzy: true}).option("authority", "-a 显示权限设置").option("showHidden", "-H 查看隐藏的选项和指令").action(async ({session: session3, options}, target) => {
if (!target) {
const commands = session3.$app._commands.filter((cmd) => cmd.parent === null);
const output = formatCommands("当前可用的指令有", session3, commands, options);
if (Message.GLOBAL_HELP_EPILOG)
output.push(Message.GLOBAL_HELP_EPILOG);
return output.join("\n");
}
const command6 = app2._commandMap[target] || app2._shortcutMap[target];
if (!(command6 == null ? void 0 : command6.context.match(session3))) {
const items = getCommands(session3, app2._commands).flatMap((cmd) => cmd._aliases);
return session3.$suggest({
target,
items,
prefix: Message.HELP_SUGGEST_PREFIX,
suffix: Message.HELP_SUGGEST_SUFFIX,
async apply(suggestion) {
await this.$observeUser(["authority", "usage", "timers"]);
const output = await showHelp(app2._commandMap[suggestion], this, options);
return session3.$send(output);
}
});
}
return showHelp(command6, session3, options);
});
}
function getCommands(session3, commands, showHidden = false) {
const {authority} = session3.$user || {};
return commands.filter((cmd) => {
return cmd.context.match(session3) && (authority === void 0 || cmd.config.authority <= authority) && (showHidden || !cmd.config.hidden);
}).sort((a, b) => a.name > b.name ? 1 : -1);
}
function formatCommands(prefix, session3, source, options) {
const commands = getCommands(session3, source, options.showHidden);
if (!commands.length)
return [];
let hasSubcommand = false;
const output = commands.map(({name, config, children}) => {
let output2 = " " + name;
if (options.authority) {
output2 += ` (${config.authority}${children.length ? (hasSubcommand = true, "*") : ""})`;
}
output2 += " " + config.description;
return output2;
});
if (options.authority) {
output.unshift(`${prefix}(括号内为对应的最低权限等级${hasSubcommand ? ",标有星号的表示含有子指令" : ""}):`);
} else {
output.unshift(`${prefix}:`);
}
return output;
}
function getOptions(command6, session3, maxUsage, config) {
if (command6.config.hideOptions && !config.showHidden)
return [];
const options = config.showHidden ? Object.values(command6._options) : Object.values(command6._options).filter((option) => !option.hidden && (!session3.$user || option.authority <= session3.$user.authority));
if (!options.length)
return [];
const output = config.authority && options.some((o) => o.authority) ? ["可用的选项有(括号内为额外要求的权限等级):"] : ["可用的选项有:"];
options.forEach((option) => {
const authority = option.authority && config.authority ? `(${option.authority}) ` : "";
let line = ` ${authority}${option.description}`;
if (option.notUsage && maxUsage !== Infinity) {
line += "(不计入总次数)";
}
output.push(line);
});
return output;
}
async function showHelp(command6, session3, config) {
const output = [command6.name + command6.declaration, command6.config.description];
if (command6.context.database) {
await session3.$observeUser(["authority", "timers", "usage"]);
}
const disabled = command6._checkers.some((checker) => checker(session3));
if (disabled)
output[1] += "(指令已禁用)";
if (command6._aliases.length > 1) {
output.push(`别名:${Array.from(command6._aliases.slice(1)).join(",")}。`);
}
const maxUsage = command6.getConfig("maxUsage", session3);
if (!disabled && session3.$user) {
const name = getUsageName(command6);
const minInterval = command6.getConfig("minInterval", session3);
const count = getUsage(name, session3.$user);
if (maxUsage < Infinity) {
output.push(`已调用次数:${Math.min(count, maxUsage)}/${maxUsage}。`);
}
const due = session3.$user.timers[name];
if (minInterval > 0) {
const nextUsage = due ? (Math.max(0, due - Date.now()) / 1e3).toFixed() : 0;
output.push(`距离下次调用还需:${nextUsage}/${minInterval / 1e3} 秒。`);
}
if (command6.config.authority > 1) {
output.push(`最低权限:${command6.config.authority} 级。`);
}
}
const usage = command6._usage;
if (usage) {
output.push(typeof usage === "string" ? usage : await usage.call(command6, session3));
}
output.push(...getOptions(command6, session3, maxUsage, config));
if (command6._examples.length) {
output.push("使用示例:", ...command6._examples.map((example) => " " + example));
}
output.push(...formatCommands("可用的子指令有", session3, command6.children, config));
return output.join("\n");
}
// packages/koishi-core/src/plugins/shortcut.ts
const koishi_utils6 = __toModule(require("koishi-utils"));
Command.prototype.shortcut = function(name, config) {
config = this._shortcuts[name] = {
name,
command: this,
authority: this.config.authority,
...config
};
this.app._shortcutMap[name] = this;
this.app._shortcuts.push(config);
return this;
};
function apply3(ctx) {
koishi_utils6.defineProperty(ctx.app, "_shortcuts", []);
koishi_utils6.defineProperty(ctx.app, "_shortcutMap", {});
ctx.on("new-command", (cmd) => {
cmd._shortcuts = {};
});
ctx.on("remove-command", (cmd) => {
for (const name in cmd._shortcuts) {
delete ctx.app._shortcutMap[name];
const index = ctx.app._shortcuts.indexOf(cmd._shortcuts[name]);
ctx.app._shortcuts.splice(index, 1);
}
});
ctx.on("parse", (message4, {$reply, $prefix, $appel}, builtin) => {
if (!builtin || $prefix || $reply)
return;
for (const shortcut2 of ctx.app._shortcuts) {
const {name, fuzzy, command: command6, oneArg, prefix, options, args = []} = shortcut2;
if (prefix && !$appel)
continue;
if (!fuzzy && message4 !== name)
continue;
if (message4.startsWith(name)) {
const _message = message4.slice(name.length);
if (fuzzy && !$appel && _message.match(/^\S/))
continue;
const result = oneArg ? {options: {}, args: [_message.trim()], rest: ""} : command6.parse(_message.trim());
result.options = {...options, ...result.options};
result.args.unshift(...args);
return {command: command6, ...result};
}
}
});
}
// packages/koishi-core/src/plugins/suggest.ts
const util3 = __toModule(require("util"));
const fastest_levenshtein = __toModule(require("fastest-levenshtein"));
function getSessionId(session3) {
return "" + session3.userId + session3.groupId;
}
Session.prototype.$use = function $use(middleware) {
const identifier = getSessionId(this);
return this.$app.prependMiddleware(async (session3, next) => {
if (identifier && getSessionId(session3) !== identifier)
return next();
return middleware(session3, next);
});
};
Session.prototype.$prompt = function $prompt(timeout = this.$app.options.promptTimeout) {
return new Promise((resolve) => {
const dispose = this.$use((session3) => {
clearTimeout(timer);
dispose();
resolve(session3.message);
});
const timer = setTimeout(() => {
dispose();
resolve("");
}, timeout);
});
};
Session.prototype.$suggest = function $suggest(options) {
const {
target,
items,
prefix = "",
suffix,
apply: apply5,
next = (callback) => callback(),
coefficient = this.$app.options.similarityCoefficient
} = options;
let suggestions, minDistance = Infinity;
for (const name of items) {
const dist = fastest_levenshtein.distance(name, target);
if (name.length <= 2 || dist > name.length * coefficient)
continue;
if (dist === minDistance) {
suggestions.push(name);
} else if (dist < minDistance) {
suggestions = [name];
minDistance = dist;
}
}
if (!suggestions)
return next(() => this.$send(prefix));
return next(() => {
const message4 = prefix + util3.format(Message.SUGGESTION, suggestions.map((name) => `“${name}”`).join("或"));
if (suggestions.length > 1)
return this.$send(message4);
const dispose = this.$use((session3, next2) => {
dispose();
const message5 = session3.message.trim();
if (message5 && message5 !== "." && message5 !== "。")
return next2();
return apply5.call(session3, suggestions[0], next2);
});
return this.$send(message4 + suffix);
});
};
function apply4(ctx) {
ctx.middleware((session3, next) => {
const {$argv, $reply, $parsed, $prefix, $appel, subType} = session3;
if ($argv || subType !== "private" && $prefix === null && !$appel)
return next();
const target = $parsed.split(/\s/, 1)[0].toLowerCase();
if (!target)
return next();
const items = getCommands(session3, ctx.app._commands).flatMap((cmd) => cmd._aliases);
return session3.$suggest({
target,
next,
items,
prefix: Message.COMMAND_SUGGEST_PREFIX,
suffix: Message.COMMAND_SUGGEST_SUFFIX,
async apply(suggestion, next2) {
const newMessage = suggestion + $parsed.slice(target.length) + ($reply ? " " + $reply.content : "");
return this.$execute(newMessage, next2);
}
});
});
}
// packages/koishi-core/src/app.ts
const lru_cache2 = __toModule(require("lru-cache"));
const http = __toModule(require("http"));
function createLeadingRE(patterns, prefix = "", suffix = "") {
return patterns.length ? new RegExp(`^${prefix}(${patterns.map(koishi_utils7.escapeRegExp).join("|")})${suffix}`) : /$^/;
}
var AppStatus;
(function(AppStatus2) {
AppStatus2[AppStatus2["closed"] = 0] = "closed";
AppStatus2[AppStatus2["opening"] = 1] = "opening";
AppStatus2[AppStatus2["open"] = 2] = "open";
AppStatus2[AppStatus2["closing"] = 3] = "closing";
})(AppStatus || (AppStatus = {}));
class App2 extends Context {
constructor(options = {}) {
super({groups: [], users: [], private: true});
this.app = this;
this.bots = {};
this.servers = {};
this.status = 0;
this._sessions = {};
options = this.options = {...App2.defaultConfig, ...options};
if (!options.bots)
options.bots = [options];
koishi_utils7.defineProperty(this, "_hooks", {});
koishi_utils7.defineProperty(this, "_commands", []);
koishi_utils7.defineProperty(this, "_commandMap", {});
koishi_utils7.defineProperty(this, "_userCache", {});
koishi_utils7.defineProperty(this, "_groupCache", new lru_cache2.default({
max: options.groupCacheLength,
maxAge: options.groupCacheAge
}));
if (options.port)
this.createServer();
for (const bot of options.bots) {
let server3 = this.servers[bot.type];
if (!server3) {
const constructor = Server.types[bot.type];
if (!constructor) {
throw new Error(`unsupported type "${bot.type}", you should import the adapter yourself`);
}
server3 = this.servers[bot.type] = Reflect.construct(constructor, [this]);
}
server3.create(bot);
}
this.prepare();
this.middleware(this._preprocess.bind(this));
this.on("message", this._receive.bind(this));
this.on("parse", this._parse.bind(this));
this.on("before-connect", this._listen.bind(this));
this.on("before-disconnect", this._close.bind(this));
this.plugin(apply);
this.plugin(apply4);
this.plugin(apply3);
this.plugin(apply2);
}
createServer() {
const koa = new (require("koa"))();
this.router = new (require("koa-router"))();
koa.use(require("koa-bodyparser")());
koa.use(this.router.routes());
koa.use(this.router.allowedMethods());
this._httpServer = http.createServer(koa.callback());
}
prepare() {
const {nickname, prefix} = this.options;
const nicknames = koishi_utils7.makeArray(nickname);
const prefixes = Array.isArray(prefix) ? prefix : [prefix || ""];
this._nameRE = createLeadingRE(nicknames, "@?", "([,,]\\s*|\\s+)");
this._prefixRE = createLeadingRE(prefixes);
}
async start() {
this.status = 1;
await this.parallel("before-connect");
this.status = 2;
this.logger("app").debug("started");
this.emit("connect");
}
async _listen() {
try {
const {port} = this.app.options;
if (port) {
this._httpServer.listen(port);
this.logger("server").info("server listening at %c", port);
}
await Promise.all(Object.values(this.servers).map((server3) => server3.listen()));
} catch (error) {
this._close();
throw error;
}
}
async stop() {
this.status = 3;
await this.parallel("before-disconnect");
this.status = 0;
this.logger("app").debug("stopped");
this.emit("disconnect");
}
_close() {
Object.values(this.servers).forEach((server3) => server3.close());
this.logger("server").debug("http server closing");
this._httpServer.close();
}
async _preprocess(session3, next) {
let message4 = this.options.processMessage(session3.message);
let capture, atSelf = false;
if (capture = message4.match(/^\[CQ:reply,id=(-?\d+)\]\s*/)) {
session3.$reply = await session3.$bot.getMessage(session3.channelId, capture[1]).catch(koishi_utils7.noop);
message4 = message4.slice(capture[0].length);
if (session3.$reply) {
const prefix = `[CQ:at,qq=${session3.$reply.sender.userId}]`;
message4 = message4.slice(prefix.length).trimStart();
}
}
const at = `[CQ:at,qq=${session3.selfId}]`;
if (session3.subType !== "private" && message4.startsWith(at)) {
atSelf = session3.$appel = true;
message4 = message4.slice(at.length).trimStart();
} else if (capture = message4.match(this._nameRE)) {
session3.$appel = true;
message4 = message4.slice(capture[0].length);
} else if (capture = message4.match(this._prefixRE)) {
session3.$prefix = capture[0];
message4 = message4.slice(capture[0].length);
}
session3.$parsed = message4;
session3.$argv = session3.$parse(message4, "", true);
if (this.database) {
if (session3.subType === "group") {
const groupFields = new Set(["flag", "assignee"]);
this.emit("before-attach-group", session3, groupFields);
const group = await session3.$observeGroup(groupFields);
if (await this.serial(session3, "attach-group", session3))
return;
if (group.flag & Group.Flag.ignore)
return;
if (group.assignee !== session3.selfId && !atSelf)
return;
}
const userFields = new Set(["flag"]);
this.emit("before-attach-user", session3, userFields);
const user = await session3.$observeUser(userFields);
if (await this.serial(session3, "attach-user", session3))
return;
if (user.flag & User.Flag.ignore)
return;
}
await this.parallel(session3, "attach", session3);
if (!session3.$argv)
return next();
session3.$argv.next = next;
return session3.$argv.command.execute(session3.$argv);
}
async _receive(session3) {
var _a, _b;
this._sessions[session3.$uuid] = session3;
const middlewares = this._hooks[Context.MIDDLEWARE_EVENT].filter(([context2]) => context2.match(session3)).map(([, middleware]) => middleware);
let index = 0, midStack = "", lastCall = "";
const {prettyErrors} = this.options;
const next = async (fallback) => {
var _a2;
if (prettyErrors) {
lastCall = new Error().stack.split("\n", 3)[2];
if (index) {
const capture = lastCall.match(/\((.+)\)/);
midStack = `
- ${capture ? capture[1] : lastCall.slice(7)}${midStack}`;
}
}
try {
if (!this._sessions[session3.$uuid]) {
throw new Error("isolated next function detected");
}
if (fallback)
middlewares.push((_, next2) => fallback(next2));
return (_a2 = middlewares[index++]) == null ? void 0 : _a2.call(middlewares, session3, next);
} catch (error) {
let stack = koishi_utils7.coerce(error);
if (prettyErrors) {
const index2 = stack.indexOf(lastCall);
stack = `${stack.slice(0, index2)}Middleware stack:${midStack}`;
}
this.logger("middleware").warn(`${session3.message}
${stack}`);
}
};
await next();
delete this._sessions[session3.$uuid];
this.emit(session3, "middleware", session3);
await ((_a = session3.$user) == null ? void 0 : _a._update());
await ((_b = session3.$group) == null ? void 0 : _b._update());
}
_parse(message4, session3, builtin, terminator = "") {
const {$reply, $prefix, $appel, subType} = session3;
if (builtin && subType !== "private" && $prefix === null && !$appel)
return;
terminator = koishi_utils7.escapeRegExp(terminator);
const name = message4.split(new RegExp(`[\\s${terminator}]`), 1)[0];
const index = name.lastIndexOf("/");
const command6 = this.app._commandMap[name.slice(index + 1).toLowerCase()];
if (!command6)
return;
message4 = message4.slice(name.length).trim() + ($reply ? " " + $reply.content : "");
const result = command6.parse(message4, terminator);
return {command: command6, ...result};
}
}
App2.defaultConfig = {
maxListeners: 64,
prettyErrors: true,
queueDelay: 0.1 * koishi_utils7.Time.second,
broadcastDelay: 0.5 * koishi_utils7.Time.second,
promptTimeout: koishi_utils7.Time.minute,
userCacheAge: koishi_utils7.Time.minute,
groupCacheAge: 5 * koishi_utils7.Time.minute,
similarityCoefficient: 0.4,
processMessage: (message4) => koishi_utils7.simplify(message4.trim())
};
//# sourceMappingURL=index.js.map

@@ -1,24 +0,18 @@

/// <reference types="node" />
import { Session, MessageType, MessageInfo } from './session';
import { Session, MessageInfo, EventTypeMap, GroupInfo, GroupMemberInfo, UserInfo } from './session';
import { App } from './app';
import * as http from 'http';
import type Router from 'koa-router';
export interface BotOptions {
selfId?: number;
type?: string;
selfId?: string;
}
export declare abstract class Server {
declare type BotStatic<T extends Bot = Bot> = new (app: App, options: BotOptions) => T;
export declare abstract class Server<T extends Bot = Bot> {
app: App;
private BotStatic;
static types: Record<string, new (app: App) => Server>;
bots: Bot[];
router?: Router;
server?: http.Server;
protected _listening: boolean;
protected abstract _listen(): Promise<void>;
protected abstract _close(): void;
constructor(app: App);
createServer(): void;
prepare(data: any): Session<never, never, {}, import("./session").PostType>;
bots: T[];
abstract listen(): Promise<void>;
abstract close(): void;
constructor(app: App, BotStatic: BotStatic<T>);
create(options: BotOptions): void;
dispatch(session: Session): void;
listen(): Promise<void>;
close(): void;
}

@@ -38,16 +32,27 @@ export declare enum BotStatusCode {

export interface Bot extends BotOptions {
[Bot.$send](session: Session, message: string): Promise<void>;
ready?: boolean;
version?: string;
getMsg(messageId: number): Promise<MessageInfo>;
getSelfId(): Promise<number>;
getSelfId(): Promise<string>;
getStatusCode(): Promise<BotStatusCode>;
getMemberMap(groupId: number): Promise<Record<number, string>>;
sendGroupMsg(groupId: number, message: string, autoEscape?: boolean): Promise<number>;
sendPrivateMsg(userId: number, message: string, autoEscape?: boolean): Promise<number>;
sendMessage(channelId: string, message: string): Promise<string>;
sendPrivateMessage(userId: string, message: string): Promise<string>;
getMessage(channelId: string, messageId: string): Promise<MessageInfo>;
deleteMessage(channelId: string, messageId: string): Promise<void>;
getUser(userId: string): Promise<UserInfo>;
getGroup(groupId: string): Promise<GroupInfo>;
getGroupList(): Promise<GroupInfo[]>;
getGroupMember(groupId: string, userId: string): Promise<GroupMemberInfo>;
getGroupMemberList(groupId: string): Promise<GroupMemberInfo[]>;
}
export declare class Bot {
app: App;
static readonly $send: unique symbol;
constructor(app: App, options: BotOptions);
createSession(messageType: MessageType, ctxType: 'group' | 'user', ctxId: number, message: string): Session<never, never, {}, import("./session").PostType>;
broadcast(groups: number[], message: string, delay?: number): Promise<number[]>;
createSession(subType: EventTypeMap['message'], ctxType: 'group' | 'user', ctxId: string, message: string): Session<never, never, {}, never, "message" | "message-edited" | "message-deleted" | "group-added" | "group-deleted" | "lifecycle" | "send">;
getGroupMemberMap(groupId: string): Promise<{
[k: string]: string;
}>;
broadcast(channels: string[], message: string, delay?: number): Promise<string[]>;
}
export {};

@@ -1,38 +0,28 @@

import { User, Group } from './database';
import { User, Group, Platforms, PlatformKind } from './database';
import { ExecuteArgv, ParsedArgv } from './command';
import { NextFunction } from './context';
import { App } from './app';
export declare type PostType = 'message' | 'notice' | 'request' | 'meta_event' | 'send';
import { Bot } from './server';
export declare type EventType = keyof EventTypeMap;
export declare type MessageType = 'private' | 'group';
export declare type NoticeType = 'group_upload' | 'group_admin' | 'group_increase' | 'group_decrease' | 'group_ban' | 'friend_add' | 'group_recall' | 'friend_recall' | 'notify';
export declare type RequestType = 'friend' | 'group';
export declare type MetaEventType = 'lifecycle' | 'heartbeat';
export interface MetaTypeMap {
message: MessageType;
notice: NoticeType;
request: RequestType;
meta_event: MetaEventType;
send: null;
export interface EventTypeMap {
'message': MessageType;
'message-edited': MessageType;
'message-deleted': MessageType;
'group-added': null;
'group-deleted': null;
'lifecycle': 'heartbeat' | 'enable' | 'disable' | 'connect';
'send': MessageType;
}
export interface SubTypeMap {
message: 'friend' | 'group' | 'other' | 'normal' | 'anonymous' | 'notice';
notice: 'set' | 'unset' | 'approve' | 'invite' | 'leave' | 'kick' | 'kick_me' | 'ban' | 'lift_ban' | 'poke' | 'lucky_king' | 'honor';
request: 'add' | 'invite';
meta_event: 'enable' | 'disable' | 'connect';
send: null;
}
/** CQHTTP Meta Information */
export interface Meta<P extends PostType = PostType> {
postType?: P;
messageType?: MetaTypeMap[P & 'message'];
noticeType?: MetaTypeMap[P & 'notice'];
requestType?: MetaTypeMap[P & 'request'];
metaEventType?: MetaTypeMap[P & 'meta_event'];
sendType?: MetaTypeMap[P & 'send'];
subType?: SubTypeMap[P];
selfId?: number;
userId?: number;
groupId?: number;
export interface Meta<E extends EventType = EventType> {
kind?: PlatformKind;
eventType?: E;
subType?: EventTypeMap[E];
channelId?: string;
selfId?: string;
userId?: string;
groupId?: string;
time?: number;
messageId?: number;
messageId?: string;
message?: string;

@@ -53,5 +43,5 @@ rawMessage?: string;

}
export interface Session<U, G, O, P extends PostType = PostType> extends Meta<P> {
export interface Session<U, G, O, K, E extends EventType = EventType> extends Meta<E> {
}
export declare class Session<U extends User.Field = never, G extends Group.Field = never, O extends {} = {}> {
export declare class Session<U extends User.Field = never, G extends Group.Field = never, O extends {} = {}, K extends PlatformKind = never> {
$user?: User.Observed<U>;

@@ -73,9 +63,11 @@ $group?: Group.Observed<G>;

};
get $bot(): import("./server").Bot;
get $bot(): [K] extends [never] ? Bot : Platforms[K];
get $username(): string;
$send(message: string): Promise<void>;
$send(message: string): any;
$cancelQueued(delay?: number): void;
$sendQueued(message: string | void, delay?: number): Promise<void>;
$getGroup<K extends Group.Field = never>(id?: string, fields?: readonly K[], assignee?: string): Promise<Pick<Group, "id" | "type" | K>>;
/** 在元数据上绑定一个可观测群实例 */
$observeGroup<T extends Group.Field = never>(fields?: Iterable<T>): Promise<Group.Observed<T | G>>;
$getUser<K extends User.Field = never>(id?: string, fields?: readonly K[], authority?: number): Promise<Pick<User, K>>;
/** 在元数据上绑定一个可观测用户实例 */

@@ -100,3 +92,3 @@ $observeUser<T extends User.Field = never>(fields?: Iterable<T>): Promise<User.Observed<T | U>>;

export interface AccountInfo {
userId: number;
userId: string;
nickname: string;

@@ -125,15 +117,20 @@ }

export interface MessageInfo {
time: number;
messageType: MessageType;
messageId: number;
realId: number;
id: string;
type: EventTypeMap['message'];
content: string;
timestamp: number;
sender: SenderInfo;
message: string;
}
/**
* get context unique id
* @example
* getContextId(session) // user123, group456
*/
export declare function getContextId(session: Session): string;
export interface GroupInfo {
id: string;
name: string;
}
export interface UserInfo {
id: string;
name: string;
}
export interface GroupMemberInfo extends UserInfo {
nick: string;
roles: string[];
}
export declare function getTargetId(target: string | number): number;
{
"name": "koishi-core",
"description": "Core features for Koishi",
"version": "2.4.2",
"version": "3.0.0-alpha.0",
"main": "dist/index.js",

@@ -48,5 +48,5 @@ "typings": "dist/index.d.ts",

"koa-router": "^10.0.0",
"koishi-utils": "^3.1.5",
"koishi-utils": "^3.2.0",
"lru-cache": "^6.0.0"
}
}

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