@adhd/apigen-cli 0.1.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/CHANGELOG.md ADDED
@@ -0,0 +1,89 @@
1
+ # Changelog — `@adhd/apigen-*`
2
+
3
+ All notable changes to the apigen toolchain (`@adhd/apigen-cli`, `-core`, `-runtime`,
4
+ `-logical`, plugins, and generators). Format based on
5
+ [Keep a Changelog](https://keepachangelog.com/); this project uses
6
+ [Semantic Versioning](https://semver.org/).
7
+
8
+ Open/actionable work lives in [`/BACKLOG.md`](../../BACKLOG.md); plan-level design in
9
+ [`docs/plan/apigen-logical-types/DESIGN.md`](../../docs/plan/apigen-logical-types/DESIGN.md).
10
+
11
+ ---
12
+
13
+ ## [Unreleased] — apigen-logical-types (PENDING)
14
+
15
+ > Cross-language serialization of non-JSON-native types and custom classes. Tracked by the
16
+ > `apigen-logical-types` plan-state-machine plan (DESIGN.md §1–§18). **Not yet released** —
17
+ > gated on the plan reaching its Definition of Done + publishing the apigen v2 packages.
18
+
19
+ ### Added
20
+ - **Rich types now round-trip through `run`/`generate` with no new flags.** Functions whose
21
+ signatures use **`Date`, `int64`/`BigInt`, `Decimal`, `bytes`, `UUID`, `Map`, `Set`,
22
+ tuples**, or **user-defined classes** are exposed and (de)serialized correctly across
23
+ MCP / HTTP / CLI. Previously these silently broke (e.g. `Date` extracted as `{}`, inputs
24
+ never rehydrated). *(dod.1/dod.2)*
25
+ - **Custom-class round-trips, including cross-host.** A `User` class survives
26
+ `TS → wire → Python → wire → TS` and is reconstructed as a **real instance on both hosts**
27
+ (not a prototype-stripped object). Discriminated unions (`Dog | Cat`) dispatch by wire
28
+ discriminator. *(dod.3/dod.4)*
29
+ - **Meaningful MCP/OpenAPI schemas.** `tools/list` and the OpenAPI emitter now carry proper
30
+ `format` (`date-time`, `int64`, `uuid`, `byte`, …) and named `$ref` for classes instead of
31
+ empty `{}` — so MCP clients (and the driving model) and generated OpenAPI clients in any
32
+ language get real, validatable types.
33
+ - **Idiomatic generated clients.** Generated TS clients emit `Date`, Python clients emit
34
+ `datetime` (via `x-apigen-*` codegen hints) — consumers get typed values, not raw ISO
35
+ strings to hand-parse.
36
+ - **Cross-host interop for rich types.** Expose a TS function, call it from a Python client
37
+ (or vice versa), with Date/Decimal/classes preserved — guaranteed **byte-equal** by a
38
+ conformance-vector matrix (a TCK every host must pass). *(dod.5)*
39
+ - **New developer tooling:** an `apigen-nx` **`host` generator** to scaffold a new host
40
+ language (a template column + conformance harness), and a workspace
41
+ **`nx run …:conformance`** target that verifies the cross-host matrix.
42
+ - **New package `@adhd/apigen-logical`** — the single source of truth: canonical wire table +
43
+ per-language native-hook template columns + generate-time emitter + run-mode closure builder.
44
+ - **Optional knob `--opt logical.decimal=branded|lib`** — zero-dependency branded-string mode
45
+ vs a real `Decimal` runtime type.
46
+
47
+ ### Changed
48
+ - `generate` emits a **standalone-runnable** `package.json`: rich-type deps (e.g. `decimal.js`)
49
+ are added per-surface (only the types actually used), so generated output runs after a plain
50
+ `npm install` — `--link-workspace` no longer required for the publish path.
51
+ *(dod.10 — resolves BUG-APIGEN-002 dep-manifest slice)*
52
+ - Validation actually enforces formats — the validate-Layer wires `ajv-formats`, so a malformed
53
+ `date-time` (`2099-02-30`) is rejected instead of silently passing. *(dod.6)*
54
+
55
+ ### Fixed
56
+ - **BUG-APIGEN-005** — language-specific serializable types (`Date → {}`, lost precision,
57
+ un-rehydrated inputs) and custom-class identity loss. *(this feature)*
58
+ - **BUG-APIGEN-004** — `run`/`generate` now **fail fast** with an actionable message on 0
59
+ extracted functions and on a used rich type whose optional dependency is missing
60
+ (e.g. *"function `quote` takes a Decimal; install `decimal.js`"*), instead of a cryptic
61
+ `ERR_MODULE_NOT_FOUND`. *(dod.9)*
62
+
63
+ ### Engineering notes
64
+ - Codegen-first design: encode uses each language's **native hook**
65
+ (`toJSON` / `JSONEncoder.default` / serde / Jackson / `MarshalJSON`); decode is schema-driven
66
+ glue emitted once by the generator — **no per-host runtime interpreter**.
67
+ - Languages with native-hook mappings resolved: **TypeScript/JS, Python, Rust, Go, Java**.
68
+ 3rd-party deps introduced: Python 0, Java 1, TS 1, Go 2, Rust ~6 (all pinned, minimal-per-surface).
69
+
70
+ ---
71
+
72
+ ## [1.0.0] — apigen v2 (released to disk, publish pending)
73
+
74
+ ### Added
75
+ - apigen v2 — an 18-package TypeScript→API toolchain. Takes any unmodified `.ts` file and
76
+ exposes its exports as an **MCP server, HTTP API, or CLI** without source changes
77
+ (`apigen-cli run/generate`). Canonical Operation descriptor (JSON-Schema-2020-12 IR),
78
+ symbol/class extractor (ts-morph), v2 plugin interface, Layer harness + streaming + single
79
+ canonical dispatch path, `mcp`/`api-fastify`/`api-express`/`cli` plugins, `apigen-nx`
80
+ generator, and a real Python host passing the conformance vectors.
81
+ Plan: `docs/plan/apigen-client-generation` (46/46 states, final audit 117/117, DoD confirmed).
82
+
83
+ ### Fixed
84
+ - **BUG-APIGEN-001** — ctx-param functions returned wrong results via generated servers
85
+ (`hasCtx` now threaded through extraction→dispatch).
86
+ - **BUG-APIGEN-002** — generated output portability via the Option-A publish model + default-off
87
+ `--link-workspace` (dep-manifest extension for rich types tracked in Unreleased above).
88
+ - **BUG-APIGEN-003** — MCP SSE transport reachable (stdio + streaming-http + sse); probe
89
+ hardened (`waitForPort` 15s→60s for cold compiles).
package/README.md ADDED
@@ -0,0 +1,146 @@
1
+ # @adhd/apigen-cli
2
+
3
+ The user-facing CLI for **apigen** — take any `.ts` file and expose its exports as an
4
+ **MCP server, HTTP API (Fastify/Express), CLI, or JSON Schema**, with **zero changes to
5
+ the source**. Functions are the single source of truth; everything is derived.
6
+
7
+ > This documents the **v1** (TypeScript-only) implementation that ships today. The
8
+ > transport-neutral, polyglot **v2** standard is specified in
9
+ > [`docs/apigen/SPEC.md`](../../../docs/apigen/SPEC.md) and is in active build-out.
10
+
11
+ ---
12
+
13
+ ## Install / launch
14
+
15
+ The CLI is not bundled to `dist` by default. Use whichever fits:
16
+
17
+ ```bash
18
+ cd <repo-root>
19
+
20
+ # A) Dev — run the TypeScript entry directly (no build):
21
+ alias apigen='npx tsx packages/apigen/cli/src/index.ts'
22
+
23
+ # B) Bundled bin — build once, then run dist (bin name: apigen-cli):
24
+ npx nx build apigen-cli
25
+ # → node packages/apigen/cli/dist/index.js
26
+ ```
27
+
28
+ **Commands:** `run`, `generate`, `run-registry`, `generate-registry`
29
+ **`--type` (target plugin):** `mcp` · `api-fastify` · `api-express` · `cli` · `jsonschema`
30
+ **Server knobs:** passed through repeatable `--opt key=value`.
31
+
32
+ A ready fixture lives at `packages/apigen/cli/src/test/fixtures/api.ts` (`getUser`, `sendEmail`),
33
+ used in the examples below.
34
+
35
+ ---
36
+
37
+ ## Run a live server — `run`
38
+
39
+ ```bash
40
+ # MCP over stdio (default transport)
41
+ apigen run --source packages/apigen/cli/src/test/fixtures/api.ts --type mcp
42
+
43
+ # MCP over HTTP transports
44
+ apigen run --source .../api.ts --type mcp --opt transport=sse --opt port=3000
45
+ apigen run --source .../api.ts --type mcp --opt transport=streaming-http --opt port=3000
46
+
47
+ # HTTP API (Fastify or Express) — --namespace sets the route prefix segment
48
+ apigen run --source packages/apigen/cli/src/test/fixtures/api.ts \
49
+ --type api-fastify --namespace api --opt port=3000
50
+ ```
51
+
52
+ Call an HTTP server — **method must be uppercase `POST`**, arguments wrapped in
53
+ `{"data":{…}}` (the request envelope), route is `/<namespace>/<fn>`:
54
+
55
+ ```bash
56
+ curl -X POST http://127.0.0.1:3000/api/getUser \
57
+ -H 'content-type: application/json' \
58
+ -d '{"data":{"userId":"abc"}}'
59
+ # → {"id":"abc"}
60
+ ```
61
+
62
+ ### `--opt` keys read by the server plugins
63
+
64
+ | key | targets | default | meaning |
65
+ |---|---|---|---|
66
+ | `transport` | `mcp` | `stdio` | `stdio` \| `sse` \| `streaming-http` |
67
+ | `port` | `mcp` (http transports), `api-fastify`, `api-express` | `3000` | listen port |
68
+ | `host` | `mcp` (http), `api-fastify`, `api-express` | `127.0.0.1` | bind host |
69
+ | `routePrefix` | `api-fastify`, `api-express` | `""` | path prefix before `/<namespace>/<fn>` |
70
+
71
+ `mcp` over `stdio` keeps **stdout** clean for the JSON-RPC channel — all logs go to stderr.
72
+
73
+ ---
74
+
75
+ ## Generate to disk — `generate`
76
+
77
+ ```bash
78
+ # MCP server source
79
+ apigen generate --source packages/apigen/cli/src/test/fixtures/api.ts \
80
+ --type mcp --out-dir /tmp/apigen-out
81
+ node /tmp/apigen-out/server.ts # or: npx tsx /tmp/apigen-out/server.ts
82
+
83
+ apigen generate --source .../api.ts --type api-fastify --out-dir /tmp/out-http # routes.ts
84
+ apigen generate --source .../api.ts --type cli --out-dir /tmp/out-cli # cli.ts
85
+ apigen generate --source .../api.ts --type jsonschema --out-dir /tmp/out-schema # *.json
86
+ ```
87
+
88
+ Generated servers import `dispatch`/`buildFnTable` from `@adhd/apigen-runtime` — no inlined
89
+ dispatch logic — so disk output behaves identically to `run`.
90
+
91
+ ---
92
+
93
+ ## Multi-package — `run-registry` / `generate-registry`
94
+
95
+ Discover packages in a directory by **nx tag** and wire them into one surface:
96
+
97
+ ```bash
98
+ apigen run-registry --packages-dir packages/apigen/cli/src/test/fixtures/registry \
99
+ --tag api --type mcp
100
+ apigen generate-registry --packages-dir <dir> --tag api --type mcp --out-dir /tmp/reg-out
101
+ ```
102
+
103
+ `--tag` / `--exclude-tag` are repeatable; untagged exports stay out of the surface.
104
+
105
+ ---
106
+
107
+ ## Flags
108
+
109
+ ### Export shapes — `--export`
110
+ - *(omit)* — named exports (`export function f`, `export const f = …`).
111
+ - `--export default` — a default-exported function/object.
112
+ - `--export <objName>` — a named object whose properties are the operations.
113
+
114
+ > **Known v1 limitations (tracked, fixed by v2):** `export { x as y }` aliases,
115
+ > anonymous default exports, and CJS-source files mis-name routes because v1 names by the
116
+ > *declaration* identifier rather than the *exported symbol*. v2 (`docs/apigen/SPEC.md`)
117
+ > names by exported symbol and closes these.
118
+
119
+ ### Resolution
120
+ - `--tsconfig <path>` — explicit tsconfig; otherwise the nearest one, else a builtin default.
121
+ - `--namespace <name>` — the package id / route prefix segment. Defaults to the tsconfig
122
+ folder name, falling back to the source file's folder.
123
+
124
+ ### Plugin options
125
+ - `--opt key=value` — repeatable; forwarded to the target plugin (see table above).
126
+
127
+ ### Logging (stderr — stdout stays protocol-clean)
128
+ - `--log-level trace|debug|info|warn|error|fatal|silent` (env `APIGEN_LOG_LEVEL`)
129
+ - `--log-format json|pretty` (env `APIGEN_LOG_FORMAT`)
130
+ - `--log-file <path>` — write logs to a file instead of stderr (env `APIGEN_LOG_FILE`)
131
+
132
+ Lifecycle logged: compile → server start → host/port → route/tool list → per-request → shutdown.
133
+
134
+ ---
135
+
136
+ ## Nx integration
137
+
138
+ - Cache-aware generate target: `@adhd/apigen-nx:generate` (see [`../nx`](../nx)).
139
+ - Scaffold a new output plugin: `nx g @adhd/apigen-nx:plugin <name>`.
140
+
141
+ ## Develop
142
+
143
+ ```bash
144
+ npx nx build apigen-cli # bundle
145
+ npx nx test apigen-cli # Vitest (unit + integration)
146
+ ```
@@ -0,0 +1,10 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "esnext",
5
+ "moduleResolution": "bundler",
6
+ "esModuleInterop": true,
7
+ "strict": true,
8
+ "skipLibCheck": true
9
+ }
10
+ }
package/index.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {};