create-nwire 0.10.1 → 0.11.1
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 +5 -5
- package/package.json +1 -1
- package/templates/enterprise/app/app.ts +11 -15
- package/templates/enterprise/app/main.ts +2 -2
- package/templates/enterprise/modules/posts/routes/reject-post.ts +1 -4
- package/templates/enterprise/package.json +7 -7
- package/templates/minimal/app/api.ts +1 -3
- package/templates/minimal/app/main.ts +3 -6
- package/templates/minimal/package.json +4 -4
- package/templates/service/README.md +1 -1
- package/templates/service/app/main.ts +9 -11
- package/templates/service/package.json +5 -5
package/README.md
CHANGED
|
@@ -15,11 +15,11 @@ yarn create nwire my-app --template service
|
|
|
15
15
|
|
|
16
16
|
## Templates
|
|
17
17
|
|
|
18
|
-
| Name | What you get
|
|
19
|
-
| ------------ |
|
|
20
|
-
| `minimal` | One POST route. `createApp` + `httpKoa`. No DI, no forge. ~40 LOC.
|
|
21
|
-
| `service` | Todo CRUD. Container plugin, structured errors, route middleware, in-memory store.
|
|
22
|
-
| `enterprise` | Moderation queue. Forge: actions + events + actors + stateful workflow + projection, multi-app composable.
|
|
18
|
+
| Name | What you get |
|
|
19
|
+
| ------------ | ---------------------------------------------------------------------------------------------------------- |
|
|
20
|
+
| `minimal` | One POST route. `createApp` + `httpKoa`. No DI, no forge. ~40 LOC. |
|
|
21
|
+
| `service` | Todo CRUD. Container plugin, structured errors, route middleware, in-memory store. |
|
|
22
|
+
| `enterprise` | Moderation queue. Forge: actions + events + actors + stateful workflow + projection, multi-app composable. |
|
|
23
23
|
|
|
24
24
|
Pick the smallest template that fits — graduating up is just re-scaffolding
|
|
25
25
|
into a new folder and porting your code one file at a time.
|
package/package.json
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `buildApp` composes the posts bounded context into a runnable App.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* registers the handlers via the `handlers` array.
|
|
4
|
+
* Each piece (events, actions, workflows, projections, queries) is
|
|
5
|
+
* imported directly; `.with(createForgePlugin(...))` installs forge with
|
|
6
|
+
* the handlers, workflows, projections, and queries the BC needs.
|
|
8
7
|
*
|
|
9
8
|
* For multi-BC apps, build each as its own App and compose:
|
|
10
9
|
*
|
|
@@ -28,17 +27,14 @@ import {
|
|
|
28
27
|
import { postsByAuthor } from "../modules/posts/queries/posts-by-author";
|
|
29
28
|
|
|
30
29
|
export function buildApp() {
|
|
31
|
-
return createApp({
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
],
|
|
40
|
-
handlers: [submitPost.handler!, approvePost.handler!, rejectPost.handler!],
|
|
41
|
-
});
|
|
30
|
+
return createApp({ appName: "{{PROJECT_NAME}}" }).with(
|
|
31
|
+
createForgePlugin({
|
|
32
|
+
handlers: [submitPost.handler!, approvePost.handler!, rejectPost.handler!],
|
|
33
|
+
workflows: [autoModerate],
|
|
34
|
+
projections: [queueDashboard],
|
|
35
|
+
queries: [listPending, queueTotals, getPost, postsByAuthor],
|
|
36
|
+
}),
|
|
37
|
+
);
|
|
42
38
|
}
|
|
43
39
|
|
|
44
40
|
export const app = buildApp();
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Entry — boot the app under HTTP.
|
|
3
3
|
*
|
|
4
|
-
* 1. buildApp
|
|
5
|
-
* queries)
|
|
4
|
+
* 1. buildApp composes the App with forge (handlers, workflows,
|
|
5
|
+
* projections, queries) via `.with(createForgePlugin(...))`.
|
|
6
6
|
* 2. app.wire(...) pairs every route binding with its handler.
|
|
7
7
|
* 3. endpoint().use(httpKoa({prefix:"/api"})).mount(app).run() starts
|
|
8
8
|
* the HTTP listener + graceful drain + K8s probes.
|
|
@@ -19,10 +19,7 @@ type Input = z.infer<typeof Params> & z.infer<typeof Body>;
|
|
|
19
19
|
|
|
20
20
|
export const rejectPostRoute = post("/posts/:postId/reject", { params: Params, body: Body });
|
|
21
21
|
|
|
22
|
-
export const rejectPostHandler = async (
|
|
23
|
-
input: Input,
|
|
24
|
-
ctx: { resolve: <T>(name: string) => T },
|
|
25
|
-
) => {
|
|
22
|
+
export const rejectPostHandler = async (input: Input, ctx: { resolve: <T>(name: string) => T }) => {
|
|
26
23
|
const dispatcher = ctx.resolve<ForgeDispatcher>("forge.dispatcher");
|
|
27
24
|
await dispatcher.dispatch(rejectPost, {
|
|
28
25
|
postId: input.postId,
|
|
@@ -8,16 +8,16 @@
|
|
|
8
8
|
"test": "vitest run"
|
|
9
9
|
},
|
|
10
10
|
"dependencies": {
|
|
11
|
-
"@nwire/app": "^0.
|
|
12
|
-
"@nwire/endpoint": "^0.
|
|
13
|
-
"@nwire/forge": "^0.
|
|
14
|
-
"@nwire/
|
|
15
|
-
"@nwire/
|
|
16
|
-
"@nwire/
|
|
11
|
+
"@nwire/app": "^0.11.1",
|
|
12
|
+
"@nwire/endpoint": "^0.11.1",
|
|
13
|
+
"@nwire/forge": "^0.11.1",
|
|
14
|
+
"@nwire/koa": "^0.11.1",
|
|
15
|
+
"@nwire/messages": "^0.11.1",
|
|
16
|
+
"@nwire/wires": "^0.11.1",
|
|
17
17
|
"zod": "^4.0.0"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
|
-
"@nwire/test-kit": "^0.
|
|
20
|
+
"@nwire/test-kit": "^0.11.1",
|
|
21
21
|
"@types/node": "^22.19.9",
|
|
22
22
|
"typescript": "^5.9.0",
|
|
23
23
|
"vite-node": "^3.2.4",
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* createApp(...) — the bounded context (container + wires)
|
|
5
5
|
* app.wire(...) — pair a binding with its handler
|
|
6
|
-
* endpoint().use(...) — install a transport
|
|
7
|
-
* .mount(app).run() — boot the app under the
|
|
6
|
+
* endpoint().use(...) — install a transport adapter (HTTP, queue, MCP, …)
|
|
7
|
+
* .mount(app).run() — boot the app under the adapters; ready for traffic
|
|
8
8
|
*
|
|
9
9
|
* The minimal shape: no DI, no forge, no domain primitives. Two packages
|
|
10
10
|
* and three files. Graduate to `service` when you need a container, store,
|
|
@@ -31,8 +31,5 @@ export function buildApp() {
|
|
|
31
31
|
|
|
32
32
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
33
33
|
const app = buildApp();
|
|
34
|
-
await endpoint("{{PROJECT_NAME}}", { port: 3000 })
|
|
35
|
-
.use(httpKoa())
|
|
36
|
-
.mount(app)
|
|
37
|
-
.run();
|
|
34
|
+
await endpoint("{{PROJECT_NAME}}", { port: 3000 }).use(httpKoa()).mount(app).run();
|
|
38
35
|
}
|
|
@@ -8,10 +8,10 @@
|
|
|
8
8
|
"test": "vitest run"
|
|
9
9
|
},
|
|
10
10
|
"dependencies": {
|
|
11
|
-
"@nwire/app": "^0.
|
|
12
|
-
"@nwire/endpoint": "^0.
|
|
13
|
-
"@nwire/
|
|
14
|
-
"@nwire/
|
|
11
|
+
"@nwire/app": "^0.11.1",
|
|
12
|
+
"@nwire/endpoint": "^0.11.1",
|
|
13
|
+
"@nwire/koa": "^0.11.1",
|
|
14
|
+
"@nwire/wires": "^0.11.1",
|
|
15
15
|
"zod": "^4.0.0"
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
@@ -63,7 +63,7 @@ curl -X POST http://localhost:3000/api/todos/<id>/complete \
|
|
|
63
63
|
| A new error type | `app/errors/<resource>-errors.ts` — `defineError({ code, status, summary })` |
|
|
64
64
|
| A new middleware | `app/middleware/<name>.ts` exports `HttpMiddleware`; `.use()` it from `api.ts` |
|
|
65
65
|
| A new resource | `app/resources/<name>.ts` — `defineResource` schema + `public` field list |
|
|
66
|
-
| Real persistence | Swap `app/store/todo-store.ts` for `@nwire/drizzle` or `@nwire/mongo`
|
|
66
|
+
| Real persistence | Swap `app/store/todo-store.ts` for `@nwire/drizzle` or `@nwire/mongo` |
|
|
67
67
|
|
|
68
68
|
## Grow up
|
|
69
69
|
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Entry — boot the app under HTTP.
|
|
3
3
|
*
|
|
4
|
-
* 1. createApp
|
|
4
|
+
* 1. createApp constructs the bounded context; `.with(plugin)` installs
|
|
5
|
+
* the TodoStore on the container.
|
|
5
6
|
* 2. app.wire(...) pairs every route binding with its handler.
|
|
6
|
-
* 3. endpoint().use(httpKoa({middleware:[requireUser]})).mount(app).run()
|
|
7
|
-
* runs the HTTP
|
|
7
|
+
* 3. endpoint().use(httpKoa({ middleware: [requireUser] })).mount(app).run()
|
|
8
|
+
* runs the HTTP adapter under graceful drain + K8s probes.
|
|
8
9
|
*
|
|
9
10
|
* Run: pnpm dev
|
|
10
11
|
* Try: curl -X POST http://localhost:3000/api/todos \
|
|
@@ -22,15 +23,12 @@ import { wires } from "./api";
|
|
|
22
23
|
import { requireUser } from "./middleware/require-user";
|
|
23
24
|
import { TodoStore } from "./store/todo-store";
|
|
24
25
|
|
|
26
|
+
const todoStorePlugin = definePlugin("todo-store", ({ bind }) => {
|
|
27
|
+
bind("todos", new TodoStore());
|
|
28
|
+
});
|
|
29
|
+
|
|
25
30
|
export function buildApp() {
|
|
26
|
-
const app = createApp({
|
|
27
|
-
appName: "{{PROJECT_NAME}}",
|
|
28
|
-
plugins: [
|
|
29
|
-
definePlugin("todo-store", ({ bind }) => {
|
|
30
|
-
bind("todos", new TodoStore());
|
|
31
|
-
}),
|
|
32
|
-
],
|
|
33
|
-
});
|
|
31
|
+
const app = createApp({ appName: "{{PROJECT_NAME}}" }).with(todoStorePlugin);
|
|
34
32
|
for (const { binding, handler } of wires) {
|
|
35
33
|
app.wire(binding, handler);
|
|
36
34
|
}
|
|
@@ -8,11 +8,11 @@
|
|
|
8
8
|
"test": "vitest run"
|
|
9
9
|
},
|
|
10
10
|
"dependencies": {
|
|
11
|
-
"@nwire/app": "^0.
|
|
12
|
-
"@nwire/endpoint": "^0.
|
|
13
|
-
"@nwire/handler": "^0.
|
|
14
|
-
"@nwire/
|
|
15
|
-
"@nwire/
|
|
11
|
+
"@nwire/app": "^0.11.1",
|
|
12
|
+
"@nwire/endpoint": "^0.11.1",
|
|
13
|
+
"@nwire/handler": "^0.11.1",
|
|
14
|
+
"@nwire/koa": "^0.11.1",
|
|
15
|
+
"@nwire/wires": "^0.11.1",
|
|
16
16
|
"koa": "^2.16.1",
|
|
17
17
|
"zod": "^4.0.0"
|
|
18
18
|
},
|