@fraqjs/fraq 0.3.3 → 0.4.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 CHANGED
@@ -5567,13 +5567,31 @@ type ServiceClass<T extends object = object> = abstract new (...args: any[]) =>
5567
5567
  //#endregion
5568
5568
  //#region src/core/plugin.d.ts
5569
5569
  type ParameterList = Array<any>;
5570
- interface Plugin<T extends ParameterList> {
5570
+ type Injection = Record<string, ServiceClass>;
5571
+ type InjectedServices<I extends Injection | undefined> = I extends Injection
5572
+ ? { [K in keyof I]: InstanceType<I[K]> }
5573
+ : {};
5574
+ type OptionalInjectedServices<I extends Injection | undefined> = I extends Injection
5575
+ ? { [K in keyof I]: InstanceType<I[K]> | undefined }
5576
+ : {};
5577
+ interface Plugin<
5578
+ T extends ParameterList,
5579
+ I extends Injection | undefined,
5580
+ OI extends Injection | undefined = undefined,
5581
+ > {
5571
5582
  name: string;
5572
5583
  requires?: readonly ServiceClass[];
5584
+ inject?: I;
5585
+ optionalRequires?: readonly ServiceClass[];
5586
+ optionalInject?: OI;
5573
5587
  provides?: readonly ServiceClass[];
5574
- apply(ctx: Context, ...args: T): void | Promise<void>;
5588
+ apply(ctx: Context & InjectedServices<I> & OptionalInjectedServices<OI>, ...args: T): void | Promise<void>;
5575
5589
  }
5576
- declare function definePlugin<T extends ParameterList>(plugin: Plugin<T>): Plugin<T>;
5590
+ declare function definePlugin<
5591
+ T extends ParameterList,
5592
+ I extends Injection | undefined,
5593
+ OI extends Injection | undefined,
5594
+ >(plugin: Plugin<T, I, OI>): Plugin<T, I, OI>;
5577
5595
  //#endregion
5578
5596
  //#region src/core/context.d.ts
5579
5597
  interface ContextOptions {
@@ -5603,7 +5621,10 @@ declare class Context {
5603
5621
  private isStarted;
5604
5622
  private constructor();
5605
5623
  on<K extends keyof EventMap>(type: K, handler: (event: EventMap[K]) => void | Promise<void>): void;
5606
- install<T extends ParameterList>(plugin: Plugin<T>, ...args: T): void;
5624
+ install<T extends ParameterList, I extends Injection | undefined, OI extends Injection | undefined>(
5625
+ plugin: Plugin<T, I, OI>,
5626
+ ...args: T
5627
+ ): void;
5607
5628
  provide<T extends object>(service: ServiceClass<T>, instance: T): void;
5608
5629
  resolve<T extends object>(service: ServiceClass<T>): T;
5609
5630
  tryResolve<T extends object>(service: ServiceClass<T>): T | undefined;
@@ -5613,6 +5634,9 @@ declare class Context {
5613
5634
  private acceptsParentEvent;
5614
5635
  private applyPlugins;
5615
5636
  private sortPlugins;
5637
+ private collectPendingProvidedServices;
5638
+ private areRequiredServicesAvailable;
5639
+ private areOptionalServicesReady;
5616
5640
  private collectAvailableServices;
5617
5641
  private createUnresolvablePluginError;
5618
5642
  private createProxyContextForPlugin;
@@ -5669,6 +5693,7 @@ export {
5669
5693
  ContextUrlOptions,
5670
5694
  EventMap,
5671
5695
  Filter,
5696
+ Injection,
5672
5697
  LogHandler,
5673
5698
  LogLevel,
5674
5699
  LogMessage,
package/dist/index.mjs CHANGED
@@ -419,7 +419,10 @@ var Context = class Context {
419
419
  const available = /* @__PURE__ */ new Set();
420
420
  for (const service of this.collectAvailableServices()) available.add(service);
421
421
  while (pending.length > 0) {
422
- const nextIndex = pending.findIndex(({ plugin }) => (plugin.requires ?? []).every((service) => available.has(service)));
422
+ const availableByPendingPlugins = this.collectPendingProvidedServices(pending);
423
+ const requiredReadyIndex = pending.findIndex(({ plugin }) => this.areRequiredServicesAvailable(plugin, available));
424
+ const optionalReadyIndex = pending.findIndex(({ plugin }) => this.areRequiredServicesAvailable(plugin, available) && this.areOptionalServicesReady(plugin, available, availableByPendingPlugins));
425
+ const nextIndex = optionalReadyIndex === -1 ? requiredReadyIndex : optionalReadyIndex;
423
426
  if (nextIndex === -1) throw this.createUnresolvablePluginError(pending, available);
424
427
  const [next] = pending.splice(nextIndex, 1);
425
428
  sorted.push(next);
@@ -427,6 +430,21 @@ var Context = class Context {
427
430
  }
428
431
  return sorted;
429
432
  }
433
+ collectPendingProvidedServices(pending) {
434
+ const providedServices = /* @__PURE__ */ new Map();
435
+ for (const installedPlugin of pending) for (const service of installedPlugin.plugin.provides ?? []) providedServices.set(service, installedPlugin);
436
+ return providedServices;
437
+ }
438
+ areRequiredServicesAvailable(plugin, available) {
439
+ return (plugin.requires ?? []).every((service) => available.has(service));
440
+ }
441
+ areOptionalServicesReady(plugin, available, pendingProviders) {
442
+ return (plugin.optionalRequires ?? []).every((service) => {
443
+ if (available.has(service)) return true;
444
+ const pendingProvider = pendingProviders.get(service);
445
+ return pendingProvider === void 0 || pendingProvider.plugin === plugin;
446
+ });
447
+ }
430
448
  collectAvailableServices() {
431
449
  const services = [...this.services.keys()];
432
450
  if (this.parent) services.push(...this.parent.collectAvailableServices());
@@ -450,8 +468,18 @@ var Context = class Context {
450
468
  }
451
469
  createProxyContextForPlugin(plugin) {
452
470
  const proxyLogger = new Logger((message) => this.logHandler?.(message), plugin.name);
471
+ let proxyInjections;
472
+ if (plugin.inject) {
473
+ proxyInjections = {};
474
+ for (const [key, service] of Object.entries(plugin.inject)) proxyInjections[key] = this.resolve(service);
475
+ }
476
+ if (plugin.optionalInject) {
477
+ proxyInjections ??= {};
478
+ for (const [key, service] of Object.entries(plugin.optionalInject)) proxyInjections[key] = this.tryResolve(service);
479
+ }
453
480
  return new Proxy(this, { get(target, prop) {
454
481
  if (prop === "logger") return proxyLogger;
482
+ else if (proxyInjections && prop in proxyInjections) return proxyInjections[prop];
455
483
  else return target[prop];
456
484
  } });
457
485
  }
@@ -615,6 +643,14 @@ let filter;
615
643
  //#endregion
616
644
  //#region src/core/plugin.ts
617
645
  function definePlugin(plugin) {
646
+ if (plugin.inject) {
647
+ if (plugin.requires) throw new Error(`Plugin "${plugin.name}" cannot have both "requires" and "inject" properties.`);
648
+ plugin.requires = Object.values(plugin.inject);
649
+ }
650
+ if (plugin.optionalInject) {
651
+ if (plugin.optionalRequires) throw new Error(`Plugin "${plugin.name}" cannot have both "optionalRequires" and "optionalInject" properties.`);
652
+ plugin.optionalRequires = Object.values(plugin.optionalInject);
653
+ }
618
654
  return plugin;
619
655
  }
620
656
  //#endregion
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@fraqjs/fraq",
3
3
  "type": "module",
4
- "version": "0.3.3",
4
+ "version": "0.4.0",
5
5
  "description": "Milky Bot framework",
6
6
  "files": [
7
7
  "dist"