@adhd/apigen-cli 0.1.0 → 0.1.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.
@@ -1,4 +1,4 @@
1
- import { OutputPlugin } from '@adhd/apigen-core';
1
+ import { OutputPlugin } from '@adhd/apigen-core-client';
2
2
  import { Command } from 'commander';
3
3
 
4
4
  export declare function registerGenerateRegistryCommand(program: Command, plugins: Record<string, OutputPlugin>): void;
@@ -1,6 +1,47 @@
1
- import { ExportMode, OutputPlugin } from '@adhd/apigen-core';
1
+ import { ExportMode, OutputPlugin, ComposedSchemas } from '@adhd/apigen-core-client';
2
2
  import { Command } from 'commander';
3
3
 
4
+ /**
5
+ * Recursively walk a JSON-Schema node (any depth) and collect every
6
+ * unique `format` string value found anywhere in the tree.
7
+ *
8
+ * Pure function: no I/O, no mutation of the input.
9
+ *
10
+ * @param node Any JSON value (Schema node, array, primitive).
11
+ * @param seen Cycle guard — the set of objects already visited (prevents
12
+ * infinite loops on schemas with `definitions` back-refs).
13
+ * @returns A `Set<string>` of all format strings found.
14
+ */
15
+ export declare function collectFormats(node: unknown, seen?: WeakSet<object>): Set<string>;
16
+ /**
17
+ * Given a surface's `ComposedSchemas`, return the npm dependency entries
18
+ * required to support the logical types actually used by its operations.
19
+ *
20
+ * Walks every input + output schema, unions their `format` annotations,
21
+ * looks each up in the authoritative {@link tsDepMap} from `@adhd/apigen-base-logical`,
22
+ * and returns a `Record<name, version>` suitable for merging into `package.json`
23
+ * `dependencies`.
24
+ *
25
+ * A surface with NO rich types returns an empty record (no `decimal.js`,
26
+ * etc.). A surface using `Decimal` returns `{ 'decimal.js': '^10' }`.
27
+ *
28
+ * @param schemas The surface's composed schema map (fn-name → {input, output}).
29
+ */
30
+ export declare function collectLogicalTypeDeps(schemas: ComposedSchemas | Record<string, {
31
+ input: Record<string, unknown>;
32
+ output: Record<string, unknown>;
33
+ }>): Record<string, string>;
34
+ /**
35
+ * Patch the generated `package.json` in `outputDir` by merging in
36
+ * `logicalTypeDeps`. When the `package.json` has no deps or the dep
37
+ * map is empty this is a no-op (safe to call unconditionally).
38
+ *
39
+ * Called AFTER {@link emitResolutionScaffolding} so the base deps
40
+ * (apigen-runtime, sdk) are already present.
41
+ *
42
+ * Exported for testing; the generate command is the normal caller.
43
+ */
44
+ export declare function patchPackageJsonDeps(outputDir: string, logicalTypeDeps: Record<string, string>): void;
4
45
  /** Resolve the --export flag value to an ExportMode. */
5
46
  export declare function resolveExportMode(exportFlag: string | undefined): ExportMode;
6
47
  export declare function registerGenerateCommand(program: Command, plugins: Record<string, OutputPlugin>): void;
@@ -0,0 +1,11 @@
1
+ import { OutputPlugin } from '@adhd/apigen-core-client';
2
+ import { Command } from 'commander';
3
+
4
+ /**
5
+ * `apigen list-types` — the only discovery mechanism for valid `--type`
6
+ * values before FEAT-APIGEN-019 was reading source, hitting an error, or
7
+ * already knowing them. Output is derived live from the same `plugins` map
8
+ * every other command receives, so it can never drift from what's actually
9
+ * registered (see `../plugin-registry`).
10
+ */
11
+ export declare function registerListTypesCommand(program: Command, plugins: Record<string, OutputPlugin>, write?: (text: string) => void): void;
@@ -1,4 +1,4 @@
1
- import { OutputPlugin } from '@adhd/apigen-core';
1
+ import { OutputPlugin } from '@adhd/apigen-core-client';
2
2
  import { Command } from 'commander';
