@adhd/apigen-core-client 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 +31 -0
- package/README.md +238 -0
- package/index.d.ts +14 -0
- package/index.js +1 -0
- package/index.mjs +1618 -0
- package/lib/apigen-core.d.ts +1 -0
- package/lib/compose-schemas.d.ts +31 -0
- package/lib/descriptor.d.ts +220 -0
- package/lib/extract-classes.d.ts +37 -0
- package/lib/extract.d.ts +43 -0
- package/lib/extraction-session.d.ts +97 -0
- package/lib/get-safety.d.ts +14 -0
- package/lib/param-defaults.d.ts +36 -0
- package/lib/plugin.d.ts +446 -0
- package/lib/schema-builders/map-set-tuple.d.ts +19 -0
- package/lib/schema-builders/morph-fallback.d.ts +13 -0
- package/lib/schema-builders/morph-walk.d.ts +59 -0
- package/lib/schema-builders/nominal.d.ts +95 -0
- package/lib/schema-builders/ts-json-schema.d.ts +65 -0
- package/lib/schema-builders/union.d.ts +74 -0
- package/lib/source-language.d.ts +81 -0
- package/lib/types.d.ts +87 -0
- package/package.json +17 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function apigenCore(): string;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { GeneratedSchemas, ComposedSchemas } from './types';
|
|
2
|
+
|
|
3
|
+
interface SlimMiddleware {
|
|
4
|
+
id: string;
|
|
5
|
+
envelope?: Record<string, unknown>;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Merges domain schemas with middleware envelope fields.
|
|
9
|
+
*
|
|
10
|
+
* The `data: {}` wrapper **property** is always present, even for zero-param
|
|
11
|
+
* functions, so `{"data": {}}` still validates for callers who send it out of
|
|
12
|
+
* habit or symmetry. FEAT-APIGEN-023: the wrapper is only listed in the outer
|
|
13
|
+
* `required` array when the function actually has ≥1 required domain param
|
|
14
|
+
* (`domainRequired.length > 0`) — mirroring the exact same condition already
|
|
15
|
+
* used for the nested `data` schema's own `required`. A truly zero-parameter
|
|
16
|
+
* function's published schema therefore does not force callers to send an
|
|
17
|
+
* empty `data: {}` (or the whole envelope, if no middleware requires anything
|
|
18
|
+
* else). Override a middleware with `false` to suppress its envelope
|
|
19
|
+
* contribution for a specific function [inv:false-suppresses-middleware].
|
|
20
|
+
*
|
|
21
|
+
* BUG-APIGEN-017: both the top-level (envelope + data) object and the nested
|
|
22
|
+
* `data` object are generated with `additionalProperties: false` so MCP hosts
|
|
23
|
+
* (and any other JSON-Schema-validating consumer) reject unknown parameters
|
|
24
|
+
* instead of silently discarding them.
|
|
25
|
+
*
|
|
26
|
+
* BUG-APIGEN-020: the top-level schema also carries a `description` that
|
|
27
|
+
* documents the `data` envelope + any transport-envelope fields — see
|
|
28
|
+
* {@link buildEnvelopeDescription}.
|
|
29
|
+
*/
|
|
30
|
+
export declare function composeSchemas(domainSchemas: GeneratedSchemas, middlewares: ReadonlyArray<SlimMiddleware>, overrides?: Record<string, Record<string, boolean>>): ComposedSchemas;
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A JSON-Schema-2020-12 document fragment.
|
|
3
|
+
*
|
|
4
|
+
* The apigen type IR **is** JSON Schema 2020-12 with `$defs`/`$ref` — there is
|
|
5
|
+
* no separate or abstract type model and no new IR (SPEC §4, §16). Named types,
|
|
6
|
+
* discriminated unions / enum-with-data / `Result`/`Option` (`oneOf` + a `const`
|
|
7
|
+
* tag + `$ref`), nominal/branded types (a named `$def`), and recursion (`$ref`)
|
|
8
|
+
* are all represented here faithfully — exactly what `ts-json-schema-generator`
|
|
9
|
+
* and `schemars` already emit.
|
|
10
|
+
*
|
|
11
|
+
* Conventions baked into this IR:
|
|
12
|
+
* - **Big-int / decimal wire convention:** 64-bit integers and decimals exceed
|
|
13
|
+
* JSON's `f64`, so they are **string-encoded** as `{ type: 'string', format:
|
|
14
|
+
* 'int64' }` (a serialization convention, not a schema-expressiveness gap).
|
|
15
|
+
* - **Optional extractor-derived hints** live under `x-apigen-*` keys (see
|
|
16
|
+
* {@link ApigenSchemaHints}) and exist only so codegen can emit idiomatic
|
|
17
|
+
* (vs accurate-but-verbose) clients; they are never required for correctness
|
|
18
|
+
* and are never sourced from a source annotation (Tenet 1).
|
|
19
|
+
*
|
|
20
|
+
* Modeled as an open record because a JSON Schema is, structurally, an arbitrary
|
|
21
|
+
* keyword object. This mirrors how `input`/`output` are already typed in
|
|
22
|
+
* {@link GeneratedSchemas} / {@link ComposedSchemas} (`Record<string, unknown>`).
|
|
23
|
+
*/
|
|
24
|
+
export type JSONSchema = Record<string, unknown> & {
|
|
25
|
+
/** Reusable type definitions referenced via `$ref` (`#/$defs/Name`). */
|
|
26
|
+
$defs?: Record<string, JSONSchema>;
|
|
27
|
+
/**
|
|
28
|
+
* Reusable type definitions under the draft-07 spelling, referenced via
|
|
29
|
+
* `$ref` (`#/definitions/Name`). `ts-json-schema-generator` — the only
|
|
30
|
+
* `$ref`/definitions producer in this codebase today — always emits this
|
|
31
|
+
* key, never `$defs`; both are recognized structurally by extract.ts's
|
|
32
|
+
* `hoistNestedDefs` / compose-schemas.ts (BUG-APIGEN-029) and by Ajv.
|
|
33
|
+
*/
|
|
34
|
+
definitions?: Record<string, JSONSchema>;
|
|
35
|
+
/** Reference to a definition, e.g. `#/$defs/User` (enables recursion). */
|
|
36
|
+
$ref?: string;
|
|
37
|
+
} & ApigenSchemaHints;
|
|
38
|
+
/**
|
|
39
|
+
* Optional, **extractor-derived** codegen hints carried inline on a
|
|
40
|
+
* {@link JSONSchema} (SPEC §4). These are advisory: a plugin MAY use them to
|
|
41
|
+
* emit idiomatic clients (e.g. a real enum, a branded type) and a codegen MAY
|
|
42
|
+
* warn on an unresolved generic — but correctness never depends on them, and
|
|
43
|
+
* they are never required. They are computed by the extractor, never written by
|
|
44
|
+
* a human in source (Tenet 1).
|
|
45
|
+
*/
|
|
46
|
+
export interface ApigenSchemaHints {
|
|
47
|
+
/**
|
|
48
|
+
* Marks a `$def` that originated from a nominal / branded type. Validation
|
|
49
|
+
* deliberately does NOT enforce nominality — on the wire a branded type *is*
|
|
50
|
+
* its base type — but codegen can re-introduce the brand for ergonomics.
|
|
51
|
+
*/
|
|
52
|
+
'x-apigen-nominal'?: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* How an enum-like type should be represented in idiomatic codegen, e.g. a
|
|
55
|
+
* native enum vs a string-literal union. Advisory only.
|
|
56
|
+
*/
|
|
57
|
+
'x-apigen-enum-repr'?: 'enum' | 'union' | string;
|
|
58
|
+
/**
|
|
59
|
+
* Fidelity of this schema fragment relative to the source type:
|
|
60
|
+
* - `'full'` — the schema captures the source type without loss.
|
|
61
|
+
* - `'lossy'` — the source type could not be fully represented (the only
|
|
62
|
+
* true residual is generic *factoring*: an unconstrained generic operation
|
|
63
|
+
* isn't serializable, so it is out of scope by physics). Codegen MAY warn.
|
|
64
|
+
*
|
|
65
|
+
* Optional; absence means `'full'`.
|
|
66
|
+
*/
|
|
67
|
+
fidelity?: 'full' | 'lossy';
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* A casing-neutral name segment (SPEC §4/§5).
|
|
71
|
+
*
|
|
72
|
+
* Identity is carried by the tokenized `words`; the original `raw` spelling is
|
|
73
|
+
* preserved so a same-host plugin can reproduce it, but every transport derives
|
|
74
|
+
* its own casing from `words` via `@adhd/apigen-naming` (kebab for HTTP/CLI,
|
|
75
|
+
* `_`-joined for MCP, Pascal for gRPC). Casing is therefore per-plugin, never
|
|
76
|
+
* baked into the descriptor.
|
|
77
|
+
*/
|
|
78
|
+
export interface Segment {
|
|
79
|
+
/** Original spelling as it appeared in source, e.g. `'humanizeBytes'`. */
|
|
80
|
+
raw: string;
|
|
81
|
+
/** Tokenized, lower-cased words, e.g. `['humanize', 'bytes']`. */
|
|
82
|
+
words: string[];
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Language-tagged textual rendering of a type — optional same-host *sugar*
|
|
86
|
+
* (SPEC §4). For a TypeScript host this is the literal TS source of the type;
|
|
87
|
+
* non-host targets ignore it and rely on {@link JSONSchema} (`input`/`output`)
|
|
88
|
+
* instead. `null` when no textual form is available/relevant.
|
|
89
|
+
*/
|
|
90
|
+
export interface TypeText {
|
|
91
|
+
/** Language tag for `input`/`output` text, e.g. `'ts'`. */
|
|
92
|
+
lang: string;
|
|
93
|
+
/** Textual rendering of the input/params type. */
|
|
94
|
+
input: string;
|
|
95
|
+
/** Textual rendering of the output/return type (unwrapped). */
|
|
96
|
+
output: string;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Classification of an exported binding (SPEC §4).
|
|
100
|
+
*
|
|
101
|
+
* - `'action'` — a callable export (function declaration, or an
|
|
102
|
+
* arrow/const function). Served by invoking it.
|
|
103
|
+
* - `'query'` — a **serializable-data** const (primitive or plain
|
|
104
|
+
* serializable object/array — no functions / non-serializable values). Served
|
|
105
|
+
* **live**: the descriptor carries the const's *type* (schema), not its value,
|
|
106
|
+
* so env-/compute-dependent consts are never stale-at-extract.
|
|
107
|
+
* - `'constructor'` — a class constructor (class export; see SPEC §10).
|
|
108
|
+
* - `'instance-method'` — a method on an exported class instance (SPEC §10).
|
|
109
|
+
*
|
|
110
|
+
* A non-serializable, non-callable export is **skipped + warned**, never
|
|
111
|
+
* emitted as an operation.
|
|
112
|
+
*/
|
|
113
|
+
export type OperationKind = 'action' | 'query' | 'constructor' | 'instance-method';
|
|
114
|
+
/**
|
|
115
|
+
* The canonical operation descriptor — the neutral contract every extractor
|
|
116
|
+
* emits and every plugin consumes (SPEC §4).
|
|
117
|
+
*
|
|
118
|
+
* One `Operation` corresponds to one selected export (an `action`, a live
|
|
119
|
+
* `query`, or a class member per §10). The descriptor is host-agnostic: a
|
|
120
|
+
* TypeScript extractor, a Python extractor, and a Rust extractor all emit the
|
|
121
|
+
* same shape, and a single plugin can project a merged set of `Operation`s into
|
|
122
|
+
* any transport.
|
|
123
|
+
*
|
|
124
|
+
* @remarks
|
|
125
|
+
* The type IR for `input`/`output`/`envelope` is JSON Schema 2020-12 with
|
|
126
|
+
* `$defs`/`$ref` (see {@link JSONSchema}) — there is no separate IR.
|
|
127
|
+
*/
|
|
128
|
+
export interface Operation {
|
|
129
|
+
/**
|
|
130
|
+
* The canonical fully-qualified slug and cross-plugin reference key, derived
|
|
131
|
+
* purely from `namespace`/`path`, e.g. `'transform/humanize/humanize-bytes'`.
|
|
132
|
+
* Globally unique within a merged descriptor and never re-cased.
|
|
133
|
+
*
|
|
134
|
+
* **Deterministic, NOT refactor-stable.** Because `id` is a pure function of
|
|
135
|
+
* `namespace`/`path`, the same source always yields the same `id` — but moving
|
|
136
|
+
* or renaming a file/export re-mints it (and thus breaks any pinned
|
|
137
|
+
* `--exclude` id or generated client that referenced the old slug). This is
|
|
138
|
+
* accepted and documented; refactor-stability is an explicit **non-goal**. It
|
|
139
|
+
* is deliberately **not** papered over with a source `@id` annotation, which
|
|
140
|
+
* Tenet 1 forbids.
|
|
141
|
+
*/
|
|
142
|
+
id: string;
|
|
143
|
+
/**
|
|
144
|
+
* The owning language runtime / host for this operation, e.g. `'ts'`,
|
|
145
|
+
* `'py'`, `'rust'`. Identifies which host's extractor produced it and which
|
|
146
|
+
* host can serve it same-process. Polyglot descriptors mix hosts.
|
|
147
|
+
*/
|
|
148
|
+
host: string;
|
|
149
|
+
/**
|
|
150
|
+
* The package segment — sourced from `--namespace` or the tsconfig folder
|
|
151
|
+
* (SPEC §4/§5). Casing-neutral; transports derive their own casing from
|
|
152
|
+
* {@link Segment.words}.
|
|
153
|
+
*/
|
|
154
|
+
namespace: Segment;
|
|
155
|
+
/**
|
|
156
|
+
* The hierarchical identity path: file → export… (SPEC §4/§5). For example a
|
|
157
|
+
* named export is `[file, name]`; a single default function is `[file]`; a
|
|
158
|
+
* default *object* is `[file, 'default', ...keys]` recursing into nested
|
|
159
|
+
* props. `index.*` drops its file segment. Each element is casing-neutral.
|
|
160
|
+
*/
|
|
161
|
+
path: Segment[];
|
|
162
|
+
/** Classification of the underlying export — see {@link OperationKind}. */
|
|
163
|
+
kind: OperationKind;
|
|
164
|
+
/** True if the export is async (returns a `Promise`). */
|
|
165
|
+
async: boolean;
|
|
166
|
+
/**
|
|
167
|
+
* True if the export returns an `AsyncIterable` / `Generator` / `Stream`
|
|
168
|
+
* (streaming is implemented in v2, SPEC §11). The `output` schema describes
|
|
169
|
+
* the per-chunk element type with streaming unwrapped.
|
|
170
|
+
*/
|
|
171
|
+
streaming: boolean;
|
|
172
|
+
/**
|
|
173
|
+
* Idempotent / no-side-effects hint (SPEC §4/§5).
|
|
174
|
+
*
|
|
175
|
+
* **Defaults from `kind`** (`query` → `true`, `action` → `false`) and is
|
|
176
|
+
* **overridable at projection time via config** (`--opt http.verb.<id>=GET`
|
|
177
|
+
* or `apigen.config`), never via a source annotation (Tenet 1). Drives the
|
|
178
|
+
* HTTP verb + cacheability (safe → GET, unsafe → POST) and gRPC
|
|
179
|
+
* idempotency-level (SPEC §5), decoupling the wire method from `kind`.
|
|
180
|
+
*/
|
|
181
|
+
safe: boolean;
|
|
182
|
+
/**
|
|
183
|
+
* The params object as JSON Schema 2020-12 (see {@link JSONSchema}).
|
|
184
|
+
*
|
|
185
|
+
* This is the operation's input type directly: the `ctx` first param is
|
|
186
|
+
* excluded (by name match only, per the `ctx-name-only` invariant) and the
|
|
187
|
+
* middleware `data`-wrapper is **dissolved** — `input` is the bare domain
|
|
188
|
+
* params object, not the composed envelope. The request-side envelope lives
|
|
189
|
+
* separately in `envelope`.
|
|
190
|
+
*/
|
|
191
|
+
input: JSONSchema;
|
|
192
|
+
/**
|
|
193
|
+
* The return type as JSON Schema 2020-12 (see {@link JSONSchema}), with
|
|
194
|
+
* `Promise<T>` and stream wrappers **unwrapped to `T`**. For a streaming
|
|
195
|
+
* operation this is the per-chunk element type.
|
|
196
|
+
*/
|
|
197
|
+
output: JSONSchema;
|
|
198
|
+
/**
|
|
199
|
+
* The effective request-side envelope as JSON Schema 2020-12 — the
|
|
200
|
+
* middleware-contributed side-channel for this operation (e.g. a `session`
|
|
201
|
+
* field), merged across the active middleware with overrides applied. Empty
|
|
202
|
+
* (no envelope fields) when no middleware contributes to this operation.
|
|
203
|
+
*/
|
|
204
|
+
envelope: JSONSchema;
|
|
205
|
+
/**
|
|
206
|
+
* Optional language-tagged textual type rendering (same-host sugar). `null`
|
|
207
|
+
* when unavailable; non-host targets ignore it. See {@link TypeText}.
|
|
208
|
+
*/
|
|
209
|
+
typeText: TypeText | null;
|
|
210
|
+
/**
|
|
211
|
+
* True when the underlying export's first parameter is named `ctx`
|
|
212
|
+
* ([inv:ctx-name-only] — matched by name only, never by type). `ctx` is
|
|
213
|
+
* excluded from `input` like every other framework param, but this flag
|
|
214
|
+
* lets a runtime dispatcher re-inject it as the first call argument
|
|
215
|
+
* (BUG-APIGEN-001) — without it a caller with no session middleware would
|
|
216
|
+
* have its first REAL domain arg land in the `ctx` slot. Only meaningful
|
|
217
|
+
* for `kind: 'action'`; absent (`undefined`) for `query` and other kinds.
|
|
218
|
+
*/
|
|
219
|
+
hasCtx?: boolean;
|
|
220
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { ExtractionSession } from './extraction-session';
|
|
2
|
+
import { Operation } from './descriptor';
|
|
3
|
+
|
|
4
|
+
export interface ExtractClassesOptions {
|
|
5
|
+
/** Absolute path to the TypeScript (or JavaScript) source file. */
|
|
6
|
+
sourceFile: string;
|
|
7
|
+
/**
|
|
8
|
+
* Namespace segment (from `--namespace` or tsconfig folder). Casing-neutral
|
|
9
|
+
* words are derived from the raw string. Defaults to `''`.
|
|
10
|
+
*/
|
|
11
|
+
namespace?: string;
|
|
12
|
+
/** Absolute path to a tsconfig.json for type resolution. Optional. */
|
|
13
|
+
tsconfig?: string;
|
|
14
|
+
/**
|
|
15
|
+
* When true, extract constructor + instance-method ops in addition to static
|
|
16
|
+
* method ops. Off by default (opt-in per SPEC §10).
|
|
17
|
+
*/
|
|
18
|
+
includeInstances?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Optional per-run shared cache (see `createExtractionSession` in
|
|
21
|
+
* `@adhd/apigen-core-client). When absent, a private session is created and
|
|
22
|
+
* disposed before returning (previous behaviour, no retention).
|
|
23
|
+
*/
|
|
24
|
+
session?: ExtractionSession;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Walks `opts.sourceFile` and emits canonical `Operation[]` descriptors for
|
|
28
|
+
* all exported class members per SPEC §10.
|
|
29
|
+
*
|
|
30
|
+
* Static methods are always extracted (they are stateless function-shaped ops).
|
|
31
|
+
* Constructor + instance methods require `opts.includeInstances = true`.
|
|
32
|
+
*
|
|
33
|
+
* @param opts - Extraction options.
|
|
34
|
+
* @returns Resolved array of canonical operations (static ops + optionally
|
|
35
|
+
* constructor + instance-method ops).
|
|
36
|
+
*/
|
|
37
|
+
export declare function extractClasses(opts: ExtractClassesOptions): Promise<Operation[]>;
|
package/lib/extract.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { ExtractionSession } from './extraction-session';
|
|
2
|
+
import { Operation } from './descriptor';
|
|
3
|
+
|
|
4
|
+
export interface ExtractOptions {
|
|
5
|
+
/** Absolute path to the TypeScript (or JavaScript) source file. */
|
|
6
|
+
sourceFile: string;
|
|
7
|
+
/**
|
|
8
|
+
* Namespace segment (from `--namespace` or tsconfig folder). Casing-neutral
|
|
9
|
+
* words are derived from the raw string. Defaults to `''`.
|
|
10
|
+
*/
|
|
11
|
+
namespace?: string;
|
|
12
|
+
/** Absolute path to a tsconfig.json for type resolution. Optional. */
|
|
13
|
+
tsconfig?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Optional per-run shared cache (see {@link createExtractionSession}).
|
|
16
|
+
* When supplied, the ts-morph Project, built schema generators, and computed
|
|
17
|
+
* schemas are shared with every other extraction call in the same run — and
|
|
18
|
+
* released together by `session.dispose()`. When absent, a private session is
|
|
19
|
+
* created and disposed before returning (previous behaviour, no retention).
|
|
20
|
+
*/
|
|
21
|
+
session?: ExtractionSession;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Walks `opts.sourceFile` and emits canonical `Operation[]` descriptors.
|
|
25
|
+
*
|
|
26
|
+
* Handles all six shapes in the export-shape matrix. Each operation is named
|
|
27
|
+
* by the **exported symbol** in source — never by position or internal name.
|
|
28
|
+
*
|
|
29
|
+
* @param opts - Extraction options.
|
|
30
|
+
* @returns Resolved array of canonical operations.
|
|
31
|
+
*/
|
|
32
|
+
export declare function extract(opts: ExtractOptions): Promise<Operation[]>;
|
|
33
|
+
/**
|
|
34
|
+
* Tokenises a camelCase / PascalCase / kebab-case / snake_case identifier into
|
|
35
|
+
* lower-cased words. Used to build casing-neutral {@link Segment} records.
|
|
36
|
+
*
|
|
37
|
+
* Examples:
|
|
38
|
+
* 'humanizeBytes' → ['humanize', 'bytes']
|
|
39
|
+
* 'HTMLParser' → ['html', 'parser']
|
|
40
|
+
* 'my-util' → ['my', 'util']
|
|
41
|
+
* 'SOME_CONST' → ['some', 'const']
|
|
42
|
+
*/
|
|
43
|
+
export declare function tokenize(raw: string): string[];
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { Project, SourceFile } from 'ts-morph';
|
|
2
|
+
|
|
3
|
+
/** Counters proving how much work a session actually did — used by perf regression tests. */
|
|
4
|
+
export interface ISessionStats {
|
|
5
|
+
/** ts-morph Projects constructed (one per distinct tsconfig per session). */
|
|
6
|
+
projectsBuilt: number;
|
|
7
|
+
/** ts-json-schema-generator generators (full TS programs) constructed. */
|
|
8
|
+
generatorsBuilt: number;
|
|
9
|
+
/**
|
|
10
|
+
* buildSchema calls answered from the session schema cache — includes both
|
|
11
|
+
* a call that found an already-RESOLVED entry and a call that joined an
|
|
12
|
+
* already-IN-FLIGHT promise for the same key (BUG-APIGEN-CORE-003:
|
|
13
|
+
* in-flight dedup — see `schemaCache`'s doc comment below).
|
|
14
|
+
*/
|
|
15
|
+
schemaCacheHits: number;
|
|
16
|
+
/** buildSchema calls that had to start a new computation (cache miss). */
|
|
17
|
+
schemaCacheMisses: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* A per-run cache shared across `extract()` / `extractClasses()` / the
|
|
21
|
+
* orchestrator. Create one per logical run with
|
|
22
|
+
* {@link createExtractionSession}, pass it to every extraction call in that
|
|
23
|
+
* run, and `dispose()` it when the run's outputs have been consumed.
|
|
24
|
+
*
|
|
25
|
+
* Passing a session is optional everywhere — calls without one create a
|
|
26
|
+
* private session internally and dispose it before returning, so existing
|
|
27
|
+
* consumers are unaffected.
|
|
28
|
+
*/
|
|
29
|
+
export interface ExtractionSession {
|
|
30
|
+
/** Drop every cached Project / generator / schema so the run's memory is collectible. */
|
|
31
|
+
dispose(): void;
|
|
32
|
+
/** Live work counters (see {@link ISessionStats}). */
|
|
33
|
+
readonly stats: ISessionStats;
|
|
34
|
+
}
|
|
35
|
+
/** Cached built generator entry: `version` is the file's `mtimeMs:size` snapshot. */
|
|
36
|
+
export type BuiltGenerator = {
|
|
37
|
+
createSchema(type: string): unknown;
|
|
38
|
+
};
|
|
39
|
+
type GeneratorEntry = {
|
|
40
|
+
version: string;
|
|
41
|
+
gen: BuiltGenerator;
|
|
42
|
+
};
|
|
43
|
+
declare const INTERNAL: unique symbol;
|
|
44
|
+
/** Full internal surface — package-private (not exported from the index). */
|
|
45
|
+
export interface InternalExtractionSession extends ExtractionSession {
|
|
46
|
+
[INTERNAL]: true;
|
|
47
|
+
/** One ts-morph Project per distinct tsconfig path ('' for none). */
|
|
48
|
+
projectFor(tsconfig?: string): Project;
|
|
49
|
+
/** Add-or-reuse the SourceFile for `filePath` in the session's Project. */
|
|
50
|
+
sourceFileFor(filePath: string, tsconfig?: string): SourceFile;
|
|
51
|
+
/**
|
|
52
|
+
* Memoized buildSchema results, key = `${sfPath}\0${tsconfig ?? ''}\0${typeText}`.
|
|
53
|
+
*
|
|
54
|
+
* BUG-APIGEN-CORE-003: values are the in-flight/resolved `Promise`, not the
|
|
55
|
+
* resolved value itself, and are stored SYNCHRONOUSLY (before the underlying
|
|
56
|
+
* computation is awaited) — this is what makes concurrent identical requests
|
|
57
|
+
* (e.g. `morph-walk.ts`'s `Promise.all(members.map(...))` over union variants
|
|
58
|
+
* that share a nested type like `SharedArrayBuffer`) share one computation
|
|
59
|
+
* instead of each independently missing the cache and recomputing. See
|
|
60
|
+
* `buildSchema()` in `schema-builders/ts-json-schema.ts` for the write side.
|
|
61
|
+
*/
|
|
62
|
+
readonly schemaCache: Map<string, Promise<Record<string, unknown>>>;
|
|
63
|
+
/** Memoized per-SourceFile import-alias maps (extractScalarAliases). */
|
|
64
|
+
readonly aliasCache: WeakMap<SourceFile, ReadonlyMap<string, string>>;
|
|
65
|
+
/** Memoized per-SourceFile zod-import check (sourceFileHasZodImport). */
|
|
66
|
+
readonly zodImportCache: WeakMap<SourceFile, boolean>;
|
|
67
|
+
/** One built generator per `${path}\0${tsconfig ?? ''}` — latest version only. */
|
|
68
|
+
readonly generatorCache: Map<string, GeneratorEntry>;
|
|
69
|
+
/** Memoized `mtimeMs:size` snapshot per file path (a run is a snapshot). */
|
|
70
|
+
statVersion(pathStr: string): string;
|
|
71
|
+
}
|
|
72
|
+
/** Compute a file's `mtimeMs:size` version string (uncached). */
|
|
73
|
+
export declare function fileVersion(pathStr: string): string;
|
|
74
|
+
/** Persistent-schema accessor for buildSchema (package-private).
|
|
75
|
+
*
|
|
76
|
+
* Computes a composite version stamp that includes the entry file's version
|
|
77
|
+
* AND the versions of all referenced files tracked by the persistent Project
|
|
78
|
+
* tier. Previously only the entry file's version was checked, so a type
|
|
79
|
+
* imported from another file that changed without the entry file changing was
|
|
80
|
+
* not detected (DEBT-APIGEN-CACHE-001).
|
|
81
|
+
*/
|
|
82
|
+
export declare function persistentSchemasFor(sfPath: string, tsconfig: string | undefined, version: string): Map<string, Record<string, unknown>>;
|
|
83
|
+
/** Drop the process-lifetime Project + schema caches (tests / explicit memory reclaim). */
|
|
84
|
+
export declare function clearPersistentProjectCache(): void;
|
|
85
|
+
/**
|
|
86
|
+
* Create a fresh {@link ExtractionSession}.
|
|
87
|
+
*
|
|
88
|
+
* One session = one run = one snapshot of the source tree. Reusing a session
|
|
89
|
+
* across file edits is unsupported by design (invalidation is "new session").
|
|
90
|
+
*/
|
|
91
|
+
export declare function createExtractionSession(): ExtractionSession;
|
|
92
|
+
/**
|
|
93
|
+
* Recover the internal surface from a public {@link ExtractionSession}.
|
|
94
|
+
* Package-private — deliberately NOT exported from the package index.
|
|
95
|
+
*/
|
|
96
|
+
export declare function internalSession(session: ExtractionSession): InternalExtractionSession;
|
|
97
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structural check over a bare domain-input JSON Schema — the exact shape
|
|
3
|
+
* carried as `Operation.input` (and, unchanged, as `GeneratedSchemas.schemas
|
|
4
|
+
* [fn].input` — orchestrator.ts's Step 5 passes `op.input` straight through):
|
|
5
|
+
* `{ type: 'object', properties, required }`.
|
|
6
|
+
*
|
|
7
|
+
* Returns `true` iff every declared property is a "properly typed primitive"
|
|
8
|
+
* (`string`/`number`/`boolean`/`integer`, optionally unioned with `null`) —
|
|
9
|
+
* including the zero-property case (vacuously true). Absence of a
|
|
10
|
+
* `properties` key at all (as opposed to an explicit empty object) is NOT the
|
|
11
|
+
* same as zero params — it means the input shape was never populated, so this
|
|
12
|
+
* returns `false` rather than assume eligibility.
|
|
13
|
+
*/
|
|
14
|
+
export declare function isPrimitiveOnlyInputSchema(input: Record<string, unknown> | undefined | null): boolean;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Node, ParameterDeclaration } from 'ts-morph';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Extracts the raw source text of a parameter's default value.
|
|
5
|
+
*
|
|
6
|
+
* @param paramDecl - The parameter's own declaration, when resolvable (may be
|
|
7
|
+
* `null`/`undefined` for parameters reached only through a `Signature`
|
|
8
|
+
* whose declaration could not be narrowed to a `ParameterDeclaration`).
|
|
9
|
+
* @param paramName - The parameter's name, used to match a JSDoc `@param` tag.
|
|
10
|
+
* @param jsDocSource - The enclosing function-like node (FunctionDeclaration,
|
|
11
|
+
* VariableStatement, ExportAssignment, …) whose leading JSDoc comment may
|
|
12
|
+
* carry a bracketed default for this parameter.
|
|
13
|
+
* @returns The raw default-value text (e.g. `"'auto'"`, `"0"`, `"false"`), or
|
|
14
|
+
* `undefined` when no default could be found.
|
|
15
|
+
*/
|
|
16
|
+
export declare function extractParamDefault(paramDecl: ParameterDeclaration | null | undefined, paramName: string, jsDocSource?: Node | null): string | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* Coerces a raw TS/JSDoc default-value literal into a properly-typed JSON
|
|
19
|
+
* value so it can be emitted verbatim as a JSON-Schema `default`.
|
|
20
|
+
*
|
|
21
|
+
* Handles quoted string literals (`'auto'`, `"auto"`, `` `auto` ``), the
|
|
22
|
+
* `true`/`false`/`null` keywords, numeric literals, and falls back to
|
|
23
|
+
* `JSON.parse` (array/object literals) before giving up and returning the
|
|
24
|
+
* trimmed raw text (e.g. an enum-member reference apigen can't evaluate
|
|
25
|
+
* statically).
|
|
26
|
+
*/
|
|
27
|
+
export declare function coerceDefaultLiteral(raw: string): unknown;
|
|
28
|
+
/**
|
|
29
|
+
* Applies an extracted default onto a built parameter schema in place:
|
|
30
|
+
* sets the native JSON-Schema `default` keyword and appends a human-readable
|
|
31
|
+
* `(default: <value>)` note to `description` (preserving any existing text).
|
|
32
|
+
*
|
|
33
|
+
* A no-op when the coerced value is `undefined` (an explicit `= undefined`
|
|
34
|
+
* initializer carries no useful default to advertise).
|
|
35
|
+
*/
|
|
36
|
+
export declare function applyParamDefault(schema: Record<string, unknown>, rawDefault: string): void;
|