@apifn/docsfn 0.0.2 → 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 ADDED
@@ -0,0 +1,128 @@
1
+ # @apifn/docsfn
2
+
3
+ docsfn plugin for ApiFn API reference. Turn an OpenAPI document into docsfn content entries and render them as an interactive API reference (React or Svelte) with embedded Try-It consoles and performance metrics.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @apifn/docsfn
9
+ ```
10
+
11
+ `docsfn` is an **optional** peer dependency — this package defines the minimal `DocsContentProvider` / `RawContentEntry` interfaces itself, so you get correct types even without docsfn installed.
12
+ The package-local build declares its React type dependencies and the file-loading
13
+ tests create their own temporary OpenAPI fixtures. `npm install --workspaces=false`,
14
+ `npm run build`, and `npm test` do not require a sibling CLI checkout or workspace
15
+ type hoisting.
16
+
17
+ ## Features
18
+
19
+ - **Content provider** — Convert an OpenAPI spec into docsfn `RawContentEntry[]`
20
+ - **Tag splitting** — One page per tag (default) or a single full-spec page
21
+ - **Sidebar generation** — Auto-built navigation with per-endpoint anchors
22
+ - **Reference renderers** — `ApifnApiReference` for React and Svelte, wrapping the `@apifn/react` / `@apifn/svelte` components
23
+
24
+ ---
25
+
26
+ ## createApifnProvider
27
+
28
+ Create a `DocsContentProvider` — a callable that returns `RawContentEntry[]` (memoized) and carries an `.entries` array.
29
+
30
+ ```typescript
31
+ import { createApifnProvider } from "@apifn/docsfn";
32
+
33
+ const provider = createApifnProvider({
34
+ specPath: "./openapi.yml", // or: spec: openApiDocument
35
+ basePath: "/api",
36
+ splitByTag: true,
37
+ });
38
+
39
+ const entries = await provider(); // RawContentEntry[]
40
+ ```
41
+
42
+ ### CreateApifnProviderOptions
43
+
44
+ | Option | Type | Default | Description |
45
+ |--------|------|---------|-------------|
46
+ | `spec` | `OpenAPIDocument` | — | The document (provide this or `specPath`) |
47
+ | `specPath` | `string` | — | Path to a JSON or YAML spec |
48
+ | `basePath` | `string` | `"/api"` | Slug prefix for generated pages |
49
+ | `splitByTag` | `boolean` | `true` | One entry per tag, or a single full-spec entry |
50
+ | `baseUrl` | `string` | — | Base URL embedded in entries (for Try-It) |
51
+
52
+ With `splitByTag: true`, each tag becomes a `RawContentEntry`. The tag is lowercased and each run of non-alphanumeric characters becomes `-`, so `My Tag Group` produces `${basePath}/my-tag-group`. Sidebar links use the lowercased method followed by the path with each non-alphanumeric character replaced by `-`; for example, `GET /users/{id}` links to `${slug}#get--users--id-`. With `splitByTag: false`, a single entry contains every endpoint. Path-level parameters are merged into each operation.
53
+
54
+ ---
55
+
56
+ ## Rendering the reference
57
+
58
+ The package root (`@apifn/docsfn`) exports the provider and types. Renderer components are available from framework-specific public subpaths:
59
+
60
+ - React: `@apifn/docsfn/react`
61
+ - Svelte: `@apifn/docsfn/svelte`
62
+
63
+ Pass each `RawContentEntry` returned by the provider to the renderer used by your docsfn integration.
64
+
65
+ React endpoint headers are native disclosure buttons with an expanded-state
66
+ announcement, so collapsed endpoints remain reachable and operable by keyboard.
67
+
68
+ ```tsx
69
+ import { ApifnApiReference } from "@apifn/docsfn/react";
70
+
71
+ <ApifnApiReference
72
+ entry={entry}
73
+ tryIt
74
+ baseUrl="https://api.example.com"
75
+ theme="light"
76
+ performanceMetrics={{
77
+ "GET /users": { p50Ms: 12, p95Ms: 40, p99Ms: 90, errorRatePct: 0.5, requestsPerMinute: 320 },
78
+ }}
79
+ />
80
+ ```
81
+
82
+ The Svelte renderer takes the same props (`entry`, `tryIt`, `baseUrl`, `theme`, `performanceMetrics`):
83
+
84
+ ```svelte
85
+ <script lang="ts">
86
+ import ApifnApiReference from "@apifn/docsfn/svelte";
87
+ </script>
88
+
89
+ <ApifnApiReference {entry} tryIt theme="auto" />
90
+ ```
91
+
92
+ ### ApifnApiReferenceProps
93
+
94
+ | Prop | Type | Default | Description |
95
+ |------|------|---------|-------------|
96
+ | `entry` | `RawContentEntry` | — | A single entry from `createApifnProvider` (required) |
97
+ | `tryIt` | `boolean` | `false` | Show an embedded Try-It console per endpoint |
98
+ | `baseUrl` | `string` | See below | Base URL for Try-It requests |
99
+ | `theme` | `"light" \| "dark" \| "auto"` | `"auto"` | Theme for embedded components |
100
+ | `performanceMetrics` | `Record<string, …>` | — | Per-endpoint metrics keyed `"METHOD /path"` (you supply `p50Ms`/`p95Ms`/`p99Ms`/`errorRatePct`/`requestsPerMinute`) |
101
+
102
+ Each endpoint renders as a collapsible card with Docs / Try It / Performance tabs, delegating to the `EndpointViewer`, `TryIt`, and `PerformanceOverlay` components from `@apifn/react` / `@apifn/svelte`.
103
+
104
+ The Try-It base URL is selected in this order: the renderer's `baseUrl` prop, the provider's `baseUrl` embedded in the entry, the first URL in `entry.spec.servers`, then `https://api.example.com` as a placeholder fallback.
105
+
106
+ ---
107
+
108
+ ## How it plugs into docsfn
109
+
110
+ `createApifnProvider(...)` yields a `DocsContentProvider` suitable for a docsfn content pipeline. Each `RawContentEntry` carries `kind: "api"`, a `sidebar` group for navigation, and the full `spec`. Register the provider and the appropriate `ApifnApiReference` renderer in your docsfn application according to its integration API.
111
+
112
+ ## Exports
113
+
114
+ ```typescript
115
+ export { createApifnProvider }
116
+ export type {
117
+ DocsContentProvider, RawContentEntry, ApiEndpoint,
118
+ SidebarItem, SidebarLink, SidebarGroup,
119
+ CreateApifnProviderOptions, ApifnApiReferenceProps,
120
+ OpenAPIDocument, OperationObject,
121
+ }
122
+ // React: import { ApifnApiReference } from "@apifn/docsfn/react"
123
+ // Svelte: import ApifnApiReference from "@apifn/docsfn/svelte"
124
+ ```
125
+
126
+ ## License
127
+
128
+ MIT
package/dist/index.cjs CHANGED
@@ -18,11 +18,11 @@ var __copyProps = (to, from, except, desc) => {
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
 
20
20
  // src/index.ts
21
- var index_exports = {};
22
- __export(index_exports, {
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
23
  createApifnProvider: () => createApifnProvider
24
24
  });
25
- module.exports = __toCommonJS(index_exports);
25
+ module.exports = __toCommonJS(src_exports);
26
26
 
27
27
  // src/provider.ts
28
28
  var import_promises = require("fs/promises");
@@ -99,7 +99,8 @@ function buildSidebarGroup(groupLabel, endpoints, slug) {
99
99
  function buildEntries(spec, options) {
100
100
  const {
101
101
  basePath = "/api",
102
- splitByTag = true
102
+ splitByTag = true,
103
+ baseUrl
103
104
  } = options;
104
105
  const info = spec.info;
105
106
  const allEndpoints = collectEndpoints(spec);
@@ -113,7 +114,8 @@ function buildEntries(spec, options) {
113
114
  title,
114
115
  endpoints: allEndpoints,
115
116
  sidebar,
116
- spec
117
+ spec,
118
+ ...baseUrl !== void 0 ? { baseUrl } : {}
117
119
  }];
118
120
  }
119
121
  const tagMap = /* @__PURE__ */ new Map();
@@ -132,7 +134,8 @@ function buildEntries(spec, options) {
132
134
  tag,
133
135
  endpoints,
134
136
  sidebar,
135
- spec
137
+ spec,
138
+ ...baseUrl !== void 0 ? { baseUrl } : {}
136
139
  };
137
140
  });