3
3
 
4
4
  export declare function registerRunRegistryCommand(program: Command, plugins: Record<string, OutputPlugin>): void;
@@ -1,4 +1,49 @@
1
- import { OutputPlugin } from '@adhd/apigen-core';
2
1
  import { Command } from 'commander';
2
+ import { ComposedSchemas, OutputPlugin, Plugin } from '@adhd/apigen-core-client';
3
3
 
4
+ /**
5
+ * Assert that the function table built from the source module is non-empty.
6
+ *
7
+ * A source that yields 0 functions is almost certainly generated output, a
8
+ * type-only file, or the wrong path — not a callable apigen surface. Failing
9
+ * here with an actionable message avoids the cryptic `ERR_MODULE_NOT_FOUND`
10
+ * crash that occurs later when the server tries to dispatch to a non-existent
11
+ * route.
12
+ *
13
+ * @param fns - Function table produced by `buildFnTable`.
14
+ * @param sourceFile - Absolute path to the source, for the error message.
15
+ * @throws if `fns` contains no entries.
16
+ */
17
+ export declare function assertFnsNonEmpty(fns: Record<string, (...args: unknown[]) => unknown>, sourceFile: string): void;
18
+ /**
19
+ * A resolver function that mimics `require.resolve` for a package name.
20
+ * Injected in tests to simulate absence without actually removing packages.
21
+ */
22
+ export type LibResolver = (pkg: string) => string;
23
+ /**
24
+ * Assert that `decimal.js` is resolvable when any function in `schemas`
25
+ * uses a `format:'decimal'` parameter or return value.
26
+ *
27
+ * @param schemas - The composed schemas for the surface.
28
+ * @param resolver - Optional resolver; defaults to `require.resolve`.
29
+ * Injected in tests to simulate the lib being absent.
30
+ * @throws if decimal-using functions are found but `decimal.js` cannot resolve.
31
+ */
32
+ export declare function assertDecimalLibPresent(schemas: ComposedSchemas, resolver?: LibResolver): void;
33
+ /**
34
+ * Resolve `--use` specifiers into loaded {@link Plugin} objects.
35
+ *
36
+ * Resolution order per specifier:
37
+ * 1. Built-in slug (`health`, `logger`) → the statically-imported plugin.
38
+ * 2. Otherwise treat the specifier as a package name or local path and
39
+ * dynamically `import()` it (default or named `plugin`/`<id>Plugin` export).
40
+ *
41
+ * The loaded plugins are threaded to the run plugin via `options.usePlugins`
42
+ * so the transport adapter can compose their `layer`/`mount` capabilities
43
+ * (RunInput carries no dedicated field).
44
+ *
45
+ * @param specifiers - The raw `--use` values (slugs, package names, or paths).
46
+ * @returns The loaded plugin objects, in declaration order.
47
+ */
48
+ export declare function loadUsePlugins(specifiers: string[]): Promise<Plugin[]>;
4
49
  export declare function registerRunCommand(program: Command, plugins: Record<string, OutputPlugin>): void;
