@danceroutine/tango-openapi 0.1.0 → 1.0.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/LICENSE +21 -0
- package/README.md +145 -0
- package/dist/chunk-BkvOhyD0.js +12 -0
- package/dist/domain/describeResources.d.ts +10 -0
- package/dist/domain/index.d.ts +2 -1
- package/dist/domain/index.js +3 -4
- package/dist/domain/types.d.ts +76 -12
- package/dist/domain-B-7sApJT.js +34 -0
- package/dist/domain-B-7sApJT.js.map +1 -0
- package/dist/generators/index.js +2 -2
- package/dist/generators/spec/generateOpenAPISpec.d.ts +3 -0
- package/dist/generators-JMALItMS.js +346 -0
- package/dist/generators-JMALItMS.js.map +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +5 -10
- package/dist/mappers/index.d.ts +1 -1
- package/dist/mappers/index.js +3 -3
- package/dist/mappers/schema/generateSchemaFromModel.d.ts +3 -0
- package/dist/mappers/schema/generateSchemaFromZod.d.ts +6 -0
- package/dist/mappers/schema/index.d.ts +1 -0
- package/dist/mappers/schema/mapTypeToOpenAPI.d.ts +3 -0
- package/dist/mappers-bmN95TGV.js +15 -0
- package/dist/{mappers-CIfnOwl2.js.map → mappers-bmN95TGV.js.map} +1 -1
- package/dist/{schema-DVxdID48.js → schema-D3ybOrpr.js} +19 -12
- package/dist/schema-D3ybOrpr.js.map +1 -0
- package/package.json +57 -53
- package/dist/domain/types.js +0 -1
- package/dist/domain-Cufz6y1q.js +0 -7
- package/dist/domain-Cufz6y1q.js.map +0 -1
- package/dist/generators/spec/generateOpenAPISpec.js +0 -133
- package/dist/generators-Bvwyyja4.js +0 -131
- package/dist/generators-Bvwyyja4.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/mappers/schema/generateSchemaFromModel.js +0 -19
- package/dist/mappers-CIfnOwl2.js +0 -13
- package/dist/schema-DVxdID48.js.map +0 -1
- package/dist/version.d.ts +0 -1
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
import { __export } from "./chunk-BkvOhyD0.js";
|
|
2
|
+
import { generateSchemaFromModel, generateSchemaFromZod } from "./schema-D3ybOrpr.js";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
//#region src/generators/spec/generateOpenAPISpec.ts
|
|
6
|
+
const JSON_CONTENT_TYPE = "application/json";
|
|
7
|
+
function toOpenAPIModel(metadata) {
|
|
8
|
+
return {
|
|
9
|
+
name: metadata.name,
|
|
10
|
+
fields: Object.fromEntries(metadata.fields.map((field) => [field.name, {
|
|
11
|
+
type: field.type,
|
|
12
|
+
nullable: field.notNull !== true,
|
|
13
|
+
default: field.default,
|
|
14
|
+
primaryKey: field.primaryKey
|
|
15
|
+
}]))
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function normalizePath(path) {
|
|
19
|
+
const trimmed = path.trim();
|
|
20
|
+
if (!trimmed) throw new Error("OpenAPI paths must not be empty.");
|
|
21
|
+
if (/(^|\/):/.test(trimmed)) throw new Error(`OpenAPI paths must use {param} syntax, received '${trimmed}'.`);
|
|
22
|
+
const withLeadingSlash = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
|
|
23
|
+
if (withLeadingSlash === "/") return "/";
|
|
24
|
+
return withLeadingSlash.replace(/\/+$/g, "");
|
|
25
|
+
}
|
|
26
|
+
function joinPath(base, segment) {
|
|
27
|
+
const normalizedSegment = segment.replace(/^\/+|\/+$/g, "");
|
|
28
|
+
return base === "/" ? `/${normalizedSegment}` : `${base}/${normalizedSegment}`;
|
|
29
|
+
}
|
|
30
|
+
function toMethodKey(method) {
|
|
31
|
+
return method.toLowerCase();
|
|
32
|
+
}
|
|
33
|
+
function isReferenceObject(value) {
|
|
34
|
+
return typeof value === "object" && value !== null && typeof value.$ref === "string";
|
|
35
|
+
}
|
|
36
|
+
function isZodSchema(value) {
|
|
37
|
+
return value instanceof z.ZodType;
|
|
38
|
+
}
|
|
39
|
+
function toSchema(value) {
|
|
40
|
+
if (isReferenceObject(value)) return value;
|
|
41
|
+
if (isZodSchema(value)) return generateSchemaFromZod(value);
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
function arraySchema(items) {
|
|
45
|
+
return {
|
|
46
|
+
type: "array",
|
|
47
|
+
items
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function paginatedResultsSchema(items) {
|
|
51
|
+
return {
|
|
52
|
+
type: "object",
|
|
53
|
+
properties: {
|
|
54
|
+
count: { type: "integer" },
|
|
55
|
+
next: { type: "string" },
|
|
56
|
+
previous: { type: "string" },
|
|
57
|
+
results: arraySchema(items)
|
|
58
|
+
},
|
|
59
|
+
required: ["count", "results"]
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function jsonResponse(description, schema) {
|
|
63
|
+
return schema ? {
|
|
64
|
+
description,
|
|
65
|
+
content: { [JSON_CONTENT_TYPE]: { schema } }
|
|
66
|
+
} : { description };
|
|
67
|
+
}
|
|
68
|
+
function jsonRequestBody(schema, required = true) {
|
|
69
|
+
return {
|
|
70
|
+
required,
|
|
71
|
+
content: { [JSON_CONTENT_TYPE]: { schema } }
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function withOverride(operation, override, fallbackTags) {
|
|
75
|
+
if (!override) return operation;
|
|
76
|
+
const nextOperation = {
|
|
77
|
+
...operation,
|
|
78
|
+
summary: override.summary ?? operation.summary,
|
|
79
|
+
description: override.description ?? operation.description,
|
|
80
|
+
tags: override.tags ?? operation.tags ?? fallbackTags,
|
|
81
|
+
parameters: override.parameters ?? operation.parameters,
|
|
82
|
+
requestBody: override.requestBody ? jsonRequestBody(toSchema(override.requestBody.schema), override.requestBody.required ?? true) : operation.requestBody,
|
|
83
|
+
responses: operation.responses
|
|
84
|
+
};
|
|
85
|
+
if (override.responses) {
|
|
86
|
+
nextOperation.responses = Object.fromEntries(Object.entries(override.responses).map(([status, response]) => {
|
|
87
|
+
return [status, jsonResponse(response.description, response.schema ? toSchema(response.schema) : undefined)];
|
|
88
|
+
}));
|
|
89
|
+
return nextOperation;
|
|
90
|
+
}
|
|
91
|
+
if (override.responseSchema || override.responseStatus || override.responseDescription) {
|
|
92
|
+
const status = override.responseStatus ?? "200";
|
|
93
|
+
nextOperation.responses = { [status]: jsonResponse(override.responseDescription ?? "Successful response", override.responseSchema ? toSchema(override.responseSchema) : undefined) };
|
|
94
|
+
}
|
|
95
|
+
return nextOperation;
|
|
96
|
+
}
|
|
97
|
+
function ensurePath(paths, path) {
|
|
98
|
+
const existing = paths[path];
|
|
99
|
+
if (existing) return existing;
|
|
100
|
+
const created = {};
|
|
101
|
+
paths[path] = created;
|
|
102
|
+
return created;
|
|
103
|
+
}
|
|
104
|
+
function setOperation(paths, path, method, operation) {
|
|
105
|
+
const pathItem = ensurePath(paths, path);
|
|
106
|
+
pathItem[toMethodKey(method)] = operation;
|
|
107
|
+
}
|
|
108
|
+
function buildListParameters(searchFields, orderingFields, usesDefaultOffsetPagination) {
|
|
109
|
+
const parameters = [];
|
|
110
|
+
if (usesDefaultOffsetPagination) parameters.push({
|
|
111
|
+
name: "limit",
|
|
112
|
+
in: "query",
|
|
113
|
+
schema: { type: "integer" }
|
|
114
|
+
}, {
|
|
115
|
+
name: "offset",
|
|
116
|
+
in: "query",
|
|
117
|
+
schema: { type: "integer" }
|
|
118
|
+
});
|
|
119
|
+
if (searchFields.length > 0) parameters.push({
|
|
120
|
+
name: "search",
|
|
121
|
+
in: "query",
|
|
122
|
+
schema: { type: "string" }
|
|
123
|
+
});
|
|
124
|
+
if (orderingFields.length > 0) parameters.push({
|
|
125
|
+
name: "ordering",
|
|
126
|
+
in: "query",
|
|
127
|
+
schema: { type: "string" }
|
|
128
|
+
});
|
|
129
|
+
return parameters;
|
|
130
|
+
}
|
|
131
|
+
function buildPathParameter(name) {
|
|
132
|
+
return {
|
|
133
|
+
name,
|
|
134
|
+
in: "path",
|
|
135
|
+
required: true,
|
|
136
|
+
schema: { type: "string" }
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function stringifyFields(fields) {
|
|
140
|
+
return fields.map(String);
|
|
141
|
+
}
|
|
142
|
+
function titleize(input) {
|
|
143
|
+
return input.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[-_]+/g, " ").trim().replace(/\b\w/g, (char) => char.toUpperCase());
|
|
144
|
+
}
|
|
145
|
+
function validateGenericDetailPath(detailPath, lookupParam) {
|
|
146
|
+
if (!detailPath.includes(`{${lookupParam}}`)) throw new Error(`GenericAPIView detail paths must include '{${lookupParam}}', received '${detailPath}'.`);
|
|
147
|
+
}
|
|
148
|
+
function ensureModelSchema(schemas, metadata) {
|
|
149
|
+
schemas[metadata.name] = generateSchemaFromModel(toOpenAPIModel(metadata));
|
|
150
|
+
}
|
|
151
|
+
function registerViewSetDescriptor(paths, schemas, descriptor) {
|
|
152
|
+
const metadata = descriptor.resource.describeOpenAPI();
|
|
153
|
+
const modelName = metadata.model.metadata.name;
|
|
154
|
+
const tags = descriptor.tags ?? [modelName];
|
|
155
|
+
const collectionPath = normalizePath(descriptor.basePath);
|
|
156
|
+
const detailPath = collectionPath === "/" ? "/{id}" : `${collectionPath}/{id}`;
|
|
157
|
+
const readSchema = toSchema(metadata.outputSchema);
|
|
158
|
+
const writeSchema = toSchema(metadata.createSchema);
|
|
159
|
+
const updateSchema = toSchema(metadata.updateSchema);
|
|
160
|
+
ensureModelSchema(schemas, metadata.model.metadata);
|
|
161
|
+
const listOperation = withOverride({
|
|
162
|
+
summary: `List ${modelName}s`,
|
|
163
|
+
tags,
|
|
164
|
+
parameters: buildListParameters(stringifyFields(metadata.searchFields), stringifyFields(metadata.orderingFields), metadata.usesDefaultOffsetPagination),
|
|
165
|
+
responses: { "200": jsonResponse("Successful response", metadata.usesDefaultOffsetPagination ? paginatedResultsSchema(readSchema) : undefined) }
|
|
166
|
+
}, undefined, tags);
|
|
167
|
+
setOperation(paths, collectionPath, "GET", listOperation);
|
|
168
|
+
setOperation(paths, collectionPath, "POST", {
|
|
169
|
+
summary: `Create ${modelName}`,
|
|
170
|
+
tags,
|
|
171
|
+
requestBody: jsonRequestBody(writeSchema),
|
|
172
|
+
responses: { "201": jsonResponse("Created", readSchema) }
|
|
173
|
+
});
|
|
174
|
+
setOperation(paths, detailPath, "GET", {
|
|
175
|
+
summary: `Get ${modelName}`,
|
|
176
|
+
tags,
|
|
177
|
+
parameters: [buildPathParameter("id")],
|
|
178
|
+
responses: {
|
|
179
|
+
"200": jsonResponse("Successful response", readSchema),
|
|
180
|
+
"404": { description: "Not found" }
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
const updateResponses = {
|
|
184
|
+
"200": jsonResponse("Updated", readSchema),
|
|
185
|
+
"404": { description: "Not found" }
|
|
186
|
+
};
|
|
187
|
+
setOperation(paths, detailPath, "PUT", {
|
|
188
|
+
summary: `Update ${modelName}`,
|
|
189
|
+
tags,
|
|
190
|
+
parameters: [buildPathParameter("id")],
|
|
191
|
+
requestBody: jsonRequestBody(updateSchema),
|
|
192
|
+
responses: updateResponses
|
|
193
|
+
});
|
|
194
|
+
setOperation(paths, detailPath, "PATCH", {
|
|
195
|
+
summary: `Update ${modelName}`,
|
|
196
|
+
tags,
|
|
197
|
+
parameters: [buildPathParameter("id")],
|
|
198
|
+
requestBody: jsonRequestBody(updateSchema),
|
|
199
|
+
responses: updateResponses
|
|
200
|
+
});
|
|
201
|
+
setOperation(paths, detailPath, "DELETE", {
|
|
202
|
+
summary: `Delete ${modelName}`,
|
|
203
|
+
tags,
|
|
204
|
+
parameters: [buildPathParameter("id")],
|
|
205
|
+
responses: {
|
|
206
|
+
"204": { description: "Deleted" },
|
|
207
|
+
"404": { description: "Not found" }
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
for (const action of metadata.actions) {
|
|
211
|
+
const actionPath = action.scope === "detail" ? joinPath(detailPath, action.path) : joinPath(collectionPath, action.path);
|
|
212
|
+
const baseOperation = {
|
|
213
|
+
summary: titleize(action.name),
|
|
214
|
+
tags,
|
|
215
|
+
...action.scope === "detail" ? { parameters: [buildPathParameter("id")] } : {},
|
|
216
|
+
responses: { "200": { description: "Successful response" } }
|
|
217
|
+
};
|
|
218
|
+
const override = descriptor.actions?.[action.name];
|
|
219
|
+
for (const method of action.methods) setOperation(paths, actionPath, method, withOverride(baseOperation, override, tags));
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
function registerGenericDescriptor(paths, schemas, descriptor) {
|
|
223
|
+
if (!descriptor.collectionPath && !descriptor.detailPath) throw new Error("GenericAPIView OpenAPI descriptors require at least one of collectionPath or detailPath.");
|
|
224
|
+
const metadata = descriptor.resource.describeOpenAPI();
|
|
225
|
+
const modelName = metadata.model.metadata.name;
|
|
226
|
+
const tags = descriptor.tags ?? [modelName];
|
|
227
|
+
const readSchema = toSchema(metadata.outputSchema);
|
|
228
|
+
const writeSchema = toSchema(metadata.createSchema);
|
|
229
|
+
const updateSchema = toSchema(metadata.updateSchema);
|
|
230
|
+
const allowed = new Set(metadata.allowedMethods);
|
|
231
|
+
ensureModelSchema(schemas, metadata.model.metadata);
|
|
232
|
+
if (descriptor.collectionPath) {
|
|
233
|
+
const collectionPath = normalizePath(descriptor.collectionPath);
|
|
234
|
+
if (allowed.has("GET")) setOperation(paths, collectionPath, "GET", withOverride({
|
|
235
|
+
summary: `List ${modelName}s`,
|
|
236
|
+
tags,
|
|
237
|
+
parameters: buildListParameters(stringifyFields(metadata.searchFields), stringifyFields(metadata.orderingFields), metadata.usesDefaultOffsetPagination),
|
|
238
|
+
responses: { "200": jsonResponse("Successful response", metadata.usesDefaultOffsetPagination ? paginatedResultsSchema(readSchema) : undefined) }
|
|
239
|
+
}, descriptor.methods?.GET, tags));
|
|
240
|
+
if (allowed.has("POST")) setOperation(paths, collectionPath, "POST", withOverride({
|
|
241
|
+
summary: `Create ${modelName}`,
|
|
242
|
+
tags,
|
|
243
|
+
requestBody: jsonRequestBody(writeSchema),
|
|
244
|
+
responses: { "201": jsonResponse("Created", readSchema) }
|
|
245
|
+
}, descriptor.methods?.POST, tags));
|
|
246
|
+
}
|
|
247
|
+
if (descriptor.detailPath) {
|
|
248
|
+
const detailPath = normalizePath(descriptor.detailPath);
|
|
249
|
+
validateGenericDetailPath(detailPath, metadata.lookupParam);
|
|
250
|
+
const detailParameters = [buildPathParameter(metadata.lookupParam)];
|
|
251
|
+
if (allowed.has("GET")) setOperation(paths, detailPath, "GET", withOverride({
|
|
252
|
+
summary: `Get ${modelName}`,
|
|
253
|
+
tags,
|
|
254
|
+
parameters: detailParameters,
|
|
255
|
+
responses: {
|
|
256
|
+
"200": jsonResponse("Successful response", readSchema),
|
|
257
|
+
"404": { description: "Not found" }
|
|
258
|
+
}
|
|
259
|
+
}, descriptor.methods?.GET, tags));
|
|
260
|
+
if (allowed.has("PUT")) setOperation(paths, detailPath, "PUT", withOverride({
|
|
261
|
+
summary: `Update ${modelName}`,
|
|
262
|
+
tags,
|
|
263
|
+
parameters: detailParameters,
|
|
264
|
+
requestBody: jsonRequestBody(updateSchema),
|
|
265
|
+
responses: {
|
|
266
|
+
"200": jsonResponse("Updated", readSchema),
|
|
267
|
+
"404": { description: "Not found" }
|
|
268
|
+
}
|
|
269
|
+
}, descriptor.methods?.PUT, tags));
|
|
270
|
+
if (allowed.has("PATCH")) setOperation(paths, detailPath, "PATCH", withOverride({
|
|
271
|
+
summary: `Update ${modelName}`,
|
|
272
|
+
tags,
|
|
273
|
+
parameters: detailParameters,
|
|
274
|
+
requestBody: jsonRequestBody(updateSchema),
|
|
275
|
+
responses: {
|
|
276
|
+
"200": jsonResponse("Updated", readSchema),
|
|
277
|
+
"404": { description: "Not found" }
|
|
278
|
+
}
|
|
279
|
+
}, descriptor.methods?.PATCH, tags));
|
|
280
|
+
if (allowed.has("DELETE")) setOperation(paths, detailPath, "DELETE", withOverride({
|
|
281
|
+
summary: `Delete ${modelName}`,
|
|
282
|
+
tags,
|
|
283
|
+
parameters: detailParameters,
|
|
284
|
+
responses: {
|
|
285
|
+
"204": { description: "Deleted" },
|
|
286
|
+
"404": { description: "Not found" }
|
|
287
|
+
}
|
|
288
|
+
}, descriptor.methods?.DELETE, tags));
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
function registerAPIViewDescriptor(paths, descriptor) {
|
|
292
|
+
const path = normalizePath(descriptor.path);
|
|
293
|
+
const allowed = new Set(descriptor.resource.getAllowedMethods());
|
|
294
|
+
const methods = Object.entries(descriptor.methods);
|
|
295
|
+
for (const [method, override] of methods) {
|
|
296
|
+
if (!allowed.has(method)) throw new Error(`APIView method '${method}' is not implemented on ${descriptor.resource.constructor.name}.`);
|
|
297
|
+
setOperation(paths, path, method, withOverride({
|
|
298
|
+
summary: `${method} ${titleize(descriptor.resource.constructor.name.replace(/APIView$/, ""))}`.trim(),
|
|
299
|
+
responses: { [override.responseStatus ?? "200"]: jsonResponse(override.responseDescription ?? "Successful response", override.responseSchema ? toSchema(override.responseSchema) : undefined) }
|
|
300
|
+
}, override, descriptor.tags ?? [descriptor.resource.constructor.name.replace(/APIView$/, "") || "APIView"]));
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
function generateOpenAPISpec(config) {
|
|
304
|
+
const paths = {};
|
|
305
|
+
const schemas = {};
|
|
306
|
+
for (const resource of config.resources ?? []) registerDescriptor(paths, schemas, resource);
|
|
307
|
+
return {
|
|
308
|
+
openapi: "3.1.0",
|
|
309
|
+
info: {
|
|
310
|
+
title: config.title,
|
|
311
|
+
version: config.version,
|
|
312
|
+
description: config.description
|
|
313
|
+
},
|
|
314
|
+
servers: config.servers,
|
|
315
|
+
paths,
|
|
316
|
+
components: { schemas }
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
function registerDescriptor(paths, schemas, descriptor) {
|
|
320
|
+
if (descriptor.kind === "viewset") {
|
|
321
|
+
registerViewSetDescriptor(paths, schemas, descriptor);
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
if (descriptor.kind === "generic") {
|
|
325
|
+
registerGenericDescriptor(paths, schemas, descriptor);
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
registerAPIViewDescriptor(paths, descriptor);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
//#endregion
|
|
332
|
+
//#region src/generators/spec/index.ts
|
|
333
|
+
var spec_exports = {};
|
|
334
|
+
__export(spec_exports, { generateOpenAPISpec: () => generateOpenAPISpec });
|
|
335
|
+
|
|
336
|
+
//#endregion
|
|
337
|
+
//#region src/generators/index.ts
|
|
338
|
+
var generators_exports = {};
|
|
339
|
+
__export(generators_exports, {
|
|
340
|
+
generateOpenAPISpec: () => generateOpenAPISpec,
|
|
341
|
+
spec: () => spec_exports
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
//#endregion
|
|
345
|
+
export { generateOpenAPISpec, generators_exports, spec_exports };
|
|
346
|
+
//# sourceMappingURL=generators-JMALItMS.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generators-JMALItMS.js","names":["metadata: ResourceModelMetadata","path: string","base: string","segment: string","method: HttpMethod","value: unknown","value: z.ZodType | SchemaObject | ReferenceObject","items: SchemaObject | ReferenceObject","description: string","schema?: SchemaObject | ReferenceObject","schema: SchemaObject | ReferenceObject","required: boolean","operation: OperationObject","override: OpenAPIOperationOverride | undefined","fallbackTags: string[]","nextOperation: OperationObject","paths: Record<string, PathItemObject>","created: PathItemObject","searchFields: readonly string[]","orderingFields: readonly string[]","usesDefaultOffsetPagination: boolean","parameters: ParameterObject[]","name: string","fields: readonly unknown[]","input: string","detailPath: string","lookupParam: string","schemas: Record<string, SchemaObject>","descriptor: OpenAPIViewSetDescriptor","baseOperation: OperationObject","descriptor: OpenAPIGenericAPIViewDescriptor","descriptor: OpenAPIAPIViewDescriptor","config: OpenAPIGeneratorConfig","descriptor: OpenAPIResourceDescriptor"],"sources":["../src/generators/spec/generateOpenAPISpec.ts","../src/generators/spec/index.ts","../src/generators/index.ts"],"sourcesContent":["import { z } from 'zod';\nimport { generateSchemaFromModel, generateSchemaFromZod } from '../../mappers/schema';\nimport type {\n OpenAPIAPIViewDescriptor,\n OpenAPIGeneratorConfig,\n OpenAPIGenericAPIViewDescriptor,\n OpenAPIOperationOverride,\n OpenAPIResourceDescriptor,\n OpenAPIViewSetDescriptor,\n OpenAPISpec,\n OperationObject,\n ParameterObject,\n PathItemObject,\n ReferenceObject,\n RequestBodyObject,\n ResponseObject,\n SchemaObject,\n} from '../../domain';\n\nconst JSON_CONTENT_TYPE = 'application/json';\nconst HTTP_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'] as const;\ntype HttpMethod = (typeof HTTP_METHODS)[number];\ntype PathMethodKey = Lowercase<HttpMethod>;\n\ntype ResourceModelMetadata = {\n name: string;\n fields: Array<{\n name: string;\n type: string;\n notNull?: boolean;\n default?: unknown;\n primaryKey?: boolean;\n }>;\n};\n\nfunction toOpenAPIModel(metadata: ResourceModelMetadata) {\n return {\n name: metadata.name,\n fields: Object.fromEntries(\n metadata.fields.map((field) => [\n field.name,\n {\n type: field.type,\n nullable: field.notNull !== true,\n default: field.default,\n primaryKey: field.primaryKey,\n },\n ])\n ),\n };\n}\n\nfunction normalizePath(path: string): string {\n const trimmed = path.trim();\n if (!trimmed) {\n throw new Error('OpenAPI paths must not be empty.');\n }\n if (/(^|\\/):/.test(trimmed)) {\n throw new Error(`OpenAPI paths must use {param} syntax, received '${trimmed}'.`);\n }\n\n const withLeadingSlash = trimmed.startsWith('/') ? trimmed : `/${trimmed}`;\n if (withLeadingSlash === '/') {\n return '/';\n }\n\n return withLeadingSlash.replace(/\\/+$/g, '');\n}\n\nfunction joinPath(base: string, segment: string): string {\n const normalizedSegment = segment.replace(/^\\/+|\\/+$/g, '');\n return base === '/' ? `/${normalizedSegment}` : `${base}/${normalizedSegment}`;\n}\n\nfunction toMethodKey(method: HttpMethod): PathMethodKey {\n return method.toLowerCase() as PathMethodKey;\n}\n\nfunction isReferenceObject(value: unknown): value is ReferenceObject {\n return typeof value === 'object' && value !== null && typeof (value as { $ref?: unknown }).$ref === 'string';\n}\n\nfunction isZodSchema(value: unknown): value is z.ZodType {\n // oxlint-disable-next-line eslint-js/no-restricted-syntax\n return value instanceof z.ZodType;\n}\n\nfunction toSchema(value: z.ZodType | SchemaObject | ReferenceObject): SchemaObject | ReferenceObject {\n if (isReferenceObject(value)) {\n return value;\n }\n if (isZodSchema(value)) {\n return generateSchemaFromZod(value);\n }\n return value;\n}\n\nfunction arraySchema(items: SchemaObject | ReferenceObject): SchemaObject {\n return {\n type: 'array',\n items,\n };\n}\n\nfunction paginatedResultsSchema(items: SchemaObject | ReferenceObject): SchemaObject {\n return {\n type: 'object',\n properties: {\n count: { type: 'integer' },\n next: { type: 'string' },\n previous: { type: 'string' },\n results: arraySchema(items),\n },\n required: ['count', 'results'],\n };\n}\n\nfunction jsonResponse(description: string, schema?: SchemaObject | ReferenceObject): ResponseObject {\n return schema\n ? {\n description,\n content: {\n [JSON_CONTENT_TYPE]: {\n schema,\n },\n },\n }\n : { description };\n}\n\nfunction jsonRequestBody(schema: SchemaObject | ReferenceObject, required: boolean = true): RequestBodyObject {\n return {\n required,\n content: {\n [JSON_CONTENT_TYPE]: {\n schema,\n },\n },\n };\n}\n\nfunction withOverride(\n operation: OperationObject,\n override: OpenAPIOperationOverride | undefined,\n fallbackTags: string[]\n): OperationObject {\n if (!override) {\n return operation;\n }\n\n const nextOperation: OperationObject = {\n ...operation,\n summary: override.summary ?? operation.summary,\n description: override.description ?? operation.description,\n tags: override.tags ?? operation.tags ?? fallbackTags,\n parameters: override.parameters ?? operation.parameters,\n requestBody: override.requestBody\n ? jsonRequestBody(toSchema(override.requestBody.schema), override.requestBody.required ?? true)\n : operation.requestBody,\n responses: operation.responses,\n };\n\n if (override.responses) {\n nextOperation.responses = Object.fromEntries(\n Object.entries(override.responses).map(([status, response]) => {\n return [\n status,\n jsonResponse(response.description, response.schema ? toSchema(response.schema) : undefined),\n ];\n })\n );\n return nextOperation;\n }\n\n if (override.responseSchema || override.responseStatus || override.responseDescription) {\n const status = override.responseStatus ?? '200';\n nextOperation.responses = {\n [status]: jsonResponse(\n override.responseDescription ?? 'Successful response',\n override.responseSchema ? toSchema(override.responseSchema) : undefined\n ),\n };\n }\n\n return nextOperation;\n}\n\nfunction ensurePath(paths: Record<string, PathItemObject>, path: string): PathItemObject {\n const existing = paths[path];\n if (existing) {\n return existing;\n }\n\n const created: PathItemObject = {};\n paths[path] = created;\n return created;\n}\n\nfunction setOperation(\n paths: Record<string, PathItemObject>,\n path: string,\n method: HttpMethod,\n operation: OperationObject\n): void {\n const pathItem = ensurePath(paths, path);\n pathItem[toMethodKey(method)] = operation;\n}\n\nfunction buildListParameters(\n searchFields: readonly string[],\n orderingFields: readonly string[],\n usesDefaultOffsetPagination: boolean\n): ParameterObject[] {\n const parameters: ParameterObject[] = [];\n\n if (usesDefaultOffsetPagination) {\n parameters.push(\n {\n name: 'limit',\n in: 'query',\n schema: { type: 'integer' },\n },\n {\n name: 'offset',\n in: 'query',\n schema: { type: 'integer' },\n }\n );\n }\n\n if (searchFields.length > 0) {\n parameters.push({\n name: 'search',\n in: 'query',\n schema: { type: 'string' },\n });\n }\n\n if (orderingFields.length > 0) {\n parameters.push({\n name: 'ordering',\n in: 'query',\n schema: { type: 'string' },\n });\n }\n\n return parameters;\n}\n\nfunction buildPathParameter(name: string): ParameterObject {\n return {\n name,\n in: 'path',\n required: true,\n schema: { type: 'string' },\n };\n}\n\nfunction stringifyFields(fields: readonly unknown[]): string[] {\n return fields.map(String);\n}\n\nfunction titleize(input: string): string {\n return input\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n .replace(/[-_]+/g, ' ')\n .trim()\n .replace(/\\b\\w/g, (char) => char.toUpperCase());\n}\n\nfunction validateGenericDetailPath(detailPath: string, lookupParam: string): void {\n if (!detailPath.includes(`{${lookupParam}}`)) {\n throw new Error(`GenericAPIView detail paths must include '{${lookupParam}}', received '${detailPath}'.`);\n }\n}\n\nfunction ensureModelSchema(schemas: Record<string, SchemaObject>, metadata: ResourceModelMetadata): void {\n schemas[metadata.name] = generateSchemaFromModel(toOpenAPIModel(metadata));\n}\n\nfunction registerViewSetDescriptor(\n paths: Record<string, PathItemObject>,\n schemas: Record<string, SchemaObject>,\n descriptor: OpenAPIViewSetDescriptor\n): void {\n const metadata = descriptor.resource.describeOpenAPI();\n const modelName = metadata.model.metadata.name;\n const tags = descriptor.tags ?? [modelName];\n const collectionPath = normalizePath(descriptor.basePath);\n const detailPath = collectionPath === '/' ? '/{id}' : `${collectionPath}/{id}`;\n const readSchema = toSchema(metadata.outputSchema);\n const writeSchema = toSchema(metadata.createSchema);\n const updateSchema = toSchema(metadata.updateSchema);\n\n ensureModelSchema(schemas, metadata.model.metadata);\n\n const listOperation = withOverride(\n {\n summary: `List ${modelName}s`,\n tags,\n parameters: buildListParameters(\n stringifyFields(metadata.searchFields),\n stringifyFields(metadata.orderingFields),\n metadata.usesDefaultOffsetPagination\n ),\n responses: {\n '200': jsonResponse(\n 'Successful response',\n metadata.usesDefaultOffsetPagination ? paginatedResultsSchema(readSchema) : undefined\n ),\n },\n },\n undefined,\n tags\n );\n\n setOperation(paths, collectionPath, 'GET', listOperation);\n setOperation(paths, collectionPath, 'POST', {\n summary: `Create ${modelName}`,\n tags,\n requestBody: jsonRequestBody(writeSchema),\n responses: {\n '201': jsonResponse('Created', readSchema),\n },\n });\n setOperation(paths, detailPath, 'GET', {\n summary: `Get ${modelName}`,\n tags,\n parameters: [buildPathParameter('id')],\n responses: {\n '200': jsonResponse('Successful response', readSchema),\n '404': { description: 'Not found' },\n },\n });\n const updateResponses = {\n '200': jsonResponse('Updated', readSchema),\n '404': { description: 'Not found' },\n };\n setOperation(paths, detailPath, 'PUT', {\n summary: `Update ${modelName}`,\n tags,\n parameters: [buildPathParameter('id')],\n requestBody: jsonRequestBody(updateSchema),\n responses: updateResponses,\n });\n setOperation(paths, detailPath, 'PATCH', {\n summary: `Update ${modelName}`,\n tags,\n parameters: [buildPathParameter('id')],\n requestBody: jsonRequestBody(updateSchema),\n responses: updateResponses,\n });\n setOperation(paths, detailPath, 'DELETE', {\n summary: `Delete ${modelName}`,\n tags,\n parameters: [buildPathParameter('id')],\n responses: {\n '204': { description: 'Deleted' },\n '404': { description: 'Not found' },\n },\n });\n\n for (const action of metadata.actions) {\n const actionPath =\n action.scope === 'detail' ? joinPath(detailPath, action.path) : joinPath(collectionPath, action.path);\n const baseOperation: OperationObject = {\n summary: titleize(action.name),\n tags,\n ...(action.scope === 'detail' ? { parameters: [buildPathParameter('id')] } : {}),\n responses: {\n '200': { description: 'Successful response' },\n },\n };\n\n const override = descriptor.actions?.[action.name];\n for (const method of action.methods) {\n setOperation(paths, actionPath, method, withOverride(baseOperation, override, tags));\n }\n }\n}\n\nfunction registerGenericDescriptor(\n paths: Record<string, PathItemObject>,\n schemas: Record<string, SchemaObject>,\n descriptor: OpenAPIGenericAPIViewDescriptor\n): void {\n if (!descriptor.collectionPath && !descriptor.detailPath) {\n throw new Error('GenericAPIView OpenAPI descriptors require at least one of collectionPath or detailPath.');\n }\n\n const metadata = descriptor.resource.describeOpenAPI();\n const modelName = metadata.model.metadata.name;\n const tags = descriptor.tags ?? [modelName];\n const readSchema = toSchema(metadata.outputSchema);\n const writeSchema = toSchema(metadata.createSchema);\n const updateSchema = toSchema(metadata.updateSchema);\n const allowed = new Set(metadata.allowedMethods);\n\n ensureModelSchema(schemas, metadata.model.metadata);\n\n if (descriptor.collectionPath) {\n const collectionPath = normalizePath(descriptor.collectionPath);\n if (allowed.has('GET')) {\n setOperation(\n paths,\n collectionPath,\n 'GET',\n withOverride(\n {\n summary: `List ${modelName}s`,\n tags,\n parameters: buildListParameters(\n stringifyFields(metadata.searchFields),\n stringifyFields(metadata.orderingFields),\n metadata.usesDefaultOffsetPagination\n ),\n responses: {\n '200': jsonResponse(\n 'Successful response',\n metadata.usesDefaultOffsetPagination ? paginatedResultsSchema(readSchema) : undefined\n ),\n },\n },\n descriptor.methods?.GET,\n tags\n )\n );\n }\n\n if (allowed.has('POST')) {\n setOperation(\n paths,\n collectionPath,\n 'POST',\n withOverride(\n {\n summary: `Create ${modelName}`,\n tags,\n requestBody: jsonRequestBody(writeSchema),\n responses: {\n '201': jsonResponse('Created', readSchema),\n },\n },\n descriptor.methods?.POST,\n tags\n )\n );\n }\n }\n\n if (descriptor.detailPath) {\n const detailPath = normalizePath(descriptor.detailPath);\n validateGenericDetailPath(detailPath, metadata.lookupParam);\n const detailParameters = [buildPathParameter(metadata.lookupParam)];\n\n if (allowed.has('GET')) {\n setOperation(\n paths,\n detailPath,\n 'GET',\n withOverride(\n {\n summary: `Get ${modelName}`,\n tags,\n parameters: detailParameters,\n responses: {\n '200': jsonResponse('Successful response', readSchema),\n '404': { description: 'Not found' },\n },\n },\n descriptor.methods?.GET,\n tags\n )\n );\n }\n\n if (allowed.has('PUT')) {\n setOperation(\n paths,\n detailPath,\n 'PUT',\n withOverride(\n {\n summary: `Update ${modelName}`,\n tags,\n parameters: detailParameters,\n requestBody: jsonRequestBody(updateSchema),\n responses: {\n '200': jsonResponse('Updated', readSchema),\n '404': { description: 'Not found' },\n },\n },\n descriptor.methods?.PUT,\n tags\n )\n );\n }\n\n if (allowed.has('PATCH')) {\n setOperation(\n paths,\n detailPath,\n 'PATCH',\n withOverride(\n {\n summary: `Update ${modelName}`,\n tags,\n parameters: detailParameters,\n requestBody: jsonRequestBody(updateSchema),\n responses: {\n '200': jsonResponse('Updated', readSchema),\n '404': { description: 'Not found' },\n },\n },\n descriptor.methods?.PATCH,\n tags\n )\n );\n }\n\n if (allowed.has('DELETE')) {\n setOperation(\n paths,\n detailPath,\n 'DELETE',\n withOverride(\n {\n summary: `Delete ${modelName}`,\n tags,\n parameters: detailParameters,\n responses: {\n '204': { description: 'Deleted' },\n '404': { description: 'Not found' },\n },\n },\n descriptor.methods?.DELETE,\n tags\n )\n );\n }\n }\n}\n\nfunction registerAPIViewDescriptor(paths: Record<string, PathItemObject>, descriptor: OpenAPIAPIViewDescriptor): void {\n const path = normalizePath(descriptor.path);\n const allowed = new Set(descriptor.resource.getAllowedMethods());\n const methods = Object.entries(descriptor.methods) as Array<[HttpMethod, OpenAPIOperationOverride]>;\n\n for (const [method, override] of methods) {\n if (!allowed.has(method)) {\n throw new Error(\n `APIView method '${method}' is not implemented on ${descriptor.resource.constructor.name}.`\n );\n }\n\n setOperation(\n paths,\n path,\n method,\n withOverride(\n {\n summary:\n `${method} ${titleize(descriptor.resource.constructor.name.replace(/APIView$/, ''))}`.trim(),\n responses: {\n [(override.responseStatus ?? '200') as string]: jsonResponse(\n override.responseDescription ?? 'Successful response',\n override.responseSchema ? toSchema(override.responseSchema) : undefined\n ),\n },\n },\n override,\n descriptor.tags ?? [descriptor.resource.constructor.name.replace(/APIView$/, '') || 'APIView']\n )\n );\n }\n}\n\n/**\n * Build an OpenAPI 3.1 document from Tango resource configuration.\n */\nexport function generateOpenAPISpec(config: OpenAPIGeneratorConfig): OpenAPISpec {\n const paths: Record<string, PathItemObject> = {};\n const schemas: Record<string, SchemaObject> = {};\n\n for (const resource of config.resources ?? []) {\n registerDescriptor(paths, schemas, resource);\n }\n\n return {\n openapi: '3.1.0',\n info: {\n title: config.title,\n version: config.version,\n description: config.description,\n },\n servers: config.servers,\n paths,\n components: {\n schemas,\n },\n };\n}\n\nfunction registerDescriptor(\n paths: Record<string, PathItemObject>,\n schemas: Record<string, SchemaObject>,\n descriptor: OpenAPIResourceDescriptor\n): void {\n if (descriptor.kind === 'viewset') {\n registerViewSetDescriptor(paths, schemas, descriptor);\n return;\n }\n\n if (descriptor.kind === 'generic') {\n registerGenericDescriptor(paths, schemas, descriptor);\n return;\n }\n\n registerAPIViewDescriptor(paths, descriptor);\n}\n","/**\n * Domain boundary barrel: centralizes this subdomain's public contract.\n */\n\nexport { generateOpenAPISpec } from './generateOpenAPISpec';\n","/**\n * Domain boundary barrel: exposes namespaced exports for Django-style drill-down\n * imports and curated flat exports for TS-native ergonomics.\n */\n\nexport * as spec from './spec/index';\nexport { generateOpenAPISpec } from './spec/index';\n"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
export * as domain from './domain/index';
|
|
6
6
|
export * as generators from './generators/index';
|
|
7
7
|
export * as mappers from './mappers/index';
|
|
8
|
-
export {
|
|
9
|
-
export
|
|
8
|
+
export type { OpenAPIAPIViewDescriptor, OpenAPIGeneratorConfig, OpenAPIModel, OpenAPIModelFieldMeta, OpenAPIOptions, OpenAPIOperationOverride, OpenAPIResponseOverride, OpenAPIResourceDescriptor, OpenAPISchemaInput, OpenAPISpec, OpenAPIGenericAPIViewDescriptor, OpenAPIViewSetDescriptor, } from './domain/index';
|
|
9
|
+
export { describeAPIView, describeGenericAPIView, describeViewSet } from './domain/index';
|
|
10
10
|
export { generateOpenAPISpec } from './generators/index';
|
|
11
|
-
export { generateSchemaFromModel, mapTypeToOpenAPI } from './mappers/index';
|
|
11
|
+
export { generateSchemaFromModel, generateSchemaFromZod, mapTypeToOpenAPI } from './mappers/index';
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { generateOpenAPISpec, generators_exports } from "./generators-
|
|
4
|
-
import { mappers_exports } from "./mappers-
|
|
1
|
+
import { describeAPIView, describeGenericAPIView, describeViewSet, domain_exports } from "./domain-B-7sApJT.js";
|
|
2
|
+
import { generateSchemaFromModel, generateSchemaFromZod, mapTypeToOpenAPI } from "./schema-D3ybOrpr.js";
|
|
3
|
+
import { generateOpenAPISpec, generators_exports } from "./generators-JMALItMS.js";
|
|
4
|
+
import { mappers_exports } from "./mappers-bmN95TGV.js";
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
const VERSION = "0.1.0";
|
|
8
|
-
|
|
9
|
-
//#endregion
|
|
10
|
-
export { domain_exports as domain, generateOpenAPISpec, generateSchemaFromModel, generators_exports as generators, mapTypeToOpenAPI, mappers_exports as mappers, VERSION as version };
|
|
11
|
-
//# sourceMappingURL=index.js.map
|
|
6
|
+
export { describeAPIView, describeGenericAPIView, describeViewSet, domain_exports as domain, generateOpenAPISpec, generateSchemaFromModel, generateSchemaFromZod, generators_exports as generators, mapTypeToOpenAPI, mappers_exports as mappers };
|
package/dist/mappers/index.d.ts
CHANGED
|
@@ -3,4 +3,4 @@
|
|
|
3
3
|
* imports and curated flat exports for TS-native ergonomics.
|
|
4
4
|
*/
|
|
5
5
|
export * as schema from './schema/index';
|
|
6
|
-
export { generateSchemaFromModel, mapTypeToOpenAPI } from './schema/index';
|
|
6
|
+
export { generateSchemaFromModel, generateSchemaFromZod, mapTypeToOpenAPI } from './schema/index';
|
package/dist/mappers/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { generateSchemaFromModel, mapTypeToOpenAPI, schema_exports } from "../schema-
|
|
2
|
-
import "../mappers-
|
|
1
|
+
import { generateSchemaFromModel, generateSchemaFromZod, mapTypeToOpenAPI, schema_exports } from "../schema-D3ybOrpr.js";
|
|
2
|
+
import "../mappers-bmN95TGV.js";
|
|
3
3
|
|
|
4
|
-
export { generateSchemaFromModel, mapTypeToOpenAPI, schema_exports as schema };
|
|
4
|
+
export { generateSchemaFromModel, generateSchemaFromZod, mapTypeToOpenAPI, schema_exports as schema };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { __export } from "./chunk-BkvOhyD0.js";
|
|
2
|
+
import { generateSchemaFromModel, generateSchemaFromZod, mapTypeToOpenAPI, schema_exports } from "./schema-D3ybOrpr.js";
|
|
3
|
+
|
|
4
|
+
//#region src/mappers/index.ts
|
|
5
|
+
var mappers_exports = {};
|
|
6
|
+
__export(mappers_exports, {
|
|
7
|
+
generateSchemaFromModel: () => generateSchemaFromModel,
|
|
8
|
+
generateSchemaFromZod: () => generateSchemaFromZod,
|
|
9
|
+
mapTypeToOpenAPI: () => mapTypeToOpenAPI,
|
|
10
|
+
schema: () => schema_exports
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
//#endregion
|
|
14
|
+
export { mappers_exports };
|
|
15
|
+
//# sourceMappingURL=mappers-bmN95TGV.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mappers-
|
|
1
|
+
{"version":3,"file":"mappers-bmN95TGV.js","names":[],"sources":["../src/mappers/index.ts"],"sourcesContent":["/**\n * Domain boundary barrel: exposes namespaced exports for Django-style drill-down\n * imports and curated flat exports for TS-native ergonomics.\n */\n\nexport * as schema from './schema/index';\nexport { generateSchemaFromModel, generateSchemaFromZod, mapTypeToOpenAPI } from './schema/index';\n"],"mappings":""}
|
|
@@ -1,14 +1,6 @@
|
|
|
1
|
+
import { __export } from "./chunk-BkvOhyD0.js";
|
|
2
|
+
import { z } from "zod";
|
|
1
3
|
|
|
2
|
-
//#region rolldown:runtime
|
|
3
|
-
var __defProp = Object.defineProperty;
|
|
4
|
-
var __export = (target, all) => {
|
|
5
|
-
for (var name in all) __defProp(target, name, {
|
|
6
|
-
get: all[name],
|
|
7
|
-
enumerable: true
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
//#endregion
|
|
12
4
|
//#region src/mappers/schema/mapTypeToOpenAPI.ts
|
|
13
5
|
function mapTypeToOpenAPI(type) {
|
|
14
6
|
const typeMap = {
|
|
@@ -47,14 +39,29 @@ function generateSchemaFromModel(model) {
|
|
|
47
39
|
};
|
|
48
40
|
}
|
|
49
41
|
|
|
42
|
+
//#endregion
|
|
43
|
+
//#region src/mappers/schema/generateSchemaFromZod.ts
|
|
44
|
+
function stripDialect(schema) {
|
|
45
|
+
if (Array.isArray(schema)) return schema.map((entry) => stripDialect(entry));
|
|
46
|
+
if (schema && typeof schema === "object") {
|
|
47
|
+
const input = schema;
|
|
48
|
+
return Object.fromEntries(Object.entries(input).filter(([key]) => key !== "$schema").map(([key, value]) => [key, stripDialect(value)]));
|
|
49
|
+
}
|
|
50
|
+
return schema;
|
|
51
|
+
}
|
|
52
|
+
function generateSchemaFromZod(schema) {
|
|
53
|
+
return stripDialect(z.toJSONSchema(schema));
|
|
54
|
+
}
|
|
55
|
+
|
|
50
56
|
//#endregion
|
|
51
57
|
//#region src/mappers/schema/index.ts
|
|
52
58
|
var schema_exports = {};
|
|
53
59
|
__export(schema_exports, {
|
|
54
60
|
generateSchemaFromModel: () => generateSchemaFromModel,
|
|
61
|
+
generateSchemaFromZod: () => generateSchemaFromZod,
|
|
55
62
|
mapTypeToOpenAPI: () => mapTypeToOpenAPI
|
|
56
63
|
});
|
|
57
64
|
|
|
58
65
|
//#endregion
|
|
59
|
-
export {
|
|
60
|
-
//# sourceMappingURL=schema-
|
|
66
|
+
export { generateSchemaFromModel, generateSchemaFromZod, mapTypeToOpenAPI, schema_exports };
|
|
67
|
+
//# sourceMappingURL=schema-D3ybOrpr.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-D3ybOrpr.js","names":["type: string","typeMap: Record<string, string>","model: OpenAPIModel","properties: Record<string, SchemaObject>","required: string[]","schema: unknown","schema: z.ZodType"],"sources":["../src/mappers/schema/mapTypeToOpenAPI.ts","../src/mappers/schema/generateSchemaFromModel.ts","../src/mappers/schema/generateSchemaFromZod.ts","../src/mappers/schema/index.ts"],"sourcesContent":["/**\n * Map Tango field types to OpenAPI scalar/object type names.\n */\nexport function mapTypeToOpenAPI(type: string): string {\n const typeMap: Record<string, string> = {\n string: 'string',\n number: 'number',\n boolean: 'boolean',\n date: 'string',\n serial: 'integer',\n int: 'integer',\n bigint: 'integer',\n text: 'string',\n bool: 'boolean',\n uuid: 'string',\n jsonb: 'object',\n timestamptz: 'string',\n };\n\n return typeMap[type] || 'string';\n}\n","import type { OpenAPIModel, SchemaObject } from '../../domain';\nimport { mapTypeToOpenAPI } from './mapTypeToOpenAPI';\n\n/**\n * Derive an OpenAPI schema object from Tango model metadata.\n */\nexport function generateSchemaFromModel(model: OpenAPIModel): SchemaObject {\n const properties: Record<string, SchemaObject> = {};\n const required: string[] = [];\n\n for (const [name, meta] of Object.entries(model.fields)) {\n properties[name] = {\n type: mapTypeToOpenAPI(meta.type),\n description: meta.description,\n };\n\n if (!meta.nullable && meta.default === undefined && !meta.primaryKey) {\n required.push(name);\n }\n }\n\n return {\n type: 'object',\n properties,\n required: required.length > 0 ? required : undefined,\n };\n}\n","import { z } from 'zod';\nimport type { SchemaObject } from '../../domain';\n\nfunction stripDialect(schema: unknown): unknown {\n if (Array.isArray(schema)) {\n return schema.map((entry) => stripDialect(entry));\n }\n\n if (schema && typeof schema === 'object') {\n const input = schema as Record<string, unknown>;\n return Object.fromEntries(\n Object.entries(input)\n .filter(([key]) => key !== '$schema')\n .map(([key, value]) => [key, stripDialect(value)])\n );\n }\n\n return schema;\n}\n\n/**\n * Derive an OpenAPI-compatible schema object from a Zod schema.\n */\nexport function generateSchemaFromZod(schema: z.ZodType): SchemaObject {\n return stripDialect(z.toJSONSchema(schema)) as SchemaObject;\n}\n","/**\n * Domain boundary barrel: centralizes this subdomain's public contract.\n */\n\nexport { generateSchemaFromModel } from './generateSchemaFromModel';\nexport { generateSchemaFromZod } from './generateSchemaFromZod';\nexport { mapTypeToOpenAPI } from './mapTypeToOpenAPI';\n"],"mappings":";;;;AAGO,SAAS,iBAAiBA,MAAsB;CACnD,MAAMC,UAAkC;EACpC,QAAQ;EACR,QAAQ;EACR,SAAS;EACT,MAAM;EACN,QAAQ;EACR,KAAK;EACL,QAAQ;EACR,MAAM;EACN,MAAM;EACN,MAAM;EACN,OAAO;EACP,aAAa;CAChB;AAED,QAAO,QAAQ,SAAS;AAC3B;;;;ACdM,SAAS,wBAAwBC,OAAmC;CACvE,MAAMC,aAA2C,CAAE;CACnD,MAAMC,WAAqB,CAAE;AAE7B,MAAK,MAAM,CAAC,MAAM,KAAK,IAAI,OAAO,QAAQ,MAAM,OAAO,EAAE;AACrD,aAAW,QAAQ;GACf,MAAM,iBAAiB,KAAK,KAAK;GACjC,aAAa,KAAK;EACrB;AAED,OAAK,KAAK,YAAY,KAAK,YAAY,cAAc,KAAK,WACtD,UAAS,KAAK,KAAK;CAE1B;AAED,QAAO;EACH,MAAM;EACN;EACA,UAAU,SAAS,SAAS,IAAI,WAAW;CAC9C;AACJ;;;;ACvBD,SAAS,aAAaC,QAA0B;AAC5C,KAAI,MAAM,QAAQ,OAAO,CACrB,QAAO,OAAO,IAAI,CAAC,UAAU,aAAa,MAAM,CAAC;AAGrD,KAAI,iBAAiB,WAAW,UAAU;EACtC,MAAM,QAAQ;AACd,SAAO,OAAO,YACV,OAAO,QAAQ,MAAM,CAChB,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,UAAU,CACpC,IAAI,CAAC,CAAC,KAAK,MAAM,KAAK,CAAC,KAAK,aAAa,MAAM,AAAC,EAAC,CACzD;CACJ;AAED,QAAO;AACV;AAKM,SAAS,sBAAsBC,QAAiC;AACnE,QAAO,aAAa,EAAE,aAAa,OAAO,CAAC;AAC9C"}
|