moost 0.6.40 → 0.6.42

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.cjs CHANGED
@@ -48,7 +48,7 @@ let stamped = false;
48
48
  if (stamped) return;
49
49
  stamped = true;
50
50
  stampModuleIdentity({
51
- version: "0.6.40",
51
+ version: "0.6.42",
52
52
  path: typeof __filename === "string" ? __filename : require("url").pathToFileURL(__filename).href
53
53
  });
54
54
  }
@@ -139,19 +139,15 @@ let sourceRefs = [];
139
139
  /**
140
140
  * Scans the registered apps' controllers for a class-scoped `@Provide`
141
141
  * carrying `token` and returns the provider controller class names. Both
142
- * string and symbol keys are checked — symbol class-tokens match by identity
143
- * because infact keys them via `Symbol.for` (same class source, same symbol).
142
+ * string and symbol keys are checked — a class token matches because
143
+ * `@Inject(Class)` and `@Provide(Class)` both key it via infact's `getClassKey`.
144
144
  */ function findTokenProviders(token) {
145
145
  const providers = [];
146
146
  for (const source of scanLiveSources()) for (const entry of source.getControllersOverview()) if (Reflect.ownKeys(entry.meta?.provide ?? {}).includes(token) && !providers.includes(entry.type.name)) providers.push(entry.type.name);
147
147
  return providers;
148
148
  }
