@fraqjs/fraq 0.6.4 → 0.8.0

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,15 @@ const ctx = Context.fromUrl('<替换成你的 Milky 协议端地址>', {
27
27
  // accessToken: '<如果你的 Milky 协议端配置了 token,请在这里添加>',
28
28
  });
29
29
 
30
- ctx.router.command(
31
- 'echo',
32
- {
30
+ ctx.router.command({
31
+ name: 'echo',
32
+ pattern: {
33
33
  content: param.greedy(),
34
34
  },
35
- (session, { content }) => {
35
+ execute(session, { content }) {
36
36
  session.reply(msg`You said: ${content}`);
37
37
  },
38
- );
38
+ });
39
39
 
40
40
  ctx.start();
41
41
  ```
package/dist/index.d.mts CHANGED
@@ -5505,21 +5505,21 @@ declare namespace param {
5505
5505
  //#region src/routing/router.d.ts
5506
5506
  type Pattern = Record<string, Parameter<any>>;
5507
5507
  type ParamsOf<P extends Pattern> = { [K in keyof P]: P[K] extends Parameter<infer T> ? T : never };
5508
- type Handler<P extends Pattern> = (session: Session, params: ParamsOf<P>) => void | Promise<void>;
5508
+ type Executor<P extends Pattern> = (session: Session, params: ParamsOf<P>) => void | Promise<void>;
5509
5509
  type SessionPredicate = (session: Session) => boolean;
5510
- interface Command {
5510
+ interface Command<P extends Pattern> {
5511
5511
  name: string;
5512
- pattern: Pattern;
5513
- handler: (session: Session, params: any) => void | Promise<void>;
5512
+ pattern: P;
5513
+ execute: Executor<P>;
5514
5514
  }
5515
- interface RawPattern {
5516
- pattern: Pattern;
5517
- handler: (session: Session, params: any) => void | Promise<void>;
5515
+ interface RawPattern<P extends Pattern> {
5516
+ pattern: P;
5517
+ execute: Executor<P>;
5518
5518
  }
5519
5519
  type RouteEntry =
5520
5520
  | {
5521
5521
  type: 'command';
5522
- command: Command;
5522
+ command: Command<Pattern>;
5523
5523
  }
5524
5524
  | {
5525
5525
  type: 'group';
@@ -5533,30 +5533,30 @@ type RouteEntry =
5533
5533
  }
5534
5534
  | {
5535
5535
  type: 'rawPattern';
5536
- rawPattern: RawPattern;
5536
+ rawPattern: RawPattern<Pattern>;
5537
5537
  };
5538
5538
  type RouteBranch =
5539
5539
  | {
5540
5540
  type: 'command';
5541
5541
  path: string[];
5542
- command: Command;
5542
+ command: Command<Pattern>;
5543
5543
  }
5544
5544
  | {
5545
5545
  type: 'rawPattern';
5546
5546
  path: string[];
5547
- rawPattern: RawPattern;
5547
+ rawPattern: RawPattern<Pattern>;
5548
5548
  };
5549
5549
  type RouteMatchResult =
5550
5550
  | {
5551
5551
  type: 'command';
5552
5552
  path: string[];
5553
- command: Command;
5553
+ command: Command<Pattern>;
5554
5554
  params: any;
5555
5555
  }
5556
5556
  | {
5557
5557
  type: 'rawPattern';
5558
5558
  path: string[];
5559
- rawPattern: RawPattern;
5559
+ rawPattern: RawPattern<Pattern>;
5560
5560
  params: any;
5561
5561
  };
5562
5562
  interface Session {
@@ -5566,8 +5566,8 @@ interface Session {
5566
5566
  declare class Router {
5567
5567
  private readonly entries;
5568
5568
  private readonly groups;
5569
- command<P extends Pattern>(name: string, pattern: P, handler: Handler<P>): this;
5570
- rawPattern<P extends Pattern>(pattern: P, handler: Handler<P>): this;
5569
+ command<P extends Pattern>(command: Command<P>): this;
5570
+ rawPattern<P extends Pattern>(rawPattern: RawPattern<P>): this;
5571
5571
  group(name: string): Router;
5572
5572
  filter(predicate: SessionPredicate): Router;
5573
5573
  routes(): RouteEntry[];
@@ -5679,6 +5679,7 @@ declare class Context {
5679
5679
  private readonly services;
5680
5680
  private readonly subContexts;
5681
5681
  private readonly parentEventForwarder?;
5682
+ private readonly timers;
5682
5683
  private readonly initialReconnectDelayMs;
5683
5684
  private readonly maxReconnectDelayMs;
5684
5685
  private readonly logHandler?;
@@ -5700,12 +5701,17 @@ declare class Context {
5700
5701
  tryResolve<T extends object>(service: ServiceClass<T>): T | undefined;
5701
5702
  isProvided<T extends object>(service: ServiceClass<T>): boolean;
5702
5703
  fork(name: string, filter?: Filter): Context;
5704
+ timeout(delayMs: number, callback: () => void | Promise<void>): NodeJS.Timeout;
5705
+ interval(intervalMs: number, callback: () => void | Promise<void>): NodeJS.Timeout;
5703
5706
  createSession(message: IncomingMessage): Session;
5704
5707
  start(): Promise<void>;
5705
5708
  stop(): Promise<void>;
5706
5709
  private startInternal;
5707
5710
  private getState;
5708
5711
  private stopInternal;
5712
+ private assertCanScheduleTimer;
5713
+ private runTimerCallback;
5714
+ private clearTimers;
5709
5715
  private stopEventStream;
5710
5716
  private acceptsParentEvent;
5711
5717
  private applyPlugins;
@@ -5775,8 +5781,8 @@ export {
5775
5781
  ContextUrlOptions,
5776
5782
  Disposable,
5777
5783
  EventMap,
5784
+ Executor,
5778
5785
  Filter,
5779
- Handler,
5780
5786
  Injection,
5781
5787
  LogHandler,
5782
5788
  LogLevel,
package/dist/index.mjs CHANGED
@@ -164,25 +164,17 @@ var Tokenizer = class {
164
164
  var Router = class Router {
165
165
  entries = [];
166
166
  groups = /* @__PURE__ */ new Map();
167
- command(name, pattern, handler) {
168
- const command = {
169
- name,
170
- pattern,
171
- handler
172
- };
167
+ command(command) {
173
168
  this.entries.push({
174
169
  type: "command",
175
170
  command
176
171
  });
177
172
  return this;
178
173
  }
179
- rawPattern(pattern, handler) {
180
- if (Object.keys(pattern).length === 0) throw new Error("Raw pattern must have at least one parameter.");
181
- if (pattern[Object.keys(pattern)[0]].capturer.typeInstruction.type === "literal") throw new Error("The first parameter of a raw pattern cannot be a literal, as it would conflict with command patterns.");
182
- const rawPattern = {
183
- pattern,
184
- handler
185
- };
174
+ rawPattern(rawPattern) {
175
+ if (Object.keys(rawPattern.pattern).length === 0) throw new Error("Raw pattern must have at least one parameter.");
176
+ const firstKey = Object.keys(rawPattern.pattern)[0];
177
+ if (rawPattern.pattern[firstKey].capturer.typeInstruction.type === "literal") throw new Error("The first parameter of a raw pattern cannot be a literal, as it would conflict with command patterns.");
186
178
  this.entries.push({
187
179
  type: "rawPattern",
188
180
  rawPattern
@@ -226,10 +218,10 @@ var Router = class Router {
226
218
  if (match === void 0) return false;
227
219
  switch (match.type) {
228
220
  case "command":
229
- await match.command.handler(session, match.params);
221
+ await match.command.execute(session, match.params);
230
222
  break;
231
223
  case "rawPattern":
232
- await match.rawPattern.handler(session, match.params);
224
+ await match.rawPattern.execute(session, match.params);
233
225
  break;
234
226
  }
235
227
  return true;
@@ -382,6 +374,7 @@ var Context = class Context {
382
374
  services = /* @__PURE__ */ new Map();
383
375
  subContexts = [];
384
376
  parentEventForwarder;
377
+ timers = /* @__PURE__ */ new Set();
385
378
  initialReconnectDelayMs;
386
379
  maxReconnectDelayMs;
387
380
  logHandler;
@@ -466,6 +459,23 @@ and implement the dispose method to clean up resources when the context stops.
466
459
  this.subContexts.push(subContext);
467
460
  return subContext;
468
461
  }
462
+ timeout(delayMs, callback) {
463
+ this.assertCanScheduleTimer();
464
+ const timeout = setTimeout(() => {
465
+ this.timers.delete(timeout);
466
+ this.runTimerCallback(callback);
467
+ }, delayMs);
468
+ this.timers.add(timeout);
469
+ return timeout;
470
+ }
471
+ interval(intervalMs, callback) {
472
+ this.assertCanScheduleTimer();
473
+ const interval = setInterval(() => {
474
+ this.runTimerCallback(callback);
475
+ }, intervalMs);
476
+ this.timers.add(interval);
477
+ return interval;
478
+ }
469
479
  createSession(message) {
470
480
  return {
471
481
  raw: message,
@@ -508,10 +518,10 @@ and implement the dispose method to clean up resources when the context stops.
508
518
  }
509
519
  }
510
520
  async stop() {
511
- if (this.state === "idle" || this.state === "stopped") return;
521
+ if (this.state === "idle" && this.timers.size === 0 || this.state === "stopped") return;
512
522
  if (this.state === "starting") await this.startPromise;
513
523
  const stateAfterStart = this.getState();
514
- if (stateAfterStart === "idle" || stateAfterStart === "stopped") return;
524
+ if (stateAfterStart === "idle" && this.timers.size === 0 || stateAfterStart === "stopped") return;
515
525
  if (stateAfterStart === "stopping") {
516
526
  await this.stopPromise;
517
527
  return;
@@ -543,12 +553,13 @@ and implement the dispose method to clean up resources when the context stops.
543
553
  }
544
554
  async stopInternal() {
545
555
  const errors = [];
546
- await this.stopEventStream(errors);
556
+ this.clearTimers();
547
557
  for (const subContext of [...this.subContexts].reverse()) try {
548
558
  await subContext.stop();
549
559
  } catch (error) {
550
560
  errors.push(error);
551
561
  }
562
+ await this.stopEventStream(errors);
552
563
  if (this.parent && this.parentEventForwarder) this.parent.eventBus.off("*", this.parentEventForwarder);
553
564
  for (const service of [...this.services.values()].reverse()) {
554
565
  if (!isDisposable(service)) continue;
@@ -561,6 +572,22 @@ and implement the dispose method to clean up resources when the context stops.
561
572
  if (errors.length === 1) throw errors[0];
562
573
  if (errors.length > 1) throw new AggregateError(errors, `Context "${this.name}" failed to stop cleanly.`);
563
574
  }
575
+ assertCanScheduleTimer() {
576
+ if (this.state === "stopping") throw new Error(`Context "${this.name}" cannot schedule timers while it is stopping.`);
577
+ if (this.state === "stopped") throw new Error(`Context "${this.name}" cannot schedule timers after it has stopped.`);
578
+ }
579
+ async runTimerCallback(callback) {
580
+ if (this.state === "stopping" || this.state === "stopped") return;
581
+ try {
582
+ await callback();
583
+ } catch (error) {
584
+ this.logger.error("Error handling timer callback", error);
585
+ }
586
+ }
587
+ clearTimers() {
588
+ for (const timer of this.timers) clearTimeout(timer);
589
+ this.timers.clear();
590
+ }
564
591
  async stopEventStream(errors) {
565
592
  this.resolveReconnectDelay();
566
593
  const subscription = this.eventSubscription;
@@ -685,6 +712,10 @@ and implement the dispose method to clean up resources when the context stops.
685
712
  }
686
713
  createProxyContextForPlugin(plugin) {
687
714
  const proxyLogger = new Logger((message) => this.logHandler?.(message), plugin.name);
715
+ const proxyProvide = (service, instance) => {
716
+ this.provide(service, instance);
717
+ this.logger.debug(`Plugin ${plugin.name} provided ${service.name}`);
718
+ };
688
719
  let proxyInjections;
689
720
  if (plugin.inject) {
690
721
  proxyInjections = {};
@@ -694,10 +725,11 @@ and implement the dispose method to clean up resources when the context stops.
694
725
  proxyInjections ??= {};
695
726
  for (const [key, service] of Object.entries(plugin.optionalInject)) proxyInjections[key] = this.tryResolve(service);
696
727
  }
697
- return new Proxy(this, { get(target, prop) {
728
+ return new Proxy(this, { get(target, prop, receiver) {
698
729
  if (prop === "logger") return proxyLogger;
730
+ else if (prop === "provide") return proxyProvide;
699
731
  else if (proxyInjections && prop in proxyInjections) return proxyInjections[prop];
700
- else return target[prop];
732
+ else return Reflect.get(target, prop, receiver);
701
733
  } });
702
734
  }
703
735
  async runEventStream() {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@fraqjs/fraq",
3
3
  "type": "module",
4
- "version": "0.6.4",
4
+ "version": "0.8.0",
5
5
  "description": "Milky Bot framework",
6
6
  "files": [
7
7
  "dist"