@chatluna/patchbay 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +57 -2
- package/lib/apply.d.ts +5 -0
- package/lib/apply.js +59 -0
- package/lib/cli.d.ts +2 -0
- package/lib/cli.js +33 -0
- package/lib/index.d.ts +4 -0
- package/lib/index.js +4 -0
- package/lib/manifest.d.ts +24 -0
- package/lib/manifest.js +13 -0
- package/lib/patch.d.ts +7 -0
- package/lib/patch.js +43 -0
- package/lib/sync.d.ts +31 -0
- package/lib/sync.js +144 -0
- package/package.json +54 -4
- package/patchbay.json +69 -0
- package/patches/@cordisjs+plugin-cli-cordis@1.0.5.patch +56 -0
- package/patches/@cordisjs+plugin-database@4.1.1.patch +15 -0
- package/patches/@cordisjs+plugin-http@1.5.2.patch +13 -0
- package/patches/@cordisjs+plugin-loader@1.0.0-rc.9.patch +82 -0
- package/patches/@cordisjs+plugin-server@1.7.0.patch +22 -0
- package/patches/@cordisjs+unyaml@2.0.3.patch +69 -0
- package/patches/@koishijs+core@4.18.11.patch +1144 -0
- package/patches/@satorijs+core@4.6.1.patch +584 -0
- package/patches/koishi-plugin-adapter-onebot@6.9.5.patch +194 -0
|
@@ -0,0 +1,584 @@
|
|
|
1
|
+
diff --git a/lib/index.d.ts b/lib/index.d.ts
|
|
2
|
+
index 642da25..b39b250 100644
|
|
3
|
+
--- a/lib/index.d.ts
|
|
4
|
+
+++ b/lib/index.d.ts
|
|
5
|
+
@@ -1,8 +1,9 @@
|
|
6
|
+
-import { Context, Service, z, Logger } from 'cordis';
|
|
7
|
+
+import { Context, Service, Logger } from 'cordis';
|
|
8
|
+
+import { default as z } from 'schemastery';
|
|
9
|
+
import { Message, SendOptions, Event, GuildMember, User, Meta, Response, Login, Methods, Status, Upload, WebSocket } from '@satorijs/protocol';
|
|
10
|
+
import h from '@satorijs/element';
|
|
11
|
+
import { Awaitable, Dict } from 'cosmokit';
|
|
12
|
+
-import { HTTP } from '@cordisjs/plugin-http';
|
|
13
|
+
+import { Http, Http as HTTP } from '@cordisjs/plugin-http';
|
|
14
|
+
import { Key } from 'path-to-regexp';
|
|
15
|
+
export abstract class MessageEncoder<C extends Context = Context, B extends Bot<C> = Bot<C>> {
|
|
16
|
+
bot: B;
|
|
17
|
+
@@ -49,7 +50,7 @@ export class Session<C extends Context = Context> {
|
|
18
|
+
id: number;
|
|
19
|
+
sn: number;
|
|
20
|
+
bot: Bot<C>;
|
|
21
|
+
- app: C['root'];
|
|
22
|
+
+ app: C[typeof Context.root];
|
|
23
|
+
event: Event;
|
|
24
|
+
locales: string[];
|
|
25
|
+
constructor(bot: Bot<C>, event: Partial<Event>);
|
|
26
|
+
@@ -73,7 +74,7 @@ export class Session<C extends Context = Context> {
|
|
27
|
+
}
|
|
28
|
+
export function defineAccessor(prototype: {}, name: string, keys: string[]): void;
|
|
29
|
+
export type { Fragment, Render } from '@satorijs/element';
|
|
30
|
+
-export { h, h as Element, h as segment, HTTP, HTTP as Quester };
|
|
31
|
+
+export { h, h as Element, h as segment, Http, Http as HTTP, Http as Quester };
|
|
32
|
+
export * from 'cordis';
|
|
33
|
+
export * from 'cosmokit';
|
|
34
|
+
export * as Universal from '@satorijs/protocol';
|
|
35
|
+
@@ -129,7 +130,7 @@ declare module 'cordis' {
|
|
36
|
+
}
|
|
37
|
+
declare module '@cordisjs/plugin-http' {
|
|
38
|
+
namespace HTTP {
|
|
39
|
+
- function createConfig(this: typeof HTTP, endpoint?: string | boolean): z<Config>;
|
|
40
|
+
+ function createConfig(this: typeof Http, endpoint?: string | boolean): z<Config>;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export type Component<S extends Session = Session> = h.Render<Awaitable<h.Fragment>, S>;
|
|
44
|
+
@@ -138,7 +139,7 @@ export namespace Component {
|
|
45
|
+
session?: boolean;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
-export type GetSession<C extends Context> = C[typeof Context.session];
|
|
49
|
+
+export type GetSession<C extends Context> = Session;
|
|
50
|
+
declare class SatoriContext extends Context {
|
|
51
|
+
constructor(config?: any);
|
|
52
|
+
}
|
|
53
|
+
@@ -152,7 +153,7 @@ declare class DisposableSet<T> {
|
|
54
|
+
add(...values: T[]): () => void;
|
|
55
|
+
[Symbol.iterator](): SetIterator<T>;
|
|
56
|
+
}
|
|
57
|
+
-export class Satori<C extends Context = Context> extends Service<unknown, C> {
|
|
58
|
+
+export class Satori<C extends Context = Context> extends Service {
|
|
59
|
+
static [Service.provide]: string;
|
|
60
|
+
static [Service.immediate]: boolean;
|
|
61
|
+
uid: string;
|
|
62
|
+
@@ -222,6 +223,7 @@ export abstract class Bot<C extends Context = Context, T = any> {
|
|
63
|
+
config: T;
|
|
64
|
+
adapterName: string;
|
|
65
|
+
static reusable: boolean;
|
|
66
|
+
+ static inject: string[];
|
|
67
|
+
static MessageEncoder?: new (bot: Bot, channelId: string, referrer?: any, options?: SendOptions) => MessageEncoder;
|
|
68
|
+
[Service.tracker]: {
|
|
69
|
+
associate: string;
|
|
70
|
+
diff --git a/lib/index.mjs b/lib/index.mjs
|
|
71
|
+
index 79916ba..93a2e63 100644
|
|
72
|
+
--- a/lib/index.mjs
|
|
73
|
+
+++ b/lib/index.mjs
|
|
74
|
+
@@ -2,7 +2,276 @@ var __defProp = Object.defineProperty;
|
|
75
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
76
|
+
|
|
77
|
+
// src/index.ts
|
|
78
|
+
-import { Context as Context4, Logger as Logger2, Service as Service4, z as z2 } from "cordis";
|
|
79
|
+
+import { Context as Context4, Inject, RegistryService, Service as Service4, getTraceable } from "cordis";
|
|
80
|
+
+import Schema4 from "schemastery";
|
|
81
|
+
+// Module-level loggers lack a Context; forward to the host target or console until upstream supports Cordis 4.
|
|
82
|
+
+class SatoriLoggerShim {
|
|
83
|
+
+ static target = null;
|
|
84
|
+
+ #name;
|
|
85
|
+
+ constructor(name) { this.#name = name; }
|
|
86
|
+
+ _log(level, ...args) {
|
|
87
|
+
+ if (SatoriLoggerShim.target) SatoriLoggerShim.target({ level, name: this.#name, args });
|
|
88
|
+
+ else if (level === "error") console.error(`[${this.#name}]`, ...args);
|
|
89
|
+
+ else if (level === "warn") console.warn(`[${this.#name}]`, ...args);
|
|
90
|
+
+ else console.log(`[${this.#name}]`, ...args);
|
|
91
|
+
+ }
|
|
92
|
+
+ debug(...args) { this._log("debug", ...args); }
|
|
93
|
+
+ info(...args) { this._log("info", ...args); }
|
|
94
|
+
+ warn(...args) { this._log("warn", ...args); }
|
|
95
|
+
+ error(...args) { this._log("error", ...args); }
|
|
96
|
+
+}
|
|
97
|
+
+const Logger2 = SatoriLoggerShim;
|
|
98
|
+
+const z2 = Schema4;
|
|
99
|
+
+// Cordis 4 has no ready event; use the fiber state transition.
|
|
100
|
+
+function onReady(ctx, cb) {
|
|
101
|
+
+ if (ctx.fiber.state === 2) {
|
|
102
|
+
+ const timer = setTimeout(cb, 0);
|
|
103
|
+
+ return () => clearTimeout(timer);
|
|
104
|
+
+ }
|
|
105
|
+
+ const off = ctx.on("internal/status", (fiber) => {
|
|
106
|
+
+ if (fiber === ctx.fiber && fiber.state === 2) { off(); Promise.resolve().then(cb); }
|
|
107
|
+
+ });
|
|
108
|
+
+ return off;
|
|
109
|
+
+}
|
|
110
|
+
+// Satori and Koishi plugins declare `inject = { required, optional }`, but Cordis 4 reads every key of
|
|
111
|
+
+// an inject object as a service name, so such a plugin waits forever for services named "required"
|
|
112
|
+
+// and "optional". Normalize the shape where Cordis resolves it, at module load so no plugin can be
|
|
113
|
+
+// registered first. Optional names are kept for the internal/get hook in the Satori service, and Bot
|
|
114
|
+
+// and Adapter plugins also wait for `satori`, which every Bot constructor reads.
|
|
115
|
+
+const kOptional = Symbol.for("satori.cordis4.optional");
|
|
116
|
+
+let resolving;
|
|
117
|
+
+if (!Object.hasOwn(RegistryService.prototype, kOptional)) {
|
|
118
|
+
+ Object.defineProperty(RegistryService.prototype, kOptional, { value: true });
|
|
119
|
+
+ const plugin = RegistryService.prototype.plugin;
|
|
120
|
+
+ RegistryService.prototype.plugin = function(target, ...args) {
|
|
121
|
+
+ const outer = resolving;
|
|
122
|
+
+ resolving = target;
|
|
123
|
+
+ try {
|
|
124
|
+
+ return plugin.call(this, target, ...args);
|
|
125
|
+
+ } finally {
|
|
126
|
+
+ resolving = outer;
|
|
127
|
+
+ }
|
|
128
|
+
+ };
|
|
129
|
+
+ const resolve = Inject.resolve;
|
|
130
|
+
+ Inject.resolve = function(inject, result) {
|
|
131
|
+
+ const shaped = !!inject && !Array.isArray(inject) && (Array.isArray(inject.required) || Array.isArray(inject.optional));
|
|
132
|
+
+ const resolved = resolve(shaped ? inject.required ?? [] : inject, result);
|
|
133
|
+
+ if (shaped) Object.defineProperty(resolved, kOptional, { value: inject.optional ?? [] });
|
|
134
|
+
+ const proto = resolving?.prototype;
|
|
135
|
+
+ if ((proto instanceof Bot || proto instanceof Adapter) && !("satori" in resolved)) resolved.satori = null;
|
|
136
|
+
+ return resolved;
|
|
137
|
+
+ };
|
|
138
|
+
+}
|
|
139
|
+
+function isOptional(fiber, name) {
|
|
140
|
+
+ for (; fiber; fiber = fiber.runtime && fiber.parent.fiber) {
|
|
141
|
+
+ if (fiber.inject[kOptional]?.includes(name)) return true;
|
|
142
|
+
+ }
|
|
143
|
+
+ return false;
|
|
144
|
+
+}
|
|
145
|
+
+const satoriFibers = /* @__PURE__ */ new WeakMap();
|
|
146
|
+
+function isSatoriFiber(fiber) {
|
|
147
|
+
+ if (!fiber.runtime) return false;
|
|
148
|
+
+ let result = satoriFibers.get(fiber);
|
|
149
|
+
+ if (result !== void 0) return result;
|
|
150
|
+
+ const proto = fiber.runtime.callback.prototype;
|
|
151
|
+
+ result = proto instanceof Bot || proto instanceof Adapter || isSatoriFiber(fiber.parent.fiber);
|
|
152
|
+
+ satoriFibers.set(fiber, result);
|
|
153
|
+
+ return result;
|
|
154
|
+
+}
|
|
155
|
+
+// Satori adapters are written against the Quester client of Cordis 3: `http(url)` resolves to
|
|
156
|
+
+// `{ data, status, headers }` and throws on 4xx/5xx, `endpoint` is joined as a string prefix, and
|
|
157
|
+
+// `file()` / `isLocal()` exist. @cordisjs/plugin-http 1.x returns a raw Response, never throws on
|
|
158
|
+
+// status, uses `baseUrl` and has neither helper. Adapter fibers see this facade over it instead.
|
|
159
|
+
+const trimSlash = /* @__PURE__ */ __name((source) => source.replace(/\/$/, ""), "trimSlash");
|
|
160
|
+
+const questers = /* @__PURE__ */ new WeakMap();
|
|
161
|
+
+let localAddresses;
|
|
162
|
+
+function toQuester(http, endpoint) {
|
|
163
|
+
+ if (endpoint === void 0 && questers.has(http)) return questers.get(http);
|
|
164
|
+
+ const resolveUrl = /* @__PURE__ */ __name((url) => {
|
|
165
|
+
+ if (!endpoint || typeof url !== "string" || URL.canParse(url)) return url;
|
|
166
|
+
+ return trimSlash(endpoint) + url;
|
|
167
|
+
+ }, "resolveUrl");
|
|
168
|
+
+ const request = /* @__PURE__ */ __name(async function(...args) {
|
|
169
|
+
+ const method = typeof args[1] === "string" || args[1] instanceof URL ? args.shift() : void 0;
|
|
170
|
+
+ const { validateStatus = /* @__PURE__ */ __name((status) => status < 400, "validateStatus"), ...config } = args[1] ?? {};
|
|
171
|
+
+ const raw = await http(resolveUrl(args[0]), { ...config, method: method ?? config.method });
|
|
172
|
+
+ const response = { data: null, url: raw.url, status: raw.status, statusText: raw.statusText, headers: raw.headers };
|
|
173
|
+
+ if (!validateStatus(raw.status)) {
|
|
174
|
+
+ response.data = await http.defaultDecoder(raw).catch(() => null);
|
|
175
|
+
+ throw new Http.Error(raw.statusText, "STATUS_ERROR", response);
|
|
176
|
+
+ }
|
|
177
|
+
+ response.data = await http._decode(raw);
|
|
178
|
+
+ return response;
|
|
179
|
+
+ }, "request");
|
|
180
|
+
+ const methods = {
|
|
181
|
+
+ get: /* @__PURE__ */ __name((url, config) => request(url, { method: "GET", ...config }).then((r) => r.data), "get"),
|
|
182
|
+
+ delete: /* @__PURE__ */ __name((url, config) => request(url, { method: "DELETE", ...config }).then((r) => r.data), "delete"),
|
|
183
|
+
+ patch: /* @__PURE__ */ __name((url, data, config) => request(url, { method: "PATCH", data, ...config }).then((r) => r.data), "patch"),
|
|
184
|
+
+ post: /* @__PURE__ */ __name((url, data, config) => request(url, { method: "POST", data, ...config }).then((r) => r.data), "post"),
|
|
185
|
+
+ put: /* @__PURE__ */ __name((url, data, config) => request(url, { method: "PUT", data, ...config }).then((r) => r.data), "put"),
|
|
186
|
+
+ head: /* @__PURE__ */ __name((url, config) => request(url, { method: "HEAD", ...config }).then((r) => r.headers), "head"),
|
|
187
|
+
+ ws: /* @__PURE__ */ __name((url, config) => http.ws(resolveUrl(url), config), "ws"),
|
|
188
|
+
+ extend: /* @__PURE__ */ __name((config = {}) => {
|
|
189
|
+
+ const { endpoint: next = endpoint, baseURL, ...rest } = config;
|
|
190
|
+
+ return toQuester(http.extend(baseURL === void 0 ? rest : { ...rest, baseUrl: baseURL }), next);
|
|
191
|
+
+ }, "extend"),
|
|
192
|
+
+ isError: Http.Error.is,
|
|
193
|
+
+ file: /* @__PURE__ */ __name(async (_url, options = {}) => {
|
|
194
|
+
+ const url = resolveUrl(_url);
|
|
195
|
+
+ const task = await http.ctx.serial(facade, "http/file", url, options);
|
|
196
|
+
+ if (task) return task;
|
|
197
|
+
+ const { headers, data, url: responseUrl } = await request(url, {
|
|
198
|
+
+ method: "GET",
|
|
199
|
+
+ responseType: "arraybuffer",
|
|
200
|
+
+ timeout: +options.timeout || void 0
|
|
201
|
+
+ });
|
|
202
|
+
+ const type = headers.get("content-type") ?? void 0;
|
|
203
|
+
+ // A data: URL response has no url to take a name from.
|
|
204
|
+
+ const filename = /.+\/([^/?]*)(?=\?)?/.exec(responseUrl)?.[1] || `file.${type?.split("/")[1]?.split(/[;+]/)[0] ?? "bin"}`;
|
|
205
|
+
+ return { type, mime: type, filename, data };
|
|
206
|
+
+ }, "file"),
|
|
207
|
+
+ isLocal: /* @__PURE__ */ __name(async (url) => {
|
|
208
|
+
+ const { hostname, protocol } = new URL(url);
|
|
209
|
+
+ if (protocol !== "http:" && protocol !== "https:") return true;
|
|
210
|
+
+ const [{ lookup }, { BlockList }] = await Promise.all([import("node:dns/promises"), import("node:net")]);
|
|
211
|
+
+ if (!localAddresses) {
|
|
212
|
+
+ localAddresses = new BlockList();
|
|
213
|
+
+ for (const range of ["0.0.0.0/8", "10.0.0.0/8", "100.64.0.0/10", "127.0.0.0/8", "169.254.0.0/16", "172.16.0.0/12", "192.0.0.0/24", "192.0.2.0/24", "192.168.0.0/16", "198.18.0.0/15", "198.51.100.0/24", "203.0.113.0/24", "224.0.0.0/3"]) {
|
|
214
|
+
+ const [address, prefix] = range.split("/");
|
|
215
|
+
+ localAddresses.addSubnet(address, +prefix, "ipv4");
|
|
216
|
+
+ }
|
|
217
|
+
+ for (const range of ["::/128", "::1/128", "64:ff9b::/96", "100::/64", "2001:db8::/32", "fc00::/7", "fe80::/10", "ff00::/8"]) {
|
|
218
|
+
+ const [address, prefix] = range.split("/");
|
|
219
|
+
+ localAddresses.addSubnet(address, +prefix, "ipv6");
|
|
220
|
+
+ }
|
|
221
|
+
+ }
|
|
222
|
+
+ try {
|
|
223
|
+
+ const { address, family } = await lookup(hostname.replace(/^\[(.+)\]$/, "$1"));
|
|
224
|
+
+ return localAddresses.check(address, family === 6 ? "ipv6" : "ipv4");
|
|
225
|
+
+ } catch {
|
|
226
|
+
+ return false;
|
|
227
|
+
+ }
|
|
228
|
+
+ }, "isLocal")
|
|
229
|
+
+ };
|
|
230
|
+
+ const facade = new Proxy(request, {
|
|
231
|
+
+ get(target, prop) {
|
|
232
|
+
+ // Symbols stay local: forwarding the client's tracker or invoke symbol would make Cordis
|
|
233
|
+
+ // trace this facade as the raw client.
|
|
234
|
+
+ if (typeof prop === "symbol") return Reflect.get(target, prop);
|
|
235
|
+
+ if (Object.hasOwn(methods, prop)) return methods[prop];
|
|
236
|
+
+ if (prop === "config") return { ...http.config, endpoint };
|
|
237
|
+
+ const value = Reflect.get(http, prop);
|
|
238
|
+
+ return typeof value === "function" ? value.bind(http) : value;
|
|
239
|
+
+ }
|
|
240
|
+
+ });
|
|
241
|
+
+ if (endpoint === void 0) questers.set(http, facade);
|
|
242
|
+
+ return facade;
|
|
243
|
+
+}
|
|
244
|
+
+// Satori adapters register Koa routes: `ctx.server.post(path, (koa, next) => ...)` reading a parsed
|
|
245
|
+
+// `koa.request.body` (raw text under `Symbol.for("unparsedBody")`) and writing `koa.status` /
|
|
246
|
+
+// `koa.body`, with koa-router paths and `config.selfUrl`. @cordisjs/plugin-server 1.x passes its own
|
|
247
|
+
+// Request and Response, parses nothing, matches path-to-regexp 8 paths and calls it `baseUrl`.
|
|
248
|
+
+const kUnparsed = Symbol.for("unparsedBody");
|
|
249
|
+
+const koaServers = /* @__PURE__ */ new WeakMap();
|
|
250
|
+
+// koa-router paths use path-to-regexp 6 modifiers (adapter-telegram serves `/:file+`); plugin-server
|
|
251
|
+
+// compiles path-to-regexp 8, which spells them as wildcards and braces, and has no inline groups.
|
|
252
|
+
+const koaPath = /* @__PURE__ */ __name((path) => {
|
|
253
|
+
+ if (typeof path !== "string") return path;
|
|
254
|
+
+ if (path.includes("(")) return new RegExp(`^${path}$`);
|
|
255
|
+
+ return path.replace(/\/:(\w+)([+*?])/g, (_, name, modifier) => modifier === "+" ? `/*${name}` : modifier === "*" ? `{/*${name}}` : `{/:${name}}`);
|
|
256
|
+
+}, "koaPath");
|
|
257
|
+
+async function toKoaContext(req, res) {
|
|
258
|
+
+ const type = req.headers.get("content-type") ?? "";
|
|
259
|
+
+ const raw = req.method === "GET" || req.method === "HEAD" ? "" : await req.text();
|
|
260
|
+
+ let body = {};
|
|
261
|
+
+ if (raw && type.includes("json")) body = JSON.parse(raw);
|
|
262
|
+
+ else if (raw && type.includes("x-www-form-urlencoded")) body = Object.fromEntries(new URLSearchParams(raw));
|
|
263
|
+
+ else if (raw) body = raw;
|
|
264
|
+
+ if (typeof body === "object") Object.defineProperty(body, kUnparsed, { value: raw });
|
|
265
|
+
+ const headers = Object.fromEntries(req.headers);
|
|
266
|
+
+ const query = Object.fromEntries(req.query);
|
|
267
|
+
+ let status, payload;
|
|
268
|
+
+ const koa = {
|
|
269
|
+
+ req: { method: req.method, url: req.url, headers },
|
|
270
|
+
+ method: req.method,
|
|
271
|
+
+ path: req.path,
|
|
272
|
+
+ url: req.url,
|
|
273
|
+
+ query,
|
|
274
|
+
+ params: req.params,
|
|
275
|
+
+ headers,
|
|
276
|
+
+ header: headers,
|
|
277
|
+
+ request: {
|
|
278
|
+
+ body,
|
|
279
|
+
+ method: req.method,
|
|
280
|
+
+ path: req.path,
|
|
281
|
+
+ url: req.url,
|
|
282
|
+
+ query,
|
|
283
|
+
+ headers,
|
|
284
|
+
+ header: headers,
|
|
285
|
+
+ is: /* @__PURE__ */ __name((...types) => types.find((name) => type.includes(name)) ?? false, "is")
|
|
286
|
+
+ },
|
|
287
|
+
+ response: {
|
|
288
|
+
+ get status() { return koa.status; },
|
|
289
|
+
+ set status(value) { koa.status = value; },
|
|
290
|
+
+ get body() { return koa.body; },
|
|
291
|
+
+ set body(value) { koa.body = value; },
|
|
292
|
+
+ set type(value) { koa.type = value; }
|
|
293
|
+
+ },
|
|
294
|
+
+ get status() { return status ?? 404; },
|
|
295
|
+
+ set status(value) { status = value; },
|
|
296
|
+
+ // adapter-onebot reads the parsed request from `ctx.body` until it writes a response.
|
|
297
|
+
+ get body() { return payload ?? body; },
|
|
298
|
+
+ set body(value) {
|
|
299
|
+
+ payload = value;
|
|
300
|
+
+ status ??= 200;
|
|
301
|
+
+ },
|
|
302
|
+
+ set type(value) { res.headers.set("content-type", value); },
|
|
303
|
+
+ get: /* @__PURE__ */ __name((field) => req.headers.get(field) ?? "", "get"),
|
|
304
|
+
+ set: /* @__PURE__ */ __name((field, value) => {
|
|
305
|
+
+ if (typeof field === "string") return res.headers.set(field, value);
|
|
306
|
+
+ for (const [key, item] of Object.entries(field)) res.headers.set(key, item);
|
|
307
|
+
+ }, "set")
|
|
308
|
+
+ };
|
|
309
|
+
+ const flush = /* @__PURE__ */ __name(() => {
|
|
310
|
+
+ if (status !== void 0) res.status = status;
|
|
311
|
+
+ if (payload === void 0) return;
|
|
312
|
+
+ const binary = typeof payload !== "object" || payload === null || ArrayBuffer.isView(payload) || payload instanceof ArrayBuffer || payload instanceof Blob || payload instanceof ReadableStream;
|
|
313
|
+
+ if (binary) res.body = payload;
|
|
314
|
+
+ else res.json(payload);
|
|
315
|
+
+ }, "flush");
|
|
316
|
+
+ return [koa, flush];
|
|
317
|
+
+}
|
|
318
|
+
+function toKoaServer(server) {
|
|
319
|
+
+ if (koaServers.has(server)) return koaServers.get(server);
|
|
320
|
+
+ const route = /* @__PURE__ */ __name((method) => (path, middleware, options) => server[method](koaPath(path), async (req, res, next) => {
|
|
321
|
+
+ const [koa, flush] = await toKoaContext(req, res);
|
|
322
|
+
+ let forwarded;
|
|
323
|
+
+ await middleware(koa, () => forwarded = next());
|
|
324
|
+
+ if (forwarded) return forwarded;
|
|
325
|
+
+ flush();
|
|
326
|
+
+ }, options), "route");
|
|
327
|
+
+ const methods = {
|
|
328
|
+
+ ...Object.fromEntries(["all", "get", "delete", "head", "post", "put", "patch"].map((method) => [method, route(method)])),
|
|
329
|
+
+ ws: /* @__PURE__ */ __name((path, handle, options) => {
|
|
330
|
+
+ const layer = server.ws(koaPath(path), handle && (async (req, accept) => {
|
|
331
|
+
+ return handle(await accept(), { method: req.method, url: req.url, headers: Object.fromEntries(req.headers) });
|
|
332
|
+
+ }), options);
|
|
333
|
+
+ return Object.assign(layer, { close: /* @__PURE__ */ __name(() => layer.dispose(), "close") });
|
|
334
|
+
+ }, "ws")
|
|
335
|
+
+ };
|
|
336
|
+
+ const facade = new Proxy(server, {
|
|
337
|
+
+ get(target, prop) {
|
|
338
|
+
+ if (typeof prop === "symbol") return Reflect.get(target, prop);
|
|
339
|
+
+ if (Object.hasOwn(methods, prop)) return methods[prop];
|
|
340
|
+
+ if (prop === "selfUrl") return server.baseUrl;
|
|
341
|
+
+ if (prop === "config") return { ...server.config, selfUrl: server.baseUrl };
|
|
342
|
+
+ const value = Reflect.get(server, prop);
|
|
343
|
+
+ return typeof value === "function" ? value.bind(server) : value;
|
|
344
|
+
+ }
|
|
345
|
+
+ });
|
|
346
|
+
+ koaServers.set(server, facade);
|
|
347
|
+
+ return facade;
|
|
348
|
+
+}
|
|
349
|
+
import { defineProperty as defineProperty2 } from "cosmokit";
|
|
350
|
+
|
|
351
|
+
// src/internal.ts
|
|
352
|
+
@@ -121,9 +390,11 @@ var JsonForm;
|
|
353
|
+
})(JsonForm || (JsonForm = {}));
|
|
354
|
+
|
|
355
|
+
// src/index.ts
|
|
356
|
+
-import { HTTP } from "@cordisjs/plugin-http";
|
|
357
|
+
+import { Http } from "@cordisjs/plugin-http";
|
|
358
|
+
import h3 from "@satorijs/element";
|
|
359
|
+
export * from "cordis";
|
|
360
|
+
+// Restore the removed Schema/z exports from Schemastery.
|
|
361
|
+
+export { Schema4 as Schema, Schema4 as z, SatoriLoggerShim as Logger };
|
|
362
|
+
export * from "cosmokit";
|
|
363
|
+
import * as Universal from "@satorijs/protocol";
|
|
364
|
+
|
|
365
|
+
@@ -158,9 +429,8 @@ var Session = class {
|
|
366
|
+
this.event = event;
|
|
367
|
+
this.sn = this.id = ++bot.ctx.satori._sessionSeq;
|
|
368
|
+
defineProperty(this, "bot", bot);
|
|
369
|
+
- defineProperty(this, "app", bot.ctx.root);
|
|
370
|
+
- defineProperty(this, Context.current, bot.ctx);
|
|
371
|
+
- return Context.associate(this, "session");
|
|
372
|
+
+ defineProperty(this, "app", bot.ctx[Context4.root]);
|
|
373
|
+
+ return getTraceable(bot.ctx, this);
|
|
374
|
+
}
|
|
375
|
+
/** @deprecated */
|
|
376
|
+
get data() {
|
|
377
|
+
@@ -299,12 +569,12 @@ var Bot = class {
|
|
378
|
+
this.logger = ctx.logger(adapterName);
|
|
379
|
+
this.platform = adapterName;
|
|
380
|
+
this.features = Object.entries(Methods).filter(([, value]) => this[value.name]).map(([key]) => key);
|
|
381
|
+
- ctx.on("ready", async () => {
|
|
382
|
+
+ onReady(ctx, async () => {
|
|
383
|
+
await Promise.resolve();
|
|
384
|
+
this.dispatchLoginEvent("login-added");
|
|
385
|
+
return this.start();
|
|
386
|
+
});
|
|
387
|
+
- ctx.on("dispose", () => this.dispose());
|
|
388
|
+
+ ctx.effect(() => () => this.dispose());
|
|
389
|
+
ctx.on("interaction/button", (session) => {
|
|
390
|
+
const cb = this.callbacks[session.event.button.id];
|
|
391
|
+
if (cb) cb(session);
|
|
392
|
+
@@ -317,10 +587,12 @@ var Bot = class {
|
|
393
|
+
__name(this, "Bot");
|
|
394
|
+
}
|
|
395
|
+
static reusable = true;
|
|
396
|
+
+ static inject = ["satori"];
|
|
397
|
+
static MessageEncoder;
|
|
398
|
+
+ // No `property: "ctx"`: adapters read `bot.ctx.http` / `bot.ctx.server` from code another plugin
|
|
399
|
+
+ // calls, and those must resolve on the bot's own fiber, not the caller's.
|
|
400
|
+
[Service3.tracker] = {
|
|
401
|
+
- associate: "bot",
|
|
402
|
+
- property: "ctx"
|
|
403
|
+
+ associate: "bot"
|
|
404
|
+
};
|
|
405
|
+
sn;
|
|
406
|
+
user;
|
|
407
|
+
@@ -414,7 +686,7 @@ var Bot = class {
|
|
408
|
+
return new Session(this, event);
|
|
409
|
+
}
|
|
410
|
+
dispatch(session) {
|
|
411
|
+
- if (!this.ctx.lifecycle.isActive) return;
|
|
412
|
+
+ if (this.ctx.fiber.state >= 4) return;
|
|
413
|
+
let events = [session.type];
|
|
414
|
+
for (const aliases of eventAliases) {
|
|
415
|
+
if (aliases.includes(session.type)) {
|
|
416
|
+
@@ -468,7 +740,7 @@ var Bot = class {
|
|
417
|
+
delete this.ctx.satori._tempStore[id];
|
|
418
|
+
}
|
|
419
|
+
}, "dispose");
|
|
420
|
+
- const _dispose = this.ctx.on("dispose", dispose);
|
|
421
|
+
+ const _dispose = this.ctx.effect(() => dispose);
|
|
422
|
+
return ids.map((id) => this.getInternalUrl(`/_tmp/${id}`));
|
|
423
|
+
}
|
|
424
|
+
async supports(name, session = {}) {
|
|
425
|
+
@@ -530,7 +802,7 @@ defineAccessor(Bot.prototype, "userId", ["user", "id"]);
|
|
426
|
+
// src/adapter.ts
|
|
427
|
+
import { remove as remove2, Time } from "cosmokit";
|
|
428
|
+
import { Status as Status2 } from "@satorijs/protocol";
|
|
429
|
+
-import { z } from "cordis";
|
|
430
|
+
+import { default as z } from "schemastery";
|
|
431
|
+
var Adapter = class {
|
|
432
|
+
constructor(ctx) {
|
|
433
|
+
this.ctx = ctx;
|
|
434
|
+
@@ -548,10 +820,15 @@ var Adapter = class {
|
|
435
|
+
fork(ctx, bot) {
|
|
436
|
+
bot.adapter = this;
|
|
437
|
+
this.bots.push(bot);
|
|
438
|
+
- ctx.on("dispose", () => {
|
|
439
|
+
+ ctx.effect(() => () => {
|
|
440
|
+
remove2(this.bots, bot);
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
+ // Cordis 4 never calls `fork`; fork the bot this adapter was mounted with once it is constructed.
|
|
444
|
+
+ [Symbol.for("cordis.init")]() {
|
|
445
|
+
+ const bot = this.ctx.fiber.config;
|
|
446
|
+
+ if (bot instanceof Bot && !this.bots.includes(bot)) return this.fork(this.ctx, bot);
|
|
447
|
+
+ }
|
|
448
|
+
};
|
|
449
|
+
((Adapter2) => {
|
|
450
|
+
Adapter2.WsClientConfig = z.object({
|
|
451
|
+
@@ -614,7 +891,8 @@ var Adapter = class {
|
|
452
|
+
socket.addEventListener("open", () => {
|
|
453
|
+
retryCount = 0;
|
|
454
|
+
this.socket = socket;
|
|
455
|
+
- logger.info("connect to server: %c", url);
|
|
456
|
+
+ // Use %C under Cordis 4: %c consumes the value without rendering it.
|
|
457
|
+
+ logger.info("connect to server: %C", url);
|
|
458
|
+
this.accept(socket);
|
|
459
|
+
});
|
|
460
|
+
}, "connect");
|
|
461
|
+
@@ -724,7 +1002,7 @@ var MessageEncoder = class {
|
|
462
|
+
|
|
463
|
+
// src/index.ts
|
|
464
|
+
h3.warn = new Logger2("element").warn;
|
|
465
|
+
-HTTP.createConfig = /* @__PURE__ */ __name(function createConfig(endpoint) {
|
|
466
|
+
+Http.createConfig = /* @__PURE__ */ __name(function createConfig(endpoint) {
|
|
467
|
+
return z2.object({
|
|
468
|
+
endpoint: z2.string().role("link").description("要连接的服务器地址。").default(typeof endpoint === "string" ? endpoint : void 0).required(typeof endpoint === "boolean" ? endpoint : false),
|
|
469
|
+
headers: z2.dict(String).role("table").description("要附加的额外请求头。"),
|
|
470
|
+
@@ -737,7 +1015,7 @@ var SatoriContext = class extends Context4 {
|
|
471
|
+
}
|
|
472
|
+
constructor(config) {
|
|
473
|
+
super(config);
|
|
474
|
+
- this.provide("satori", void 0, true);
|
|
475
|
+
+ // The Service constructor registers itself; pre-providing would conflict.
|
|
476
|
+
this.plugin(Satori);
|
|
477
|
+
}
|
|
478
|
+
};
|
|
479
|
+
@@ -799,7 +1077,37 @@ var Satori = class extends Service4 {
|
|
480
|
+
_loginSeq = 0;
|
|
481
|
+
_sessionSeq = 0;
|
|
482
|
+
constructor(ctx) {
|
|
483
|
+
- super(ctx);
|
|
484
|
+
+ super(ctx, "satori");
|
|
485
|
+
+ // Cordis 4 dropped the ready and dispose events adapters still listen to; map both onto the fiber.
|
|
486
|
+
+ ctx.on("internal/listener", function(name, listener) {
|
|
487
|
+
+ if (name === "ready") return onReady(this, listener);
|
|
488
|
+
+ if (name !== "dispose") return;
|
|
489
|
+
+ let active = true;
|
|
490
|
+
+ const dispose = this.effect(() => () => active && listener());
|
|
491
|
+
+ return () => {
|
|
492
|
+
+ active = false;
|
|
493
|
+
+ dispose();
|
|
494
|
+
+ };
|
|
495
|
+
+ });
|
|
496
|
+
+ // Adapters were written for Koishi 4, which never enforced inject: adapter-onebot calls
|
|
497
|
+
+ // `bot.ctx.setTimeout` without injecting `timer`, so their lookups never throw here. Prepended so
|
|
498
|
+
+ // a host's own global lookup cannot hand an adapter the raw services first.
|
|
499
|
+
+ ctx.on("internal/get", (target, name, error, next) => {
|
|
500
|
+
+ return isSatoriFiber(target.fiber) || isOptional(target.fiber, name) ? target.get(name) : next();
|
|
501
|
+
+ }, { prepend: true });
|
|
502
|
+
+ // `ctx.get(name)` skips internal/get, and adapter-telegram reads `ctx.get("server")`.
|
|
503
|
+
+ const reflect = ctx.reflect.constructor.prototype;
|
|
504
|
+
+ if (!Object.hasOwn(reflect, kOptional)) {
|
|
505
|
+
+ Object.defineProperty(reflect, kOptional, { value: true });
|
|
506
|
+
+ const get = reflect.get;
|
|
507
|
+
+ reflect.get = function(name, strict) {
|
|
508
|
+
+ const value = get.call(this, name, strict);
|
|
509
|
+
+ if (value === void 0 || !isSatoriFiber(this.ctx.fiber)) return value;
|
|
510
|
+
+ if (name === "http") return toQuester(value);
|
|
511
|
+
+ if (name === "server") return toKoaServer(value);
|
|
512
|
+
+ return value;
|
|
513
|
+
+ };
|
|
514
|
+
+ }
|
|
515
|
+
ctx.mixin("satori", ["bots", "component"]);
|
|
516
|
+
defineProperty2(this.bots, Service4.tracker, {});
|
|
517
|
+
const self = this;
|
|
518
|
+
@@ -871,6 +1179,8 @@ var Satori = class extends Service4 {
|
|
519
|
+
const result = await component(attrs, children, session);
|
|
520
|
+
return session.transform(h3.normalize(result));
|
|
521
|
+
}, "render");
|
|
522
|
+
+ // Cordis 4 `set` requires a declared property; provide it first, fiber-bound.
|
|
523
|
+
+ this.ctx.provide("component:" + name, render);
|
|
524
|
+
return this.ctx.set("component:" + name, render);
|
|
525
|
+
}
|
|
526
|
+
defineInternalRoute(path, callback) {
|
|
527
|
+
@@ -900,13 +1210,14 @@ export {
|
|
528
|
+
Bot,
|
|
529
|
+
SatoriContext as Context,
|
|
530
|
+
h3 as Element,
|
|
531
|
+
- HTTP,
|
|
532
|
+
+ Http,
|
|
533
|
+
+ Http as HTTP,
|
|
534
|
+
InternalRouter,
|
|
535
|
+
JsonForm,
|
|
536
|
+
MessageEncoder,
|
|
537
|
+
MessageEncoder as Messenger,
|
|
538
|
+
MessageEncoder as Modulator,
|
|
539
|
+
- HTTP as Quester,
|
|
540
|
+
+ Http as Quester,
|
|
541
|
+
Satori,
|
|
542
|
+
Session,
|
|
543
|
+
Universal,
|
|
544
|
+
diff --git a/package.json b/package.json
|
|
545
|
+
index d3af7e0..d609a16 100644
|
|
546
|
+
--- a/package.json
|
|
547
|
+
+++ b/package.json
|
|
548
|
+
@@ -3,13 +3,13 @@
|
|
549
|
+
"description": "Core components of Satorijs",
|
|
550
|
+
"version": "4.6.1",
|
|
551
|
+
"type": "module",
|
|
552
|
+
- "main": "lib/index.cjs",
|
|
553
|
+
+ "main": "lib/index.mjs",
|
|
554
|
+
"module": "lib/index.mjs",
|
|
555
|
+
"types": "lib/index.d.ts",
|
|
556
|
+
"exports": {
|
|
557
|
+
".": {
|
|
558
|
+
"types": "./lib/index.d.ts",
|
|
559
|
+
- "require": "./lib/index.cjs",
|
|
560
|
+
+ "require": "./lib/index.mjs",
|
|
561
|
+
"import": "./lib/index.mjs"
|
|
562
|
+
},
|
|
563
|
+
"./src/*": "./src/*",
|
|
564
|
+
@@ -54,14 +54,15 @@
|
|
565
|
+
}
|
|
566
|
+
},
|
|
567
|
+
"peerDependencies": {
|
|
568
|
+
- "cordis": "^3.18.1"
|
|
569
|
+
+ "cordis": "^4.0.0-rc.7",
|
|
570
|
+
+ "@cordisjs/plugin-http": "^1.5.1"
|
|
571
|
+
},
|
|
572
|
+
"dependencies": {
|
|
573
|
+
- "@cordisjs/plugin-http": "^0.6.3",
|
|
574
|
+
"@satorijs/element": "^3.2.0",
|
|
575
|
+
"@satorijs/protocol": "^1.7.0",
|
|
576
|
+
- "cordis": "^3.18.1",
|
|
577
|
+
+ "cordis": "^4.0.0-rc.8",
|
|
578
|
+
"cosmokit": "^1.8.1",
|
|
579
|
+
- "path-to-regexp": "^8.2.0"
|
|
580
|
+
+ "path-to-regexp": "^8.2.0",
|
|
581
|
+
+ "schemastery": "^3.18.0"
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|