149
- const CLASS_SOURCE_RE = /^class\s+([\w$]+)/;
150
- /** Renders a provide token readably: strings as-is, class-source symbols by class name. */ function stringifyToken(token) {
151
- if (typeof token === "string") return token;
152
- const description = token.description ?? token.toString();
153
- const match = CLASS_SOURCE_RE.exec(description);
154
- return match ? match[1] : description;
149
+ /** Renders a provide token readably: strings as-is, symbols (class keys carry the class name) by description. */ function stringifyToken(token) {
150
+ return typeof token === "string" ? token : token.description || token.toString();
155
151
  }
156
152
  /**
157
153
  * D4: when an unresolved `@Inject(token)` IS provided somewhere — via a
@@ -450,7 +446,13 @@ function nextScopeId() {
450
446
  }
451
447
  return `__moost_${_scopeChar}_${++_scopeNum}`;
452
448
  }
453
- /** Composable returning the moost scope ID for the current event. Generates on first call, caches for subsequent calls. */ const useScopeId = (0, _wooksjs_event_core.defineWook)(() => nextScopeId());
449
+ const scopeIdSlot = (0, _wooksjs_event_core.cached)(() => nextScopeId());
450
+ /**
451
+ * Composable returning the moost DI scope ID of the current event. Generated on first call
452
+ * and cached on the event context itself — never inherited from a parent context, so a child
453
+ * event (a workflow run started with `eventContext`, a WebSocket message) owns its scope
454
+ * instead of sharing, and later releasing, its parent's.
455
+ */ const useScopeId = (ctx) => (ctx ?? (0, _wooksjs_event_core.current)()).getOwn(scopeIdSlot);
454
456
  /** Registers a DI scope for the given ID and returns an unscope function. */ function registerEventScope(scopeId) {
455
457
  const infact = getMoostInfact();
456
458
  infact.registerScope(scopeId);
@@ -458,12 +460,42 @@ function nextScopeId() {
458
460
  infact.unregisterScope(scopeId);
459
461
  };
460
462
  }
463
+ const noop = () => {};
464
+ /**
465
+ * Registers an event's DI scope, held by its handler lifecycle and, with `adapterHolds`, by
466
+ * the adapter too. Each release is idempotent; the scope is dropped once every holder let go,
467
+ * so an adapter signal that fires early (a client disconnect mid-handler) cannot pull the
468
+ * scope from under a running handler. Built outside the handler closure so a long-lived
469
+ * adapter listener (the response 'close' of a streamed body) retains only the scope.
470
+ */ function holdEventScope(scopeId, adapterHolds) {
471
+ const drop = registerEventScope(scopeId);
472
+ let holders = adapterHolds ? 2 : 1;
473
+ const hold = () => {
474
+ let held = true;
475
+ return () => {
476
+ if (held) {
477
+ held = false;
478
+ if (--holders === 0) drop();
479
+ }
480
+ };
481
+ };
482
+ const releaseLifecycle = hold();
483
+ return {
484
+ releaseLifecycle,
485
+ failLifecycle: (error) => {
486
+ releaseLifecycle();
487
+ throw error;
488
+ },
489
+ unscope: adapterHolds ? hold() : noop
490
+ };
491
+ }
461
492
  /**
462
493
  * Creates the complete event handler lifecycle processor used by adapters.
463
494
  * Handles scope registration, controller resolution, interceptors, argument resolution,
464
495
  * handler invocation, and cleanup — optimised for sync-first execution.
465
496
  */ function defineMoostEventHandler(options) {
466
497
  const ci = (0, _wooksjs_event_core.getContextInjector)();
498
+ const manualUnscope = !!options.manualUnscope;
467
499
  const handlerSpanName = `Handler:${options.targetPath}`;
468
500
  const handlerAttrs = {
469
501
  "moost.handler": options.controllerMethod || "",
@@ -473,7 +505,7 @@ function nextScopeId() {
473
505
  const ctx = (0, _wooksjs_event_core.current)();
474
506
  const scopeId = useScopeId(ctx);
475
507
  const logger = (0, _wooksjs_event_core.useLogger)(options.loggerTitle, ctx);
476
- const unscope = registerEventScope(scopeId);
508
+ const { unscope, releaseLifecycle, failLifecycle } = holdEventScope(scopeId, manualUnscope);
477
509
  let response;
478
510
  let hookOptions;
479
511
  function getHookOptions() {
@@ -489,14 +521,17 @@ function nextScopeId() {
489
521
  let interceptorHandler;
490
522
  let raise = false;
491
523
  try {
524
+ const result = start();
525
+ return isThenable(result) ? result.then(void 0, failLifecycle) : result;
526
+ } catch (error) {
527
+ return failLifecycle(error);
528
+ }
529
+ function start() {
492
530
  if (options.hooks?.init) {
493
531
  const hookResult = options.hooks.init(getHookOptions());
494
532
  if (isThenable(hookResult)) return hookResult.then(afterInit);
495
533
  }
496
534
  return afterInit();
497
- } catch (error) {
498
- if (!options.manualUnscope) unscope();
499
- throw error;
500
535
  }
501
536
  function afterInit() {
502
537
  const instanceResult = options.getControllerInstance();
@@ -580,7 +615,6 @@ function nextScopeId() {
580
615
  return finalize();
581
616
  }, (error) => {
582
617
  if (options.logErrors) logger.error(String(error));
583
- if (!options.manualUnscope) unscope();
584
618
  throw error;
585
619
  });
586
620
  response = afterResult;
@@ -589,7 +623,7 @@ function nextScopeId() {
589
623
  return finalize();
590
624
  }
591
625
  function finalize() {
592
- if (!options.manualUnscope) unscope();
626
+ releaseLifecycle();
593
627
  if (options.hooks?.end) {
594
628
  const endResult = options.hooks.end(getHookOptions());
595
629
  if (isThenable(endResult)) return endResult.then(() => {
@@ -1307,7 +1341,7 @@ var TInterceptorPriority = /* @__PURE__ */ function(TInterceptorPriority) {
1307
1341
  * (For optional values use with @Optional())
1308
1342
  * @param type - string or class constructor
1309
1343
  */ function Inject(type) {
1310
- const key = typeof type === "string" ? type : Symbol.for(String(type));
1344
+ const key = typeof type === "string" ? type : (0, _prostojs_infact.getClassKey)(type);
1311
1345
  return getMoostMate().decorate("inject", key);
1312
1346
  }
1313
1347
  /**
@@ -2671,6 +2705,12 @@ Object.defineProperty(exports, 'eventTypeKey', {
2671
2705
  return _wooksjs_event_core.eventTypeKey;
2672
2706
  }
2673
2707
  });
2708
+ Object.defineProperty(exports, 'getClassKey', {
2709
+ enumerable: true,
2710
+ get: function () {
2711
+ return _prostojs_infact.getClassKey;
2712
+ }
2713
+ });
2674
2714
  Object.defineProperty(exports, 'getConstructor', {
2675
2715
  enumerable: true,
2676
2716
  get: function () {
package/dist/index.d.ts CHANGED
@@ -1,8 +1,7 @@
1
- import * as _wooksjs_event_core from '@wooksjs/event-core';
2
1
  import { EventContext, Logger, Key } from '@wooksjs/event-core';
3
2
  export { Cached, ContextInjector, EventContext, EventContextOptions, Key, Logger, cached, createEventContext, current, eventTypeKey, getContextInjector, key, replaceContextInjector, resetContextInjector, run, useLogger } from '@wooksjs/event-core';
4
3
  import { TProvideRegistry, TReplaceRegistry, Infact, TProvideFn } from '@prostojs/infact';
5
- export { TProvideRegistry, createProvideRegistry, createReplaceRegistry } from '@prostojs/infact';
4
+ export { TProvideRegistry, createProvideRegistry, createReplaceRegistry, getClassKey } from '@prostojs/infact';
6
5
  import * as _prostojs_mate from '@prostojs/mate';
7
6
  import { TMateParamMeta, Mate } from '@prostojs/mate';
8
7
  export { Mate, TMateParamMeta, getConstructor, isConstructor } from '@prostojs/mate';
@@ -1288,8 +1287,13 @@ interface TMoostEventHandlerOptions<T> {
1288
1287
  controllerPrefix?: string;
1289
1288
  handlerType: string;
1290
1289
  }
1291
- /** Composable returning the moost scope ID for the current event. Generates on first call, caches for subsequent calls. */
1292
- declare const useScopeId: _wooksjs_event_core.WookComposable<string>;
1290
+ /**
1291
+ * Composable returning the moost DI scope ID of the current event. Generated on first call
1292
+ * and cached on the event context itself — never inherited from a parent context, so a child
1293
+ * event (a workflow run started with `eventContext`, a WebSocket message) owns its scope
1294
+ * instead of sharing, and later releasing, its parent's.
1295
+ */
1296
+ declare const useScopeId: (ctx?: EventContext) => string;
1293
1297
  /** Registers a DI scope for the given ID and returns an unscope function. */
1294
1298
  declare function registerEventScope(scopeId: string): () => void;
1295
1299
  /**
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { ContextInjector, cached, createEventContext, createEventContext as createEventContext$1, current, current as current$1, defineWook, eventTypeKey, getContextInjector, getContextInjector as getContextInjector$1, key, key as key$1, replaceContextInjector, resetContextInjector, run, useLogger, useLogger as useLogger$1, useRouteParams } from "@wooksjs/event-core";
2
- import { Infact, createProvideRegistry, createProvideRegistry as createProvideRegistry$1, createReplaceRegistry, createReplaceRegistry as createReplaceRegistry$1 } from "@prostojs/infact";
1
+ import { ContextInjector, cached, cached as cached$1, createEventContext, createEventContext as createEventContext$1, current, current as current$1, eventTypeKey, getContextInjector, getContextInjector as getContextInjector$1, key, key as key$1, replaceContextInjector, resetContextInjector, run, useLogger, useLogger as useLogger$1, useRouteParams } from "@wooksjs/event-core";
2
+ import { Infact, createProvideRegistry, createProvideRegistry as createProvideRegistry$1, createReplaceRegistry, createReplaceRegistry as createReplaceRegistry$1, getClassKey, getClassKey as getClassKey$1 } from "@prostojs/infact";
3
3
  import { Mate, getConstructor, getConstructor as getConstructor$1, isConstructor, isConstructor as isConstructor$1 } from "@prostojs/mate";
4
4
  import { ProstoLogger, ProstoLogger as ProstoLogger$1, coloredConsole, createConsoleTransort } from "@prostojs/logger";
5
5
  import { Hookable } from "hookable";
@@ -47,7 +47,7 @@ let stamped = false;
47
47
  if (stamped) return;
48
48
  stamped = true;
49
49
  stampModuleIdentity({
50
- version: "0.6.40",
50
+ version: "0.6.42",
51
51
  path: typeof __filename === "string" ? __filename : import.meta.url
52
52
  });
53
53
  }
@@ -138,19 +138,15 @@ let sourceRefs = [];
138
138
  /**
139
139
  * Scans the registered apps' controllers for a class-scoped `@Provide`
140
140
  * carrying `token` and returns the provider controller class names. Both
141
- * string and symbol keys are checked — symbol class-tokens match by identity
142
- * because infact keys them via `Symbol.for` (same class source, same symbol).
141
+ * string and symbol keys are checked — a class token matches because
142
+ * `@Inject(Class)` and `@Provide(Class)` both key it via infact's `getClassKey`.
143
143
  */ function findTokenProviders(token) {
144
144
  const providers = [];
145
145
  for (const source of scanLiveSources()) for (const entry of source.getControllersOverview()) if (Reflect.ownKeys(entry.meta?.provide ?? {}).includes(token) && !providers.includes(entry.type.name)) providers.push(entry.type.name);
146
146
  return providers;
147
147
  }
148
- const CLASS_SOURCE_RE = /^class\s+([\w$]+)/;
149
- /** Renders a provide token readably: strings as-is, class-source symbols by class name. */ function stringifyToken(token) {
150
- if (typeof token === "string") return token;
151
- const description = token.description ?? token.toString();
152
- const match = CLASS_SOURCE_RE.exec(description);
153
- return match ? match[1] : description;
148
+ /** Renders a provide token readably: strings as-is, symbols (class keys carry the class name) by description. */ function stringifyToken(token) {
149
+ return typeof token === "string" ? token : token.description || token.toString();
154
150
  }
155
151
  /**
156
152
  * D4: when an unresolved `@Inject(token)` IS provided somewhere — via a
@@ -449,7 +445,13 @@ function nextScopeId() {
449
445
  }
450
446
  return `__moost_${_scopeChar}_${++_scopeNum}`;
451
447
  }
452
- /** Composable returning the moost scope ID for the current event. Generates on first call, caches for subsequent calls. */ const useScopeId = defineWook(() => nextScopeId());
448
+ const scopeIdSlot = cached$1(() => nextScopeId());
449
+ /**
450
+ * Composable returning the moost DI scope ID of the current event. Generated on first call
451
+ * and cached on the event context itself — never inherited from a parent context, so a child
452
+ * event (a workflow run started with `eventContext`, a WebSocket message) owns its scope
453
+ * instead of sharing, and later releasing, its parent's.
454
+ */ const useScopeId = (ctx) => (ctx ?? current$1()).getOwn(scopeIdSlot);
453
455
  /** Registers a DI scope for the given ID and returns an unscope function. */ function registerEventScope(scopeId) {
454
456
  const infact = getMoostInfact();
455
457
  infact.registerScope(scopeId);
@@ -457,12 +459,42 @@ function nextScopeId() {
457
459
  infact.unregisterScope(scopeId);
458
460
  };
459
461
  }
462
+ const noop = () => {};
463
+ /**
464
+ * Registers an event's DI scope, held by its handler lifecycle and, with `adapterHolds`, by
465
+ * the adapter too. Each release is idempotent; the scope is dropped once every holder let go,
466
+ * so an adapter signal that fires early (a client disconnect mid-handler) cannot pull the
467
+ * scope from under a running handler. Built outside the handler closure so a long-lived
468
+ * adapter listener (the response 'close' of a streamed body) retains only the scope.
469
+ */ function holdEventScope(scopeId, adapterHolds) {
470
+ const drop = registerEventScope(scopeId);
471
+ let holders = adapterHolds ? 2 : 1;
472
+ const hold = () => {
473
+ let held = true;
474
+ return () => {
475
+ if (held) {
476
+ held = false;
477
+ if (--holders === 0) drop();
478
+ }
479
+ };
480
+ };
481
+ const releaseLifecycle = hold();
482
+ return {
483
+ releaseLifecycle,
484
+ failLifecycle: (error) => {
485
+ releaseLifecycle();
486
+ throw error;
487
+ },
488
+ unscope: adapterHolds ? hold() : noop
489
+ };
490
+ }
460
491
  /**
461
492
  * Creates the complete event handler lifecycle processor used by adapters.
462
493
  * Handles scope registration, controller resolution, interceptors, argument resolution,
463
494
  * handler invocation, and cleanup — optimised for sync-first execution.
464
495
  */ function defineMoostEventHandler(options) {
465
496
  const ci = getContextInjector$1();
497
+ const manualUnscope = !!options.manualUnscope;
466
498
  const handlerSpanName = `Handler:${options.targetPath}`;
467
499
  const handlerAttrs = {
468
500
  "moost.handler": options.controllerMethod || "",
@@ -472,7 +504,7 @@ function nextScopeId() {
472
504
  const ctx = current$1();
473
505
  const scopeId = useScopeId(ctx);
474
506
  const logger = useLogger$1(options.loggerTitle, ctx);
475
- const unscope = registerEventScope(scopeId);
507
+ const { unscope, releaseLifecycle, failLifecycle } = holdEventScope(scopeId, manualUnscope);
476
508
  let response;
477
509
  let hookOptions;
478
510
  function getHookOptions() {
@@ -488,14 +520,17 @@ function nextScopeId() {
488
520
  let interceptorHandler;
489
521
  let raise = false;
490
522
  try {
523
+ const result = start();
524
+ return isThenable(result) ? result.then(void 0, failLifecycle) : result;
525
+ } catch (error) {
526
+ return failLifecycle(error);
527
+ }
528
+ function start() {
491
529
  if (options.hooks?.init) {
492
530
  const hookResult = options.hooks.init(getHookOptions());
493
531
  if (isThenable(hookResult)) return hookResult.then(afterInit);
494
532
  }
495
533
  return afterInit();
496
- } catch (error) {
497
- if (!options.manualUnscope) unscope();
498
- throw error;
499
534
  }
500
535
  function afterInit() {
501
536
  const instanceResult = options.getControllerInstance();
@@ -579,7 +614,6 @@ function nextScopeId() {
579
614
  return finalize();
580
615
  }, (error) => {
581
616
  if (options.logErrors) logger.error(String(error));
582
- if (!options.manualUnscope) unscope();
583
617
  throw error;
584
618
  });
585
619
  response = afterResult;
@@ -588,7 +622,7 @@ function nextScopeId() {
588
622
  return finalize();
589
623
  }
590
624
  function finalize() {
591
- if (!options.manualUnscope) unscope();
625
+ releaseLifecycle();
592
626
  if (options.hooks?.end) {
593
627
  const endResult = options.hooks.end(getHookOptions());
594
628
  if (isThenable(endResult)) return endResult.then(() => {
@@ -1306,7 +1340,7 @@ var TInterceptorPriority = /* @__PURE__ */ function(TInterceptorPriority) {
1306
1340
  * (For optional values use with @Optional())
1307
1341
  * @param type - string or class constructor
1308
1342
  */ function Inject(type) {
1309
- const key = typeof type === "string" ? type : Symbol.for(String(type));
1343
+ const key = typeof type === "string" ? type : getClassKey$1(type);
1310
1344
  return getMoostMate().decorate("inject", key);
1311
1345
  }
1312
1346
  /**
@@ -2565,4 +2599,4 @@ function _define_property(obj, key, value) {
2565
2599
  }
2566
2600
 
2567
2601
  //#endregion
2568
- export { After, ApplyDecorators, Before, Circular, Const, ConstFactory, ContextInjector, Controller, Description, HandlerPaths, Id, ImportController, Inherit, Inject, InjectEventLogger, InjectFromScope, InjectMoost, InjectMoostLogger, InjectScopeVars, Injectable, Intercept, Interceptor, InterceptorHandler, Label, LoggerTopic, MOOST_MODULE_IDENTITY_KEY, Moost, MoostDispose, MoostInit, OnError, Optional, Overtake, Param, Params, Pipe, ProstoLogger, Provide, Replace, Required, Resolve, Response, TInterceptorPriority, TPipePriority, Value, cached, clearGlobalWooks, createEventContext, createLogger, createProvideRegistry, createReplaceRegistry, current, defineAfterInterceptor, defineBeforeInterceptor, defineErrorInterceptor, defineInfactScope, defineInterceptor, defineMoostEventHandler, definePipeFn, disposeInstances, eventTypeKey, getConstructor, getContextInjector, getGlobalWooks, getHandlerPaths, getInfactScopeVars, getInfactSingletonInstances, getInstanceOwnMethods, getInstanceOwnProps, getMoostInfact, getMoostMate, getNewMoostInfact, globalKey, isConstructor, isThenable, key, loggerConsoleTransport, mergeSorted, registerEventScope, replaceContextInjector, resetContextInjector, resolvePipe, run, setControllerContext, setInfactLoggingOptions, setInterceptResult, setOvertake, stampModuleIdentity, stampOnce, useControllerContext, useHandlerPaths, useInterceptResult, useLogger, useOvertake, useScopeId };
2602
+ export { After, ApplyDecorators, Before, Circular, Const, ConstFactory, ContextInjector, Controller, Description, HandlerPaths, Id, ImportController, Inherit, Inject, InjectEventLogger, InjectFromScope, InjectMoost, InjectMoostLogger, InjectScopeVars, Injectable, Intercept, Interceptor, InterceptorHandler, Label, LoggerTopic, MOOST_MODULE_IDENTITY_KEY, Moost, MoostDispose, MoostInit, OnError, Optional, Overtake, Param, Params, Pipe, ProstoLogger, Provide, Replace, Required, Resolve, Response, TInterceptorPriority, TPipePriority, Value, cached, clearGlobalWooks, createEventContext, createLogger, createProvideRegistry, createReplaceRegistry, current, defineAfterInterceptor, defineBeforeInterceptor, defineErrorInterceptor, defineInfactScope, defineInterceptor, defineMoostEventHandler, definePipeFn, disposeInstances, eventTypeKey, getClassKey, getConstructor, getContextInjector, getGlobalWooks, getHandlerPaths, getInfactScopeVars, getInfactSingletonInstances, getInstanceOwnMethods, getInstanceOwnProps, getMoostInfact, getMoostMate, getNewMoostInfact, globalKey, isConstructor, isThenable, key, loggerConsoleTransport, mergeSorted, registerEventScope, replaceContextInjector, resetContextInjector, resolvePipe, run, setControllerContext, setInfactLoggingOptions, setInterceptResult, setOvertake, stampModuleIdentity, stampOnce, useControllerContext, useHandlerPaths, useInterceptResult, useLogger, useOvertake, useScopeId };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "moost",
3
- "version": "0.6.40",
3
+ "version": "0.6.42",
4
4
  "description": "moost",
5
5
  "keywords": [
6
6
  "composables",
@@ -38,18 +38,18 @@
38
38
  }
39
39
  },
40
40
  "dependencies": {
41
- "@prostojs/infact": "^0.5.1",
41
+ "@prostojs/infact": "^0.6.0",
42
42
  "@prostojs/logger": "^0.4.3",
43
43
  "@prostojs/mate": "^0.5.0",
44
- "@wooksjs/event-core": "^0.7.24",
44
+ "@wooksjs/event-core": "^0.7.25",
45
45
  "hookable": "^5.5.3",
46
- "wooks": "^0.7.24"
46
+ "wooks": "^0.7.25"
47
47
  },
48
48
  "devDependencies": {
49
- "@wooksjs/event-http": "^0.7.24",
50
- "@wooksjs/http-body": "^0.7.24",
49
+ "@wooksjs/event-http": "^0.7.25",
50
+ "@wooksjs/http-body": "^0.7.25",
51
51
  "vitest": "3.2.7",
52
- "@moostjs/event-http": "^0.6.40"
52
+ "@moostjs/event-http": "^0.6.42"
53
53
  },
54
54
  "scripts": {
55
55
  "pub": "pnpm publish --access public",