@rexeus/typeweaver-gen 0.8.0 → 0.9.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.cjs CHANGED
@@ -1,4 +1,4 @@
1
- Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
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
- for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
12
- key = keys[i];
13
- if (!__hasOwnProp.call(to, key) && key !== except) {
14
- __defProp(to, key, {
15
- get: ((k) => from[k]).bind(null, key),
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(resources) {
80
- return resources;
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/BaseTemplatePlugin.ts
97
- /**
98
- * Base class for template-based generator plugins
99
- * Provides utilities for working with EJS templates
100
- */
101
- var BaseTemplatePlugin = class extends BasePlugin {
102
- /**
103
- * Render an EJS template with the given data
104
- */
105
- renderTemplate(templatePath, data) {
106
- return (0, ejs.render)(node_fs.default.readFileSync(templatePath, "utf8"), data);
107
- }
108
- /**
109
- * Write a file relative to the output directory
110
- */
111
- writeFile(context, relativePath, content) {
112
- context.writeFile(relativePath, content);
113
- }
114
- /**
115
- * Ensure a directory exists
116
- */
117
- ensureDir(context, relativePath) {
118
- const fullPath = node_path.default.join(context.outputDir, relativePath);
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/plugins/PluginRegistry.ts
131
- /**
132
- * Registry for managing typeweaver plugins
133
- */
134
- var PluginRegistry = class {
135
- plugins;
136
- constructor() {
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
- * Register a plugin
141
- */
142
- register(plugin, config) {
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/PluginContext.ts
183
- /**
184
- * Builder for plugin contexts
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
- * Create a generator context with utilities
200
- */
201
- createGeneratorContext(params) {
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
- ...this.createPluginContext(params),
204
- resources: params.resources,
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
- this.generatedFiles.add(relativePath);
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
- return (0, ejs.render)(node_fs.default.readFileSync(fullTemplatePath, "utf8"), data);
432
+ const template = node_fs.default.readFileSync(fullTemplatePath, "utf8");
433
+ return ejs.default.render(template, data);
218
434
  },
219
435
  addGeneratedFile: (relativePath) => {
220
- this.generatedFiles.add(relativePath);
436
+ generatedFiles.add(relativePath);
221
437
  },
222
438
  getGeneratedFiles: () => {
223
- return Array.from(this.generatedFiles);
439
+ return Array.from(generatedFiles);
224
440
  }
225
441
  };
226
- }
227
- /**
228
- * Get all generated files
229
- */
230
- getGeneratedFiles() {
231
- return Array.from(this.generatedFiles);
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/Path.ts
243
- var Path = class {
244
- static relative(from, to) {
245
- const relativePath = node_path.default.relative(from, to);
246
- if (relativePath.includes("node_modules")) {
247
- const parts = relativePath.split(node_path.default.sep);
248
- const index = parts.indexOf("node_modules");
249
- return parts.slice(index + 1).join("/");
250
- }
251
- const posixPath = relativePath.split(node_path.default.sep).join("/");
252
- if (!posixPath.startsWith("./") && !posixPath.startsWith("../")) return `./${posixPath}`;
253
- return posixPath;
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.BaseTemplatePlugin = BaseTemplatePlugin;
260
- exports.Path = Path;
261
- exports.PluginContextBuilder = PluginContextBuilder;
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.PluginRegistry = PluginRegistry;
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;