@avodado/sync 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,264 @@
1
+ import { parse } from 'yaml';
2
+ import { z } from 'zod';
3
+
4
+ // src/openapi/parse.ts
5
+ var refSchema = z.object({ $ref: z.string() }).passthrough();
6
+ var propertySchema = z.lazy(
7
+ () => z.object({
8
+ type: z.string().optional(),
9
+ format: z.string().optional(),
10
+ description: z.string().optional(),
11
+ enum: z.array(z.unknown()).optional(),
12
+ items: z.union([refSchema, propertySchema]).optional(),
13
+ properties: z.record(z.string(), z.union([refSchema, propertySchema])).optional(),
14
+ required: z.array(z.string()).optional(),
15
+ example: z.unknown().optional(),
16
+ $ref: z.string().optional()
17
+ }).passthrough()
18
+ );
19
+ var componentSchema = z.object({
20
+ type: z.string().optional(),
21
+ description: z.string().optional(),
22
+ properties: z.record(z.string(), z.union([refSchema, propertySchema])).optional(),
23
+ required: z.array(z.string()).optional(),
24
+ enum: z.array(z.unknown()).optional(),
25
+ allOf: z.array(z.union([refSchema, propertySchema])).optional()
26
+ }).passthrough();
27
+ var responseSchema = z.object({
28
+ description: z.string().optional(),
29
+ content: z.record(z.string(), z.unknown()).optional()
30
+ }).passthrough();
31
+ var parameterSchema = z.object({
32
+ name: z.string(),
33
+ in: z.string(),
34
+ required: z.boolean().optional(),
35
+ description: z.string().optional(),
36
+ schema: z.union([refSchema, propertySchema]).optional()
37
+ }).passthrough();
38
+ var operationSchema = z.object({
39
+ operationId: z.string().optional(),
40
+ summary: z.string().optional(),
41
+ description: z.string().optional(),
42
+ tags: z.array(z.string()).optional(),
43
+ parameters: z.array(parameterSchema).optional(),
44
+ requestBody: z.unknown().optional(),
45
+ responses: z.record(z.string(), responseSchema).optional()
46
+ }).passthrough();
47
+ var pathItemSchema = z.object({
48
+ get: operationSchema.optional(),
49
+ post: operationSchema.optional(),
50
+ put: operationSchema.optional(),
51
+ patch: operationSchema.optional(),
52
+ delete: operationSchema.optional(),
53
+ head: operationSchema.optional(),
54
+ options: operationSchema.optional(),
55
+ parameters: z.array(parameterSchema).optional()
56
+ }).passthrough();
57
+ var infoSchema = z.object({
58
+ title: z.string(),
59
+ description: z.string().optional(),
60
+ version: z.string()
61
+ }).passthrough();
62
+ var openApiSchema = z.object({
63
+ openapi: z.string().optional(),
64
+ swagger: z.string().optional(),
65
+ info: infoSchema,
66
+ paths: z.record(z.string(), pathItemSchema).optional(),
67
+ components: z.object({
68
+ schemas: z.record(z.string(), componentSchema).optional()
69
+ }).passthrough().optional()
70
+ }).passthrough();
71
+ var HTTP_METHODS = ["get", "post", "put", "patch", "delete"];
72
+
73
+ // src/openapi/parse.ts
74
+ function parseOpenApi(source) {
75
+ let raw;
76
+ try {
77
+ raw = parse(source);
78
+ } catch (err) {
79
+ throw new Error(`OpenAPI spec is not valid YAML/JSON: ${err.message}`);
80
+ }
81
+ const result = openApiSchema.safeParse(raw);
82
+ if (!result.success) {
83
+ const issues = result.error.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ");
84
+ throw new Error(`OpenAPI spec failed validation: ${issues}`);
85
+ }
86
+ return result.data;
87
+ }
88
+
89
+ // src/openapi/generate.ts
90
+ var METHOD_ORDER = {
91
+ get: 0,
92
+ post: 1,
93
+ put: 2,
94
+ patch: 3,
95
+ delete: 4
96
+ };
97
+ function collectEndpoints(spec) {
98
+ const out = [];
99
+ const paths = spec.paths ?? {};
100
+ for (const path of Object.keys(paths).sort()) {
101
+ const item = paths[path];
102
+ if (item === void 0) continue;
103
+ for (const method of HTTP_METHODS) {
104
+ const op = item[method];
105
+ if (op === void 0) continue;
106
+ out.push({ method, path, op });
107
+ }
108
+ }
109
+ out.sort((a, b) => {
110
+ const p = a.path.localeCompare(b.path);
111
+ if (p !== 0) return p;
112
+ return METHOD_ORDER[a.method] - METHOD_ORDER[b.method];
113
+ });
114
+ return out;
115
+ }
116
+ function slugify(s) {
117
+ return s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
118
+ }
119
+ function endpointId(slug, ep) {
120
+ return `${slug}-${ep.method}-${slugify(ep.path) || "root"}`;
121
+ }
122
+ function yamlString(s) {
123
+ return `"${s.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
124
+ }
125
+ function renderEndpointTable(endpoints) {
126
+ const rows = endpoints.map((ep) => {
127
+ const summary = (ep.op.summary ?? "").replace(/\s+/g, " ").trim();
128
+ return ` - [${ep.method.toUpperCase()}, ${yamlString(ep.path)}, ${yamlString(summary)}]`;
129
+ }).join("\n");
130
+ return ["```table", "columns: [Method, Path, Summary]", "rows:", rows, "```"].join("\n");
131
+ }
132
+ function renderEndpointSequence(slug, ep) {
133
+ const id = endpointId(slug, ep);
134
+ const title = (ep.op.summary ?? `${ep.method.toUpperCase()} ${ep.path}`).trim();
135
+ const desc = (ep.op.description ?? "").replace(/\s+/g, " ").trim();
136
+ const responses = ep.op.responses ?? {};
137
+ const codes = Object.keys(responses).sort();
138
+ const okCodes = codes.filter((c) => /^[12]/.test(c));
139
+ const errCodes = codes.filter((c) => /^[345]/.test(c));
140
+ const lines = [
141
+ "```sequence",
142
+ `id: ${id}`,
143
+ `title: ${yamlString(title)}`
144
+ ];
145
+ if (desc.length > 0) lines.push(`description: ${yamlString(desc)}`);
146
+ lines.push(`endpoint: { method: ${ep.method.toUpperCase()}, path: ${yamlString(ep.path)} }`);
147
+ lines.push("actors:");
148
+ lines.push(" - { id: Client, name: Client }");
149
+ lines.push(" - { id: API, name: API }");
150
+ lines.push("messages:");
151
+ lines.push(
152
+ ` - { from: Client, to: API, label: ${yamlString(`${ep.method.toUpperCase()} ${ep.path}`)}, kind: sync }`
153
+ );
154
+ for (const code of okCodes) {
155
+ const summary = (responses[code]?.description ?? "").replace(/\s+/g, " ").trim();
156
+ const label = summary.length > 0 ? `${code} ${summary}` : code;
157
+ lines.push(` - { from: API, to: Client, label: ${yamlString(label)}, kind: response }`);
158
+ }
159
+ for (const code of errCodes) {
160
+ const summary = (responses[code]?.description ?? "").replace(/\s+/g, " ").trim();
161
+ const label = summary.length > 0 ? `${code} ${summary}` : code;
162
+ lines.push(` - { from: API, to: Client, label: ${yamlString(label)}, kind: error }`);
163
+ }
164
+ lines.push("```");
165
+ return lines.join("\n");
166
+ }
167
+ var PRIMITIVE_TYPE_REMAP = {
168
+ integer: "int",
169
+ number: "number",
170
+ string: "string",
171
+ boolean: "bool"
172
+ };
173
+ function schemaTypeLabel(schema, propName, propSchema) {
174
+ const s = propSchema ?? schema;
175
+ if (s.$ref !== void 0) {
176
+ const name = String(s.$ref).split("/").pop() ?? "ref";
177
+ return name;
178
+ }
179
+ const fmt = s.format;
180
+ if (fmt === "uuid") return "uuid";
181
+ if (fmt === "date-time" || fmt === "date") return "timestamp";
182
+ if (s.type === "array") {
183
+ const items = s.items;
184
+ return `${items !== void 0 ? schemaTypeLabel(s, propName, items) : "any"}[]`;
185
+ }
186
+ if (typeof s.type === "string") {
187
+ return PRIMITIVE_TYPE_REMAP[s.type] ?? s.type;
188
+ }
189
+ return "any";
190
+ }
191
+ function flattenColumns(schema) {
192
+ const out = [];
193
+ const required = new Set(schema.required ?? []);
194
+ const props = schema.properties ?? {};
195
+ for (const name of Object.keys(props)) {
196
+ const propSchema = props[name];
197
+ if (propSchema === void 0) continue;
198
+ const type = schemaTypeLabel(schema, name, propSchema);
199
+ const pk = name === "id";
200
+ const fk = !pk && /_id$/i.test(name);
201
+ out.push({
202
+ name,
203
+ type: required.has(name) ? type : `${type}?`,
204
+ pk,
205
+ fk
206
+ });
207
+ }
208
+ return out;
209
+ }
210
+ function renderSchemasErd(slug, spec) {
211
+ const schemas = spec.components?.schemas ?? {};
212
+ const names = Object.keys(schemas).sort();
213
+ if (names.length === 0) return "";
214
+ const entityLines = ["entities:"];
215
+ for (const name of names) {
216
+ const schema = schemas[name];
217
+ if (schema === void 0 || schema.type !== "object") continue;
218
+ const cols = flattenColumns(schema);
219
+ if (cols.length === 0) continue;
220
+ entityLines.push(` - name: ${name}`);
221
+ entityLines.push(" columns:");
222
+ for (const c of cols) {
223
+ const flags = [];
224
+ if (c.pk) flags.push("pk: true");
225
+ if (c.fk) flags.push("fk: true");
226
+ const tail = flags.length > 0 ? `, ${flags.join(", ")}` : "";
227
+ entityLines.push(` - { name: ${c.name}, type: ${yamlString(c.type)}${tail} }`);
228
+ }
229
+ }
230
+ if (entityLines.length === 1) return "";
231
+ return ["```erd", `id: ${slug}-schemas`, ...entityLines, "```"].join("\n");
232
+ }
233
+ function openapiToMarkdown(spec, opts = {}) {
234
+ const slug = opts.slug ?? "api";
235
+ const endpoints = collectEndpoints(spec);
236
+ const description = (spec.info.description ?? "").trim();
237
+ const out = [];
238
+ out.push(
239
+ "```meta",
240
+ `title: ${yamlString(spec.info.title)}`,
241
+ description.length > 0 ? `subtitle: ${yamlString(description.split(/\r?\n/)[0] ?? "")}` : "",
242
+ `tag: ${yamlString(`API \xB7 v${spec.info.version}`)}`,
243
+ "```"
244
+ );
245
+ if (description.length > 0) {
246
+ out.push("", "## Overview", "", description);
247
+ }
248
+ if (endpoints.length > 0) {
249
+ out.push("", "## Endpoints", "", renderEndpointTable(endpoints));
250
+ }
251
+ for (const ep of endpoints) {
252
+ out.push("", `## ${ep.method.toUpperCase()} ${ep.path}`, "", renderEndpointSequence(slug, ep));
253
+ }
254
+ const erd = renderSchemasErd(slug, spec);
255
+ if (erd.length > 0) {
256
+ out.push("", "## Schemas", "", erd);
257
+ }
258
+ const cleaned = out.filter((s, i, a) => !(s === "" && a[i - 1] === ""));
259
+ return cleaned.join("\n").replace(/\n+$/, "") + "\n";
260
+ }
261
+
262
+ export { HTTP_METHODS, openApiSchema, openapiToMarkdown, parseOpenApi };
263
+ //# sourceMappingURL=index.js.map
264
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/openapi/schemas.ts","../src/openapi/parse.ts","../src/openapi/generate.ts"],"names":["yamlParse"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Minimal zod schemas for the OpenAPI 3.x subset we actually use during\n * generation. Anything not listed here is allowed via `passthrough` so we\n * don't fail on extra fields the spec author put in.\n */\n\nimport { z } from 'zod';\n\nconst refSchema = z.object({ $ref: z.string() }).passthrough();\n\nconst propertySchema: z.ZodType = z.lazy(() =>\n z\n .object({\n type: z.string().optional(),\n format: z.string().optional(),\n description: z.string().optional(),\n enum: z.array(z.unknown()).optional(),\n items: z.union([refSchema, propertySchema]).optional(),\n properties: z.record(z.string(), z.union([refSchema, propertySchema])).optional(),\n required: z.array(z.string()).optional(),\n example: z.unknown().optional(),\n $ref: z.string().optional(),\n })\n .passthrough(),\n);\n\nconst componentSchema = z\n .object({\n type: z.string().optional(),\n description: z.string().optional(),\n properties: z.record(z.string(), z.union([refSchema, propertySchema])).optional(),\n required: z.array(z.string()).optional(),\n enum: z.array(z.unknown()).optional(),\n allOf: z.array(z.union([refSchema, propertySchema])).optional(),\n })\n .passthrough();\n\nconst responseSchema = z\n .object({\n description: z.string().optional(),\n content: z.record(z.string(), z.unknown()).optional(),\n })\n .passthrough();\n\nconst parameterSchema = z\n .object({\n name: z.string(),\n in: z.string(),\n required: z.boolean().optional(),\n description: z.string().optional(),\n schema: z.union([refSchema, propertySchema]).optional(),\n })\n .passthrough();\n\nconst operationSchema = z\n .object({\n operationId: z.string().optional(),\n summary: z.string().optional(),\n description: z.string().optional(),\n tags: z.array(z.string()).optional(),\n parameters: z.array(parameterSchema).optional(),\n requestBody: z.unknown().optional(),\n responses: z.record(z.string(), responseSchema).optional(),\n })\n .passthrough();\n\nconst pathItemSchema = z\n .object({\n get: operationSchema.optional(),\n post: operationSchema.optional(),\n put: operationSchema.optional(),\n patch: operationSchema.optional(),\n delete: operationSchema.optional(),\n head: operationSchema.optional(),\n options: operationSchema.optional(),\n parameters: z.array(parameterSchema).optional(),\n })\n .passthrough();\n\nconst infoSchema = z\n .object({\n title: z.string(),\n description: z.string().optional(),\n version: z.string(),\n })\n .passthrough();\n\nexport const openApiSchema = z\n .object({\n openapi: z.string().optional(),\n swagger: z.string().optional(),\n info: infoSchema,\n paths: z.record(z.string(), pathItemSchema).optional(),\n components: z\n .object({\n schemas: z.record(z.string(), componentSchema).optional(),\n })\n .passthrough()\n .optional(),\n })\n .passthrough();\n\n/** Parsed OpenAPI spec (validated subset). */\nexport type OpenApiSpec = z.infer<typeof openApiSchema>;\nexport type OpenApiOperation = z.infer<typeof operationSchema>;\nexport type OpenApiSchema = z.infer<typeof componentSchema>;\nexport type OpenApiParameter = z.infer<typeof parameterSchema>;\n\n/** HTTP methods we generate sequences for. */\nexport const HTTP_METHODS = ['get', 'post', 'put', 'patch', 'delete'] as const;\nexport type HttpMethod = (typeof HTTP_METHODS)[number];\n","/**\n * Parses an OpenAPI 3.x spec from a YAML or JSON string and validates it\n * against {@link openApiSchema}. Returns the typed spec or throws with a\n * structured message.\n */\n\nimport { parse as yamlParse } from 'yaml';\nimport { openApiSchema, type OpenApiSpec } from './schemas.js';\n\n/**\n * Parses + validates an OpenAPI spec.\n *\n * @param source - YAML or JSON source string.\n * @returns The validated spec.\n * @throws If the source isn't valid YAML/JSON or doesn't match the OpenAPI\n * subset we support.\n */\nexport function parseOpenApi(source: string): OpenApiSpec {\n let raw: unknown;\n try {\n raw = yamlParse(source) as unknown;\n } catch (err) {\n throw new Error(`OpenAPI spec is not valid YAML/JSON: ${(err as Error).message}`);\n }\n const result = openApiSchema.safeParse(raw);\n if (!result.success) {\n const issues = result.error.issues\n .map((i) => `${i.path.join('.') || '(root)'}: ${i.message}`)\n .join('; ');\n throw new Error(`OpenAPI spec failed validation: ${issues}`);\n }\n return result.data;\n}\n","/**\n * Converts an OpenAPI spec into Avodado markdown.\n *\n * The output is **deterministic**: same spec in → same markdown out. That\n * makes drift detection a simple string compare.\n *\n * The doc contains:\n * - `meta` block (title / subtitle / tag from `info`)\n * - `prose` block with the API description\n * - `table` block listing every endpoint\n * - one `sequence` block per endpoint\n * - `erd` block from `components.schemas`\n */\n\nimport {\n HTTP_METHODS,\n type HttpMethod,\n type OpenApiOperation,\n type OpenApiSchema,\n type OpenApiSpec,\n} from './schemas.js';\n\n/** Options for {@link openapiToMarkdown}. */\nexport interface GenerateOptions {\n /**\n * The doc slug — also drives generated block ids (e.g. `<slug>-post-orders`).\n * Defaults to `api`.\n */\n readonly slug?: string;\n}\n\nconst METHOD_ORDER: Record<HttpMethod, number> = {\n get: 0,\n post: 1,\n put: 2,\n patch: 3,\n delete: 4,\n};\n\ninterface Endpoint {\n readonly method: HttpMethod;\n readonly path: string;\n readonly op: OpenApiOperation;\n}\n\nfunction collectEndpoints(spec: OpenApiSpec): Endpoint[] {\n const out: Endpoint[] = [];\n const paths = spec.paths ?? {};\n for (const path of Object.keys(paths).sort()) {\n const item = paths[path];\n if (item === undefined) continue;\n for (const method of HTTP_METHODS) {\n const op = item[method];\n if (op === undefined) continue;\n out.push({ method, path, op });\n }\n }\n out.sort((a, b) => {\n const p = a.path.localeCompare(b.path);\n if (p !== 0) return p;\n return METHOD_ORDER[a.method] - METHOD_ORDER[b.method];\n });\n return out;\n}\n\n/** Sanitises an arbitrary string into a slug-safe id segment. */\nfunction slugify(s: string): string {\n return s\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n}\n\nfunction endpointId(slug: string, ep: Endpoint): string {\n return `${slug}-${ep.method}-${slugify(ep.path) || 'root'}`;\n}\n\n/** Escapes a string for safe use inside a single-line YAML scalar. */\nfunction yamlString(s: string): string {\n return `\"${s.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"')}\"`;\n}\n\n/** Renders the endpoint table block. */\nfunction renderEndpointTable(endpoints: readonly Endpoint[]): string {\n const rows = endpoints\n .map((ep) => {\n const summary = (ep.op.summary ?? '').replace(/\\s+/g, ' ').trim();\n return ` - [${ep.method.toUpperCase()}, ${yamlString(ep.path)}, ${yamlString(summary)}]`;\n })\n .join('\\n');\n return ['```table', 'columns: [Method, Path, Summary]', 'rows:', rows, '```'].join('\\n');\n}\n\n/** Renders the `sequence` block for one endpoint. */\nfunction renderEndpointSequence(slug: string, ep: Endpoint): string {\n const id = endpointId(slug, ep);\n const title = (ep.op.summary ?? `${ep.method.toUpperCase()} ${ep.path}`).trim();\n const desc = (ep.op.description ?? '').replace(/\\s+/g, ' ').trim();\n const responses = ep.op.responses ?? {};\n const codes = Object.keys(responses).sort();\n const okCodes = codes.filter((c) => /^[12]/.test(c));\n const errCodes = codes.filter((c) => /^[345]/.test(c));\n\n const lines: string[] = [\n '```sequence',\n `id: ${id}`,\n `title: ${yamlString(title)}`,\n ];\n if (desc.length > 0) lines.push(`description: ${yamlString(desc)}`);\n lines.push(`endpoint: { method: ${ep.method.toUpperCase()}, path: ${yamlString(ep.path)} }`);\n lines.push('actors:');\n lines.push(' - { id: Client, name: Client }');\n lines.push(' - { id: API, name: API }');\n lines.push('messages:');\n lines.push(\n ` - { from: Client, to: API, label: ${yamlString(`${ep.method.toUpperCase()} ${ep.path}`)}, kind: sync }`,\n );\n for (const code of okCodes) {\n const summary = (responses[code]?.description ?? '').replace(/\\s+/g, ' ').trim();\n const label = summary.length > 0 ? `${code} ${summary}` : code;\n lines.push(` - { from: API, to: Client, label: ${yamlString(label)}, kind: response }`);\n }\n for (const code of errCodes) {\n const summary = (responses[code]?.description ?? '').replace(/\\s+/g, ' ').trim();\n const label = summary.length > 0 ? `${code} ${summary}` : code;\n lines.push(` - { from: API, to: Client, label: ${yamlString(label)}, kind: error }`);\n }\n lines.push('```');\n return lines.join('\\n');\n}\n\ninterface ColumnSpec {\n readonly name: string;\n readonly type: string;\n readonly pk: boolean;\n readonly fk: boolean;\n}\n\nconst PRIMITIVE_TYPE_REMAP: Record<string, string> = {\n integer: 'int',\n number: 'number',\n string: 'string',\n boolean: 'bool',\n};\n\nfunction schemaTypeLabel(schema: OpenApiSchema, propName: string | undefined, propSchema?: unknown): string {\n const s = (propSchema as OpenApiSchema | undefined) ?? schema;\n if (s.$ref !== undefined) {\n const name = String(s.$ref).split('/').pop() ?? 'ref';\n return name;\n }\n const fmt = s.format;\n if (fmt === 'uuid') return 'uuid';\n if (fmt === 'date-time' || fmt === 'date') return 'timestamp';\n if (s.type === 'array') {\n const items = s.items as OpenApiSchema | undefined;\n return `${items !== undefined ? schemaTypeLabel(s, propName, items) : 'any'}[]`;\n }\n if (typeof s.type === 'string') {\n return PRIMITIVE_TYPE_REMAP[s.type] ?? s.type;\n }\n return 'any';\n}\n\nfunction flattenColumns(schema: OpenApiSchema): ColumnSpec[] {\n const out: ColumnSpec[] = [];\n const required = new Set(schema.required ?? []);\n const props = schema.properties ?? {};\n for (const name of Object.keys(props)) {\n const propSchema = props[name] as OpenApiSchema | undefined;\n if (propSchema === undefined) continue;\n const type = schemaTypeLabel(schema, name, propSchema);\n const pk = name === 'id';\n const fk = !pk && /_id$/i.test(name);\n out.push({\n name,\n type: required.has(name) ? type : `${type}?`,\n pk,\n fk,\n });\n }\n return out;\n}\n\n/** Renders the `erd` block from `components.schemas`. */\nfunction renderSchemasErd(slug: string, spec: OpenApiSpec): string {\n const schemas = spec.components?.schemas ?? {};\n const names = Object.keys(schemas).sort();\n if (names.length === 0) return '';\n const entityLines: string[] = ['entities:'];\n for (const name of names) {\n const schema = schemas[name];\n if (schema === undefined || schema.type !== 'object') continue;\n const cols = flattenColumns(schema);\n if (cols.length === 0) continue;\n entityLines.push(` - name: ${name}`);\n entityLines.push(' columns:');\n for (const c of cols) {\n const flags: string[] = [];\n if (c.pk) flags.push('pk: true');\n if (c.fk) flags.push('fk: true');\n const tail = flags.length > 0 ? `, ${flags.join(', ')}` : '';\n entityLines.push(` - { name: ${c.name}, type: ${yamlString(c.type)}${tail} }`);\n }\n }\n if (entityLines.length === 1) return '';\n return ['```erd', `id: ${slug}-schemas`, ...entityLines, '```'].join('\\n');\n}\n\n/**\n * Generates an Avodado markdown document from an OpenAPI spec.\n *\n * @param spec - The parsed OpenAPI spec.\n * @param opts - Options including the doc slug.\n * @returns The markdown source.\n */\nexport function openapiToMarkdown(spec: OpenApiSpec, opts: GenerateOptions = {}): string {\n const slug = opts.slug ?? 'api';\n const endpoints = collectEndpoints(spec);\n const description = (spec.info.description ?? '').trim();\n\n const out: string[] = [];\n\n out.push(\n '```meta',\n `title: ${yamlString(spec.info.title)}`,\n description.length > 0\n ? `subtitle: ${yamlString(description.split(/\\r?\\n/)[0] ?? '')}`\n : '',\n `tag: ${yamlString(`API · v${spec.info.version}`)}`,\n '```',\n );\n\n if (description.length > 0) {\n out.push('', '## Overview', '', description);\n }\n\n if (endpoints.length > 0) {\n out.push('', '## Endpoints', '', renderEndpointTable(endpoints));\n }\n\n for (const ep of endpoints) {\n out.push('', `## ${ep.method.toUpperCase()} ${ep.path}`, '', renderEndpointSequence(slug, ep));\n }\n\n const erd = renderSchemasErd(slug, spec);\n if (erd.length > 0) {\n out.push('', '## Schemas', '', erd);\n }\n\n // Strip empty lines from the meta block (description was empty).\n const cleaned = out.filter((s, i, a) => !(s === '' && a[i - 1] === ''));\n return cleaned.join('\\n').replace(/\\n+$/, '') + '\\n';\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@avodado/sync",
3
+ "version": "0.0.1",
4
+ "description": "Import external sources (OpenAPI, SQL DDL) into Avodado documents.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md"
18
+ ],
19
+ "sideEffects": false,
20
+ "dependencies": {
21
+ "yaml": "^2.6.1",
22
+ "zod": "^3.24.1",
23
+ "@avodado/core": "0.0.1"
24
+ },
25
+ "publishConfig": {
26
+ "access": "public",
27
+ "provenance": true
28
+ },
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "git+https://github.com/jdiejim/avodado.git",
32
+ "directory": "packages/sync"
33
+ },
34
+ "homepage": "https://github.com/jdiejim/avodado#readme",
35
+ "bugs": "https://github.com/jdiejim/avodado/issues",
36
+ "keywords": [
37
+ "avodado",
38
+ "documentation",
39
+ "docs-as-code",
40
+ "markdown",
41
+ "openapi",
42
+ "codegen"
43
+ ],
44
+ "engines": {
45
+ "node": ">=20"
46
+ },
47
+ "scripts": {
48
+ "build": "tsup",
49
+ "typecheck": "tsc --noEmit"
50
+ }
51
+ }