@fraqjs/fraq 0.3.3 → 0.5.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.d.mts +99 -9
- package/dist/index.mjs +113 -24
- package/package.json +4 -1
package/dist/index.d.mts
CHANGED
|
@@ -5507,6 +5507,58 @@ 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
5508
|
type Handler<P extends Pattern> = (session: Session, params: ParamsOf<P>) => void | Promise<void>;
|
|
5509
5509
|
type SessionPredicate = (session: Session) => boolean;
|
|
5510
|
+
interface Command {
|
|
5511
|
+
name: string;
|
|
5512
|
+
pattern: Pattern;
|
|
5513
|
+
handler: (session: Session, params: any) => void | Promise<void>;
|
|
5514
|
+
}
|
|
5515
|
+
interface RawPattern {
|
|
5516
|
+
pattern: Pattern;
|
|
5517
|
+
handler: (session: Session, params: any) => void | Promise<void>;
|
|
5518
|
+
}
|
|
5519
|
+
type RouteEntry =
|
|
5520
|
+
| {
|
|
5521
|
+
type: 'command';
|
|
5522
|
+
command: Command;
|
|
5523
|
+
}
|
|
5524
|
+
| {
|
|
5525
|
+
type: 'group';
|
|
5526
|
+
name: string;
|
|
5527
|
+
router: Router;
|
|
5528
|
+
}
|
|
5529
|
+
| {
|
|
5530
|
+
type: 'filter';
|
|
5531
|
+
predicate: SessionPredicate;
|
|
5532
|
+
router: Router;
|
|
5533
|
+
}
|
|
5534
|
+
| {
|
|
5535
|
+
type: 'rawPattern';
|
|
5536
|
+
rawPattern: RawPattern;
|
|
5537
|
+
};
|
|
5538
|
+
type RouteBranch =
|
|
5539
|
+
| {
|
|
5540
|
+
type: 'command';
|
|
5541
|
+
path: string[];
|
|
5542
|
+
command: Command;
|
|
5543
|
+
}
|
|
5544
|
+
| {
|
|
5545
|
+
type: 'rawPattern';
|
|
5546
|
+
path: string[];
|
|
5547
|
+
rawPattern: RawPattern;
|
|
5548
|
+
};
|
|
5549
|
+
type RouteMatchResult =
|
|
5550
|
+
| {
|
|
5551
|
+
type: 'command';
|
|
5552
|
+
path: string[];
|
|
5553
|
+
command: Command;
|
|
5554
|
+
params: any;
|
|
5555
|
+
}
|
|
5556
|
+
| {
|
|
5557
|
+
type: 'rawPattern';
|
|
5558
|
+
path: string[];
|
|
5559
|
+
rawPattern: RawPattern;
|
|
5560
|
+
params: any;
|
|
5561
|
+
};
|
|
5510
5562
|
interface Session {
|
|
5511
5563
|
raw: IncomingMessage;
|
|
5512
5564
|
reply(segments: OutgoingSegment_ZodInput[]): Promise<void>;
|
|
@@ -5518,12 +5570,16 @@ declare class Router {
|
|
|
5518
5570
|
rawPattern<P extends Pattern>(pattern: P, handler: Handler<P>): this;
|
|
5519
5571
|
group(name: string): Router;
|
|
5520
5572
|
filter(predicate: SessionPredicate): Router;
|
|
5573
|
+
routes(): RouteEntry[];
|
|
5574
|
+
branches(session: Session): RouteBranch[];
|
|
5575
|
+
match(session: Session, message: IncomingMessage): RouteMatchResult | undefined;
|
|
5521
5576
|
dispatch(session: Session, message: IncomingMessage): Promise<boolean>;
|
|
5522
|
-
private
|
|
5523
|
-
private
|
|
5524
|
-
private
|
|
5525
|
-
private
|
|
5526
|
-
private
|
|
5577
|
+
private matchFrom;
|
|
5578
|
+
private matchEntry;
|
|
5579
|
+
private matchCommand;
|
|
5580
|
+
private matchGroup;
|
|
5581
|
+
private matchRawPattern;
|
|
5582
|
+
private branchesFrom;
|
|
5527
5583
|
private capturePattern;
|
|
5528
5584
|
}
|
|
5529
5585
|
//#endregion
|
|
@@ -5567,13 +5623,31 @@ type ServiceClass<T extends object = object> = abstract new (...args: any[]) =>
|
|
|
5567
5623
|
//#endregion
|
|
5568
5624
|
//#region src/core/plugin.d.ts
|
|
5569
5625
|
type ParameterList = Array<any>;
|
|
5570
|
-
|
|
5626
|
+
type Injection = Record<string, ServiceClass>;
|
|
5627
|
+
type InjectedServices<I extends Injection | undefined> = I extends Injection
|
|
5628
|
+
? { [K in keyof I]: InstanceType<I[K]> }
|
|
5629
|
+
: {};
|
|
5630
|
+
type OptionalInjectedServices<I extends Injection | undefined> = I extends Injection
|
|
5631
|
+
? { [K in keyof I]: InstanceType<I[K]> | undefined }
|
|
5632
|
+
: {};
|
|
5633
|
+
interface Plugin<
|
|
5634
|
+
T extends ParameterList,
|
|
5635
|
+
I extends Injection | undefined,
|
|
5636
|
+
OI extends Injection | undefined = undefined,
|
|
5637
|
+
> {
|
|
5571
5638
|
name: string;
|
|
5572
5639
|
requires?: readonly ServiceClass[];
|
|
5640
|
+
inject?: I;
|
|
5641
|
+
optionalRequires?: readonly ServiceClass[];
|
|
5642
|
+
optionalInject?: OI;
|
|
5573
5643
|
provides?: readonly ServiceClass[];
|
|
5574
|
-
apply(ctx: Context
|
|
5644
|
+
apply(ctx: Context & InjectedServices<I> & OptionalInjectedServices<OI>, ...args: T): void | Promise<void>;
|
|
5575
5645
|
}
|
|
5576
|
-
declare function definePlugin<
|
|
5646
|
+
declare function definePlugin<
|
|
5647
|
+
T extends ParameterList,
|
|
5648
|
+
I extends Injection | undefined,
|
|
5649
|
+
OI extends Injection | undefined,
|
|
5650
|
+
>(plugin: Plugin<T, I, OI>): Plugin<T, I, OI>;
|
|
5577
5651
|
//#endregion
|
|
5578
5652
|
//#region src/core/context.d.ts
|
|
5579
5653
|
interface ContextOptions {
|
|
@@ -5603,7 +5677,10 @@ declare class Context {
|
|
|
5603
5677
|
private isStarted;
|
|
5604
5678
|
private constructor();
|
|
5605
5679
|
on<K extends keyof EventMap>(type: K, handler: (event: EventMap[K]) => void | Promise<void>): void;
|
|
5606
|
-
install<T extends ParameterList
|
|
5680
|
+
install<T extends ParameterList, I extends Injection | undefined, OI extends Injection | undefined>(
|
|
5681
|
+
plugin: Plugin<T, I, OI>,
|
|
5682
|
+
...args: T
|
|
5683
|
+
): void;
|
|
5607
5684
|
provide<T extends object>(service: ServiceClass<T>, instance: T): void;
|
|
5608
5685
|
resolve<T extends object>(service: ServiceClass<T>): T;
|
|
5609
5686
|
tryResolve<T extends object>(service: ServiceClass<T>): T | undefined;
|
|
@@ -5613,6 +5690,9 @@ declare class Context {
|
|
|
5613
5690
|
private acceptsParentEvent;
|
|
5614
5691
|
private applyPlugins;
|
|
5615
5692
|
private sortPlugins;
|
|
5693
|
+
private collectPendingProvidedServices;
|
|
5694
|
+
private areRequiredServicesAvailable;
|
|
5695
|
+
private areOptionalServicesReady;
|
|
5616
5696
|
private collectAvailableServices;
|
|
5617
5697
|
private createUnresolvablePluginError;
|
|
5618
5698
|
private createProxyContextForPlugin;
|
|
@@ -5664,11 +5744,14 @@ declare namespace seg {
|
|
|
5664
5744
|
//#endregion
|
|
5665
5745
|
export {
|
|
5666
5746
|
ApiEndpoints,
|
|
5747
|
+
Command,
|
|
5667
5748
|
Context,
|
|
5668
5749
|
ContextOptions,
|
|
5669
5750
|
ContextUrlOptions,
|
|
5670
5751
|
EventMap,
|
|
5671
5752
|
Filter,
|
|
5753
|
+
Handler,
|
|
5754
|
+
Injection,
|
|
5672
5755
|
LogHandler,
|
|
5673
5756
|
LogLevel,
|
|
5674
5757
|
LogMessage,
|
|
@@ -5677,10 +5760,17 @@ export {
|
|
|
5677
5760
|
MilkyEventSubscription,
|
|
5678
5761
|
Parameter,
|
|
5679
5762
|
ParameterList,
|
|
5763
|
+
ParamsOf,
|
|
5764
|
+
Pattern,
|
|
5680
5765
|
Plugin,
|
|
5766
|
+
RawPattern,
|
|
5767
|
+
RouteBranch,
|
|
5768
|
+
RouteEntry,
|
|
5769
|
+
RouteMatchResult,
|
|
5681
5770
|
Router,
|
|
5682
5771
|
type ServiceClass,
|
|
5683
5772
|
Session,
|
|
5773
|
+
SessionPredicate,
|
|
5684
5774
|
combineLogHandlers,
|
|
5685
5775
|
createMilkyClient,
|
|
5686
5776
|
definePlugin,
|
package/dist/index.mjs
CHANGED
|
@@ -211,49 +211,102 @@ var Router = class Router {
|
|
|
211
211
|
});
|
|
212
212
|
return router;
|
|
213
213
|
}
|
|
214
|
-
|
|
214
|
+
routes() {
|
|
215
|
+
return this.entries;
|
|
216
|
+
}
|
|
217
|
+
branches(session) {
|
|
218
|
+
return this.branchesFrom(session, []);
|
|
219
|
+
}
|
|
220
|
+
match(session, message) {
|
|
215
221
|
const tokenizer = new Tokenizer(message.segments);
|
|
216
|
-
return
|
|
222
|
+
return this.matchFrom(session, tokenizer, []);
|
|
223
|
+
}
|
|
224
|
+
async dispatch(session, message) {
|
|
225
|
+
const match = this.match(session, message);
|
|
226
|
+
if (match === void 0) return false;
|
|
227
|
+
switch (match.type) {
|
|
228
|
+
case "command":
|
|
229
|
+
await match.command.handler(session, match.params);
|
|
230
|
+
break;
|
|
231
|
+
case "rawPattern":
|
|
232
|
+
await match.rawPattern.handler(session, match.params);
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
return true;
|
|
217
236
|
}
|
|
218
|
-
|
|
237
|
+
matchFrom(session, tokenizer, path) {
|
|
219
238
|
const initialState = tokenizer.getState();
|
|
220
239
|
for (const entry of this.entries) {
|
|
221
240
|
tokenizer.setState(initialState);
|
|
222
|
-
|
|
241
|
+
const match = this.matchEntry(entry, session, tokenizer, path);
|
|
242
|
+
if (match !== void 0) return match;
|
|
223
243
|
}
|
|
224
244
|
tokenizer.setState(initialState);
|
|
225
|
-
return false;
|
|
226
245
|
}
|
|
227
|
-
|
|
246
|
+
matchEntry(entry, session, tokenizer, path) {
|
|
228
247
|
switch (entry.type) {
|
|
229
|
-
case "command": return
|
|
230
|
-
case "group": return
|
|
248
|
+
case "command": return this.matchCommand(entry.command, tokenizer, path);
|
|
249
|
+
case "group": return this.matchGroup(entry.name, entry.router, session, tokenizer, path);
|
|
231
250
|
case "filter":
|
|
232
|
-
if (entry.predicate(session) !== true) return
|
|
233
|
-
return
|
|
234
|
-
case "rawPattern": return
|
|
251
|
+
if (entry.predicate(session) !== true) return;
|
|
252
|
+
return entry.router.matchFrom(session, tokenizer, path);
|
|
253
|
+
case "rawPattern": return this.matchRawPattern(entry.rawPattern, tokenizer, path);
|
|
235
254
|
}
|
|
236
255
|
}
|
|
237
|
-
|
|
256
|
+
matchCommand(command, tokenizer, path) {
|
|
238
257
|
const token = tokenizer.peek();
|
|
239
|
-
if (typeof token !== "string" || token !== command.name) return
|
|
258
|
+
if (typeof token !== "string" || token !== command.name) return;
|
|
240
259
|
tokenizer.next();
|
|
241
260
|
const params = this.capturePattern(command.pattern, tokenizer);
|
|
242
|
-
if (params === void 0 || tokenizer.hasNext()) return
|
|
243
|
-
|
|
244
|
-
|
|
261
|
+
if (params === void 0 || tokenizer.hasNext()) return;
|
|
262
|
+
return {
|
|
263
|
+
type: "command",
|
|
264
|
+
path: [...path],
|
|
265
|
+
command,
|
|
266
|
+
params
|
|
267
|
+
};
|
|
245
268
|
}
|
|
246
|
-
|
|
269
|
+
matchGroup(name, router, session, tokenizer, path) {
|
|
247
270
|
const token = tokenizer.peek();
|
|
248
|
-
if (typeof token !== "string" || token !== name) return
|
|
271
|
+
if (typeof token !== "string" || token !== name) return;
|
|
249
272
|
tokenizer.next();
|
|
250
|
-
return
|
|
273
|
+
return router.matchFrom(session, tokenizer, [...path, name]);
|
|
251
274
|
}
|
|
252
|
-
|
|
275
|
+
matchRawPattern(rawPattern, tokenizer, path) {
|
|
253
276
|
const params = this.capturePattern(rawPattern.pattern, tokenizer);
|
|
254
|
-
if (params === void 0 || tokenizer.hasNext()) return
|
|
255
|
-
|
|
256
|
-
|
|
277
|
+
if (params === void 0 || tokenizer.hasNext()) return;
|
|
278
|
+
return {
|
|
279
|
+
type: "rawPattern",
|
|
280
|
+
path: [...path],
|
|
281
|
+
rawPattern,
|
|
282
|
+
params
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
branchesFrom(session, path) {
|
|
286
|
+
const branches = [];
|
|
287
|
+
for (const entry of this.entries) switch (entry.type) {
|
|
288
|
+
case "command":
|
|
289
|
+
branches.push({
|
|
290
|
+
type: "command",
|
|
291
|
+
path: [...path],
|
|
292
|
+
command: entry.command
|
|
293
|
+
});
|
|
294
|
+
break;
|
|
295
|
+
case "group":
|
|
296
|
+
branches.push(...entry.router.branchesFrom(session, [...path, entry.name]));
|
|
297
|
+
break;
|
|
298
|
+
case "filter":
|
|
299
|
+
if (entry.predicate(session) === true) branches.push(...entry.router.branchesFrom(session, path));
|
|
300
|
+
break;
|
|
301
|
+
case "rawPattern":
|
|
302
|
+
branches.push({
|
|
303
|
+
type: "rawPattern",
|
|
304
|
+
path: [...path],
|
|
305
|
+
rawPattern: entry.rawPattern
|
|
306
|
+
});
|
|
307
|
+
break;
|
|
308
|
+
}
|
|
309
|
+
return branches;
|
|
257
310
|
}
|
|
258
311
|
capturePattern(pattern, tokenizer) {
|
|
259
312
|
const initialState = tokenizer.getState();
|
|
@@ -419,7 +472,10 @@ var Context = class Context {
|
|
|
419
472
|
const available = /* @__PURE__ */ new Set();
|
|
420
473
|
for (const service of this.collectAvailableServices()) available.add(service);
|
|
421
474
|
while (pending.length > 0) {
|
|
422
|
-
const
|
|
475
|
+
const availableByPendingPlugins = this.collectPendingProvidedServices(pending);
|
|
476
|
+
const requiredReadyIndex = pending.findIndex(({ plugin }) => this.areRequiredServicesAvailable(plugin, available));
|
|
477
|
+
const optionalReadyIndex = pending.findIndex(({ plugin }) => this.areRequiredServicesAvailable(plugin, available) && this.areOptionalServicesReady(plugin, available, availableByPendingPlugins));
|
|
478
|
+
const nextIndex = optionalReadyIndex === -1 ? requiredReadyIndex : optionalReadyIndex;
|
|
423
479
|
if (nextIndex === -1) throw this.createUnresolvablePluginError(pending, available);
|
|
424
480
|
const [next] = pending.splice(nextIndex, 1);
|
|
425
481
|
sorted.push(next);
|
|
@@ -427,6 +483,21 @@ var Context = class Context {
|
|
|
427
483
|
}
|
|
428
484
|
return sorted;
|
|
429
485
|
}
|
|
486
|
+
collectPendingProvidedServices(pending) {
|
|
487
|
+
const providedServices = /* @__PURE__ */ new Map();
|
|
488
|
+
for (const installedPlugin of pending) for (const service of installedPlugin.plugin.provides ?? []) providedServices.set(service, installedPlugin);
|
|
489
|
+
return providedServices;
|
|
490
|
+
}
|
|
491
|
+
areRequiredServicesAvailable(plugin, available) {
|
|
492
|
+
return (plugin.requires ?? []).every((service) => available.has(service));
|
|
493
|
+
}
|
|
494
|
+
areOptionalServicesReady(plugin, available, pendingProviders) {
|
|
495
|
+
return (plugin.optionalRequires ?? []).every((service) => {
|
|
496
|
+
if (available.has(service)) return true;
|
|
497
|
+
const pendingProvider = pendingProviders.get(service);
|
|
498
|
+
return pendingProvider === void 0 || pendingProvider.plugin === plugin;
|
|
499
|
+
});
|
|
500
|
+
}
|
|
430
501
|
collectAvailableServices() {
|
|
431
502
|
const services = [...this.services.keys()];
|
|
432
503
|
if (this.parent) services.push(...this.parent.collectAvailableServices());
|
|
@@ -450,8 +521,18 @@ var Context = class Context {
|
|
|
450
521
|
}
|
|
451
522
|
createProxyContextForPlugin(plugin) {
|
|
452
523
|
const proxyLogger = new Logger((message) => this.logHandler?.(message), plugin.name);
|
|
524
|
+
let proxyInjections;
|
|
525
|
+
if (plugin.inject) {
|
|
526
|
+
proxyInjections = {};
|
|
527
|
+
for (const [key, service] of Object.entries(plugin.inject)) proxyInjections[key] = this.resolve(service);
|
|
528
|
+
}
|
|
529
|
+
if (plugin.optionalInject) {
|
|
530
|
+
proxyInjections ??= {};
|
|
531
|
+
for (const [key, service] of Object.entries(plugin.optionalInject)) proxyInjections[key] = this.tryResolve(service);
|
|
532
|
+
}
|
|
453
533
|
return new Proxy(this, { get(target, prop) {
|
|
454
534
|
if (prop === "logger") return proxyLogger;
|
|
535
|
+
else if (proxyInjections && prop in proxyInjections) return proxyInjections[prop];
|
|
455
536
|
else return target[prop];
|
|
456
537
|
} });
|
|
457
538
|
}
|
|
@@ -615,6 +696,14 @@ let filter;
|
|
|
615
696
|
//#endregion
|
|
616
697
|
//#region src/core/plugin.ts
|
|
617
698
|
function definePlugin(plugin) {
|
|
699
|
+
if (plugin.inject) {
|
|
700
|
+
if (plugin.requires) throw new Error(`Plugin "${plugin.name}" cannot have both "requires" and "inject" properties.`);
|
|
701
|
+
plugin.requires = Object.values(plugin.inject);
|
|
702
|
+
}
|
|
703
|
+
if (plugin.optionalInject) {
|
|
704
|
+
if (plugin.optionalRequires) throw new Error(`Plugin "${plugin.name}" cannot have both "optionalRequires" and "optionalInject" properties.`);
|
|
705
|
+
plugin.optionalRequires = Object.values(plugin.optionalInject);
|
|
706
|
+
}
|
|
618
707
|
return plugin;
|
|
619
708
|
}
|
|
620
709
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fraqjs/fraq",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.5.0",
|
|
5
5
|
"description": "Milky Bot framework",
|
|
6
6
|
"files": [
|
|
7
7
|
"dist"
|
|
@@ -19,6 +19,9 @@
|
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"mitt": "^3.0.1"
|
|
21
21
|
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@fraqjs/mock": "^0.1.0"
|
|
24
|
+
},
|
|
22
25
|
"engines": {
|
|
23
26
|
"node": ">=22"
|
|
24
27
|
},
|