moost 0.6.41 → 0.6.43
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 +211 -26
- package/dist/index.d.ts +93 -8
- package/dist/index.mjs +209 -27
- package/package.json +6 -6
package/dist/index.cjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
let _wooksjs_event_core = require("@wooksjs/event-core");
|
|
3
|
-
let _prostojs_infact = require("@prostojs/infact");
|
|
4
3
|
let _prostojs_mate = require("@prostojs/mate");
|
|
5
4
|
let _prostojs_logger = require("@prostojs/logger");
|
|
5
|
+
let _prostojs_infact = require("@prostojs/infact");
|
|
6
6
|
let hookable = require("hookable");
|
|
7
7
|
let wooks = require("wooks");
|
|
8
8
|
|
|
@@ -48,12 +48,38 @@ let stamped = false;
|
|
|
48
48
|
if (stamped) return;
|
|
49
49
|
stamped = true;
|
|
50
50
|
stampModuleIdentity({
|
|
51
|
-
version: "0.6.
|
|
51
|
+
version: "0.6.43",
|
|
52
52
|
path: typeof __filename === "string" ? __filename : require("url").pathToFileURL(__filename).href
|
|
53
53
|
});
|
|
54
54
|
}
|
|
55
55
|
stampOnce();
|
|
56
56
|
|
|
57
|
+
//#endregion
|
|
58
|
+
//#region packages/moost/src/adapter-brand.ts
|
|
59
|
+
/**
|
|
60
|
+
* Static key an adapter class names itself under, independently of its class
|
|
61
|
+
* identity — e.g. `static readonly [MOOST_ADAPTER_BRAND] = '@moostjs/event-http/MoostHttp'`.
|
|
62
|
+
*
|
|
63
|
+
* DI keys classes by identity. When two copies of an adapter package are loaded
|
|
64
|
+
* (a dev server evaluating one copy while Node loads another for an externalized
|
|
65
|
+
* dependency, or a bundle next to an external), a library resolving the adapter
|
|
66
|
+
* with `useControllerContext().instantiate(MoostHttp)` holds the *other* copy's
|
|
67
|
+
* class and finds nothing. With a brand, `instantiate()` still returns the
|
|
68
|
+
* attached adapter (and warns once about the duplicate copy).
|
|
69
|
+
*/ const MOOST_ADAPTER_BRAND = Symbol.for("moost:adapter-brand");
|
|
70
|
+
/** The {@link MOOST_ADAPTER_BRAND} of a class (inherited by subclasses), if it carries one. */ function getAdapterBrand(classConstructor) {
|
|
71
|
+
if (typeof classConstructor !== "function") return;
|
|
72
|
+
const brand = classConstructor[MOOST_ADAPTER_BRAND];
|
|
73
|
+
return typeof brand === "string" ? brand : void 0;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Provide-registry token `Moost.init()` files a branded adapter under next to its
|
|
77
|
+
* class — the lookup `instantiate()` uses for an adapter class that is not the
|
|
78
|
+
* attached one (another package copy, or a base class of the attached adapter).
|
|
79
|
+
*/ function adapterProvideToken(brand) {
|
|
80
|
+
return `moost:adapter:${brand}`;
|
|
81
|
+
}
|
|
82
|
+
|
|
57
83
|
//#endregion
|
|
58
84
|
//#region packages/moost/src/logger.ts
|
|
59
85
|
let defaultLogger;
|
|
@@ -96,6 +122,124 @@ function getDefaultLogger(topic) {
|
|
|
96
122
|
return merged.toSorted((a, b) => a.priority - b.priority);
|
|
97
123
|
}
|
|
98
124
|
|
|
125
|
+
//#endregion
|
|
126
|
+
//#region packages/moost/src/event-scope.ts
|
|
127
|
+
function _define_property$2(obj, key, value) {
|
|
128
|
+
if (key in obj) Object.defineProperty(obj, key, {
|
|
129
|
+
value,
|
|
130
|
+
enumerable: true,
|
|
131
|
+
configurable: true,
|
|
132
|
+
writable: true
|
|
133
|
+
});
|
|
134
|
+
else obj[key] = value;
|
|
135
|
+
return obj;
|
|
136
|
+
}
|
|
137
|
+
let _scopeChar = "a";
|
|
138
|
+
let _scopeNum = 0;
|
|
139
|
+
function nextScopeId() {
|
|
140
|
+
if (_scopeNum >= Number.MAX_SAFE_INTEGER) {
|
|
141
|
+
_scopeNum = 0;
|
|
142
|
+
_scopeChar = _scopeChar === "z" ? "a" : String.fromCodePoint((_scopeChar.codePointAt(0) ?? 97) + 1);
|
|
143
|
+
}
|
|
144
|
+
return `__moost_${_scopeChar}_${++_scopeNum}`;
|
|
145
|
+
}
|
|
146
|
+
const scopeIdSlot = (0, _wooksjs_event_core.cached)(() => nextScopeId());
|
|
147
|
+
/**
|
|
148
|
+
* Composable returning the moost DI scope ID of the current event. Generated on first call
|
|
149
|
+
* and cached on the event context itself — never inherited from a parent context, so a child
|
|
150
|
+
* event (a workflow run started with `eventContext`, a WebSocket message) owns its scope
|
|
151
|
+
* instead of sharing, and later releasing, its parent's.
|
|
152
|
+
*/ const useScopeId = (ctx) => (ctx ?? (0, _wooksjs_event_core.current)()).getOwn(scopeIdSlot);
|
|
153
|
+
/** Registers a DI scope for the given ID and returns an unscope function. */ function registerEventScope(scopeId) {
|
|
154
|
+
const infact = getMoostInfact();
|
|
155
|
+
infact.registerScope(scopeId);
|
|
156
|
+
return () => {
|
|
157
|
+
infact.unregisterScope(scopeId);
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
const NO_ISOLATION = /* @__PURE__ */ new Set();
|
|
161
|
+
/** Copy-on-write child: reads fall through to the parent, every write stays in the child. */ let ForkedEventContext = class ForkedEventContext extends _wooksjs_event_core.EventContext {
|
|
162
|
+
set(slot, value) {
|
|
163
|
+
this.setOwn(slot, value);
|
|
164
|
+
}
|
|
165
|
+
_shouldTraverseParent(id) {
|
|
166
|
+
return !this.isolated.has(id);
|
|
167
|
+
}
|
|
168
|
+
constructor(parent, isolated) {
|
|
169
|
+
super({
|
|
170
|
+
logger: parent.logger,
|
|
171
|
+
parent
|
|
172
|
+
}), _define_property$2(this, "isolated", void 0), this.isolated = isolated;
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
/**
|
|
176
|
+
* Creates a copy-on-write child of an event context. Reads fall through to the parent
|
|
177
|
+
* (request, headers, auth, already computed slots); every write — `ctx.set()`, a controller
|
|
178
|
+
* context, a composable's state — stays in the child, so running code in the child never
|
|
179
|
+
* clobbers the parent event's state. The child owns its moost DI scope id (see
|
|
180
|
+
* {@link withControllerContext} to share the parent's).
|
|
181
|
+
*
|
|
182
|
+
* Run code in it with `run(child, fn)`.
|
|
183
|
+
*
|
|
184
|
+
* @example
|
|
185
|
+
* ```ts
|
|
186
|
+
* const child = forkEventContext({ isolate: [useMyState] })
|
|
187
|
+
* await run(child, () => doWork())
|
|
188
|
+
* ```
|
|
189
|
+
*/ function forkEventContext(opts) {
|
|
190
|
+
const parent = opts?.parent ?? (0, _wooksjs_event_core.current)();
|
|
191
|
+
if (!opts?.isolate) return new ForkedEventContext(parent, NO_ISOLATION);
|
|
192
|
+
const ids = /* @__PURE__ */ new Set();
|
|
193
|
+
for (const slot of opts.isolate) ids.add(("_slot" in slot ? slot._slot : slot)._id);
|
|
194
|
+
return new ForkedEventContext(parent, ids);
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Runs `fn` in a copy-on-write child of the current event (see {@link forkEventContext})
|
|
198
|
+
* whose controller context is `instance` / `method` — another controller's hooks, permission
|
|
199
|
+
* checks and `useControllerContext()` consumers run as if the event had been routed to it,
|
|
200
|
+
* without touching the current event's state. `FOR_EVENT` dependencies resolve in the parent's
|
|
201
|
+
* DI scope by default (`shareScope`).
|
|
202
|
+
*
|
|
203
|
+
* Use it to call into another controller's logic within the same event. Do not run a moost
|
|
204
|
+
* event handler (a routed handler function) inside a scope-sharing child: a handler registers
|
|
205
|
+
* and releases its own scope — use an adapter-level invocation (e.g. `MoostHttp.invoke()`) for that.
|
|
206
|
+
*
|
|
207
|
+
* @returns what `fn` returns (a promise stays a promise)
|
|
208
|
+
*
|
|
209
|
+
* @example
|
|
210
|
+
* ```ts
|
|
211
|
+
* const source = await useControllerContext().instantiate(IssueController)
|
|
212
|
+
* const allowed = await withControllerContext(source, 'list', () => source.allowedFor(ids))
|
|
213
|
+
* ```
|
|
214
|
+
*/ function withControllerContext(instance, method, fn, opts) {
|
|
215
|
+
const parent = opts?.parent ?? (0, _wooksjs_event_core.current)();
|
|
216
|
+
const child = forkEventContext({
|
|
217
|
+
parent,
|
|
218
|
+
isolate: opts?.isolate
|
|
219
|
+
});
|
|
220
|
+
setControllerContext(instance, method, opts?.route ?? "", {
|
|
221
|
+
prefix: opts?.prefix,
|
|
222
|
+
ctx: child
|
|
223
|
+
});
|
|
224
|
+
if (opts?.shareScope !== false) {
|
|
225
|
+
child.setOwn(scopeIdSlot, useScopeId(parent));
|
|
226
|
+
return (0, _wooksjs_event_core.run)(child, fn);
|
|
227
|
+
}
|
|
228
|
+
const release = registerEventScope(useScopeId(child));
|
|
229
|
+
let result;
|
|
230
|
+
try {
|
|
231
|
+
result = (0, _wooksjs_event_core.run)(child, fn);
|
|
232
|
+
} catch (error) {
|
|
233
|
+
release();
|
|
234
|
+
throw error;
|
|
235
|
+
}
|
|
236
|
+
if (!isThenable(result)) {
|
|
237
|
+
release();
|
|
238
|
+
return result;
|
|
239
|
+
}
|
|
240
|
+
return Promise.resolve(result).finally(release);
|
|
241
|
+
}
|
|
242
|
+
|
|
99
243
|
//#endregion
|
|
100
244
|
//#region packages/moost/src/pipes/run-pipes.ts
|
|
101
245
|
function runPipes(pipes, initialValue, metas, level) {
|
|
@@ -389,6 +533,23 @@ const controllerPrefixKey = globalKey("controller.prefix");
|
|
|
389
533
|
_ctx.set(controllerRouteKey, route);
|
|
390
534
|
if (opts?.prefix !== void 0) _ctx.set(controllerPrefixKey, opts.prefix);
|
|
391
535
|
}
|
|
536
|
+
/** Brands already warned about by {@link brandedAdapterFor} (one warning per process). */ const reportedAdapterCopies = /* @__PURE__ */ new Set();
|
|
537
|
+
/**
|
|
538
|
+
* The attached adapter `instantiate(c)` resolves through its brand (see `MOOST_ADAPTER_BRAND`)
|
|
539
|
+
* when `c` is not that adapter's own class: a base class of a custom adapter, or the adapter class
|
|
540
|
+
* from another copy of its package (warned once). `undefined` → the plain DI lookup applies.
|
|
541
|
+
*/ function brandedAdapterFor(controller, c) {
|
|
542
|
+
const brand = getAdapterBrand(c);
|
|
543
|
+
if (brand === void 0) return;
|
|
544
|
+
const adapter = getMoostInfact().getInstanceRegistries(controller).provide?.[adapterProvideToken(brand)]?.fn();
|
|
545
|
+
const attached = adapter ? (0, _prostojs_mate.getConstructor)(adapter) : void 0;
|
|
546
|
+
if (!attached || attached === c || c.prototype instanceof attached) return;
|
|
547
|
+
if (!(adapter instanceof c) && !reportedAdapterCopies.has(brand)) {
|
|
548
|
+
reportedAdapterCopies.add(brand);
|
|
549
|
+
getDefaultLogger("moost").warn(`instantiate(${c.name}) resolved the attached adapter through its brand "${brand}": the class passed in comes from another copy of its package. Two copies of a package split its module state — make the bundler / SSR externalization load one copy (for @moostjs/vite see the ssrExternalCheck warnings).`);
|
|
550
|
+
}
|
|
551
|
+
return adapter;
|
|
552
|
+
}
|
|
392
553
|
/**
|
|
393
554
|
* Provides access to the current controller context within an event handler.
|
|
394
555
|
* Returns utilities for accessing the controller instance, method metadata, and DI.
|
|
@@ -401,7 +562,9 @@ const controllerPrefixKey = globalKey("controller.prefix");
|
|
|
401
562
|
const getControllerMeta = () => getMoostMate().read(getController());
|
|
402
563
|
const getMethodMeta = (name) => getMoostMate().read(getController(), name || getMethod());
|
|
403
564
|
function instantiate(c) {
|
|
404
|
-
|
|
565
|
+
const controller = getController();
|
|
566
|
+
const adapter = brandedAdapterFor(controller, c);
|
|
567
|
+
return adapter ? Promise.resolve(adapter) : getMoostInfact().getForInstance(controller, c);
|
|
405
568
|
}
|
|
406
569
|
return {
|
|
407
570
|
instantiate,
|
|
@@ -437,21 +600,33 @@ const interceptResultKey = globalKey("interceptor.result");
|
|
|
437
600
|
|
|
438
601
|
//#endregion
|
|
439
602
|
//#region packages/moost/src/adapter-utils.ts
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
603
|
+
const noop = () => {};
|
|
604
|
+
/**
|
|
605
|
+
* Registers an event's DI scope, held by its handler lifecycle and, with `adapterHolds`, by
|
|
606
|
+
* the adapter too. Each release is idempotent; the scope is dropped once every holder let go,
|
|
607
|
+
* so an adapter signal that fires early (a client disconnect mid-handler) cannot pull the
|
|
608
|
+
* scope from under a running handler. Built outside the handler closure so a long-lived
|
|
609
|
+
* adapter listener (the response 'close' of a streamed body) retains only the scope.
|
|
610
|
+
*/ function holdEventScope(scopeId, adapterHolds) {
|
|
611
|
+
const drop = registerEventScope(scopeId);
|
|
612
|
+
let holders = adapterHolds ? 2 : 1;
|
|
613
|
+
const hold = () => {
|
|
614
|
+
let held = true;
|
|
615
|
+
return () => {
|
|
616
|
+
if (held) {
|
|
617
|
+
held = false;
|
|
618
|
+
if (--holders === 0) drop();
|
|
619
|
+
}
|
|
620
|
+
};
|
|
621
|
+
};
|
|
622
|
+
const releaseLifecycle = hold();
|
|
623
|
+
return {
|
|
624
|
+
releaseLifecycle,
|
|
625
|
+
failLifecycle: (error) => {
|
|
626
|
+
releaseLifecycle();
|
|
627
|
+
throw error;
|
|
628
|
+
},
|
|
629
|
+
unscope: adapterHolds ? hold() : noop
|
|
455
630
|
};
|
|
456
631
|
}
|
|
457
632
|
/**
|
|
@@ -460,6 +635,7 @@ function nextScopeId() {
|
|
|
460
635
|
* handler invocation, and cleanup — optimised for sync-first execution.
|
|
461
636
|
*/ function defineMoostEventHandler(options) {
|
|
462
637
|
const ci = (0, _wooksjs_event_core.getContextInjector)();
|
|
638
|
+
const manualUnscope = !!options.manualUnscope;
|
|
463
639
|
const handlerSpanName = `Handler:${options.targetPath}`;
|
|
464
640
|
const handlerAttrs = {
|
|
465
641
|
"moost.handler": options.controllerMethod || "",
|
|
@@ -469,7 +645,7 @@ function nextScopeId() {
|
|
|
469
645
|
const ctx = (0, _wooksjs_event_core.current)();
|
|
470
646
|
const scopeId = useScopeId(ctx);
|
|
471
647
|
const logger = (0, _wooksjs_event_core.useLogger)(options.loggerTitle, ctx);
|
|
472
|
-
const unscope =
|
|
648
|
+
const { unscope, releaseLifecycle, failLifecycle } = holdEventScope(scopeId, manualUnscope);
|
|
473
649
|
let response;
|
|
474
650
|
let hookOptions;
|
|
475
651
|
function getHookOptions() {
|
|
@@ -485,14 +661,17 @@ function nextScopeId() {
|
|
|
485
661
|
let interceptorHandler;
|
|
486
662
|
let raise = false;
|
|
487
663
|
try {
|
|
664
|
+
const result = start();
|
|
665
|
+
return isThenable(result) ? result.then(void 0, failLifecycle) : result;
|
|
666
|
+
} catch (error) {
|
|
667
|
+
return failLifecycle(error);
|
|
668
|
+
}
|
|
669
|
+
function start() {
|
|
488
670
|
if (options.hooks?.init) {
|
|
489
671
|
const hookResult = options.hooks.init(getHookOptions());
|
|
490
672
|
if (isThenable(hookResult)) return hookResult.then(afterInit);
|
|
491
673
|
}
|
|
492
674
|
return afterInit();
|
|
493
|
-
} catch (error) {
|
|
494
|
-
if (!options.manualUnscope) unscope();
|
|
495
|
-
throw error;
|
|
496
675
|
}
|
|
497
676
|
function afterInit() {
|
|
498
677
|
const instanceResult = options.getControllerInstance();
|
|
@@ -576,7 +755,6 @@ function nextScopeId() {
|
|
|
576
755
|
return finalize();
|
|
577
756
|
}, (error) => {
|
|
578
757
|
if (options.logErrors) logger.error(String(error));
|
|
579
|
-
if (!options.manualUnscope) unscope();
|
|
580
758
|
throw error;
|
|
581
759
|
});
|
|
582
760
|
response = afterResult;
|
|
@@ -585,7 +763,7 @@ function nextScopeId() {
|
|
|
585
763
|
return finalize();
|
|
586
764
|
}
|
|
587
765
|
function finalize() {
|
|
588
|
-
|
|
766
|
+
releaseLifecycle();
|
|
589
767
|
if (options.hooks?.end) {
|
|
590
768
|
const endResult = options.hooks.end(getHookOptions());
|
|
591
769
|
if (isThenable(endResult)) return endResult.then(() => {
|
|
@@ -2138,7 +2316,11 @@ function _define_property(obj, key, value) {
|
|
|
2138
2316
|
this.setProvideRegistry((0, _prostojs_infact.createProvideRegistry)([Moost, () => this], [_prostojs_logger.ProstoLogger, () => this.logger], ["MOOST_LOGGER", () => this.logger]));
|
|
2139
2317
|
for (const a of this.adapters) {
|
|
2140
2318
|
const constructor = (0, _prostojs_mate.getConstructor)(a);
|
|
2141
|
-
if (constructor)
|
|
2319
|
+
if (constructor) {
|
|
2320
|
+
this.setProvideRegistry((0, _prostojs_infact.createProvideRegistry)([constructor, () => a]));
|
|
2321
|
+
const brand = getAdapterBrand(constructor);
|
|
2322
|
+
if (brand !== void 0) this.setProvideRegistry((0, _prostojs_infact.createProvideRegistry)([adapterProvideToken(brand), () => a]));
|
|
2323
|
+
}
|
|
2142
2324
|
if (typeof a.getProvideRegistry === "function") this.setProvideRegistry(a.getProvideRegistry());
|
|
2143
2325
|
}
|
|
2144
2326
|
this.unregisteredControllers.unshift({ controller: this });
|
|
@@ -2592,6 +2774,7 @@ exports.Interceptor = Interceptor;
|
|
|
2592
2774
|
exports.InterceptorHandler = InterceptorHandler;
|
|
2593
2775
|
exports.Label = Label;
|
|
2594
2776
|
exports.LoggerTopic = LoggerTopic;
|
|
2777
|
+
exports.MOOST_ADAPTER_BRAND = MOOST_ADAPTER_BRAND;
|
|
2595
2778
|
exports.MOOST_MODULE_IDENTITY_KEY = MOOST_MODULE_IDENTITY_KEY;
|
|
2596
2779
|
exports.Moost = Moost;
|
|
2597
2780
|
exports.MoostDispose = MoostDispose;
|
|
@@ -2667,6 +2850,7 @@ Object.defineProperty(exports, 'eventTypeKey', {
|
|
|
2667
2850
|
return _wooksjs_event_core.eventTypeKey;
|
|
2668
2851
|
}
|
|
2669
2852
|
});
|
|
2853
|
+
exports.forkEventContext = forkEventContext;
|
|
2670
2854
|
Object.defineProperty(exports, 'getClassKey', {
|
|
2671
2855
|
enumerable: true,
|
|
2672
2856
|
get: function () {
|
|
@@ -2751,4 +2935,5 @@ Object.defineProperty(exports, 'useLogger', {
|
|
|
2751
2935
|
}
|
|
2752
2936
|
});
|
|
2753
2937
|
exports.useOvertake = useOvertake;
|
|
2754
|
-
exports.useScopeId = useScopeId;
|
|
2938
|
+
exports.useScopeId = useScopeId;
|
|
2939
|
+
exports.withControllerContext = withControllerContext;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { EventContext, Logger, Key } from '@wooksjs/event-core';
|
|
1
|
+
import { EventContext, Logger, Accessor, 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
4
|
export { TProvideRegistry, createProvideRegistry, createReplaceRegistry, getClassKey } from '@prostojs/infact';
|
|
@@ -1288,10 +1287,6 @@ 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>;
|
|
1293
|
-
/** Registers a DI scope for the given ID and returns an unscope function. */
|
|
1294
|
-
declare function registerEventScope(scopeId: string): () => void;
|
|
1295
1290
|
/**
|
|
1296
1291
|
* Creates the complete event handler lifecycle processor used by adapters.
|
|
1297
1292
|
* Handles scope registration, controller resolution, interceptors, argument resolution,
|
|
@@ -1299,6 +1294,96 @@ declare function registerEventScope(scopeId: string): () => void;
|
|
|
1299
1294
|
*/
|
|
1300
1295
|
declare function defineMoostEventHandler<T>(options: TMoostEventHandlerOptions<T>): () => unknown;
|
|
1301
1296
|
|
|
1297
|
+
/**
|
|
1298
|
+
* Static key an adapter class names itself under, independently of its class
|
|
1299
|
+
* identity — e.g. `static readonly [MOOST_ADAPTER_BRAND] = '@moostjs/event-http/MoostHttp'`.
|
|
1300
|
+
*
|
|
1301
|
+
* DI keys classes by identity. When two copies of an adapter package are loaded
|
|
1302
|
+
* (a dev server evaluating one copy while Node loads another for an externalized
|
|
1303
|
+
* dependency, or a bundle next to an external), a library resolving the adapter
|
|
1304
|
+
* with `useControllerContext().instantiate(MoostHttp)` holds the *other* copy's
|
|
1305
|
+
* class and finds nothing. With a brand, `instantiate()` still returns the
|
|
1306
|
+
* attached adapter (and warns once about the duplicate copy).
|
|
1307
|
+
*/
|
|
1308
|
+
declare const MOOST_ADAPTER_BRAND: unique symbol;
|
|
1309
|
+
|
|
1310
|
+
/**
|
|
1311
|
+
* Composable returning the moost DI scope ID of the current event. Generated on first call
|
|
1312
|
+
* and cached on the event context itself — never inherited from a parent context, so a child
|
|
1313
|
+
* event (a workflow run started with `eventContext`, a WebSocket message) owns its scope
|
|
1314
|
+
* instead of sharing, and later releasing, its parent's.
|
|
1315
|
+
*/
|
|
1316
|
+
declare const useScopeId: (ctx?: EventContext) => string;
|
|
1317
|
+
/** Registers a DI scope for the given ID and returns an unscope function. */
|
|
1318
|
+
declare function registerEventScope(scopeId: string): () => void;
|
|
1319
|
+
/** A slot a child context must not read through from its parent: a `key()` / `cached()` slot, or a `defineWook` composable. */
|
|
1320
|
+
type TIsolatedSlot = Accessor<any> | {
|
|
1321
|
+
readonly _slot: Accessor<any>;
|
|
1322
|
+
};
|
|
1323
|
+
/** Options for {@link forkEventContext}. */
|
|
1324
|
+
interface TForkEventContextOptions {
|
|
1325
|
+
/** The context to fork. Default: the current event context. */
|
|
1326
|
+
parent?: EventContext;
|
|
1327
|
+
/**
|
|
1328
|
+
* Slots the child never reads through from the parent — they are computed or stored in
|
|
1329
|
+
* the child only. Pass the per-event state the child must not inherit (a composable the
|
|
1330
|
+
* parent already built is bound to the parent's context).
|
|
1331
|
+
*/
|
|
1332
|
+
isolate?: Iterable<TIsolatedSlot>;
|
|
1333
|
+
}
|
|
1334
|
+
/**
|
|
1335
|
+
* Creates a copy-on-write child of an event context. Reads fall through to the parent
|
|
1336
|
+
* (request, headers, auth, already computed slots); every write — `ctx.set()`, a controller
|
|
1337
|
+
* context, a composable's state — stays in the child, so running code in the child never
|
|
1338
|
+
* clobbers the parent event's state. The child owns its moost DI scope id (see
|
|
1339
|
+
* {@link withControllerContext} to share the parent's).
|
|
1340
|
+
*
|
|
1341
|
+
* Run code in it with `run(child, fn)`.
|
|
1342
|
+
*
|
|
1343
|
+
* @example
|
|
1344
|
+
* ```ts
|
|
1345
|
+
* const child = forkEventContext({ isolate: [useMyState] })
|
|
1346
|
+
* await run(child, () => doWork())
|
|
1347
|
+
* ```
|
|
1348
|
+
*/
|
|
1349
|
+
declare function forkEventContext(opts?: TForkEventContextOptions): EventContext;
|
|
1350
|
+
/** Options for {@link withControllerContext}. */
|
|
1351
|
+
interface TWithControllerContextOptions extends TForkEventContextOptions {
|
|
1352
|
+
/**
|
|
1353
|
+
* `true` (default): the child runs in the parent event's DI scope — `FOR_EVENT` instances
|
|
1354
|
+
* resolve to (and are shared with) the parent event's. The parent must be a moost event
|
|
1355
|
+
* whose handler is still running.
|
|
1356
|
+
*
|
|
1357
|
+
* `false`: the child gets its own DI scope, registered for the duration of `fn` and
|
|
1358
|
+
* released when `fn` settles — `FOR_EVENT` instances are fresh and dropped afterwards.
|
|
1359
|
+
*/
|
|
1360
|
+
shareScope?: boolean;
|
|
1361
|
+
/** The route reported by `useControllerContext().getRoute()` in the child. Default: `''`. */
|
|
1362
|
+
route?: string;
|
|
1363
|
+
/** The prefix reported by `useControllerContext().getPrefix()` in the child. */
|
|
1364
|
+
prefix?: string;
|
|
1365
|
+
}
|
|
1366
|
+
/**
|
|
1367
|
+
* Runs `fn` in a copy-on-write child of the current event (see {@link forkEventContext})
|
|
1368
|
+
* whose controller context is `instance` / `method` — another controller's hooks, permission
|
|
1369
|
+
* checks and `useControllerContext()` consumers run as if the event had been routed to it,
|
|
1370
|
+
* without touching the current event's state. `FOR_EVENT` dependencies resolve in the parent's
|
|
1371
|
+
* DI scope by default (`shareScope`).
|
|
1372
|
+
*
|
|
1373
|
+
* Use it to call into another controller's logic within the same event. Do not run a moost
|
|
1374
|
+
* event handler (a routed handler function) inside a scope-sharing child: a handler registers
|
|
1375
|
+
* and releases its own scope — use an adapter-level invocation (e.g. `MoostHttp.invoke()`) for that.
|
|
1376
|
+
*
|
|
1377
|
+
* @returns what `fn` returns (a promise stays a promise)
|
|
1378
|
+
*
|
|
1379
|
+
* @example
|
|
1380
|
+
* ```ts
|
|
1381
|
+
* const source = await useControllerContext().instantiate(IssueController)
|
|
1382
|
+
* const allowed = await withControllerContext(source, 'list', () => source.allowedFor(ids))
|
|
1383
|
+
* ```
|
|
1384
|
+
*/
|
|
1385
|
+
declare function withControllerContext<T extends object, R>(instance: T, method: keyof T | string, fn: () => R, opts?: TWithControllerContextOptions): R;
|
|
1386
|
+
|
|
1302
1387
|
/**
|
|
1303
1388
|
* Returns the own method names of an instance, including inherited methods
|
|
1304
1389
|
* from parent classes.
|
|
@@ -1455,5 +1540,5 @@ declare function createLogger(opts?: Partial<TProstoLoggerOptions>): ProstoLogge
|
|
|
1455
1540
|
/** Default colored console transport used by Moost loggers. */
|
|
1456
1541
|
declare const loggerConsoleTransport: _prostojs_logger.TProstoLoggerTransportFn<any>;
|
|
1457
1542
|
|
|
1458
|
-
export { After, ApplyDecorators, Before, Circular, Const, ConstFactory, 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, Provide, Replace, Required, Resolve, Response, TInterceptorPriority, TPipePriority, Value, createLogger, defineAfterInterceptor, defineBeforeInterceptor, defineErrorInterceptor, defineInfactScope, defineInterceptor, defineMoostEventHandler, definePipeFn, disposeInstances, getHandlerPaths, getInfactScopeVars, getInfactSingletonInstances, getInstanceOwnMethods, getInstanceOwnProps, getMoostInfact, getMoostMate, getNewMoostInfact, globalKey, isThenable, loggerConsoleTransport, mergeSorted, registerEventScope, resolvePipe, setControllerContext, setInfactLoggingOptions, setInterceptResult, setOvertake, stampModuleIdentity, stampOnce, useControllerContext, useHandlerPaths, useInterceptResult, useOvertake, useScopeId };
|
|
1459
|
-
export type { TAny, TAnyFn, TClassConstructor, TContextInjectorHook, TControllerOverview, TControllerRegistration, TControllersGroup, TDisposeError, TDisposeOptions, TDisposeResult, TEmpty, TFunction, TGetHandlerPathsOptions, TInjectableScope, TInterceptorAfterFn, TInterceptorBeforeFn, TInterceptorData, TInterceptorDef, TInterceptorDefFactory, TInterceptorEntry, TInterceptorErrorFn, TLogger, TMoostAdapter, TMoostAdapterOptions, TMoostEventHandlerHookOptions, TMoostEventHandlerOptions, TMoostHandler, TMoostMetadata, TMoostModuleIdentity, TMoostOptions, TMoostParamsMetadata, TObject, TOvertakeFn, TPipeData, TPipeFn, TPipeMetas, TPrimitives };
|
|
1543
|
+
export { After, ApplyDecorators, Before, Circular, Const, ConstFactory, Controller, Description, HandlerPaths, Id, ImportController, Inherit, Inject, InjectEventLogger, InjectFromScope, InjectMoost, InjectMoostLogger, InjectScopeVars, Injectable, Intercept, Interceptor, InterceptorHandler, Label, LoggerTopic, MOOST_ADAPTER_BRAND, MOOST_MODULE_IDENTITY_KEY, Moost, MoostDispose, MoostInit, OnError, Optional, Overtake, Param, Params, Pipe, Provide, Replace, Required, Resolve, Response, TInterceptorPriority, TPipePriority, Value, createLogger, defineAfterInterceptor, defineBeforeInterceptor, defineErrorInterceptor, defineInfactScope, defineInterceptor, defineMoostEventHandler, definePipeFn, disposeInstances, forkEventContext, getHandlerPaths, getInfactScopeVars, getInfactSingletonInstances, getInstanceOwnMethods, getInstanceOwnProps, getMoostInfact, getMoostMate, getNewMoostInfact, globalKey, isThenable, loggerConsoleTransport, mergeSorted, registerEventScope, resolvePipe, setControllerContext, setInfactLoggingOptions, setInterceptResult, setOvertake, stampModuleIdentity, stampOnce, useControllerContext, useHandlerPaths, useInterceptResult, useOvertake, useScopeId, withControllerContext };
|
|
1544
|
+
export type { TAny, TAnyFn, TClassConstructor, TContextInjectorHook, TControllerOverview, TControllerRegistration, TControllersGroup, TDisposeError, TDisposeOptions, TDisposeResult, TEmpty, TForkEventContextOptions, TFunction, TGetHandlerPathsOptions, TInjectableScope, TInterceptorAfterFn, TInterceptorBeforeFn, TInterceptorData, TInterceptorDef, TInterceptorDefFactory, TInterceptorEntry, TInterceptorErrorFn, TIsolatedSlot, TLogger, TMoostAdapter, TMoostAdapterOptions, TMoostEventHandlerHookOptions, TMoostEventHandlerOptions, TMoostHandler, TMoostMetadata, TMoostModuleIdentity, TMoostOptions, TMoostParamsMetadata, TObject, TOvertakeFn, TPipeData, TPipeFn, TPipeMetas, TPrimitives, TWithControllerContextOptions };
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { ContextInjector, cached, createEventContext, createEventContext as createEventContext$1, current, current as current$1,
|
|
2
|
-
import { Infact, createProvideRegistry, createProvideRegistry as createProvideRegistry$1, createReplaceRegistry, createReplaceRegistry as createReplaceRegistry$1, getClassKey, getClassKey as getClassKey$1 } from "@prostojs/infact";
|
|
1
|
+
import { ContextInjector, EventContext, 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, run as run$1, useLogger, useLogger as useLogger$1, useRouteParams } from "@wooksjs/event-core";
|
|
3
2
|
import { Mate, getConstructor, getConstructor as getConstructor$1, isConstructor, isConstructor as isConstructor$1 } from "@prostojs/mate";
|
|
4
3
|
import { ProstoLogger, ProstoLogger as ProstoLogger$1, coloredConsole, createConsoleTransort } from "@prostojs/logger";
|
|
4
|
+
import { Infact, createProvideRegistry, createProvideRegistry as createProvideRegistry$1, createReplaceRegistry, createReplaceRegistry as createReplaceRegistry$1, getClassKey, getClassKey as getClassKey$1 } from "@prostojs/infact";
|
|
5
5
|
import { Hookable } from "hookable";
|
|
6
6
|
import { clearGlobalWooks, getGlobalWooks } from "wooks";
|
|
7
7
|
|
|
@@ -47,12 +47,38 @@ let stamped = false;
|
|
|
47
47
|
if (stamped) return;
|
|
48
48
|
stamped = true;
|
|
49
49
|
stampModuleIdentity({
|
|
50
|
-
version: "0.6.
|
|
50
|
+
version: "0.6.43",
|
|
51
51
|
path: typeof __filename === "string" ? __filename : import.meta.url
|
|
52
52
|
});
|
|
53
53
|
}
|
|
54
54
|
stampOnce();
|
|
55
55
|
|
|
56
|
+
//#endregion
|
|
57
|
+
//#region packages/moost/src/adapter-brand.ts
|
|
58
|
+
/**
|
|
59
|
+
* Static key an adapter class names itself under, independently of its class
|
|
60
|
+
* identity — e.g. `static readonly [MOOST_ADAPTER_BRAND] = '@moostjs/event-http/MoostHttp'`.
|
|
61
|
+
*
|
|
62
|
+
* DI keys classes by identity. When two copies of an adapter package are loaded
|
|
63
|
+
* (a dev server evaluating one copy while Node loads another for an externalized
|
|
64
|
+
* dependency, or a bundle next to an external), a library resolving the adapter
|
|
65
|
+
* with `useControllerContext().instantiate(MoostHttp)` holds the *other* copy's
|
|
66
|
+
* class and finds nothing. With a brand, `instantiate()` still returns the
|
|
67
|
+
* attached adapter (and warns once about the duplicate copy).
|
|
68
|
+
*/ const MOOST_ADAPTER_BRAND = Symbol.for("moost:adapter-brand");
|
|
69
|
+
/** The {@link MOOST_ADAPTER_BRAND} of a class (inherited by subclasses), if it carries one. */ function getAdapterBrand(classConstructor) {
|
|
70
|
+
if (typeof classConstructor !== "function") return;
|
|
71
|
+
const brand = classConstructor[MOOST_ADAPTER_BRAND];
|
|
72
|
+
return typeof brand === "string" ? brand : void 0;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Provide-registry token `Moost.init()` files a branded adapter under next to its
|
|
76
|
+
* class — the lookup `instantiate()` uses for an adapter class that is not the
|
|
77
|
+
* attached one (another package copy, or a base class of the attached adapter).
|
|
78
|
+
*/ function adapterProvideToken(brand) {
|
|
79
|
+
return `moost:adapter:${brand}`;
|
|
80
|
+
}
|
|
81
|
+
|
|
56
82
|
//#endregion
|
|
57
83
|
//#region packages/moost/src/logger.ts
|
|
58
84
|
let defaultLogger;
|
|
@@ -95,6 +121,124 @@ function getDefaultLogger(topic) {
|
|
|
95
121
|
return merged.toSorted((a, b) => a.priority - b.priority);
|
|
96
122
|
}
|
|
97
123
|
|
|
124
|
+
//#endregion
|
|
125
|
+
//#region packages/moost/src/event-scope.ts
|
|
126
|
+
function _define_property$2(obj, key, value) {
|
|
127
|
+
if (key in obj) Object.defineProperty(obj, key, {
|
|
128
|
+
value,
|
|
129
|
+
enumerable: true,
|
|
130
|
+
configurable: true,
|
|
131
|
+
writable: true
|
|
132
|
+
});
|
|
133
|
+
else obj[key] = value;
|
|
134
|
+
return obj;
|
|
135
|
+
}
|
|
136
|
+
let _scopeChar = "a";
|
|
137
|
+
let _scopeNum = 0;
|
|
138
|
+
function nextScopeId() {
|
|
139
|
+
if (_scopeNum >= Number.MAX_SAFE_INTEGER) {
|
|
140
|
+
_scopeNum = 0;
|
|
141
|
+
_scopeChar = _scopeChar === "z" ? "a" : String.fromCodePoint((_scopeChar.codePointAt(0) ?? 97) + 1);
|
|
142
|
+
}
|
|
143
|
+
return `__moost_${_scopeChar}_${++_scopeNum}`;
|
|
144
|
+
}
|
|
145
|
+
const scopeIdSlot = cached$1(() => nextScopeId());
|
|
146
|
+
/**
|
|
147
|
+
* Composable returning the moost DI scope ID of the current event. Generated on first call
|
|
148
|
+
* and cached on the event context itself — never inherited from a parent context, so a child
|
|
149
|
+
* event (a workflow run started with `eventContext`, a WebSocket message) owns its scope
|
|
150
|
+
* instead of sharing, and later releasing, its parent's.
|
|
151
|
+
*/ const useScopeId = (ctx) => (ctx ?? current$1()).getOwn(scopeIdSlot);
|
|
152
|
+
/** Registers a DI scope for the given ID and returns an unscope function. */ function registerEventScope(scopeId) {
|
|
153
|
+
const infact = getMoostInfact();
|
|
154
|
+
infact.registerScope(scopeId);
|
|
155
|
+
return () => {
|
|
156
|
+
infact.unregisterScope(scopeId);
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
const NO_ISOLATION = /* @__PURE__ */ new Set();
|
|
160
|
+
/** Copy-on-write child: reads fall through to the parent, every write stays in the child. */ let ForkedEventContext = class ForkedEventContext extends EventContext {
|
|
161
|
+
set(slot, value) {
|
|
162
|
+
this.setOwn(slot, value);
|
|
163
|
+
}
|
|
164
|
+
_shouldTraverseParent(id) {
|
|
165
|
+
return !this.isolated.has(id);
|
|
166
|
+
}
|
|
167
|
+
constructor(parent, isolated) {
|
|
168
|
+
super({
|
|
169
|
+
logger: parent.logger,
|
|
170
|
+
parent
|
|
171
|
+
}), _define_property$2(this, "isolated", void 0), this.isolated = isolated;
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
/**
|
|
175
|
+
* Creates a copy-on-write child of an event context. Reads fall through to the parent
|
|
176
|
+
* (request, headers, auth, already computed slots); every write — `ctx.set()`, a controller
|
|
177
|
+
* context, a composable's state — stays in the child, so running code in the child never
|
|
178
|
+
* clobbers the parent event's state. The child owns its moost DI scope id (see
|
|
179
|
+
* {@link withControllerContext} to share the parent's).
|
|
180
|
+
*
|
|
181
|
+
* Run code in it with `run(child, fn)`.
|
|
182
|
+
*
|
|
183
|
+
* @example
|
|
184
|
+
* ```ts
|
|
185
|
+
* const child = forkEventContext({ isolate: [useMyState] })
|
|
186
|
+
* await run(child, () => doWork())
|
|
187
|
+
* ```
|
|
188
|
+
*/ function forkEventContext(opts) {
|
|
189
|
+
const parent = opts?.parent ?? current$1();
|
|
190
|
+
if (!opts?.isolate) return new ForkedEventContext(parent, NO_ISOLATION);
|
|
191
|
+
const ids = /* @__PURE__ */ new Set();
|
|
192
|
+
for (const slot of opts.isolate) ids.add(("_slot" in slot ? slot._slot : slot)._id);
|
|
193
|
+
return new ForkedEventContext(parent, ids);
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Runs `fn` in a copy-on-write child of the current event (see {@link forkEventContext})
|
|
197
|
+
* whose controller context is `instance` / `method` — another controller's hooks, permission
|
|
198
|
+
* checks and `useControllerContext()` consumers run as if the event had been routed to it,
|
|
199
|
+
* without touching the current event's state. `FOR_EVENT` dependencies resolve in the parent's
|
|
200
|
+
* DI scope by default (`shareScope`).
|
|
201
|
+
*
|
|
202
|
+
* Use it to call into another controller's logic within the same event. Do not run a moost
|
|
203
|
+
* event handler (a routed handler function) inside a scope-sharing child: a handler registers
|
|
204
|
+
* and releases its own scope — use an adapter-level invocation (e.g. `MoostHttp.invoke()`) for that.
|
|
205
|
+
*
|
|
206
|
+
* @returns what `fn` returns (a promise stays a promise)
|
|
207
|
+
*
|
|
208
|
+
* @example
|
|
209
|
+
* ```ts
|
|
210
|
+
* const source = await useControllerContext().instantiate(IssueController)
|
|
211
|
+
* const allowed = await withControllerContext(source, 'list', () => source.allowedFor(ids))
|
|
212
|
+
* ```
|
|
213
|
+
*/ function withControllerContext(instance, method, fn, opts) {
|
|
214
|
+
const parent = opts?.parent ?? current$1();
|
|
215
|
+
const child = forkEventContext({
|
|
216
|
+
parent,
|
|
217
|
+
isolate: opts?.isolate
|
|
218
|
+
});
|
|
219
|
+
setControllerContext(instance, method, opts?.route ?? "", {
|
|
220
|
+
prefix: opts?.prefix,
|
|
221
|
+
ctx: child
|
|
222
|
+
});
|
|
223
|
+
if (opts?.shareScope !== false) {
|
|
224
|
+
child.setOwn(scopeIdSlot, useScopeId(parent));
|
|
225
|
+
return run$1(child, fn);
|
|
226
|
+
}
|
|
227
|
+
const release = registerEventScope(useScopeId(child));
|
|
228
|
+
let result;
|
|
229
|
+
try {
|
|
230
|
+
result = run$1(child, fn);
|
|
231
|
+
} catch (error) {
|
|
232
|
+
release();
|
|
233
|
+
throw error;
|
|
234
|
+
}
|
|
235
|
+
if (!isThenable(result)) {
|
|
236
|
+
release();
|
|
237
|
+
return result;
|
|
238
|
+
}
|
|
239
|
+
return Promise.resolve(result).finally(release);
|
|
240
|
+
}
|
|
241
|
+
|
|
98
242
|
//#endregion
|
|
99
243
|
//#region packages/moost/src/pipes/run-pipes.ts
|
|
100
244
|
function runPipes(pipes, initialValue, metas, level) {
|
|
@@ -388,6 +532,23 @@ const controllerPrefixKey = globalKey("controller.prefix");
|
|
|
388
532
|
_ctx.set(controllerRouteKey, route);
|
|
389
533
|
if (opts?.prefix !== void 0) _ctx.set(controllerPrefixKey, opts.prefix);
|
|
390
534
|
}
|
|
535
|
+
/** Brands already warned about by {@link brandedAdapterFor} (one warning per process). */ const reportedAdapterCopies = /* @__PURE__ */ new Set();
|
|
536
|
+
/**
|
|
537
|
+
* The attached adapter `instantiate(c)` resolves through its brand (see `MOOST_ADAPTER_BRAND`)
|
|
538
|
+
* when `c` is not that adapter's own class: a base class of a custom adapter, or the adapter class
|
|
539
|
+
* from another copy of its package (warned once). `undefined` → the plain DI lookup applies.
|
|
540
|
+
*/ function brandedAdapterFor(controller, c) {
|
|
541
|
+
const brand = getAdapterBrand(c);
|
|
542
|
+
if (brand === void 0) return;
|
|
543
|
+
const adapter = getMoostInfact().getInstanceRegistries(controller).provide?.[adapterProvideToken(brand)]?.fn();
|
|
544
|
+
const attached = adapter ? getConstructor$1(adapter) : void 0;
|
|
545
|
+
if (!attached || attached === c || c.prototype instanceof attached) return;
|
|
546
|
+
if (!(adapter instanceof c) && !reportedAdapterCopies.has(brand)) {
|
|
547
|
+
reportedAdapterCopies.add(brand);
|
|
548
|
+
getDefaultLogger("moost").warn(`instantiate(${c.name}) resolved the attached adapter through its brand "${brand}": the class passed in comes from another copy of its package. Two copies of a package split its module state — make the bundler / SSR externalization load one copy (for @moostjs/vite see the ssrExternalCheck warnings).`);
|
|
549
|
+
}
|
|
550
|
+
return adapter;
|
|
551
|
+
}
|
|
391
552
|
/**
|
|
392
553
|
* Provides access to the current controller context within an event handler.
|
|
393
554
|
* Returns utilities for accessing the controller instance, method metadata, and DI.
|
|
@@ -400,7 +561,9 @@ const controllerPrefixKey = globalKey("controller.prefix");
|
|
|
400
561
|
const getControllerMeta = () => getMoostMate().read(getController());
|
|
401
562
|
const getMethodMeta = (name) => getMoostMate().read(getController(), name || getMethod());
|
|
402
563
|
function instantiate(c) {
|
|
403
|
-
|
|
564
|
+
const controller = getController();
|
|
565
|
+
const adapter = brandedAdapterFor(controller, c);
|
|
566
|
+
return adapter ? Promise.resolve(adapter) : getMoostInfact().getForInstance(controller, c);
|
|
404
567
|
}
|
|
405
568
|
return {
|
|
406
569
|
instantiate,
|
|
@@ -436,21 +599,33 @@ const interceptResultKey = globalKey("interceptor.result");
|
|
|
436
599
|
|
|
437
600
|
//#endregion
|
|
438
601
|
//#region packages/moost/src/adapter-utils.ts
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
602
|
+
const noop = () => {};
|
|
603
|
+
/**
|
|
604
|
+
* Registers an event's DI scope, held by its handler lifecycle and, with `adapterHolds`, by
|
|
605
|
+
* the adapter too. Each release is idempotent; the scope is dropped once every holder let go,
|
|
606
|
+
* so an adapter signal that fires early (a client disconnect mid-handler) cannot pull the
|
|
607
|
+
* scope from under a running handler. Built outside the handler closure so a long-lived
|
|
608
|
+
* adapter listener (the response 'close' of a streamed body) retains only the scope.
|
|
609
|
+
*/ function holdEventScope(scopeId, adapterHolds) {
|
|
610
|
+
const drop = registerEventScope(scopeId);
|
|
611
|
+
let holders = adapterHolds ? 2 : 1;
|
|
612
|
+
const hold = () => {
|
|
613
|
+
let held = true;
|
|
614
|
+
return () => {
|
|
615
|
+
if (held) {
|
|
616
|
+
held = false;
|
|
617
|
+
if (--holders === 0) drop();
|
|
618
|
+
}
|
|
619
|
+
};
|
|
620
|
+
};
|
|
621
|
+
const releaseLifecycle = hold();
|
|
622
|
+
return {
|
|
623
|
+
releaseLifecycle,
|
|
624
|
+
failLifecycle: (error) => {
|
|
625
|
+
releaseLifecycle();
|
|
626
|
+
throw error;
|
|
627
|
+
},
|
|
628
|
+
unscope: adapterHolds ? hold() : noop
|
|
454
629
|
};
|
|
455
630
|
}
|
|
456
631
|
/**
|
|
@@ -459,6 +634,7 @@ function nextScopeId() {
|
|
|
459
634
|
* handler invocation, and cleanup — optimised for sync-first execution.
|
|
460
635
|
*/ function defineMoostEventHandler(options) {
|
|
461
636
|
const ci = getContextInjector$1();
|
|
637
|
+
const manualUnscope = !!options.manualUnscope;
|
|
462
638
|
const handlerSpanName = `Handler:${options.targetPath}`;
|
|
463
639
|
const handlerAttrs = {
|
|
464
640
|
"moost.handler": options.controllerMethod || "",
|
|
@@ -468,7 +644,7 @@ function nextScopeId() {
|
|
|
468
644
|
const ctx = current$1();
|
|
469
645
|
const scopeId = useScopeId(ctx);
|
|
470
646
|
const logger = useLogger$1(options.loggerTitle, ctx);
|
|
471
|
-
const unscope =
|
|
647
|
+
const { unscope, releaseLifecycle, failLifecycle } = holdEventScope(scopeId, manualUnscope);
|
|
472
648
|
let response;
|
|
473
649
|
let hookOptions;
|
|
474
650
|
function getHookOptions() {
|
|
@@ -484,14 +660,17 @@ function nextScopeId() {
|
|
|
484
660
|
let interceptorHandler;
|
|
485
661
|
let raise = false;
|
|
486
662
|
try {
|
|
663
|
+
const result = start();
|
|
664
|
+
return isThenable(result) ? result.then(void 0, failLifecycle) : result;
|
|
665
|
+
} catch (error) {
|
|
666
|
+
return failLifecycle(error);
|
|
667
|
+
}
|
|
668
|
+
function start() {
|
|
487
669
|
if (options.hooks?.init) {
|
|
488
670
|
const hookResult = options.hooks.init(getHookOptions());
|
|
489
671
|
if (isThenable(hookResult)) return hookResult.then(afterInit);
|
|
490
672
|
}
|
|
491
673
|
return afterInit();
|
|
492
|
-
} catch (error) {
|
|
493
|
-
if (!options.manualUnscope) unscope();
|
|
494
|
-
throw error;
|
|
495
674
|
}
|
|
496
675
|
function afterInit() {
|
|
497
676
|
const instanceResult = options.getControllerInstance();
|
|
@@ -575,7 +754,6 @@ function nextScopeId() {
|
|
|
575
754
|
return finalize();
|
|
576
755
|
}, (error) => {
|
|
577
756
|
if (options.logErrors) logger.error(String(error));
|
|
578
|
-
if (!options.manualUnscope) unscope();
|
|
579
757
|
throw error;
|
|
580
758
|
});
|
|
581
759
|
response = afterResult;
|
|
@@ -584,7 +762,7 @@ function nextScopeId() {
|
|
|
584
762
|
return finalize();
|
|
585
763
|
}
|
|
586
764
|
function finalize() {
|
|
587
|
-
|
|
765
|
+
releaseLifecycle();
|
|
588
766
|
if (options.hooks?.end) {
|
|
589
767
|
const endResult = options.hooks.end(getHookOptions());
|
|
590
768
|
if (isThenable(endResult)) return endResult.then(() => {
|
|
@@ -2137,7 +2315,11 @@ function _define_property(obj, key, value) {
|
|
|
2137
2315
|
this.setProvideRegistry(createProvideRegistry$1([Moost, () => this], [ProstoLogger$1, () => this.logger], ["MOOST_LOGGER", () => this.logger]));
|
|
2138
2316
|
for (const a of this.adapters) {
|
|
2139
2317
|
const constructor = getConstructor$1(a);
|
|
2140
|
-
if (constructor)
|
|
2318
|
+
if (constructor) {
|
|
2319
|
+
this.setProvideRegistry(createProvideRegistry$1([constructor, () => a]));
|
|
2320
|
+
const brand = getAdapterBrand(constructor);
|
|
2321
|
+
if (brand !== void 0) this.setProvideRegistry(createProvideRegistry$1([adapterProvideToken(brand), () => a]));
|
|
2322
|
+
}
|
|
2141
2323
|
if (typeof a.getProvideRegistry === "function") this.setProvideRegistry(a.getProvideRegistry());
|
|
2142
2324
|
}
|
|
2143
2325
|
this.unregisteredControllers.unshift({ controller: this });
|
|
@@ -2561,4 +2743,4 @@ function _define_property(obj, key, value) {
|
|
|
2561
2743
|
}
|
|
2562
2744
|
|
|
2563
2745
|
//#endregion
|
|
2564
|
-
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 };
|
|
2746
|
+
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_ADAPTER_BRAND, 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, forkEventContext, 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, withControllerContext };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "moost",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.43",
|
|
4
4
|
"description": "moost",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"composables",
|
|
@@ -41,15 +41,15 @@
|
|
|
41
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.
|
|
44
|
+
"@wooksjs/event-core": "^0.7.26",
|
|
45
45
|
"hookable": "^5.5.3",
|
|
46
|
-
"wooks": "^0.7.
|
|
46
|
+
"wooks": "^0.7.26"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
-
"@wooksjs/event-http": "^0.7.
|
|
50
|
-
"@wooksjs/http-body": "^0.7.
|
|
49
|
+
"@wooksjs/event-http": "^0.7.26",
|
|
50
|
+
"@wooksjs/http-body": "^0.7.26",
|
|
51
51
|
"vitest": "3.2.7",
|
|
52
|
-
"@moostjs/event-http": "^0.6.
|
|
52
|
+
"@moostjs/event-http": "^0.6.43"
|
|
53
53
|
},
|
|
54
54
|
"scripts": {
|
|
55
55
|
"pub": "pnpm publish --access public",
|