@fraqjs/fraq 0.8.2 → 0.9.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/README.md CHANGED
@@ -27,15 +27,12 @@ const ctx = Context.fromUrl('<替换成你的 Milky 协议端地址>', {
27
27
  // accessToken: '<如果你的 Milky 协议端配置了 token,请在这里添加>',
28
28
  });
29
29
 
30
- ctx.router.command({
31
- name: 'echo',
32
- pattern: {
33
- content: param.greedy(),
34
- },
35
- execute(session, { content }) {
30
+ ctx.router
31
+ .command('echo')
32
+ .arg('content', param.greedy())
33
+ .execute((session, { content }) => {
36
34
  session.reply(msg`You said: ${content}`);
37
- },
38
- });
35
+ });
39
36
 
40
37
  ctx.start();
41
38
  ```
package/dist/index.d.mts CHANGED
@@ -5506,20 +5506,54 @@ declare namespace param {
5506
5506
  >;
5507
5507
  }
5508
5508
  //#endregion
5509
- //#region src/routing/router.d.ts
5509
+ //#region src/routing/command.d.ts
5510
5510
  type Pattern = Record<string, Parameter<any>>;
5511
5511
  type ParamsOf<P extends Pattern> = { [K in keyof P]: P[K] extends Parameter<infer T> ? T : never };
5512
5512
  type Executor<P extends Pattern> = (session: Session, params: ParamsOf<P>) => void | Promise<void>;
5513
- type SessionPredicate = (session: Session) => boolean;
5514
5513
  interface Command<P extends Pattern> {
5515
5514
  name: string;
5516
5515
  pattern: P;
5517
5516
  execute: Executor<P>;
5517
+ description?: string;
5518
+ aliases?: string[];
5519
+ hidden?: boolean;
5518
5520
  }
5519
5521
  interface RawPattern<P extends Pattern> {
5520
5522
  pattern: P;
5521
5523
  execute: Executor<P>;
5522
5524
  }
5525
+ interface Session {
5526
+ raw: IncomingMessage;
5527
+ reply(segments: OutgoingSegment_ZodInput[]): Promise<void>;
5528
+ }
5529
+ declare class CommandBuilder<P extends Pattern = {}, S = Command<P>> {
5530
+ readonly name: string;
5531
+ private readonly sink;
5532
+ private readonly pattern;
5533
+ private executor?;
5534
+ private description?;
5535
+ private aliases?;
5536
+ private hidden?;
5537
+ constructor(name: string, sink?: (command: Command<P>) => S);
5538
+ arg<K extends string, T>(
5539
+ key: K,
5540
+ parameter: Parameter<T>,
5541
+ ): CommandBuilder<
5542
+ P & { [K2 in K]: Parameter<T> },
5543
+ S extends Command<P>
5544
+ ? Command<P & { [K2 in K]: Parameter<T> }>
5545
+ : S extends RawPattern<P>
5546
+ ? RawPattern<P & { [K2 in K]: Parameter<T> }>
5547
+ : S
5548
+ >;
5549
+ describe(description: string): CommandBuilder<P, S>;
5550
+ alias(...aliases: string[]): CommandBuilder<P, S>;
5551
+ hide(): CommandBuilder<P, S>;
5552
+ execute(executor: Executor<P>): S;
5553
+ }
5554
+ //#endregion
5555
+ //#region src/routing/router.d.ts
5556
+ type SessionPredicate = (session: Session) => boolean;
5523
5557
  type RouteEntry =
5524
5558
  | {
5525
5559
  type: 'command';
@@ -5563,18 +5597,17 @@ type RouteMatchResult =
5563
5597
  rawPattern: RawPattern<Pattern>;
5564
5598
  params: any;
5565
5599
  };
5566
- interface Session {
5567
- raw: IncomingMessage;
5568
- reply(segments: OutgoingSegment_ZodInput[]): Promise<void>;
5569
- }
5570
5600
  declare class Router {
5571
5601
  private readonly entries;
5572
5602
  private readonly groups;
5603
+ command(name: string): CommandBuilder;
5573
5604
  command<P extends Pattern>(command: Command<P>): this;
5605
+ rawPattern(): CommandBuilder<{}, RawPattern<{}>>;
5574
5606
  rawPattern<P extends Pattern>(rawPattern: RawPattern<P>): this;
5575
5607
  group(name: string): Router;
5576
5608
  filter(predicate: SessionPredicate): Router;
5577
5609
  routes(): RouteEntry[];
5610
+ aliasesOf(name: string): string[];
5578
5611
  branches(session: Session): RouteBranch[];
5579
5612
  match(session: Session, message: IncomingMessage): RouteMatchResult | undefined;
5580
5613
  dispatch(session: Session, message: IncomingMessage): Promise<boolean>;
@@ -5584,6 +5617,7 @@ declare class Router {
5584
5617
  private matchGroup;
5585
5618
  private matchRawPattern;
5586
5619
  private branchesFrom;
5620
+ private resolveAliasConflicts;
5587
5621
  private validatePattern;
5588
5622
  private capturePattern;
5589
5623
  }
@@ -5784,6 +5818,7 @@ export {
5784
5818
  ApiEndpoints,
5785
5819
  Capturer,
5786
5820
  Command,
5821
+ CommandBuilder,
5787
5822
  Context,
5788
5823
  ContextOptions,
5789
5824
  ContextUrlOptions,
package/dist/index.mjs CHANGED
@@ -64,6 +64,50 @@ function createMilkyClient(...params) {
64
64
  } });
65
65
  }
66
66
  //#endregion
67
+ //#region src/routing/command.ts
68
+ var CommandBuilder = class {
69
+ name;
70
+ sink;
71
+ pattern = {};
72
+ executor;
73
+ description;
74
+ aliases;
75
+ hidden;
76
+ constructor(name, sink = (command) => command) {
77
+ this.name = name;
78
+ this.sink = sink;
79
+ }
80
+ arg(key, parameter) {
81
+ this.pattern[key] = parameter;
82
+ return this;
83
+ }
84
+ describe(description) {
85
+ this.description = description;
86
+ return this;
87
+ }
88
+ alias(...aliases) {
89
+ if (!this.aliases) this.aliases = [];
90
+ this.aliases.push(...aliases);
91
+ return this;
92
+ }
93
+ hide() {
94
+ this.hidden = true;
95
+ return this;
96
+ }
97
+ execute(executor) {
98
+ this.executor = executor;
99
+ const command = {
100
+ name: this.name,
101
+ pattern: this.pattern,
102
+ execute: this.executor
103
+ };
104
+ if (this.description !== void 0) command.description = this.description;
105
+ if (this.aliases !== void 0) command.aliases = this.aliases;
106
+ if (this.hidden !== void 0) command.hidden = this.hidden;
107
+ return this.sink(command);
108
+ }
109
+ };
110
+ //#endregion
67
111
  //#region src/routing/tokenizer.ts
68
112
  var Tokenizer = class {
69
113
  segments;
@@ -192,7 +236,12 @@ var Router = class Router {
192
236
  entries = [];
193
237
  groups = /* @__PURE__ */ new Map();
194
238
  command(command) {
239
+ if (typeof command === "string") return new CommandBuilder(command, (builtCommand) => {
240
+ this.command(builtCommand);
241
+ return builtCommand;
242
+ });
195
243
  this.validatePattern(command.pattern);
244
+ this.resolveAliasConflicts(command);
196
245
  this.entries.push({
197
246
  type: "command",
198
247
  command
@@ -200,6 +249,10 @@ var Router = class Router {
200
249
  return this;
201
250
  }
202
251
  rawPattern(rawPattern) {
252
+ if (!rawPattern) return new CommandBuilder("", (builtCommand) => {
253
+ this.rawPattern(builtCommand);
254
+ return builtCommand;
255
+ });
203
256
  this.validatePattern(rawPattern.pattern, { rawPattern: true });
204
257
  this.entries.push({
205
258
  type: "rawPattern",
@@ -232,6 +285,10 @@ var Router = class Router {
232
285
  routes() {
233
286
  return this.entries;
234
287
  }
288
+ aliasesOf(name) {
289
+ for (const entry of this.entries) if (entry.type === "command" && entry.command.name === name) return entry.command.aliases ? [...entry.command.aliases] : [];
290
+ return [];
291
+ }
235
292
  branches(session) {
236
293
  return this.branchesFrom(session, []);
237
294
  }
@@ -273,7 +330,7 @@ var Router = class Router {
273
330
  }
274
331
  matchCommand(command, tokenizer, path) {
275
332
  const token = tokenizer.peek();
276
- if (typeof token !== "string" || token !== command.name) return;
333
+ if (typeof token !== "string" || token !== command.name && !command.aliases?.includes(token)) return;
277
334
  tokenizer.next();
278
335
  const params = this.capturePattern(command.pattern, tokenizer);
279
336
  if (params === void 0 || tokenizer.hasNext()) return;
@@ -304,7 +361,7 @@ var Router = class Router {
304
361
  const branches = [];
305
362
  for (const entry of this.entries) switch (entry.type) {
306
363
  case "command":
307
- branches.push({
364
+ if (!entry.command.hidden) branches.push({
308
365
  type: "command",
309
366
  path: [...path],
310
367
  command: entry.command
@@ -326,6 +383,27 @@ var Router = class Router {
326
383
  }
327
384
  return branches;
328
385
  }
386
+ resolveAliasConflicts(command) {
387
+ for (const entry of this.entries) {
388
+ if (entry.type !== "command") continue;
389
+ const existing = entry.command;
390
+ if (existing.aliases?.includes(command.name)) {
391
+ existing.aliases = existing.aliases.filter((a) => a !== command.name);
392
+ console.warn(`Command "${command.name}" conflicts with alias of existing command "${existing.name}". The alias "${command.name}" has been removed from "${existing.name}".`);
393
+ }
394
+ if (command.aliases) {
395
+ const dropped = [];
396
+ for (const alias of command.aliases) if (existing.name === alias) {
397
+ dropped.push(alias);
398
+ console.warn(`Alias "${alias}" of command "${command.name}" conflicts with existing command name "${existing.name}". The alias has been dropped.`);
399
+ } else if (existing.aliases?.includes(alias)) {
400
+ existing.aliases = existing.aliases.filter((a) => a !== alias);
401
+ console.warn(`Alias "${alias}" of command "${command.name}" conflicts with alias of existing command "${existing.name}". The alias has been removed from "${existing.name}".`);
402
+ }
403
+ if (dropped.length > 0) command.aliases = command.aliases.filter((a) => !dropped.includes(a));
404
+ }
405
+ }
406
+ }
329
407
  validatePattern(pattern, options) {
330
408
  const entries = Object.entries(pattern);
331
409
  if (options?.rawPattern && entries.length === 0) throw new Error("Raw pattern must have at least one parameter.");
@@ -648,7 +726,15 @@ and implement the dispose method to clean up resources when the context stops.
648
726
  for (const installedPlugin of sortedPlugins) {
649
727
  const { plugin, args } = installedPlugin;
650
728
  const providedBeforeApply = new Set(this.services.keys());
651
- this.logger.debug(`Applying plugin ${plugin.name}`);
729
+ let debugMessage = `Applying plugin ${plugin.name}`;
730
+ const requiredServices = [];
731
+ const providedServices = [];
732
+ if (plugin.requires) for (const service of plugin.requires) requiredServices.push(service.name);
733
+ if (plugin.optionalRequires) for (const service of plugin.optionalRequires) requiredServices.push(`${service.name}?`);
734
+ if (plugin.provides) for (const service of plugin.provides) providedServices.push(service.name);
735
+ if (requiredServices.length > 0) debugMessage += `, requires: [${requiredServices.join(", ")}]`;
736
+ if (providedServices.length > 0) debugMessage += `, provides: [${providedServices.join(", ")}]`;
737
+ this.logger.debug(debugMessage);
652
738
  await plugin.apply(this.getPluginContext(installedPlugin), ...args);
653
739
  for (const service of plugin.provides ?? []) if (!this.services.has(service) || providedBeforeApply.has(service)) throw new Error(`${plugin.name} declares service ${service.name} but did not provide it.`);
654
740
  }
@@ -747,10 +833,6 @@ and implement the dispose method to clean up resources when the context stops.
747
833
  }
748
834
  createProxyContextForPlugin(plugin) {
749
835
  const proxyLogger = new Logger((message) => this.logHandler?.(message), plugin.name);
750
- const proxyProvide = (service, instance) => {
751
- this.provide(service, instance);
752
- this.logger.debug(`Plugin ${plugin.name} provided ${service.name}`);
753
- };
754
836
  let proxyInjections;
755
837
  if (plugin.inject) {
756
838
  proxyInjections = {};
@@ -762,7 +844,6 @@ and implement the dispose method to clean up resources when the context stops.
762
844
  }
763
845
  return new Proxy(this, { get(target, prop, receiver) {
764
846
  if (prop === "logger") return proxyLogger;
765
- else if (prop === "provide") return proxyProvide;
766
847
  else if (proxyInjections && prop in proxyInjections) return proxyInjections[prop];
767
848
  else return Reflect.get(target, prop, receiver);
768
849
  } });
@@ -1230,4 +1311,4 @@ let param;
1230
1311
  _param.segment = segment;
1231
1312
  })(param || (param = {}));
1232
1313
  //#endregion
1233
- export { Context, Logger, Parameter, Router, combineLogHandlers, createMilkyClient, definePlugin, filter, implementsESNextDisposable, isDisposable, milkyPackageVersion, milkyVersion, msg, param, seg };
1314
+ export { CommandBuilder, Context, Logger, Parameter, Router, combineLogHandlers, createMilkyClient, definePlugin, filter, implementsESNextDisposable, isDisposable, milkyPackageVersion, milkyVersion, msg, param, seg };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@fraqjs/fraq",
3
3
  "type": "module",
4
- "version": "0.8.2",
4
+ "version": "0.9.1",
5
5
  "description": "Milky Bot framework",
6
6
  "files": [
7
7
  "dist"