@@ -0,0 +1,158 @@
1
+ import { PluginLanguage } from '@adhd/apigen-core-client';
2
+ import { ChildProcess } from 'node:child_process';
3
+ import { Command } from 'commander';
4
+
5
+ import * as http from 'node:http';
6
+ /** A single mounted source: its namespace, language, plugin, file, and runtime. */
7
+ export interface Host {
8
+ /** Namespace prefix this host serves under (`/<ns>/*`). */
9
+ namespace: string;
10
+ /** The host language (`ts` | `py` | …). */
11
+ language: PluginLanguage;
12
+ /** The plugin id driving the child (`api-fastify`, `py-flask`, `py-grpc`, …). */
13
+ plugin: string;
14
+ /** Absolute path to the source file. */
15
+ source: string;
16
+ /** Internal loopback port the child listens on. */
17
+ port: number;
18
+ /**
19
+ * Transport protocol for this host.
20
+ * - `'http'` — child is an HTTP/1.1 server (api-fastify, py-flask).
21
+ * - `'grpc'` — child is a gRPC (HTTP/2) server (py-grpc).
22
+ */
23
+ transport: 'http' | 'grpc';
24
+ /** The child process (set once spawned). */
25
+ child?: ChildProcess;
26
+ /** Liveness flag flipped to `false` when the child exits. */
27
+ alive: boolean;
28
+ /** Readiness flag flipped to `true` once the child is accepting connections. */
29
+ ready: boolean;
30
+ }
31
+ /** Per-host status in the aggregate `_meta/health` (§13.1). */
32
+ export type HostHealthStatus = 'ready' | 'down';
33
+ /**
34
+ * Parse `--mount <ns>=<plugin>` pairs into a `{ ns → plugin }` record.
35
+ *
36
+ * @throws if a pair is missing the `=` separator or has an empty side.
37
+ */
38
+ export declare function parseMounts(pairs: string[]): Record<string, string>;
39
+ /** Derive a source's default namespace: its filename stem. */
40
+ export declare function namespaceOfSource(file: string): string;
41
+ /**
42
+ * Resolve the {@link Host} descriptors (sans runtime fields) for the given
43
+ * `--source` list, applying any `--mount` overrides.
44
+ *
45
+ * Resolution per source:
46
+ * 1. `language = languageOfSource(file)` — unknown extension → error.
47
+ * 2. `namespace = stem(file)`.
48
+ * 3. `plugin = mounts[namespace] ?? DEFAULT_PLUGIN_FOR_LANGUAGE[language]`.
49
+ * 4. `transport = GRPC_PLUGINS.has(plugin) ? 'grpc' : 'http'`.
50
+ *
51
+ * @throws on an unknown extension, an unmappable language, or a duplicate
52
+ * namespace (two sources would collide on the same `/<ns>/*` prefix).
53
+ */
54
+ export declare function resolveHosts(sources: string[], mounts: Record<string, string>): Host[];
55
+ /**
56
+ * Extract the leading path segment of a request URL (the namespace).
57
+ *
58
+ * `/users/getUser?x=1` → `users`; `/_meta/health` → `_meta`; `/` → `''`.
59
+ */
60
+ export declare function namespaceFromUrl(url: string): string;
61
+ /**
62
+ * Ask the OS for a free loopback TCP port by binding port 0 and reading back
63
+ * the assigned port, then releasing it. There is an unavoidable TOCTOU window
64
+ * between release and the child binding it; in practice the child binds within
65
+ * milliseconds and the kernel does not immediately re-hand the same ephemeral
66
+ * port, so collisions are vanishingly rare. Each host gets its own port.
67
+ */
68
+ export declare function findFreePort(): Promise<number>;
69
+ /**
70
+ * Poll an HTTP host's `_meta/health` until it answers 2xx or the deadline passes.
71
+ *
72
+ * Event-driven: it re-probes on a short interval, but each probe is a real HTTP
73
+ * round-trip to the child — readiness is the child *actually answering*, never
74
+ * a wall-clock sleep. Aborts early (rejects) if the child exits first.
75
+ *
76
+ * @param host - The host to probe.
77
+ * @param timeoutMs - Overall budget (default 15 s — Python cold-start + import).
78
+ * @param intervalMs - Delay between probes (default 100 ms).
79
+ */
80
+ export declare function waitForReady(host: Host, timeoutMs?: number, intervalMs?: number): Promise<void>;
81
+ /**
82
+ * The path used to re-invoke this CLI for a child `run`. When the CLI is the
83
+ * bundled standalone (`dist/.../index.js`), `process.argv[1]` is that bundle;
84
+ * spawning `node <bundle> run …` reuses the same inlined plugin graph. Tests
85
+ * override this to point at a stub.
86
+ */
87
+ export declare function selfCliPath(): string;
88
+ /**
89
+ * Spawn one child `apigen run` for a HTTP host and wire its lifecycle.
90
+ *
91
+ * The child is started detached-free (same process group) so the front's
92
+ * teardown can signal it directly. `alive` flips to `false` on exit; if a
93
+ * child dies, its `/<ns>/*` routes start returning 503 (partial availability).
94
+ *
95
+ * @param host - The host to start (mutated: `child`, `alive`).
96
+ * @param cliPath - Path to the apigen CLI entry to spawn (see {@link selfCliPath}).
97
+ * @param onExit - Callback invoked when the child exits (for logging).
98
+ * @param extraArgs - Extra args appended to the child `run` invocation (tests).
99
+ */
100
+ export declare function spawnHost(host: Host, cliPath: string, onExit?: (host: Host, code: number | null, signal: NodeJS.Signals | null) => void, extraArgs?: string[]): ChildProcess;
101
+ /**
102
+ * Kill every child process and resolve once all have exited (or the deadline
103
+ * passes, after which a hard `SIGKILL` is sent). Idempotent.
104
+ *
105
+ * @param hosts - The hosts whose children to terminate.
106
+ * @param graceMs - Grace period before escalating SIGTERM → SIGKILL.
107
+ */
108
+ export declare function killAll(hosts: Host[], graceMs?: number): Promise<void>;
109
+ /**
110
+ * Build the aggregate `_meta/health` payload (§13.1).
111
+ *
112
+ * ```json
113
+ * { "status": "ok"|"degraded", "hosts": { "<ns>": "ready"|"down", … } }
114
+ * ```
115
+ *
116
+ * `status` is `ok` only when every host is ready; any down host → `degraded`
117
+ * (the front itself is still up — partial availability, never whole-surface
118
+ * down).
119
+ */
120
+ export declare function aggregateHealth(hosts: Host[]): {
121
+ status: 'ok' | 'degraded';
122
+ hosts: Record<string, HostHealthStatus>;
123
+ };
124
+ /**
125
+ * Create the combined front server: HTTP/1.1 proxy + gRPC (HTTP/2) proxy,
126
+ * both on the same TCP port, disambiguated by inspecting the first bytes of
127
+ * each connection.
128
+ *
129
+ * @param hosts - The resolved, spawned hosts (with live ports).
130
+ * @param frontPort - The TCP port the front should listen on.
131
+ */
132
+ export declare function createFrontServer(hosts: Host[]): http.Server;
133
+ /**
134
+ * Start the full serve stack: spawn every host, await readiness, then start the
135
+ * front. Resolves to a teardown function and the listening front server.
136
+ *
137
+ * Exported (not just inlined in the action) so the behavioural test can drive
138
+ * the real stack in-process with bounded waits.
139
+ *
140
+ * @param opts.sources - The `--source` files.
141
+ * @param opts.port - The front port.
142
+ * @param opts.mounts - `{ ns → plugin }` overrides.
143
+ * @param opts.cliPath - The CLI entry to spawn children with (defaults to self).
144
+ * @param opts.log - Optional logger sink (defaults to console.error).
145
+ */
146
+ export declare function startServe(opts: {
147
+ sources: string[];
148
+ port: number;
149
+ mounts?: Record<string, string>;
150
+ cliPath?: string;
151
+ log?: (msg: string) => void;
152
+ }): Promise<{
153
+ hosts: Host[];
154
+ front: http.Server;
155
+ shutdown: () => Promise<void>;
156
+ }>;
157
+ /** Register the `serve` command on the program. */
158
+ export declare function registerServeCommand(program: Command): void;
@@ -1,8 +1,19 @@
1
1
  /**
2
- * Dynamically import a (possibly TypeScript) source module, registering the tsx
3
- * ESM loader for the duration of the import so that `.ts` entry files resolve in
4
- * a plain `node` process. The loader is unregistered afterward so no global hook
5
- * leaks into the rest of the run.
2
+ * Dynamically import a (possibly TypeScript) source module, registering both the
3
+ * tsx ESM loader and the tsx CJS `require` patch for the duration of the import so
4
+ * that `.ts` entry files resolve in a plain `node` process. Both loaders are
5
+ * unregistered afterward so no global hook leaks into the rest of the run.
6
+ *
7
+ * Both hooks are required, not just the ESM one: when the target (or a package it
8
+ * imports) has no `"type": "module"` in its nearest package.json — as internal
9
+ * workspace libs commonly don't — Node's ESM loader detects the `.ts` file as
10
+ * CommonJS and routes it through the CJS translator, which drives real
11
+ * `require()` calls for every relative import. Those requires use Node's classic
12
+ * CJS resolver, not the ESM resolve hook, so a source written with the
13
+ * NodeNext-style `./db.js` specifier (resolving to `./db.ts` on disk) fails with
14
+ * `MODULE_NOT_FOUND` unless `tsx/cjs/api` has also patched CJS resolution.
15
+ * Registering only `tsx/esm/api` reproduces exactly this failure; the full `tsx`
16
+ * CLI works because it patches both loaders — this mirrors that.
6
17
  *
7
18
  * Under a transpiling test runner (vitest) tsx registration is a harmless no-op,
8
19
  * so this same path works in-repo and standalone.
package/lib/logging.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Logger } from '@adhd/apigen-runtime';
1
+ import { Logger } from '@adhd/apigen-engine-runtime';
2
2
  import { Command } from 'commander';
3
3
 
4
4
  export interface LoggingFlags {
@@ -1,5 +1,5 @@
1
1
  import { ProjectionConfig } from '@adhd/apigen-naming';
2
- import { Operation, Descriptor, ComposedSchemas, ExportMode, Logger, OutputPlugin } from '@adhd/apigen-core';
2
+ import { Operation, Descriptor, ComposedSchemas, ExportMode, Logger, OutputPlugin } from '@adhd/apigen-core-client';
3
3
 
4
4
  /**
5
5
  * A single source entry passed to the orchestrator.
@@ -12,9 +12,20 @@ export interface SourceEntry {
12
12
  /** Absolute path to the source file. */