138
141
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/provider.ts"],"sourcesContent":["/**\n * @apifn/docsfn — public API\n *\n * Provides a DocsContentProvider and API reference renderer components\n * for embedding ApiFn API documentation in docsfn sites.\n */\n\n// Provider\nexport { createApifnProvider } from \"./provider.js\";\n\n// Types\nexport type {\n DocsContentProvider,\n RawContentEntry,\n ApiEndpoint,\n SidebarItem,\n SidebarLink,\n SidebarGroup,\n CreateApifnProviderOptions,\n ApifnApiReferenceProps,\n OpenAPIDocument,\n OperationObject,\n} from \"./types.js\";\n","/**\n * createApifnProvider — DocsContentProvider implementation (DOCS-001, DOCS-004, DOCS-006).\n *\n * Accepts an OpenAPI document (object or file path), groups endpoints by tag,\n * and returns RawContentEntry[] suitable for docsfn's manifest pipeline.\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport { resolve } from \"node:path\";\nimport { parseOpenAPI, type OpenAPIDocument, type OperationObject } from \"@apifn/core\";\nimport type {\n CreateApifnProviderOptions,\n DocsContentProvider,\n RawContentEntry,\n ApiEndpoint,\n SidebarGroup,\n SidebarLink,\n} from \"./types.js\";\n\nconst HTTP_METHODS = [\n \"get\", \"post\", \"put\", \"patch\", \"delete\", \"head\", \"options\",\n] as const;\n\ntype OperationParameters = NonNullable<OperationObject[\"parameters\"]>;\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction tagToSlug(tag: string, basePath: string): string {\n const segment = tag.toLowerCase().replace(/[^a-z0-9]+/g, \"-\");\n return `${basePath.replace(/\\/$/, \"\")}/${segment}`;\n}\n\nfunction collectEndpoints(spec: OpenAPIDocument): ApiEndpoint[] {\n const items: ApiEndpoint[] = [];\n for (const [path, pathItem] of Object.entries(spec.paths ?? {})) {\n if (!pathItem) continue;\n const pathParameters = Array.isArray(pathItem.parameters) ? pathItem.parameters : [];\n for (const method of HTTP_METHODS) {\n const op = pathItem[method] as OperationObject | undefined;\n if (!op) continue;\n const operation = mergePathParameters(pathParameters, op);\n const tags = (op.tags as string[]) ?? [\"default\"];\n items.push({ path, method, operation, tag: tags[0] ?? \"default\" });\n }\n }\n return items;\n}\n\nfunction parameterKey(parameter: unknown): string | undefined {\n if (!parameter || typeof parameter !== \"object\") {\n return undefined;\n }\n\n const reference = parameter as { $ref?: unknown };\n if (typeof reference.$ref === \"string\") {\n return `$ref:${reference.$ref}`;\n }\n\n const candidate = parameter as { name?: unknown; in?: unknown };\n if (typeof candidate.name !== \"string\" || typeof candidate.in !== \"string\") {\n return undefined;\n }\n\n return `${candidate.in}:${candidate.name}`;\n}\n\nfunction mergePathParameters(pathParameters: OperationParameters, operation: OperationObject): OperationObject {\n if (pathParameters.length === 0) {\n return operation;\n }\n\n const operationParameters: OperationParameters = Array.isArray(operation.parameters) ? operation.parameters : [];\n const operationKeys = new Set(operationParameters.map(parameterKey).filter((key): key is string => Boolean(key)));\n const inheritedParameters = pathParameters.filter((parameter) => {\n const key = parameterKey(parameter);\n return !key || !operationKeys.has(key);\n });\n\n if (inheritedParameters.length === 0) {\n return operation;\n }\n\n return {\n ...operation,\n parameters: [...inheritedParameters, ...operationParameters],\n };\n}\n\nfunction buildSidebarGroup(\n groupLabel: string,\n endpoints: ApiEndpoint[],\n slug: string\n): SidebarGroup {\n const links: SidebarLink[] = endpoints.map((e) => ({\n type: \"link\",\n label: `${e.method.toUpperCase()} ${e.path}`,\n href: `${slug}#${e.method.toLowerCase()}-${e.path.replace(/[^a-z0-9]/gi, \"-\")}`,\n }));\n return { type: \"group\", label: groupLabel, items: links };\n}\n\nfunction buildEntries(\n spec: OpenAPIDocument,\n options: CreateApifnProviderOptions\n): RawContentEntry[] {\n const {\n basePath = \"/api\",\n splitByTag = true,\n } = options;\n\n const info = spec.info as { title?: string; version?: string } | undefined;\n const allEndpoints = collectEndpoints(spec);\n\n if (!splitByTag) {\n // Single full-spec entry\n const slug = basePath.replace(/\\/$/, \"\") || \"/api\";\n const title = info?.title ?? \"API Reference\";\n const sidebar = buildSidebarGroup(title, allEndpoints, slug);\n return [{\n kind: \"api\",\n slug,\n title,\n endpoints: allEndpoints,\n sidebar,\n spec,\n }];\n }\n\n // Split by tag — one entry per tag (DOCS-006)\n const tagMap = new Map<string, ApiEndpoint[]>();\n for (const endpoint of allEndpoints) {\n if (!tagMap.has(endpoint.tag)) tagMap.set(endpoint.tag, []);\n tagMap.get(endpoint.tag)!.push(endpoint);\n }\n\n return [...tagMap.entries()].map(([tag, endpoints]) => {\n const slug = tagToSlug(tag, basePath);\n const title = `${tag.charAt(0).toUpperCase()}${tag.slice(1)}`;\n const sidebar = buildSidebarGroup(title, endpoints, slug);\n return {\n kind: \"api\" as const,\n slug,\n title,\n tag,\n endpoints,\n sidebar,\n spec,\n };\n });\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Creates a DocsContentProvider that produces RawContentEntry[] for docsfn.\n *\n * @example\n * const provider = createApifnProvider({ specPath: \"./openapi.yml\", splitByTag: true });\n * const entries = await provider();\n */\nexport function createApifnProvider(\n options: CreateApifnProviderOptions\n): DocsContentProvider {\n let cachedEntries: RawContentEntry[] | null = null;\n\n async function loadSpec(): Promise<OpenAPIDocument> {\n if (options.spec) return options.spec;\n if (options.specPath) {\n const abs = resolve(options.specPath);\n const raw = await readFile(abs, \"utf8\");\n return parseOpenAPI(raw);\n }\n throw new Error(\"createApifnProvider: must provide either `spec` or `specPath`\");\n }\n\n const provider = async (): Promise<RawContentEntry[]> => {\n if (cachedEntries) return cachedEntries;\n const spec = await loadSpec();\n cachedEntries = buildEntries(spec, options);\n provider.entries = cachedEntries;\n return cachedEntries;\n };\n\n provider.entries = [] as RawContentEntry[];\n return provider as DocsContentProvider;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/provider.ts"],"sourcesContent":["/**\n * @apifn/docsfn — public API\n *\n * Provides a DocsContentProvider and API reference renderer components\n * for embedding ApiFn API documentation in docsfn sites.\n */\n\n// Provider\nexport { createApifnProvider } from \"./provider.js\";\n\n// Types\nexport type {\n DocsContentProvider,\n RawContentEntry,\n ApiEndpoint,\n SidebarItem,\n SidebarLink,\n SidebarGroup,\n CreateApifnProviderOptions,\n ApifnApiReferenceProps,\n OpenAPIDocument,\n OperationObject,\n} from \"./types.js\";\n","/**\n * createApifnProvider — DocsContentProvider implementation (DOCS-001, DOCS-004, DOCS-006).\n *\n * Accepts an OpenAPI document (object or file path), groups endpoints by tag,\n * and returns RawContentEntry[] suitable for docsfn's manifest pipeline.\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport { resolve } from \"node:path\";\nimport { parseOpenAPI, type OpenAPIDocument, type OperationObject } from \"@apifn/core\";\nimport type {\n CreateApifnProviderOptions,\n DocsContentProvider,\n RawContentEntry,\n ApiEndpoint,\n SidebarGroup,\n SidebarLink,\n} from \"./types.js\";\n\nconst HTTP_METHODS = [\n \"get\", \"post\", \"put\", \"patch\", \"delete\", \"head\", \"options\",\n] as const;\n\ntype OperationParameters = NonNullable<OperationObject[\"parameters\"]>;\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction tagToSlug(tag: string, basePath: string): string {\n const segment = tag.toLowerCase().replace(/[^a-z0-9]+/g, \"-\");\n return `${basePath.replace(/\\/$/, \"\")}/${segment}`;\n}\n\nfunction collectEndpoints(spec: OpenAPIDocument): ApiEndpoint[] {\n const items: ApiEndpoint[] = [];\n for (const [path, pathItem] of Object.entries(spec.paths ?? {})) {\n if (!pathItem) continue;\n const pathParameters = Array.isArray(pathItem.parameters) ? pathItem.parameters : [];\n for (const method of HTTP_METHODS) {\n const op = pathItem[method] as OperationObject | undefined;\n if (!op) continue;\n const operation = mergePathParameters(pathParameters, op);\n const tags = (op.tags as string[]) ?? [\"default\"];\n items.push({ path, method, operation, tag: tags[0] ?? \"default\" });\n }\n }\n return items;\n}\n\nfunction parameterKey(parameter: unknown): string | undefined {\n if (!parameter || typeof parameter !== \"object\") {\n return undefined;\n }\n\n const reference = parameter as { $ref?: unknown };\n if (typeof reference.$ref === \"string\") {\n return `$ref:${reference.$ref}`;\n }\n\n const candidate = parameter as { name?: unknown; in?: unknown };\n if (typeof candidate.name !== \"string\" || typeof candidate.in !== \"string\") {\n return undefined;\n }\n\n return `${candidate.in}:${candidate.name}`;\n}\n\nfunction mergePathParameters(pathParameters: OperationParameters, operation: OperationObject): OperationObject {\n if (pathParameters.length === 0) {\n return operation;\n }\n\n const operationParameters: OperationParameters = Array.isArray(operation.parameters) ? operation.parameters : [];\n const operationKeys = new Set(operationParameters.map(parameterKey).filter((key): key is string => Boolean(key)));\n const inheritedParameters = pathParameters.filter((parameter) => {\n const key = parameterKey(parameter);\n return !key || !operationKeys.has(key);\n });\n\n if (inheritedParameters.length === 0) {\n return operation;\n }\n\n return {\n ...operation,\n parameters: [...inheritedParameters, ...operationParameters],\n };\n}\n\nfunction buildSidebarGroup(\n groupLabel: string,\n endpoints: ApiEndpoint[],\n slug: string\n): SidebarGroup {\n const links: SidebarLink[] = endpoints.map((e) => ({\n type: \"link\",\n label: `${e.method.toUpperCase()} ${e.path}`,\n href: `${slug}#${e.method.toLowerCase()}-${e.path.replace(/[^a-z0-9]/gi, \"-\")}`,\n }));\n return { type: \"group\", label: groupLabel, items: links };\n}\n\nfunction buildEntries(\n spec: OpenAPIDocument,\n options: CreateApifnProviderOptions\n): RawContentEntry[] {\n const {\n basePath = \"/api\",\n splitByTag = true,\n baseUrl,\n } = options;\n\n const info = spec.info as { title?: string; version?: string } | undefined;\n const allEndpoints = collectEndpoints(spec);\n\n if (!splitByTag) {\n // Single full-spec entry\n const slug = basePath.replace(/\\/$/, \"\") || \"/api\";\n const title = info?.title ?? \"API Reference\";\n const sidebar = buildSidebarGroup(title, allEndpoints, slug);\n return [{\n kind: \"api\",\n slug,\n title,\n endpoints: allEndpoints,\n sidebar,\n spec,\n ...(baseUrl !== undefined ? { baseUrl } : {}),\n }];\n }\n\n // Split by tag — one entry per tag (DOCS-006)\n const tagMap = new Map<string, ApiEndpoint[]>();\n for (const endpoint of allEndpoints) {\n if (!tagMap.has(endpoint.tag)) tagMap.set(endpoint.tag, []);\n tagMap.get(endpoint.tag)!.push(endpoint);\n }\n\n return [...tagMap.entries()].map(([tag, endpoints]) => {\n const slug = tagToSlug(tag, basePath);\n const title = `${tag.charAt(0).toUpperCase()}${tag.slice(1)}`;\n const sidebar = buildSidebarGroup(title, endpoints, slug);\n return {\n kind: \"api\" as const,\n slug,\n title,\n tag,\n endpoints,\n sidebar,\n spec,\n ...(baseUrl !== undefined ? { baseUrl } : {}),\n };\n });\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Creates a DocsContentProvider that produces RawContentEntry[] for docsfn.\n *\n * @example\n * const provider = createApifnProvider({ specPath: \"./openapi.yml\", splitByTag: true });\n * const entries = await provider();\n */\nexport function createApifnProvider(\n options: CreateApifnProviderOptions\n): DocsContentProvider {\n let cachedEntries: RawContentEntry[] | null = null;\n\n async function loadSpec(): Promise<OpenAPIDocument> {\n if (options.spec) return options.spec;\n if (options.specPath) {\n const abs = resolve(options.specPath);\n const raw = await readFile(abs, \"utf8\");\n return parseOpenAPI(raw);\n }\n throw new Error(\"createApifnProvider: must provide either `spec` or `specPath`\");\n }\n\n const provider = async (): Promise<RawContentEntry[]> => {\n if (cachedEntries) return cachedEntries;\n const spec = await loadSpec();\n cachedEntries = buildEntries(spec, options);\n provider.entries = cachedEntries;\n return cachedEntries;\n };\n\n provider.entries = [] as RawContentEntry[];\n return provider as DocsContentProvider;\n}\n"],"mappings":"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","names":[]}
package/dist/index.d.cts CHANGED
@@ -1,98 +1,7 @@
1
- import { OperationObject, OpenAPIDocument } from '@apifn/core';
1
+ import { C as CreateApifnProviderOptions, D as DocsContentProvider } from './types-D4LTWU9V.cjs';
2
+ export { A as ApiEndpoint, a as ApifnApiReferenceProps, R as RawContentEntry, S as SidebarGroup, b as SidebarItem, c as SidebarLink } from './types-D4LTWU9V.cjs';
2
3
  export { OpenAPIDocument, OperationObject } from '@apifn/core';
