create-nwire 0.10.0 → 0.11.0

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
@@ -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,6 +1,6 @@
1
1
  {
2
2
  "name": "create-nwire",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "Scaffolder for new Nwire projects. Run `pnpm create nwire <name>` or `npm create nwire <name>` to bootstrap.",
5
5
  "keywords": [
6
6
  "nwire",
@@ -1,10 +1,9 @@
1
1
  /**
2
2
  * `buildApp` composes the posts bounded context into a runnable App.
3
3
  *
4
- * 0.10 shape — no modules. Each piece (events, actions, workflows,
5
- * projections, queries) is imported directly; createApp installs a
6
- * forge plugin with the workflows/projections/queries it needs, and
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
- appName: "{{PROJECT_NAME}}",
33
- plugins: [
34
- createForgePlugin({
35
- workflows: [autoModerate],
36
- projections: [queueDashboard],
37
- queries: [listPending, queueTotals, getPost, postsByAuthor],
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 registers handlers + forge plugin (workflows, projections,
5
- * queries) on the container.
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.10.0",
12
- "@nwire/endpoint": "^0.10.0",
13
- "@nwire/forge": "^0.10.0",
14
- "@nwire/wires": "^0.10.0",
15
- "@nwire/koa": "^0.10.0",
16
- "@nwire/messages": "^0.10.0",
11
+ "@nwire/app": "^0.11.0",
12
+ "@nwire/endpoint": "^0.11.0",
13
+ "@nwire/forge": "^0.11.0",
14
+ "@nwire/koa": "^0.11.0",
15
+ "@nwire/messages": "^0.11.0",
16
+ "@nwire/wires": "^0.11.0",
17
17
  "zod": "^4.0.0"
18
18
  },
19
19
  "devDependencies": {
20
- "@nwire/test-kit": "^0.10.0",
20
+ "@nwire/test-kit": "^0.11.0",
21
21
  "@types/node": "^22.19.9",
22
22
  "typescript": "^5.9.0",
23
23
  "vite-node": "^3.2.4",
@@ -15,6 +15,4 @@
15
15
 
16
16
  import { helloRoute, helloHandler } from "./routes/hello";
17
17
 
18
- export const wires = [
19
- { binding: helloRoute, handler: helloHandler },
20
- ] as const;
18
+ export const wires = [{ binding: helloRoute, handler: helloHandler }] as const;
@@ -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 adopter (HTTP, queue, MCP, …)
7
- * .mount(app).run() — boot the app under the adopters; ready for traffic
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.10.0",
12
- "@nwire/endpoint": "^0.10.0",
13
- "@nwire/wires": "^0.10.0",
14
- "@nwire/koa": "^0.10.0",
11
+ "@nwire/app": "^0.11.0",
12
+ "@nwire/endpoint": "^0.11.0",
13
+ "@nwire/koa": "^0.11.0",
14
+ "@nwire/wires": "^0.11.0",
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 registers the TodoStore on the container via a plugin.
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 adopter under graceful drain + K8s probes.
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
  }
@@ -123,7 +123,7 @@ export const todoStorePlugin = definePlugin("todos", ({ provide }) => {
123
123
  },
124
124
  healthCheck: (store) => {
125
125
  // Trivial — proves the store is alive. A real adapter would
126
- // SELECT 1 against the DB. Lightship calls this on every /readyz.
126
+ // SELECT 1 against the DB. Lightship calls this on every /ready.
127
127
  if (store.size() < 0) throw new Error("store corrupt");
128
128
  },
129
129
  });
@@ -8,11 +8,11 @@
8
8
  "test": "vitest run"
9
9
  },
10
10
  "dependencies": {
11
- "@nwire/app": "^0.10.0",
12
- "@nwire/endpoint": "^0.10.0",
13
- "@nwire/handler": "^0.10.0",
14
- "@nwire/wires": "^0.10.0",
15
- "@nwire/koa": "^0.10.0",
11
+ "@nwire/app": "^0.11.0",
12
+ "@nwire/endpoint": "^0.11.0",
13
+ "@nwire/handler": "^0.11.0",
14
+ "@nwire/koa": "^0.11.0",
15
+ "@nwire/wires": "^0.11.0",
16
16
  "koa": "^2.16.1",
17
17
  "zod": "^4.0.0"
18
18
  },