13
13
  file: string;
14
14
  /**
15
- * Export mode for this source. Defaults to `{ type: 'named' }` when omitted.
16
- * Per-source because a single `run` may compose multiple differently-shaped
17
- * sources.
15
+ * Export mode for this source scopes which of the fully-extracted
16
+ * operations are actually composed into `packageSchemas` / served
17
+ * (BUG-APIGEN-034 fix). v2's `extract()` walks the FULL export-shape
18
+ * matrix (named, default, named-object, CJS — see extract.ts's header
19
+ * comment) unconditionally in a single pass, unlike v1's three
20
+ * mutually-exclusive extractors (`extractNamed`/`extractDefault`/
21
+ * `extractNamedObject`); `buildDescriptor()`'s Step 5 restores the same
22
+ * effective scoping by reclassifying each `Operation` by its `path` shape
23
+ * (see `opMatchesExportMode()`) rather than by re-running a separate
24
+ * extraction pass. Does NOT affect `descriptor.operations` (collision
25
+ * check, `--use` mount plugins) — only the served surface — matching v1's
26
+ * own behaviour where the collision-check step never respected
27
+ * `exportMode` either. Omitted → no scoping (all extracted operations are
28
+ * served); this is what `generate-registry`/`run-registry` rely on today.
18
29
  */
