@fraqjs/fraq 0.1.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/dist/index.mjs ADDED
@@ -0,0 +1,777 @@
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
+ this.baseUrl = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
10
+ this.wsBaseUrl = this.baseUrl.replace(/^http/, "ws");
11
+ this.accessToken = options?.accessToken;
12
+ this.baseHeaders = { "Content-Type": "application/json" };
13
+ if (options?.accessToken) this.baseHeaders.Authorization = `Bearer ${options.accessToken}`;
14
+ }
15
+ async callApi(endpoint, params) {
16
+ const response = await fetch(`${this.baseUrl}/api/${endpoint}`, {
17
+ method: "POST",
18
+ body: JSON.stringify(params ?? {}),
19
+ headers: this.baseHeaders
20
+ });
21
+ if (!response.ok) throw new Error(`API call failed with status ${response.status}`);
22
+ const json = await response.json();
23
+ if (json.status === "failed") throw new Error(`API call failed with retcode ${json.retcode}: ${json.message}`);
24
+ return json.data;
25
+ }
26
+ async openEventWebSocket(onEvent) {
27
+ const ws = new WebSocket(`${this.wsBaseUrl}/event${this.accessToken ? `?access_token=${this.accessToken}` : ""}`);
28
+ ws.addEventListener("message", onEvent);
29
+ return new Promise((resolve, reject) => {
30
+ ws.addEventListener("open", () => resolve(ws));
31
+ ws.addEventListener("error", (event) => reject(/* @__PURE__ */ new Error(`WebSocket error: ${event}`)));
32
+ });
33
+ }
34
+ };
35
+ function createMilkyClient(...params) {
36
+ const base = new MilkyClientBase(...params);
37
+ return new Proxy(base, { get(target, endpoint) {
38
+ if (typeof endpoint === "string" && endpoint.includes("_")) return (params) => target.callApi(endpoint, params);
39
+ else return target[endpoint];
40
+ } });
41
+ }
42
+ //#endregion
43
+ //#region src/routing/tokenizer.ts
44
+ var Tokenizer = class {
45
+ segments;
46
+ offset = 0;
47
+ subOffset = void 0;
48
+ constructor(segments) {
49
+ this.segments = segments;
50
+ }
51
+ getState() {
52
+ return {
53
+ offset: this.offset,
54
+ subOffset: this.subOffset
55
+ };
56
+ }
57
+ setState(state) {
58
+ this.offset = state.offset;
59
+ this.subOffset = state.subOffset;
60
+ }
61
+ hasNext() {
62
+ return this.findNextPosition() !== void 0;
63
+ }
64
+ peek() {
65
+ const position = this.findNextPosition();
66
+ if (position === void 0) return;
67
+ const segment = this.segments[position.offset];
68
+ if (segment.type === "text") return this.readTextToken(segment.data.text, position.subOffset ?? 0);
69
+ return segment;
70
+ }
71
+ next() {
72
+ const position = this.findNextPosition();
73
+ if (position === void 0) return;
74
+ const segment = this.segments[position.offset];
75
+ if (segment.type !== "text") {
76
+ this.offset = position.offset + 1;
77
+ this.subOffset = void 0;
78
+ return segment;
79
+ }
80
+ const subOffset = position.subOffset ?? 0;
81
+ const token = this.readTextToken(segment.data.text, subOffset);
82
+ const tokenEnd = subOffset + token.length;
83
+ const nextSubOffset = this.findTextTokenStart(segment.data.text, tokenEnd);
84
+ if (nextSubOffset < segment.data.text.length) {
85
+ this.offset = position.offset;
86
+ this.subOffset = nextSubOffset;
87
+ } else {
88
+ this.offset = position.offset + 1;
89
+ this.subOffset = void 0;
90
+ }
91
+ return token;
92
+ }
93
+ isGreedyAvailable() {
94
+ if (this.offset !== this.segments.length - 1) return false;
95
+ const segment = this.segments[this.offset];
96
+ if (segment?.type !== "text") return false;
97
+ return this.findTextTokenStart(segment.data.text, this.subOffset ?? 0) < segment.data.text.length;
98
+ }
99
+ greedy() {
100
+ if (!this.isGreedyAvailable()) throw new Error("Greedy token is not available");
101
+ const segment = this.segments[this.offset];
102
+ if (segment.type !== "text") throw new Error("Greedy token is not available");
103
+ const tokenStart = this.findTextTokenStart(segment.data.text, this.subOffset ?? 0);
104
+ const token = segment.data.text.slice(tokenStart);
105
+ this.offset = this.segments.length;
106
+ this.subOffset = void 0;
107
+ return token;
108
+ }
109
+ findNextPosition() {
110
+ let offset = this.offset;
111
+ let subOffset = this.subOffset;
112
+ while (offset < this.segments.length) {
113
+ const segment = this.segments[offset];
114
+ if (segment.type !== "text") return {
115
+ offset,
116
+ subOffset: void 0
117
+ };
118
+ const tokenStart = this.findTextTokenStart(segment.data.text, subOffset ?? 0);
119
+ if (tokenStart < segment.data.text.length) return {
120
+ offset,
121
+ subOffset: tokenStart
122
+ };
123
+ offset += 1;
124
+ subOffset = void 0;
125
+ }
126
+ }
127
+ findTextTokenStart(text, from) {
128
+ let offset = from;
129
+ while (offset < text.length && text[offset] === " ") offset += 1;
130
+ return offset;
131
+ }
132
+ readTextToken(text, from) {
133
+ let offset = from;
134
+ while (offset < text.length && text[offset] !== " ") offset += 1;
135
+ return text.slice(from, offset);
136
+ }
137
+ };
138
+ //#endregion
139
+ //#region src/routing/router.ts
140
+ var Router = class Router {
141
+ entries = [];
142
+ groups = /* @__PURE__ */ new Map();
143
+ command(name, pattern, handler) {
144
+ const command = {
145
+ name,
146
+ pattern,
147
+ handler
148
+ };
149
+ this.entries.push({
150
+ type: "command",
151
+ command
152
+ });
153
+ return this;
154
+ }
155
+ rawPattern(pattern, handler) {
156
+ if (Object.keys(pattern).length === 0) throw new Error("Raw pattern must have at least one parameter.");
157
+ 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.");
158
+ const rawPattern = {
159
+ pattern,
160
+ handler
161
+ };
162
+ this.entries.push({
163
+ type: "rawPattern",
164
+ rawPattern
165
+ });
166
+ return this;
167
+ }
168
+ group(name) {
169
+ let router = this.groups.get(name);
170
+ if (!router) {
171
+ router = new Router();
172
+ this.groups.set(name, router);
173
+ this.entries.push({
174
+ type: "group",
175
+ name,
176
+ router
177
+ });
178
+ }
179
+ return router;
180
+ }
181
+ filter(predicate) {
182
+ const router = new Router();
183
+ this.entries.push({
184
+ type: "filter",
185
+ predicate,
186
+ router
187
+ });
188
+ return router;
189
+ }
190
+ async dispatch(session, message) {
191
+ const tokenizer = new Tokenizer(message.segments);
192
+ return await this.process(session, tokenizer);
193
+ }
194
+ async process(session, tokenizer) {
195
+ const initialState = tokenizer.getState();
196
+ for (const entry of this.entries) {
197
+ tokenizer.setState(initialState);
198
+ if (await this.processEntry(entry, session, tokenizer)) return true;
199
+ }
200
+ tokenizer.setState(initialState);
201
+ return false;
202
+ }
203
+ async processEntry(entry, session, tokenizer) {
204
+ switch (entry.type) {
205
+ case "command": return await this.processCommand(entry.command, session, tokenizer);
206
+ case "group": return await this.processGroup(entry.name, entry.router, session, tokenizer);
207
+ case "filter":
208
+ if (entry.predicate(session) !== true) return false;
209
+ return await entry.router.process(session, tokenizer);
210
+ case "rawPattern": return await this.processRawPattern(entry.rawPattern, session, tokenizer);
211
+ }
212
+ }
213
+ async processCommand(command, session, tokenizer) {
214
+ const token = tokenizer.peek();
215
+ if (typeof token !== "string" || token !== command.name) return false;
216
+ tokenizer.next();
217
+ const params = this.capturePattern(command.pattern, tokenizer);
218
+ if (params === void 0 || tokenizer.hasNext()) return false;
219
+ await command.handler(session, params);
220
+ return true;
221
+ }
222
+ async processGroup(name, router, session, tokenizer) {
223
+ const token = tokenizer.peek();
224
+ if (typeof token !== "string" || token !== name) return false;
225
+ tokenizer.next();
226
+ return await router.process(session, tokenizer);
227
+ }
228
+ async processRawPattern(rawPattern, session, tokenizer) {
229
+ const params = this.capturePattern(rawPattern.pattern, tokenizer);
230
+ if (params === void 0 || tokenizer.hasNext()) return false;
231
+ await rawPattern.handler(session, params);
232
+ return true;
233
+ }
234
+ capturePattern(pattern, tokenizer) {
235
+ const initialState = tokenizer.getState();
236
+ const params = {};
237
+ for (const [name, parameter] of Object.entries(pattern)) {
238
+ const value = parameter.capturer.capture(tokenizer);
239
+ if (value === void 0) {
240
+ tokenizer.setState(initialState);
241
+ return;
242
+ }
243
+ params[name] = value;
244
+ }
245
+ return params;
246
+ }
247
+ };
248
+ //#endregion
249
+ //#region src/core/service.ts
250
+ function getServiceName(service) {
251
+ return service.name || "<anonymous service>";
252
+ }
253
+ //#endregion
254
+ //#region src/core/context.ts
255
+ const INITIAL_RECONNECT_DELAY_MS = 1e3;
256
+ const MAX_RECONNECT_DELAY_MS = 3e4;
257
+ var Context = class Context {
258
+ client;
259
+ parent;
260
+ router = new Router();
261
+ eventBus = mitt();
262
+ plugins = [];
263
+ services = /* @__PURE__ */ new Map();
264
+ subContexts = [];
265
+ isStarted = false;
266
+ constructor(client, parent, filter) {
267
+ this.client = client;
268
+ this.parent = parent;
269
+ parent?.eventBus.on("*", (type, event) => {
270
+ if (filter) {
271
+ const predicate = filter[type];
272
+ if (predicate && !predicate(event)) return;
273
+ }
274
+ this.eventBus.emit(type, event);
275
+ });
276
+ this.eventBus.on("message_receive", async ({ data: message }) => {
277
+ try {
278
+ this.router.dispatch(this.createSession(message), message);
279
+ } catch (error) {
280
+ console.error("Error routing command:", error);
281
+ }
282
+ });
283
+ }
284
+ on(type, handler) {
285
+ this.eventBus.on(type, async (event) => {
286
+ try {
287
+ await handler(event);
288
+ } catch (error) {
289
+ console.error(`Error handling event ${type}:`, error);
290
+ }
291
+ });
292
+ }
293
+ install(plugin, ...args) {
294
+ this.plugins.push({
295
+ plugin,
296
+ args
297
+ });
298
+ }
299
+ provide(service, instance) {
300
+ if (this.services.has(service)) throw new Error(`Service ${getServiceName(service)} has already been provided in this context.`);
301
+ this.services.set(service, instance);
302
+ }
303
+ resolve(service) {
304
+ const instance = this.tryResolve(service);
305
+ if (instance === void 0) throw new Error(`Service ${getServiceName(service)} has not been provided.`);
306
+ return instance;
307
+ }
308
+ tryResolve(service) {
309
+ if (this.services.has(service)) return this.services.get(service);
310
+ return this.parent?.tryResolve(service);
311
+ }
312
+ isProvided(service) {
313
+ return this.tryResolve(service) !== void 0;
314
+ }
315
+ fork(filter) {
316
+ const subContext = new Context(this.client, this, filter);
317
+ this.subContexts.push(subContext);
318
+ return subContext;
319
+ }
320
+ async start() {
321
+ if (this.isStarted) return;
322
+ await this.applyPlugins();
323
+ for (const subContext of this.subContexts) await subContext.start();
324
+ this.isStarted = true;
325
+ if (!this.parent) this.connectEventWebSocket();
326
+ }
327
+ async applyPlugins() {
328
+ for (const { plugin, args } of this.sortPlugins()) {
329
+ const providedBeforeApply = new Set(this.services.keys());
330
+ await plugin.apply(this, ...args);
331
+ for (const service of plugin.provides ?? []) if (!this.services.has(service) || providedBeforeApply.has(service)) throw new Error(`${this.describePlugin(plugin)} declares service ${getServiceName(service)} but did not provide it.`);
332
+ }
333
+ }
334
+ sortPlugins() {
335
+ this.validateUniqueProvidedServices();
336
+ const pending = [...this.plugins];
337
+ const sorted = [];
338
+ const available = /* @__PURE__ */ new Set();
339
+ for (const service of this.collectAvailableServices()) available.add(service);
340
+ while (pending.length > 0) {
341
+ const nextIndex = pending.findIndex(({ plugin }) => this.areRequirementsAvailable(plugin, available));
342
+ if (nextIndex === -1) throw this.createUnresolvablePluginError(pending, available);
343
+ const [next] = pending.splice(nextIndex, 1);
344
+ sorted.push(next);
345
+ for (const service of next.plugin.provides ?? []) available.add(service);
346
+ }
347
+ return sorted;
348
+ }
349
+ validateUniqueProvidedServices() {
350
+ const providers = /* @__PURE__ */ new Map();
351
+ for (const { plugin } of this.plugins) for (const service of plugin.provides ?? []) {
352
+ const existingProvider = providers.get(service);
353
+ if (existingProvider) throw new Error(`Service ${getServiceName(service)} is declared by multiple plugins: ${this.describePlugin(existingProvider)} and ${this.describePlugin(plugin)}.`);
354
+ providers.set(service, plugin);
355
+ }
356
+ }
357
+ collectAvailableServices() {
358
+ const services = [...this.services.keys()];
359
+ if (this.parent) services.push(...this.parent.collectAvailableServices());
360
+ return services;
361
+ }
362
+ areRequirementsAvailable(plugin, available) {
363
+ return (plugin.requires ?? []).every((service) => available.has(service));
364
+ }
365
+ createUnresolvablePluginError(pending, available) {
366
+ const missingRequirements = /* @__PURE__ */ new Map();
367
+ const pendingProviders = /* @__PURE__ */ new Set();
368
+ for (const { plugin } of pending) for (const service of plugin.provides ?? []) pendingProviders.add(service);
369
+ for (const { plugin } of pending) for (const service of plugin.requires ?? []) if (!available.has(service)) {
370
+ const plugins = missingRequirements.get(service) ?? [];
371
+ plugins.push(plugin);
372
+ missingRequirements.set(service, plugins);
373
+ }
374
+ const lines = [...missingRequirements].map(([service, plugins]) => {
375
+ const dependents = plugins.map((plugin) => this.describePlugin(plugin)).join(", ");
376
+ const reason = pendingProviders.has(service) ? "blocked by a dependency cycle" : "no installed plugin provides it";
377
+ return `${getServiceName(service)} required by ${dependents} (${reason})`;
378
+ });
379
+ return /* @__PURE__ */ new Error(`Unable to resolve plugin service dependencies: ${lines.join("; ")}.`);
380
+ }
381
+ describePlugin(plugin) {
382
+ const pluginIndex = this.plugins.findIndex((installed) => installed.plugin === plugin);
383
+ return plugin.constructor.name && plugin.constructor.name !== "Object" ? plugin.constructor.name : `plugin #${pluginIndex + 1}`;
384
+ }
385
+ async connectEventWebSocket() {
386
+ let reconnectDelay = INITIAL_RECONNECT_DELAY_MS;
387
+ while (this.isStarted) {
388
+ try {
389
+ const ws = await this.client.openEventWebSocket((event) => this.handleWebSocketMessage(event));
390
+ reconnectDelay = INITIAL_RECONNECT_DELAY_MS;
391
+ await new Promise((resolve) => {
392
+ ws.addEventListener("close", () => resolve(), { once: true });
393
+ });
394
+ } catch (error) {
395
+ console.error("Error connecting event WebSocket:", error);
396
+ }
397
+ await this.delay(reconnectDelay);
398
+ reconnectDelay = Math.min(reconnectDelay * 2, MAX_RECONNECT_DELAY_MS);
399
+ }
400
+ }
401
+ handleWebSocketMessage(event) {
402
+ try {
403
+ if (typeof event.data !== "string") throw new Error(`Expected text frame, got ${typeof event.data}`);
404
+ const payload = JSON.parse(event.data);
405
+ this.eventBus.emit(payload.event_type, payload);
406
+ } catch (error) {
407
+ console.error("Error handling WebSocket event:", error);
408
+ }
409
+ }
410
+ delay(ms) {
411
+ return new Promise((resolve) => setTimeout(resolve, ms));
412
+ }
413
+ createSession(message) {
414
+ return {
415
+ raw: message,
416
+ reply: async (segments) => {
417
+ try {
418
+ switch (message.message_scene) {
419
+ case "friend":
420
+ await this.client.send_private_message({
421
+ user_id: message.peer_id,
422
+ message: segments
423
+ });
424
+ break;
425
+ case "group":
426
+ await this.client.send_group_message({
427
+ group_id: message.peer_id,
428
+ message: segments
429
+ });
430
+ break;
431
+ }
432
+ } catch (error) {
433
+ console.error("Error sending reply:", error);
434
+ }
435
+ }
436
+ };
437
+ }
438
+ static create(options) {
439
+ return new Context(createMilkyClient(options.connect.baseUrl, { accessToken: options.connect.accessToken }));
440
+ }
441
+ };
442
+ //#endregion
443
+ //#region src/core/filter.ts
444
+ const eventKeys = [
445
+ "bot_offline",
446
+ "message_receive",
447
+ "message_recall",
448
+ "peer_pin_change",
449
+ "friend_request",
450
+ "group_join_request",
451
+ "group_invited_join_request",
452
+ "group_invitation",
453
+ "friend_nudge",
454
+ "friend_file_upload",
455
+ "group_admin_change",
456
+ "group_essence_message_change",
457
+ "group_member_increase",
458
+ "group_member_decrease",
459
+ "group_disband",
460
+ "group_name_change",
461
+ "group_message_reaction",
462
+ "group_mute",
463
+ "group_whole_mute",
464
+ "group_nudge",
465
+ "group_file_upload"
466
+ ];
467
+ let filter;
468
+ (function(_filter) {
469
+ function define(filter) {
470
+ return filter;
471
+ }
472
+ _filter.define = define;
473
+ function allPass() {
474
+ const result = {};
475
+ for (const key of eventKeys) result[key] = () => true;
476
+ return result;
477
+ }
478
+ _filter.allPass = allPass;
479
+ function friend(uinList) {
480
+ const uinSet = new Set(uinList);
481
+ return {
482
+ message_receive: ({ data }) => data.message_scene === "friend" && uinSet.has(data.peer_id),
483
+ message_recall: ({ data }) => data.message_scene === "friend" && uinSet.has(data.peer_id),
484
+ peer_pin_change: ({ data }) => data.message_scene === "friend" && uinSet.has(data.peer_id),
485
+ group_invitation: ({ data }) => uinSet.has(data.initiator_id),
486
+ friend_nudge: ({ data }) => uinSet.has(data.user_id),
487
+ friend_file_upload: ({ data }) => uinSet.has(data.user_id)
488
+ };
489
+ }
490
+ _filter.friend = friend;
491
+ function group(uinList) {
492
+ const uinSet = new Set(uinList);
493
+ return {
494
+ message_receive: ({ data }) => data.message_scene === "group" && uinSet.has(data.peer_id),
495
+ message_recall: ({ data }) => data.message_scene === "group" && uinSet.has(data.peer_id),
496
+ peer_pin_change: ({ data }) => data.message_scene === "group" && uinSet.has(data.peer_id),
497
+ group_join_request: ({ data }) => uinSet.has(data.group_id),
498
+ group_invited_join_request: ({ data }) => uinSet.has(data.group_id),
499
+ group_admin_change: ({ data }) => uinSet.has(data.group_id),
500
+ group_essence_message_change: ({ data }) => uinSet.has(data.group_id),
501
+ group_member_increase: ({ data }) => uinSet.has(data.group_id),
502
+ group_member_decrease: ({ data }) => uinSet.has(data.group_id),
503
+ group_name_change: ({ data }) => uinSet.has(data.group_id),
504
+ group_message_reaction: ({ data }) => uinSet.has(data.group_id),
505
+ group_mute: ({ data }) => uinSet.has(data.group_id),
506
+ group_whole_mute: ({ data }) => uinSet.has(data.group_id),
507
+ group_nudge: ({ data }) => uinSet.has(data.group_id),
508
+ group_file_upload: ({ data }) => uinSet.has(data.group_id)
509
+ };
510
+ }
511
+ _filter.group = group;
512
+ function admin() {
513
+ return { message_receive: ({ data }) => data.message_scene === "group" && data.group_member.role !== "member" };
514
+ }
515
+ _filter.admin = admin;
516
+ function or(...filters) {
517
+ const result = {};
518
+ for (const key of eventKeys) result[key] = (event) => {
519
+ for (const filter of filters) {
520
+ const predicate = filter[key];
521
+ if (predicate?.(event)) return true;
522
+ }
523
+ return false;
524
+ };
525
+ return result;
526
+ }
527
+ _filter.or = or;
528
+ function and(...filters) {
529
+ const result = {};
530
+ for (const key of eventKeys) result[key] = (event) => {
531
+ for (const filter of filters) {
532
+ const predicate = filter[key];
533
+ if (!predicate?.(event)) return false;
534
+ }
535
+ return true;
536
+ };
537
+ return result;
538
+ }
539
+ _filter.and = and;
540
+ })(filter || (filter = {}));
541
+ //#endregion
542
+ //#region src/core/plugin.ts
543
+ function definePlugin(plugin) {
544
+ return plugin;
545
+ }
546
+ //#endregion
547
+ //#region src/protocol/segment.ts
548
+ function msg(strings, ...values) {
549
+ let buffer = "";
550
+ const segments = [];
551
+ for (let i = 0; i < strings.length; i++) {
552
+ buffer += strings[i];
553
+ if (i < values.length) {
554
+ const value = values[i];
555
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") buffer += value.toString();
556
+ else {
557
+ if (buffer) {
558
+ segments.push({
559
+ type: "text",
560
+ data: { text: buffer }
561
+ });
562
+ buffer = "";
563
+ }
564
+ segments.push(value);
565
+ }
566
+ }
567
+ }
568
+ if (buffer) segments.push({
569
+ type: "text",
570
+ data: { text: buffer }
571
+ });
572
+ return segments;
573
+ }
574
+ let seg;
575
+ (function(_seg) {
576
+ function mention(userId) {
577
+ return {
578
+ type: "mention",
579
+ data: { user_id: userId }
580
+ };
581
+ }
582
+ _seg.mention = mention;
583
+ function mentionAll() {
584
+ return {
585
+ type: "mention_all",
586
+ data: {}
587
+ };
588
+ }
589
+ _seg.mentionAll = mentionAll;
590
+ function face(faceId, options) {
591
+ return {
592
+ type: "face",
593
+ data: {
594
+ face_id: faceId.toString(),
595
+ is_large: options?.isLarge ?? false
596
+ }
597
+ };
598
+ }
599
+ _seg.face = face;
600
+ function reply(messageSeq) {
601
+ return {
602
+ type: "reply",
603
+ data: { message_seq: messageSeq }
604
+ };
605
+ }
606
+ _seg.reply = reply;
607
+ function image(uri, options) {
608
+ return {
609
+ type: "image",
610
+ data: {
611
+ uri,
612
+ sub_type: options?.subType,
613
+ summary: options?.summary
614
+ }
615
+ };
616
+ }
617
+ _seg.image = image;
618
+ function record(uri) {
619
+ return {
620
+ type: "record",
621
+ data: { uri }
622
+ };
623
+ }
624
+ _seg.record = record;
625
+ function video(uri, options) {
626
+ return {
627
+ type: "video",
628
+ data: {
629
+ uri,
630
+ thumb_uri: options?.thumbUri
631
+ }
632
+ };
633
+ }
634
+ _seg.video = video;
635
+ function forward(messages, options) {
636
+ return {
637
+ type: "forward",
638
+ data: {
639
+ messages,
640
+ title: options?.title,
641
+ preview: options?.preview,
642
+ summary: options?.summary,
643
+ prompt: options?.prompt
644
+ }
645
+ };
646
+ }
647
+ _seg.forward = forward;
648
+ })(seg || (seg = {}));
649
+ //#endregion
650
+ //#region src/protocol/types.ts
651
+ const milkyVersion = "1.3";
652
+ const milkyPackageVersion = "1.3.0-rc.1";
653
+ //#endregion
654
+ //#region src/routing/capturer.ts
655
+ let capturer;
656
+ (function(_capturer) {
657
+ function literal(literal) {
658
+ return {
659
+ typeInstruction: {
660
+ type: "literal",
661
+ literal
662
+ },
663
+ capture(tokenizer) {
664
+ const token = tokenizer.peek();
665
+ if (typeof token === "string" && token === literal) {
666
+ tokenizer.next();
667
+ return token;
668
+ }
669
+ }
670
+ };
671
+ }
672
+ _capturer.literal = literal;
673
+ _capturer.num = {
674
+ typeInstruction: { type: "number" },
675
+ capture(tokenizer) {
676
+ const token = tokenizer.peek();
677
+ if (typeof token === "string") {
678
+ const parsed = Number(token);
679
+ if (!Number.isNaN(parsed)) {
680
+ tokenizer.next();
681
+ return parsed;
682
+ }
683
+ }
684
+ }
685
+ };
686
+ _capturer.str = {
687
+ typeInstruction: { type: "string" },
688
+ capture(tokenizer) {
689
+ const token = tokenizer.peek();
690
+ if (typeof token === "string") {
691
+ tokenizer.next();
692
+ return token;
693
+ }
694
+ }
695
+ };
696
+ _capturer.greedy = {
697
+ typeInstruction: { type: "greedy" },
698
+ capture(tokenizer) {
699
+ if (tokenizer.isGreedyAvailable()) return tokenizer.greedy();
700
+ }
701
+ };
702
+ function union(...members) {
703
+ const literalSet = new Set(members);
704
+ return {
705
+ typeInstruction: {
706
+ type: "union",
707
+ members
708
+ },
709
+ capture(tokenizer) {
710
+ const token = tokenizer.peek();
711
+ if (typeof token === "string" && literalSet.has(token)) {
712
+ tokenizer.next();
713
+ return token;
714
+ }
715
+ }
716
+ };
717
+ }
718
+ _capturer.union = union;
719
+ function segment(type) {
720
+ return {
721
+ typeInstruction: {
722
+ type: "segment",
723
+ segmentType: type
724
+ },
725
+ capture(tokenizer) {
726
+ const token = tokenizer.peek();
727
+ if (typeof token === "object" && token !== null && token.type === type) {
728
+ tokenizer.next();
729
+ return token;
730
+ }
731
+ }
732
+ };
733
+ }
734
+ _capturer.segment = segment;
735
+ })(capturer || (capturer = {}));
736
+ //#endregion
737
+ //#region src/routing/parameter.ts
738
+ var Parameter = class {
739
+ capturer;
740
+ description;
741
+ constructor(capturer) {
742
+ this.capturer = capturer;
743
+ }
744
+ describe(description) {
745
+ this.description = description;
746
+ return this;
747
+ }
748
+ };
749
+ let param;
750
+ (function(_param) {
751
+ function literal(literal) {
752
+ return new Parameter(capturer.literal(literal));
753
+ }
754
+ _param.literal = literal;
755
+ function num() {
756
+ return new Parameter(capturer.num);
757
+ }
758
+ _param.num = num;
759
+ function str() {
760
+ return new Parameter(capturer.str);
761
+ }
762
+ _param.str = str;
763
+ function greedy() {
764
+ return new Parameter(capturer.greedy);
765
+ }
766
+ _param.greedy = greedy;
767
+ function union(...literals) {
768
+ return new Parameter(capturer.union(...literals));
769
+ }
770
+ _param.union = union;
771
+ function segment(type) {
772
+ return new Parameter(capturer.segment(type));
773
+ }
774
+ _param.segment = segment;
775
+ })(param || (param = {}));
776
+ //#endregion
777
+ export { Context, Parameter, Router, createMilkyClient, definePlugin, filter, milkyPackageVersion, milkyVersion, msg, param, seg };