@fraqjs/fraq 0.11.0 → 0.12.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
@@ -50,4 +50,4 @@ npx tsx index.ts
50
50
 
51
51
  上面的例子只是 Fraq 的最小使用方式,你还可以继续了解如何构造更丰富的消息、定义更复杂的指令参数、编写可复用的插件等。
52
52
 
53
- 完整文档请见 [fraq.ntqqrev.org](https://fraq.ntqqrev.org/)。
53
+ 完整文档请见 [fraq.dev](https://fraq.dev/)。
package/dist/index.d.mts CHANGED
@@ -5377,62 +5377,6 @@ interface ApiEndpoints$1 {
5377
5377
  };
5378
5378
  }
5379
5379
  //#endregion
5380
- //#region src/protocol/endpoint.d.ts
5381
- type RequiredKeys<T> = { [K in keyof T]-?: {} extends Pick<T, K> ? never : K }[keyof T];
5382
- type AllOptional<T> = RequiredKeys<T> extends never ? true : false;
5383
- type RawApiEndpoint<E extends keyof ApiEndpoints$1> = {
5384
- request: ApiEndpoints$1[E]['request_ZodInput'] extends null ? null : ApiEndpoints$1[E]['request_ZodInput'];
5385
- response: ApiEndpoints$1[E]['response'] extends null ? null : ApiEndpoints$1[E]['response'];
5386
- };
5387
- type ApiEndpointFunction<E extends keyof ApiEndpoints$1> = RawApiEndpoint<E>['request'] extends null
5388
- ? () => Promise<RawApiEndpoint<E>['response']>
5389
- : AllOptional<RawApiEndpoint<E>['request']> extends true
5390
- ? (params?: RawApiEndpoint<E>['request']) => Promise<RawApiEndpoint<E>['response']>
5391
- : (params: RawApiEndpoint<E>['request']) => Promise<RawApiEndpoint<E>['response']>;
5392
- type ApiEndpoints = { [E in keyof ApiEndpoints$1]: ApiEndpointFunction<E> };
5393
- type EventMap = {
5394
- [K in Event['event_type']]: Extract<
5395
- Event,
5396
- {
5397
- event_type: K;
5398
- }
5399
- >;
5400
- };
5401
- //#endregion
5402
- //#region src/protocol/client.d.ts
5403
- interface MilkyClient extends ApiEndpoints {}
5404
- declare class MilkyClientBase {
5405
- readonly baseUrl: string;
5406
- readonly wsBaseUrl: string;
5407
- private readonly accessToken?;
5408
- private readonly baseHeaders;
5409
- constructor(
5410
- baseUrl: string | URL,
5411
- options?: {
5412
- accessToken?: string;
5413
- },
5414
- );
5415
- callApi(endpoint: string, params?: unknown): Promise<unknown>;
5416
- }
5417
- declare function createMilkyClient(...params: ConstructorParameters<typeof MilkyClientBase>): MilkyClient;
5418
- //#endregion
5419
- //#region src/protocol/event.d.ts
5420
- interface MilkyEventSubscription {
5421
- closed: Promise<void>;
5422
- stop(): void | Promise<void>;
5423
- }
5424
- interface MilkyEventSource {
5425
- readonly name?: string;
5426
- start(onEvent: (event: Event) => void | Promise<void>): Promise<MilkyEventSubscription>;
5427
- }
5428
- interface MilkyWebSocketEventSourceOptions {
5429
- accessToken?: string;
5430
- }
5431
- declare function createMilkyWebSocketEventSource(
5432
- baseUrl: string | URL,
5433
- options?: MilkyWebSocketEventSourceOptions,
5434
- ): MilkyEventSource;
5435
- //#endregion
5436
5380
  //#region src/routing/tokenizer.d.ts
5437
5381
  type Token =
5438
5382
  | string
@@ -5460,6 +5404,8 @@ declare class Tokenizer {
5460
5404
  greedy(): string;
5461
5405
  isCatchAllAvailable(): boolean;
5462
5406
  catchAll(): IncomingSegment[];
5407
+ consumeMention(userId: number): boolean;
5408
+ consumeTextPrefix(prefix: string): boolean;
5463
5409
  private findNextPosition;
5464
5410
  private findTextTokenStart;
5465
5411
  private readTextToken;
@@ -5526,6 +5472,9 @@ declare namespace param {
5526
5472
  type Pattern = Record<string, Parameter<any>>;
5527
5473
  type ParamsOf<P extends Pattern> = { [K in keyof P]: P[K] extends Parameter<infer T> ? T : never };
5528
5474
  type Executor<P extends Pattern> = (session: Session, params: ParamsOf<P>) => void | Promise<void>;
5475
+ type RouteMeta = Record<string, unknown> & {
5476
+ tags?: readonly string[];
5477
+ };
5529
5478
  interface Command<P extends Pattern> {
5530
5479
  name: string;
5531
5480
  pattern: P;
@@ -5533,10 +5482,12 @@ interface Command<P extends Pattern> {
5533
5482
  description?: string;
5534
5483
  aliases?: string[];
5535
5484
  hidden?: boolean;
5485
+ meta?: RouteMeta;
5536
5486
  }
5537
5487
  interface RawPattern<P extends Pattern> {
5538
5488
  pattern: P;
5539
5489
  execute: Executor<P>;
5490
+ meta?: RouteMeta;
5540
5491
  }
5541
5492
  interface SessionReplyOptions {
5542
5493
  withQuote?: boolean;
@@ -5561,6 +5512,7 @@ declare class CommandBuilder<P extends Pattern = {}, S = Command<P>> {
5561
5512
  private description?;
5562
5513
  private aliases?;
5563
5514
  private hidden?;
5515
+ private routeMeta?;
5564
5516
  constructor(name: string, sink?: (command: Command<P>) => S);
5565
5517
  arg<K extends string, T>(
5566
5518
  key: K,
@@ -5576,11 +5528,40 @@ declare class CommandBuilder<P extends Pattern = {}, S = Command<P>> {
5576
5528
  describe(description: string): CommandBuilder<P, S>;
5577
5529
  alias(...aliases: string[]): CommandBuilder<P, S>;
5578
5530
  hide(): CommandBuilder<P, S>;
5531
+ meta(meta: RouteMeta): CommandBuilder<P, S>;
5532
+ tag(...tags: string[]): CommandBuilder<P, S>;
5579
5533
  execute(executor: Executor<P>): S;
5580
5534
  }
5535
+ declare function mergeRouteMeta(left?: RouteMeta, right?: RouteMeta): RouteMeta | undefined;
5581
5536
  //#endregion
5582
5537
  //#region src/routing/router.d.ts
5583
5538
  type SessionPredicate = (session: Session) => boolean;
5539
+ type RouteActivation =
5540
+ | {
5541
+ type: 'direct';
5542
+ }
5543
+ | {
5544
+ type: 'mention';
5545
+ }
5546
+ | {
5547
+ type: 'prefix';
5548
+ prefix: string;
5549
+ };
5550
+ type RouteDescriptor =
5551
+ | {
5552
+ type: 'command';
5553
+ path: readonly string[];
5554
+ name: string;
5555
+ aliases?: readonly string[];
5556
+ meta?: RouteMeta;
5557
+ }
5558
+ | {
5559
+ type: 'rawPattern';
5560
+ path: readonly string[];
5561
+ meta?: RouteMeta;
5562
+ };
5563
+ type RouteActivationResolver = (route: RouteDescriptor, session: Session) => readonly RouteActivation[] | undefined;
5564
+ declare const defaultRouteActivationResolver: RouteActivationResolver;
5584
5565
  type RouteEntry =
5585
5566
  | {
5586
5567
  type: 'command';
@@ -5605,11 +5586,13 @@ type RouteBranch =
5605
5586
  type: 'command';
5606
5587
  path: string[];
5607
5588
  command: Command<Pattern>;
5589
+ meta?: RouteMeta;
5608
5590
  }
5609
5591
  | {
5610
5592
  type: 'rawPattern';
5611
5593
  path: string[];
5612
5594
  rawPattern: RawPattern<Pattern>;
5595
+ meta?: RouteMeta;
5613
5596
  };
5614
5597
  type RouteMatchResult =
5615
5598
  | {
@@ -5617,16 +5600,22 @@ type RouteMatchResult =
5617
5600
  path: string[];
5618
5601
  command: Command<Pattern>;
5619
5602
  params: any;
5603
+ activation?: RouteActivation;
5620
5604
  }
5621
5605
  | {
5622
5606
  type: 'rawPattern';
5623
5607
  path: string[];
5624
5608
  rawPattern: RawPattern<Pattern>;
5625
5609
  params: any;
5610
+ activation?: RouteActivation;
5626
5611
  };
5627
5612
  declare class Router {
5628
- private readonly entries;
5629
- private readonly groups;
5613
+ private entries;
5614
+ private groups;
5615
+ private activationResolver;
5616
+ private scopeMeta?;
5617
+ setActivationResolver(resolver: RouteActivationResolver): this;
5618
+ withMeta(meta: RouteMeta): Router;
5630
5619
  command(name: string): CommandBuilder;
5631
5620
  command<P extends Pattern>(command: Command<P>): this;
5632
5621
  rawPattern(): CommandBuilder<{}, RawPattern<{}>>;
@@ -5638,17 +5627,107 @@ declare class Router {
5638
5627
  branches(session: Session): RouteBranch[];
5639
5628
  match(session: Session, message: IncomingMessage): RouteMatchResult | undefined;
5640
5629
  dispatch(session: Session, message: IncomingMessage): Promise<boolean>;
5641
- private matchFrom;
5642
- private matchEntry;
5630
+ private describeBranch;
5631
+ private consumeActivation;
5632
+ private matchBranch;
5633
+ private matchPath;
5643
5634
  private matchCommand;
5644
- private matchGroup;
5645
5635
  private matchRawPattern;
5646
5636
  private branchesFrom;
5637
+ private applyScopeMeta;
5647
5638
  private resolveAliasConflicts;
5648
5639
  private validatePattern;
5649
5640
  private capturePattern;
5650
5641
  }
5651
5642
  //#endregion
5643
+ //#region src/core/activation.d.ts
5644
+ type ContextRouteActivationByScene = Partial<
5645
+ Record<IncomingMessage['message_scene'] | 'default', readonly RouteActivation[]>
5646
+ >;
5647
+ type ContextRouteActivation = readonly RouteActivation[] | ContextRouteActivationByScene;
5648
+ interface ContextRouteActivationMatcher {
5649
+ type?: RouteDescriptor['type'] | readonly RouteDescriptor['type'][];
5650
+ plugin?: string | readonly string[];
5651
+ context?: string | readonly string[];
5652
+ tag?: string | readonly string[];
5653
+ command?: string | readonly string[];
5654
+ path?: readonly string[];
5655
+ route?: readonly string[];
5656
+ predicate?: (route: RouteDescriptor, session: Session) => boolean;
5657
+ }
5658
+ interface ContextRouteActivationRule {
5659
+ match?: ContextRouteActivationMatcher;
5660
+ activation: ContextRouteActivation;
5661
+ }
5662
+ interface ContextRouteActivationConfig {
5663
+ default?: ContextRouteActivation;
5664
+ rules?: ContextRouteActivationRule | readonly ContextRouteActivationRule[];
5665
+ }
5666
+ interface ContextRoutingOptions {
5667
+ activation?: ContextRouteActivationConfig;
5668
+ activationResolver?: RouteActivationResolver;
5669
+ }
5670
+ declare function createContextRouteActivationResolver(
5671
+ routing: ContextRoutingOptions | undefined,
5672
+ fallback?: RouteActivationResolver,
5673
+ ): RouteActivationResolver;
5674
+ //#endregion
5675
+ //#region src/protocol/endpoint.d.ts
5676
+ type RequiredKeys<T> = { [K in keyof T]-?: {} extends Pick<T, K> ? never : K }[keyof T];
5677
+ type AllOptional<T> = RequiredKeys<T> extends never ? true : false;
5678
+ type RawApiEndpoint<E extends keyof ApiEndpoints$1> = {
5679
+ request: ApiEndpoints$1[E]['request_ZodInput'] extends null ? null : ApiEndpoints$1[E]['request_ZodInput'];
5680
+ response: ApiEndpoints$1[E]['response'] extends null ? null : ApiEndpoints$1[E]['response'];
5681
+ };
5682
+ type ApiEndpointFunction<E extends keyof ApiEndpoints$1> = RawApiEndpoint<E>['request'] extends null
5683
+ ? () => Promise<RawApiEndpoint<E>['response']>
5684
+ : AllOptional<RawApiEndpoint<E>['request']> extends true
5685
+ ? (params?: RawApiEndpoint<E>['request']) => Promise<RawApiEndpoint<E>['response']>
5686
+ : (params: RawApiEndpoint<E>['request']) => Promise<RawApiEndpoint<E>['response']>;
5687
+ type ApiEndpoints = { [E in keyof ApiEndpoints$1]: ApiEndpointFunction<E> };
5688
+ type EventMap = {
5689
+ [K in Event['event_type']]: Extract<
5690
+ Event,
5691
+ {
5692
+ event_type: K;
5693
+ }
5694
+ >;
5695
+ };
5696
+ //#endregion
5697
+ //#region src/protocol/client.d.ts
5698
+ interface MilkyClient extends ApiEndpoints {}
5699
+ declare class MilkyClientBase {
5700
+ readonly baseUrl: string;
5701
+ readonly wsBaseUrl: string;
5702
+ private readonly accessToken?;
5703
+ private readonly baseHeaders;
5704
+ constructor(
5705
+ baseUrl: string | URL,
5706
+ options?: {
5707
+ accessToken?: string;
5708
+ },
5709
+ );
5710
+ callApi(endpoint: string, params?: unknown): Promise<unknown>;
5711
+ }
5712
+ declare function createMilkyClient(...params: ConstructorParameters<typeof MilkyClientBase>): MilkyClient;
5713
+ //#endregion
5714
+ //#region src/protocol/event.d.ts
5715
+ interface MilkyEventSubscription {
5716
+ closed: Promise<void>;
5717
+ stop(): void | Promise<void>;
5718
+ }
5719
+ interface MilkyEventSource {
5720
+ readonly name?: string;
5721
+ start(onEvent: (event: Event) => void | Promise<void>): Promise<MilkyEventSubscription>;
5722
+ }
5723
+ interface MilkyWebSocketEventSourceOptions {
5724
+ accessToken?: string;
5725
+ }
5726
+ declare function createMilkyWebSocketEventSource(
5727
+ baseUrl: string | URL,
5728
+ options?: MilkyWebSocketEventSourceOptions,
5729
+ ): MilkyEventSource;
5730
+ //#endregion
5652
5731
  //#region src/core/filter.d.ts
5653
5732
  type Filter = { [K in keyof EventMap]?: (event: EventMap[K]) => boolean };
5654
5733
  declare namespace filter {
@@ -5731,6 +5810,7 @@ interface ContextOptions {
5731
5810
  maxDelayMs?: number;
5732
5811
  };
5733
5812
  logHandler?: LogHandler;
5813
+ routing?: ContextRoutingOptions;
5734
5814
  }
5735
5815
  interface ContextUrlOptions {
5736
5816
  accessToken?: string;
@@ -5741,6 +5821,7 @@ declare class Context {
5741
5821
  readonly router: Router;
5742
5822
  readonly logger: Logger;
5743
5823
  readonly name: string;
5824
+ readonly routeActivationResolver: RouteActivationResolver;
5744
5825
  private readonly parent?;
5745
5826
  private readonly filter?;
5746
5827
  private readonly eventBus;
@@ -5848,6 +5929,12 @@ export {
5848
5929
  CommandBuilder,
5849
5930
  Context,
5850
5931
  ContextOptions,
5932
+ ContextRouteActivation,
5933
+ ContextRouteActivationByScene,
5934
+ ContextRouteActivationConfig,
5935
+ ContextRouteActivationMatcher,
5936
+ ContextRouteActivationRule,
5937
+ ContextRoutingOptions,
5851
5938
  ContextUrlOptions,
5852
5939
  Disposable,
5853
5940
  EventMap,
@@ -5868,9 +5955,13 @@ export {
5868
5955
  Pattern,
5869
5956
  Plugin,
5870
5957
  RawPattern,
5958
+ RouteActivation,
5959
+ RouteActivationResolver,
5871
5960
  RouteBranch,
5961
+ RouteDescriptor,
5872
5962
  RouteEntry,
5873
5963
  RouteMatchResult,
5964
+ RouteMeta,
5874
5965
  Router,
5875
5966
  ServiceClass,
5876
5967
  Session,
@@ -5878,12 +5969,15 @@ export {
5878
5969
  SessionReplyOptions,
5879
5970
  TypeInstruction,
5880
5971
  combineLogHandlers,
5972
+ createContextRouteActivationResolver,
5881
5973
  createMilkyClient,
5882
5974
  createMilkyWebSocketEventSource,
5975
+ defaultRouteActivationResolver,
5883
5976
  definePlugin,
5884
5977
  filter,
5885
5978
  implementsESNextDisposable,
5886
5979
  isDisposable,
5980
+ mergeRouteMeta,
5887
5981
  type types_d_exports as milky,
5888
5982
  milkyPackageVersion,
5889
5983
  milkyVersion,
package/dist/index.mjs CHANGED
@@ -1,210 +1,4 @@
1
1
  import mitt from "mitt";
2
- //#region src/protocol/client.ts
3
- var MilkyClientBase = class {
4
- baseUrl;
5
- wsBaseUrl;
6
- accessToken;
7
- baseHeaders;
8
- constructor(baseUrl, options) {
9
- const normalizedBaseUrl = baseUrl.toString();
10
- this.baseUrl = normalizedBaseUrl.endsWith("/") ? normalizedBaseUrl.slice(0, -1) : normalizedBaseUrl;
11
- this.wsBaseUrl = this.baseUrl.replace(/^http/, "ws");
12
- this.accessToken = options?.accessToken;
13
- this.baseHeaders = { "Content-Type": "application/json" };
14
- if (options?.accessToken) this.baseHeaders.Authorization = `Bearer ${options.accessToken}`;
15
- }
16
- async callApi(endpoint, params) {
17
- const response = await fetch(`${this.baseUrl}/api/${endpoint}`, {
18
- method: "POST",
19
- body: JSON.stringify(params ?? {}),
20
- headers: this.baseHeaders
21
- });
22
- if (!response.ok) throw new Error(`API call ${endpoint} failed with HTTP status ${response.status}`);
23
- const json = await response.json();
24
- if (json.status === "failed") throw new Error(`API call ${endpoint} failed with retcode ${json.retcode}: ${json.message}`);
25
- return json.data;
26
- }
27
- };
28
- function createMilkyClient(...params) {
29
- const base = new MilkyClientBase(...params);
30
- return new Proxy(base, { get(target, endpoint) {
31
- if (typeof endpoint === "string" && endpoint.includes("_")) return (params) => target.callApi(endpoint, params);
32
- else return target[endpoint];
33
- } });
34
- }
35
- //#endregion
36
- //#region src/protocol/event.ts
37
- function createMilkyWebSocketEventSource(baseUrl, options) {
38
- const normalizedBaseUrl = baseUrl.toString();
39
- const wsBaseUrl = normalizedBaseUrl.endsWith("/") ? normalizedBaseUrl.slice(0, -1).replace(/^http/, "ws") : normalizedBaseUrl.replace(/^http/, "ws");
40
- return {
41
- name: "websocket",
42
- async start(onEvent) {
43
- const ws = new WebSocket(`${wsBaseUrl}/event${options?.accessToken ? `?access_token=${options.accessToken}` : ""}`);
44
- let closeSubscription = () => {};
45
- const closed = new Promise((resolve, reject) => {
46
- closeSubscription = (error) => {
47
- if (error) reject(error);
48
- else resolve();
49
- };
50
- });
51
- ws.addEventListener("message", async (event) => {
52
- try {
53
- if (typeof event.data !== "string") throw new Error(`Expected text frame, got ${typeof event.data}`);
54
- await onEvent(JSON.parse(event.data));
55
- } catch (error) {
56
- closeSubscription(error);
57
- ws.close();
58
- }
59
- });
60
- await new Promise((resolve, reject) => {
61
- ws.addEventListener("open", () => resolve(), { once: true });
62
- ws.addEventListener("error", (event) => reject(/* @__PURE__ */ new Error(`WebSocket error: ${event}`)));
63
- });
64
- ws.addEventListener("error", (event) => closeSubscription(/* @__PURE__ */ new Error(`WebSocket error: ${event}`)));
65
- ws.addEventListener("close", () => closeSubscription(), { once: true });
66
- return {
67
- closed,
68
- stop() {
69
- ws.close();
70
- }
71
- };
72
- }
73
- };
74
- }
75
- //#endregion
76
- //#region src/protocol/segment.ts
77
- function msg(strings, ...values) {
78
- let buffer = "";
79
- const segments = [];
80
- for (let i = 0; i < strings.length; i++) {
81
- buffer += strings[i];
82
- if (i < values.length) {
83
- const value = values[i];
84
- if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") buffer += value.toString();
85
- else {
86
- if (buffer) {
87
- segments.push({
88
- type: "text",
89
- data: { text: buffer }
90
- });
91
- buffer = "";
92
- }
93
- segments.push(value);
94
- }
95
- }
96
- }
97
- if (buffer) segments.push({
98
- type: "text",
99
- data: { text: buffer }
100
- });
101
- return trimBoundaryTextSegments(segments);
102
- }
103
- function isTextSegment(segment) {
104
- return segment.type === "text";
105
- }
106
- function withText(segment, text) {
107
- return {
108
- ...segment,
109
- data: {
110
- ...segment.data,
111
- text
112
- }
113
- };
114
- }
115
- function trimBoundaryTextSegments(segments) {
116
- const result = [...segments];
117
- const first = result[0];
118
- if (first && isTextSegment(first)) {
119
- const text = first.data.text.trimStart();
120
- if (text) result[0] = withText(first, text);
121
- else result.shift();
122
- }
123
- const lastIndex = result.length - 1;
124
- const last = result[lastIndex];
125
- if (last && isTextSegment(last)) {
126
- const text = last.data.text.trimEnd();
127
- if (text) result[lastIndex] = withText(last, text);
128
- else result.pop();
129
- }
130
- return result;
131
- }
132
- let seg;
133
- (function(_seg) {
134
- function mention(userId) {
135
- return {
136
- type: "mention",
137
- data: { user_id: userId }
138
- };
139
- }
140
- _seg.mention = mention;
141
- function mentionAll() {
142
- return {
143
- type: "mention_all",
144
- data: {}
145
- };
146
- }
147
- _seg.mentionAll = mentionAll;
148
- function face(faceId, options) {
149
- return {
150
- type: "face",
151
- data: {
152
- face_id: faceId.toString(),
153
- is_large: options?.isLarge ?? false
154
- }
155
- };
156
- }
157
- _seg.face = face;
158
- function reply(messageSeq) {
159
- return {
160
- type: "reply",
161
- data: { message_seq: messageSeq }
162
- };
163
- }
164
- _seg.reply = reply;
165
- function image(uri, options) {
166
- return {
167
- type: "image",
168
- data: {
169
- uri,
170
- sub_type: options?.subType,
171
- summary: options?.summary
172
- }
173
- };
174
- }
175
- _seg.image = image;
176
- function record(uri) {
177
- return {
178
- type: "record",
179
- data: { uri }
180
- };
181
- }
182
- _seg.record = record;
183
- function video(uri, options) {
184
- return {
185
- type: "video",
186
- data: {
187
- uri,
188
- thumb_uri: options?.thumbUri
189
- }
190
- };
191
- }
192
- _seg.video = video;
193
- function forward(messages, options) {
194
- return {
195
- type: "forward",
196
- data: {
197
- messages,
198
- title: options?.title,
199
- preview: options?.preview,
200
- summary: options?.summary,
201
- prompt: options?.prompt
202
- }
203
- };
204
- }
205
- _seg.forward = forward;
206
- })(seg || (seg = {}));
207
- //#endregion
208
2
  //#region src/routing/command.ts
209
3
  var CommandBuilder = class {
210
4
  name;
@@ -214,6 +8,7 @@ var CommandBuilder = class {
214
8
  description;
215
9
  aliases;
216
10
  hidden;
11
+ routeMeta;
217
12
  constructor(name, sink = (command) => command) {
218
13
  this.name = name;
219
14
  this.sink = sink;
@@ -235,6 +30,14 @@ var CommandBuilder = class {
235
30
  this.hidden = true;
236
31
  return this;
237
32
  }
33
+ meta(meta) {
34
+ this.routeMeta = mergeRouteMeta(this.routeMeta, meta);
35
+ return this;
36
+ }
37
+ tag(...tags) {
38
+ this.routeMeta = mergeRouteMeta(this.routeMeta, { tags });
39
+ return this;
40
+ }
238
41
  execute(executor) {
239
42
  this.executor = executor;
240
43
  const command = {
@@ -245,9 +48,31 @@ var CommandBuilder = class {
245
48
  if (this.description !== void 0) command.description = this.description;
246
49
  if (this.aliases !== void 0) command.aliases = this.aliases;
247
50
  if (this.hidden !== void 0) command.hidden = this.hidden;
51
+ if (this.routeMeta !== void 0) command.meta = this.routeMeta;
248
52
  return this.sink(command);
249
53
  }
250
54
  };
55
+ function mergeRouteMeta(left, right) {
56
+ if (left === void 0 && right === void 0) return;
57
+ const merged = {
58
+ ...left ?? {},
59
+ ...right ?? {}
60
+ };
61
+ const tags = uniqueTags(left?.tags, right?.tags);
62
+ if (tags.length > 0) merged.tags = tags;
63
+ else delete merged.tags;
64
+ return Object.keys(merged).length > 0 ? merged : void 0;
65
+ }
66
+ function uniqueTags(...tagLists) {
67
+ const tags = [];
68
+ const seen = /* @__PURE__ */ new Set();
69
+ for (const tagList of tagLists) for (const tag of tagList ?? []) {
70
+ if (seen.has(tag)) continue;
71
+ seen.add(tag);
72
+ tags.push(tag);
73
+ }
74
+ return tags;
75
+ }
251
76
  //#endregion
252
77
  //#region src/routing/tokenizer.ts
253
78
  const WHITESPACE_CHARS = new Set([
@@ -352,6 +177,33 @@ var Tokenizer = class {
352
177
  this.subOffset = void 0;
353
178
  return segments;
354
179
  }
180
+ consumeMention(userId) {
181
+ const token = this.peek();
182
+ if (typeof token === "object" && token !== null && token.type === "mention" && token.data.user_id === userId) {
183
+ this.next();
184
+ return true;
185
+ }
186
+ return false;
187
+ }
188
+ consumeTextPrefix(prefix) {
189
+ if (prefix.length === 0) return false;
190
+ const position = this.findNextPosition();
191
+ if (position === void 0) return false;
192
+ const segment = this.segments[position.offset];
193
+ if (segment.type !== "text") return false;
194
+ const prefixStart = this.findTextTokenStart(segment.data.text, position.subOffset ?? 0);
195
+ if (!segment.data.text.startsWith(prefix, prefixStart)) return false;
196
+ const prefixEnd = prefixStart + prefix.length;
197
+ const nextSubOffset = this.findTextTokenStart(segment.data.text, prefixEnd);
198
+ if (nextSubOffset < segment.data.text.length) {
199
+ this.offset = position.offset;
200
+ this.subOffset = nextSubOffset;
201
+ } else {
202
+ this.offset = position.offset + 1;
203
+ this.subOffset = void 0;
204
+ }
205
+ return true;
206
+ }
355
207
  findNextPosition() {
356
208
  let offset = this.offset;
357
209
  let subOffset = this.subOffset;
@@ -383,19 +235,35 @@ var Tokenizer = class {
383
235
  };
384
236
  //#endregion
385
237
  //#region src/routing/router.ts
238
+ const DEFAULT_ACTIVATIONS = [{ type: "direct" }];
239
+ const defaultRouteActivationResolver = () => DEFAULT_ACTIVATIONS;
386
240
  var Router = class Router {
387
241
  entries = [];
388
242
  groups = /* @__PURE__ */ new Map();
243
+ activationResolver = defaultRouteActivationResolver;
244
+ scopeMeta;
245
+ setActivationResolver(resolver) {
246
+ this.activationResolver = resolver;
247
+ return this;
248
+ }
249
+ withMeta(meta) {
250
+ const router = new Router();
251
+ router.entries = this.entries;
252
+ router.groups = this.groups;
253
+ router.scopeMeta = mergeRouteMeta(this.scopeMeta, meta);
254
+ return router;
255
+ }
389
256
  command(command) {
390
257
  if (typeof command === "string") return new CommandBuilder(command, (builtCommand) => {
391
258
  this.command(builtCommand);
392
259
  return builtCommand;
393
260
  });
394
- this.validatePattern(command.pattern);
395
- this.resolveAliasConflicts(command);
261
+ const scopedCommand = this.applyScopeMeta(command);
262
+ this.validatePattern(scopedCommand.pattern);
263
+ this.resolveAliasConflicts(scopedCommand);
396
264
  this.entries.push({
397
265
  type: "command",
398
- command
266
+ command: scopedCommand
399
267
  });
400
268
  return this;
401
269
  }
@@ -404,10 +272,11 @@ var Router = class Router {
404
272
  this.rawPattern(builtCommand);
405
273
  return builtCommand;
406
274
  });
407
- this.validatePattern(rawPattern.pattern, { rawPattern: true });
275
+ const scopedRawPattern = this.applyScopeMeta(rawPattern);
276
+ this.validatePattern(scopedRawPattern.pattern, { rawPattern: true });
408
277
  this.entries.push({
409
278
  type: "rawPattern",
410
- rawPattern
279
+ rawPattern: scopedRawPattern
411
280
  });
412
281
  return this;
413
282
  }
@@ -422,7 +291,7 @@ var Router = class Router {
422
291
  router
423
292
  });
424
293
  }
425
- return router;
294
+ return this.scopeMeta ? router.withMeta(this.scopeMeta) : router;
426
295
  }
427
296
  filter(predicate) {
428
297
  const router = new Router();
@@ -431,7 +300,7 @@ var Router = class Router {
431
300
  predicate,
432
301
  router
433
302
  });
434
- return router;
303
+ return this.scopeMeta ? router.withMeta(this.scopeMeta) : router;
435
304
  }
436
305
  routes() {
437
306
  return this.entries;
@@ -441,11 +310,19 @@ var Router = class Router {
441
310
  return [];
442
311
  }
443
312
  branches(session) {
444
- return this.branchesFrom(session, []);
313
+ return [...this.branchesFrom(session, [], { includeHidden: false })];
445
314
  }
446
315
  match(session, message) {
447
- const tokenizer = new Tokenizer(message.segments);
448
- return this.matchFrom(session, tokenizer, []);
316
+ for (const branch of this.branchesFrom(session, [], { includeHidden: true })) {
317
+ const descriptor = this.describeBranch(branch);
318
+ const activations = this.activationResolver(descriptor, session) ?? DEFAULT_ACTIVATIONS;
319
+ for (const activation of activations) {
320
+ const tokenizer = new Tokenizer(message.segments);
321
+ if (!this.consumeActivation(tokenizer, activation, session)) continue;
322
+ const match = this.matchBranch(branch, tokenizer, activation);
323
+ if (match !== void 0) return match;
324
+ }
325
+ }
449
326
  }
450
327
  async dispatch(session, message) {
451
328
  const match = this.match(session, message);
@@ -460,26 +337,51 @@ var Router = class Router {
460
337
  }
461
338
  return true;
462
339
  }
463
- matchFrom(session, tokenizer, path) {
464
- const initialState = tokenizer.getState();
465
- for (const entry of this.entries) {
466
- tokenizer.setState(initialState);
467
- const match = this.matchEntry(entry, session, tokenizer, path);
468
- if (match !== void 0) return match;
340
+ describeBranch(branch) {
341
+ switch (branch.type) {
342
+ case "command": {
343
+ const descriptor = {
344
+ type: "command",
345
+ path: [...branch.path],
346
+ name: branch.command.name
347
+ };
348
+ if (branch.command.aliases !== void 0) descriptor.aliases = [...branch.command.aliases];
349
+ if (branch.command.meta !== void 0) descriptor.meta = branch.command.meta;
350
+ return descriptor;
351
+ }
352
+ case "rawPattern": {
353
+ const descriptor = {
354
+ type: "rawPattern",
355
+ path: [...branch.path]
356
+ };
357
+ if (branch.rawPattern.meta !== void 0) descriptor.meta = branch.rawPattern.meta;
358
+ return descriptor;
359
+ }
469
360
  }
470
- tokenizer.setState(initialState);
471
361
  }
472
- matchEntry(entry, session, tokenizer, path) {
473
- switch (entry.type) {
474
- case "command": return this.matchCommand(entry.command, tokenizer, path);
475
- case "group": return this.matchGroup(entry.name, entry.router, session, tokenizer, path);
476
- case "filter":
477
- if (entry.predicate(session) !== true) return;
478
- return entry.router.matchFrom(session, tokenizer, path);
479
- case "rawPattern": return this.matchRawPattern(entry.rawPattern, tokenizer, path);
362
+ consumeActivation(tokenizer, activation, session) {
363
+ switch (activation.type) {
364
+ case "direct": return true;
365
+ case "mention": return tokenizer.consumeMention(session.selfId);
366
+ case "prefix": return tokenizer.consumeTextPrefix(activation.prefix);
367
+ }
368
+ }
369
+ matchBranch(branch, tokenizer, activation) {
370
+ if (!this.matchPath(branch.path, tokenizer)) return;
371
+ switch (branch.type) {
372
+ case "command": return this.matchCommand(branch.command, tokenizer, branch.path, activation);
373
+ case "rawPattern": return this.matchRawPattern(branch.rawPattern, tokenizer, branch.path, activation);
374
+ }
375
+ }
376
+ matchPath(path, tokenizer) {
377
+ for (const name of path) {
378
+ const token = tokenizer.peek();
379
+ if (typeof token !== "string" || token !== name) return false;
380
+ tokenizer.next();
480
381
  }
382
+ return true;
481
383
  }
482
- matchCommand(command, tokenizer, path) {
384
+ matchCommand(command, tokenizer, path, activation) {
483
385
  const token = tokenizer.peek();
484
386
  if (typeof token !== "string" || token !== command.name && !command.aliases?.includes(token)) return;
485
387
  tokenizer.next();
@@ -489,50 +391,55 @@ var Router = class Router {
489
391
  type: "command",
490
392
  path: [...path],
491
393
  command,
492
- params
394
+ params,
395
+ activation
493
396
  };
494
397
  }
495
- matchGroup(name, router, session, tokenizer, path) {
496
- const token = tokenizer.peek();
497
- if (typeof token !== "string" || token !== name) return;
498
- tokenizer.next();
499
- return router.matchFrom(session, tokenizer, [...path, name]);
500
- }
501
- matchRawPattern(rawPattern, tokenizer, path) {
398
+ matchRawPattern(rawPattern, tokenizer, path, activation) {
502
399
  const params = this.capturePattern(rawPattern.pattern, tokenizer);
503
400
  if (params === void 0 || tokenizer.hasNext()) return;
504
401
  return {
505
402
  type: "rawPattern",
506
403
  path: [...path],
507
404
  rawPattern,
508
- params
405
+ params,
406
+ activation
509
407
  };
510
408
  }
511
- branchesFrom(session, path) {
512
- const branches = [];
409
+ *branchesFrom(session, path, options) {
513
410
  for (const entry of this.entries) switch (entry.type) {
514
411
  case "command":
515
- if (!entry.command.hidden) branches.push({
412
+ if (options.includeHidden || !entry.command.hidden) yield {
516
413
  type: "command",
517
414
  path: [...path],
518
- command: entry.command
519
- });
415
+ command: entry.command,
416
+ meta: entry.command.meta
417
+ };
520
418
  break;
521
419
  case "group":
522
- branches.push(...entry.router.branchesFrom(session, [...path, entry.name]));
420
+ yield* entry.router.branchesFrom(session, [...path, entry.name], options);
523
421
  break;
524
422
  case "filter":
525
- if (entry.predicate(session) === true) branches.push(...entry.router.branchesFrom(session, path));
423
+ if (entry.predicate(session) === true) yield* entry.router.branchesFrom(session, path, options);
526
424
  break;
527
425
  case "rawPattern":
528
- branches.push({
426
+ yield {
529
427
  type: "rawPattern",
530
428
  path: [...path],
531
- rawPattern: entry.rawPattern
532
- });
429
+ rawPattern: entry.rawPattern,
430
+ meta: entry.rawPattern.meta
431
+ };
533
432
  break;
534
433
  }
535
- return branches;
434
+ }
435
+ applyScopeMeta(route) {
436
+ if (this.scopeMeta === void 0) return route;
437
+ const meta = mergeRouteMeta(route.meta, this.scopeMeta);
438
+ if (meta === void 0) return route;
439
+ return {
440
+ ...route,
441
+ meta
442
+ };
536
443
  }
537
444
  resolveAliasConflicts(command) {
538
445
  for (const entry of this.entries) {
@@ -579,6 +486,283 @@ var Router = class Router {
579
486
  }
580
487
  };
581
488
  //#endregion
489
+ //#region src/core/activation.ts
490
+ function createContextRouteActivationResolver(routing, fallback = defaultRouteActivationResolver) {
491
+ if (!routing) return fallback;
492
+ if (routing.activation && routing.activationResolver) throw new Error("Context routing cannot specify both activation and activationResolver.");
493
+ if (routing.activationResolver) return routing.activationResolver;
494
+ if (routing.activation) return compileContextRouteActivationConfig(routing.activation, fallback);
495
+ return fallback;
496
+ }
497
+ function compileContextRouteActivationConfig(config, fallback) {
498
+ return (route, session) => {
499
+ for (const rule of contextRouteActivationRules(config.rules)) {
500
+ if (!matchesContextRouteActivationRule(route, session, rule)) continue;
501
+ const activation = resolveContextRouteActivation(rule.activation, session.raw.message_scene);
502
+ if (activation !== void 0) return activation;
503
+ }
504
+ if (config.default !== void 0) {
505
+ const activation = resolveContextRouteActivation(config.default, session.raw.message_scene);
506
+ if (activation !== void 0) return activation;
507
+ }
508
+ return fallback(route, session);
509
+ };
510
+ }
511
+ function contextRouteActivationRules(rules) {
512
+ if (rules === void 0) return [];
513
+ return isContextRouteActivationRuleArray(rules) ? rules : [rules];
514
+ }
515
+ function isContextRouteActivationRuleArray(rules) {
516
+ return Array.isArray(rules);
517
+ }
518
+ function resolveContextRouteActivation(activation, scene) {
519
+ if (isRouteActivationArray(activation)) return activation;
520
+ return activation[scene] ?? activation.default;
521
+ }
522
+ function isRouteActivationArray(activation) {
523
+ return Array.isArray(activation);
524
+ }
525
+ function matchesContextRouteActivationRule(route, session, rule) {
526
+ const match = rule.match;
527
+ if (!match) return true;
528
+ if (!matchesStringSelector(match.type, route.type)) return false;
529
+ if (!matchesStringSelector(match.plugin, routeMetaString(route, "plugin"))) return false;
530
+ if (!matchesStringSelector(match.context, routeMetaString(route, "context"))) return false;
531
+ if (!matchesTagSelector(match.tag, route.meta?.tags)) return false;
532
+ if (match.command !== void 0 && (route.type !== "command" || !matchesStringSelector(match.command, route.name))) return false;
533
+ if (match.path !== void 0 && !sameStringArray(match.path, route.path)) return false;
534
+ if (match.route !== void 0 && !sameStringArray(match.route, routeSegments(route))) return false;
535
+ if (match.predicate && match.predicate(route, session) !== true) return false;
536
+ return true;
537
+ }
538
+ function routeMetaString(route, key) {
539
+ const value = route.meta?.[key];
540
+ return typeof value === "string" ? value : void 0;
541
+ }
542
+ function matchesStringSelector(selector, value) {
543
+ if (selector === void 0) return true;
544
+ if (value === void 0) return false;
545
+ return typeof selector === "string" ? selector === value : selector.includes(value);
546
+ }
547
+ function matchesTagSelector(selector, tags) {
548
+ if (selector === void 0) return true;
549
+ if (!tags) return false;
550
+ return typeof selector === "string" ? tags.includes(selector) : selector.some((tag) => tags.includes(tag));
551
+ }
552
+ function routeSegments(route) {
553
+ if (route.type === "command") return [...route.path, route.name];
554
+ return route.path;
555
+ }
556
+ function sameStringArray(left, right) {
557
+ return left.length === right.length && left.every((value, index) => value === right[index]);
558
+ }
559
+ //#endregion
560
+ //#region src/protocol/client.ts
561
+ var MilkyClientBase = class {
562
+ baseUrl;
563
+ wsBaseUrl;
564
+ accessToken;
565
+ baseHeaders;
566
+ constructor(baseUrl, options) {
567
+ const normalizedBaseUrl = baseUrl.toString();
568
+ this.baseUrl = normalizedBaseUrl.endsWith("/") ? normalizedBaseUrl.slice(0, -1) : normalizedBaseUrl;
569
+ this.wsBaseUrl = this.baseUrl.replace(/^http/, "ws");
570
+ this.accessToken = options?.accessToken;
571
+ this.baseHeaders = { "Content-Type": "application/json" };
572
+ if (options?.accessToken) this.baseHeaders.Authorization = `Bearer ${options.accessToken}`;
573
+ }
574
+ async callApi(endpoint, params) {
575
+ const response = await fetch(`${this.baseUrl}/api/${endpoint}`, {
576
+ method: "POST",
577
+ body: JSON.stringify(params ?? {}),
578
+ headers: this.baseHeaders
579
+ });
580
+ if (!response.ok) throw new Error(`API call ${endpoint} failed with HTTP status ${response.status}`);
581
+ const json = await response.json();
582
+ if (json.status === "failed") throw new Error(`API call ${endpoint} failed with retcode ${json.retcode}: ${json.message}`);
583
+ return json.data;
584
+ }
585
+ };
586
+ function createMilkyClient(...params) {
587
+ const base = new MilkyClientBase(...params);
588
+ return new Proxy(base, { get(target, endpoint) {
589
+ if (typeof endpoint === "string" && endpoint.includes("_")) return (params) => target.callApi(endpoint, params);
590
+ else return target[endpoint];
591
+ } });
592
+ }
593
+ //#endregion
594
+ //#region src/protocol/event.ts
595
+ function createMilkyWebSocketEventSource(baseUrl, options) {
596
+ const normalizedBaseUrl = baseUrl.toString();
597
+ const wsBaseUrl = normalizedBaseUrl.endsWith("/") ? normalizedBaseUrl.slice(0, -1).replace(/^http/, "ws") : normalizedBaseUrl.replace(/^http/, "ws");
598
+ return {
599
+ name: "websocket",
600
+ async start(onEvent) {
601
+ const ws = new WebSocket(`${wsBaseUrl}/event${options?.accessToken ? `?access_token=${options.accessToken}` : ""}`);
602
+ let closeSubscription = () => {};
603
+ const closed = new Promise((resolve, reject) => {
604
+ closeSubscription = (error) => {
605
+ if (error) reject(error);
606
+ else resolve();
607
+ };
608
+ });
609
+ ws.addEventListener("message", async (event) => {
610
+ try {
611
+ if (typeof event.data !== "string") throw new Error(`Expected text frame, got ${typeof event.data}`);
612
+ await onEvent(JSON.parse(event.data));
613
+ } catch (error) {
614
+ closeSubscription(error);
615
+ ws.close();
616
+ }
617
+ });
618
+ await new Promise((resolve, reject) => {
619
+ ws.addEventListener("open", () => resolve(), { once: true });
620
+ ws.addEventListener("error", (event) => reject(/* @__PURE__ */ new Error(`WebSocket error: ${event}`)));
621
+ });
622
+ ws.addEventListener("error", (event) => closeSubscription(/* @__PURE__ */ new Error(`WebSocket error: ${event}`)));
623
+ ws.addEventListener("close", () => closeSubscription(), { once: true });
624
+ return {
625
+ closed,
626
+ stop() {
627
+ ws.close();
628
+ }
629
+ };
630
+ }
631
+ };
632
+ }
633
+ //#endregion
634
+ //#region src/protocol/segment.ts
635
+ function msg(strings, ...values) {
636
+ let buffer = "";
637
+ const segments = [];
638
+ for (let i = 0; i < strings.length; i++) {
639
+ buffer += strings[i];
640
+ if (i < values.length) {
641
+ const value = values[i];
642
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") buffer += value.toString();
643
+ else {
644
+ if (buffer) {
645
+ segments.push({
646
+ type: "text",
647
+ data: { text: buffer }
648
+ });
649
+ buffer = "";
650
+ }
651
+ segments.push(value);
652
+ }
653
+ }
654
+ }
655
+ if (buffer) segments.push({
656
+ type: "text",
657
+ data: { text: buffer }
658
+ });
659
+ return trimBoundaryTextSegments(segments);
660
+ }
661
+ function isTextSegment(segment) {
662
+ return segment.type === "text";
663
+ }
664
+ function withText(segment, text) {
665
+ return {
666
+ ...segment,
667
+ data: {
668
+ ...segment.data,
669
+ text
670
+ }
671
+ };
672
+ }
673
+ function trimBoundaryTextSegments(segments) {
674
+ const result = [...segments];
675
+ const first = result[0];
676
+ if (first && isTextSegment(first)) {
677
+ const text = first.data.text.trimStart();
678
+ if (text) result[0] = withText(first, text);
679
+ else result.shift();
680
+ }
681
+ const lastIndex = result.length - 1;
682
+ const last = result[lastIndex];
683
+ if (last && isTextSegment(last)) {
684
+ const text = last.data.text.trimEnd();
685
+ if (text) result[lastIndex] = withText(last, text);
686
+ else result.pop();
687
+ }
688
+ return result;
689
+ }
690
+ let seg;
691
+ (function(_seg) {
692
+ function mention(userId) {
693
+ return {
694
+ type: "mention",
695
+ data: { user_id: userId }
696
+ };
697
+ }
698
+ _seg.mention = mention;
699
+ function mentionAll() {
700
+ return {
701
+ type: "mention_all",
702
+ data: {}
703
+ };
704
+ }
705
+ _seg.mentionAll = mentionAll;
706
+ function face(faceId, options) {
707
+ return {
708
+ type: "face",
709
+ data: {
710
+ face_id: faceId.toString(),
711
+ is_large: options?.isLarge ?? false
712
+ }
713
+ };
714
+ }
715
+ _seg.face = face;
716
+ function reply(messageSeq) {
717
+ return {
718
+ type: "reply",
719
+ data: { message_seq: messageSeq }
720
+ };
721
+ }
722
+ _seg.reply = reply;
723
+ function image(uri, options) {
724
+ return {
725
+ type: "image",
726
+ data: {
727
+ uri,
728
+ sub_type: options?.subType,
729
+ summary: options?.summary
730
+ }
731
+ };
732
+ }
733
+ _seg.image = image;
734
+ function record(uri) {
735
+ return {
736
+ type: "record",
737
+ data: { uri }
738
+ };
739
+ }
740
+ _seg.record = record;
741
+ function video(uri, options) {
742
+ return {
743
+ type: "video",
744
+ data: {
745
+ uri,
746
+ thumb_uri: options?.thumbUri
747
+ }
748
+ };
749
+ }
750
+ _seg.video = video;
751
+ function forward(messages, options) {
752
+ return {
753
+ type: "forward",
754
+ data: {
755
+ messages,
756
+ title: options?.title,
757
+ preview: options?.preview,
758
+ summary: options?.summary,
759
+ prompt: options?.prompt
760
+ }
761
+ };
762
+ }
763
+ _seg.forward = forward;
764
+ })(seg || (seg = {}));
765
+ //#endregion
582
766
  //#region src/core/logging.ts
583
767
  var Logger = class {
584
768
  logHandler;
@@ -631,6 +815,7 @@ var Context = class Context {
631
815
  router = new Router();
632
816
  logger;
633
817
  name;
818
+ routeActivationResolver;
634
819
  parent;
635
820
  filter;
636
821
  eventBus = mitt();
@@ -653,6 +838,8 @@ var Context = class Context {
653
838
  this.logHandler = options?.logHandler ?? parent?.logHandler;
654
839
  this.name = name ?? "root";
655
840
  this.logger = new Logger((message) => this.logHandler?.(message), `context:${this.name}`);
841
+ this.routeActivationResolver = createContextRouteActivationResolver(options?.routing, parent?.routeActivationResolver);
842
+ this.router.setActivationResolver(this.routeActivationResolver);
656
843
  this.parent = parent;
657
844
  this.filter = filter;
658
845
  if (parent) {
@@ -1015,6 +1202,10 @@ and implement the dispose method to clean up resources when the context stops.
1015
1202
  }
1016
1203
  createProxyContextForPlugin(plugin) {
1017
1204
  const proxyLogger = new Logger((message) => this.logHandler?.(message), `plugin:${this.name ? `${this.name}/` : ""}${plugin.name}`);
1205
+ const proxyRouter = this.router.withMeta({
1206
+ context: this.name,
1207
+ plugin: plugin.name
1208
+ });
1018
1209
  let proxyInjections;
1019
1210
  if (plugin.inject) {
1020
1211
  proxyInjections = {};
@@ -1026,6 +1217,7 @@ and implement the dispose method to clean up resources when the context stops.
1026
1217
  }
1027
1218
  return new Proxy(this, { get(target, prop, receiver) {
1028
1219
  if (prop === "logger") return proxyLogger;
1220
+ else if (prop === "router") return proxyRouter;
1029
1221
  else if (proxyInjections && prop in proxyInjections) return proxyInjections[prop];
1030
1222
  else return Reflect.get(target, prop, receiver);
1031
1223
  } });
@@ -1382,4 +1574,4 @@ let param;
1382
1574
  _param.segment = segment;
1383
1575
  })(param || (param = {}));
1384
1576
  //#endregion
1385
- export { CommandBuilder, Context, Logger, Parameter, Router, combineLogHandlers, createMilkyClient, createMilkyWebSocketEventSource, definePlugin, filter, implementsESNextDisposable, isDisposable, milkyPackageVersion, milkyVersion, msg, param, seg };
1577
+ export { CommandBuilder, Context, Logger, Parameter, Router, combineLogHandlers, createContextRouteActivationResolver, createMilkyClient, createMilkyWebSocketEventSource, defaultRouteActivationResolver, definePlugin, filter, implementsESNextDisposable, isDisposable, mergeRouteMeta, 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.11.0",
4
+ "version": "0.12.0",
5
5
  "description": "Milky Bot framework",
6
6
  "files": [
7
7
  "dist"
@@ -14,7 +14,7 @@
14
14
  "type": "git",
15
15
  "url": "git+https://github.com/fraqjs/fraq.git"
16
16
  },
17
- "homepage": "https://fraq.ntqqrev.org/",
17
+ "homepage": "https://fraq.dev/",
18
18
  "license": "MIT",
19
19
  "dependencies": {
20
20
  "mitt": "^3.0.1"