@fraqjs/fraq 0.7.0 → 0.8.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,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
@@ -5444,12 +5444,14 @@ declare class Tokenizer {
5444
5444
  next(): Token | undefined;
5445
5445
  isGreedyAvailable(): boolean;
5446
5446
  greedy(): string;
5447
+ isCatchAllAvailable(): boolean;
5448
+ catchAll(): IncomingSegment[];
5447
5449
  private findNextPosition;
5448
5450
  private findTextTokenStart;
5449
5451
  private readTextToken;
5450
5452
  }
5451
5453
  //#endregion
5452
- //#region src/routing/capturer.d.ts
5454
+ //#region src/routing/parameter.d.ts
5453
5455
  interface Capturer<T> {
5454
5456
  typeInstruction: TypeInstruction;
5455
5457
  capture(tokenizer: Tokenizer): T | undefined;
@@ -5468,6 +5470,9 @@ type TypeInstruction =
5468
5470
  | {
5469
5471
  type: 'greedy';
5470
5472
  }
5473
+ | {
5474
+ type: 'catchAll';
5475
+ }
5471
5476
  | {
5472
5477
  type: 'union';
5473
5478
  members: string[];
@@ -5476,8 +5481,6 @@ type TypeInstruction =
5476
5481
  type: 'segment';
5477
5482
  segmentType: Exclude<Token, string>['type'];
5478
5483
  };
5479
- //#endregion
5480
- //#region src/routing/parameter.d.ts
5481
5484
  declare class Parameter<T> {
5482
5485
  readonly capturer: Capturer<T>;
5483
5486
  description?: string;
@@ -5489,6 +5492,7 @@ declare namespace param {
5489
5492
  function num(): Parameter<number>;
5490
5493
  function str(): Parameter<string>;
5491
5494
  function greedy(): Parameter<string>;
5495
+ function catchAll(): Parameter<IncomingSegment[]>;
5492
5496
  function union<T extends string>(...literals: T[]): Parameter<T>;
5493
5497
  function segment<T extends Exclude<Token, string>['type']>(
5494
5498
  type: T,
@@ -5505,21 +5509,21 @@ declare namespace param {
5505
5509
  //#region src/routing/router.d.ts
5506
5510
  type Pattern = Record<string, Parameter<any>>;
5507
5511
  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>;
5512
+ type Executor<P extends Pattern> = (session: Session, params: ParamsOf<P>) => void | Promise<void>;
5509
5513
  type SessionPredicate = (session: Session) => boolean;
5510
- interface Command {
5514
+ interface Command<P extends Pattern> {
5511
5515
  name: string;
5512
- pattern: Pattern;
5513
- handler: (session: Session, params: any) => void | Promise<void>;
5516
+ pattern: P;
5517
+ execute: Executor<P>;
5514
5518
  }
5515
- interface RawPattern {
5516
- pattern: Pattern;
5517
- handler: (session: Session, params: any) => void | Promise<void>;
5519
+ interface RawPattern<P extends Pattern> {
5520
+ pattern: P;
5521
+ execute: Executor<P>;
5518
5522
  }
5519
5523
  type RouteEntry =
5520
5524
  | {
5521
5525
  type: 'command';
5522
- command: Command;
5526
+ command: Command<Pattern>;
5523
5527
  }
5524
5528
  | {
5525
5529
  type: 'group';
@@ -5533,30 +5537,30 @@ type RouteEntry =
5533
5537
  }
5534
5538
  | {
5535
5539
  type: 'rawPattern';
5536
- rawPattern: RawPattern;
5540
+ rawPattern: RawPattern<Pattern>;
5537
5541
  };
5538
5542
  type RouteBranch =
5539
5543
  | {
5540
5544
  type: 'command';
5541
5545
  path: string[];
5542
- command: Command;
5546
+ command: Command<Pattern>;
5543
5547
  }
5544
5548
  | {
5545
5549
  type: 'rawPattern';
5546
5550
  path: string[];
5547
- rawPattern: RawPattern;
5551
+ rawPattern: RawPattern<Pattern>;
5548
5552
  };
5549
5553
  type RouteMatchResult =
5550
5554
  | {
5551
5555
  type: 'command';
5552
5556
  path: string[];
5553
- command: Command;
5557
+ command: Command<Pattern>;
5554
5558
  params: any;
5555
5559
  }
5556
5560
  | {
5557
5561
  type: 'rawPattern';
5558
5562
  path: string[];
5559
- rawPattern: RawPattern;
5563
+ rawPattern: RawPattern<Pattern>;
5560
5564
  params: any;
5561
5565
  };
5562
5566
  interface Session {
@@ -5566,8 +5570,8 @@ interface Session {
5566
5570
  declare class Router {
5567
5571
  private readonly entries;
5568
5572
  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;
5573
+ command<P extends Pattern>(command: Command<P>): this;
5574
+ rawPattern<P extends Pattern>(rawPattern: RawPattern<P>): this;
5571
5575
  group(name: string): Router;
5572
5576
  filter(predicate: SessionPredicate): Router;
5573
5577
  routes(): RouteEntry[];
@@ -5580,6 +5584,7 @@ declare class Router {
5580
5584
  private matchGroup;
5581
5585
  private matchRawPattern;
5582
5586
  private branchesFrom;
5587
+ private validatePattern;
5583
5588
  private capturePattern;
5584
5589
  }
5585
5590
  //#endregion
@@ -5775,14 +5780,15 @@ declare namespace seg {
5775
5780
  //#endregion
5776
5781
  export {
5777
5782
  ApiEndpoints,
5783
+ Capturer,
5778
5784
  Command,
5779
5785
  Context,
5780
5786
  ContextOptions,
5781
5787
  ContextUrlOptions,
5782
5788
  Disposable,
5783
5789
  EventMap,
5790
+ Executor,
5784
5791
  Filter,
5785
- Handler,
5786
5792
  Injection,
5787
5793
  LogHandler,
5788
5794
  LogLevel,
@@ -5803,6 +5809,7 @@ export {
5803
5809
  ServiceClass,
5804
5810
  Session,
5805
5811
  SessionPredicate,
5812
+ TypeInstruction,
5806
5813
  combineLogHandlers,
5807
5814
  createMilkyClient,
5808
5815
  definePlugin,
package/dist/index.mjs CHANGED
@@ -115,21 +115,48 @@ var Tokenizer = class {
115
115
  return token;
116
116
  }
117
117
  isGreedyAvailable() {
118
- if (this.offset !== this.segments.length - 1) return false;
119
- const segment = this.segments[this.offset];
118
+ const position = this.findNextPosition();
119
+ if (position === void 0) return false;
120
+ const segment = this.segments[position.offset];
120
121
  if (segment?.type !== "text") return false;
121
- return this.findTextTokenStart(segment.data.text, this.subOffset ?? 0) < segment.data.text.length;
122
+ return this.findTextTokenStart(segment.data.text, position.subOffset ?? 0) < segment.data.text.length;
122
123
  }
123
124
  greedy() {
124
- if (!this.isGreedyAvailable()) throw new Error("Greedy token is not available");
125
- const segment = this.segments[this.offset];
125
+ const position = this.findNextPosition();
126
+ if (position === void 0) throw new Error("Greedy token is not available");
127
+ const segment = this.segments[position.offset];
126
128
  if (segment.type !== "text") throw new Error("Greedy token is not available");
127
- const tokenStart = this.findTextTokenStart(segment.data.text, this.subOffset ?? 0);
129
+ const tokenStart = this.findTextTokenStart(segment.data.text, position.subOffset ?? 0);
130
+ if (tokenStart >= segment.data.text.length) throw new Error("Greedy token is not available");
128
131
  const token = segment.data.text.slice(tokenStart);
129
- this.offset = this.segments.length;
132
+ this.offset = position.offset + 1;
130
133
  this.subOffset = void 0;
131
134
  return token;
132
135
  }
136
+ isCatchAllAvailable() {
137
+ return this.findNextPosition() !== void 0;
138
+ }
139
+ catchAll() {
140
+ const position = this.findNextPosition();
141
+ if (position === void 0) throw new Error("Catch-all token is not available");
142
+ const segment = this.segments[position.offset];
143
+ const segments = [];
144
+ if (segment.type === "text") {
145
+ const textStart = this.findTextTokenStart(segment.data.text, position.subOffset ?? 0);
146
+ const text = segment.data.text.slice(textStart);
147
+ if (text.length > 0) segments.push({
148
+ ...segment,
149
+ data: {
150
+ ...segment.data,
151
+ text
152
+ }
153
+ });
154
+ segments.push(...this.segments.slice(position.offset + 1));
155
+ } else segments.push(...this.segments.slice(position.offset));
156
+ this.offset = this.segments.length;
157
+ this.subOffset = void 0;
158
+ return segments;
159
+ }
133
160
  findNextPosition() {
134
161
  let offset = this.offset;
135
162
  let subOffset = this.subOffset;
@@ -164,25 +191,16 @@ var Tokenizer = class {
164
191
  var Router = class Router {
165
192
  entries = [];
166
193
  groups = /* @__PURE__ */ new Map();
167
- command(name, pattern, handler) {
168
- const command = {
169
- name,
170
- pattern,
171
- handler
172
- };
194
+ command(command) {
195
+ this.validatePattern(command.pattern);
173
196
  this.entries.push({
174
197
  type: "command",
175
198
  command
176
199
  });
177
200
  return this;
178
201
  }
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
- };
202
+ rawPattern(rawPattern) {
203
+ this.validatePattern(rawPattern.pattern, { rawPattern: true });
186
204
  this.entries.push({
187
205
  type: "rawPattern",
188
206
  rawPattern
@@ -226,10 +244,10 @@ var Router = class Router {
226
244
  if (match === void 0) return false;
227
245
  switch (match.type) {
228
246
  case "command":
229
- await match.command.handler(session, match.params);
247
+ await match.command.execute(session, match.params);
230
248
  break;
231
249
  case "rawPattern":
232
- await match.rawPattern.handler(session, match.params);
250
+ await match.rawPattern.execute(session, match.params);
233
251
  break;
234
252
  }
235
253
  return true;
@@ -308,6 +326,15 @@ var Router = class Router {
308
326
  }
309
327
  return branches;
310
328
  }
329
+ validatePattern(pattern, options) {
330
+ const entries = Object.entries(pattern);
331
+ if (options?.rawPattern && entries.length === 0) throw new Error("Raw pattern must have at least one parameter.");
332
+ if (options?.rawPattern && entries[0]?.[1].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.");
333
+ const catchAllEntryIndex = entries.findIndex(([, parameter]) => {
334
+ return parameter.capturer.typeInstruction.type === "catchAll";
335
+ });
336
+ if (catchAllEntryIndex !== -1 && catchAllEntryIndex !== entries.length - 1) throw new Error("Catch-all parameters must be the last parameter in a pattern.");
337
+ }
311
338
  capturePattern(pattern, tokenizer) {
312
339
  const initialState = tokenizer.getState();
313
340
  const params = {};
@@ -1060,11 +1087,22 @@ let seg;
1060
1087
  const milkyVersion = "1.3";
1061
1088
  const milkyPackageVersion = "1.3.0-rc.1";
1062
1089
  //#endregion
1063
- //#region src/routing/capturer.ts
1064
- let capturer;
1065
- (function(_capturer) {
1090
+ //#region src/routing/parameter.ts
1091
+ var Parameter = class {
1092
+ capturer;
1093
+ description;
1094
+ constructor(capturer) {
1095
+ this.capturer = capturer;
1096
+ }
1097
+ describe(description) {
1098
+ this.description = description;
1099
+ return this;
1100
+ }
1101
+ };
1102
+ let param;
1103
+ (function(_param) {
1066
1104
  function literal(literal) {
1067
- return {
1105
+ return new Parameter({
1068
1106
  typeInstruction: {
1069
1107
  type: "literal",
1070
1108
  literal
@@ -1076,44 +1114,62 @@ let capturer;
1076
1114
  return token;
1077
1115
  }
1078
1116
  }
1079
- };
1117
+ });
1080
1118
  }
1081
- _capturer.literal = literal;
1082
- _capturer.num = {
1083
- typeInstruction: { type: "number" },
1084
- capture(tokenizer) {
1085
- const token = tokenizer.peek();
1086
- if (typeof token === "string") {
1087
- const parsed = Number(token);
1088
- if (!Number.isNaN(parsed)) {
1119
+ _param.literal = literal;
1120
+ function num() {
1121
+ return new Parameter({
1122
+ typeInstruction: { type: "number" },
1123
+ capture(tokenizer) {
1124
+ const token = tokenizer.peek();
1125
+ if (typeof token === "string") {
1126
+ const parsed = Number(token);
1127
+ if (!Number.isNaN(parsed)) {
1128
+ tokenizer.next();
1129
+ return parsed;
1130
+ }
1131
+ }
1132
+ }
1133
+ });
1134
+ }
1135
+ _param.num = num;
1136
+ function str() {
1137
+ return new Parameter({
1138
+ typeInstruction: { type: "string" },
1139
+ capture(tokenizer) {
1140
+ const token = tokenizer.peek();
1141
+ if (typeof token === "string") {
1089
1142
  tokenizer.next();
1090
- return parsed;
1143
+ return token;
1091
1144
  }
1092
1145
  }
1093
- }
1094
- };
1095
- _capturer.str = {
1096
- typeInstruction: { type: "string" },
1097
- capture(tokenizer) {
1098
- const token = tokenizer.peek();
1099
- if (typeof token === "string") {
1100
- tokenizer.next();
1101
- return token;
1146
+ });
1147
+ }
1148
+ _param.str = str;
1149
+ function greedy() {
1150
+ return new Parameter({
1151
+ typeInstruction: { type: "greedy" },
1152
+ capture(tokenizer) {
1153
+ if (tokenizer.isGreedyAvailable()) return tokenizer.greedy();
1102
1154
  }
1103
- }
1104
- };
1105
- _capturer.greedy = {
1106
- typeInstruction: { type: "greedy" },
1107
- capture(tokenizer) {
1108
- if (tokenizer.isGreedyAvailable()) return tokenizer.greedy();
1109
- }
1110
- };
1111
- function union(...members) {
1112
- const literalSet = new Set(members);
1113
- return {
1155
+ });
1156
+ }
1157
+ _param.greedy = greedy;
1158
+ function catchAll() {
1159
+ return new Parameter({
1160
+ typeInstruction: { type: "catchAll" },
1161
+ capture(tokenizer) {
1162
+ if (tokenizer.isCatchAllAvailable()) return tokenizer.catchAll();
1163
+ }
1164
+ });
1165
+ }
1166
+ _param.catchAll = catchAll;
1167
+ function union(...literals) {
1168
+ const literalSet = new Set(literals);
1169
+ return new Parameter({
1114
1170
  typeInstruction: {
1115
1171
  type: "union",
1116
- members
1172
+ members: literals
1117
1173
  },
1118
1174
  capture(tokenizer) {
1119
1175
  const token = tokenizer.peek();
@@ -1122,11 +1178,11 @@ let capturer;
1122
1178
  return token;
1123
1179
  }
1124
1180
  }
1125
- };
1181
+ });
1126
1182
  }
1127
- _capturer.union = union;
1183
+ _param.union = union;
1128
1184
  function segment(type) {
1129
- return {
1185
+ return new Parameter({
1130
1186
  typeInstruction: {
1131
1187
  type: "segment",
1132
1188
  segmentType: type
@@ -1138,47 +1194,7 @@ let capturer;
1138
1194
  return token;
1139
1195
  }
1140
1196
  }
1141
- };
1142
- }
1143
- _capturer.segment = segment;
1144
- })(capturer || (capturer = {}));
1145
- //#endregion
1146
- //#region src/routing/parameter.ts
1147
- var Parameter = class {
1148
- capturer;
1149
- description;
1150
- constructor(capturer) {
1151
- this.capturer = capturer;
1152
- }
1153
- describe(description) {
1154
- this.description = description;
1155
- return this;
1156
- }
1157
- };
1158
- let param;
1159
- (function(_param) {
1160
- function literal(literal) {
1161
- return new Parameter(capturer.literal(literal));
1162
- }
1163
- _param.literal = literal;
1164
- function num() {
1165
- return new Parameter(capturer.num);
1166
- }
1167
- _param.num = num;
1168
- function str() {
1169
- return new Parameter(capturer.str);
1170
- }
1171
- _param.str = str;
1172
- function greedy() {
1173
- return new Parameter(capturer.greedy);
1174
- }
1175
- _param.greedy = greedy;
1176
- function union(...literals) {
1177
- return new Parameter(capturer.union(...literals));
1178
- }
1179
- _param.union = union;
1180
- function segment(type) {
1181
- return new Parameter(capturer.segment(type));
1197
+ });
1182
1198
  }
1183
1199
  _param.segment = segment;
1184
1200
  })(param || (param = {}));
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@fraqjs/fraq",
3
3
  "type": "module",
4
- "version": "0.7.0",
4
+ "version": "0.8.1",
5
5
  "description": "Milky Bot framework",
6
6
  "files": [
7
7
  "dist"