3
4
 
4
- /**
5
- * docsfn integration types for @apifn/docsfn.
6
- *
7
- * docsfn is an optional peer dependency that may not be installed.
8
- * We define the minimal interfaces needed for our provider here so
9
- * consumers get correct types even without the full docsfn package.
10
- */
11
-
12
- /** Sidebar leaf link. */
13
- interface SidebarLink {
14
- type: "link";
15
- label: string;
16
- href: string;
17
- }
18
- /** Sidebar group containing links. */
19
- interface SidebarGroup {
20
- type: "group";
21
- label: string;
22
- items: Array<SidebarLink | SidebarGroup>;
23
- }
24
- type SidebarItem = SidebarLink | SidebarGroup;
25
- /** A processed endpoint grouped in a docsfn page. */
26
- interface ApiEndpoint {
27
- path: string;
28
- method: string;
29
- operation: OperationObject;
30
- tag: string;
31
- }
32
- /**
33
- * A single raw content entry produced by a DocsContentProvider.
34
- * `kind: "api"` signals to docsfn that this is an API reference page.
35
- */
36
- interface RawContentEntry {
37
- kind: "api";
38
- /** URL slug for this page (e.g. "/api/users") */
39
- slug: string;
40
- /** Human-readable page title */
41
- title: string;
42
- /** Tag name when split by tag */
43
- tag?: string;
44
- /** Endpoint list for this page */
45
- endpoints: ApiEndpoint[];
46
- /** Sidebar items generated for this entry */
47
- sidebar: SidebarGroup;
48
- /** Full OpenAPI spec (so renderers can look up $refs etc.) */
49
- spec: OpenAPIDocument;
50
- }
51
- /** A DocsContentProvider is a callable that returns RawContentEntry[]. */
52
- interface DocsContentProvider {
53
- (): Promise<RawContentEntry[]>;
54
- entries: RawContentEntry[];
55
- }
56
- interface CreateApifnProviderOptions {
57
- /** OpenAPI document (object) */
58
- spec?: OpenAPIDocument;
59
- /** File path to a JSON or YAML OpenAPI spec */
60
- specPath?: string;
61
- /** Base path prefix for slug generation (default: "/api") */
62
- basePath?: string;
63
- /**
64
- * Split the spec into one page per tag.
65
- * - `true` → one RawContentEntry per tag
66
- * - `false` → one RawContentEntry with all endpoints
67
- * @default true
68
- */
69
- splitByTag?: boolean;
70
- /** Optional base URL to embed in entries (for TryIt) */
71
- baseUrl?: string;
72
- }
73
- interface PerformanceMetrics {
74
- endpoint: string;
75
- method: string;
76
- p50Ms: number;
77
- p95Ms: number;
78
- p99Ms: number;
79
- errorRatePct: number;
80
- requestsPerMinute: number;
81
- lastUpdated: string;
82
- }
83
- interface ApifnApiReferenceProps {
84
- /** Single content entry from createApifnProvider */
85
- entry: RawContentEntry;
86
- /** Show embedded Try-It console per endpoint (DOCS-005) */
87
- tryIt?: boolean;
88
- /** Base URL for Try-It requests */
89
- baseUrl?: string;
90
- /** Theme for the embedded components */
91
- theme?: "light" | "dark" | "auto";
92
- /** Optional watchfn metrics per endpoint key ("METHOD /path") */
93
- performanceMetrics?: Record<string, Omit<PerformanceMetrics, "endpoint" | "method" | "lastUpdated">>;
94
- }
95
-
96
5
  /**
97
6
  * createApifnProvider — DocsContentProvider implementation (DOCS-001, DOCS-004, DOCS-006).
98
7
  *
@@ -109,4 +18,4 @@ interface ApifnApiReferenceProps {
109
18
  */
