moost 0.6.5 → 0.6.7
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 +10 -4
- package/dist/index.d.ts +6 -1
- package/dist/index.mjs +10 -4
- package/package.json +8 -14
- package/scripts/setup-skills.js +0 -76
- package/skills/moost/SKILL.md +0 -34
- package/skills/moost/core.md +0 -174
- package/skills/moost/custom-adapters.md +0 -744
- package/skills/moost/decorators.md +0 -185
- package/skills/moost/di.md +0 -161
- package/skills/moost/interceptors.md +0 -199
- package/skills/moost/pipes.md +0 -213
package/dist/index.cjs
CHANGED
|
@@ -248,14 +248,16 @@ const scopeVarsMap = /* @__PURE__ */ new Map();
|
|
|
248
248
|
const controllerInstanceKey = (0, __wooksjs_event_core.key)("controller.instance");
|
|
249
249
|
const controllerMethodKey = (0, __wooksjs_event_core.key)("controller.method");
|
|
250
250
|
const controllerRouteKey = (0, __wooksjs_event_core.key)("controller.route");
|
|
251
|
+
const controllerPrefixKey = (0, __wooksjs_event_core.key)("controller.prefix");
|
|
251
252
|
/**
|
|
252
253
|
* Sets the controller context for the current event scope.
|
|
253
254
|
* Called internally by adapters when dispatching events to handlers.
|
|
254
|
-
*/ function setControllerContext(controller, method, route,
|
|
255
|
-
const _ctx = ctx || (0, __wooksjs_event_core.current)();
|
|
255
|
+
*/ function setControllerContext(controller, method, route, opts) {
|
|
256
|
+
const _ctx = opts?.ctx || (0, __wooksjs_event_core.current)();
|
|
256
257
|
_ctx.set(controllerInstanceKey, controller);
|
|
257
258
|
_ctx.set(controllerMethodKey, method);
|
|
258
259
|
_ctx.set(controllerRouteKey, route);
|
|
260
|
+
if (opts?.prefix !== void 0) _ctx.set(controllerPrefixKey, opts.prefix);
|
|
259
261
|
}
|
|
260
262
|
/**
|
|
261
263
|
* Provides access to the current controller context within an event handler.
|
|
@@ -265,6 +267,7 @@ const controllerRouteKey = (0, __wooksjs_event_core.key)("controller.route");
|
|
|
265
267
|
const getController = () => _ctx.get(controllerInstanceKey);
|
|
266
268
|
const getMethod = () => _ctx.get(controllerMethodKey);
|
|
267
269
|
const getRoute = () => _ctx.get(controllerRouteKey);
|
|
270
|
+
const getPrefix = () => _ctx.get(controllerPrefixKey);
|
|
268
271
|
const getControllerMeta = () => getMoostMate().read(getController());
|
|
269
272
|
const getMethodMeta = (name) => getMoostMate().read(getController(), name || getMethod());
|
|
270
273
|
function instantiate(c) {
|
|
@@ -273,6 +276,7 @@ const controllerRouteKey = (0, __wooksjs_event_core.key)("controller.route");
|
|
|
273
276
|
return {
|
|
274
277
|
instantiate,
|
|
275
278
|
getRoute,
|
|
279
|
+
getPrefix,
|
|
276
280
|
getController,
|
|
277
281
|
getMethod,
|
|
278
282
|
getControllerMeta,
|
|
@@ -370,7 +374,7 @@ function nextScopeId() {
|
|
|
370
374
|
}
|
|
371
375
|
function afterInstance(instance) {
|
|
372
376
|
if (instance) {
|
|
373
|
-
setControllerContext(instance, options.controllerMethod || "", options.targetPath);
|
|
377
|
+
setControllerContext(instance, options.controllerMethod || "", options.targetPath, { prefix: options.controllerPrefix });
|
|
374
378
|
ci?.hook(options.handlerType, "Controller:registered");
|
|
375
379
|
}
|
|
376
380
|
interceptorHandler = options.getIterceptorHandler();
|
|
@@ -1512,6 +1516,8 @@ function _define_property(obj, key$3, value) {
|
|
|
1512
1516
|
const classMeta = mate$1.read(controller);
|
|
1513
1517
|
const infact = getMoostInfact();
|
|
1514
1518
|
const isControllerConsructor = (0, __prostojs_mate.isConstructor)(controller);
|
|
1519
|
+
const ownPrefix = typeof replaceOwnPrefix === "string" ? replaceOwnPrefix : classMeta?.controller?.prefix || "";
|
|
1520
|
+
const computedPrefix = `${globalPrefix}/${ownPrefix}`;
|
|
1515
1521
|
const pipes = mergeSorted(this.pipes, classMeta?.pipes);
|
|
1516
1522
|
let instance;
|
|
1517
1523
|
const infactOpts = {
|
|
@@ -1520,7 +1526,7 @@ function _define_property(obj, key$3, value) {
|
|
|
1520
1526
|
customData: { pipes }
|
|
1521
1527
|
};
|
|
1522
1528
|
if (isControllerConsructor && (classMeta?.injectable === "SINGLETON" || classMeta?.injectable === true)) await (0, __wooksjs_event_core.createEventContext)({ logger: this.logger }, async () => {
|
|
1523
|
-
setControllerContext(this, "bindController", "");
|
|
1529
|
+
setControllerContext(this, "bindController", "", { prefix: computedPrefix });
|
|
1524
1530
|
instance = await infact.get(controller, infactOpts);
|
|
1525
1531
|
});
|
|
1526
1532
|
else if (!isControllerConsructor) {
|
package/dist/index.d.ts
CHANGED
|
@@ -577,6 +577,7 @@ interface TMoostEventHandlerOptions<T> {
|
|
|
577
577
|
end?: (opts: TMoostEventHandlerHookOptions<T>) => unknown;
|
|
578
578
|
};
|
|
579
579
|
targetPath: string;
|
|
580
|
+
controllerPrefix?: string;
|
|
580
581
|
handlerType: string;
|
|
581
582
|
}
|
|
582
583
|
/** Composable returning the moost scope ID for the current event. Generates on first call, caches for subsequent calls. */
|
|
@@ -774,7 +775,10 @@ interface TMoostAdapter<H> {
|
|
|
774
775
|
* Sets the controller context for the current event scope.
|
|
775
776
|
* Called internally by adapters when dispatching events to handlers.
|
|
776
777
|
*/
|
|
777
|
-
declare function setControllerContext<T>(controller: T, method: keyof T, route: string,
|
|
778
|
+
declare function setControllerContext<T>(controller: T, method: keyof T, route: string, opts?: {
|
|
779
|
+
prefix?: string;
|
|
780
|
+
ctx?: EventContext;
|
|
781
|
+
}): void;
|
|
778
782
|
/**
|
|
779
783
|
* Provides access to the current controller context within an event handler.
|
|
780
784
|
* Returns utilities for accessing the controller instance, method metadata, and DI.
|
|
@@ -782,6 +786,7 @@ declare function setControllerContext<T>(controller: T, method: keyof T, route:
|
|
|
782
786
|
declare function useControllerContext<T extends object>(ctx?: EventContext): {
|
|
783
787
|
instantiate: <TT>(c: TClassConstructor<TT>) => Promise<TT>;
|
|
784
788
|
getRoute: () => string;
|
|
789
|
+
getPrefix: () => string;
|
|
785
790
|
getController: () => T;
|
|
786
791
|
getMethod: () => string | undefined;
|
|
787
792
|
getControllerMeta: <TT extends object>() => (TMoostMetadata<TEmpty> & TT & {
|
package/dist/index.mjs
CHANGED
|
@@ -225,14 +225,16 @@ const scopeVarsMap = /* @__PURE__ */ new Map();
|
|
|
225
225
|
const controllerInstanceKey = key$1("controller.instance");
|
|
226
226
|
const controllerMethodKey = key$1("controller.method");
|
|
227
227
|
const controllerRouteKey = key$1("controller.route");
|
|
228
|
+
const controllerPrefixKey = key$1("controller.prefix");
|
|
228
229
|
/**
|
|
229
230
|
* Sets the controller context for the current event scope.
|
|
230
231
|
* Called internally by adapters when dispatching events to handlers.
|
|
231
|
-
*/ function setControllerContext(controller, method, route,
|
|
232
|
-
const _ctx = ctx || current$1();
|
|
232
|
+
*/ function setControllerContext(controller, method, route, opts) {
|
|
233
|
+
const _ctx = opts?.ctx || current$1();
|
|
233
234
|
_ctx.set(controllerInstanceKey, controller);
|
|
234
235
|
_ctx.set(controllerMethodKey, method);
|
|
235
236
|
_ctx.set(controllerRouteKey, route);
|
|
237
|
+
if (opts?.prefix !== void 0) _ctx.set(controllerPrefixKey, opts.prefix);
|
|
236
238
|
}
|
|
237
239
|
/**
|
|
238
240
|
* Provides access to the current controller context within an event handler.
|
|
@@ -242,6 +244,7 @@ const controllerRouteKey = key$1("controller.route");
|
|
|
242
244
|
const getController = () => _ctx.get(controllerInstanceKey);
|
|
243
245
|
const getMethod = () => _ctx.get(controllerMethodKey);
|
|
244
246
|
const getRoute = () => _ctx.get(controllerRouteKey);
|
|
247
|
+
const getPrefix = () => _ctx.get(controllerPrefixKey);
|
|
245
248
|
const getControllerMeta = () => getMoostMate().read(getController());
|
|
246
249
|
const getMethodMeta = (name) => getMoostMate().read(getController(), name || getMethod());
|
|
247
250
|
function instantiate(c) {
|
|
@@ -250,6 +253,7 @@ const controllerRouteKey = key$1("controller.route");
|
|
|
250
253
|
return {
|
|
251
254
|
instantiate,
|
|
252
255
|
getRoute,
|
|
256
|
+
getPrefix,
|
|
253
257
|
getController,
|
|
254
258
|
getMethod,
|
|
255
259
|
getControllerMeta,
|
|
@@ -347,7 +351,7 @@ function nextScopeId() {
|
|
|
347
351
|
}
|
|
348
352
|
function afterInstance(instance) {
|
|
349
353
|
if (instance) {
|
|
350
|
-
setControllerContext(instance, options.controllerMethod || "", options.targetPath);
|
|
354
|
+
setControllerContext(instance, options.controllerMethod || "", options.targetPath, { prefix: options.controllerPrefix });
|
|
351
355
|
ci?.hook(options.handlerType, "Controller:registered");
|
|
352
356
|
}
|
|
353
357
|
interceptorHandler = options.getIterceptorHandler();
|
|
@@ -1489,6 +1493,8 @@ function _define_property(obj, key$2, value) {
|
|
|
1489
1493
|
const classMeta = mate$1.read(controller);
|
|
1490
1494
|
const infact = getMoostInfact();
|
|
1491
1495
|
const isControllerConsructor = isConstructor$1(controller);
|
|
1496
|
+
const ownPrefix = typeof replaceOwnPrefix === "string" ? replaceOwnPrefix : classMeta?.controller?.prefix || "";
|
|
1497
|
+
const computedPrefix = `${globalPrefix}/${ownPrefix}`;
|
|
1492
1498
|
const pipes = mergeSorted(this.pipes, classMeta?.pipes);
|
|
1493
1499
|
let instance;
|
|
1494
1500
|
const infactOpts = {
|
|
@@ -1497,7 +1503,7 @@ function _define_property(obj, key$2, value) {
|
|
|
1497
1503
|
customData: { pipes }
|
|
1498
1504
|
};
|
|
1499
1505
|
if (isControllerConsructor && (classMeta?.injectable === "SINGLETON" || classMeta?.injectable === true)) await createEventContext$1({ logger: this.logger }, async () => {
|
|
1500
|
-
setControllerContext(this, "bindController", "");
|
|
1506
|
+
setControllerContext(this, "bindController", "", { prefix: computedPrefix });
|
|
1501
1507
|
instance = await infact.get(controller, infactOpts);
|
|
1502
1508
|
});
|
|
1503
1509
|
else if (!isControllerConsructor) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "moost",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.7",
|
|
4
4
|
"description": "moost",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"composables",
|
|
@@ -21,13 +21,8 @@
|
|
|
21
21
|
"url": "git+https://github.com/moostjs/moostjs.git",
|
|
22
22
|
"directory": "packages/moost"
|
|
23
23
|
},
|
|
24
|
-
"bin": {
|
|
25
|
-
"moost-skill": "./scripts/setup-skills.js"
|
|
26
|
-
},
|
|
27
24
|
"files": [
|
|
28
|
-
"dist"
|
|
29
|
-
"skills",
|
|
30
|
-
"scripts/setup-skills.js"
|
|
25
|
+
"dist"
|
|
31
26
|
],
|
|
32
27
|
"type": "module",
|
|
33
28
|
"sideEffects": false,
|
|
@@ -46,19 +41,18 @@
|
|
|
46
41
|
"@prostojs/infact": "^0.4.1",
|
|
47
42
|
"@prostojs/logger": "^0.4.3",
|
|
48
43
|
"@prostojs/mate": "^0.4.0",
|
|
49
|
-
"@wooksjs/event-core": "^0.7.
|
|
44
|
+
"@wooksjs/event-core": "^0.7.9",
|
|
50
45
|
"hookable": "^5.5.3",
|
|
51
|
-
"wooks": "^0.7.
|
|
46
|
+
"wooks": "^0.7.9"
|
|
52
47
|
},
|
|
53
48
|
"devDependencies": {
|
|
54
|
-
"@wooksjs/event-http": "^0.7.
|
|
55
|
-
"@wooksjs/http-body": "^0.7.
|
|
49
|
+
"@wooksjs/event-http": "^0.7.9",
|
|
50
|
+
"@wooksjs/http-body": "^0.7.9",
|
|
56
51
|
"vitest": "3.2.4",
|
|
57
|
-
"@moostjs/event-http": "^0.6.
|
|
52
|
+
"@moostjs/event-http": "^0.6.7"
|
|
58
53
|
},
|
|
59
54
|
"scripts": {
|
|
60
55
|
"pub": "pnpm publish --access public",
|
|
61
|
-
"test": "vitest"
|
|
62
|
-
"setup-skills": "node ./scripts/setup-skills.js"
|
|
56
|
+
"test": "vitest"
|
|
63
57
|
}
|
|
64
58
|
}
|
package/scripts/setup-skills.js
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/* prettier-ignore */
|
|
3
|
-
import fs from 'fs'
|
|
4
|
-
import path from 'path'
|
|
5
|
-
import os from 'os'
|
|
6
|
-
import { fileURLToPath } from 'url'
|
|
7
|
-
|
|
8
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
9
|
-
|
|
10
|
-
const SKILL_NAME = 'moost'
|
|
11
|
-
const SKILL_SRC = path.join(__dirname, '..', 'skills', SKILL_NAME)
|
|
12
|
-
|
|
13
|
-
if (!fs.existsSync(SKILL_SRC)) {
|
|
14
|
-
console.error(`No skills found at ${SKILL_SRC}`)
|
|
15
|
-
console.error('Add your SKILL.md files to the skills/' + SKILL_NAME + '/ directory first.')
|
|
16
|
-
process.exit(1)
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const AGENTS = {
|
|
20
|
-
'Claude Code': { dir: '.claude/skills', global: path.join(os.homedir(), '.claude', 'skills') },
|
|
21
|
-
'Cursor': { dir: '.cursor/skills', global: path.join(os.homedir(), '.cursor', 'skills') },
|
|
22
|
-
'Windsurf': { dir: '.windsurf/skills', global: path.join(os.homedir(), '.windsurf', 'skills') },
|
|
23
|
-
'Codex': { dir: '.codex/skills', global: path.join(os.homedir(), '.codex', 'skills') },
|
|
24
|
-
'OpenCode': { dir: '.opencode/skills', global: path.join(os.homedir(), '.opencode', 'skills') },
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const args = process.argv.slice(2)
|
|
28
|
-
const isGlobal = args.includes('--global') || args.includes('-g')
|
|
29
|
-
const isPostinstall = args.includes('--postinstall')
|
|
30
|
-
let installed = 0, skipped = 0
|
|
31
|
-
const installedDirs = []
|
|
32
|
-
|
|
33
|
-
for (const [agentName, cfg] of Object.entries(AGENTS)) {
|
|
34
|
-
const targetBase = isGlobal ? cfg.global : path.join(process.cwd(), cfg.dir)
|
|
35
|
-
const agentRootDir = path.dirname(cfg.global)
|
|
36
|
-
|
|
37
|
-
if (isPostinstall || isGlobal) {
|
|
38
|
-
if (!fs.existsSync(agentRootDir)) { skipped++; continue }
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const dest = path.join(targetBase, SKILL_NAME)
|
|
42
|
-
try {
|
|
43
|
-
fs.mkdirSync(dest, { recursive: true })
|
|
44
|
-
fs.cpSync(SKILL_SRC, dest, { recursive: true })
|
|
45
|
-
console.log(`[${SKILL_NAME}] installed to ${dest}`)
|
|
46
|
-
installed++
|
|
47
|
-
if (!isGlobal) installedDirs.push(cfg.dir + '/' + SKILL_NAME)
|
|
48
|
-
} catch (err) {
|
|
49
|
-
console.warn(`[${SKILL_NAME}] failed — ${err.message}`)
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
if (!isGlobal && installedDirs.length > 0) {
|
|
54
|
-
const gitignorePath = path.join(process.cwd(), '.gitignore')
|
|
55
|
-
let gitignoreContent = ''
|
|
56
|
-
try { gitignoreContent = fs.readFileSync(gitignorePath, 'utf8') } catch {}
|
|
57
|
-
const linesToAdd = installedDirs.filter(d => !gitignoreContent.includes(d))
|
|
58
|
-
if (linesToAdd.length > 0) {
|
|
59
|
-
const hasHeader = gitignoreContent.includes('# AI agent skills')
|
|
60
|
-
const block = (gitignoreContent && !gitignoreContent.endsWith('\n') ? '\n' : '')
|
|
61
|
-
+ (hasHeader ? '' : '\n# AI agent skills (auto-generated by setup-skills)\n')
|
|
62
|
-
+ linesToAdd.join('\n') + '\n'
|
|
63
|
-
fs.appendFileSync(gitignorePath, block)
|
|
64
|
-
console.log(`[${SKILL_NAME}] added entries to .gitignore`)
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (installed === 0 && isPostinstall) {
|
|
69
|
-
// Silence is fine — no agents present, nothing to do
|
|
70
|
-
} else if (installed === 0 && skipped === Object.keys(AGENTS).length) {
|
|
71
|
-
console.log('No agent directories detected. Try --global or run without it for project-local install.')
|
|
72
|
-
} else if (installed === 0) {
|
|
73
|
-
console.log('Nothing installed. Run without --global to install project-locally.')
|
|
74
|
-
} else {
|
|
75
|
-
console.log(`Done! Restart your AI agent to pick up the "${SKILL_NAME}" skill.`)
|
|
76
|
-
}
|
package/skills/moost/SKILL.md
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: moost
|
|
3
|
-
description: Use this skill when working with moost — to create a Moost application, register controllers with @Controller(), build custom event adapters implementing TMoostAdapter, use defineMoostEventHandler() to bridge event sources, define routes with decorators, configure dependency injection with @Injectable()/@Inject()/@Provide(), create interceptors with @Intercept() and InterceptorHandler, build pipe pipelines with @Pipe()/@Resolve(), access metadata with getMoostMate(), or understand the adapter pattern used by MoostHttp, MoostWs, and MoostWf.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# moost
|
|
7
|
-
|
|
8
|
-
Moost is a metadata-driven event processing framework for TypeScript. It uses decorators and a composable architecture (powered by Wooks) to handle HTTP, CLI, WebSocket, and Workflow events — or any custom event type via the adapter pattern.
|
|
9
|
-
|
|
10
|
-
## How to use this skill
|
|
11
|
-
|
|
12
|
-
Read the domain file that matches the task. Do not load all files — only what you need.
|
|
13
|
-
|
|
14
|
-
| Domain | File | Load when... |
|
|
15
|
-
|--------|------|------------|
|
|
16
|
-
| Core concepts & setup | [core.md](core.md) | Starting a new project, understanding the Moost class, registering controllers, configuring adapters |
|
|
17
|
-
| Custom adapters | [custom-adapters.md](custom-adapters.md) | Building a new event adapter implementing TMoostAdapter, using defineMoostEventHandler, understanding bindHandler lifecycle |
|
|
18
|
-
| Decorators & metadata | [decorators.md](decorators.md) | Creating or using decorators, reading/writing metadata with getMoostMate(), defining handler types |
|
|
19
|
-
| Dependency injection | [di.md](di.md) | Configuring DI scopes, @Injectable, @Inject, @Provide, @Circular, createProvideRegistry |
|
|
20
|
-
| Interceptors | [interceptors.md](interceptors.md) | Creating interceptors, understanding priority levels, @Intercept, InterceptorHandler lifecycle |
|
|
21
|
-
| Pipes & resolvers | [pipes.md](pipes.md) | Building pipes, @Pipe, @Resolve, validation/transform stages, argument resolution |
|
|
22
|
-
|
|
23
|
-
## Quick reference
|
|
24
|
-
|
|
25
|
-
```ts
|
|
26
|
-
import { Moost, Controller, Param, Intercept, Injectable, Resolve } from 'moost'
|
|
27
|
-
import { defineMoostEventHandler, getMoostMate, createProvideRegistry } from 'moost'
|
|
28
|
-
import type { TMoostAdapter, TMoostAdapterOptions } from 'moost'
|
|
29
|
-
|
|
30
|
-
const app = new Moost()
|
|
31
|
-
app.adapter(myAdapter) // attach event adapter
|
|
32
|
-
app.registerControllers(MyCtrl) // register controllers
|
|
33
|
-
await app.init() // bind all handlers, call adapter.onInit()
|
|
34
|
-
```
|
package/skills/moost/core.md
DELETED
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
# Core Concepts & Setup — moost
|
|
2
|
-
|
|
3
|
-
> Moost application lifecycle, controller registration, adapter attachment, and the mental model for event-driven TypeScript applications.
|
|
4
|
-
|
|
5
|
-
## Concepts
|
|
6
|
-
|
|
7
|
-
Moost is a **metadata-driven event processing framework**. The core mental model:
|
|
8
|
-
|
|
9
|
-
1. **Controllers** — Classes decorated with `@Controller()` that contain handler methods
|
|
10
|
-
2. **Adapters** — Implementations of `TMoostAdapter<H>` that bridge external event sources (HTTP, CLI, WS, etc.) to Moost's handler system
|
|
11
|
-
3. **Handlers** — Methods on controllers decorated with adapter-specific decorators (e.g., `@Get()`, `@Cli()`, `@Message()`) that process events
|
|
12
|
-
4. **Pipes** — Transform/resolve/validate method arguments before handler execution
|
|
13
|
-
5. **Interceptors** — Cross-cutting logic (auth, logging, error handling) wrapping handler execution
|
|
14
|
-
|
|
15
|
-
Unlike NestJS, Moost has **no module abstraction** — controllers are registered directly on the Moost instance.
|
|
16
|
-
|
|
17
|
-
### Lifecycle
|
|
18
|
-
|
|
19
|
-
```
|
|
20
|
-
app.adapter(adapter) // 1. Attach adapters
|
|
21
|
-
app.registerControllers(Ctrl) // 2. Register controller classes
|
|
22
|
-
await app.init() // 3. Bind all handlers + call adapter.onInit()
|
|
23
|
-
adapter.listen(3000) // 4. Start listening (adapter-specific)
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
During `init()`:
|
|
27
|
-
1. Adapter provide registries are merged into DI
|
|
28
|
-
2. All controller methods are scanned for handler metadata
|
|
29
|
-
3. For each handler, `adapter.bindHandler()` is called with full context
|
|
30
|
-
4. Each adapter's `onInit(moost)` hook fires last
|
|
31
|
-
|
|
32
|
-
## Installation
|
|
33
|
-
|
|
34
|
-
```bash
|
|
35
|
-
npm install moost
|
|
36
|
-
# or
|
|
37
|
-
pnpm add moost
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
## API Reference
|
|
41
|
-
|
|
42
|
-
### `Moost` (class)
|
|
43
|
-
|
|
44
|
-
The main application class. Can be extended or used directly.
|
|
45
|
-
|
|
46
|
-
```ts
|
|
47
|
-
import { Moost } from 'moost'
|
|
48
|
-
|
|
49
|
-
const app = new Moost()
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
#### `app.adapter<T>(a: T): T`
|
|
53
|
-
|
|
54
|
-
Attaches an event adapter. Returns the adapter for chaining.
|
|
55
|
-
|
|
56
|
-
```ts
|
|
57
|
-
const http = app.adapter(new MoostHttp())
|
|
58
|
-
http.listen(3000)
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
#### `app.registerControllers(...controllers)`
|
|
62
|
-
|
|
63
|
-
Registers one or more controller classes or instances.
|
|
64
|
-
|
|
65
|
-
```ts
|
|
66
|
-
app.registerControllers(UserController, OrderController)
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
#### `app.init(): Promise<void>`
|
|
70
|
-
|
|
71
|
-
Initializes the application: binds all controllers to all adapters, then calls each adapter's `onInit()`.
|
|
72
|
-
|
|
73
|
-
#### `app.interceptor(...interceptors)`
|
|
74
|
-
|
|
75
|
-
Registers global interceptors that apply to all handlers.
|
|
76
|
-
|
|
77
|
-
```ts
|
|
78
|
-
app.interceptor(myLoggingInterceptor)
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
#### `app.getLogger(topic?: string)`
|
|
82
|
-
|
|
83
|
-
Returns a logger instance, optionally scoped to a topic.
|
|
84
|
-
|
|
85
|
-
#### `app.getGlobalInterceptorHandler()`
|
|
86
|
-
|
|
87
|
-
Returns an `InterceptorHandler` for global interceptors. Used by adapters for 404/not-found handlers.
|
|
88
|
-
|
|
89
|
-
### `@Controller(prefix?: string)`
|
|
90
|
-
|
|
91
|
-
Class decorator marking a class as a controller. The optional prefix is prepended to all handler paths.
|
|
92
|
-
|
|
93
|
-
```ts
|
|
94
|
-
@Controller('users')
|
|
95
|
-
class UserController {
|
|
96
|
-
@Get(':id') // resolves to /users/:id
|
|
97
|
-
getUser(@Param('id') id: string) { return { id } }
|
|
98
|
-
}
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
### `@ImportController(ctrl, opts?)`
|
|
102
|
-
|
|
103
|
-
Registers a nested controller within another controller, inheriting its prefix.
|
|
104
|
-
|
|
105
|
-
```ts
|
|
106
|
-
@Controller('api')
|
|
107
|
-
@ImportController(UserController)
|
|
108
|
-
@ImportController(OrderController)
|
|
109
|
-
class ApiController {}
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
## Common Patterns
|
|
113
|
-
|
|
114
|
-
### Pattern: Extending Moost
|
|
115
|
-
|
|
116
|
-
Create an application class extending Moost. Handler methods can live directly on it.
|
|
117
|
-
|
|
118
|
-
```ts
|
|
119
|
-
class MyApp extends Moost {
|
|
120
|
-
@Get('health')
|
|
121
|
-
health() { return { ok: true } }
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const app = new MyApp()
|
|
125
|
-
const http = app.adapter(new MoostHttp())
|
|
126
|
-
http.listen(3000)
|
|
127
|
-
app.init()
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
### Pattern: Multi-adapter application
|
|
131
|
-
|
|
132
|
-
Attach multiple adapters to handle different event types simultaneously.
|
|
133
|
-
|
|
134
|
-
```ts
|
|
135
|
-
const app = new Moost()
|
|
136
|
-
const http = app.adapter(new MoostHttp())
|
|
137
|
-
const ws = app.adapter(new MoostWs({ httpApp: http.getHttpApp() }))
|
|
138
|
-
const cli = app.adapter(new MoostCli())
|
|
139
|
-
|
|
140
|
-
app.registerControllers(HttpController, WsController, CliController)
|
|
141
|
-
await app.init()
|
|
142
|
-
http.listen(3000)
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
Each adapter only processes handlers matching its own type (e.g., HTTP adapter ignores `@Cli()` handlers).
|
|
146
|
-
|
|
147
|
-
### Pattern: Controller-only registration
|
|
148
|
-
|
|
149
|
-
Controllers are not tied to a specific adapter. The same controller class can contain handlers for multiple adapters.
|
|
150
|
-
|
|
151
|
-
```ts
|
|
152
|
-
@Controller('app')
|
|
153
|
-
class AppController {
|
|
154
|
-
@Get('status') // handled by HTTP adapter
|
|
155
|
-
httpStatus() { return { up: true } }
|
|
156
|
-
|
|
157
|
-
@Cli('status') // handled by CLI adapter
|
|
158
|
-
cliStatus() { console.log('up') }
|
|
159
|
-
}
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
## Best Practices
|
|
163
|
-
|
|
164
|
-
- Call `app.init()` **after** registering all controllers and adapters
|
|
165
|
-
- Start listening (`adapter.listen()`) before or after `init()` — the adapter buffers requests until handlers are bound
|
|
166
|
-
- Use `@Controller(prefix)` to namespace related handlers
|
|
167
|
-
- Keep controllers focused — one per feature area
|
|
168
|
-
- Extend `Moost` for simple apps; use `registerControllers()` for larger projects
|
|
169
|
-
|
|
170
|
-
## Gotchas
|
|
171
|
-
|
|
172
|
-
- `app.init()` must be `await`ed — it's async because singleton controllers may need async DI resolution
|
|
173
|
-
- Handler methods are bound to **all** attached adapters, but each adapter filters by handler `type` — a `@Get()` decorator is ignored by the CLI adapter
|
|
174
|
-
- The order of `adapter()` calls doesn't matter for functionality but affects log output order
|