Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Default owners for this repository.
# GitHub: openmirai/mirai-openapi-codegen
# npm: @openmirai/openapi-codegen
# npm: @openmirai/typeforge
* @openmirai/platform-foundations @openmirai/openmirai-engineer
4 changes: 2 additions & 2 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ jobs:
- name: Publish package
run: |
set -euo pipefail
if npm view "@openmirai/openapi-codegen@${RELEASE_VERSION}" version >/dev/null 2>&1; then
echo "@openmirai/openapi-codegen@${RELEASE_VERSION} is already published"
if npm view "@openmirai/typeforge@${RELEASE_VERSION}" version >/dev/null 2>&1; then
echo "@openmirai/typeforge@${RELEASE_VERSION} is already published"
else
npm publish --access public
fi
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
dist/
node_modules/
coverage/
.typeforge/
.openapi-codegen/
test/fixtures/layouts/
*.tsbuildinfo
.tmp/
typeforge.local.json
openapi-codegen.local.json
.env
54 changes: 31 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
# @openmirai/openapi-codegen
<p align="center">
<img src="./assets/typeforge-logo.png" alt="Typeforge logo" width="280" />
</p>

Headless **OpenAPI / Swagger → TypeScript** codegen. The CLI is `openapi-codegen`. It reads a spec, writes typed route enums, request types, and HTTP caller functions, and never talks to a network.
# @openmirai/typeforge

