@forge-ts/api 0.3.0 → 0.4.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/dist/index.d.ts CHANGED
@@ -48,6 +48,12 @@ interface SDKType {
48
48
  *
49
49
  * @param symbols - The symbols produced by the core AST walker.
50
50
  * @returns An array of {@link SDKType} objects for public-facing type definitions.
51
+ * @example
52
+ * ```typescript
53
+ * import { extractSDKTypes } from "@forge-ts/api";
54
+ * const sdkTypes = extractSDKTypes(symbols);
55
+ * console.log(sdkTypes.length); // number of public SDK types
56
+ * ```
51
57
  * @public
52
58
  */
53
59
  declare function extractSDKTypes(symbols: ForgeSymbol[]): SDKType[];
@@ -67,10 +73,18 @@ declare function extractSDKTypes(symbols: ForgeSymbol[]): SDKType[];
67
73
  *
68
74
  * @param config - The resolved {@link ForgeConfig}.
69
75
  * @param sdkTypes - SDK types to include as component schemas.
76
+ * @param symbols - Raw symbols used to extract HTTP route paths from `@route` tags.
70
77
  * @returns An {@link OpenAPIDocument} object.
78
+ * @example
79
+ * ```typescript
80
+ * import { generateOpenAPISpec } from "@forge-ts/api";
81
+ * import { extractSDKTypes } from "@forge-ts/api";
82
+ * const spec = generateOpenAPISpec(config, extractSDKTypes(symbols), symbols);
83
+ * console.log(spec.openapi); // "3.2.0"
84
+ * ```
71
85
  * @public
72
86
  */
73
- declare function generateOpenAPISpec(config: ForgeConfig, sdkTypes: SDKType[]): OpenAPIDocument;
87
+ declare function generateOpenAPISpec(config: ForgeConfig, sdkTypes: SDKType[], symbols?: ForgeSymbol[]): OpenAPIDocument;
74
88
 
75
89
  /**
76
90
  * A single entry in the generated API reference.
@@ -130,6 +144,12 @@ interface ReferenceEntry {
130
144
  *
131
145
  * @param symbols - All symbols from the AST walker.
132
146
  * @returns An array of {@link ReferenceEntry} objects sorted by name.
147
+ * @example
148
+ * ```typescript
149
+ * import { buildReference } from "@forge-ts/api";
150
+ * const entries = buildReference(symbols);
151
+ * console.log(entries[0].name); // first symbol name, alphabetically
152
+ * ```
133
153
  * @public
134
154
  */
135
155
  declare function buildReference(symbols: ForgeSymbol[]): ReferenceEntry[];
@@ -148,6 +168,12 @@ declare function buildReference(symbols: ForgeSymbol[]): ReferenceEntry[];
148
168
  * @param signature - A TypeScript type signature string, e.g. `"string"`, `"number[]"`,
149
169
  * `"string | number"`, `"Record<string, boolean>"`.
150
170
  * @returns An OpenAPI schema object.
171
+ * @example
172
+ * ```typescript
173
+ * import { signatureToSchema } from "@forge-ts/api";
174
+ * const schema = signatureToSchema("string[]");
175
+ * // { type: "array", items: { type: "string" } }
176
+ * ```
151
177
  * @public
152
178
  */
153
179
  declare function signatureToSchema(signature: string): OpenAPISchemaObject;
@@ -159,7 +185,6 @@ declare function signatureToSchema(signature: string): OpenAPISchemaObject;
159
185
  * OpenAPI 3.2 document and a structured API reference.
160
186
  *
161
187
  * @packageDocumentation
162
- * @public
163
188
  */
164
189
 
165
190
  /**
@@ -167,7 +192,14 @@ declare function signatureToSchema(signature: string): OpenAPISchemaObject;
167
192
  *
168
193
  * @param config - The resolved {@link ForgeConfig} for the project.
169
194
  * @returns A {@link ForgeResult} with success/failure and any diagnostics.
195
+ * @example
196
+ * ```typescript
197
+ * import { generateApi } from "@forge-ts/api";
198
+ * const result = await generateApi(config);
199
+ * console.log(result.success); // true if spec was written successfully
200
+ * ```
170
201
  * @public
202
+ * @packageDocumentation
171
203
  */
172
204
  declare function generateApi(config: ForgeConfig): Promise<ForgeResult>;
173
205
 
package/dist/index.js CHANGED
@@ -61,7 +61,7 @@ function splitUnion(signature) {
61
61
  }
62
62
 
63
63
  // src/openapi.ts
64
- function generateOpenAPISpec(config, sdkTypes) {
64
+ function generateOpenAPISpec(config, sdkTypes, symbols = []) {
65
65
  const visibleTypes = sdkTypes.filter(
66
66
  (t) => t.visibility !== Visibility.Internal && t.visibility !== Visibility.Private
67
67
  );
@@ -71,11 +71,12 @@ function generateOpenAPISpec(config, sdkTypes) {
71
71
  }
72
72
  const tagNames = deriveTagNames(visibleTypes);
73
73
  const tags = tagNames.map((name) => ({ name }));
74
+ const paths = extractPaths(symbols);
74
75
  return {
75
76
  openapi: "3.2.0",
76
77
  info: buildInfo(config),
77
78
  ...tags.length > 0 ? { tags } : {},
78
- paths: {},
79
+ paths,
79
80
  components: { schemas }
80
81
  };
81
82
  }
@@ -159,6 +160,67 @@ function buildTypeAliasSchema(sdkType) {
159
160
  const rawType = equalsIndex !== -1 ? sdkType.signature.slice(equalsIndex + 1).trim() : sdkType.signature;
160
161
  return signatureToSchema(rawType);
161
162
  }
163
+ function extractPaths(symbols) {
164
+ const paths = {};
165
+ for (const symbol of symbols) {
166
+ const routeTags = symbol.documentation?.tags?.route ?? [];
167
+ for (const routeTag of routeTags) {
168
+ const match = routeTag.match(
169
+ /^(GET|POST|PUT|DELETE|PATCH|OPTIONS|HEAD)\s+(\S+)/i
170
+ );
171
+ if (!match) continue;
172
+ const method = match[1].toLowerCase();
173
+ const path = match[2];
174
+ if (!paths[path]) {
175
+ paths[path] = {};
176
+ }
177
+ const fileBasename = symbol.filePath.split("/").pop()?.replace(/\.ts$/, "") ?? "default";
178
+ const operation = {
179
+ operationId: symbol.name,
180
+ summary: symbol.documentation?.summary,
181
+ description: symbol.documentation?.summary,
182
+ tags: [fileBasename]
183
+ };
184
+ const pathParams = [...path.matchAll(/\{(\w+)\}/g)].map((m) => m[1]);
185
+ const parameters = [];
186
+ for (const paramName of pathParams) {
187
+ const paramDoc = symbol.documentation?.params?.find(
188
+ (p) => p.name === paramName
189
+ );
190
+ parameters.push({
191
+ name: paramName,
192
+ in: "path",
193
+ required: true,
194
+ description: paramDoc?.description ?? `The ${paramName} parameter`,
195
+ schema: { type: "string" }
196
+ });
197
+ }
198
+ const nonPathParams = (symbol.documentation?.params ?? []).filter(
199
+ (p) => !pathParams.includes(p.name)
200
+ );
201
+ for (const param of nonPathParams) {
202
+ parameters.push({
203
+ name: param.name,
204
+ in: "query",
205
+ description: param.description,
206
+ schema: { type: "string" }
207
+ });
208
+ }
209
+ if (parameters.length > 0) {
210
+ operation.parameters = parameters;
211
+ }
212
+ if (symbol.documentation?.returns) {
213
+ operation.responses = {
214
+ "200": {
215
+ description: symbol.documentation.returns.description
216
+ }
217
+ };
218
+ }
219
+ paths[path][method] = operation;
220
+ }
221
+ }
222
+ return paths;
223
+ }
162
224
  function deriveTagNames(types) {
163
225
  const seen = /* @__PURE__ */ new Set();
164
226
  for (const t of types) {
@@ -266,7 +328,7 @@ async function generateApi(config) {
266
328
  const walker = createWalker(config);
267
329
  const symbols = walker.walk();
268
330
  const sdkTypes = extractSDKTypes(symbols);
269
- const spec = generateOpenAPISpec(config, sdkTypes);
331
+ const spec = generateOpenAPISpec(config, sdkTypes, symbols);
270
332
  await mkdir(dirname(config.api.openapiPath), { recursive: true });
271
333
  await writeFile(config.api.openapiPath, JSON.stringify(spec, null, 2), "utf8");
272
334
  return {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/openapi.ts","../src/schema-mapper.ts","../src/reference.ts","../src/sdk-extractor.ts","../src/index.ts"],"sourcesContent":["import type {\n\tForgeConfig,\n\tOpenAPIDocument,\n\tOpenAPIInfoObject,\n\tOpenAPISchemaObject,\n} from \"@forge-ts/core\";\nimport { Visibility } from \"@forge-ts/core\";\nimport { signatureToSchema } from \"./schema-mapper.js\";\nimport type { SDKProperty, SDKType } from \"./sdk-extractor.js\";\n\nexport type { OpenAPIDocument };\n\n/**\n * Generates a production-quality OpenAPI 3.2 document from the extracted SDK\n * types.\n *\n * The document is populated with:\n * - An `info` block sourced from the config or reasonable defaults.\n * - A `components.schemas` section with one schema per exported type.\n * - `tags` derived from unique source file paths (grouping by file).\n * - Visibility filtering: `@internal` symbols are never emitted.\n *\n * HTTP paths are not yet emitted (`paths` is always `{}`); route extraction\n * will be added in a future release.\n *\n * @param config - The resolved {@link ForgeConfig}.\n * @param sdkTypes - SDK types to include as component schemas.\n * @returns An {@link OpenAPIDocument} object.\n * @public\n */\nexport function generateOpenAPISpec(config: ForgeConfig, sdkTypes: SDKType[]): OpenAPIDocument {\n\t// Visibility filtering: never emit @internal or @private symbols.\n\tconst visibleTypes = sdkTypes.filter(\n\t\t(t) => t.visibility !== Visibility.Internal && t.visibility !== Visibility.Private,\n\t);\n\n\tconst schemas: Record<string, OpenAPISchemaObject> = {};\n\tfor (const t of visibleTypes) {\n\t\tschemas[t.name] = buildSchema(t);\n\t}\n\n\t// Derive tags from unique source files (basename without extension).\n\tconst tagNames = deriveTagNames(visibleTypes);\n\tconst tags = tagNames.map((name) => ({ name }));\n\n\treturn {\n\t\topenapi: \"3.2.0\",\n\t\tinfo: buildInfo(config),\n\t\t...(tags.length > 0 ? { tags } : {}),\n\t\tpaths: {},\n\t\tcomponents: { schemas },\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// Info block\n// ---------------------------------------------------------------------------\n\n/**\n * Builds the OpenAPI `info` block from the config.\n *\n * @param config - The resolved {@link ForgeConfig}.\n * @returns An info object with title, version, and description.\n * @internal\n */\nfunction buildInfo(_config: ForgeConfig): OpenAPIInfoObject {\n\t// We attempt to read name/version from the project root's package.json at\n\t// runtime. If unavailable, fall back to safe defaults.\n\treturn {\n\t\ttitle: \"API Reference\",\n\t\tversion: \"0.0.0\",\n\t\tdescription: \"Generated by forge-ts\",\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// Schema builders\n// ---------------------------------------------------------------------------\n\n/**\n * Converts an {@link SDKType} to an OpenAPI schema object.\n *\n * - `interface` / `class` → object schema with `properties` and `required`\n * - `enum` → schema with `enum` array and `type: \"string\"`\n * - `type` alias → schema derived from signature parsing\n *\n * @param sdkType - The type descriptor to convert.\n * @returns An OpenAPI schema object.\n * @internal\n */\nfunction buildSchema(sdkType: SDKType): OpenAPISchemaObject {\n\tconst base: OpenAPISchemaObject = {};\n\n\tif (sdkType.description) {\n\t\tbase.description = sdkType.description;\n\t}\n\n\tif (sdkType.deprecated) {\n\t\tbase.deprecated = true;\n\t}\n\n\tswitch (sdkType.kind) {\n\t\tcase \"interface\":\n\t\tcase \"class\":\n\t\t\treturn { ...base, ...buildObjectSchema(sdkType) };\n\t\tcase \"enum\":\n\t\t\treturn { ...base, ...buildEnumSchema(sdkType) };\n\t\tcase \"type\":\n\t\t\treturn { ...base, ...buildTypeAliasSchema(sdkType) };\n\t\tdefault:\n\t\t\treturn { ...base, type: \"object\" };\n\t}\n}\n\n/**\n * Builds an object schema for an interface or class type.\n *\n * @param sdkType - An interface or class {@link SDKType}.\n * @returns An OpenAPI object schema.\n * @internal\n */\nfunction buildObjectSchema(sdkType: SDKType): OpenAPISchemaObject {\n\tif (sdkType.properties.length === 0) {\n\t\treturn { type: \"object\" };\n\t}\n\n\tconst properties: Record<string, OpenAPISchemaObject> = {};\n\tconst required: string[] = [];\n\n\tfor (const prop of sdkType.properties) {\n\t\tproperties[prop.name] = buildPropertySchema(prop);\n\t\tif (prop.required) {\n\t\t\trequired.push(prop.name);\n\t\t}\n\t}\n\n\tconst schema: OpenAPISchemaObject = { type: \"object\", properties };\n\tif (required.length > 0) {\n\t\tschema.required = required;\n\t}\n\n\treturn schema;\n}\n\n/**\n * Builds the schema for a single property.\n *\n * @param prop - The {@link SDKProperty} to convert.\n * @returns A partial OpenAPI schema for the property.\n * @internal\n */\nfunction buildPropertySchema(prop: SDKProperty): OpenAPISchemaObject {\n\tconst schema: OpenAPISchemaObject = { ...signatureToSchema(prop.type) };\n\n\tif (prop.description) {\n\t\tschema.description = prop.description;\n\t}\n\n\tif (prop.deprecated) {\n\t\tschema.deprecated = true;\n\t}\n\n\treturn schema;\n}\n\n/**\n * Builds an enum schema for an enum type.\n *\n * If the enum has properties (members), their names are used as the enum\n * values. The type defaults to `\"string\"` unless a member type suggests\n * otherwise.\n *\n * @param sdkType - An enum {@link SDKType}.\n * @returns An OpenAPI schema with `enum` values.\n * @internal\n */\nfunction buildEnumSchema(sdkType: SDKType): OpenAPISchemaObject {\n\tif (sdkType.properties.length === 0) {\n\t\treturn { type: \"string\", enum: [] };\n\t}\n\n\tconst values = sdkType.properties.map((p) => {\n\t\t// Use the type value if it looks like a string literal; otherwise use name.\n\t\tconst t = p.type.trim();\n\t\tif (t.startsWith('\"') || t.startsWith(\"'\")) {\n\t\t\treturn t.replace(/^['\"]|['\"]$/g, \"\");\n\t\t}\n\t\treturn p.name;\n\t});\n\n\t// Detect numeric enums: if all values are parseable numbers use \"number\".\n\tconst allNumeric = values.every((v) => !Number.isNaN(Number(v)));\n\n\treturn {\n\t\ttype: allNumeric ? \"number\" : \"string\",\n\t\tenum: allNumeric ? values.map(Number) : values,\n\t};\n}\n\n/**\n * Builds a schema for a type alias.\n *\n * Attempts to parse the signature string directly; falls back to\n * `{ type: \"object\" }` for complex generics or intersections.\n *\n * @param sdkType - A type alias {@link SDKType}.\n * @returns An OpenAPI schema.\n * @internal\n */\nfunction buildTypeAliasSchema(sdkType: SDKType): OpenAPISchemaObject {\n\tif (!sdkType.signature) {\n\t\treturn { type: \"object\" };\n\t}\n\n\t// The signature for a type alias is often `type Name = ActualType`.\n\t// Strip the leading declaration if present.\n\tconst equalsIndex = sdkType.signature.indexOf(\"=\");\n\tconst rawType =\n\t\tequalsIndex !== -1 ? sdkType.signature.slice(equalsIndex + 1).trim() : sdkType.signature;\n\n\treturn signatureToSchema(rawType);\n}\n\n// ---------------------------------------------------------------------------\n// Tag derivation\n// ---------------------------------------------------------------------------\n\n/**\n * Derives OpenAPI tag names from the unique source file basenames of the\n * provided types (without directory path or extension).\n *\n * @param types - The visible SDK types.\n * @returns A sorted, deduplicated array of tag name strings.\n * @internal\n */\nfunction deriveTagNames(types: SDKType[]): string[] {\n\tconst seen = new Set<string>();\n\tfor (const t of types) {\n\t\tconst basename = t.sourceFile.split(\"/\").pop() ?? t.sourceFile;\n\t\tconst withoutExt = basename.replace(/\\.[^.]+$/, \"\");\n\t\tseen.add(withoutExt);\n\t}\n\treturn Array.from(seen).sort();\n}\n","/**\n * Utility for mapping TypeScript type signatures to OpenAPI 3.2 schemas.\n * @public\n */\n\nimport type { OpenAPISchemaObject } from \"@forge-ts/core\";\n\nexport type { OpenAPISchemaObject };\n\n/**\n * Maps a TypeScript type signature string to an OpenAPI 3.2 schema object.\n *\n * Handles common primitives, arrays, unions, `Record<K, V>`, and falls back\n * to `{ type: \"object\" }` for anything it cannot parse.\n *\n * @param signature - A TypeScript type signature string, e.g. `\"string\"`, `\"number[]\"`,\n * `\"string | number\"`, `\"Record<string, boolean>\"`.\n * @returns An OpenAPI schema object.\n * @public\n */\nexport function signatureToSchema(signature: string): OpenAPISchemaObject {\n\tconst trimmed = signature.trim();\n\n\t// Strip trailing `| undefined` or `| null` for optional detection — callers\n\t// handle the required array separately; here we just produce the base schema.\n\tconst withoutUndefined = trimmed\n\t\t.replace(/\\s*\\|\\s*undefined/g, \"\")\n\t\t.replace(/\\s*\\|\\s*null/g, \"\")\n\t\t.trim();\n\n\t// Union type: A | B\n\tif (withoutUndefined.includes(\" | \")) {\n\t\tconst parts = splitUnion(withoutUndefined);\n\t\tif (parts.length > 1) {\n\t\t\treturn { oneOf: parts.map((p) => signatureToSchema(p)) };\n\t\t}\n\t}\n\n\t// Array shorthand: T[]\n\tconst arrayShorthand = /^(.+)\\[\\]$/.exec(withoutUndefined);\n\tif (arrayShorthand) {\n\t\treturn { type: \"array\", items: signatureToSchema(arrayShorthand[1]) };\n\t}\n\n\t// Generic Array<T>\n\tconst genericArray = /^Array<(.+)>$/.exec(withoutUndefined);\n\tif (genericArray) {\n\t\treturn { type: \"array\", items: signatureToSchema(genericArray[1]) };\n\t}\n\n\t// Record<string, V>\n\tconst record = /^Record<[^,]+,\\s*(.+)>$/.exec(withoutUndefined);\n\tif (record) {\n\t\treturn { type: \"object\", additionalProperties: signatureToSchema(record[1]) };\n\t}\n\n\t// Primitives\n\tswitch (withoutUndefined) {\n\t\tcase \"string\":\n\t\t\treturn { type: \"string\" };\n\t\tcase \"number\":\n\t\t\treturn { type: \"number\" };\n\t\tcase \"boolean\":\n\t\t\treturn { type: \"boolean\" };\n\t\tcase \"null\":\n\t\t\treturn { type: \"null\" };\n\t\tcase \"unknown\":\n\t\tcase \"any\":\n\t\t\treturn {};\n\t\tcase \"void\":\n\t\t\treturn { type: \"null\" };\n\t\tdefault:\n\t\t\treturn { type: \"object\" };\n\t}\n}\n\n/**\n * Splits a union type string by top-level `|` separators (ignoring `|` inside\n * angle brackets or parentheses).\n *\n * @param signature - A union type string such as `\"string | number | boolean\"`.\n * @returns An array of trimmed member type strings.\n * @internal\n */\nfunction splitUnion(signature: string): string[] {\n\tconst parts: string[] = [];\n\tlet depth = 0;\n\tlet start = 0;\n\n\tfor (let i = 0; i < signature.length; i++) {\n\t\tconst ch = signature[i];\n\t\tif (ch === \"<\" || ch === \"(\" || ch === \"{\") {\n\t\t\tdepth++;\n\t\t} else if (ch === \">\" || ch === \")\" || ch === \"}\") {\n\t\t\tdepth--;\n\t\t} else if (ch === \"|\" && depth === 0) {\n\t\t\tparts.push(signature.slice(start, i).trim());\n\t\t\tstart = i + 1;\n\t\t}\n\t}\n\n\tparts.push(signature.slice(start).trim());\n\treturn parts.filter(Boolean);\n}\n","import type { ForgeSymbol } from \"@forge-ts/core\";\nimport { Visibility } from \"@forge-ts/core\";\n\n/**\n * A single entry in the generated API reference.\n * @public\n */\nexport interface ReferenceEntry {\n\t/** Symbol name. */\n\tname: string;\n\t/** Symbol kind. */\n\tkind: ForgeSymbol[\"kind\"];\n\t/** TSDoc summary. */\n\tsummary?: string;\n\t/** Human-readable type signature. */\n\tsignature?: string;\n\t/** Resolved visibility level. */\n\tvisibility: Visibility;\n\t/** Deprecation notice, if present. */\n\tdeprecated?: string;\n\t/** Documented parameters. */\n\tparams?: Array<{ name: string; description: string; type?: string }>;\n\t/** Documented return value. */\n\treturns?: { description: string; type?: string };\n\t/** Documented thrown exceptions. */\n\tthrows?: Array<{ type?: string; description: string }>;\n\t/** Code examples from TSDoc `@example` tags. */\n\texamples?: Array<{ code: string; language: string }>;\n\t/** Nested child symbols (class methods, interface properties, enum members). */\n\tchildren?: ReferenceEntry[];\n\t/** Source file location. */\n\tlocation: { filePath: string; line: number };\n}\n\n/**\n * Builds a structured API reference from a list of exported symbols.\n *\n * Unlike the minimal stub, this version includes nested children (class\n * methods, interface properties) and all available TSDoc metadata.\n *\n * Symbols with {@link Visibility.Internal} or {@link Visibility.Private} are\n * excluded from the top-level results. Children with private/internal\n * visibility are also filtered out.\n *\n * @param symbols - All symbols from the AST walker.\n * @returns An array of {@link ReferenceEntry} objects sorted by name.\n * @public\n */\nexport function buildReference(symbols: ForgeSymbol[]): ReferenceEntry[] {\n\treturn symbols\n\t\t.filter(\n\t\t\t(s) =>\n\t\t\t\ts.exported && s.visibility !== Visibility.Internal && s.visibility !== Visibility.Private,\n\t\t)\n\t\t.map(symbolToEntry)\n\t\t.sort((a, b) => a.name.localeCompare(b.name));\n}\n\n/**\n * Converts a single {@link ForgeSymbol} to a {@link ReferenceEntry}.\n *\n * @param symbol - The symbol to convert.\n * @returns A populated {@link ReferenceEntry}.\n * @internal\n */\nfunction symbolToEntry(symbol: ForgeSymbol): ReferenceEntry {\n\tconst entry: ReferenceEntry = {\n\t\tname: symbol.name,\n\t\tkind: symbol.kind,\n\t\tsummary: symbol.documentation?.summary,\n\t\tsignature: symbol.signature,\n\t\tvisibility: symbol.visibility,\n\t\tdeprecated: symbol.documentation?.deprecated,\n\t\tlocation: { filePath: symbol.filePath, line: symbol.line },\n\t};\n\n\tif (symbol.documentation?.params && symbol.documentation.params.length > 0) {\n\t\tentry.params = symbol.documentation.params;\n\t}\n\n\tif (symbol.documentation?.returns) {\n\t\tentry.returns = symbol.documentation.returns;\n\t}\n\n\tif (symbol.documentation?.throws && symbol.documentation.throws.length > 0) {\n\t\tentry.throws = symbol.documentation.throws;\n\t}\n\n\tif (symbol.documentation?.examples && symbol.documentation.examples.length > 0) {\n\t\tentry.examples = symbol.documentation.examples.map((ex) => ({\n\t\t\tcode: ex.code,\n\t\t\tlanguage: ex.language,\n\t\t}));\n\t}\n\n\tif (symbol.children && symbol.children.length > 0) {\n\t\tconst visibleChildren = symbol.children.filter(\n\t\t\t(c) => c.visibility !== Visibility.Internal && c.visibility !== Visibility.Private,\n\t\t);\n\t\tif (visibleChildren.length > 0) {\n\t\t\tentry.children = visibleChildren.map(symbolToEntry);\n\t\t}\n\t}\n\n\treturn entry;\n}\n","import type { ForgeSymbol } from \"@forge-ts/core\";\nimport { Visibility } from \"@forge-ts/core\";\n\n/**\n * A single property extracted from an interface or class symbol.\n * @public\n */\nexport interface SDKProperty {\n\t/** The property name. */\n\tname: string;\n\t/** The TypeScript type string of the property. */\n\ttype: string;\n\t/** TSDoc summary for this property. */\n\tdescription?: string;\n\t/** Whether the property is required (not optional). */\n\trequired: boolean;\n\t/** Deprecation notice, if present. */\n\tdeprecated?: string;\n}\n\n/**\n * An SDK type descriptor extracted from the symbol graph.\n * @public\n */\nexport interface SDKType {\n\t/** The symbol name. */\n\tname: string;\n\t/** Syntactic kind of the type. */\n\tkind: \"interface\" | \"type\" | \"class\" | \"enum\";\n\t/** Human-readable type signature. */\n\tsignature?: string;\n\t/** TSDoc summary. */\n\tdescription?: string;\n\t/** Deprecation notice, if present. */\n\tdeprecated?: string;\n\t/** Resolved visibility level. */\n\tvisibility: Visibility;\n\t/** Extracted properties (for interfaces, classes) or values (for enums). */\n\tproperties: SDKProperty[];\n\t/** Absolute path to the source file. */\n\tsourceFile: string;\n}\n\n/**\n * Extracts SDK-relevant types (interfaces, type aliases, classes, enums) from\n * a list of {@link ForgeSymbol} objects.\n *\n * Only exported symbols whose visibility is not {@link Visibility.Internal} or\n * {@link Visibility.Private} are included.\n *\n * @param symbols - The symbols produced by the core AST walker.\n * @returns An array of {@link SDKType} objects for public-facing type definitions.\n * @public\n */\nexport function extractSDKTypes(symbols: ForgeSymbol[]): SDKType[] {\n\treturn symbols\n\t\t.filter(\n\t\t\t(s) =>\n\t\t\t\ts.exported &&\n\t\t\t\t(s.kind === \"interface\" || s.kind === \"type\" || s.kind === \"class\" || s.kind === \"enum\") &&\n\t\t\t\ts.visibility !== Visibility.Internal &&\n\t\t\t\ts.visibility !== Visibility.Private,\n\t\t)\n\t\t.map((s) => ({\n\t\t\tname: s.name,\n\t\t\tkind: s.kind as SDKType[\"kind\"],\n\t\t\tsignature: s.signature,\n\t\t\tdescription: s.documentation?.summary,\n\t\t\tdeprecated: s.documentation?.deprecated,\n\t\t\tvisibility: s.visibility,\n\t\t\tproperties: extractProperties(s),\n\t\t\tsourceFile: s.filePath,\n\t\t}));\n}\n\n/**\n * Extracts the property list from a symbol's children.\n *\n * For interfaces and classes, children with kind `\"property\"` are mapped to\n * {@link SDKProperty}. For enums, children with kind `\"property\"` (enum\n * members) are also included. Method children are excluded.\n *\n * @param symbol - The parent symbol whose children to extract from.\n * @returns An array of {@link SDKProperty} objects.\n * @internal\n */\nfunction extractProperties(symbol: ForgeSymbol): SDKProperty[] {\n\tif (!symbol.children || symbol.children.length === 0) {\n\t\treturn [];\n\t}\n\n\treturn symbol.children\n\t\t.filter(\n\t\t\t(child) =>\n\t\t\t\tchild.kind === \"property\" &&\n\t\t\t\tchild.visibility !== Visibility.Private &&\n\t\t\t\tchild.visibility !== Visibility.Internal,\n\t\t)\n\t\t.map((child) => {\n\t\t\tconst isOptional = child.signature ? child.signature.includes(\"?\") : false;\n\t\t\tconst rawType = resolveChildType(child);\n\t\t\treturn {\n\t\t\t\tname: child.name,\n\t\t\t\ttype: rawType,\n\t\t\t\tdescription: child.documentation?.summary,\n\t\t\t\trequired: !isOptional,\n\t\t\t\tdeprecated: child.documentation?.deprecated,\n\t\t\t};\n\t\t});\n}\n\n/**\n * Resolves the type string for a child symbol.\n *\n * Uses the signature if present; otherwise falls back to `\"unknown\"`.\n *\n * @param child - A child {@link ForgeSymbol}.\n * @returns A TypeScript type string.\n * @internal\n */\nfunction resolveChildType(child: ForgeSymbol): string {\n\tif (!child.signature) {\n\t\treturn \"unknown\";\n\t}\n\n\t// Signatures for properties often look like `name: type` or `name?: type`.\n\t// Strip the leading `name:` or `name?:` prefix to get the bare type.\n\tconst colonIndex = child.signature.indexOf(\":\");\n\tif (colonIndex !== -1) {\n\t\treturn child.signature.slice(colonIndex + 1).trim();\n\t}\n\n\treturn child.signature;\n}\n","/**\n * @forge-ts/api — OpenAPI spec and API reference generator.\n *\n * Extracts public SDK types from the symbol graph and generates an\n * OpenAPI 3.2 document and a structured API reference.\n *\n * @packageDocumentation\n * @public\n */\n\nexport { generateOpenAPISpec, type OpenAPIDocument } from \"./openapi.js\";\nexport { buildReference, type ReferenceEntry } from \"./reference.js\";\nexport { type OpenAPISchemaObject, signatureToSchema } from \"./schema-mapper.js\";\nexport { extractSDKTypes, type SDKProperty, type SDKType } from \"./sdk-extractor.js\";\n\nimport { mkdir, writeFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport { createWalker, type ForgeConfig, type ForgeResult } from \"@forge-ts/core\";\nimport { generateOpenAPISpec } from \"./openapi.js\";\nimport { extractSDKTypes } from \"./sdk-extractor.js\";\n\n/**\n * Runs the API generation pipeline: walk → extract → generate → write.\n *\n * @param config - The resolved {@link ForgeConfig} for the project.\n * @returns A {@link ForgeResult} with success/failure and any diagnostics.\n * @public\n */\nexport async function generateApi(config: ForgeConfig): Promise<ForgeResult> {\n\tconst start = Date.now();\n\n\tconst walker = createWalker(config);\n\tconst symbols = walker.walk();\n\tconst sdkTypes = extractSDKTypes(symbols);\n\tconst spec = generateOpenAPISpec(config, sdkTypes);\n\n\tawait mkdir(dirname(config.api.openapiPath), { recursive: true });\n\tawait writeFile(config.api.openapiPath, JSON.stringify(spec, null, 2), \"utf8\");\n\n\treturn {\n\t\tsuccess: true,\n\t\tsymbols,\n\t\terrors: [],\n\t\twarnings: [],\n\t\tduration: Date.now() - start,\n\t};\n}\n"],"mappings":"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","names":["Visibility","Visibility"]}
1
+ {"version":3,"sources":["../src/openapi.ts","../src/schema-mapper.ts","../src/reference.ts","../src/sdk-extractor.ts","../src/index.ts"],"sourcesContent":["import type {\n\tForgeConfig,\n\tForgeSymbol,\n\tOpenAPIDocument,\n\tOpenAPIInfoObject,\n\tOpenAPIOperationObject,\n\tOpenAPIParameterObject,\n\tOpenAPIPathItemObject,\n\tOpenAPISchemaObject,\n} from \"@forge-ts/core\";\nimport { Visibility } from \"@forge-ts/core\";\nimport { signatureToSchema } from \"./schema-mapper.js\";\nimport type { SDKProperty, SDKType } from \"./sdk-extractor.js\";\n\nexport type { OpenAPIDocument };\n\n/**\n * Generates a production-quality OpenAPI 3.2 document from the extracted SDK\n * types.\n *\n * The document is populated with:\n * - An `info` block sourced from the config or reasonable defaults.\n * - A `components.schemas` section with one schema per exported type.\n * - `tags` derived from unique source file paths (grouping by file).\n * - Visibility filtering: `@internal` symbols are never emitted.\n *\n * HTTP paths are not yet emitted (`paths` is always `{}`); route extraction\n * will be added in a future release.\n *\n * @param config - The resolved {@link ForgeConfig}.\n * @param sdkTypes - SDK types to include as component schemas.\n * @param symbols - Raw symbols used to extract HTTP route paths from `@route` tags.\n * @returns An {@link OpenAPIDocument} object.\n * @example\n * ```typescript\n * import { generateOpenAPISpec } from \"@forge-ts/api\";\n * import { extractSDKTypes } from \"@forge-ts/api\";\n * const spec = generateOpenAPISpec(config, extractSDKTypes(symbols), symbols);\n * console.log(spec.openapi); // \"3.2.0\"\n * ```\n * @public\n */\nexport function generateOpenAPISpec(\n\tconfig: ForgeConfig,\n\tsdkTypes: SDKType[],\n\tsymbols: ForgeSymbol[] = [],\n): OpenAPIDocument {\n\t// Visibility filtering: never emit @internal or @private symbols.\n\tconst visibleTypes = sdkTypes.filter(\n\t\t(t) => t.visibility !== Visibility.Internal && t.visibility !== Visibility.Private,\n\t);\n\n\tconst schemas: Record<string, OpenAPISchemaObject> = {};\n\tfor (const t of visibleTypes) {\n\t\tschemas[t.name] = buildSchema(t);\n\t}\n\n\t// Derive tags from unique source files (basename without extension).\n\tconst tagNames = deriveTagNames(visibleTypes);\n\tconst tags = tagNames.map((name) => ({ name }));\n\n\tconst paths = extractPaths(symbols);\n\n\treturn {\n\t\topenapi: \"3.2.0\",\n\t\tinfo: buildInfo(config),\n\t\t...(tags.length > 0 ? { tags } : {}),\n\t\tpaths,\n\t\tcomponents: { schemas },\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// Info block\n// ---------------------------------------------------------------------------\n\n/**\n * Builds the OpenAPI `info` block from the config.\n *\n * @param config - The resolved {@link ForgeConfig}.\n * @returns An info object with title, version, and description.\n * @internal\n */\nfunction buildInfo(_config: ForgeConfig): OpenAPIInfoObject {\n\t// We attempt to read name/version from the project root's package.json at\n\t// runtime. If unavailable, fall back to safe defaults.\n\treturn {\n\t\ttitle: \"API Reference\",\n\t\tversion: \"0.0.0\",\n\t\tdescription: \"Generated by forge-ts\",\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// Schema builders\n// ---------------------------------------------------------------------------\n\n/**\n * Converts an {@link SDKType} to an OpenAPI schema object.\n *\n * - `interface` / `class` → object schema with `properties` and `required`\n * - `enum` → schema with `enum` array and `type: \"string\"`\n * - `type` alias → schema derived from signature parsing\n *\n * @param sdkType - The type descriptor to convert.\n * @returns An OpenAPI schema object.\n * @internal\n */\nfunction buildSchema(sdkType: SDKType): OpenAPISchemaObject {\n\tconst base: OpenAPISchemaObject = {};\n\n\tif (sdkType.description) {\n\t\tbase.description = sdkType.description;\n\t}\n\n\tif (sdkType.deprecated) {\n\t\tbase.deprecated = true;\n\t}\n\n\tswitch (sdkType.kind) {\n\t\tcase \"interface\":\n\t\tcase \"class\":\n\t\t\treturn { ...base, ...buildObjectSchema(sdkType) };\n\t\tcase \"enum\":\n\t\t\treturn { ...base, ...buildEnumSchema(sdkType) };\n\t\tcase \"type\":\n\t\t\treturn { ...base, ...buildTypeAliasSchema(sdkType) };\n\t\tdefault:\n\t\t\treturn { ...base, type: \"object\" };\n\t}\n}\n\n/**\n * Builds an object schema for an interface or class type.\n *\n * @param sdkType - An interface or class {@link SDKType}.\n * @returns An OpenAPI object schema.\n * @internal\n */\nfunction buildObjectSchema(sdkType: SDKType): OpenAPISchemaObject {\n\tif (sdkType.properties.length === 0) {\n\t\treturn { type: \"object\" };\n\t}\n\n\tconst properties: Record<string, OpenAPISchemaObject> = {};\n\tconst required: string[] = [];\n\n\tfor (const prop of sdkType.properties) {\n\t\tproperties[prop.name] = buildPropertySchema(prop);\n\t\tif (prop.required) {\n\t\t\trequired.push(prop.name);\n\t\t}\n\t}\n\n\tconst schema: OpenAPISchemaObject = { type: \"object\", properties };\n\tif (required.length > 0) {\n\t\tschema.required = required;\n\t}\n\n\treturn schema;\n}\n\n/**\n * Builds the schema for a single property.\n *\n * @param prop - The {@link SDKProperty} to convert.\n * @returns A partial OpenAPI schema for the property.\n * @internal\n */\nfunction buildPropertySchema(prop: SDKProperty): OpenAPISchemaObject {\n\tconst schema: OpenAPISchemaObject = { ...signatureToSchema(prop.type) };\n\n\tif (prop.description) {\n\t\tschema.description = prop.description;\n\t}\n\n\tif (prop.deprecated) {\n\t\tschema.deprecated = true;\n\t}\n\n\treturn schema;\n}\n\n/**\n * Builds an enum schema for an enum type.\n *\n * If the enum has properties (members), their names are used as the enum\n * values. The type defaults to `\"string\"` unless a member type suggests\n * otherwise.\n *\n * @param sdkType - An enum {@link SDKType}.\n * @returns An OpenAPI schema with `enum` values.\n * @internal\n */\nfunction buildEnumSchema(sdkType: SDKType): OpenAPISchemaObject {\n\tif (sdkType.properties.length === 0) {\n\t\treturn { type: \"string\", enum: [] };\n\t}\n\n\tconst values = sdkType.properties.map((p) => {\n\t\t// Use the type value if it looks like a string literal; otherwise use name.\n\t\tconst t = p.type.trim();\n\t\tif (t.startsWith('\"') || t.startsWith(\"'\")) {\n\t\t\treturn t.replace(/^['\"]|['\"]$/g, \"\");\n\t\t}\n\t\treturn p.name;\n\t});\n\n\t// Detect numeric enums: if all values are parseable numbers use \"number\".\n\tconst allNumeric = values.every((v) => !Number.isNaN(Number(v)));\n\n\treturn {\n\t\ttype: allNumeric ? \"number\" : \"string\",\n\t\tenum: allNumeric ? values.map(Number) : values,\n\t};\n}\n\n/**\n * Builds a schema for a type alias.\n *\n * Attempts to parse the signature string directly; falls back to\n * `{ type: \"object\" }` for complex generics or intersections.\n *\n * @param sdkType - A type alias {@link SDKType}.\n * @returns An OpenAPI schema.\n * @internal\n */\nfunction buildTypeAliasSchema(sdkType: SDKType): OpenAPISchemaObject {\n\tif (!sdkType.signature) {\n\t\treturn { type: \"object\" };\n\t}\n\n\t// The signature for a type alias is often `type Name = ActualType`.\n\t// Strip the leading declaration if present.\n\tconst equalsIndex = sdkType.signature.indexOf(\"=\");\n\tconst rawType =\n\t\tequalsIndex !== -1 ? sdkType.signature.slice(equalsIndex + 1).trim() : sdkType.signature;\n\n\treturn signatureToSchema(rawType);\n}\n\n// ---------------------------------------------------------------------------\n// Path extraction\n// ---------------------------------------------------------------------------\n\n/**\n * Builds OpenAPI path items from symbols that carry `@route` TSDoc tags.\n *\n * Each `@route` tag must be in the format `METHOD /path` (e.g. `GET /users/{id}`).\n * Path template variables like `{id}` are emitted as required path parameters.\n * Any `@param` entries not matched to a path variable become query parameters.\n *\n * @param symbols - All {@link ForgeSymbol} instances from the walker.\n * @returns A map of path template strings to their OpenAPI path item objects.\n * @internal\n */\nfunction extractPaths(symbols: ForgeSymbol[]): Record<string, OpenAPIPathItemObject> {\n\tconst paths: Record<string, OpenAPIPathItemObject> = {};\n\n\tfor (const symbol of symbols) {\n\t\tconst routeTags: string[] = symbol.documentation?.tags?.route ?? [];\n\t\tfor (const routeTag of routeTags) {\n\t\t\tconst match = routeTag.match(\n\t\t\t\t/^(GET|POST|PUT|DELETE|PATCH|OPTIONS|HEAD)\\s+(\\S+)/i,\n\t\t\t);\n\t\t\tif (!match) continue;\n\n\t\t\tconst method = match[1].toLowerCase() as\n\t\t\t\t| \"get\"\n\t\t\t\t| \"post\"\n\t\t\t\t| \"put\"\n\t\t\t\t| \"delete\"\n\t\t\t\t| \"patch\"\n\t\t\t\t| \"options\"\n\t\t\t\t| \"head\";\n\t\t\tconst path = match[2];\n\n\t\t\tif (!paths[path]) {\n\t\t\t\tpaths[path] = {};\n\t\t\t}\n\n\t\t\tconst fileBasename =\n\t\t\t\tsymbol.filePath.split(\"/\").pop()?.replace(/\\.ts$/, \"\") ?? \"default\";\n\n\t\t\tconst operation: OpenAPIOperationObject = {\n\t\t\t\toperationId: symbol.name,\n\t\t\t\tsummary: symbol.documentation?.summary,\n\t\t\t\tdescription: symbol.documentation?.summary,\n\t\t\t\ttags: [fileBasename],\n\t\t\t};\n\n\t\t\t// Extract path template variables like {id}\n\t\t\tconst pathParams = [...path.matchAll(/\\{(\\w+)\\}/g)].map((m) => m[1]);\n\t\t\tconst parameters: OpenAPIParameterObject[] = [];\n\n\t\t\tfor (const paramName of pathParams) {\n\t\t\t\tconst paramDoc = symbol.documentation?.params?.find(\n\t\t\t\t\t(p) => p.name === paramName,\n\t\t\t\t);\n\t\t\t\tparameters.push({\n\t\t\t\t\tname: paramName,\n\t\t\t\t\tin: \"path\",\n\t\t\t\t\trequired: true,\n\t\t\t\t\tdescription: paramDoc?.description ?? `The ${paramName} parameter`,\n\t\t\t\t\tschema: { type: \"string\" },\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Non-path @param entries become query parameters\n\t\t\tconst nonPathParams = (symbol.documentation?.params ?? []).filter(\n\t\t\t\t(p) => !pathParams.includes(p.name),\n\t\t\t);\n\t\t\tfor (const param of nonPathParams) {\n\t\t\t\tparameters.push({\n\t\t\t\t\tname: param.name,\n\t\t\t\t\tin: \"query\",\n\t\t\t\t\tdescription: param.description,\n\t\t\t\t\tschema: { type: \"string\" },\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (parameters.length > 0) {\n\t\t\t\toperation.parameters = parameters;\n\t\t\t}\n\n\t\t\t// @returns becomes the 200 response description\n\t\t\tif (symbol.documentation?.returns) {\n\t\t\t\toperation.responses = {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\tdescription: symbol.documentation.returns.description,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tpaths[path][method] = operation;\n\t\t}\n\t}\n\n\treturn paths;\n}\n\n// ---------------------------------------------------------------------------\n// Tag derivation\n// ---------------------------------------------------------------------------\n\n/**\n * Derives OpenAPI tag names from the unique source file basenames of the\n * provided types (without directory path or extension).\n *\n * @param types - The visible SDK types.\n * @returns A sorted, deduplicated array of tag name strings.\n * @internal\n */\nfunction deriveTagNames(types: SDKType[]): string[] {\n\tconst seen = new Set<string>();\n\tfor (const t of types) {\n\t\tconst basename = t.sourceFile.split(\"/\").pop() ?? t.sourceFile;\n\t\tconst withoutExt = basename.replace(/\\.[^.]+$/, \"\");\n\t\tseen.add(withoutExt);\n\t}\n\treturn Array.from(seen).sort();\n}\n","/**\n * Utility for mapping TypeScript type signatures to OpenAPI 3.2 schemas.\n * @public\n */\n\nimport type { OpenAPISchemaObject } from \"@forge-ts/core\";\n\nexport type { OpenAPISchemaObject };\n\n/**\n * Maps a TypeScript type signature string to an OpenAPI 3.2 schema object.\n *\n * Handles common primitives, arrays, unions, `Record<K, V>`, and falls back\n * to `{ type: \"object\" }` for anything it cannot parse.\n *\n * @param signature - A TypeScript type signature string, e.g. `\"string\"`, `\"number[]\"`,\n * `\"string | number\"`, `\"Record<string, boolean>\"`.\n * @returns An OpenAPI schema object.\n * @example\n * ```typescript\n * import { signatureToSchema } from \"@forge-ts/api\";\n * const schema = signatureToSchema(\"string[]\");\n * // { type: \"array\", items: { type: \"string\" } }\n * ```\n * @public\n */\nexport function signatureToSchema(signature: string): OpenAPISchemaObject {\n\tconst trimmed = signature.trim();\n\n\t// Strip trailing `| undefined` or `| null` for optional detection — callers\n\t// handle the required array separately; here we just produce the base schema.\n\tconst withoutUndefined = trimmed\n\t\t.replace(/\\s*\\|\\s*undefined/g, \"\")\n\t\t.replace(/\\s*\\|\\s*null/g, \"\")\n\t\t.trim();\n\n\t// Union type: A | B\n\tif (withoutUndefined.includes(\" | \")) {\n\t\tconst parts = splitUnion(withoutUndefined);\n\t\tif (parts.length > 1) {\n\t\t\treturn { oneOf: parts.map((p) => signatureToSchema(p)) };\n\t\t}\n\t}\n\n\t// Array shorthand: T[]\n\tconst arrayShorthand = /^(.+)\\[\\]$/.exec(withoutUndefined);\n\tif (arrayShorthand) {\n\t\treturn { type: \"array\", items: signatureToSchema(arrayShorthand[1]) };\n\t}\n\n\t// Generic Array<T>\n\tconst genericArray = /^Array<(.+)>$/.exec(withoutUndefined);\n\tif (genericArray) {\n\t\treturn { type: \"array\", items: signatureToSchema(genericArray[1]) };\n\t}\n\n\t// Record<string, V>\n\tconst record = /^Record<[^,]+,\\s*(.+)>$/.exec(withoutUndefined);\n\tif (record) {\n\t\treturn { type: \"object\", additionalProperties: signatureToSchema(record[1]) };\n\t}\n\n\t// Primitives\n\tswitch (withoutUndefined) {\n\t\tcase \"string\":\n\t\t\treturn { type: \"string\" };\n\t\tcase \"number\":\n\t\t\treturn { type: \"number\" };\n\t\tcase \"boolean\":\n\t\t\treturn { type: \"boolean\" };\n\t\tcase \"null\":\n\t\t\treturn { type: \"null\" };\n\t\tcase \"unknown\":\n\t\tcase \"any\":\n\t\t\treturn {};\n\t\tcase \"void\":\n\t\t\treturn { type: \"null\" };\n\t\tdefault:\n\t\t\treturn { type: \"object\" };\n\t}\n}\n\n/**\n * Splits a union type string by top-level `|` separators (ignoring `|` inside\n * angle brackets or parentheses).\n *\n * @param signature - A union type string such as `\"string | number | boolean\"`.\n * @returns An array of trimmed member type strings.\n * @internal\n */\nfunction splitUnion(signature: string): string[] {\n\tconst parts: string[] = [];\n\tlet depth = 0;\n\tlet start = 0;\n\n\tfor (let i = 0; i < signature.length; i++) {\n\t\tconst ch = signature[i];\n\t\tif (ch === \"<\" || ch === \"(\" || ch === \"{\") {\n\t\t\tdepth++;\n\t\t} else if (ch === \">\" || ch === \")\" || ch === \"}\") {\n\t\t\tdepth--;\n\t\t} else if (ch === \"|\" && depth === 0) {\n\t\t\tparts.push(signature.slice(start, i).trim());\n\t\t\tstart = i + 1;\n\t\t}\n\t}\n\n\tparts.push(signature.slice(start).trim());\n\treturn parts.filter(Boolean);\n}\n","import type { ForgeSymbol } from \"@forge-ts/core\";\nimport { Visibility } from \"@forge-ts/core\";\n\n/**\n * A single entry in the generated API reference.\n * @public\n */\nexport interface ReferenceEntry {\n\t/** Symbol name. */\n\tname: string;\n\t/** Symbol kind. */\n\tkind: ForgeSymbol[\"kind\"];\n\t/** TSDoc summary. */\n\tsummary?: string;\n\t/** Human-readable type signature. */\n\tsignature?: string;\n\t/** Resolved visibility level. */\n\tvisibility: Visibility;\n\t/** Deprecation notice, if present. */\n\tdeprecated?: string;\n\t/** Documented parameters. */\n\tparams?: Array<{ name: string; description: string; type?: string }>;\n\t/** Documented return value. */\n\treturns?: { description: string; type?: string };\n\t/** Documented thrown exceptions. */\n\tthrows?: Array<{ type?: string; description: string }>;\n\t/** Code examples from TSDoc `@example` tags. */\n\texamples?: Array<{ code: string; language: string }>;\n\t/** Nested child symbols (class methods, interface properties, enum members). */\n\tchildren?: ReferenceEntry[];\n\t/** Source file location. */\n\tlocation: { filePath: string; line: number };\n}\n\n/**\n * Builds a structured API reference from a list of exported symbols.\n *\n * Unlike the minimal stub, this version includes nested children (class\n * methods, interface properties) and all available TSDoc metadata.\n *\n * Symbols with {@link Visibility.Internal} or {@link Visibility.Private} are\n * excluded from the top-level results. Children with private/internal\n * visibility are also filtered out.\n *\n * @param symbols - All symbols from the AST walker.\n * @returns An array of {@link ReferenceEntry} objects sorted by name.\n * @example\n * ```typescript\n * import { buildReference } from \"@forge-ts/api\";\n * const entries = buildReference(symbols);\n * console.log(entries[0].name); // first symbol name, alphabetically\n * ```\n * @public\n */\nexport function buildReference(symbols: ForgeSymbol[]): ReferenceEntry[] {\n\treturn symbols\n\t\t.filter(\n\t\t\t(s) =>\n\t\t\t\ts.exported && s.visibility !== Visibility.Internal && s.visibility !== Visibility.Private,\n\t\t)\n\t\t.map(symbolToEntry)\n\t\t.sort((a, b) => a.name.localeCompare(b.name));\n}\n\n/**\n * Converts a single {@link ForgeSymbol} to a {@link ReferenceEntry}.\n *\n * @param symbol - The symbol to convert.\n * @returns A populated {@link ReferenceEntry}.\n * @internal\n */\nfunction symbolToEntry(symbol: ForgeSymbol): ReferenceEntry {\n\tconst entry: ReferenceEntry = {\n\t\tname: symbol.name,\n\t\tkind: symbol.kind,\n\t\tsummary: symbol.documentation?.summary,\n\t\tsignature: symbol.signature,\n\t\tvisibility: symbol.visibility,\n\t\tdeprecated: symbol.documentation?.deprecated,\n\t\tlocation: { filePath: symbol.filePath, line: symbol.line },\n\t};\n\n\tif (symbol.documentation?.params && symbol.documentation.params.length > 0) {\n\t\tentry.params = symbol.documentation.params;\n\t}\n\n\tif (symbol.documentation?.returns) {\n\t\tentry.returns = symbol.documentation.returns;\n\t}\n\n\tif (symbol.documentation?.throws && symbol.documentation.throws.length > 0) {\n\t\tentry.throws = symbol.documentation.throws;\n\t}\n\n\tif (symbol.documentation?.examples && symbol.documentation.examples.length > 0) {\n\t\tentry.examples = symbol.documentation.examples.map((ex) => ({\n\t\t\tcode: ex.code,\n\t\t\tlanguage: ex.language,\n\t\t}));\n\t}\n\n\tif (symbol.children && symbol.children.length > 0) {\n\t\tconst visibleChildren = symbol.children.filter(\n\t\t\t(c) => c.visibility !== Visibility.Internal && c.visibility !== Visibility.Private,\n\t\t);\n\t\tif (visibleChildren.length > 0) {\n\t\t\tentry.children = visibleChildren.map(symbolToEntry);\n\t\t}\n\t}\n\n\treturn entry;\n}\n","import type { ForgeSymbol } from \"@forge-ts/core\";\nimport { Visibility } from \"@forge-ts/core\";\n\n/**\n * A single property extracted from an interface or class symbol.\n * @public\n */\nexport interface SDKProperty {\n\t/** The property name. */\n\tname: string;\n\t/** The TypeScript type string of the property. */\n\ttype: string;\n\t/** TSDoc summary for this property. */\n\tdescription?: string;\n\t/** Whether the property is required (not optional). */\n\trequired: boolean;\n\t/** Deprecation notice, if present. */\n\tdeprecated?: string;\n}\n\n/**\n * An SDK type descriptor extracted from the symbol graph.\n * @public\n */\nexport interface SDKType {\n\t/** The symbol name. */\n\tname: string;\n\t/** Syntactic kind of the type. */\n\tkind: \"interface\" | \"type\" | \"class\" | \"enum\";\n\t/** Human-readable type signature. */\n\tsignature?: string;\n\t/** TSDoc summary. */\n\tdescription?: string;\n\t/** Deprecation notice, if present. */\n\tdeprecated?: string;\n\t/** Resolved visibility level. */\n\tvisibility: Visibility;\n\t/** Extracted properties (for interfaces, classes) or values (for enums). */\n\tproperties: SDKProperty[];\n\t/** Absolute path to the source file. */\n\tsourceFile: string;\n}\n\n/**\n * Extracts SDK-relevant types (interfaces, type aliases, classes, enums) from\n * a list of {@link ForgeSymbol} objects.\n *\n * Only exported symbols whose visibility is not {@link Visibility.Internal} or\n * {@link Visibility.Private} are included.\n *\n * @param symbols - The symbols produced by the core AST walker.\n * @returns An array of {@link SDKType} objects for public-facing type definitions.\n * @example\n * ```typescript\n * import { extractSDKTypes } from \"@forge-ts/api\";\n * const sdkTypes = extractSDKTypes(symbols);\n * console.log(sdkTypes.length); // number of public SDK types\n * ```\n * @public\n */\nexport function extractSDKTypes(symbols: ForgeSymbol[]): SDKType[] {\n\treturn symbols\n\t\t.filter(\n\t\t\t(s) =>\n\t\t\t\ts.exported &&\n\t\t\t\t(s.kind === \"interface\" || s.kind === \"type\" || s.kind === \"class\" || s.kind === \"enum\") &&\n\t\t\t\ts.visibility !== Visibility.Internal &&\n\t\t\t\ts.visibility !== Visibility.Private,\n\t\t)\n\t\t.map((s) => ({\n\t\t\tname: s.name,\n\t\t\tkind: s.kind as SDKType[\"kind\"],\n\t\t\tsignature: s.signature,\n\t\t\tdescription: s.documentation?.summary,\n\t\t\tdeprecated: s.documentation?.deprecated,\n\t\t\tvisibility: s.visibility,\n\t\t\tproperties: extractProperties(s),\n\t\t\tsourceFile: s.filePath,\n\t\t}));\n}\n\n/**\n * Extracts the property list from a symbol's children.\n *\n * For interfaces and classes, children with kind `\"property\"` are mapped to\n * {@link SDKProperty}. For enums, children with kind `\"property\"` (enum\n * members) are also included. Method children are excluded.\n *\n * @param symbol - The parent symbol whose children to extract from.\n * @returns An array of {@link SDKProperty} objects.\n * @internal\n */\nfunction extractProperties(symbol: ForgeSymbol): SDKProperty[] {\n\tif (!symbol.children || symbol.children.length === 0) {\n\t\treturn [];\n\t}\n\n\treturn symbol.children\n\t\t.filter(\n\t\t\t(child) =>\n\t\t\t\tchild.kind === \"property\" &&\n\t\t\t\tchild.visibility !== Visibility.Private &&\n\t\t\t\tchild.visibility !== Visibility.Internal,\n\t\t)\n\t\t.map((child) => {\n\t\t\tconst isOptional = child.signature ? child.signature.includes(\"?\") : false;\n\t\t\tconst rawType = resolveChildType(child);\n\t\t\treturn {\n\t\t\t\tname: child.name,\n\t\t\t\ttype: rawType,\n\t\t\t\tdescription: child.documentation?.summary,\n\t\t\t\trequired: !isOptional,\n\t\t\t\tdeprecated: child.documentation?.deprecated,\n\t\t\t};\n\t\t});\n}\n\n/**\n * Resolves the type string for a child symbol.\n *\n * Uses the signature if present; otherwise falls back to `\"unknown\"`.\n *\n * @param child - A child {@link ForgeSymbol}.\n * @returns A TypeScript type string.\n * @internal\n */\nfunction resolveChildType(child: ForgeSymbol): string {\n\tif (!child.signature) {\n\t\treturn \"unknown\";\n\t}\n\n\t// Signatures for properties often look like `name: type` or `name?: type`.\n\t// Strip the leading `name:` or `name?:` prefix to get the bare type.\n\tconst colonIndex = child.signature.indexOf(\":\");\n\tif (colonIndex !== -1) {\n\t\treturn child.signature.slice(colonIndex + 1).trim();\n\t}\n\n\treturn child.signature;\n}\n","/**\n * @forge-ts/api — OpenAPI spec and API reference generator.\n *\n * Extracts public SDK types from the symbol graph and generates an\n * OpenAPI 3.2 document and a structured API reference.\n *\n * @packageDocumentation\n */\n\nexport { generateOpenAPISpec, type OpenAPIDocument } from \"./openapi.js\";\nexport { buildReference, type ReferenceEntry } from \"./reference.js\";\nexport { type OpenAPISchemaObject, signatureToSchema } from \"./schema-mapper.js\";\nexport { extractSDKTypes, type SDKProperty, type SDKType } from \"./sdk-extractor.js\";\n\nimport { mkdir, writeFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport { createWalker, type ForgeConfig, type ForgeResult } from \"@forge-ts/core\";\nimport { generateOpenAPISpec } from \"./openapi.js\";\nimport { extractSDKTypes } from \"./sdk-extractor.js\";\n\n/**\n * Runs the API generation pipeline: walk → extract → generate → write.\n *\n * @param config - The resolved {@link ForgeConfig} for the project.\n * @returns A {@link ForgeResult} with success/failure and any diagnostics.\n * @example\n * ```typescript\n * import { generateApi } from \"@forge-ts/api\";\n * const result = await generateApi(config);\n * console.log(result.success); // true if spec was written successfully\n * ```\n * @public\n * @packageDocumentation\n */\nexport async function generateApi(config: ForgeConfig): Promise<ForgeResult> {\n\tconst start = Date.now();\n\n\tconst walker = createWalker(config);\n\tconst symbols = walker.walk();\n\tconst sdkTypes = extractSDKTypes(symbols);\n\tconst spec = generateOpenAPISpec(config, sdkTypes, symbols);\n\n\tawait mkdir(dirname(config.api.openapiPath), { recursive: true });\n\tawait writeFile(config.api.openapiPath, JSON.stringify(spec, null, 2), \"utf8\");\n\n\treturn {\n\t\tsuccess: true,\n\t\tsymbols,\n\t\terrors: [],\n\t\twarnings: [],\n\t\tduration: Date.now() - start,\n\t};\n}\n"],"mappings":"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","names":["Visibility","Visibility"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forge-ts/api",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
5
  "description": "OpenAPI spec and API reference generator for forge-ts",
6
6
  "license": "MIT",
@@ -24,7 +24,7 @@
24
24
  }
25
25
  },
26
26
  "dependencies": {
27
- "@forge-ts/core": "0.3.0"
27
+ "@forge-ts/core": "0.4.0"
28
28
  },
29
29
  "devDependencies": {
30
30
  "tsup": "^8.3.5",