19
30
  exportMode?: ExportMode;
20
31
  /**
@@ -24,15 +35,25 @@ export interface SourceEntry {
24
35
  namespace?: string;
25
36
  /** Explicit tsconfig.json path. Resolved per source file when omitted. */
26
37
  tsconfig?: string;
38
+ /**
39
+ * Import specifier written into generated code for this source, e.g. an
40
+ * npm package name (`'@adhd/foo'`) instead of the absolute extraction
41
+ * path. Defaults to `file` when omitted — true for the single-source
42
+ * `generate`/`run` CLI commands, where the resolved source path IS the
43
+ * import path. `generate-registry`/`run-registry` set this explicitly:
44
+ * their package discovery resolves a physical entry file for extraction
45
+ * (`file`) that differs from the package's published import specifier.
46
+ */
47
+ importPath?: string;
27
48
  }
28
49
  /**
29
50
  * Projection-override config (Tenet 1).
30
51
  *
31
52
  * Accepted from `--opt http.verb.<id>=GET` pairs or an `apigen.config` file.
32
53
  * Overrides are NEVER written to source.
54
+ * Extend here as other projection dimensions are added (route, name, …).
33
55
  */
34
- export interface OverrideConfig extends ProjectionConfig {
35
- }
56
+ export type OverrideConfig = ProjectionConfig;
36
57
  /** Options passed to the v2 orchestrator. */
