@fraqjs/fraq 0.6.3 → 0.7.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 +8 -0
- package/dist/index.mjs +72 -11
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -5679,6 +5679,7 @@ declare class Context {
|
|
|
5679
5679
|
private readonly services;
|
|
5680
5680
|
private readonly subContexts;
|
|
5681
5681
|
private readonly parentEventForwarder?;
|
|
5682
|
+
private readonly timers;
|
|
5682
5683
|
private readonly initialReconnectDelayMs;
|
|
5683
5684
|
private readonly maxReconnectDelayMs;
|
|
5684
5685
|
private readonly logHandler?;
|
|
@@ -5700,16 +5701,23 @@ declare class Context {
|
|
|
5700
5701
|
tryResolve<T extends object>(service: ServiceClass<T>): T | undefined;
|
|
5701
5702
|
isProvided<T extends object>(service: ServiceClass<T>): boolean;
|
|
5702
5703
|
fork(name: string, filter?: Filter): Context;
|
|
5704
|
+
timeout(delayMs: number, callback: () => void | Promise<void>): NodeJS.Timeout;
|
|
5705
|
+
interval(intervalMs: number, callback: () => void | Promise<void>): NodeJS.Timeout;
|
|
5703
5706
|
createSession(message: IncomingMessage): Session;
|
|
5704
5707
|
start(): Promise<void>;
|
|
5705
5708
|
stop(): Promise<void>;
|
|
5706
5709
|
private startInternal;
|
|
5707
5710
|
private getState;
|
|
5708
5711
|
private stopInternal;
|
|
5712
|
+
private assertCanScheduleTimer;
|
|
5713
|
+
private runTimerCallback;
|
|
5714
|
+
private clearTimers;
|
|
5709
5715
|
private stopEventStream;
|
|
5710
5716
|
private acceptsParentEvent;
|
|
5711
5717
|
private applyPlugins;
|
|
5718
|
+
private recursiveApplyPlugins;
|
|
5712
5719
|
private startPlugins;
|
|
5720
|
+
private recursiveStartPlugins;
|
|
5713
5721
|
private sortPlugins;
|
|
5714
5722
|
private collectPendingProvidedServices;
|
|
5715
5723
|
private areRequiredServicesAvailable;
|
package/dist/index.mjs
CHANGED
|
@@ -382,6 +382,7 @@ var Context = class Context {
|
|
|
382
382
|
services = /* @__PURE__ */ new Map();
|
|
383
383
|
subContexts = [];
|
|
384
384
|
parentEventForwarder;
|
|
385
|
+
timers = /* @__PURE__ */ new Set();
|
|
385
386
|
initialReconnectDelayMs;
|
|
386
387
|
maxReconnectDelayMs;
|
|
387
388
|
logHandler;
|
|
@@ -466,6 +467,23 @@ and implement the dispose method to clean up resources when the context stops.
|
|
|
466
467
|
this.subContexts.push(subContext);
|
|
467
468
|
return subContext;
|
|
468
469
|
}
|
|
470
|
+
timeout(delayMs, callback) {
|
|
471
|
+
this.assertCanScheduleTimer();
|
|
472
|
+
const timeout = setTimeout(() => {
|
|
473
|
+
this.timers.delete(timeout);
|
|
474
|
+
this.runTimerCallback(callback);
|
|
475
|
+
}, delayMs);
|
|
476
|
+
this.timers.add(timeout);
|
|
477
|
+
return timeout;
|
|
478
|
+
}
|
|
479
|
+
interval(intervalMs, callback) {
|
|
480
|
+
this.assertCanScheduleTimer();
|
|
481
|
+
const interval = setInterval(() => {
|
|
482
|
+
this.runTimerCallback(callback);
|
|
483
|
+
}, intervalMs);
|
|
484
|
+
this.timers.add(interval);
|
|
485
|
+
return interval;
|
|
486
|
+
}
|
|
469
487
|
createSession(message) {
|
|
470
488
|
return {
|
|
471
489
|
raw: message,
|
|
@@ -508,10 +526,10 @@ and implement the dispose method to clean up resources when the context stops.
|
|
|
508
526
|
}
|
|
509
527
|
}
|
|
510
528
|
async stop() {
|
|
511
|
-
if (this.state === "idle" || this.state === "stopped") return;
|
|
529
|
+
if (this.state === "idle" && this.timers.size === 0 || this.state === "stopped") return;
|
|
512
530
|
if (this.state === "starting") await this.startPromise;
|
|
513
531
|
const stateAfterStart = this.getState();
|
|
514
|
-
if (stateAfterStart === "idle" || stateAfterStart === "stopped") return;
|
|
532
|
+
if (stateAfterStart === "idle" && this.timers.size === 0 || stateAfterStart === "stopped") return;
|
|
515
533
|
if (stateAfterStart === "stopping") {
|
|
516
534
|
await this.stopPromise;
|
|
517
535
|
return;
|
|
@@ -526,16 +544,16 @@ and implement the dispose method to clean up resources when the context stops.
|
|
|
526
544
|
}
|
|
527
545
|
}
|
|
528
546
|
async startInternal() {
|
|
547
|
+
const appliedContextPlugins = [];
|
|
548
|
+
const startingContexts = [];
|
|
529
549
|
try {
|
|
530
|
-
|
|
531
|
-
await this.
|
|
532
|
-
await this.startPlugins(sortedPlugins);
|
|
550
|
+
await this.recursiveApplyPlugins(appliedContextPlugins, startingContexts);
|
|
551
|
+
await this.recursiveStartPlugins(appliedContextPlugins);
|
|
533
552
|
} catch (error) {
|
|
534
|
-
|
|
553
|
+
for (const context of startingContexts) if (context.state === "starting") context.state = "idle";
|
|
535
554
|
throw error;
|
|
536
555
|
}
|
|
537
|
-
for (const
|
|
538
|
-
this.state = "started";
|
|
556
|
+
for (const { context } of appliedContextPlugins) context.state = "started";
|
|
539
557
|
if (!this.parent) this.eventStreamTask = this.runEventStream();
|
|
540
558
|
}
|
|
541
559
|
getState() {
|
|
@@ -543,12 +561,13 @@ and implement the dispose method to clean up resources when the context stops.
|
|
|
543
561
|
}
|
|
544
562
|
async stopInternal() {
|
|
545
563
|
const errors = [];
|
|
546
|
-
|
|
564
|
+
this.clearTimers();
|
|
547
565
|
for (const subContext of [...this.subContexts].reverse()) try {
|
|
548
566
|
await subContext.stop();
|
|
549
567
|
} catch (error) {
|
|
550
568
|
errors.push(error);
|
|
551
569
|
}
|
|
570
|
+
await this.stopEventStream(errors);
|
|
552
571
|
if (this.parent && this.parentEventForwarder) this.parent.eventBus.off("*", this.parentEventForwarder);
|
|
553
572
|
for (const service of [...this.services.values()].reverse()) {
|
|
554
573
|
if (!isDisposable(service)) continue;
|
|
@@ -561,6 +580,22 @@ and implement the dispose method to clean up resources when the context stops.
|
|
|
561
580
|
if (errors.length === 1) throw errors[0];
|
|
562
581
|
if (errors.length > 1) throw new AggregateError(errors, `Context "${this.name}" failed to stop cleanly.`);
|
|
563
582
|
}
|
|
583
|
+
assertCanScheduleTimer() {
|
|
584
|
+
if (this.state === "stopping") throw new Error(`Context "${this.name}" cannot schedule timers while it is stopping.`);
|
|
585
|
+
if (this.state === "stopped") throw new Error(`Context "${this.name}" cannot schedule timers after it has stopped.`);
|
|
586
|
+
}
|
|
587
|
+
async runTimerCallback(callback) {
|
|
588
|
+
if (this.state === "stopping" || this.state === "stopped") return;
|
|
589
|
+
try {
|
|
590
|
+
await callback();
|
|
591
|
+
} catch (error) {
|
|
592
|
+
this.logger.error("Error handling timer callback", error);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
clearTimers() {
|
|
596
|
+
for (const timer of this.timers) clearTimeout(timer);
|
|
597
|
+
this.timers.clear();
|
|
598
|
+
}
|
|
564
599
|
async stopEventStream(errors) {
|
|
565
600
|
this.resolveReconnectDelay();
|
|
566
601
|
const subscription = this.eventSubscription;
|
|
@@ -591,6 +626,24 @@ and implement the dispose method to clean up resources when the context stops.
|
|
|
591
626
|
for (const service of plugin.provides ?? []) if (!this.services.has(service) || providedBeforeApply.has(service)) throw new Error(`${plugin.name} declares service ${service.name} but did not provide it.`);
|
|
592
627
|
}
|
|
593
628
|
}
|
|
629
|
+
async recursiveApplyPlugins(appliedContextPlugins, startingContexts) {
|
|
630
|
+
if (this.state === "started") return;
|
|
631
|
+
if (this.state === "starting" && this.startPromise) {
|
|
632
|
+
await this.startPromise;
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
if (this.state === "stopping") throw new Error(`Context "${this.name}" cannot be started while it is stopping.`);
|
|
636
|
+
if (this.state === "stopped") throw new Error(`Context "${this.name}" cannot be restarted after it has been stopped.`);
|
|
637
|
+
if (this.state === "idle") this.state = "starting";
|
|
638
|
+
startingContexts.push(this);
|
|
639
|
+
const sortedPlugins = this.sortPlugins();
|
|
640
|
+
await this.applyPlugins(sortedPlugins);
|
|
641
|
+
appliedContextPlugins.push({
|
|
642
|
+
context: this,
|
|
643
|
+
sortedPlugins
|
|
644
|
+
});
|
|
645
|
+
for (const subContext of this.subContexts) await subContext.recursiveApplyPlugins(appliedContextPlugins, startingContexts);
|
|
646
|
+
}
|
|
594
647
|
async startPlugins(sortedPlugins) {
|
|
595
648
|
for (const installedPlugin of sortedPlugins) {
|
|
596
649
|
const { plugin } = installedPlugin;
|
|
@@ -599,6 +652,9 @@ and implement the dispose method to clean up resources when the context stops.
|
|
|
599
652
|
await plugin.start(this.getPluginContext(installedPlugin));
|
|
600
653
|
}
|
|
601
654
|
}
|
|
655
|
+
async recursiveStartPlugins(appliedContextPlugins) {
|
|
656
|
+
for (const { context, sortedPlugins } of appliedContextPlugins) await context.startPlugins(sortedPlugins);
|
|
657
|
+
}
|
|
602
658
|
sortPlugins() {
|
|
603
659
|
const providers = /* @__PURE__ */ new Map();
|
|
604
660
|
for (const { plugin } of this.plugins) for (const service of plugin.provides ?? []) {
|
|
@@ -664,6 +720,10 @@ and implement the dispose method to clean up resources when the context stops.
|
|
|
664
720
|
}
|
|
665
721
|
createProxyContextForPlugin(plugin) {
|
|
666
722
|
const proxyLogger = new Logger((message) => this.logHandler?.(message), plugin.name);
|
|
723
|
+
const proxyProvide = (service, instance) => {
|
|
724
|
+
this.provide(service, instance);
|
|
725
|
+
this.logger.debug(`Plugin ${plugin.name} provided ${service.name}`);
|
|
726
|
+
};
|
|
667
727
|
let proxyInjections;
|
|
668
728
|
if (plugin.inject) {
|
|
669
729
|
proxyInjections = {};
|
|
@@ -673,10 +733,11 @@ and implement the dispose method to clean up resources when the context stops.
|
|
|
673
733
|
proxyInjections ??= {};
|
|
674
734
|
for (const [key, service] of Object.entries(plugin.optionalInject)) proxyInjections[key] = this.tryResolve(service);
|
|
675
735
|
}
|
|
676
|
-
return new Proxy(this, { get(target, prop) {
|
|
736
|
+
return new Proxy(this, { get(target, prop, receiver) {
|
|
677
737
|
if (prop === "logger") return proxyLogger;
|
|
738
|
+
else if (prop === "provide") return proxyProvide;
|
|
678
739
|
else if (proxyInjections && prop in proxyInjections) return proxyInjections[prop];
|
|
679
|
-
else return target
|
|
740
|
+
else return Reflect.get(target, prop, receiver);
|
|
680
741
|
} });
|
|
681
742
|
}
|
|
682
743
|
async runEventStream() {
|