110
19
  declare function createApifnProvider(options: CreateApifnProviderOptions): DocsContentProvider;
111
20
 
112
- export { type ApiEndpoint, type ApifnApiReferenceProps, type CreateApifnProviderOptions, type DocsContentProvider, type RawContentEntry, type SidebarGroup, type SidebarItem, type SidebarLink, createApifnProvider };
21
+ export { CreateApifnProviderOptions, DocsContentProvider, createApifnProvider };
package/dist/index.d.ts CHANGED
@@ -1,98 +1,7 @@
1
- import { OperationObject, OpenAPIDocument } from '@apifn/core';
1
+ import { C as CreateApifnProviderOptions, D as DocsContentProvider } from './types-D4LTWU9V.js';
2
+ export { A as ApiEndpoint, a as ApifnApiReferenceProps, R as RawContentEntry, S as SidebarGroup, b as SidebarItem, c as SidebarLink } from './types-D4LTWU9V.js';
2
3
  export { OpenAPIDocument, OperationObject } from '@apifn/core';
3
4
 
4
- /**
5
- * docsfn integration types for @apifn/docsfn.
6
- *
7
- * docsfn is an optional peer dependency that may not be installed.
8
- * We define the minimal interfaces needed for our provider here so
9
- * consumers get correct types even without the full docsfn package.
10
- */
11
-
12
- /** Sidebar leaf link. */
13
- interface SidebarLink {
14
- type: "link";
15
- label: string;
16
- href: string;
17
- }
18
- /** Sidebar group containing links. */
19
- interface SidebarGroup {
20
- type: "group";
21
- label: string;
22
- items: Array<SidebarLink | SidebarGroup>;
23
- }
24
- type SidebarItem = SidebarLink | SidebarGroup;
25
- /** A processed endpoint grouped in a docsfn page. */
26
- interface ApiEndpoint {
27
- path: string;
28
- method: string;
29
- operation: OperationObject;
30
- tag: string;
31
- }
32
- /**
33
- * A single raw content entry produced by a DocsContentProvider.
34
- * `kind: "api"` signals to docsfn that this is an API reference page.
35
- */
36
- interface RawContentEntry {
37
- kind: "api";
38
- /** URL slug for this page (e.g. "/api/users") */
39
- slug: string;
40
- /** Human-readable page title */
41
- title: string;
42
- /** Tag name when split by tag */
43
- tag?: string;
44
- /** Endpoint list for this page */
45
- endpoints: ApiEndpoint[];
46
- /** Sidebar items generated for this entry */
47
- sidebar: SidebarGroup;
48
- /** Full OpenAPI spec (so renderers can look up $refs etc.) */
49
- spec: OpenAPIDocument;
50
- }
51
- /** A DocsContentProvider is a callable that returns RawContentEntry[]. */
52
- interface DocsContentProvider {
53
- (): Promise<RawContentEntry[]>;
54
- entries: RawContentEntry[];
55
- }
56
- interface CreateApifnProviderOptions {
57
- /** OpenAPI document (object) */
58
- spec?: OpenAPIDocument;
59
- /** File path to a JSON or YAML OpenAPI spec */
60
- specPath?: string;
61
- /** Base path prefix for slug generation (default: "/api") */
62
- basePath?: string;
63
- /**
64
- * Split the spec into one page per tag.
65
- * - `true` → one RawContentEntry per tag
66
- * - `false` → one RawContentEntry with all endpoints
67
- * @default true
68
- */
69
- splitByTag?: boolean;
70
- /** Optional base URL to embed in entries (for TryIt) */
71
- baseUrl?: string;
72
- }
73
- interface PerformanceMetrics {
74
- endpoint: string;
75
- method: string;
76
- p50Ms: number;
77
- p95Ms: number;
78
- p99Ms: number;
79
- errorRatePct: number;
80
- requestsPerMinute: number;
81
- lastUpdated: string;
82
- }
83
- interface ApifnApiReferenceProps {
84
- /** Single content entry from createApifnProvider */
85
- entry: RawContentEntry;
86
- /** Show embedded Try-It console per endpoint (DOCS-005) */
87
- tryIt?: boolean;
88
- /** Base URL for Try-It requests */
89
- baseUrl?: string;
90
- /** Theme for the embedded components */
91
- theme?: "light" | "dark" | "auto";
92
- /** Optional watchfn metrics per endpoint key ("METHOD /path") */
93
- performanceMetrics?: Record<string, Omit<PerformanceMetrics, "endpoint" | "method" | "lastUpdated">>;
94
- }
95
-
96
5
  /**
97
6
  * createApifnProvider — DocsContentProvider implementation (DOCS-001, DOCS-004, DOCS-006).
98
7
  *
@@ -109,4 +18,4 @@ interface ApifnApiReferenceProps {
109
18
  */
