moost 0.6.6 → 0.6.8

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/README.md CHANGED
@@ -1,7 +1,5 @@
1
1
  # moost
2
2
 
3
- **!!! This is work-in-progress library, breaking changes are expected !!!**
4
-
5
3
  <p align="center">
6
4
  <img src="../../moost-logo.png" width="450px"><br>
7
5
  <a href="https://github.com/moostjs/moostjs/blob/main/LICENSE">
@@ -9,9 +7,7 @@
9
7
  </a>
10
8
  </p>
11
9
 
12
- Welcome to Moostjs, a metadata-driven Event Processing Framework inspired by [NestJS](https://nestjs.com/) and powered by [Wooks](https://wooks.moost.org). While Moost is currently a work-in-progress library, we're excited about its potential and invite you to explore, experiment, and contribute to its development.
13
-
14
- **Note:** As Moostjs is under active development, breaking changes can be expected.
10
+ Welcome to Moostjs, a metadata-driven Event Processing Framework inspired by [NestJS](https://nestjs.com/) and powered by [Wooks](https://wooks.moost.org).
15
11
 
16
12
  ## Motivation
17
13
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "moost",
3
- "version": "0.6.6",
3
+ "version": "0.6.8",
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.8",
44
+ "@wooksjs/event-core": "^0.7.10",
50
45
  "hookable": "^5.5.3",
51
- "wooks": "^0.7.8"
46
+ "wooks": "^0.7.10"
52
47
  },
53
48
  "devDependencies": {
54
- "@wooksjs/event-http": "^0.7.8",
55
- "@wooksjs/http-body": "^0.7.8",
49
+ "@wooksjs/event-http": "^0.7.10",
50
+ "@wooksjs/http-body": "^0.7.10",
56
51
  "vitest": "3.2.4",
57
- "@moostjs/event-http": "^0.6.6"
52
+ "@moostjs/event-http": "^0.6.8"
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
  }
@@ -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
- }
@@ -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
- ```
@@ -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