37
58
  export interface OrchestratorOptions {
38
59
  /** Source files to extract and merge. Must be non-empty. */
@@ -106,6 +127,42 @@ export declare function loadOverrideConfig(configPath: string | undefined, cliOv
106
127
  * @param perSourceOps - Arrays produced by {@link extractSource} per file.
107
128
  */
108
129
  export declare function mergeOperations(perSourceOps: Operation[][]): Operation[];
130
+ /**
131
+ * BUG-APIGEN-034: restores v1's `--export <mode>` scoping of the SERVED
132
+ * surface. v1 ran exactly one of three mutually-exclusive extractors
133
+ * (`extractNamed`/`extractDefault`/`extractNamedObject`) per `exportMode`;
134
+ * v2's `extract()` walks the full export-shape matrix unconditionally (see
135
+ * extract.ts's header comment), so this reclassifies each already-extracted
136
+ * `Operation` by `path` shape and filters Step 5's schema composition only —
137
+ * `descriptor.operations` (collision-check, `--use` mount plugins) stays
138
+ * unscoped, matching pre-v1-retirement behaviour (the old collision-check
139
+ * step never respected `exportMode` either — only what actually got SERVED
140
+ * did).
141
+ *
142
+ * Path-shape correspondence (extract.ts's six-shape matrix):
143
+ * - `'named'` → `path.length === 2` (shapes 1/2: a plain named
144
+ * function/const export). This shape is also produced by shape 6 (a CJS
145
+ * `module.exports` property) and shape 4's bare
146
+ * `export default function foo(){}` form — neither is distinguishable
147
+ * from a plain named export via `path` alone, since `Operation` carries
148
+ * no export-shape discriminator (that would require extending
149
+ * `extract()`/`Operation` itself — out of scope here, filed as a
150
+ * follow-up). v1 never supported CJS sources or a bare default-fn-decl
151
+ * under ANY `--export` mode (see extract.ts's shape 4/6 comments), so
152
+ * folding them into `'named'` is a strict superset of v1's true
153
+ * `extractNamed` coverage, never a subset: nothing v1 used to serve
154
+ * under `--export` (omitted) goes missing here.
155
+ * - `'default'` → `path.length === 3 && path[1].raw === 'default'`
156
+ * (shape 4's default-OBJECT branch only, e.g. `export default { a, b }`).
157
+ * v1's `extractDefault` covered EXCLUSIVELY this object-literal
158
+ * default-export form — never a bare `export default function foo(){}`
159
+ * (shape 4's fn-decl branch) or an anonymous default fn (shape 5), both
160
+ * new in v2 with no v1 predecessor — so excluding them here matches v1's
161
+ * actual historical coverage exactly.
162
+ * - `'named-object'` → `path.length === 3 && path[1].raw === mode.name`
163
+ * (shape 3: `export const <mode.name> = { ... }`).
164
+ */
165
+ export declare function opMatchesExportMode(op: Operation, mode: ExportMode): boolean;
109
166
  /**
110
167
  * Build the unified `OrchestratorDescriptor` from a set of source entries.
111
168
  *
@@ -114,7 +171,11 @@ export declare function mergeOperations(perSourceOps: Operation[][]): Operation[
114
171
  * 2. Extract canonical `Operation[]` per source (v2 extract).
115
172
  * 3. Merge into one list.
116
173
  * 4. Run the collision check (SPEC §5 uniqueness invariant).
117
- * 5. Build per-source `ComposedSchemas` for the v1 plugin surface.
174
+ * 5. Derive per-source `ComposedSchemas` (the plugin-facing surface every
175
+ * `OutputPlugin.generate()`/`run()` actually dispatches against) FROM
176
+ * the operations already extracted in step 2 — see the note on Step 5
177
+ * below for why this must not re-extract. Scoped per-source by
178
+ * `SourceEntry.exportMode` (BUG-APIGEN-034) via `opMatchesExportMode()`.
118
179
  *
119
180
  * @param opts - Orchestrator options.
120
181
  */
@@ -140,13 +201,17 @@ export declare function orchestrateGenerate(opts: OrchestratorOptions, plugin: O
140
201
  * @param opts - Orchestrator options.
141
202
  * @param plugin - The selected output plugin (`--type`).
142
203
  * @param buildFnTables - Async function that imports each source and returns
143
- * `(fns, createClient)` for that source. Injected by
144
- * the command layer so the orchestrator stays testable
145
- * without live imports.
204
+ * `(fns, createClient)` for that source. Receives the
205
+ * source's composed `ComposedSchemas` too, so the
206
+ * command layer can run schema-driven precondition
207
+ * guards (e.g. the `decimal.js` optional-peer-dep
208
+ * check) before importing the live module. Injected
209
+ * by the command layer so the orchestrator stays
210
+ * testable without live imports.
146
211
  * @param signal - Abort signal forwarded from the process SIGINT handler.
147
212
  * @param pluginOpts - Plugin-level options.
148
213
  */
149
- export declare function orchestrateRun(opts: OrchestratorOptions, plugin: OutputPlugin, buildFnTables: (entry: SourceEntry) => Promise<{
214
+ export declare function orchestrateRun(opts: OrchestratorOptions, plugin: OutputPlugin, buildFnTables: (entry: SourceEntry, schemas: ComposedSchemas) => Promise<{
150
215
  fns: Record<string, (...args: unknown[]) => unknown>;
151
216
  createClient: (envelope: Record<string, unknown>) => Promise<object>;
152
217
  }>, signal: AbortSignal, pluginOpts?: Record<string, unknown>): Promise<void>;
@@ -0,0 +1,33 @@
1
+ import { OutputPlugin } from '@adhd/apigen-core-client';
2
+
3
+ /**
4
+ * Single source of truth for everything the CLI derives from the `--type`
5
+ * plugin registry: help text, `--list-types` output, and the "unknown
6
+ * --type"/"doesn't support run" errors. Every one of those was previously a
7
+ * hand-maintained string that drifted from the real `plugins` map passed
8
+ * into each command's `registerXCommand()` (FEAT-APIGEN-019) — this module
9
+ * is the only place that reads `Object.keys(plugins)` / `plugin.run` so
10
+ * they can never drift again.
11
+ */
12
+ /** All registered `--type` ids, in registration order (includes aliases like `cli`/`cli-output`). */
13
+ export declare function pluginTypeIds(plugins: Record<string, OutputPlugin>): string[];
14
+ /** `--type` ids whose plugin implements `run()` — usable with `run` / `run-registry`. */
15
+ export declare function runCapableTypeIds(plugins: Record<string, OutputPlugin>): string[];
16
+ /** `--type` ids whose plugin has no `run()` — generate-only targets. */
17
+ export declare function generateOnlyTypeIds(plugins: Record<string, OutputPlugin>): string[];
18
+ /** Commander help text for `generate` / `generate-registry`'s `--type <plugin-id>` option. */
19
+ export declare function describeTypeOption(plugins: Record<string, OutputPlugin>): string;
20
+ /** Commander help text for `run` / `run-registry`'s `--type <plugin-id>` option (run-capable subset only). */
21
+ export declare function describeRunTypeOption(plugins: Record<string, OutputPlugin>): string;
22
+ /** The error thrown when `--type` isn't a registered plugin id at all. */
23
+ export declare function unknownTypeError(type: string, plugins: Record<string, OutputPlugin>): Error;
24
+ /**
25
+ * The error thrown when `--type` names a real, registered plugin that simply
26
+ * has no `run()` (e.g. `jsonschema`, `cli`) — distinct from {@link unknownTypeError}
27
+ * so a typo'd/unrecognized `--type` (e.g. `express` instead of `api-express`)
28
+ * doesn't get misreported as "exists but doesn't support run" (BACKLOG
29
+ * FEAT-APIGEN-019 live field confirmation).
30
+ */
31
+ export declare function unsupportedRunError(type: string, plugins: Record<string, OutputPlugin>): Error;
32
+ /** Human-readable multi-line listing for the `list-types` command. */
33
+ export declare function formatTypesList(plugins: Record<string, OutputPlugin>): string;
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@adhd/apigen-cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "bin": {
5
- "apigen-cli": "./index.js"
5
+ "apigen": "./index.js"
6
6
  },
7
7
  "dependencies": {
8
8
  "commander": "^14.0.3",
@@ -15,13 +15,27 @@
15
15
  "pino": "10.3.1",
16
16
  "pino-pretty": "13.1.3",
17
17
  "pino-http": "11.0.0",
18
- "@adhd/apigen-core": "^0.1.0",
19
- "@adhd/apigen-runtime": "^0.1.0",
20
- "@adhd/apigen-plugin-mcp": "^0.1.0",
21
- "@adhd/apigen-plugin-jsonschema": "^0.1.0",
22
- "@adhd/apigen-plugin-api-fastify": "^0.1.0",
23
- "@adhd/apigen-plugin-api-express": "^0.1.0",
24
- "@adhd/apigen-plugin-cli-output": "^0.1.0"
18
+ "@adhd/apigen-plugin-mcp": "^0.1.1",
19
+ "@adhd/apigen-plugin-jsonschema": "^0.1.1",
20
+ "@adhd/apigen-plugin-api-fastify": "^0.1.1",
21
+ "@adhd/apigen-plugin-api-express": "^0.1.1",
22
+ "@adhd/apigen-plugin-cli-output": "^0.1.1",
23
+ "@adhd/apigen-plugin-py-flask": "^0.1.1",
24
+ "@adhd/apigen-plugin-py-grpc": "^0.1.1",
25
+ "@adhd/apigen-plugin-health": "^0.1.2",
26
+ "@adhd/apigen-plugin-logger": "^0.1.2",
27
+ "@adhd/apigen-python-env": "^0.1.0",
28
+ "@adhd/apigen-base-logical": "^0.0.1",
29
+ "@adhd/apigen-engine-runtime": "^0.1.0",
30
+ "@adhd/apigen-core-client": "^0.1.0",
31
+ "@adhd/apigen-engine-naming": "^0.1.1",
32
+ "@adhd/apigen-plugin-openapi": "^0.1.2"
33
+ },
34
+ "peerDependencies": {},
35
+ "peerDependenciesMeta": {
36
+ "decimal.js": {
37
+ "optional": true
38
+ }
25
39
  },
26
40
  "main": "./index.js",
27
41
  "module": "./index.mjs",
@@ -1,10 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2020",
4
- "module": "esnext",
5
- "moduleResolution": "bundler",
6
- "esModuleInterop": true,
7
- "strict": true,
8
- "skipLibCheck": true
9
- }
10
- }
package/lib/pipeline.d.ts DELETED
@@ -1,22 +0,0 @@
1
- import { ComposedSchemas, ExportMode, Logger } from '@adhd/apigen-core';
2
-
3
- export interface PipelineOptions {
4
- sourceFile: string;
5
- exportMode?: ExportMode;
6
- middlewares?: Array<{
7
- id: string;
8
- envelope?: Record<string, unknown>;
9
- }>;
10
- overrides?: Record<string, Record<string, boolean>>;
11
- namespace?: string;
12
- /** Explicit --tsconfig flag; when omitted, the nearest/builtin config is resolved from sourceFile. */
13
- tsconfig?: string;
14
- /** Optional shared logger; when present the pipeline logs schema extraction. */
15
- logger?: Logger;
16
- }
17
- export interface PipelineResult {
18
- schemas: ComposedSchemas;
19
- createClient: (envelope: Record<string, unknown>) => Promise<object>;
20
- }
21
- /** Run the generate + compose pipeline for a single source file. */
22
- export declare function runPipeline(opts: PipelineOptions): Promise<PipelineResult>;