110
19
  declare function createApifnProvider(options: CreateApifnProviderOptions): DocsContentProvider;
111
20
 
112
- export { type ApiEndpoint, type ApifnApiReferenceProps, type CreateApifnProviderOptions, type DocsContentProvider, type RawContentEntry, type SidebarGroup, type SidebarItem, type SidebarLink, createApifnProvider };
21
+ export { CreateApifnProviderOptions, DocsContentProvider, createApifnProvider };
package/dist/index.js CHANGED
@@ -73,7 +73,8 @@ function buildSidebarGroup(groupLabel, endpoints, slug) {
73
73
  function buildEntries(spec, options) {
74
74
  const {
75
75
  basePath = "/api",
76
- splitByTag = true
76
+ splitByTag = true,
77
+ baseUrl
77
78
  } = options;
78
79
  const info = spec.info;
79
80
  const allEndpoints = collectEndpoints(spec);
@@ -87,7 +88,8 @@ function buildEntries(spec, options) {
87
88
  title,
88
89
  endpoints: allEndpoints,
89
90
  sidebar,
90
- spec
91
+ spec,
92
+ ...baseUrl !== void 0 ? { baseUrl } : {}
91
93
  }];
92
94
  }
93
95
  const tagMap = /* @__PURE__ */ new Map();
