moost 0.6.6 → 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/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/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
|