create-nwire 0.13.0 → 0.13.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/package.json +1 -1
- package/templates/enterprise/README.md +15 -10
- package/templates/enterprise/app/api.ts +3 -1
- package/templates/enterprise/app/main.ts +3 -3
- package/templates/enterprise/modules/posts/projections/queue-dashboard.ts +7 -2
- package/templates/enterprise/package.json +8 -8
- package/templates/mcp/_pnpm-workspace.yaml +8 -2
- package/templates/mcp/package.json +4 -4
- package/templates/minimal/AGENTS.md +11 -4
- package/templates/minimal/README.md +20 -10
- package/templates/minimal/app/main.ts +6 -3
- package/templates/minimal/package.json +5 -5
- package/templates/service/AGENTS.md +1 -1
- package/templates/service/README.md +5 -3
- package/templates/service/app/main.ts +3 -3
- package/templates/service/package.json +6 -6
package/package.json
CHANGED
|
@@ -16,19 +16,24 @@ pnpm dev
|
|
|
16
16
|
|
|
17
17
|
## Try
|
|
18
18
|
|
|
19
|
+
`pnpm dev` serves the wire + Studio on http://localhost:4000.
|
|
20
|
+
|
|
19
21
|
```bash
|
|
20
22
|
# submit a post (becomes pending, auto-moderation triggers)
|
|
21
|
-
curl -X POST http://localhost:
|
|
23
|
+
curl -X POST http://localhost:4000/api/posts \
|
|
22
24
|
-H "content-type: application/json" \
|
|
23
25
|
-d '{"authorId":"alice","body":"Hello, world!"}'
|
|
24
26
|
|
|
25
|
-
# read the dashboard projection
|
|
26
|
-
curl http://localhost:
|
|
27
|
+
# read the dashboard projection — copy a pending postId from here
|
|
28
|
+
curl http://localhost:4000/api/queue
|
|
27
29
|
|
|
28
|
-
# decide
|
|
29
|
-
curl -X POST http://localhost:
|
|
30
|
+
# decide one manually (paste the postId from /api/queue above)
|
|
31
|
+
curl -X POST http://localhost:4000/api/posts/<postId>/approve \
|
|
30
32
|
-H "content-type: application/json" \
|
|
31
33
|
-d '{"moderatorId":"miri"}'
|
|
34
|
+
|
|
35
|
+
# all posts by an author, across statuses
|
|
36
|
+
curl "http://localhost:4000/api/posts/by-author?authorId=alice"
|
|
32
37
|
```
|
|
33
38
|
|
|
34
39
|
## Test
|
|
@@ -42,8 +47,8 @@ pnpm test
|
|
|
42
47
|
```
|
|
43
48
|
{{PROJECT_NAME}}/
|
|
44
49
|
├── app/
|
|
45
|
-
│ ├── main.ts ← endpoint
|
|
46
|
-
│ ├── api.ts ←
|
|
50
|
+
│ ├── main.ts ← endpoint().use(httpKoa()).mount(app).run()
|
|
51
|
+
│ ├── api.ts ← wires[] — every route + handler pair
|
|
47
52
|
│ └── app.ts ← createApp({ modules: [...] })
|
|
48
53
|
├── modules/
|
|
49
54
|
│ └── posts/ ← bounded context: posts moderation
|
|
@@ -90,11 +95,11 @@ actor (`defineActor`) versus a direct DB write inside a handler.
|
|
|
90
95
|
|
|
91
96
|
## Studio
|
|
92
97
|
|
|
93
|
-
`
|
|
94
|
-
Studio
|
|
98
|
+
`pnpm dev` already serves Studio alongside the wire on http://localhost:4000.
|
|
99
|
+
For the standalone Studio process (multi-project shell, process manager):
|
|
95
100
|
|
|
96
101
|
```bash
|
|
97
|
-
|
|
102
|
+
pnpm studio
|
|
98
103
|
```
|
|
99
104
|
|
|
100
105
|
You'll get live traces, action / event / workflow browsers, projection
|
|
@@ -18,6 +18,8 @@ export const wires = [
|
|
|
18
18
|
{ binding: approvePostRoute, handler: approvePostHandler },
|
|
19
19
|
{ binding: rejectPostRoute, handler: rejectPostHandler },
|
|
20
20
|
{ binding: listQueueRoute, handler: listQueueHandler },
|
|
21
|
-
|
|
21
|
+
// Literal `/posts/by-author` must be wired before the `/posts/:postId`
|
|
22
|
+
// param route — otherwise `:postId` captures "by-author" and shadows it.
|
|
22
23
|
{ binding: postsByAuthorRoute, handler: postsByAuthorHandler },
|
|
24
|
+
{ binding: getPostRoute, handler: getPostHandler },
|
|
23
25
|
] as const;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Entry — the only file that boots a real HTTP server.
|
|
3
3
|
*
|
|
4
|
-
* pnpm dev
|
|
4
|
+
* pnpm dev # wire + Studio on http://localhost:4000
|
|
5
5
|
*
|
|
6
|
-
* curl -X POST http://localhost:
|
|
6
|
+
* curl -X POST http://localhost:4000/api/posts \
|
|
7
7
|
* -H "content-type: application/json" \
|
|
8
8
|
* -d '{"authorId":"alice","body":"Hello, world!"}'
|
|
9
|
-
* curl http://localhost:
|
|
9
|
+
* curl http://localhost:4000/api/queue
|
|
10
10
|
*
|
|
11
11
|
* `app` is a pure value — routes are wired in `./app` on import.
|
|
12
12
|
* This file adds the one side effect: binding a port and serving traffic.
|
|
@@ -74,7 +74,10 @@ export const queueDashboard = defineProjection<QueueState>(
|
|
|
74
74
|
|
|
75
75
|
when(PostWasApproved, (state, event) => {
|
|
76
76
|
const item = state.byId[event.postId];
|
|
77
|
-
|
|
77
|
+
// Only a pending post transitions. Re-applying a decision (a duplicate
|
|
78
|
+
// event, or an approve after a reject) must be a no-op — otherwise the
|
|
79
|
+
// pending counter is decremented twice and the totals drift negative.
|
|
80
|
+
if (!item || item.status !== "pending") return state;
|
|
78
81
|
return {
|
|
79
82
|
byId: {
|
|
80
83
|
...state.byId,
|
|
@@ -96,7 +99,9 @@ export const queueDashboard = defineProjection<QueueState>(
|
|
|
96
99
|
|
|
97
100
|
when(PostWasRejected, (state, event) => {
|
|
98
101
|
const item = state.byId[event.postId];
|
|
99
|
-
|
|
102
|
+
// Same idempotency guard as approve: a post that already left the queue
|
|
103
|
+
// must not decrement `pending` a second time.
|
|
104
|
+
if (!item || item.status !== "pending") return state;
|
|
100
105
|
return {
|
|
101
106
|
byId: {
|
|
102
107
|
...state.byId,
|
|
@@ -11,17 +11,17 @@
|
|
|
11
11
|
"cache": "nwire cache"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@nwire/app": "^0.13.
|
|
15
|
-
"@nwire/endpoint": "^0.13.
|
|
16
|
-
"@nwire/forge": "^0.13.
|
|
17
|
-
"@nwire/koa": "^0.13.
|
|
18
|
-
"@nwire/messages": "^0.13.
|
|
19
|
-
"@nwire/wires": "^0.13.
|
|
14
|
+
"@nwire/app": "^0.13.1",
|
|
15
|
+
"@nwire/endpoint": "^0.13.1",
|
|
16
|
+
"@nwire/forge": "^0.13.1",
|
|
17
|
+
"@nwire/koa": "^0.13.1",
|
|
18
|
+
"@nwire/messages": "^0.13.1",
|
|
19
|
+
"@nwire/wires": "^0.13.1",
|
|
20
20
|
"zod": "^4.0.0"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@nwire/cli": "^0.13.
|
|
24
|
-
"@nwire/test-kit": "^0.13.
|
|
23
|
+
"@nwire/cli": "^0.13.1",
|
|
24
|
+
"@nwire/test-kit": "^0.13.1",
|
|
25
25
|
"@types/node": "^22.19.9",
|
|
26
26
|
"typescript": "^5.9.0",
|
|
27
27
|
"vitest": "^4.0.18"
|
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
# pnpm
|
|
2
|
-
#
|
|
1
|
+
# pnpm reads build-allow + run settings here (not the package.json "pnpm"
|
|
2
|
+
# field). This server runs TypeScript via tsx, which pulls esbuild — a
|
|
3
|
+
# dependency with a build script. pnpm gates build scripts: without an
|
|
4
|
+
# explicit decision it writes an `allowBuilds` stub and exits non-zero on
|
|
5
|
+
# the very first `pnpm install`. Pre-approve esbuild here so install is
|
|
6
|
+
# clean out of the box. `onlyBuiltDependencies` covers older pnpm; skip the
|
|
3
7
|
# pre-run deps check so `pnpm dev` runs clean right after install.
|
|
8
|
+
allowBuilds:
|
|
9
|
+
esbuild: true
|
|
4
10
|
onlyBuiltDependencies:
|
|
5
11
|
- esbuild
|
|
6
12
|
verifyDepsBeforeRun: false
|
|
@@ -10,10 +10,10 @@
|
|
|
10
10
|
"typecheck": "tsc --noEmit"
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@nwire/app": "^0.13.
|
|
14
|
-
"@nwire/endpoint": "^0.13.
|
|
15
|
-
"@nwire/mcp": "^0.13.
|
|
16
|
-
"@nwire/wires": "^0.13.
|
|
13
|
+
"@nwire/app": "^0.13.1",
|
|
14
|
+
"@nwire/endpoint": "^0.13.1",
|
|
15
|
+
"@nwire/mcp": "^0.13.1",
|
|
16
|
+
"@nwire/wires": "^0.13.1",
|
|
17
17
|
"zod": "^4.0.0"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
@@ -9,12 +9,19 @@ handler never knows which transport called it.
|
|
|
9
9
|
```ts
|
|
10
10
|
import { createApp } from "@nwire/app";
|
|
11
11
|
import { endpoint } from "@nwire/endpoint";
|
|
12
|
-
import {
|
|
12
|
+
import { post } from "@nwire/wires/http";
|
|
13
13
|
import { httpKoa } from "@nwire/koa";
|
|
14
|
+
import { z } from "zod";
|
|
14
15
|
|
|
15
16
|
const app = createApp({ appName: "app" });
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
// The route declares its schema; the handler receives the validated input.
|
|
18
|
+
app.wire(post("/hello", { body: z.object({ name: z.string().min(1) }) }), async (input) => ({
|
|
19
|
+
message: `Hello, ${input.name}!`,
|
|
20
|
+
}));
|
|
21
|
+
await endpoint("app", { port: Number(process.env.PORT) || 3000 })
|
|
22
|
+
.use(httpKoa())
|
|
23
|
+
.mount(app)
|
|
24
|
+
.run();
|
|
18
25
|
```
|
|
19
26
|
|
|
20
27
|
## What's installed (don't import beyond these)
|
|
@@ -33,5 +40,5 @@ This tier is HTTP-only. Add capabilities by installing the package first:
|
|
|
33
40
|
|
|
34
41
|
## Commands
|
|
35
42
|
|
|
36
|
-
- `pnpm dev` —
|
|
43
|
+
- `pnpm dev` — wire + Studio on http://localhost:4000. `pnpm test` — tests.
|
|
37
44
|
- `pnpm doctor` — health-check the setup. `pnpm studio` — trace console.
|
|
@@ -16,8 +16,10 @@ pnpm dev
|
|
|
16
16
|
|
|
17
17
|
## Try
|
|
18
18
|
|
|
19
|
+
`pnpm dev` serves the wire + Studio on http://localhost:4000.
|
|
20
|
+
|
|
19
21
|
```bash
|
|
20
|
-
curl -X POST http://localhost:
|
|
22
|
+
curl -X POST http://localhost:4000/hello \
|
|
21
23
|
-H "content-type: application/json" \
|
|
22
24
|
-d '{"name":"Alice"}'
|
|
23
25
|
```
|
|
@@ -33,8 +35,9 @@ pnpm test
|
|
|
33
35
|
```
|
|
34
36
|
{{PROJECT_NAME}}/
|
|
35
37
|
├── app/
|
|
36
|
-
│ ├── main.ts ← endpoint().
|
|
37
|
-
│ ├──
|
|
38
|
+
│ ├── main.ts ← endpoint().use(httpKoa()).mount(app).run()
|
|
39
|
+
│ ├── app.ts ← createApp().wire(...) — the app value
|
|
40
|
+
│ ├── api.ts ← wires[] (route + handler pairs)
|
|
38
41
|
│ └── routes/
|
|
39
42
|
│ └── hello.ts ← POST /hello — route + handler pair
|
|
40
43
|
└── __tests__/
|
|
@@ -51,13 +54,20 @@ pnpm test
|
|
|
51
54
|
|
|
52
55
|
## What you get free
|
|
53
56
|
|
|
54
|
-
| Feature
|
|
55
|
-
|
|
|
56
|
-
| Zod request validation
|
|
57
|
-
|
|
|
58
|
-
|
|
|
59
|
-
|
|
60
|
-
|
|
57
|
+
| Feature | Comes from |
|
|
58
|
+
| ------------------------------------ | ----------------- |
|
|
59
|
+
| Zod request validation | `@nwire/koa` |
|
|
60
|
+
| Graceful SIGTERM drain | `@nwire/endpoint` |
|
|
61
|
+
| K8s probes (port 9400/`$PROBE_PORT`) | `@nwire/endpoint` |
|
|
62
|
+
|
|
63
|
+
OpenAPI + Scalar docs are one opt-in away — pass them to the transport:
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
endpoint("{{PROJECT_NAME}}")
|
|
67
|
+
.use(httpKoa({ openapi: { auto: true }, docs: true })) // /openapi.json + /docs
|
|
68
|
+
.mount(app)
|
|
69
|
+
.run();
|
|
70
|
+
```
|
|
61
71
|
|
|
62
72
|
## Grow up
|
|
63
73
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Entry — the only file that boots a real HTTP server.
|
|
3
3
|
*
|
|
4
|
-
* pnpm dev
|
|
4
|
+
* pnpm dev # wire + Studio on http://localhost:4000
|
|
5
5
|
*
|
|
6
|
-
* curl -X POST http://localhost:
|
|
6
|
+
* curl -X POST http://localhost:4000/hello \
|
|
7
7
|
* -H "content-type: application/json" \
|
|
8
8
|
* -d '{"name":"Alice"}'
|
|
9
9
|
*
|
|
@@ -18,4 +18,7 @@ import { endpoint } from "@nwire/endpoint";
|
|
|
18
18
|
import { httpKoa } from "@nwire/koa";
|
|
19
19
|
import { app } from "./app";
|
|
20
20
|
|
|
21
|
-
await endpoint("{{PROJECT_NAME}}", { port: 3000 })
|
|
21
|
+
await endpoint("{{PROJECT_NAME}}", { port: Number(process.env.PORT) || 3000 })
|
|
22
|
+
.use(httpKoa())
|
|
23
|
+
.mount(app)
|
|
24
|
+
.run();
|
|
@@ -11,14 +11,14 @@
|
|
|
11
11
|
"cache": "nwire cache"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@nwire/app": "^0.13.
|
|
15
|
-
"@nwire/endpoint": "^0.13.
|
|
16
|
-
"@nwire/koa": "^0.13.
|
|
17
|
-
"@nwire/wires": "^0.13.
|
|
14
|
+
"@nwire/app": "^0.13.1",
|
|
15
|
+
"@nwire/endpoint": "^0.13.1",
|
|
16
|
+
"@nwire/koa": "^0.13.1",
|
|
17
|
+
"@nwire/wires": "^0.13.1",
|
|
18
18
|
"zod": "^4.0.0"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
|
-
"@nwire/cli": "^0.13.
|
|
21
|
+
"@nwire/cli": "^0.13.1",
|
|
22
22
|
"@types/node": "^22.19.9",
|
|
23
23
|
"typescript": "^5.9.0",
|
|
24
24
|
"vitest": "^4.0.18"
|
|
@@ -58,7 +58,7 @@ listener file (not `on` — `on` is for lifecycle hooks).
|
|
|
58
58
|
|
|
59
59
|
## Commands
|
|
60
60
|
|
|
61
|
-
- `pnpm dev` —
|
|
61
|
+
- `pnpm dev` — wire + Studio on http://localhost:4000.
|
|
62
62
|
- `pnpm test` — run the test suite.
|
|
63
63
|
- `pnpm doctor` — health-check the project setup.
|
|
64
64
|
- `pnpm studio` — open the live trace/inspect console.
|
|
@@ -16,18 +16,20 @@ pnpm dev
|
|
|
16
16
|
|
|
17
17
|
## Try
|
|
18
18
|
|
|
19
|
+
`pnpm dev` serves the wire + Studio on http://localhost:4000.
|
|
20
|
+
|
|
19
21
|
```bash
|
|
20
22
|
# create a todo
|
|
21
|
-
curl -X POST http://localhost:
|
|
23
|
+
curl -X POST http://localhost:4000/api/todos \
|
|
22
24
|
-H "content-type: application/json" \
|
|
23
25
|
-H "x-user-id: alice" \
|
|
24
26
|
-d '{"text":"buy milk"}'
|
|
25
27
|
|
|
26
28
|
# list this user's todos
|
|
27
|
-
curl -H "x-user-id: alice" http://localhost:
|
|
29
|
+
curl -H "x-user-id: alice" http://localhost:4000/api/todos
|
|
28
30
|
|
|
29
31
|
# complete one
|
|
30
|
-
curl -X POST http://localhost:
|
|
32
|
+
curl -X POST http://localhost:4000/api/todos/<id>/complete \
|
|
31
33
|
-H "x-user-id: alice"
|
|
32
34
|
```
|
|
33
35
|
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Entry — the only file that boots a real HTTP server.
|
|
3
3
|
*
|
|
4
|
-
* pnpm dev
|
|
4
|
+
* pnpm dev # wire + Studio on http://localhost:4000
|
|
5
5
|
*
|
|
6
|
-
* curl -X POST http://localhost:
|
|
6
|
+
* curl -X POST http://localhost:4000/api/todos \
|
|
7
7
|
* -H "content-type: application/json" \
|
|
8
8
|
* -H "x-user-id: alice" \
|
|
9
9
|
* -d '{"text":"buy milk"}'
|
|
10
|
-
* curl -H "x-user-id: alice" http://localhost:
|
|
10
|
+
* curl -H "x-user-id: alice" http://localhost:4000/api/todos
|
|
11
11
|
*
|
|
12
12
|
* `app` is a pure value — routes are wired in `./app` on import.
|
|
13
13
|
* This file adds the one side effect: binding a port and serving traffic.
|
|
@@ -11,16 +11,16 @@
|
|
|
11
11
|
"cache": "nwire cache"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@nwire/app": "^0.13.
|
|
15
|
-
"@nwire/endpoint": "^0.13.
|
|
16
|
-
"@nwire/handler": "^0.13.
|
|
17
|
-
"@nwire/koa": "^0.13.
|
|
18
|
-
"@nwire/wires": "^0.13.
|
|
14
|
+
"@nwire/app": "^0.13.1",
|
|
15
|
+
"@nwire/endpoint": "^0.13.1",
|
|
16
|
+
"@nwire/handler": "^0.13.1",
|
|
17
|
+
"@nwire/koa": "^0.13.1",
|
|
18
|
+
"@nwire/wires": "^0.13.1",
|
|
19
19
|
"koa": "^2.16.1",
|
|
20
20
|
"zod": "^4.0.0"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@nwire/cli": "^0.13.
|
|
23
|
+
"@nwire/cli": "^0.13.1",
|
|
24
24
|
"@types/koa": "^2.15.0",
|
|
25
25
|
"@types/node": "^22.19.9",
|
|
26
26
|
"typescript": "^5.9.0",
|