@@ -106,7 +108,8 @@ function buildEntries(spec, options) {
106
108
  tag,
107
109
  endpoints,
108
110
  sidebar,
109
- spec
111
+ spec,
112
+ ...baseUrl !== void 0 ? { baseUrl } : {}
110
113
  };
111
114
  });
112
115
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/provider.ts"],"sourcesContent":["/**\n * createApifnProvider — DocsContentProvider implementation (DOCS-001, DOCS-004, DOCS-006).\n *\n * Accepts an OpenAPI document (object or file path), groups endpoints by tag,\n * and returns RawContentEntry[] suitable for docsfn's manifest pipeline.\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport { resolve } from \"node:path\";\nimport { parseOpenAPI, type OpenAPIDocument, type OperationObject } from \"@apifn/core\";\nimport type {\n CreateApifnProviderOptions,\n DocsContentProvider,\n RawContentEntry,\n ApiEndpoint,\n SidebarGroup,\n SidebarLink,\n} from \"./types.js\";\n\nconst HTTP_METHODS = [\n \"get\", \"post\", \"put\", \"patch\", \"delete\", \"head\", \"options\",\n] as const;\n\ntype OperationParameters = NonNullable<OperationObject[\"parameters\"]>;\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction tagToSlug(tag: string, basePath: string): string {\n const segment = tag.toLowerCase().replace(/[^a-z0-9]+/g, \"-\");\n return `${basePath.replace(/\\/$/, \"\")}/${segment}`;\n}\n\nfunction collectEndpoints(spec: OpenAPIDocument): ApiEndpoint[] {\n const items: ApiEndpoint[] = [];\n for (const [path, pathItem] of Object.entries(spec.paths ?? {})) {\n if (!pathItem) continue;\n const pathParameters = Array.isArray(pathItem.parameters) ? pathItem.parameters : [];\n for (const method of HTTP_METHODS) {\n const op = pathItem[method] as OperationObject | undefined;\n if (!op) continue;\n const operation = mergePathParameters(pathParameters, op);\n const tags = (op.tags as string[]) ?? [\"default\"];\n items.push({ path, method, operation, tag: tags[0] ?? \"default\" });\n }\n }\n return items;\n}\n\nfunction parameterKey(parameter: unknown): string | undefined {\n if (!parameter || typeof parameter !== \"object\") {\n return undefined;\n }\n\n const reference = parameter as { $ref?: unknown };\n if (typeof reference.$ref === \"string\") {\n return `$ref:${reference.$ref}`;\n }\n\n const candidate = parameter as { name?: unknown; in?: unknown };\n if (typeof candidate.name !== \"string\" || typeof candidate.in !== \"string\") {\n return undefined;\n }\n\n return `${candidate.in}:${candidate.name}`;\n}\n\nfunction mergePathParameters(pathParameters: OperationParameters, operation: OperationObject): OperationObject {\n if (pathParameters.length === 0) {\n return operation;\n }\n\n const operationParameters: OperationParameters = Array.isArray(operation.parameters) ? operation.parameters : [];\n const operationKeys = new Set(operationParameters.map(parameterKey).filter((key): key is string => Boolean(key)));\n const inheritedParameters = pathParameters.filter((parameter) => {\n const key = parameterKey(parameter);\n return !key || !operationKeys.has(key);\n });\n\n if (inheritedParameters.length === 0) {\n return operation;\n }\n\n return {\n ...operation,\n parameters: [...inheritedParameters, ...operationParameters],\n };\n}\n\nfunction buildSidebarGroup(\n groupLabel: string,\n endpoints: ApiEndpoint[],\n slug: string\n): SidebarGroup {\n const links: SidebarLink[] = endpoints.map((e) => ({\n type: \"link\",\n label: `${e.method.toUpperCase()} ${e.path}`,\n href: `${slug}#${e.method.toLowerCase()}-${e.path.replace(/[^a-z0-9]/gi, \"-\")}`,\n }));\n return { type: \"group\", label: groupLabel, items: links };\n}\n\nfunction buildEntries(\n spec: OpenAPIDocument,\n options: CreateApifnProviderOptions\n): RawContentEntry[] {\n const {\n basePath = \"/api\",\n splitByTag = true,\n } = options;\n\n const info = spec.info as { title?: string; version?: string } | undefined;\n const allEndpoints = collectEndpoints(spec);\n\n if (!splitByTag) {\n // Single full-spec entry\n const slug = basePath.replace(/\\/$/, \"\") || \"/api\";\n const title = info?.title ?? \"API Reference\";\n const sidebar = buildSidebarGroup(title, allEndpoints, slug);\n return [{\n kind: \"api\",\n slug,\n title,\n endpoints: allEndpoints,\n sidebar,\n spec,\n }];\n }\n\n // Split by tag — one entry per tag (DOCS-006)\n const tagMap = new Map<string, ApiEndpoint[]>();\n for (const endpoint of allEndpoints) {\n if (!tagMap.has(endpoint.tag)) tagMap.set(endpoint.tag, []);\n tagMap.get(endpoint.tag)!.push(endpoint);\n }\n\n return [...tagMap.entries()].map(([tag, endpoints]) => {\n const slug = tagToSlug(tag, basePath);\n const title = `${tag.charAt(0).toUpperCase()}${tag.slice(1)}`;\n const sidebar = buildSidebarGroup(title, endpoints, slug);\n return {\n kind: \"api\" as const,\n slug,\n title,\n tag,\n endpoints,\n sidebar,\n spec,\n };\n });\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Creates a DocsContentProvider that produces RawContentEntry[] for docsfn.\n *\n * @example\n * const provider = createApifnProvider({ specPath: \"./openapi.yml\", splitByTag: true });\n * const entries = await provider();\n */\nexport function createApifnProvider(\n options: CreateApifnProviderOptions\n): DocsContentProvider {\n let cachedEntries: RawContentEntry[] | null = null;\n\n async function loadSpec(): Promise<OpenAPIDocument> {\n if (options.spec) return options.spec;\n if (options.specPath) {\n const abs = resolve(options.specPath);\n const raw = await readFile(abs, \"utf8\");\n return parseOpenAPI(raw);\n }\n throw new Error(\"createApifnProvider: must provide either `spec` or `specPath`\");\n }\n\n const provider = async (): Promise<RawContentEntry[]> => {\n if (cachedEntries) return cachedEntries;\n const spec = await loadSpec();\n cachedEntries = buildEntries(spec, options);\n provider.entries = cachedEntries;\n return cachedEntries;\n };\n\n provider.entries = [] as RawContentEntry[];\n return provider as DocsContentProvider;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/provider.ts"],"sourcesContent":["/**\n * createApifnProvider — DocsContentProvider implementation (DOCS-001, DOCS-004, DOCS-006).\n *\n * Accepts an OpenAPI document (object or file path), groups endpoints by tag,\n * and returns RawContentEntry[] suitable for docsfn's manifest pipeline.\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport { resolve } from \"node:path\";\nimport { parseOpenAPI, type OpenAPIDocument, type OperationObject } from \"@apifn/core\";\nimport type {\n CreateApifnProviderOptions,\n DocsContentProvider,\n RawContentEntry,\n ApiEndpoint,\n SidebarGroup,\n SidebarLink,\n} from \"./types.js\";\n\nconst HTTP_METHODS = [\n \"get\", \"post\", \"put\", \"patch\", \"delete\", \"head\", \"options\",\n] as const;\n\ntype OperationParameters = NonNullable<OperationObject[\"parameters\"]>;\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction tagToSlug(tag: string, basePath: string): string {\n const segment = tag.toLowerCase().replace(/[^a-z0-9]+/g, \"-\");\n return `${basePath.replace(/\\/$/, \"\")}/${segment}`;\n}\n\nfunction collectEndpoints(spec: OpenAPIDocument): ApiEndpoint[] {\n const items: ApiEndpoint[] = [];\n for (const [path, pathItem] of Object.entries(spec.paths ?? {})) {\n if (!pathItem) continue;\n const pathParameters = Array.isArray(pathItem.parameters) ? pathItem.parameters : [];\n for (const method of HTTP_METHODS) {\n const op = pathItem[method] as OperationObject | undefined;\n if (!op) continue;\n const operation = mergePathParameters(pathParameters, op);\n const tags = (op.tags as string[]) ?? [\"default\"];\n items.push({ path, method, operation, tag: tags[0] ?? \"default\" });\n }\n }\n return items;\n}\n\nfunction parameterKey(parameter: unknown): string | undefined {\n if (!parameter || typeof parameter !== \"object\") {\n return undefined;\n }\n\n const reference = parameter as { $ref?: unknown };\n if (typeof reference.$ref === \"string\") {\n return `$ref:${reference.$ref}`;\n }\n\n const candidate = parameter as { name?: unknown; in?: unknown };\n if (typeof candidate.name !== \"string\" || typeof candidate.in !== \"string\") {\n return undefined;\n }\n\n return `${candidate.in}:${candidate.name}`;\n}\n\nfunction mergePathParameters(pathParameters: OperationParameters, operation: OperationObject): OperationObject {\n if (pathParameters.length === 0) {\n return operation;\n }\n\n const operationParameters: OperationParameters = Array.isArray(operation.parameters) ? operation.parameters : [];\n const operationKeys = new Set(operationParameters.map(parameterKey).filter((key): key is string => Boolean(key)));\n const inheritedParameters = pathParameters.filter((parameter) => {\n const key = parameterKey(parameter);\n return !key || !operationKeys.has(key);\n });\n\n if (inheritedParameters.length === 0) {\n return operation;\n }\n\n return {\n ...operation,\n parameters: [...inheritedParameters, ...operationParameters],\n };\n}\n\nfunction buildSidebarGroup(\n groupLabel: string,\n endpoints: ApiEndpoint[],\n slug: string\n): SidebarGroup {\n const links: SidebarLink[] = endpoints.map((e) => ({\n type: \"link\",\n label: `${e.method.toUpperCase()} ${e.path}`,\n href: `${slug}#${e.method.toLowerCase()}-${e.path.replace(/[^a-z0-9]/gi, \"-\")}`,\n }));\n return { type: \"group\", label: groupLabel, items: links };\n}\n\nfunction buildEntries(\n spec: OpenAPIDocument,\n options: CreateApifnProviderOptions\n): RawContentEntry[] {\n const {\n basePath = \"/api\",\n splitByTag = true,\n baseUrl,\n } = options;\n\n const info = spec.info as { title?: string; version?: string } | undefined;\n const allEndpoints = collectEndpoints(spec);\n\n if (!splitByTag) {\n // Single full-spec entry\n const slug = basePath.replace(/\\/$/, \"\") || \"/api\";\n const title = info?.title ?? \"API Reference\";\n const sidebar = buildSidebarGroup(title, allEndpoints, slug);\n return [{\n kind: \"api\",\n slug,\n title,\n endpoints: allEndpoints,\n sidebar,\n spec,\n ...(baseUrl !== undefined ? { baseUrl } : {}),\n }];\n }\n\n // Split by tag — one entry per tag (DOCS-006)\n const tagMap = new Map<string, ApiEndpoint[]>();\n for (const endpoint of allEndpoints) {\n if (!tagMap.has(endpoint.tag)) tagMap.set(endpoint.tag, []);\n tagMap.get(endpoint.tag)!.push(endpoint);\n }\n\n return [...tagMap.entries()].map(([tag, endpoints]) => {\n const slug = tagToSlug(tag, basePath);\n const title = `${tag.charAt(0).toUpperCase()}${tag.slice(1)}`;\n const sidebar = buildSidebarGroup(title, endpoints, slug);\n return {\n kind: \"api\" as const,\n slug,\n title,\n tag,\n endpoints,\n sidebar,\n spec,\n ...(baseUrl !== undefined ? { baseUrl } : {}),\n };\n });\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Creates a DocsContentProvider that produces RawContentEntry[] for docsfn.\n *\n * @example\n * const provider = createApifnProvider({ specPath: \"./openapi.yml\", splitByTag: true });\n * const entries = await provider();\n */\nexport function createApifnProvider(\n options: CreateApifnProviderOptions\n): DocsContentProvider {\n let cachedEntries: RawContentEntry[] | null = null;\n\n async function loadSpec(): Promise<OpenAPIDocument> {\n if (options.spec) return options.spec;\n if (options.specPath) {\n const abs = resolve(options.specPath);\n const raw = await readFile(abs, \"utf8\");\n return parseOpenAPI(raw);\n }\n throw new Error(\"createApifnProvider: must provide either `spec` or `specPath`\");\n }\n\n const provider = async (): Promise<RawContentEntry[]> => {\n if (cachedEntries) return cachedEntries;\n const spec = await loadSpec();\n cachedEntries = buildEntries(spec, options);\n provider.entries = cachedEntries;\n return cachedEntries;\n };\n\n provider.entries = [] as RawContentEntry[];\n return provider as DocsContentProvider;\n}\n"],"mappings":"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","names":[]}