- **npm:** [`@openmirai/openapi-codegen`](https://www.npmjs.com/package/@openmirai/openapi-codegen)
Headless **OpenAPI / Swagger → TypeScript** codegen. The CLI is `typeforge`. It reads a spec, writes typed route enums, request types, and HTTP caller functions, and never talks to a network.

- **npm:** [`@openmirai/typeforge`](https://www.npmjs.com/package/@openmirai/typeforge)
- **GitHub:** [openmirai/mirai-openapi-codegen](https://github.com/openmirai/mirai-openapi-codegen)

You own `http.ts` (the `HTTPFetch` adapter). Generated files import that adapter — they do not invent axios/fetch calls inline.

## Migrating from `@openmirai/openapi-codegen`

Install `@openmirai/typeforge` and update package imports and scripts to use the canonical `typeforge` name. During migration, the package also exposes the legacy `openapi-codegen` binary and reads `openapi-codegen.json`, `openapi-codegen.local.json`, and the `openapiCodegen` package.json key. New projects created by `typeforge init` use the Typeforge names.

## What it generates

For each **source** (a named API, e.g. `atlas`), under `<apiRoot>/<source>/generated/`:
Expand All @@ -22,18 +30,18 @@ For each **source** (a named API, e.g. `atlas`), under `<apiRoot>/<source>/gener
Optional:

- **TanStack Query** — set `tanstackQuery: true` in `source.ts` **and** add `<apiRoot>/query-scope.ts`.
- **Zod** — wrap a schema with `createZodValidator` from `@openmirai/openapi-codegen/validation/zod` and pass it as `config.validateResponse`.
- **Zod** — wrap a schema with `createZodValidator` from `@openmirai/typeforge/validation/zod` and pass it as `config.validateResponse`.

## Install

Requires **Node.js 20.11+** (LTS). Use any package manager.

| Package manager | Install |
| --- | --- |
| npm | `npm install --save-dev @openmirai/openapi-codegen` |
| pnpm | `pnpm add -D @openmirai/openapi-codegen` |
| yarn | `yarn add -D @openmirai/openapi-codegen` |
| bun | `bun add -d @openmirai/openapi-codegen` |
| npm | `npm install --save-dev @openmirai/typeforge` |
| pnpm | `pnpm add -D @openmirai/typeforge` |
| yarn | `yarn add -D @openmirai/typeforge` |
| bun | `bun add -d @openmirai/typeforge` |

Axios is an **optional peer**. Install `axios` only if you use `--client axios`.

Expand All @@ -42,7 +50,7 @@ Add a script so every package manager resolves the CLI from `node_modules/.bin`:
```json
{
"scripts": {
"generate:types": "openapi-codegen generate --all"
"generate:types": "typeforge generate --all"
}
}
```
Expand All @@ -55,10 +63,10 @@ Prefer the `package.json` script above. To invoke the binary directly:

| Command | npm | pnpm | yarn | bun |
| --- | --- | --- | --- | --- |
| Init a source | `npx openapi-codegen init --source atlas --client axios` | `pnpm exec openapi-codegen init --source atlas --client axios` | `yarn openapi-codegen init --source atlas --client axios` | `bunx openapi-codegen init --source atlas --client axios` |
| Generate one source | `npx openapi-codegen generate --source atlas` | `pnpm exec openapi-codegen generate --source atlas` | `yarn openapi-codegen generate --source atlas` | `bunx openapi-codegen generate --source atlas` |
| Generate all sources | `npx openapi-codegen generate --all` | `pnpm exec openapi-codegen generate --all` | `yarn openapi-codegen generate --all` | `bunx openapi-codegen generate --all` |
| Drift check (CI) | `npx openapi-codegen generate --all --check` | `pnpm exec openapi-codegen generate --all --check` | `yarn openapi-codegen generate --all --check` | `bunx openapi-codegen generate --all --check` |
| Init a source | `npx typeforge init --source atlas --client axios` | `pnpm exec typeforge init --source atlas --client axios` | `yarn typeforge init --source atlas --client axios` | `bunx typeforge init --source atlas --client axios` |
| Generate one source | `npx typeforge generate --source atlas` | `pnpm exec typeforge generate --source atlas` | `yarn typeforge generate --source atlas` | `bunx typeforge generate --source atlas` |
| Generate all sources | `npx typeforge generate --all` | `pnpm exec typeforge generate --all` | `yarn typeforge generate --all` | `bunx typeforge generate --all` |
| Drift check (CI) | `npx typeforge generate --all --check` | `pnpm exec typeforge generate --all --check` | `yarn typeforge generate --all --check` | `bunx typeforge generate --all --check` |

| Subcommand | Purpose |
| --- | --- |
Expand All @@ -78,15 +86,15 @@ init → source.ts + http.ts → resolve spec → generate → typed cal
### 1. Init a source

```bash
openapi-codegen init --source atlas --client axios
openapi-codegen init --source orbit --client fetch --layout packages
typeforge init --source atlas --client axios
typeforge init --source orbit --client fetch --layout packages
```

`--client` is `axios` | `fetch` | `custom`. `--layout` is `monolith` (default, `apiRoot` = `src/api`) or `packages` (`apiRoot` = `packages/utils/src/api`).

Init creates (if missing):

- `openapi-codegen.json` with `apiRoot`
- `typeforge.json` with `apiRoot`
- `<apiRoot>/http.ts` — your `HTTPFetch` implementation
- `<apiRoot>/known-types.ts` — optional schema → local type mapping
- `<apiRoot>/<source>/source.ts` — per-API config (type-safe template)
Expand All @@ -99,7 +107,7 @@ Existing files are skipped.
Use `defineSourceConfig` for autocomplete and compile-time checks:

```ts
import { defineSourceConfig } from "@openmirai/openapi-codegen";
import { defineSourceConfig } from "@openmirai/typeforge";

export default defineSourceConfig({
spec: "./specs/acme.json",
Expand Down Expand Up @@ -156,7 +164,7 @@ Re-exported types from the package root:
1. `--spec <path>`
2. Env `OPENAPI_SPEC_<KEY>` — source key uppercased, hyphens → underscores
3. `spec` in that source’s `source.ts`
4. `openapi-codegen.local.json` (gitignored) map of `{ "<source>": "<path>" }`
4. `typeforge.local.json` (gitignored) map of `{ "<source>": "<path>" }`
5. Committed snapshot `<apiRoot>/<source>/spec.json`

### 4. Envelope modes
Expand All @@ -181,7 +189,7 @@ Axios and Fetch adapters.

### 5. HTTPFetch (`http.ts`)

Adapters implement `HTTPFetch` from `@openmirai/openapi-codegen/http` (or the axios/fetch adapter packages). Methods return `Promise<{ data: TResponse }>`.
Adapters implement `HTTPFetch` from `@openmirai/typeforge/http` (or the axios/fetch adapter packages). Methods return `Promise<{ data: TResponse }>`.

- If `http.ts` **exports `httpFetch`**, generated functions call that singleton.
- Otherwise they take `props.http: HTTPFetch` (injected).
Expand All @@ -198,13 +206,13 @@ types import the generated base declaration (`base.ts` in monolith output, or

## Where files go

`openapi-codegen.json`:
`typeforge.json`:

```json
{ "apiRoot": "packages/utils/src/api" }
```

You can also set `"openapiCodegen": { "apiRoot": "..." }` in `package.json`. The JSON file wins.
You can also set `"typeforge": { "apiRoot": "..." }` in `package.json`. The JSON file wins.

**Monolith** (`--layout monolith`, default):

Expand All @@ -228,7 +236,7 @@ aliases automatically.
## Zod (optional)

```ts
import { createZodValidator } from "@openmirai/openapi-codegen/validation/zod";
import { createZodValidator } from "@openmirai/typeforge/validation/zod";
import { widgetListSchema } from "./widget-list";

await getWidgets({
Expand All @@ -243,7 +251,7 @@ Publishes go through [npm Trusted Publishing](https://docs.npmjs.com/trusted-pub

| | Value |
| --- | --- |
| npm package | `@openmirai/openapi-codegen` |
| npm package | `@openmirai/typeforge` |
| GitHub repo | `openmirai/mirai-openapi-codegen` |
| Workflow | `.github/workflows/publish.yml` |
| Tag | `v*` (e.g. `v0.1.3`) |
Expand Down
Binary file added assets/typeforge-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
32 changes: 17 additions & 15 deletions docs/cli.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
# CLI reference

Install `@openmirai/openapi-codegen` from [npmjs](https://www.npmjs.com/package/@openmirai/openapi-codegen). Add a script so the binary resolves from `node_modules/.bin`:
Install `@openmirai/typeforge` from [npmjs](https://www.npmjs.com/package/@openmirai/typeforge). Add a script so the binary resolves from `node_modules/.bin`:

The legacy `openapi-codegen` binary and configuration filenames remain readable during migration, but all new usage should use `typeforge`.

```json
{
"scripts": {
"generate:types": "openapi-codegen generate --all"
"generate:types": "typeforge generate --all"
}
}
```
Expand All @@ -14,21 +16,21 @@ Install `@openmirai/openapi-codegen` from [npmjs](https://www.npmjs.com/package/

| Package manager | Command |
| --- | --- |
| npm | `npm install --save-dev @openmirai/openapi-codegen` |
| pnpm | `pnpm add -D @openmirai/openapi-codegen` |
| yarn | `yarn add -D @openmirai/openapi-codegen` |
| bun | `bun add -d @openmirai/openapi-codegen` |
| npm | `npm install --save-dev @openmirai/typeforge` |
| pnpm | `pnpm add -D @openmirai/typeforge` |
| yarn | `yarn add -D @openmirai/typeforge` |
| bun | `bun add -d @openmirai/typeforge` |

## Commands × package managers

Replace `<args>` with the flags for that subcommand (see below).

| Subcommand | npm | pnpm | yarn | bun |
| --- | --- | --- | --- | --- |
| `init <args>` | `npx openapi-codegen init <args>` | `pnpm exec openapi-codegen init <args>` | `yarn openapi-codegen init <args>` | `bunx openapi-codegen init <args>` |
| `generate <args>` | `npx openapi-codegen generate <args>` | `pnpm exec openapi-codegen generate <args>` | `yarn openapi-codegen generate <args>` | `bunx openapi-codegen generate <args>` |
| `check <args>` | `npx openapi-codegen check <args>` | `pnpm exec openapi-codegen check <args>` | `yarn openapi-codegen check <args>` | `bunx openapi-codegen check <args>` |
| `accept-base <args>` | `npx openapi-codegen accept-base <args>` | `pnpm exec openapi-codegen accept-base <args>` | `yarn openapi-codegen accept-base <args>` | `bunx openapi-codegen accept-base <args>` |
| `init <args>` | `npx typeforge init <args>` | `pnpm exec typeforge init <args>` | `yarn typeforge init <args>` | `bunx typeforge init <args>` |
| `generate <args>` | `npx typeforge generate <args>` | `pnpm exec typeforge generate <args>` | `yarn typeforge generate <args>` | `bunx typeforge generate <args>` |
| `check <args>` | `npx typeforge check <args>` | `pnpm exec typeforge check <args>` | `yarn typeforge check <args>` | `bunx typeforge check <args>` |
| `accept-base <args>` | `npx typeforge accept-base <args>` | `pnpm exec typeforge accept-base <args>` | `yarn typeforge accept-base <args>` | `bunx typeforge accept-base <args>` |

Recommended day-to-day: `npm run generate:types` (or the equivalent for your package manager).

Expand All @@ -37,16 +39,16 @@ Recommended day-to-day: `npm run generate:types` (or the equivalent for your pac
### `init`

```bash
openapi-codegen init --source <key> --client axios|fetch|custom [--layout monolith|packages]
typeforge init --source <key> --client axios|fetch|custom [--layout monolith|packages]
```

Creates `http.ts`, `known-types.ts`, `source.ts`, and `generated/` under `apiRoot`.

### `generate`

```bash
openapi-codegen generate --source <key> [--source <key2> ...] [--spec <path>] [--check] [--accept-base]
openapi-codegen generate --all [--check] [--accept-base]
typeforge generate --source <key> [--source <key2> ...] [--spec <path>] [--check] [--accept-base]
typeforge generate --all [--check] [--accept-base]
```

`--all` walks every directory under `apiRoot` that contains `source.ts`.
Expand Down Expand Up @@ -78,13 +80,13 @@ Updates `generated/base.ts` and patches `models.ts` `BaseResponse` to match the
1. `--spec <path>`
2. Environment variable `OPENAPI_SPEC_<KEY>` (key uppercased, `-` → `_`)
3. `spec` field in `<apiRoot>/<key>/source.ts`
4. `openapi-codegen.local.json` (gitignored)
4. `typeforge.local.json` (gitignored)
5. `<apiRoot>/<key>/spec.json` snapshot

## Type-safe `source.ts`

```ts
import { defineSourceConfig } from "@openmirai/openapi-codegen";
import { defineSourceConfig } from "@openmirai/typeforge";

export default defineSourceConfig({
spec: "./specs/acme.json",
Expand Down
2 changes: 1 addition & 1 deletion docs/envelope.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,4 @@ recognized without changing their source schemas.

## accept-base

`openapi-codegen accept-base --source atlas` regenerates `generated/base.ts` and rewrites `BaseResponse` in `models.ts` to match the spec. Use it when the envelope shape in the spec is the source of truth and `models.ts` is stale. Do not combine with `--check`.
`typeforge accept-base --source atlas` regenerates `generated/base.ts` and rewrites `BaseResponse` in `models.ts` to match the spec. Use it when the envelope shape in the spec is the source of truth and `models.ts` is stale. Do not combine with `--check`.
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@openmirai/openapi-codegen",
"name": "@openmirai/typeforge",
"version": "0.1.7",
"description": "Headless OpenAPI to TypeScript codegen CLI and HTTPFetch runtime",
"description": "Typeforge: headless OpenAPI to TypeScript codegen CLI and HTTPFetch runtime",
"homepage": "https://github.com/openmirai/mirai-openapi-codegen#readme",
"bugs": {
"url": "https://github.com/openmirai/mirai-openapi-codegen/issues"
Expand All @@ -12,9 +12,11 @@
"url": "git+https://github.com/openmirai/mirai-openapi-codegen.git"
},
"bin": {
"openapi-codegen": "./dist/cli.js"
"openapi-codegen": "./dist/cli.js",
"typeforge": "./dist/cli.js"
},
"files": [
"assets/typeforge-logo.png",
"dist"
],
"type": "module",
Expand Down
34 changes: 14 additions & 20 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,27 +117,27 @@ export function parseArgs(argv: Array<string>): ParsedArgs {
}

function printHelp(): void {
process.stdout.write(`openapi-codegen — headless OpenAPI TypeScript codegen
process.stdout.write(`typeforge — headless OpenAPI TypeScript codegen

Usage:
openapi-codegen init --source <key> --client axios|fetch|custom [--layout monolith|packages]
openapi-codegen generate --source <key> [--source <key2> ...] [--spec <path>] [--check] [--accept-base]
openapi-codegen generate --all [--check] [--accept-base]
openapi-codegen check --source <key> [--spec <path>]
openapi-codegen accept-base --source <key> [--spec <path>]
typeforge init --source <key> --client axios|fetch|custom [--layout monolith|packages]
typeforge generate --source <key> [--source <key2> ...] [--spec <path>] [--check] [--accept-base]
typeforge generate --all [--check] [--accept-base]
typeforge check --source <key> [--spec <path>]
typeforge accept-base --source <key> [--spec <path>]

Multi-source generate:
Provide multiple --source flags, or use --all to generate every source under apiRoot.
Configure per-source spec paths in each source.ts:
import { defineSourceConfig } from "@openmirai/openapi-codegen";
import { defineSourceConfig } from "@openmirai/typeforge";
export default defineSourceConfig({ spec: "./specs/acme.json", ... });
`);
}

async function runGenerate(args: ParsedArgs): Promise<number> {
if (args.check === true && args.acceptBase === true) {
process.stderr.write(
"openapi-codegen: --accept-base is not allowed with --check\n"
"typeforge: --accept-base is not allowed with --check\n"
);
return 1;
}
Expand All @@ -155,9 +155,7 @@ async function runGenerate(args: ParsedArgs): Promise<number> {
}

if (sources.length === 0) {
process.stderr.write(
"openapi-codegen: --source <key> or --all is required\n"
);
process.stderr.write("typeforge: --source <key> or --all is required\n");
return 1;
}

Expand All @@ -180,20 +178,18 @@ async function runGenerate(args: ParsedArgs): Promise<number> {
if (args.check === true) {
if (result.changed.length > 0) {
process.stderr.write(
`openapi-codegen: stale generated files for "${sourceKey}":\n`
`typeforge: stale generated files for "${sourceKey}":\n`
);
for (const file of result.changed) {
process.stderr.write(` ${file}\n`);
}
exitCode = 1;
} else {
process.stdout.write(
`openapi-codegen: "${sourceKey}" is up to date\n`
);
process.stdout.write(`typeforge: "${sourceKey}" is up to date\n`);
}
} else {
process.stdout.write(
`openapi-codegen: generated ${result.files} files for "${sourceKey}" (${result.changed.length} changed)\n`
`typeforge: generated ${result.files} files for "${sourceKey}" (${result.changed.length} changed)\n`
);
}
} catch (error) {
Expand Down Expand Up @@ -221,9 +217,7 @@ async function main(): Promise<void> {

if (args.command === "init") {
if (args.source === undefined || args.client === undefined) {
process.stderr.write(
"openapi-codegen: init requires --source and --client\n"
);
process.stderr.write("typeforge: init requires --source and --client\n");
process.exit(1);
}

Expand Down Expand Up @@ -260,7 +254,7 @@ async function main(): Promise<void> {
process.exit(await runGenerate(args));
}

process.stderr.write(`openapi-codegen: unknown command "${args.command}"\n`);
process.stderr.write(`typeforge: unknown command "${args.command}"\n`);
printHelp();
process.exit(1);
}
Expand Down
Loading
Loading