@adhd/apigen-core 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/README.md +38 -0
- package/index.d.ts +10 -0
- package/index.js +31412 -0
- package/index.mjs +380115 -0
- package/lib/apigen-core.d.ts +1 -0
- package/lib/compose-schemas.d.ts +16 -0
- package/lib/descriptor.d.ts +202 -0
- package/lib/extract-classes.d.ts +30 -0
- package/lib/extract.d.ts +34 -0
- package/lib/extractors/default-export.d.ts +8 -0
- package/lib/extractors/named-object.d.ts +8 -0
- package/lib/extractors/named.d.ts +17 -0
- package/lib/generate-schemas.d.ts +9 -0
- package/lib/plugin.d.ts +433 -0
- package/lib/schema-builders/morph-fallback.d.ts +2 -0
- package/lib/schema-builders/ts-json-schema.d.ts +4 -0
- package/lib/types.d.ts +68 -0
- package/package.json +15 -0
package/lib/plugin.d.ts
ADDED
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
import { Operation, JSONSchema } from './descriptor';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The four canonical transport carriers apigen knows about (SPEC §5/§7/§9.1).
|
|
5
|
+
*
|
|
6
|
+
* A `MountedOperation` may opt in to a subset — `transports` omitted → all.
|
|
7
|
+
*/
|
|
8
|
+
export type Transport = 'http' | 'grpc' | 'mcp' | 'cli';
|
|
9
|
+
/**
|
|
10
|
+
* A type-keyed, mutable-during-compose map threaded through every Layer and
|
|
11
|
+
* into the dispatch function as `ctx` (SPEC §8.1).
|
|
12
|
+
*
|
|
13
|
+
* Layers insert typed values with a class or symbol key and downstream layers /
|
|
14
|
+
* the function implementation read them back — the same mental model as
|
|
15
|
+
* `http::Extensions` (Rust) and `AsyncLocalStorage` (Node).
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* // insert (in a Layer):
|
|
20
|
+
* call.ctx.set(Logger, new Logger({ level: 'info' }))
|
|
21
|
+
* // read (in a Layer or dispatch):
|
|
22
|
+
* const log = call.ctx.get(Logger)
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export interface Extensions {
|
|
26
|
+
/**
|
|
27
|
+
* Retrieve the value stored under the given class constructor or symbol key.
|
|
28
|
+
* Returns `undefined` when the key has not been set.
|
|
29
|
+
*/
|
|
30
|
+
get<T>(key: abstract new (...args: never[]) => T): T | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* Store a value under the given class constructor or symbol key.
|
|
33
|
+
* Overwrites any existing value for that key.
|
|
34
|
+
*/
|
|
35
|
+
set<T>(key: abstract new (...args: never[]) => T, value: T): void;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The inbound call descriptor passed to every layer and ultimately to dispatch
|
|
39
|
+
* (SPEC §7.1 / §8.1).
|
|
40
|
+
*
|
|
41
|
+
* `data` contains the bare domain params (envelope dissolved); `envelope`
|
|
42
|
+
* contains transport-native side-channel fields (session, auth tokens, …).
|
|
43
|
+
* `raw` is an escape hatch for transport-specific adapters that need access to
|
|
44
|
+
* the native request object — ordinary plugins should never need it.
|
|
45
|
+
*/
|
|
46
|
+
export interface Call {
|
|
47
|
+
/** The operation being invoked (from the merged canonical descriptor). */
|
|
48
|
+
operation: Operation;
|
|
49
|
+
/** Bare domain params (the `data`-wrapper is dissolved; ctx excluded). */
|
|
50
|
+
data: Record<string, unknown>;
|
|
51
|
+
/**
|
|
52
|
+
* Side-channel metadata from the transport-native carrier, keyed as
|
|
53
|
+
* `x-<pluginId>-<field>` (SPEC §9 / §9.1).
|
|
54
|
+
*/
|
|
55
|
+
envelope: Record<string, unknown>;
|
|
56
|
+
/**
|
|
57
|
+
* Typed request-extensions map — threaded `mw → mw → fn` (SPEC §8.1).
|
|
58
|
+
* Layers insert context values; downstream layers and the domain function
|
|
59
|
+
* read them back.
|
|
60
|
+
*/
|
|
61
|
+
ctx: Extensions;
|
|
62
|
+
/** Which transport delivered this call. */
|
|
63
|
+
transport: Transport;
|
|
64
|
+
/**
|
|
65
|
+
* Cancellation signal — wired to the transport's native cancellation
|
|
66
|
+
* mechanism (HTTP abort, gRPC cancel, MCP cancel, Ctrl-C for CLI).
|
|
67
|
+
* Layers must propagate it to any async work they initiate (SPEC §11).
|
|
68
|
+
*/
|
|
69
|
+
signal: AbortSignal;
|
|
70
|
+
/**
|
|
71
|
+
* Transport-native request object — escape hatch for adapters that need
|
|
72
|
+
* raw access to e.g. a Fastify `Request` or an MCP `CallToolRequest`.
|
|
73
|
+
* Ordinary plugins must NOT depend on this; it degrades portability.
|
|
74
|
+
*/
|
|
75
|
+
raw?: unknown;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* A single chunk emitted by a streaming operation (SPEC §11).
|
|
79
|
+
*
|
|
80
|
+
* The type is intentionally open (`unknown`) because the per-chunk element
|
|
81
|
+
* type is described by `operation.output` (JSON Schema) — static typing is
|
|
82
|
+
* achieved per-plugin via generics when the element type is known.
|
|
83
|
+
*/
|
|
84
|
+
export type Chunk = unknown;
|
|
85
|
+
/**
|
|
86
|
+
* The non-streaming result of a Layer invocation — the value that flows
|
|
87
|
+
* back out to the transport adapter.
|
|
88
|
+
*/
|
|
89
|
+
export type Result = unknown;
|
|
90
|
+
/**
|
|
91
|
+
* The continuation function passed to a Layer.
|
|
92
|
+
*
|
|
93
|
+
* Calling `next()` invokes the **remaining** layers and ultimately `dispatch`.
|
|
94
|
+
* A Layer may call it at most once per request. Not calling it short-circuits
|
|
95
|
+
* all downstream layers and dispatch (SPEC §8.1 rule 1).
|
|
96
|
+
*
|
|
97
|
+
* The return type is a union to support both unary and streaming operations
|
|
98
|
+
* from a single `LayerCapability.layer` signature (SPEC §11).
|
|
99
|
+
*/
|
|
100
|
+
export type Next = () => Promise<Result> | AsyncIterable<Chunk>;
|
|
101
|
+
/**
|
|
102
|
+
* A single emitted file produced by {@link TargetCapability.generate}.
|
|
103
|
+
*
|
|
104
|
+
* `content` is always a UTF-8 string — plugins emit source code, config,
|
|
105
|
+
* or structured text in any language (TS, Python, proto, YAML, …).
|
|
106
|
+
* Nothing in core restricts the language (SPEC [inv:language-agnostic-output]).
|
|
107
|
+
*/
|
|
108
|
+
export interface File {
|
|
109
|
+
/** Relative or absolute path where the file should be written. */
|
|
110
|
+
path: string;
|
|
111
|
+
/** UTF-8 string content to write. */
|
|
112
|
+
content: string;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Opaque handle returned by {@link TargetCapability.serve}.
|
|
116
|
+
*
|
|
117
|
+
* The minimal contract is `close()` for graceful shutdown. Plugins may
|
|
118
|
+
* extend this with transport-specific members (e.g. `port`, `url`).
|
|
119
|
+
*/
|
|
120
|
+
export interface Server {
|
|
121
|
+
/** Gracefully shut down the server and release all resources. */
|
|
122
|
+
close(): Promise<void>;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* The runtime harness injected into {@link TargetCapability.serve}.
|
|
126
|
+
*
|
|
127
|
+
* Provides `invoke` — the composed Layer stack that wraps `dispatch`.
|
|
128
|
+
* Transports call `harness.invoke(op, partialCall)` once per inbound request;
|
|
129
|
+
* the harness threads the active plugins' layers around the operation and
|
|
130
|
+
* returns/streams the result (SPEC §8).
|
|
131
|
+
*/
|
|
132
|
+
export interface Harness {
|
|
133
|
+
/**
|
|
134
|
+
* Invoke the full composed-Layer stack for `op` with the given call context.
|
|
135
|
+
* Partial — the harness fills in `operation`, `ctx`, and wires `signal`.
|
|
136
|
+
*
|
|
137
|
+
* Returns a `Promise<Result>` for unary operations or an
|
|
138
|
+
* `AsyncIterable<Chunk>` for streaming operations (SPEC §11).
|
|
139
|
+
*/
|
|
140
|
+
invoke(op: Operation, call: Omit<Call, 'operation' | 'ctx'>): Promise<Result> | AsyncIterable<Chunk>;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* The merged canonical descriptor: the full set of `Operation`s produced by
|
|
144
|
+
* merging one or more per-language extractors (SPEC §4).
|
|
145
|
+
*
|
|
146
|
+
* Plugins receive a `Descriptor` at generate/serve time; they **must not**
|
|
147
|
+
* modify it — it is the single source of truth.
|
|
148
|
+
*/
|
|
149
|
+
export interface Descriptor {
|
|
150
|
+
/** All extracted operations, across all host languages, in insertion order. */
|
|
151
|
+
operations: Operation[];
|
|
152
|
+
/**
|
|
153
|
+
* The host tag of the primary language runtime, e.g. `'ts'`, `'py'`.
|
|
154
|
+
* Plugins may use this to restrict generated code to the primary host.
|
|
155
|
+
*/
|
|
156
|
+
host: string;
|
|
157
|
+
/**
|
|
158
|
+
* Optional namespace segment from `--namespace` or the tsconfig root folder.
|
|
159
|
+
* Typically the npm package name or a short domain slug.
|
|
160
|
+
*/
|
|
161
|
+
namespace?: string;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* **`target`** capability — project the descriptor to a transport or format
|
|
165
|
+
* (SPEC §7.1).
|
|
166
|
+
*
|
|
167
|
+
* A `TargetCapability` is selected by `--type <name>` at CLI time. It
|
|
168
|
+
* optionally both *generates* (emits static files) and *serves* (runs a live
|
|
169
|
+
* server hosting the domain functions in-process).
|
|
170
|
+
*
|
|
171
|
+
* @typeParam Opts - Plugin-specific options, validated via `optionsSchema`.
|
|
172
|
+
*
|
|
173
|
+
* @remarks
|
|
174
|
+
* **v1 compatibility:** The v1 `OutputPlugin` interface (`generate(PluginInput)`)
|
|
175
|
+
* is the direct precursor. v2 `TargetCapability.generate` receives a
|
|
176
|
+
* `Descriptor` instead of `PluginInput` — the richer, host-agnostic contract.
|
|
177
|
+
* v1 plugins migrate by wrapping their `PluginInput` construction in the new
|
|
178
|
+
* `generate` signature. `serve()` is the new addition for live server targets.
|
|
179
|
+
*/
|
|
180
|
+
export interface TargetCapability<Opts = Record<string, unknown>> {
|
|
181
|
+
/**
|
|
182
|
+
* Short identifier for this target — the value the user passes to `--type`.
|
|
183
|
+
* Examples: `'mcp'`, `'http-fastify'`, `'http-express'`, `'cli'`, `'proto'`.
|
|
184
|
+
*/
|
|
185
|
+
name: string;
|
|
186
|
+
/**
|
|
187
|
+
* Project the descriptor to a set of static files (generate mode).
|
|
188
|
+
*
|
|
189
|
+
* Called once per `apigen generate` invocation with the full merged
|
|
190
|
+
* descriptor and the resolved plugin options. The returned `File[]` is
|
|
191
|
+
* written to the output directory by the CLI.
|
|
192
|
+
*
|
|
193
|
+
* @param descriptor - The merged canonical descriptor (read-only).
|
|
194
|
+
* @param opts - Plugin-specific options (already validated).
|
|
195
|
+
* @returns Array of files to emit (may be empty; never `null`).
|
|
196
|
+
*/
|
|
197
|
+
generate(descriptor: Descriptor, opts: Opts): File[] | Promise<File[]>;
|
|
198
|
+
/**
|
|
199
|
+
* Start a live server hosting the domain functions in-process (run mode).
|
|
200
|
+
*
|
|
201
|
+
* Called once per `apigen run` invocation. The transport adapter wires the
|
|
202
|
+
* harness's `invoke()` to the native request/response cycle.
|
|
203
|
+
*
|
|
204
|
+
* Optional — omit for codegen-only plugins (clients, proto, docs).
|
|
205
|
+
*
|
|
206
|
+
* @param descriptor - The merged canonical descriptor (read-only).
|
|
207
|
+
* @param harness - The composed Layer stack; call `harness.invoke()` per request.
|
|
208
|
+
* @param opts - Plugin-specific options (already validated).
|
|
209
|
+
* @returns A {@link Server} handle; the CLI calls `server.close()` on SIGINT/SIGTERM.
|
|
210
|
+
*/
|
|
211
|
+
serve?(descriptor: Descriptor, harness: Harness, opts: Opts): Promise<Server>;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* **`layer`** capability — wrap operations (the onion) (SPEC §7.1 / §8 / §8.1).
|
|
215
|
+
*
|
|
216
|
+
* A `LayerCapability` is loaded via `--use <plugin>` and is composed by the
|
|
217
|
+
* harness around the `dispatch` call. Hook sugar (`onRequest`/`onResponse`/
|
|
218
|
+
* `onError`) compiles to a `LayerCapability` — one execution model (SPEC §7.1).
|
|
219
|
+
*
|
|
220
|
+
* @remarks
|
|
221
|
+
* **Streaming:** `layer` may return an `AsyncIterable<Chunk>` — making it an
|
|
222
|
+
* `async function*` that wraps `next()` with `for await … yield` — to
|
|
223
|
+
* participate in the full per-chunk stream lifecycle (SPEC §11).
|
|
224
|
+
*/
|
|
225
|
+
export interface LayerCapability {
|
|
226
|
+
/**
|
|
227
|
+
* Extra envelope fields this layer needs on the request side — merged into
|
|
228
|
+
* the effective descriptor's envelope schema before serving begins.
|
|
229
|
+
*
|
|
230
|
+
* Keys are bare field names; values are JSON Schema fragments.
|
|
231
|
+
* Example: `{ session: { type: 'string', description: 'session token' } }`.
|
|
232
|
+
*/
|
|
233
|
+
envelopeFields?: Record<string, JSONSchema>;
|
|
234
|
+
/**
|
|
235
|
+
* The layer function — owns the continuation.
|
|
236
|
+
*
|
|
237
|
+
* Call `next()` to invoke the remaining layers and `dispatch`. Not calling
|
|
238
|
+
* `next()` short-circuits all downstream layers (SPEC §8.1 rule 1).
|
|
239
|
+
*
|
|
240
|
+
* For streaming operations, return an `AsyncIterable<Chunk>` wrapping the
|
|
241
|
+
* iterable returned by `next()` (SPEC §11).
|
|
242
|
+
*
|
|
243
|
+
* @param call - The inbound call descriptor.
|
|
244
|
+
* @param next - The continuation — call at most once.
|
|
245
|
+
* @returns `Promise<Result>` for unary operations; `AsyncIterable<Chunk>`
|
|
246
|
+
* for streaming operations.
|
|
247
|
+
*/
|
|
248
|
+
layer(call: Call, next: Next): Promise<Result> | AsyncIterable<Chunk>;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* **`mount`** capability — add synthetic operations to the descriptor
|
|
252
|
+
* (SPEC §7.1 / §7.2b / §7.2c).
|
|
253
|
+
*
|
|
254
|
+
* A `MountCapability` is loaded via `--use <plugin>` and contributes extra
|
|
255
|
+
* `Operation`-like entries (with an in-process `handler`) that flow through the
|
|
256
|
+
* harness and Layer stack exactly like extracted operations. Typical uses:
|
|
257
|
+
* `/meta/openapi`, `/meta/health`, version endpoints.
|
|
258
|
+
*/
|
|
259
|
+
export interface MountCapability {
|
|
260
|
+
/**
|
|
261
|
+
* Return the set of synthetic operations this plugin contributes.
|
|
262
|
+
*
|
|
263
|
+
* `MountedOperation` extends `Operation` with an in-process `handler` and
|
|
264
|
+
* an optional `transports` filter (default: all transports).
|
|
265
|
+
*
|
|
266
|
+
* @param descriptor - The current merged descriptor (read-only).
|
|
267
|
+
* @param opts - Plugin-specific options.
|
|
268
|
+
* @returns Array of `MountedOperation`s; may be empty.
|
|
269
|
+
*/
|
|
270
|
+
operations(descriptor: Descriptor, opts?: Record<string, unknown>): MountedOperation[];
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* A synthetic operation contributed by a {@link MountCapability}.
|
|
274
|
+
*
|
|
275
|
+
* Extends the base {@link Operation} with:
|
|
276
|
+
* - `transports` — optional filter to restrict which transports expose this
|
|
277
|
+
* operation (default: all four, matching the host plugin's transport set).
|
|
278
|
+
* - `handler` — the in-process function called when a request arrives.
|
|
279
|
+
* Called with the same {@link Call} context as extracted operations; the
|
|
280
|
+
* returned value is marshalled by the transport adapter.
|
|
281
|
+
*/
|
|
282
|
+
export type MountedOperation = Operation & {
|
|
283
|
+
/**
|
|
284
|
+
* Optional transport filter. When omitted the operation is exposed on all
|
|
285
|
+
* transports supported by the active target plugin.
|
|
286
|
+
*/
|
|
287
|
+
transports?: Transport[];
|
|
288
|
+
/**
|
|
289
|
+
* The in-process handler for this synthetic operation.
|
|
290
|
+
*
|
|
291
|
+
* Called with the full {@link Call} context (same as extracted operations)
|
|
292
|
+
* after the composed Layer stack has run. The return value is serialised by
|
|
293
|
+
* the transport adapter and may be a `Promise` or an `AsyncIterable` for
|
|
294
|
+
* streaming mounts.
|
|
295
|
+
*/
|
|
296
|
+
handler(call: Call): unknown | Promise<unknown> | AsyncIterable<Chunk>;
|
|
297
|
+
};
|
|
298
|
+
/**
|
|
299
|
+
* **`envelope`** capability — declare request/response side-channel fields
|
|
300
|
+
* (SPEC §7.1 / §9 / §9.1).
|
|
301
|
+
*
|
|
302
|
+
* A plugin with an `envelope` capability advertises the transport-agnostic
|
|
303
|
+
* side-channel fields it reads from (request) or writes to (response) without
|
|
304
|
+
* wrapping the operation in a Layer. The harness merges these schemas into the
|
|
305
|
+
* effective descriptor's envelope before serving.
|
|
306
|
+
*
|
|
307
|
+
* Canonical field identity is `(pluginId, field)` (SPEC §9.1); fields declared
|
|
308
|
+
* here are surfaced by each transport adapter per the binding table:
|
|
309
|
+
* - HTTP/gRPC/MCP: `x-<pluginId>-<field>` header/metadata/`_meta` key
|
|
310
|
+
* - CLI: `--<pluginId>-<field>` flag + `APIGEN_<PLUGINID>_<FIELD>` env var
|
|
311
|
+
*/
|
|
312
|
+
export interface EnvelopeCapability {
|
|
313
|
+
/**
|
|
314
|
+
* JSON Schema fragments for fields this plugin reads from the incoming
|
|
315
|
+
* transport-native metadata (HTTP headers, gRPC metadata, MCP `_meta`,
|
|
316
|
+
* CLI flags/env).
|
|
317
|
+
*
|
|
318
|
+
* Keys are bare field names (e.g. `'session'`); the adapter prepends
|
|
319
|
+
* `x-<pluginId>-` when surfacing on k/v carriers.
|
|
320
|
+
*/
|
|
321
|
+
request?: Record<string, JSONSchema>;
|
|
322
|
+
/**
|
|
323
|
+
* JSON Schema fragments for fields this plugin writes to the outgoing
|
|
324
|
+
* transport-native metadata (response headers, trailers, `_meta`, stderr).
|
|
325
|
+
*
|
|
326
|
+
* Keys follow the same `x-<pluginId>-<field>` convention as `request`.
|
|
327
|
+
*/
|
|
328
|
+
response?: Record<string, JSONSchema>;
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* The v2 plugin interface (SPEC §7.1).
|
|
332
|
+
*
|
|
333
|
+
* Every apigen plugin is an object that satisfies this interface. A plugin
|
|
334
|
+
* declares one or more **capabilities** — the harness fans them out at compose
|
|
335
|
+
* time. All four capability slots are optional; a minimal "noop" plugin omits
|
|
336
|
+
* all of them (useful as a template).
|
|
337
|
+
*
|
|
338
|
+
* @typeParam Opts - Plugin-specific CLI options, validated against
|
|
339
|
+
* `capabilities.target.optionsSchema` (if present) before being passed to
|
|
340
|
+
* `generate` / `serve` / `mount.operations`.
|
|
341
|
+
*
|
|
342
|
+
* @example Logger layer (SPEC §7.2a)
|
|
343
|
+
* ```ts
|
|
344
|
+
* export default {
|
|
345
|
+
* id: 'logger',
|
|
346
|
+
* capabilities: {
|
|
347
|
+
* layer: {
|
|
348
|
+
* layer: async (call, next) => {
|
|
349
|
+
* const t = Date.now()
|
|
350
|
+
* console.error(`→ ${call.operation.id}`)
|
|
351
|
+
* try { const r = await next(); console.error(`← ${call.operation.id} ${Date.now()-t}ms`); return r }
|
|
352
|
+
* catch (e) { console.error(`✗ ${call.operation.id}`); throw e }
|
|
353
|
+
* },
|
|
354
|
+
* },
|
|
355
|
+
* },
|
|
356
|
+
* } satisfies Plugin
|
|
357
|
+
* ```
|
|
358
|
+
*
|
|
359
|
+
* @example OpenAPI mount (SPEC §7.2b)
|
|
360
|
+
* ```ts
|
|
361
|
+
* import { toOpenApi } from '@adhd/apigen-openapi'
|
|
362
|
+
* export default {
|
|
363
|
+
* id: 'openapi',
|
|
364
|
+
* capabilities: {
|
|
365
|
+
* mount: {
|
|
366
|
+
* operations: (d) => [{
|
|
367
|
+
* ...syntheticOp('_meta/openapi', d),
|
|
368
|
+
* handler: () => toOpenApi(d),
|
|
369
|
+
* }],
|
|
370
|
+
* },
|
|
371
|
+
* },
|
|
372
|
+
* } satisfies Plugin
|
|
373
|
+
* ```
|
|
374
|
+
*/
|
|
375
|
+
export interface Plugin<Opts = Record<string, unknown>> {
|
|
376
|
+
/**
|
|
377
|
+
* Canonical fully-qualified plugin identifier (SPEC §7.1).
|
|
378
|
+
*
|
|
379
|
+
* Use the package name (e.g. `'@adhd/apigen-ts-plugin-logger'`) or a short
|
|
380
|
+
* slug (e.g. `'logger'`). The CLI accepts either as the `--use` / `--type`
|
|
381
|
+
* argument. The id is also used as the `pluginId` in envelope field naming
|
|
382
|
+
* (`x-<id>-<field>`, SPEC §9.1).
|
|
383
|
+
*/
|
|
384
|
+
id: string;
|
|
385
|
+
/**
|
|
386
|
+
* Optional human-readable description shown in `apigen plugins list` output
|
|
387
|
+
* and generated documentation.
|
|
388
|
+
*/
|
|
389
|
+
description?: string;
|
|
390
|
+
/**
|
|
391
|
+
* Optional JSON Schema for plugin-specific options.
|
|
392
|
+
*
|
|
393
|
+
* When provided, the CLI validates the `--opt` values supplied via
|
|
394
|
+
* `--use <plugin> --opt key=value` before constructing `opts`.
|
|
395
|
+
*/
|
|
396
|
+
optionsSchema?: Record<string, unknown>;
|
|
397
|
+
/**
|
|
398
|
+
* The set of capabilities this plugin contributes. All four are optional.
|
|
399
|
+
*
|
|
400
|
+
* At least one capability is expected in practice; the harness warns
|
|
401
|
+
* (at debug level) when a loaded plugin declares no capabilities.
|
|
402
|
+
*/
|
|
403
|
+
capabilities: {
|
|
404
|
+
/**
|
|
405
|
+
* Target capability — project the descriptor to a transport/format and/or
|
|
406
|
+
* host domain functions in-process (SPEC §7.1 / §5).
|
|
407
|
+
*
|
|
408
|
+
* Selected by `--type <plugin>`.
|
|
409
|
+
*/
|
|
410
|
+
target?: TargetCapability<Opts>;
|
|
411
|
+
/**
|
|
412
|
+
* Layer capability — wrap all operations in the onion (SPEC §7.1 / §8).
|
|
413
|
+
*
|
|
414
|
+
* Loaded by `--use <plugin>` when the plugin declares this capability.
|
|
415
|
+
*/
|
|
416
|
+
layer?: LayerCapability;
|
|
417
|
+
/**
|
|
418
|
+
* Mount capability — add synthetic operations to the descriptor
|
|
419
|
+
* (SPEC §7.1). Typical uses: `/meta/openapi`, `/meta/health`.
|
|
420
|
+
*
|
|
421
|
+
* Loaded by `--use <plugin>`.
|
|
422
|
+
*/
|
|
423
|
+
mount?: MountCapability;
|
|
424
|
+
/**
|
|
425
|
+
* Envelope capability — declare request/response side-channel fields
|
|
426
|
+
* (SPEC §7.1 / §9.1). Loaded by `--use <plugin>`.
|
|
427
|
+
*
|
|
428
|
+
* A plugin may combine `envelope` with `layer` to both *declare* the
|
|
429
|
+
* fields it needs and *read/write* them in its layer function.
|
|
430
|
+
*/
|
|
431
|
+
envelope?: EnvelopeCapability;
|
|
432
|
+
};
|
|
433
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Project, SourceFile } from 'ts-morph';
|
|
2
|
+
|
|
3
|
+
/** Attempts ts-json-schema-generator first; falls back to morphFallback for inline/anonymous types. */
|
|
4
|
+
export declare function buildSchema(_project: Project, sf: SourceFile, typeText: string, tsconfig?: string): Promise<Record<string, unknown>>;
|
package/lib/types.d.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Logger } from 'pino';
|
|
2
|
+
|
|
3
|
+
export interface GeneratedSchemas {
|
|
4
|
+
metadata: {
|
|
5
|
+
namespace: string;
|
|
6
|
+
phase: string;
|
|
7
|
+
};
|
|
8
|
+
schemas: Record<string, {
|
|
9
|
+
input: Record<string, unknown>;
|
|
10
|
+
output: Record<string, unknown>;
|
|
11
|
+
hasCtx?: boolean;
|
|
12
|
+
}>;
|
|
13
|
+
}
|
|
14
|
+
export type ComposedSchemas = Record<string, {
|
|
15
|
+
input: Record<string, unknown>;
|
|
16
|
+
output: Record<string, unknown>;
|
|
17
|
+
hasCtx?: boolean;
|
|
18
|
+
}>;
|
|
19
|
+
export type ExportMode = {
|
|
20
|
+
type: 'named';
|
|
21
|
+
} | {
|
|
22
|
+
type: 'default';
|
|
23
|
+
} | {
|
|
24
|
+
type: 'named-object';
|
|
25
|
+
name: string;
|
|
26
|
+
};
|
|
27
|
+
export interface GenerateSchemasOptions {
|
|
28
|
+
sourceFile: string;
|
|
29
|
+
exportMode?: ExportMode;
|
|
30
|
+
namespace?: string;
|
|
31
|
+
phase?: string;
|
|
32
|
+
tsconfig?: string;
|
|
33
|
+
}
|
|
34
|
+
export interface PluginInput {
|
|
35
|
+
packages: Array<{
|
|
36
|
+
id: string;
|
|
37
|
+
schemas: ComposedSchemas;
|
|
38
|
+
importPath: string;
|
|
39
|
+
fns?: Record<string, (...args: unknown[]) => unknown>;
|
|
40
|
+
createClient?: (envelope: Record<string, unknown>) => Promise<unknown>;
|
|
41
|
+
}>;
|
|
42
|
+
outputDir: string;
|
|
43
|
+
options: Record<string, unknown>;
|
|
44
|
+
/**
|
|
45
|
+
* Shared structured logger (pino). Built once by the CLI and threaded through
|
|
46
|
+
* the pipeline + plugins. Always targets stderr or a file — never stdout —
|
|
47
|
+
* so the MCP stdio JSON-RPC channel stays clean. Plugins should fall back to
|
|
48
|
+
* a default stderr logger when this is absent.
|
|
49
|
+
*/
|
|
50
|
+
logger?: Logger;
|
|
51
|
+
}
|
|
52
|
+
export interface PluginOutput {
|
|
53
|
+
files: Array<{
|
|
54
|
+
path: string;
|
|
55
|
+
content: string;
|
|
56
|
+
}>;
|
|
57
|
+
postCommands?: string[];
|
|
58
|
+
}
|
|
59
|
+
export interface RunInput extends PluginInput {
|
|
60
|
+
signal?: AbortSignal;
|
|
61
|
+
}
|
|
62
|
+
export interface OutputPlugin {
|
|
63
|
+
id: string;
|
|
64
|
+
description: string;
|
|
65
|
+
optionsSchema?: Record<string, unknown>;
|
|
66
|
+
generate(input: PluginInput): PluginOutput | Promise<PluginOutput>;
|
|
67
|
+
run?(input: RunInput): Promise<void>;
|
|
68
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@adhd/apigen-core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"dependencies": {
|
|
5
|
+
"ts-morph": "^23.0.0",
|
|
6
|
+
"ts-json-schema-generator": "^2.3.0",
|
|
7
|
+
"pino": "10.3.1"
|
|
8
|
+
},
|
|
9
|
+
"main": "./index.js",
|
|
10
|
+
"module": "./index.mjs",
|
|
11
|
+
"typings": "./index.d.ts",
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public"
|
|
14
|
+
}
|
|
15
|
+
}
|