@rexeus/typeweaver-gen 0.8.0 → 0.9.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 +58 -29
- package/dist/index.cjs +409 -153
- package/dist/index.d.cts +196 -149
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +196 -149
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +386 -139
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -4
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Object.defineProperty(exports, Symbol.toStringTag, { value:
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
//#region \0rolldown/runtime.js
|
|
3
3
|
var __create = Object.create;
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
@@ -7,16 +7,12 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
|
7
7
|
var __getProtoOf = Object.getPrototypeOf;
|
|
8
8
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
9
|
var __copyProps = (to, from, except, desc) => {
|
|
10
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
}
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
11
|
+
key = keys[i];
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
13
|
+
get: ((k) => from[k]).bind(null, key),
|
|
14
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
15
|
+
});
|
|
20
16
|
}
|
|
21
17
|
return to;
|
|
22
18
|
};
|
|
@@ -24,14 +20,244 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
20
|
value: mod,
|
|
25
21
|
enumerable: true
|
|
26
22
|
}) : target, mod));
|
|
27
|
-
|
|
28
23
|
//#endregion
|
|
24
|
+
let _rexeus_typeweaver_core = require("@rexeus/typeweaver-core");
|
|
25
|
+
let zod = require("zod");
|
|
29
26
|
let node_fs = require("node:fs");
|
|
30
27
|
node_fs = __toESM(node_fs);
|
|
31
28
|
let node_path = require("node:path");
|
|
32
29
|
node_path = __toESM(node_path);
|
|
33
30
|
let ejs = require("ejs");
|
|
34
|
-
|
|
31
|
+
ejs = __toESM(ejs);
|
|
32
|
+
//#region src/errors/DerivedResponseCycleError.ts
|
|
33
|
+
var DerivedResponseCycleError = class extends Error {
|
|
34
|
+
constructor(responseName) {
|
|
35
|
+
super(`Derived response '${responseName}' contains a cyclic lineage.`);
|
|
36
|
+
this.name = "DerivedResponseCycleError";
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
//#endregion
|
|
40
|
+
//#region src/errors/DuplicateOperationIdError.ts
|
|
41
|
+
var DuplicateOperationIdError = class extends Error {
|
|
42
|
+
constructor(operationId) {
|
|
43
|
+
super(`Operation ID '${operationId}' must be globally unique within a spec.`);
|
|
44
|
+
this.name = "DuplicateOperationIdError";
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
//#endregion
|
|
48
|
+
//#region src/errors/DuplicateRouteError.ts
|
|
49
|
+
var DuplicateRouteError = class extends Error {
|
|
50
|
+
constructor(method, path, normalizedPath) {
|
|
51
|
+
super(`Route '${method} ${path}' conflicts with an existing route using normalized path '${normalizedPath}'.`);
|
|
52
|
+
this.name = "DuplicateRouteError";
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
//#endregion
|
|
56
|
+
//#region src/errors/EmptyOperationResponsesError.ts
|
|
57
|
+
var EmptyOperationResponsesError = class extends Error {
|
|
58
|
+
constructor(operationId) {
|
|
59
|
+
super(`Operation '${operationId}' must declare at least one response.`);
|
|
60
|
+
this.name = "EmptyOperationResponsesError";
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
//#endregion
|
|
64
|
+
//#region src/errors/EmptyResourceOperationsError.ts
|
|
65
|
+
var EmptyResourceOperationsError = class extends Error {
|
|
66
|
+
constructor(resourceName) {
|
|
67
|
+
super(`Resource '${resourceName}' must contain at least one operation.`);
|
|
68
|
+
this.name = "EmptyResourceOperationsError";
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
//#endregion
|
|
72
|
+
//#region src/errors/EmptySpecResourcesError.ts
|
|
73
|
+
var EmptySpecResourcesError = class extends Error {
|
|
74
|
+
constructor() {
|
|
75
|
+
super("Spec definition must contain at least one resource.");
|
|
76
|
+
this.name = "EmptySpecResourcesError";
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
//#endregion
|
|
80
|
+
//#region src/errors/InvalidDerivedResponseError.ts
|
|
81
|
+
var InvalidDerivedResponseError = class extends Error {
|
|
82
|
+
constructor(responseName) {
|
|
83
|
+
super(`Derived response '${responseName}' contains invalid lineage metadata.`);
|
|
84
|
+
this.name = "InvalidDerivedResponseError";
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
//#endregion
|
|
88
|
+
//#region src/errors/InvalidRequestSchemaError.ts
|
|
89
|
+
var InvalidRequestSchemaError = class extends Error {
|
|
90
|
+
constructor(operationId, requestPart) {
|
|
91
|
+
super(`Operation '${operationId}' has an invalid request.${requestPart} schema definition.`);
|
|
92
|
+
this.name = "InvalidRequestSchemaError";
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
//#endregion
|
|
96
|
+
//#region src/errors/MissingDerivedResponseParentError.ts
|
|
97
|
+
var MissingDerivedResponseParentError = class extends Error {
|
|
98
|
+
constructor(responseName, parentName) {
|
|
99
|
+
super(`Derived response '${responseName}' references missing canonical parent '${parentName}'.`);
|
|
100
|
+
this.name = "MissingDerivedResponseParentError";
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
//#endregion
|
|
104
|
+
//#region src/errors/PathParameterMismatchError.ts
|
|
105
|
+
var PathParameterMismatchError = class extends Error {
|
|
106
|
+
constructor(operationId, path, pathParams, requestParams) {
|
|
107
|
+
super(`Operation '${operationId}' has mismatched path parameters for '${path}'. Path params: [${pathParams.join(", ")}], request.param keys: [${requestParams.join(", ")}].`);
|
|
108
|
+
this.name = "PathParameterMismatchError";
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
//#endregion
|
|
112
|
+
//#region src/helpers/routePath.ts
|
|
113
|
+
const PATH_PARAMETER_PATTERN = /:([A-Za-z0-9_]+)/g;
|
|
114
|
+
const normalizeRoutePath = (path) => {
|
|
115
|
+
const segments = path.split("/").filter(Boolean);
|
|
116
|
+
if (segments.length === 0) return "/";
|
|
117
|
+
return `/${segments.map((segment) => segment.startsWith(":") ? ":" : segment).join("/")}`;
|
|
118
|
+
};
|
|
119
|
+
const getPathParameterNames = (path) => {
|
|
120
|
+
return Array.from(path.matchAll(PATH_PARAMETER_PATTERN), (match) => match[1]);
|
|
121
|
+
};
|
|
122
|
+
//#endregion
|
|
123
|
+
//#region src/validation/derivedResponseValidation.ts
|
|
124
|
+
const validateDerivedResponseMetadata = (response) => {
|
|
125
|
+
const derived = response.derived;
|
|
126
|
+
if (derived === void 0) return;
|
|
127
|
+
if (derived.parentName === response.name) throw new DerivedResponseCycleError(response.name);
|
|
128
|
+
if (derived.lineage.length === 0) throw new InvalidDerivedResponseError(response.name);
|
|
129
|
+
if (derived.lineage.at(-1) !== response.name) throw new InvalidDerivedResponseError(response.name);
|
|
130
|
+
if (derived.lineage.length !== derived.depth) throw new InvalidDerivedResponseError(response.name);
|
|
131
|
+
if (new Set(derived.lineage).size !== derived.lineage.length) throw new DerivedResponseCycleError(response.name);
|
|
132
|
+
if (derived.depth > 1 && derived.lineage.at(-2) !== derived.parentName) throw new InvalidDerivedResponseError(response.name);
|
|
133
|
+
};
|
|
134
|
+
const collectCanonicalResponseDefinitions = (definition) => {
|
|
135
|
+
const canonicalResponses = /* @__PURE__ */ new Map();
|
|
136
|
+
for (const resource of Object.values(definition.resources)) for (const operation of resource.operations) for (const response of operation.responses) {
|
|
137
|
+
if (!(0, _rexeus_typeweaver_core.isNamedResponseDefinition)(response)) continue;
|
|
138
|
+
validateDerivedResponseMetadata(response);
|
|
139
|
+
canonicalResponses.set(response.name, response);
|
|
140
|
+
}
|
|
141
|
+
return canonicalResponses;
|
|
142
|
+
};
|
|
143
|
+
const getDerivedResponseChain = (response, canonicalResponses) => {
|
|
144
|
+
const chain = [response.name];
|
|
145
|
+
const visitedResponseNames = new Set(chain);
|
|
146
|
+
let parentName = response.derived?.parentName;
|
|
147
|
+
while (parentName !== void 0) {
|
|
148
|
+
if (visitedResponseNames.has(parentName)) throw new DerivedResponseCycleError(response.name);
|
|
149
|
+
const parentResponse = canonicalResponses.get(parentName);
|
|
150
|
+
if (parentResponse === void 0) throw new MissingDerivedResponseParentError(response.name, parentName);
|
|
151
|
+
chain.unshift(parentResponse.name);
|
|
152
|
+
visitedResponseNames.add(parentResponse.name);
|
|
153
|
+
parentName = parentResponse.derived?.parentName;
|
|
154
|
+
}
|
|
155
|
+
return chain;
|
|
156
|
+
};
|
|
157
|
+
const validateDerivedResponseGraph = (canonicalResponses) => {
|
|
158
|
+
for (const response of canonicalResponses.values()) {
|
|
159
|
+
if (response.derived === void 0) continue;
|
|
160
|
+
const materializedLineage = getDerivedResponseChain(response, canonicalResponses).slice(1);
|
|
161
|
+
if (response.derived.depth !== materializedLineage.length) throw new InvalidDerivedResponseError(response.name);
|
|
162
|
+
if (materializedLineage.length !== response.derived.lineage.length || materializedLineage.some((lineageEntry, index) => lineageEntry !== response.derived?.lineage[index])) throw new InvalidDerivedResponseError(response.name);
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
const normalizeResponseDefinition = (response) => {
|
|
166
|
+
return {
|
|
167
|
+
name: response.name,
|
|
168
|
+
statusCode: response.statusCode,
|
|
169
|
+
statusCodeName: _rexeus_typeweaver_core.HttpStatusCodeNameMap[response.statusCode],
|
|
170
|
+
description: response.description,
|
|
171
|
+
header: response.header,
|
|
172
|
+
body: response.body,
|
|
173
|
+
kind: response.derived === void 0 ? "response" : "derived-response",
|
|
174
|
+
derivedFrom: response.derived?.parentName,
|
|
175
|
+
lineage: response.derived?.lineage,
|
|
176
|
+
depth: response.derived?.depth
|
|
177
|
+
};
|
|
178
|
+
};
|
|
179
|
+
const collectCanonicalResponses = (definition) => {
|
|
180
|
+
const canonicalResponseDefinitions = collectCanonicalResponseDefinitions(definition);
|
|
181
|
+
validateDerivedResponseGraph(canonicalResponseDefinitions);
|
|
182
|
+
return new Map(Array.from(canonicalResponseDefinitions.entries(), ([responseName, response]) => [responseName, normalizeResponseDefinition(response)]));
|
|
183
|
+
};
|
|
184
|
+
//#endregion
|
|
185
|
+
//#region src/normalizeSpec.ts
|
|
186
|
+
const isZodType = (schema) => {
|
|
187
|
+
return schema instanceof zod.z.ZodType;
|
|
188
|
+
};
|
|
189
|
+
const isZodObject = (schema) => {
|
|
190
|
+
return schema instanceof zod.z.ZodObject;
|
|
191
|
+
};
|
|
192
|
+
const validateRequestSchema = (operationId, requestPart, schema) => {
|
|
193
|
+
if (!isZodType(schema)) throw new InvalidRequestSchemaError(operationId, requestPart);
|
|
194
|
+
if (requestPart === "param" && !isZodObject(schema)) throw new InvalidRequestSchemaError(operationId, requestPart);
|
|
195
|
+
};
|
|
196
|
+
const validateRequest = (operationId, path, request) => {
|
|
197
|
+
if (request.header !== void 0) validateRequestSchema(operationId, "header", request.header);
|
|
198
|
+
if (request.param !== void 0) validateRequestSchema(operationId, "param", request.param);
|
|
199
|
+
if (request.query !== void 0) validateRequestSchema(operationId, "query", request.query);
|
|
200
|
+
if (request.body !== void 0) validateRequestSchema(operationId, "body", request.body);
|
|
201
|
+
const pathParams = getPathParameterNames(path);
|
|
202
|
+
const requestParams = request.param === void 0 ? [] : Object.keys(request.param.shape);
|
|
203
|
+
if (pathParams.length !== requestParams.length || pathParams.some((pathParam) => !requestParams.includes(pathParam))) throw new PathParameterMismatchError(operationId, path, pathParams, requestParams);
|
|
204
|
+
if (request.header === void 0 && request.param === void 0 && request.query === void 0 && request.body === void 0) return;
|
|
205
|
+
return {
|
|
206
|
+
header: request.header,
|
|
207
|
+
param: request.param,
|
|
208
|
+
query: request.query,
|
|
209
|
+
body: request.body
|
|
210
|
+
};
|
|
211
|
+
};
|
|
212
|
+
const normalizeOperationResponses = (responses) => {
|
|
213
|
+
return responses.map((response) => {
|
|
214
|
+
if ((0, _rexeus_typeweaver_core.isNamedResponseDefinition)(response)) return {
|
|
215
|
+
responseName: response.name,
|
|
216
|
+
source: "canonical"
|
|
217
|
+
};
|
|
218
|
+
return {
|
|
219
|
+
responseName: response.name,
|
|
220
|
+
source: "inline",
|
|
221
|
+
response: normalizeResponseDefinition(response)
|
|
222
|
+
};
|
|
223
|
+
});
|
|
224
|
+
};
|
|
225
|
+
const normalizeOperation = (operationIds, routeKeys, operation) => {
|
|
226
|
+
if (operationIds.has(operation.operationId)) throw new DuplicateOperationIdError(operation.operationId);
|
|
227
|
+
operationIds.add(operation.operationId);
|
|
228
|
+
const normalizedPath = normalizeRoutePath(operation.path);
|
|
229
|
+
const routeKey = `${operation.method}:${normalizedPath}`;
|
|
230
|
+
if (routeKeys.has(routeKey)) throw new DuplicateRouteError(operation.method, operation.path, normalizedPath);
|
|
231
|
+
routeKeys.add(routeKey);
|
|
232
|
+
if (operation.responses.length === 0) throw new EmptyOperationResponsesError(operation.operationId);
|
|
233
|
+
return {
|
|
234
|
+
operationId: operation.operationId,
|
|
235
|
+
method: operation.method,
|
|
236
|
+
path: operation.path,
|
|
237
|
+
summary: operation.summary,
|
|
238
|
+
request: validateRequest(operation.operationId, operation.path, operation.request),
|
|
239
|
+
responses: normalizeOperationResponses(operation.responses)
|
|
240
|
+
};
|
|
241
|
+
};
|
|
242
|
+
const normalizeSpec = (definition) => {
|
|
243
|
+
const resourceEntries = Object.entries(definition.resources);
|
|
244
|
+
if (resourceEntries.length === 0) throw new EmptySpecResourcesError();
|
|
245
|
+
(0, _rexeus_typeweaver_core.validateUniqueResponseNames)(definition.resources);
|
|
246
|
+
const canonicalResponses = collectCanonicalResponses(definition);
|
|
247
|
+
const operationIds = /* @__PURE__ */ new Set();
|
|
248
|
+
const routeKeys = /* @__PURE__ */ new Set();
|
|
249
|
+
return {
|
|
250
|
+
resources: resourceEntries.map(([resourceName, resource]) => {
|
|
251
|
+
if (resource.operations.length === 0) throw new EmptyResourceOperationsError(resourceName);
|
|
252
|
+
return {
|
|
253
|
+
name: resourceName,
|
|
254
|
+
operations: resource.operations.map((operation) => normalizeOperation(operationIds, routeKeys, operation))
|
|
255
|
+
};
|
|
256
|
+
}),
|
|
257
|
+
responses: Array.from(canonicalResponses.values())
|
|
258
|
+
};
|
|
259
|
+
};
|
|
260
|
+
//#endregion
|
|
35
261
|
//#region src/plugins/types.ts
|
|
36
262
|
/**
|
|
37
263
|
* Plugin loading error
|
|
@@ -54,7 +280,6 @@ var PluginDependencyError = class extends Error {
|
|
|
54
280
|
this.name = "PluginDependencyError";
|
|
55
281
|
}
|
|
56
282
|
};
|
|
57
|
-
|
|
58
283
|
//#endregion
|
|
59
284
|
//#region src/plugins/BasePlugin.ts
|
|
60
285
|
/**
|
|
@@ -76,8 +301,8 @@ var BasePlugin = class {
|
|
|
76
301
|
/**
|
|
77
302
|
* Default implementation - override in subclasses if needed
|
|
78
303
|
*/
|
|
79
|
-
collectResources(
|
|
80
|
-
return
|
|
304
|
+
collectResources(normalizedSpec) {
|
|
305
|
+
return normalizedSpec;
|
|
81
306
|
}
|
|
82
307
|
/**
|
|
83
308
|
* Default implementation - override in subclasses if needed
|
|
@@ -91,174 +316,205 @@ var BasePlugin = class {
|
|
|
91
316
|
if (node_fs.default.existsSync(node_path.default.join(libDir, "index.ts"))) context.addGeneratedFile(libIndexPath);
|
|
92
317
|
}
|
|
93
318
|
};
|
|
94
|
-
|
|
95
319
|
//#endregion
|
|
96
|
-
//#region src/plugins/
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
node_fs.default.mkdirSync(fullPath, { recursive: true });
|
|
120
|
-
}
|
|
121
|
-
/**
|
|
122
|
-
* Get the template path for this plugin
|
|
123
|
-
*/
|
|
124
|
-
getTemplatePath(context, templateName) {
|
|
125
|
-
return node_path.default.join(context.templateDir, templateName);
|
|
126
|
-
}
|
|
127
|
-
};
|
|
128
|
-
|
|
320
|
+
//#region src/plugins/pluginRegistry.ts
|
|
321
|
+
function createPluginRegistry() {
|
|
322
|
+
const plugins = /* @__PURE__ */ new Map();
|
|
323
|
+
return {
|
|
324
|
+
register: (plugin, config) => {
|
|
325
|
+
if (plugins.has(plugin.name)) {
|
|
326
|
+
console.info(`Skipping duplicate registration of required plugin: ${plugin.name}`);
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
const registration = {
|
|
330
|
+
name: plugin.name,
|
|
331
|
+
plugin,
|
|
332
|
+
config
|
|
333
|
+
};
|
|
334
|
+
plugins.set(plugin.name, registration);
|
|
335
|
+
console.info(`Registered plugin: ${plugin.name}`);
|
|
336
|
+
},
|
|
337
|
+
get: (name) => plugins.get(name),
|
|
338
|
+
getAll: () => Array.from(plugins.values()),
|
|
339
|
+
has: (name) => plugins.has(name),
|
|
340
|
+
clear: () => plugins.clear()
|
|
341
|
+
};
|
|
342
|
+
}
|
|
129
343
|
//#endregion
|
|
130
|
-
//#region src/
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
this.plugins = /* @__PURE__ */ new Map();
|
|
344
|
+
//#region src/helpers/path.ts
|
|
345
|
+
function relative(from, to) {
|
|
346
|
+
const relativePath = node_path.default.relative(from, to);
|
|
347
|
+
if (relativePath.includes("node_modules")) {
|
|
348
|
+
const parts = relativePath.split(node_path.default.sep);
|
|
349
|
+
const index = parts.indexOf("node_modules");
|
|
350
|
+
return parts.slice(index + 1).join("/");
|
|
138
351
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
if (this.plugins.has(plugin.name)) {
|
|
144
|
-
console.info(`Skipping duplicate registration of required plugin: ${plugin.name}`);
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
const registration = {
|
|
148
|
-
name: plugin.name,
|
|
149
|
-
plugin,
|
|
150
|
-
config
|
|
151
|
-
};
|
|
152
|
-
this.plugins.set(plugin.name, registration);
|
|
153
|
-
console.info(`Registered plugin: ${plugin.name}`);
|
|
154
|
-
}
|
|
155
|
-
/**
|
|
156
|
-
* Get a registered plugin
|
|
157
|
-
*/
|
|
158
|
-
get(name) {
|
|
159
|
-
return this.plugins.get(name);
|
|
160
|
-
}
|
|
161
|
-
/**
|
|
162
|
-
* Get all registered plugins
|
|
163
|
-
*/
|
|
164
|
-
getAll() {
|
|
165
|
-
return Array.from(this.plugins.values());
|
|
166
|
-
}
|
|
167
|
-
/**
|
|
168
|
-
* Check if a plugin is registered
|
|
169
|
-
*/
|
|
170
|
-
has(name) {
|
|
171
|
-
return this.plugins.has(name);
|
|
172
|
-
}
|
|
173
|
-
/**
|
|
174
|
-
* Clear all registered plugins (except required ones)
|
|
175
|
-
*/
|
|
176
|
-
clear() {
|
|
177
|
-
this.plugins.clear();
|
|
178
|
-
}
|
|
179
|
-
};
|
|
180
|
-
|
|
352
|
+
const posixPath = relativePath.split(node_path.default.sep).join("/");
|
|
353
|
+
if (!posixPath.startsWith("./") && !posixPath.startsWith("../")) return `./${posixPath}`;
|
|
354
|
+
return posixPath;
|
|
355
|
+
}
|
|
181
356
|
//#endregion
|
|
182
|
-
//#region src/plugins/
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
var PluginContextBuilder = class {
|
|
187
|
-
generatedFiles = /* @__PURE__ */ new Set();
|
|
188
|
-
/**
|
|
189
|
-
* Create a basic plugin context
|
|
190
|
-
*/
|
|
191
|
-
createPluginContext(params) {
|
|
357
|
+
//#region src/plugins/pluginContext.ts
|
|
358
|
+
function createPluginContextBuilder() {
|
|
359
|
+
const generatedFiles = /* @__PURE__ */ new Set();
|
|
360
|
+
const createPluginContext = (params) => {
|
|
192
361
|
return {
|
|
193
362
|
outputDir: params.outputDir,
|
|
194
363
|
inputDir: params.inputDir,
|
|
195
364
|
config: params.config
|
|
196
365
|
};
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
366
|
+
};
|
|
367
|
+
const createGeneratorContext = (params) => {
|
|
368
|
+
const pluginContext = createPluginContext(params);
|
|
369
|
+
const canonicalResponsesByName = new Map(params.normalizedSpec.responses.map((response) => [response.name, response]));
|
|
370
|
+
const getResourceOutputDir = (resourceName) => {
|
|
371
|
+
return node_path.default.join(params.outputDir, resourceName);
|
|
372
|
+
};
|
|
373
|
+
const getOperationOutputPaths = (config) => {
|
|
374
|
+
const outputDir = getResourceOutputDir(config.resourceName);
|
|
375
|
+
const requestFileName = `${config.operationId}Request.ts`;
|
|
376
|
+
const responseFileName = `${config.operationId}Response.ts`;
|
|
377
|
+
const requestValidationFileName = `${config.operationId}RequestValidator.ts`;
|
|
378
|
+
const responseValidationFileName = `${config.operationId}ResponseValidator.ts`;
|
|
379
|
+
const clientFileName = `${config.operationId}Client.ts`;
|
|
380
|
+
return {
|
|
381
|
+
outputDir,
|
|
382
|
+
requestFile: node_path.default.join(outputDir, requestFileName),
|
|
383
|
+
requestFileName,
|
|
384
|
+
responseFile: node_path.default.join(outputDir, responseFileName),
|
|
385
|
+
responseFileName,
|
|
386
|
+
requestValidationFile: node_path.default.join(outputDir, requestValidationFileName),
|
|
387
|
+
requestValidationFileName,
|
|
388
|
+
responseValidationFile: node_path.default.join(outputDir, responseValidationFileName),
|
|
389
|
+
responseValidationFileName,
|
|
390
|
+
clientFile: node_path.default.join(outputDir, clientFileName),
|
|
391
|
+
clientFileName
|
|
392
|
+
};
|
|
393
|
+
};
|
|
394
|
+
const getCanonicalResponse = (responseName) => {
|
|
395
|
+
const response = canonicalResponsesByName.get(responseName);
|
|
396
|
+
if (response === void 0) throw new Error(`Missing canonical response '${responseName}'.`);
|
|
397
|
+
return response;
|
|
398
|
+
};
|
|
399
|
+
const getCanonicalResponseOutputFile = (responseName) => {
|
|
400
|
+
return node_path.default.join(params.responsesOutputDir, `${responseName}Response.ts`);
|
|
401
|
+
};
|
|
202
402
|
return {
|
|
203
|
-
...
|
|
204
|
-
|
|
403
|
+
...pluginContext,
|
|
404
|
+
normalizedSpec: params.normalizedSpec,
|
|
205
405
|
templateDir: params.templateDir,
|
|
206
406
|
coreDir: params.coreDir,
|
|
407
|
+
responsesOutputDir: params.responsesOutputDir,
|
|
408
|
+
specOutputDir: params.specOutputDir,
|
|
409
|
+
getCanonicalResponse,
|
|
410
|
+
getCanonicalResponseOutputFile,
|
|
411
|
+
getCanonicalResponseImportPath: (config) => {
|
|
412
|
+
return relative(config.importerDir, getCanonicalResponseOutputFile(config.responseName).replace(/\.ts$/, ""));
|
|
413
|
+
},
|
|
414
|
+
getSpecImportPath: (config) => {
|
|
415
|
+
return relative(config.importerDir, node_path.default.join(params.specOutputDir, "spec").replace(/\.ts$/, ""));
|
|
416
|
+
},
|
|
417
|
+
getOperationDefinitionAccessor: (config) => {
|
|
418
|
+
return `getOperationDefinition(spec, ${JSON.stringify(config.resourceName)}, ${JSON.stringify(config.operationId)})`;
|
|
419
|
+
},
|
|
420
|
+
getOperationOutputPaths,
|
|
421
|
+
getResourceOutputDir,
|
|
207
422
|
writeFile: (relativePath, content) => {
|
|
208
423
|
const fullPath = node_path.default.join(params.outputDir, relativePath);
|
|
209
424
|
const dir = node_path.default.dirname(fullPath);
|
|
210
425
|
node_fs.default.mkdirSync(dir, { recursive: true });
|
|
211
426
|
node_fs.default.writeFileSync(fullPath, content);
|
|
212
|
-
|
|
427
|
+
generatedFiles.add(relativePath);
|
|
213
428
|
console.info(`Generated: ${relativePath}`);
|
|
214
429
|
},
|
|
215
430
|
renderTemplate: (templatePath, data) => {
|
|
216
431
|
const fullTemplatePath = node_path.default.isAbsolute(templatePath) ? templatePath : node_path.default.join(params.templateDir, templatePath);
|
|
217
|
-
|
|
432
|
+
const template = node_fs.default.readFileSync(fullTemplatePath, "utf8");
|
|
433
|
+
return ejs.default.render(template, data);
|
|
218
434
|
},
|
|
219
435
|
addGeneratedFile: (relativePath) => {
|
|
220
|
-
|
|
436
|
+
generatedFiles.add(relativePath);
|
|
221
437
|
},
|
|
222
438
|
getGeneratedFiles: () => {
|
|
223
|
-
return Array.from(
|
|
439
|
+
return Array.from(generatedFiles);
|
|
224
440
|
}
|
|
225
441
|
};
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
* Clear generated files tracking
|
|
235
|
-
*/
|
|
236
|
-
clearGeneratedFiles() {
|
|
237
|
-
this.generatedFiles.clear();
|
|
238
|
-
}
|
|
239
|
-
};
|
|
240
|
-
|
|
442
|
+
};
|
|
443
|
+
return {
|
|
444
|
+
createPluginContext,
|
|
445
|
+
createGeneratorContext,
|
|
446
|
+
getGeneratedFiles: () => Array.from(generatedFiles),
|
|
447
|
+
clearGeneratedFiles: () => generatedFiles.clear()
|
|
448
|
+
};
|
|
449
|
+
}
|
|
241
450
|
//#endregion
|
|
242
|
-
//#region src/helpers/
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
451
|
+
//#region src/helpers/routeSort.ts
|
|
452
|
+
/**
|
|
453
|
+
* HTTP method priority for route ordering.
|
|
454
|
+
* Lower numbers = higher priority (sorted first).
|
|
455
|
+
*/
|
|
456
|
+
const METHOD_PRIORITY = {
|
|
457
|
+
[_rexeus_typeweaver_core.HttpMethod.GET]: 1,
|
|
458
|
+
[_rexeus_typeweaver_core.HttpMethod.POST]: 2,
|
|
459
|
+
[_rexeus_typeweaver_core.HttpMethod.PUT]: 3,
|
|
460
|
+
[_rexeus_typeweaver_core.HttpMethod.PATCH]: 4,
|
|
461
|
+
[_rexeus_typeweaver_core.HttpMethod.DELETE]: 5,
|
|
462
|
+
[_rexeus_typeweaver_core.HttpMethod.OPTIONS]: 6,
|
|
463
|
+
[_rexeus_typeweaver_core.HttpMethod.HEAD]: 7
|
|
464
|
+
};
|
|
465
|
+
/**
|
|
466
|
+
* Returns the sort priority for an HTTP method.
|
|
467
|
+
* Unrecognized methods default to priority 999.
|
|
468
|
+
*/
|
|
469
|
+
const getMethodPriority = (method) => METHOD_PRIORITY[method] ?? 999;
|
|
470
|
+
/**
|
|
471
|
+
* Compares two path segments for route ordering.
|
|
472
|
+
* Returns negative if a should come before b, positive if after.
|
|
473
|
+
*
|
|
474
|
+
* Order: static segments before parameters, then alphabetically.
|
|
475
|
+
*/
|
|
476
|
+
const comparePathSegments = (a, b) => {
|
|
477
|
+
const aIsParam = a.startsWith(":");
|
|
478
|
+
if (aIsParam !== b.startsWith(":")) return aIsParam ? 1 : -1;
|
|
479
|
+
return a.localeCompare(b);
|
|
480
|
+
};
|
|
481
|
+
/**
|
|
482
|
+
* Compares two routes for ordering.
|
|
483
|
+
* Routes are sorted by:
|
|
484
|
+
* 1. Path depth (shallow to deep)
|
|
485
|
+
* 2. Static segments before parameters
|
|
486
|
+
* 3. Alphabetical within same segment type
|
|
487
|
+
* 4. HTTP method priority
|
|
488
|
+
*/
|
|
489
|
+
const compareRoutes = (a, b) => {
|
|
490
|
+
const aSegments = a.path.split("/").filter(Boolean);
|
|
491
|
+
const bSegments = b.path.split("/").filter(Boolean);
|
|
492
|
+
if (aSegments.length !== bSegments.length) return aSegments.length - bSegments.length;
|
|
493
|
+
for (let i = 0; i < aSegments.length; i++) {
|
|
494
|
+
const cmp = comparePathSegments(aSegments[i], bSegments[i]);
|
|
495
|
+
if (cmp !== 0) return cmp;
|
|
254
496
|
}
|
|
497
|
+
return getMethodPriority(a.method) - getMethodPriority(b.method);
|
|
255
498
|
};
|
|
256
|
-
|
|
257
499
|
//#endregion
|
|
258
500
|
exports.BasePlugin = BasePlugin;
|
|
259
|
-
exports.
|
|
260
|
-
exports.
|
|
261
|
-
exports.
|
|
501
|
+
exports.DerivedResponseCycleError = DerivedResponseCycleError;
|
|
502
|
+
exports.DuplicateOperationIdError = DuplicateOperationIdError;
|
|
503
|
+
exports.DuplicateRouteError = DuplicateRouteError;
|
|
504
|
+
exports.EmptyOperationResponsesError = EmptyOperationResponsesError;
|
|
505
|
+
exports.EmptyResourceOperationsError = EmptyResourceOperationsError;
|
|
506
|
+
exports.EmptySpecResourcesError = EmptySpecResourcesError;
|
|
507
|
+
exports.InvalidDerivedResponseError = InvalidDerivedResponseError;
|
|
508
|
+
exports.InvalidRequestSchemaError = InvalidRequestSchemaError;
|
|
509
|
+
exports.MissingDerivedResponseParentError = MissingDerivedResponseParentError;
|
|
510
|
+
exports.PathParameterMismatchError = PathParameterMismatchError;
|
|
262
511
|
exports.PluginDependencyError = PluginDependencyError;
|
|
263
512
|
exports.PluginLoadError = PluginLoadError;
|
|
264
|
-
exports.
|
|
513
|
+
exports.compareRoutes = compareRoutes;
|
|
514
|
+
exports.createPluginContextBuilder = createPluginContextBuilder;
|
|
515
|
+
exports.createPluginRegistry = createPluginRegistry;
|
|
516
|
+
exports.getMethodPriority = getMethodPriority;
|
|
517
|
+
exports.getPathParameterNames = getPathParameterNames;
|
|
518
|
+
exports.normalizeRoutePath = normalizeRoutePath;
|
|
519
|
+
exports.normalizeSpec = normalizeSpec;
|
|
520
|
+
exports.relative = relative;
|