@valkyrjaio/sindri 26.5.1 → 26.6.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.
Files changed (92) hide show
  1. package/LICENSE.md +1 -1
  2. package/dist/bin/sindri.cjs +574 -268
  3. package/package.json +2 -2
  4. package/src/Sindri/Ast/Abstract/AstReader.ts +187 -5
  5. package/src/Sindri/Ast/Abstract/RouteAttributeReader.ts +2 -3
  6. package/src/Sindri/Ast/CliRouteAttributeReader.ts +102 -17
  7. package/src/Sindri/Ast/CliRouteParameterReader.ts +44 -26
  8. package/src/Sindri/Ast/ComponentProviderReader.ts +2 -3
  9. package/src/Sindri/Ast/ConfigReader.ts +2 -3
  10. package/src/Sindri/Ast/ConfigSourceReader.ts +2 -3
  11. package/src/Sindri/Ast/Contract/CliRouteAttributeReaderContract.ts +2 -3
  12. package/src/Sindri/Ast/Contract/CliRouteParameterReaderContract.ts +2 -3
  13. package/src/Sindri/Ast/Contract/ComponentProviderReaderContract.ts +2 -3
  14. package/src/Sindri/Ast/Contract/ConfigReaderContract.ts +2 -3
  15. package/src/Sindri/Ast/Contract/ConfigSourceReaderContract.ts +2 -3
  16. package/src/Sindri/Ast/Contract/HttpRouteAttributeReaderContract.ts +2 -3
  17. package/src/Sindri/Ast/Contract/HttpRouteMiddlewareReaderContract.ts +5 -10
  18. package/src/Sindri/Ast/Contract/HttpRouteParameterReaderContract.ts +3 -7
  19. package/src/Sindri/Ast/Contract/ListenerAttributeReaderContract.ts +2 -3
  20. package/src/Sindri/Ast/Contract/ListenerProviderReaderContract.ts +2 -3
  21. package/src/Sindri/Ast/Contract/RouteProviderReaderContract.ts +2 -3
  22. package/src/Sindri/Ast/Contract/ServiceProviderReaderContract.ts +2 -3
  23. package/src/Sindri/Ast/Data/CliArgumentParameterData.ts +2 -3
  24. package/src/Sindri/Ast/Data/CliOptionParameterData.ts +2 -3
  25. package/src/Sindri/Ast/Data/CliRouteData.ts +2 -3
  26. package/src/Sindri/Ast/Data/ConfigData.ts +2 -3
  27. package/src/Sindri/Ast/Data/ConfigImport.ts +2 -3
  28. package/src/Sindri/Ast/Data/Contract/CliArgumentParameterDataContract.ts +2 -3
  29. package/src/Sindri/Ast/Data/Contract/CliOptionParameterDataContract.ts +2 -3
  30. package/src/Sindri/Ast/Data/Contract/CliRouteDataContract.ts +2 -3
  31. package/src/Sindri/Ast/Data/Contract/ConfigDataContract.ts +2 -3
  32. package/src/Sindri/Ast/Data/Contract/HandlerDataContract.ts +2 -3
  33. package/src/Sindri/Ast/Data/Contract/HttpParameterDataContract.ts +4 -3
  34. package/src/Sindri/Ast/Data/Contract/HttpRouteDataContract.ts +2 -3
  35. package/src/Sindri/Ast/Data/Contract/ListenerDataContract.ts +2 -3
  36. package/src/Sindri/Ast/Data/HandlerData.ts +2 -3
  37. package/src/Sindri/Ast/Data/HttpParameterData.ts +3 -3
  38. package/src/Sindri/Ast/Data/HttpRouteData.ts +2 -3
  39. package/src/Sindri/Ast/Data/ListenerData.ts +2 -3
  40. package/src/Sindri/Ast/Data/Result/CliRouteAttributeResult.ts +7 -4
  41. package/src/Sindri/Ast/Data/Result/ComponentProviderResult.ts +2 -3
  42. package/src/Sindri/Ast/Data/Result/ConfigResult.ts +2 -3
  43. package/src/Sindri/Ast/Data/Result/ConfigSourceResult.ts +2 -3
  44. package/src/Sindri/Ast/Data/Result/HttpRouteAttributeResult.ts +4 -3
  45. package/src/Sindri/Ast/Data/Result/ListenerAttributeResult.ts +2 -3
  46. package/src/Sindri/Ast/Data/Result/ListenerProviderResult.ts +2 -3
  47. package/src/Sindri/Ast/Data/Result/RouteProviderResult.ts +2 -3
  48. package/src/Sindri/Ast/Data/Result/ServiceProviderResult.ts +2 -3
  49. package/src/Sindri/Ast/HttpRouteAttributeReader.ts +180 -51
  50. package/src/Sindri/Ast/HttpRouteMiddlewareReader.ts +38 -18
  51. package/src/Sindri/Ast/HttpRouteParameterReader.ts +50 -109
  52. package/src/Sindri/Ast/ListenerAttributeReader.ts +2 -3
  53. package/src/Sindri/Ast/ListenerProviderReader.ts +2 -3
  54. package/src/Sindri/Ast/RouteProviderReader.ts +2 -3
  55. package/src/Sindri/Ast/ServiceProviderReader.ts +2 -3
  56. package/src/Sindri/Ast/Throwable/Contract/AstThrowable.ts +2 -3
  57. package/src/Sindri/Ast/Throwable/Exception/Abstract/AstInvalidArgumentException.ts +2 -3
  58. package/src/Sindri/Ast/Throwable/Exception/Abstract/AstRuntimeException.ts +2 -3
  59. package/src/Sindri/Ast/Throwable/Exception/AstFileReadException.ts +2 -3
  60. package/src/Sindri/Cli/Command/GenerateCachedConfigCommand.ts +2 -3
  61. package/src/Sindri/Cli/Command/GenerateDataFromConfigCommand.ts +2 -3
  62. package/src/Sindri/Constant/CommandName.ts +2 -3
  63. package/src/Sindri/Constant/SindriInfo.ts +4 -5
  64. package/src/Sindri/Constant/SindriServiceId.ts +2 -3
  65. package/src/Sindri/Generate/Abstract/GenerateCachedConfigFromAst.ts +2 -3
  66. package/src/Sindri/Generate/Abstract/GenerateDataFromAst.ts +39 -27
  67. package/src/Sindri/Generate/Abstract/GenerateFromAst.ts +2 -3
  68. package/src/Sindri/Generator/Abstract/AstFileGenerator.ts +2 -3
  69. package/src/Sindri/Generator/Abstract/FileGenerator.ts +2 -3
  70. package/src/Sindri/Generator/Ast/Cli/AstCliDataFileGenerator.ts +21 -6
  71. package/src/Sindri/Generator/Ast/Config/AstCachedConfigFileGenerator.ts +2 -3
  72. package/src/Sindri/Generator/Ast/Container/AstContainerDataFileGenerator.ts +2 -3
  73. package/src/Sindri/Generator/Ast/Event/AstEventDataFileGenerator.ts +2 -3
  74. package/src/Sindri/Generator/Ast/Http/AstHttpDataFileGenerator.ts +83 -45
  75. package/src/Sindri/Generator/Cli/Contract/CliDataFileGeneratorContract.ts +10 -3
  76. package/src/Sindri/Generator/Config/Contract/CachedConfigFileGeneratorContract.ts +2 -3
  77. package/src/Sindri/Generator/Container/Contract/ContainerDataFileGeneratorContract.ts +2 -3
  78. package/src/Sindri/Generator/Contract/FileGeneratorContract.ts +2 -3
  79. package/src/Sindri/Generator/Enum/GenerateStatus.ts +2 -3
  80. package/src/Sindri/Generator/Event/Contract/EventDataFileGeneratorContract.ts +2 -3
  81. package/src/Sindri/Generator/Http/Contract/HttpDataFileGeneratorContract.ts +11 -3
  82. package/src/Sindri/Generator/Throwable/Contract/GeneratorThrowable.ts +2 -3
  83. package/src/Sindri/Generator/Throwable/Exception/Abstract/GeneratorInvalidArgumentException.ts +2 -3
  84. package/src/Sindri/Generator/Throwable/Exception/Abstract/GeneratorRuntimeException.ts +2 -3
  85. package/src/Sindri/Generator/Throwable/Exception/GeneratorUnreachableException.ts +2 -3
  86. package/src/Sindri/Provider/SindriAstServiceProvider.ts +2 -3
  87. package/src/Sindri/Provider/SindriCliRouteProvider.ts +2 -3
  88. package/src/Sindri/Provider/SindriCommandServiceProvider.ts +2 -3
  89. package/src/Sindri/Provider/SindriComponentProvider.ts +2 -3
  90. package/src/Sindri/Throwable/Contract/SindriThrowable.ts +2 -3
  91. package/src/Sindri/Throwable/Exception/Abstract/SindriInvalidArgumentException.ts +2 -3
  92. package/src/Sindri/Throwable/Exception/Abstract/SindriRuntimeException.ts +2 -3
@@ -33,7 +33,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
33
33
  // node_modules/@ts-morph/common/dist/typescript.js
34
34
  var require_typescript = __commonJS({
35
35
  "node_modules/@ts-morph/common/dist/typescript.js"(exports2, module2) {
36
- var ts13 = {};
36
+ var ts12 = {};
37
37
  ((module3) => {
38
38
  "use strict";
39
39
  var __defProp2 = Object.defineProperty;
@@ -211530,9 +211530,9 @@ Additional information: BADCLIENT: Bad error code, ${badCode} not found in range
211530
211530
  };
211531
211531
  }
211532
211532
  })({ get exports() {
211533
- return ts13;
211533
+ return ts12;
211534
211534
  }, set exports(v) {
211535
- ts13 = v;
211535
+ ts12 = v;
211536
211536
  if (typeof module2 !== "undefined" && module2.exports) {
211537
211537
  module2.exports = v;
211538
211538
  }
@@ -216503,7 +216503,7 @@ var require_dist2 = __commonJS({
216503
216503
  var require_ts_morph_common = __commonJS({
216504
216504
  "node_modules/@ts-morph/common/dist/ts-morph-common.js"(exports2) {
216505
216505
  "use strict";
216506
- var ts13 = require_typescript();
216506
+ var ts12 = require_typescript();
216507
216507
  var minimatch = require_commonjs3();
216508
216508
  var path$1 = require_path_browserify();
216509
216509
  var fs$1 = require("node:fs");
@@ -216533,7 +216533,7 @@ var require_ts_morph_common = __commonJS({
216533
216533
  n.default = e;
216534
216534
  return Object.freeze(n);
216535
216535
  }
216536
- var ts__namespace = /* @__PURE__ */ _interopNamespaceCompat(ts13);
216536
+ var ts__namespace = /* @__PURE__ */ _interopNamespaceCompat(ts12);
216537
216537
  var minimatch__namespace = /* @__PURE__ */ _interopNamespaceCompat(minimatch);
216538
216538
  var path__default = /* @__PURE__ */ _interopDefaultCompat(path$1);
216539
216539
  var fs__namespace = /* @__PURE__ */ _interopNamespaceCompat(fs$1);
@@ -219643,85 +219643,85 @@ ${nodeLocation}` : message;
219643
219643
  Object.defineProperty(exports2, "DiagnosticCategory", {
219644
219644
  enumerable: true,
219645
219645
  get: function() {
219646
- return ts13.DiagnosticCategory;
219646
+ return ts12.DiagnosticCategory;
219647
219647
  }
219648
219648
  });
219649
219649
  Object.defineProperty(exports2, "EmitHint", {
219650
219650
  enumerable: true,
219651
219651
  get: function() {
219652
- return ts13.EmitHint;
219652
+ return ts12.EmitHint;
219653
219653
  }
219654
219654
  });
219655
219655
  Object.defineProperty(exports2, "LanguageVariant", {
219656
219656
  enumerable: true,
219657
219657
  get: function() {
219658
- return ts13.LanguageVariant;
219658
+ return ts12.LanguageVariant;
219659
219659
  }
219660
219660
  });
219661
219661
  Object.defineProperty(exports2, "ModuleKind", {
219662
219662
  enumerable: true,
219663
219663
  get: function() {
219664
- return ts13.ModuleKind;
219664
+ return ts12.ModuleKind;
219665
219665
  }
219666
219666
  });
219667
219667
  Object.defineProperty(exports2, "ModuleResolutionKind", {
219668
219668
  enumerable: true,
219669
219669
  get: function() {
219670
- return ts13.ModuleResolutionKind;
219670
+ return ts12.ModuleResolutionKind;
219671
219671
  }
219672
219672
  });
219673
219673
  Object.defineProperty(exports2, "NewLineKind", {
219674
219674
  enumerable: true,
219675
219675
  get: function() {
219676
- return ts13.NewLineKind;
219676
+ return ts12.NewLineKind;
219677
219677
  }
219678
219678
  });
219679
219679
  Object.defineProperty(exports2, "NodeFlags", {
219680
219680
  enumerable: true,
219681
219681
  get: function() {
219682
- return ts13.NodeFlags;
219682
+ return ts12.NodeFlags;
219683
219683
  }
219684
219684
  });
219685
219685
  Object.defineProperty(exports2, "ObjectFlags", {
219686
219686
  enumerable: true,
219687
219687
  get: function() {
219688
- return ts13.ObjectFlags;
219688
+ return ts12.ObjectFlags;
219689
219689
  }
219690
219690
  });
219691
219691
  Object.defineProperty(exports2, "ScriptKind", {
219692
219692
  enumerable: true,
219693
219693
  get: function() {
219694
- return ts13.ScriptKind;
219694
+ return ts12.ScriptKind;
219695
219695
  }
219696
219696
  });
219697
219697
  Object.defineProperty(exports2, "ScriptTarget", {
219698
219698
  enumerable: true,
219699
219699
  get: function() {
219700
- return ts13.ScriptTarget;
219700
+ return ts12.ScriptTarget;
219701
219701
  }
219702
219702
  });
219703
219703
  Object.defineProperty(exports2, "SymbolFlags", {
219704
219704
  enumerable: true,
219705
219705
  get: function() {
219706
- return ts13.SymbolFlags;
219706
+ return ts12.SymbolFlags;
219707
219707
  }
219708
219708
  });
219709
219709
  Object.defineProperty(exports2, "SyntaxKind", {
219710
219710
  enumerable: true,
219711
219711
  get: function() {
219712
- return ts13.SyntaxKind;
219712
+ return ts12.SyntaxKind;
219713
219713
  }
219714
219714
  });
219715
219715
  Object.defineProperty(exports2, "TypeFlags", {
219716
219716
  enumerable: true,
219717
219717
  get: function() {
219718
- return ts13.TypeFlags;
219718
+ return ts12.TypeFlags;
219719
219719
  }
219720
219720
  });
219721
219721
  Object.defineProperty(exports2, "TypeFormatFlags", {
219722
219722
  enumerable: true,
219723
219723
  get: function() {
219724
- return ts13.TypeFormatFlags;
219724
+ return ts12.TypeFormatFlags;
219725
219725
  }
219726
219726
  });
219727
219727
  exports2.ts = ts__namespace;
@@ -243006,6 +243006,96 @@ var AstReader = class _AstReader {
243006
243006
  getDecoratorArg(decorator, position) {
243007
243007
  return decorator.getArguments()[position];
243008
243008
  }
243009
+ // -------------------------------------------------------------------------
243010
+ // Object-literal decorator helpers — the shipped decorators accept a single
243011
+ // options object (`@Route({ path, name, ... })`) rather than positional args.
243012
+ // -------------------------------------------------------------------------
243013
+ /**
243014
+ * Return the decorator's first argument when it is an object literal, else undefined.
243015
+ */
243016
+ getDecoratorObjectArg(decorator) {
243017
+ const arg = this.getDecoratorArg(decorator, 0);
243018
+ if (arg === void 0) {
243019
+ return void 0;
243020
+ }
243021
+ const tsNode = arg.compilerNode;
243022
+ return import_ts_morph.ts.isObjectLiteralExpression(tsNode) ? tsNode : void 0;
243023
+ }
243024
+ /**
243025
+ * Return the value expression of the named property on an object literal, or
243026
+ * undefined when the property is absent (or is not a plain property assignment).
243027
+ */
243028
+ getObjectProp(obj, key) {
243029
+ for (const prop of obj.properties) {
243030
+ if (import_ts_morph.ts.isPropertyAssignment(prop) && this.propertyNameToString(prop.name) === key) {
243031
+ return prop.initializer;
243032
+ }
243033
+ }
243034
+ return void 0;
243035
+ }
243036
+ /**
243037
+ * Resolve a property name node (identifier or string literal) to its text.
243038
+ */
243039
+ propertyNameToString(name) {
243040
+ if (import_ts_morph.ts.isIdentifier(name) || import_ts_morph.ts.isStringLiteral(name)) {
243041
+ return name.text;
243042
+ }
243043
+ return void 0;
243044
+ }
243045
+ /**
243046
+ * Extract a string-valued object property, falling back to defaultValue.
243047
+ */
243048
+ getObjectStringProp(obj, key, useMap, currentFilePath, currentClass, defaultValue = "") {
243049
+ const value = this.extractExprValue(this.getObjectProp(obj, key), useMap, currentFilePath, currentClass);
243050
+ return typeof value === "string" ? value : defaultValue;
243051
+ }
243052
+ /**
243053
+ * Extract a boolean-valued object property, falling back to defaultValue.
243054
+ */
243055
+ getObjectBoolProp(obj, key, useMap, currentFilePath, currentClass, defaultValue = false) {
243056
+ const value = this.extractExprValue(this.getObjectProp(obj, key), useMap, currentFilePath, currentClass);
243057
+ return typeof value === "boolean" ? value : defaultValue;
243058
+ }
243059
+ /**
243060
+ * Extract a class-list object property (an array literal of class references),
243061
+ * returning an empty array when the property is absent or not an array.
243062
+ */
243063
+ getObjectClassListProp(obj, key, useMap, currentFilePath, currentClass) {
243064
+ const node = this.getObjectProp(obj, key);
243065
+ if (node === void 0 || !import_ts_morph.ts.isArrayLiteralExpression(node)) {
243066
+ return [];
243067
+ }
243068
+ return this.extractClassListFromArrayExpr(node, useMap, currentFilePath, currentClass);
243069
+ }
243070
+ /**
243071
+ * Extract a string-list object property (an array literal of string values),
243072
+ * returning an empty array when the property is absent or not an array.
243073
+ */
243074
+ getObjectStringListProp(obj, key, useMap, currentFilePath, currentClass) {
243075
+ const node = this.getObjectProp(obj, key);
243076
+ if (node === void 0 || !import_ts_morph.ts.isArrayLiteralExpression(node)) {
243077
+ return [];
243078
+ }
243079
+ const values = [];
243080
+ for (const element of node.elements) {
243081
+ const value = this.extractExprValue(element, useMap, currentFilePath, currentClass);
243082
+ if (typeof value === "string") {
243083
+ values.push(value);
243084
+ }
243085
+ }
243086
+ return values;
243087
+ }
243088
+ /**
243089
+ * Extract a handler-reference object property (`[Class, 'method']`), returning
243090
+ * undefined when the property is absent or not a two-element handler array.
243091
+ */
243092
+ getObjectHandlerProp(obj, key, useMap, currentFilePath, currentClass) {
243093
+ const node = this.getObjectProp(obj, key);
243094
+ if (node === void 0 || !import_ts_morph.ts.isArrayLiteralExpression(node)) {
243095
+ return void 0;
243096
+ }
243097
+ return this.extractHandlerFromTsArray(node, useMap, currentFilePath, currentClass);
243098
+ }
243009
243099
  /**
243010
243100
  * Convert a simple expression node to a scalar value or HandlerData.
243011
243101
  */
@@ -243062,7 +243152,12 @@ var AstReader = class _AstReader {
243062
243152
  if (array.elements.length !== 2) {
243063
243153
  return void 0;
243064
243154
  }
243065
- const classValue = this.extractExprValue(array.elements[0], useMap, currentFilePath, currentClass);
243155
+ const classValue = this.extractExprValue(
243156
+ this.unwrapClassThunk(array.elements[0]),
243157
+ useMap,
243158
+ currentFilePath,
243159
+ currentClass
243160
+ );
243066
243161
  const methodValue = this.extractExprValue(array.elements[1], useMap, currentFilePath, currentClass);
243067
243162
  if (typeof classValue !== "string" || classValue === "") {
243068
243163
  return void 0;
@@ -243072,10 +243167,35 @@ var AstReader = class _AstReader {
243072
243167
  }
243073
243168
  return new HandlerData(classValue, methodValue);
243074
243169
  }
243170
+ /**
243171
+ * Unwrap a class **thunk** to the class expression it returns —
243172
+ * `() => HomeController` becomes `HomeController`.
243173
+ *
243174
+ * Every class reference in a decorator argument is authored as a thunk —
243175
+ * `[() => SomeClass, 'method']` for a handler, `@Middleware(() => SomeClass)`
243176
+ * for middleware — rather than as a bare class name. A TC39 Stage-3
243177
+ * decorator runs while the class binding is still in its temporal dead zone,
243178
+ * so naming the class directly throws at class-definition time on a circular
243179
+ * or self-referential import. The thunk is purely a runtime deferral: the
243180
+ * emitted cache is unchanged (`HttpRouteProvider.versionHandler`), so the
243181
+ * reader simply looks through it. A bare class expression is returned
243182
+ * untouched, and a block-bodied arrow (`() => { … }`) is left alone so it
243183
+ * falls through to the usual "not a class reference" handling.
243184
+ */
243185
+ unwrapClassThunk(node) {
243186
+ if (node === void 0) {
243187
+ return node;
243188
+ }
243189
+ const tsNode = import_ts_morph.Node.isNode(node) ? node.compilerNode : node;
243190
+ if (!import_ts_morph.ts.isArrowFunction(tsNode) || tsNode.parameters.length !== 0) {
243191
+ return node;
243192
+ }
243193
+ return import_ts_morph.ts.isBlock(tsNode.body) ? node : tsNode.body;
243194
+ }
243075
243195
  extractClassListFromArrayExpr(array, useMap, currentFilePath, currentClass = "") {
243076
243196
  const classes = [];
243077
243197
  for (const element of array.elements) {
243078
- const value = this.extractExprValue(element, useMap, currentFilePath, currentClass);
243198
+ const value = this.extractExprValue(this.unwrapClassThunk(element), useMap, currentFilePath, currentClass);
243079
243199
  if (typeof value === "string" && value !== "") {
243080
243200
  classes.push(value);
243081
243201
  }
@@ -243705,8 +243825,8 @@ var WarningFormatter = class extends Formatter {
243705
243825
 
243706
243826
  // node_modules/@valkyrjaio/valkyrja/src/Valkyrja/Application/Constant/ApplicationInfo.ts
243707
243827
  var ApplicationInfo = class {
243708
- static VERSION = "26.5.1";
243709
- static VERSION_BUILD_DATE_TIME = "August 1 2026 07:55:41 MST";
243828
+ static VERSION = "26.6.1";
243829
+ static VERSION_BUILD_DATE_TIME = "August 2 2026 08:04:47 MST";
243710
243830
  static ASCII = `
243711
243831
  _ _ _
243712
243832
  /\\ /\\__ _| | | ___ _ _ __(_) __ _
@@ -243815,8 +243935,8 @@ var Header = class extends Message {
243815
243935
 
243816
243936
  // src/Sindri/Constant/SindriInfo.ts
243817
243937
  var SindriInfo = class {
243818
- static VERSION = "26.5.1";
243819
- static VERSION_BUILD_DATE_TIME = "August 1 2026 14:12:56 MST";
243938
+ static VERSION = "26.6.1";
243939
+ static VERSION_BUILD_DATE_TIME = "August 2 2026 08:21:26 MST";
243820
243940
  static ICON = ` \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588
243821
243941
  \u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588
243822
243942
  \u2588\u2588\u2588\u2588
@@ -244292,10 +244412,12 @@ var CliRouteData = class {
244292
244412
 
244293
244413
  // src/Sindri/Ast/Data/Result/CliRouteAttributeResult.ts
244294
244414
  var CliRouteAttributeResult = class {
244295
- constructor(routes = {}) {
244415
+ constructor(routes = {}, importMap = {}) {
244296
244416
  this.routes = routes;
244417
+ this.importMap = importMap;
244297
244418
  }
244298
244419
  routes;
244420
+ importMap;
244299
244421
  };
244300
244422
 
244301
244423
  // src/Sindri/Ast/CliRouteParameterReader.ts
@@ -244345,7 +244467,7 @@ var CliOptionParameterData = class {
244345
244467
  var CliRouteParameterReader = class extends AstReader {
244346
244468
  buildParameterArgs(data) {
244347
244469
  const args = [];
244348
- if (data.arguments.length > 0) {
244470
+ if (data.arguments.length > 0 || data.options.length > 0) {
244349
244471
  args.push(this.buildArgumentListExpr(data.arguments));
244350
244472
  }
244351
244473
  if (data.options.length > 0) {
@@ -244374,26 +244496,30 @@ var CliRouteParameterReader = class extends AstReader {
244374
244496
  return options;
244375
244497
  }
244376
244498
  buildArgumentData(decorator, useMap, currentFilePath, currentClass) {
244377
- const name = this.extractStringArg(decorator, 0, useMap, currentFilePath, currentClass);
244378
- const description = this.extractStringArg(decorator, 1, useMap, currentFilePath, currentClass);
244499
+ const obj = this.getDecoratorObjectArg(decorator);
244500
+ if (obj === void 0) {
244501
+ return null;
244502
+ }
244503
+ const name = this.getObjectStringProp(obj, "name", useMap, currentFilePath, currentClass);
244504
+ const description = this.getObjectStringProp(obj, "description", useMap, currentFilePath, currentClass);
244379
244505
  if (name === "" || description === "") {
244380
244506
  return null;
244381
244507
  }
244382
244508
  return new CliArgumentParameterData(
244383
244509
  name,
244384
244510
  description,
244385
- this.extractStringArg(decorator, 2, useMap, currentFilePath, currentClass) || null,
244386
- this.extractStringArg(
244387
- decorator,
244388
- 3,
244511
+ this.getObjectStringProp(obj, "cast", useMap, currentFilePath, currentClass) || null,
244512
+ this.getObjectStringProp(
244513
+ obj,
244514
+ "mode",
244389
244515
  useMap,
244390
244516
  currentFilePath,
244391
244517
  currentClass,
244392
244518
  "Valkyrja\\Cli\\Routing\\Enum\\ArgumentMode::OPTIONAL"
244393
244519
  ),
244394
- this.extractStringArg(
244395
- decorator,
244396
- 4,
244520
+ this.getObjectStringProp(
244521
+ obj,
244522
+ "valueMode",
244397
244523
  useMap,
244398
244524
  currentFilePath,
244399
244525
  currentClass,
@@ -244402,30 +244528,34 @@ var CliRouteParameterReader = class extends AstReader {
244402
244528
  );
244403
244529
  }
244404
244530
  buildOptionData(decorator, useMap, currentFilePath, currentClass) {
244405
- const name = this.extractStringArg(decorator, 0, useMap, currentFilePath, currentClass);
244406
- const description = this.extractStringArg(decorator, 1, useMap, currentFilePath, currentClass);
244531
+ const obj = this.getDecoratorObjectArg(decorator);
244532
+ if (obj === void 0) {
244533
+ return null;
244534
+ }
244535
+ const name = this.getObjectStringProp(obj, "name", useMap, currentFilePath, currentClass);
244536
+ const description = this.getObjectStringProp(obj, "description", useMap, currentFilePath, currentClass);
244407
244537
  if (name === "" || description === "") {
244408
244538
  return null;
244409
244539
  }
244410
244540
  return new CliOptionParameterData(
244411
244541
  name,
244412
244542
  description,
244413
- this.extractStringArg(decorator, 2, useMap, currentFilePath, currentClass),
244414
- this.extractStringArg(decorator, 3, useMap, currentFilePath, currentClass) || null,
244415
- this.extractStringArg(decorator, 4, useMap, currentFilePath, currentClass),
244416
- this.extractStringListArgFromDecorator(decorator, 5, useMap, currentFilePath, currentClass),
244417
- this.extractStringListArgFromDecorator(decorator, 6, useMap, currentFilePath, currentClass),
244418
- this.extractStringArg(
244419
- decorator,
244420
- 7,
244543
+ this.getObjectStringProp(obj, "valueDisplayName", useMap, currentFilePath, currentClass),
244544
+ this.getObjectStringProp(obj, "cast", useMap, currentFilePath, currentClass) || null,
244545
+ this.getObjectStringProp(obj, "defaultValue", useMap, currentFilePath, currentClass),
244546
+ this.getObjectStringListProp(obj, "shortNames", useMap, currentFilePath, currentClass),
244547
+ this.getObjectStringListProp(obj, "validValues", useMap, currentFilePath, currentClass),
244548
+ this.getObjectStringProp(
244549
+ obj,
244550
+ "mode",
244421
244551
  useMap,
244422
244552
  currentFilePath,
244423
244553
  currentClass,
244424
244554
  "Valkyrja\\Cli\\Routing\\Enum\\OptionMode::OPTIONAL"
244425
244555
  ),
244426
- this.extractStringArg(
244427
- decorator,
244428
- 8,
244556
+ this.getObjectStringProp(
244557
+ obj,
244558
+ "valueMode",
244429
244559
  useMap,
244430
244560
  currentFilePath,
244431
244561
  currentClass,
@@ -244469,6 +244599,7 @@ var CliRouteParameterReader = class extends AstReader {
244469
244599
  } else {
244470
244600
  args.push(import_ts_morph5.ts.factory.createArrayLiteralExpression([]));
244471
244601
  }
244602
+ args.push(import_ts_morph5.ts.factory.createArrayLiteralExpression([]));
244472
244603
  args.push(this.buildEnumCaseExpr(data.mode));
244473
244604
  args.push(this.buildEnumCaseExpr(data.valueMode));
244474
244605
  return this.buildNewExpr("Valkyrja\\Cli\\Routing\\Data\\OptionParameter", args);
@@ -244489,22 +244620,29 @@ var CliRouteAttributeReader = class extends RouteAttributeReader {
244489
244620
  }
244490
244621
  const { classDecl, useMap, currentClass } = context;
244491
244622
  const routes = {};
244623
+ const routeData = {};
244492
244624
  for (const method of classDecl.getMethods()) {
244493
244625
  for (const decorator of this.findDecoratorsOnNode(method, "Route", useMap, filePath)) {
244494
244626
  const data = this.buildRouteData(decorator, method, useMap, filePath, currentClass);
244495
244627
  if (data !== null) {
244496
244628
  routes[data.name] = this.buildRouteExpr(data);
244629
+ routeData[data.name] = data;
244497
244630
  }
244498
244631
  }
244499
244632
  }
244500
- return new CliRouteAttributeResult(routes);
244633
+ const importMap = this.buildImportMap(routeData, useMap, filePath, currentClass);
244634
+ return new CliRouteAttributeResult(routes, importMap);
244501
244635
  }
244502
244636
  getRouteHandlerDecoratorName() {
244503
244637
  return "RouteHandler";
244504
244638
  }
244505
244639
  buildRouteData(decorator, method, useMap, currentFilePath, currentClass) {
244506
- const name = this.extractStringArg(decorator, 0, useMap, currentFilePath, currentClass);
244507
- const description = this.extractStringArg(decorator, 1, useMap, currentFilePath, currentClass);
244640
+ const obj = this.getDecoratorObjectArg(decorator);
244641
+ if (obj === void 0) {
244642
+ return null;
244643
+ }
244644
+ const name = this.getObjectStringProp(obj, "name", useMap, currentFilePath, currentClass);
244645
+ const description = this.getObjectStringProp(obj, "description", useMap, currentFilePath, currentClass);
244508
244646
  if (name === "" || description === "") {
244509
244647
  return null;
244510
244648
  }
@@ -244514,16 +244652,13 @@ var CliRouteAttributeReader = class extends RouteAttributeReader {
244514
244652
  useMap,
244515
244653
  currentFilePath,
244516
244654
  currentClass,
244517
- this.extractClassListArgFromDecorator(decorator, 4, useMap, currentFilePath, currentClass),
244518
- this.extractClassListArgFromDecorator(decorator, 5, useMap, currentFilePath, currentClass),
244519
- this.extractClassListArgFromDecorator(decorator, 6, useMap, currentFilePath, currentClass),
244520
- this.extractClassListArgFromDecorator(decorator, 7, useMap, currentFilePath, currentClass)
244655
+ this.getObjectClassListProp(obj, "middleware", useMap, currentFilePath, currentClass)
244521
244656
  );
244522
244657
  return new CliRouteData(
244523
244658
  updatedName,
244524
244659
  description,
244525
- this.updateHandler(method, useMap, currentFilePath, currentClass),
244526
- null,
244660
+ this.resolveHandler(obj, method, useMap, currentFilePath, currentClass),
244661
+ this.getObjectHandlerProp(obj, "helpText", useMap, currentFilePath, currentClass) ?? null,
244527
244662
  routeMatchedMiddleware,
244528
244663
  routeDispatchedMiddleware,
244529
244664
  throwableCaughtMiddleware,
@@ -244532,6 +244667,13 @@ var CliRouteAttributeReader = class extends RouteAttributeReader {
244532
244667
  this.parameterReader.updateOptions(method, useMap, currentFilePath, currentClass)
244533
244668
  );
244534
244669
  }
244670
+ resolveHandler(obj, method, useMap, currentFilePath, currentClass) {
244671
+ const fromObj = this.getObjectHandlerProp(obj, "handler", useMap, currentFilePath, currentClass);
244672
+ if (fromObj !== void 0) {
244673
+ return fromObj;
244674
+ }
244675
+ return this.updateHandler(method, useMap, currentFilePath, currentClass);
244676
+ }
244535
244677
  updateName(name, method, useMap, currentFilePath, currentClass) {
244536
244678
  for (const decorator of this.findDecoratorsOnNode(method, "Name", useMap, currentFilePath)) {
244537
244679
  const override = this.extractExprValue(
@@ -244546,10 +244688,25 @@ var CliRouteAttributeReader = class extends RouteAttributeReader {
244546
244688
  }
244547
244689
  return name;
244548
244690
  }
244549
- updateMiddleware(method, useMap, currentFilePath, currentClass, routeMatchedMiddleware, routeDispatchedMiddleware, throwableCaughtMiddleware, processExitingMiddleware) {
244691
+ updateMiddleware(method, useMap, currentFilePath, currentClass, middleware) {
244692
+ let routeMatchedMiddleware = [];
244693
+ let routeDispatchedMiddleware = [];
244694
+ let throwableCaughtMiddleware = [];
244695
+ let processExitingMiddleware = [];
244696
+ for (const mwName of middleware) {
244697
+ [routeMatchedMiddleware, routeDispatchedMiddleware, throwableCaughtMiddleware, processExitingMiddleware] = this.classifyMiddleware(
244698
+ mwName,
244699
+ useMap,
244700
+ currentFilePath,
244701
+ routeMatchedMiddleware,
244702
+ routeDispatchedMiddleware,
244703
+ throwableCaughtMiddleware,
244704
+ processExitingMiddleware
244705
+ );
244706
+ }
244550
244707
  for (const decorator of this.findDecoratorsOnNode(method, "Middleware", useMap, currentFilePath)) {
244551
244708
  const mwName = this.extractExprValue(
244552
- this.getDecoratorArg(decorator, 0),
244709
+ this.unwrapClassThunk(this.getDecoratorArg(decorator, 0)),
244553
244710
  useMap,
244554
244711
  currentFilePath,
244555
244712
  currentClass
@@ -244608,6 +244765,31 @@ var CliRouteAttributeReader = class extends RouteAttributeReader {
244608
244765
  this.buildClassArrayExpr(data.processExitingMiddleware)
244609
244766
  ];
244610
244767
  }
244768
+ /**
244769
+ * Build the class-name → absolute-file-path map for every handler and
244770
+ * help-text class the generated data cache references, so the generator can
244771
+ * emit the matching import statements. (Middleware are emitted as string
244772
+ * literals and therefore need no import.)
244773
+ */
244774
+ buildImportMap(routeData, useMap, currentFilePath, currentClass) {
244775
+ const importMap = {};
244776
+ for (const data of Object.values(routeData)) {
244777
+ if (data.handler !== null) {
244778
+ this.addClassImport(importMap, data.handler.class, useMap, currentFilePath, currentClass);
244779
+ }
244780
+ if (data.helpText !== null) {
244781
+ this.addClassImport(importMap, data.helpText.class, useMap, currentFilePath, currentClass);
244782
+ }
244783
+ }
244784
+ return importMap;
244785
+ }
244786
+ addClassImport(importMap, className, useMap, currentFilePath, currentClass) {
244787
+ const shortName = className.slice(className.lastIndexOf("\\") + 1);
244788
+ const filePath = shortName === currentClass ? currentFilePath : this.resolveImportToFilePath(shortName, useMap, currentFilePath);
244789
+ if (filePath !== "") {
244790
+ importMap[shortName] = filePath;
244791
+ }
244792
+ }
244611
244793
  };
244612
244794
 
244613
244795
  // src/Sindri/Ast/Data/Result/ComponentProviderResult.ts
@@ -244694,23 +244876,20 @@ var HttpRouteData = class {
244694
244876
 
244695
244877
  // src/Sindri/Ast/Data/Result/HttpRouteAttributeResult.ts
244696
244878
  var HttpRouteAttributeResult = class {
244697
- constructor(routes = {}, routeData = {}) {
244879
+ constructor(routes = {}, routeData = {}, importMap = {}) {
244698
244880
  this.routes = routes;
244699
244881
  this.routeData = routeData;
244882
+ this.importMap = importMap;
244700
244883
  }
244701
244884
  routes;
244702
244885
  routeData;
244886
+ importMap;
244703
244887
  };
244704
244888
 
244705
244889
  // src/Sindri/Ast/HttpRouteMiddlewareReader.ts
244706
- var import_ts_morph6 = __toESM(require_ts_morph(), 1);
244707
244890
  var HttpRouteMiddlewareReader = class extends AstReader {
244708
- extractInlineRequestMethods(decoratorArgs, useMap, namespace, currentClass) {
244709
- const requestMethodsArg = decoratorArgs[3];
244710
- if (requestMethodsArg === void 0 || !import_ts_morph6.ts.isArrayLiteralExpression(requestMethodsArg)) {
244711
- return [];
244712
- }
244713
- return this.extractClassListFromArrayExpr(requestMethodsArg, useMap, namespace, currentClass);
244891
+ extractObjectRequestMethods(obj, useMap, namespace, currentClass) {
244892
+ return this.getObjectClassListProp(obj, "requestMethods", useMap, namespace, currentClass);
244714
244893
  }
244715
244894
  updateRequestMethods(requestMethods, method, useMap, namespace, currentClass) {
244716
244895
  for (const decorator of this.findDecoratorsOnNode(method, "RequestMethod", useMap, namespace)) {
@@ -244729,9 +244908,37 @@ var HttpRouteMiddlewareReader = class extends AstReader {
244729
244908
  }
244730
244909
  return requestMethods;
244731
244910
  }
244732
- updateMiddleware(method, useMap, namespace, currentClass, routeMatchedMiddleware, routeDispatchedMiddleware, throwableCaughtMiddleware, sendingResponseMiddleware, responseSentMiddleware) {
244911
+ updateMiddleware(method, useMap, namespace, currentClass, middleware) {
244912
+ let routeMatchedMiddleware = [];
244913
+ let routeDispatchedMiddleware = [];
244914
+ let throwableCaughtMiddleware = [];
244915
+ let sendingResponseMiddleware = [];
244916
+ let responseSentMiddleware = [];
244917
+ for (const mwName of middleware) {
244918
+ [
244919
+ routeMatchedMiddleware,
244920
+ routeDispatchedMiddleware,
244921
+ throwableCaughtMiddleware,
244922
+ sendingResponseMiddleware,
244923
+ responseSentMiddleware
244924
+ ] = this.classifyMiddleware(
244925
+ mwName,
244926
+ useMap,
244927
+ namespace,
244928
+ routeMatchedMiddleware,
244929
+ routeDispatchedMiddleware,
244930
+ throwableCaughtMiddleware,
244931
+ sendingResponseMiddleware,
244932
+ responseSentMiddleware
244933
+ );
244934
+ }
244733
244935
  for (const decorator of this.findDecoratorsOnNode(method, "Middleware", useMap, namespace)) {
244734
- const mwName = this.extractExprValue(this.getDecoratorArg(decorator, 0), useMap, namespace, currentClass);
244936
+ const mwName = this.extractExprValue(
244937
+ this.unwrapClassThunk(this.getDecoratorArg(decorator, 0)),
244938
+ useMap,
244939
+ namespace,
244940
+ currentClass
244941
+ );
244735
244942
  if (typeof mwName !== "string" || mwName === "") {
244736
244943
  continue;
244737
244944
  }
@@ -244828,111 +245035,71 @@ var HttpRouteMiddlewareReader = class extends AstReader {
244828
245035
  };
244829
245036
 
244830
245037
  // src/Sindri/Ast/HttpRouteParameterReader.ts
244831
- var import_ts_morph7 = __toESM(require_ts_morph(), 1);
245038
+ var import_ts_morph6 = __toESM(require_ts_morph(), 1);
244832
245039
 
244833
245040
  // src/Sindri/Ast/Data/HttpParameterData.ts
244834
245041
  var HttpParameterData = class {
244835
- constructor(name, regex, cast = null, isOptional = false, shouldCapture = true) {
245042
+ constructor(name, regex, cast = null, isOptional = false, shouldCapture = true, defaultValue = null) {
244836
245043
  this.name = name;
244837
245044
  this.regex = regex;
244838
245045
  this.cast = cast;
244839
245046
  this.isOptional = isOptional;
244840
245047
  this.shouldCapture = shouldCapture;
245048
+ this.defaultValue = defaultValue;
244841
245049
  }
244842
245050
  name;
244843
245051
  regex;
244844
245052
  cast;
244845
245053
  isOptional;
244846
245054
  shouldCapture;
245055
+ defaultValue;
244847
245056
  };
244848
245057
 
244849
245058
  // src/Sindri/Ast/HttpRouteParameterReader.ts
244850
245059
  var HttpRouteParameterReader = class extends AstReader {
244851
- updateParameters(decoratorArgs, method, useMap, namespace, currentClass) {
244852
- return [
244853
- ...this.collectInlineParameters(decoratorArgs, useMap, namespace, currentClass),
244854
- ...this.collectAttributeParameters(method, useMap, namespace, currentClass),
244855
- ...this.collectMethodParamParameters(method, useMap, namespace, currentClass)
244856
- ];
244857
- }
244858
- buildParameterListExpr(parameters) {
244859
- const elements = parameters.map((p) => this.buildParameterExpr(p));
244860
- return import_ts_morph7.ts.factory.createArrayLiteralExpression(elements);
244861
- }
244862
- collectInlineParameters(decoratorArgs, useMap, currentFilePath, currentClass) {
244863
- const inlineArg = decoratorArgs[2];
244864
- if (inlineArg === void 0 || !import_ts_morph7.ts.isArrayLiteralExpression(inlineArg)) {
245060
+ updateParameters(obj, useMap, namespace, currentClass) {
245061
+ const node = this.getObjectProp(obj, "parameters");
245062
+ if (node === void 0 || !import_ts_morph6.ts.isArrayLiteralExpression(node)) {
244865
245063
  return [];
244866
245064
  }
244867
245065
  const parameters = [];
244868
- for (const element of inlineArg.elements) {
244869
- if (!import_ts_morph7.ts.isNewExpression(element) || element.arguments === void 0) {
245066
+ for (const element of node.elements) {
245067
+ if (!import_ts_morph6.ts.isObjectLiteralExpression(element)) {
244870
245068
  continue;
244871
245069
  }
244872
- const param = this.buildParameterDataFromTsArgs(element.arguments, useMap, currentFilePath, currentClass);
244873
- if (param !== null) {
244874
- parameters.push(param);
244875
- }
244876
- }
244877
- return parameters;
244878
- }
244879
- collectAttributeParameters(method, useMap, currentFilePath, currentClass) {
244880
- const parameters = [];
244881
- for (const decorator of this.findDecoratorsOnNode(method, "Parameter", useMap, currentFilePath)) {
244882
- const param = this.buildParameterDataFromDecorator(decorator, useMap, currentFilePath, currentClass);
245070
+ const param = this.buildParameterData(element, useMap, namespace, currentClass);
244883
245071
  if (param !== null) {
244884
245072
  parameters.push(param);
244885
245073
  }
244886
245074
  }
244887
245075
  return parameters;
244888
245076
  }
244889
- collectMethodParamParameters(method, useMap, currentFilePath, currentClass) {
244890
- const parameters = [];
244891
- for (const methodParam of method.getParameters()) {
244892
- for (const decorator of this.findDecoratorsOnNode(methodParam, "Parameter", useMap, currentFilePath)) {
244893
- const param = this.buildParameterDataFromDecorator(decorator, useMap, currentFilePath, currentClass);
244894
- if (param !== null) {
244895
- parameters.push(param);
244896
- }
244897
- }
244898
- }
244899
- return parameters;
245077
+ buildParameterListExpr(parameters) {
245078
+ const elements = parameters.map((p) => this.buildParameterExpr(p));
245079
+ return import_ts_morph6.ts.factory.createArrayLiteralExpression(elements);
244900
245080
  }
244901
- buildParameterDataFromDecorator(decorator, useMap, currentFilePath, currentClass) {
244902
- const name = this.extractStringArg(decorator, 0, useMap, currentFilePath, currentClass);
244903
- const regex = this.extractStringArg(decorator, 1, useMap, currentFilePath, currentClass);
245081
+ buildParameterData(obj, useMap, currentFilePath, currentClass) {
245082
+ const name = this.getObjectStringProp(obj, "name", useMap, currentFilePath, currentClass);
245083
+ const regex = this.getObjectStringProp(obj, "regex", useMap, currentFilePath, currentClass);
244904
245084
  if (name === "" || regex === "") {
244905
245085
  return null;
244906
245086
  }
245087
+ const cast = this.getObjectStringProp(obj, "cast", useMap, currentFilePath, currentClass) || null;
244907
245088
  return new HttpParameterData(
244908
245089
  name,
244909
245090
  regex,
244910
- this.extractStringArg(decorator, 2, useMap, currentFilePath, currentClass) || null,
244911
- this.extractBoolArg(decorator, 3, useMap, currentFilePath, currentClass, false),
244912
- this.extractBoolArg(decorator, 4, useMap, currentFilePath, currentClass, true)
245091
+ cast,
245092
+ this.getObjectBoolProp(obj, "isOptional", useMap, currentFilePath, currentClass, false),
245093
+ this.getObjectBoolProp(obj, "shouldCapture", useMap, currentFilePath, currentClass, true),
245094
+ this.extractDefault(obj, useMap, currentFilePath, currentClass)
244913
245095
  );
244914
245096
  }
244915
- buildParameterDataFromTsArgs(args, useMap, currentFilePath, currentClass) {
244916
- const nameNode = args[0];
244917
- const regexNode = args[1];
244918
- if (nameNode === void 0 || regexNode === void 0) {
244919
- return null;
244920
- }
244921
- const name = this.extractExprValue(nameNode, useMap, currentFilePath, currentClass);
244922
- const regex = this.extractExprValue(regexNode, useMap, currentFilePath, currentClass);
244923
- if (typeof name !== "string" || name === "" || typeof regex !== "string" || regex === "") {
244924
- return null;
245097
+ extractDefault(obj, useMap, currentFilePath, currentClass) {
245098
+ const value = this.extractExprValue(this.getObjectProp(obj, "default"), useMap, currentFilePath, currentClass);
245099
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
245100
+ return value;
244925
245101
  }
244926
- const castNode = args[2];
244927
- const castRaw = castNode !== void 0 ? this.extractExprValue(castNode, useMap, currentFilePath, currentClass) : null;
244928
- const cast = typeof castRaw === "string" && castRaw !== "" ? castRaw : null;
244929
- const isOptionalNode = args[3];
244930
- const isOptionalRaw = isOptionalNode !== void 0 ? this.extractExprValue(isOptionalNode, useMap, currentFilePath, currentClass) : false;
244931
- const isOptional = typeof isOptionalRaw === "boolean" ? isOptionalRaw : false;
244932
- const shouldCaptureNode = args[4];
244933
- const shouldCaptureRaw = shouldCaptureNode !== void 0 ? this.extractExprValue(shouldCaptureNode, useMap, currentFilePath, currentClass) : true;
244934
- const shouldCapture = typeof shouldCaptureRaw === "boolean" ? shouldCaptureRaw : true;
244935
- return new HttpParameterData(name, regex, cast, isOptional, shouldCapture);
245102
+ return null;
244936
245103
  }
244937
245104
  buildParameterExpr(data) {
244938
245105
  const regexExpr = data.regex.includes("::") ? this.buildEnumCaseExpr(data.regex) : this.buildStringExpr(data.regex);
@@ -244943,8 +245110,20 @@ var HttpRouteParameterReader = class extends AstReader {
244943
245110
  this.buildBoolExpr(data.isOptional),
244944
245111
  this.buildBoolExpr(data.shouldCapture)
244945
245112
  ];
245113
+ if (data.defaultValue !== null) {
245114
+ args.push(this.buildScalarExpr(data.defaultValue));
245115
+ }
244946
245116
  return this.buildNewExpr("Valkyrja\\Http\\Routing\\Data\\Parameter", args);
244947
245117
  }
245118
+ buildScalarExpr(value) {
245119
+ if (typeof value === "string") {
245120
+ return this.buildStringExpr(value);
245121
+ }
245122
+ if (typeof value === "boolean") {
245123
+ return this.buildBoolExpr(value);
245124
+ }
245125
+ return import_ts_morph6.ts.factory.createNumericLiteral(value);
245126
+ }
244948
245127
  };
244949
245128
 
244950
245129
  // src/Sindri/Ast/HttpRouteAttributeReader.ts
@@ -245002,7 +245181,7 @@ var HttpRouteAttributeReader = class extends RouteAttributeReader {
245002
245181
  const routeData = {};
245003
245182
  for (const method of classDecl.getMethods()) {
245004
245183
  for (const decorator of this.findDecoratorsOnNode(method, "Route", useMap, currentFilePath)) {
245005
- const data = this.buildRouteData(
245184
+ this.collectRoute(
245006
245185
  decorator,
245007
245186
  method,
245008
245187
  useMap,
@@ -245010,15 +245189,13 @@ var HttpRouteAttributeReader = class extends RouteAttributeReader {
245010
245189
  currentClass,
245011
245190
  classPathPrefix,
245012
245191
  classNamePrefix,
245013
- false
245192
+ false,
245193
+ routes,
245194
+ routeData
245014
245195
  );
245015
- if (data !== null) {
245016
- routes[data.name] = this.buildRouteExpr(data);
245017
- routeData[data.name] = data;
245018
- }
245019
245196
  }
245020
245197
  for (const decorator of this.findDecoratorsOnNode(method, "DynamicRoute", useMap, currentFilePath)) {
245021
- const data = this.buildRouteData(
245198
+ this.collectRoute(
245022
245199
  decorator,
245023
245200
  method,
245024
245201
  useMap,
@@ -245026,28 +245203,46 @@ var HttpRouteAttributeReader = class extends RouteAttributeReader {
245026
245203
  currentClass,
245027
245204
  classPathPrefix,
245028
245205
  classNamePrefix,
245029
- true
245206
+ true,
245207
+ routes,
245208
+ routeData
245030
245209
  );
245031
- if (data !== null) {
245032
- routes[data.name] = this.buildRouteExpr(data);
245033
- routeData[data.name] = data;
245034
- }
245035
245210
  }
245036
245211
  }
245037
- return new HttpRouteAttributeResult(routes, routeData);
245212
+ const importMap = this.buildImportMap(routeData, useMap, currentFilePath, currentClass);
245213
+ return new HttpRouteAttributeResult(routes, routeData, importMap);
245214
+ }
245215
+ collectRoute(decorator, method, useMap, currentFilePath, currentClass, classPathPrefix, classNamePrefix, isDynamic, routes, routeData) {
245216
+ const data = this.buildRouteData(
245217
+ decorator,
245218
+ method,
245219
+ useMap,
245220
+ currentFilePath,
245221
+ currentClass,
245222
+ classPathPrefix,
245223
+ classNamePrefix,
245224
+ isDynamic
245225
+ );
245226
+ if (data !== null) {
245227
+ routes[data.name] = this.buildRouteExpr(data);
245228
+ routeData[data.name] = data;
245229
+ }
245038
245230
  }
245039
245231
  buildRouteData(decorator, method, useMap, currentFilePath, currentClass, classPathPrefix, classNamePrefix, isDynamic) {
245040
- const path6 = this.extractStringArg(decorator, 0, useMap, currentFilePath, currentClass);
245041
- const name = this.extractStringArg(decorator, 1, useMap, currentFilePath, currentClass);
245232
+ const obj = this.getDecoratorObjectArg(decorator);
245233
+ if (obj === void 0) {
245234
+ return null;
245235
+ }
245236
+ const path6 = this.getObjectStringProp(obj, "path", useMap, currentFilePath, currentClass);
245237
+ const name = this.getObjectStringProp(obj, "name", useMap, currentFilePath, currentClass);
245042
245238
  if (path6 === "" || name === "") {
245043
245239
  return null;
245044
245240
  }
245045
245241
  const updatedPath = this.updatePath(path6, classPathPrefix, method, useMap, currentFilePath, currentClass);
245046
245242
  const updatedName = this.updateName(name, classNamePrefix, method, useMap, currentFilePath, currentClass);
245047
245243
  const resolvedIsDynamic = isDynamic || updatedPath.includes("{");
245048
- const decoratorArgs = decorator.getArguments().map((a) => a.compilerNode);
245049
245244
  const requestMethods = this.middlewareReader.updateRequestMethods(
245050
- this.middlewareReader.extractInlineRequestMethods(decoratorArgs, useMap, currentFilePath, currentClass),
245245
+ this.middlewareReader.extractObjectRequestMethods(obj, useMap, currentFilePath, currentClass),
245051
245246
  method,
245052
245247
  useMap,
245053
245248
  currentFilePath,
@@ -245064,27 +245259,54 @@ var HttpRouteAttributeReader = class extends RouteAttributeReader {
245064
245259
  useMap,
245065
245260
  currentFilePath,
245066
245261
  currentClass,
245067
- this.extractClassListArgFromDecorator(decorator, 5, useMap, currentFilePath, currentClass),
245068
- this.extractClassListArgFromDecorator(decorator, 6, useMap, currentFilePath, currentClass),
245069
- this.extractClassListArgFromDecorator(decorator, 7, useMap, currentFilePath, currentClass),
245070
- this.extractClassListArgFromDecorator(decorator, 8, useMap, currentFilePath, currentClass),
245071
- this.extractClassListArgFromDecorator(decorator, 9, useMap, currentFilePath, currentClass)
245262
+ this.getObjectClassListProp(obj, "middleware", useMap, currentFilePath, currentClass)
245072
245263
  );
245073
245264
  return new HttpRouteData(
245074
245265
  updatedPath,
245075
245266
  updatedName,
245076
- this.updateHandler(method, useMap, currentFilePath, currentClass),
245267
+ this.resolveHandler(obj, method, useMap, currentFilePath, currentClass),
245077
245268
  requestMethods,
245078
245269
  routeMatchedMiddleware,
245079
245270
  routeDispatchedMiddleware,
245080
245271
  throwableCaughtMiddleware,
245081
245272
  sendingResponseMiddleware,
245082
245273
  responseSentMiddleware,
245083
- this.middlewareReader.updateRequestStruct(method, useMap, currentFilePath, currentClass),
245084
- this.middlewareReader.updateResponseStruct(method, useMap, currentFilePath, currentClass),
245274
+ this.resolveRequestStruct(obj, method, useMap, currentFilePath, currentClass),
245275
+ this.resolveResponseStruct(obj, method, useMap, currentFilePath, currentClass),
245085
245276
  resolvedIsDynamic,
245086
- resolvedIsDynamic ? this.updateParameters(decoratorArgs, method, useMap, currentFilePath, currentClass) : []
245277
+ resolvedIsDynamic ? this.parameterReader.updateParameters(obj, useMap, currentFilePath, currentClass) : []
245278
+ );
245279
+ }
245280
+ resolveHandler(obj, method, useMap, currentFilePath, currentClass) {
245281
+ const fromObj = this.getObjectHandlerProp(obj, "handler", useMap, currentFilePath, currentClass);
245282
+ if (fromObj !== void 0) {
245283
+ return fromObj;
245284
+ }
245285
+ return this.updateHandler(method, useMap, currentFilePath, currentClass);
245286
+ }
245287
+ resolveRequestStruct(obj, method, useMap, currentFilePath, currentClass) {
245288
+ const value = this.extractExprValue(
245289
+ this.getObjectProp(obj, "requestStruct"),
245290
+ useMap,
245291
+ currentFilePath,
245292
+ currentClass
245087
245293
  );
245294
+ if (typeof value === "string" && value !== "") {
245295
+ return value;
245296
+ }
245297
+ return this.middlewareReader.updateRequestStruct(method, useMap, currentFilePath, currentClass);
245298
+ }
245299
+ resolveResponseStruct(obj, method, useMap, currentFilePath, currentClass) {
245300
+ const value = this.extractExprValue(
245301
+ this.getObjectProp(obj, "responseStruct"),
245302
+ useMap,
245303
+ currentFilePath,
245304
+ currentClass
245305
+ );
245306
+ if (typeof value === "string" && value !== "") {
245307
+ return value;
245308
+ }
245309
+ return this.middlewareReader.updateResponseStruct(method, useMap, currentFilePath, currentClass);
245088
245310
  }
245089
245311
  updatePath(path6, classPathPrefix, method, useMap, currentFilePath, currentClass) {
245090
245312
  if (classPathPrefix !== "") {
@@ -245120,26 +245342,59 @@ var HttpRouteAttributeReader = class extends RouteAttributeReader {
245120
245342
  }
245121
245343
  return name;
245122
245344
  }
245345
+ /**
245346
+ * Emit `new DynamicRoute(path, name, regex, parameters, handler,
245347
+ * requestMethods, ...5 middleware, requestStruct, responseStruct)` or
245348
+ * `new Route(path, name, handler, requestMethods, ...5 middleware,
245349
+ * requestStruct, responseStruct)`, matching the framework constructor order.
245350
+ *
245351
+ * For dynamic routes the regex slot is emitted as an empty-string
245352
+ * placeholder; the generator computes and injects the real regex (it owns
245353
+ * the routing `Processor`), and the parameters array is always emitted (`[]`
245354
+ * when empty).
245355
+ */
245123
245356
  buildRouteExpr(data) {
245124
245357
  const args = [this.buildEnumCaseExpr(data.path), this.buildEnumCaseExpr(data.name)];
245125
- if (data.isDynamic && data.parameters.length > 0) {
245358
+ if (data.isDynamic) {
245359
+ args.push(this.buildStringExpr(""));
245126
245360
  args.push(this.parameterReader.buildParameterListExpr([...data.parameters]));
245127
245361
  }
245128
- if (data.handler !== null) {
245129
- args.push(this.buildHandlerExpr(data.handler));
245130
- } else {
245131
- args.push(this.buildNullExpr());
245132
- }
245133
- if (data.requestMethods.length > 0) {
245134
- args.push(this.buildEnumCaseArrayExpr(data.requestMethods));
245135
- }
245362
+ args.push(data.handler !== null ? this.buildHandlerExpr(data.handler) : this.buildNullExpr());
245363
+ args.push(this.buildEnumCaseArrayExpr(data.requestMethods));
245136
245364
  args.push(...this.middlewareReader.buildRouteMiddlewareArgs(data));
245137
245365
  args.push(...this.middlewareReader.buildRouteStructArgs(data));
245138
245366
  const targetClass = data.isDynamic ? "Valkyrja\\Http\\Routing\\Data\\DynamicRoute" : "Valkyrja\\Http\\Routing\\Data\\Route";
245139
245367
  return this.buildNewExpr(targetClass, args);
245140
245368
  }
245141
- updateParameters(decoratorArgs, method, useMap, currentFilePath, currentClass) {
245142
- return this.parameterReader.updateParameters(decoratorArgs, method, useMap, currentFilePath, currentClass);
245369
+ /**
245370
+ * Build the class-name → absolute-file-path map for every handler and
245371
+ * middleware class the generated data cache references, so the generator can
245372
+ * emit the matching import statements.
245373
+ */
245374
+ buildImportMap(routeData, useMap, currentFilePath, currentClass) {
245375
+ const importMap = {};
245376
+ for (const data of Object.values(routeData)) {
245377
+ if (data.handler !== null) {
245378
+ this.addClassImport(importMap, data.handler.class, useMap, currentFilePath, currentClass);
245379
+ }
245380
+ for (const middleware of [
245381
+ ...data.routeMatchedMiddleware,
245382
+ ...data.routeDispatchedMiddleware,
245383
+ ...data.throwableCaughtMiddleware,
245384
+ ...data.sendingResponseMiddleware,
245385
+ ...data.responseSentMiddleware
245386
+ ]) {
245387
+ this.addClassImport(importMap, middleware, useMap, currentFilePath, currentClass);
245388
+ }
245389
+ }
245390
+ return importMap;
245391
+ }
245392
+ addClassImport(importMap, className, useMap, currentFilePath, currentClass) {
245393
+ const shortName = className.slice(className.lastIndexOf("\\") + 1);
245394
+ const filePath = shortName === currentClass ? currentFilePath : this.resolveImportToFilePath(shortName, useMap, currentFilePath);
245395
+ if (filePath !== "") {
245396
+ importMap[shortName] = filePath;
245397
+ }
245143
245398
  }
245144
245399
  };
245145
245400
 
@@ -245272,7 +245527,7 @@ var ListenerProviderReader = class _ListenerProviderReader extends AstReader {
245272
245527
  };
245273
245528
 
245274
245529
  // src/Sindri/Ast/RouteProviderReader.ts
245275
- var import_ts_morph8 = __toESM(require_ts_morph(), 1);
245530
+ var import_ts_morph7 = __toESM(require_ts_morph(), 1);
245276
245531
 
245277
245532
  // src/Sindri/Ast/Data/Result/RouteProviderResult.ts
245278
245533
  var RouteProviderResult = class _RouteProviderResult {
@@ -245356,12 +245611,12 @@ var RouteProviderReader = class _RouteProviderReader extends AstReader {
245356
245611
  if (array === void 0) {
245357
245612
  return [];
245358
245613
  }
245359
- return array.elements.filter((element) => import_ts_morph8.ts.isNewExpression(element));
245614
+ return array.elements.filter((element) => import_ts_morph7.ts.isNewExpression(element));
245360
245615
  }
245361
245616
  };
245362
245617
 
245363
245618
  // src/Sindri/Ast/ServiceProviderReader.ts
245364
- var import_ts_morph9 = __toESM(require_ts_morph(), 1);
245619
+ var import_ts_morph8 = __toESM(require_ts_morph(), 1);
245365
245620
 
245366
245621
  // src/Sindri/Ast/Data/Result/ServiceProviderResult.ts
245367
245622
  var ServiceProviderResult = class _ServiceProviderResult {
@@ -245414,7 +245669,7 @@ var ServiceProviderReader = class _ServiceProviderReader extends AstReader {
245414
245669
  }
245415
245670
  const map = {};
245416
245671
  for (const prop of obj.properties) {
245417
- if (!import_ts_morph9.ts.isPropertyAssignment(prop)) {
245672
+ if (!import_ts_morph8.ts.isPropertyAssignment(prop)) {
245418
245673
  continue;
245419
245674
  }
245420
245675
  const serviceId = this.resolvePropertyKeyToString(prop.name, useMap, filePath);
@@ -245435,8 +245690,8 @@ var ServiceProviderReader = class _ServiceProviderReader extends AstReader {
245435
245690
  * Handles: `ProviderClass.publishMethod` and `[ProviderClass, 'method']`.
245436
245691
  */
245437
245692
  extractHandlerFromValue(node, useMap, filePath, currentClass) {
245438
- if (import_ts_morph9.ts.isPropertyAccessExpression(node)) {
245439
- const className = import_ts_morph9.ts.isIdentifier(node.expression) ? node.expression.text : void 0;
245693
+ if (import_ts_morph8.ts.isPropertyAccessExpression(node)) {
245694
+ const className = import_ts_morph8.ts.isIdentifier(node.expression) ? node.expression.text : void 0;
245440
245695
  if (className === void 0) {
245441
245696
  return void 0;
245442
245697
  }
@@ -245444,7 +245699,7 @@ var ServiceProviderReader = class _ServiceProviderReader extends AstReader {
245444
245699
  const resolvedClass = className === "self" || className === "this" ? currentClass : className;
245445
245700
  return [resolvedClass, methodName];
245446
245701
  }
245447
- if (import_ts_morph9.ts.isArrayLiteralExpression(node) && node.elements.length === 2) {
245702
+ if (import_ts_morph8.ts.isArrayLiteralExpression(node) && node.elements.length === 2) {
245448
245703
  const raw = this.extractHandlerFromTsArray(node, useMap, filePath, currentClass);
245449
245704
  if (raw !== void 0) {
245450
245705
  return [raw.class, raw.method];
@@ -245455,7 +245710,7 @@ var ServiceProviderReader = class _ServiceProviderReader extends AstReader {
245455
245710
  };
245456
245711
 
245457
245712
  // src/Sindri/Generator/Ast/Cli/AstCliDataFileGenerator.ts
245458
- var import_ts_morph10 = __toESM(require_ts_morph(), 1);
245713
+ var import_ts_morph9 = __toESM(require_ts_morph(), 1);
245459
245714
  var AstCliDataFileGenerator = class _AstCliDataFileGenerator extends AstFileGenerator {
245460
245715
  /** The framework classes every generated CLI data file imports as values. */
245461
245716
  static FRAMEWORK_IMPORTS = {
@@ -245473,13 +245728,13 @@ var AstCliDataFileGenerator = class _AstCliDataFileGenerator extends AstFileGene
245473
245728
  RouteContract: "@valkyrjaio/valkyrja/Cli/Routing/Data/Contract/RouteContract.ts"
245474
245729
  };
245475
245730
  classImportMap = {};
245476
- printer = import_ts_morph10.ts.createPrinter({ newLine: import_ts_morph10.ts.NewLineKind.LineFeed });
245477
- dummySourceFile = import_ts_morph10.ts.createSourceFile(
245731
+ printer = import_ts_morph9.ts.createPrinter({ newLine: import_ts_morph9.ts.NewLineKind.LineFeed });
245732
+ dummySourceFile = import_ts_morph9.ts.createSourceFile(
245478
245733
  "_dummy.ts",
245479
245734
  "",
245480
- import_ts_morph10.ts.ScriptTarget.ESNext,
245735
+ import_ts_morph9.ts.ScriptTarget.ESNext,
245481
245736
  false,
245482
- import_ts_morph10.ts.ScriptKind.TS
245737
+ import_ts_morph9.ts.ScriptKind.TS
245483
245738
  );
245484
245739
  generateFile(directory, className, namespace, routes) {
245485
245740
  const userImportsBlock = this.buildUserImportsBlock(this.classImportMap, [
@@ -245508,18 +245763,27 @@ var AstCliDataFileGenerator = class _AstCliDataFileGenerator extends AstFileGene
245508
245763
  * its command name (the first constructor argument) and emitted verbatim.
245509
245764
  */
245510
245765
  generateFileFromRoutes(directory, className, namespace, routeExprs) {
245511
- const routes = {};
245766
+ return this.generateMergedFile(directory, className, namespace, {}, routeExprs);
245767
+ }
245768
+ /**
245769
+ * Generate the CLI routing data file by MERGING attribute-scanned command
245770
+ * routes with imperative `getRoutes()` route objects — both are keyed by
245771
+ * command name into a single routes map. Attribute entries come first;
245772
+ * imperative entries override on a name collision.
245773
+ */
245774
+ generateMergedFile(directory, className, namespace, routes, routeExprs) {
245775
+ const merged = { ...routes };
245512
245776
  for (const expr of routeExprs) {
245513
- if (!import_ts_morph10.ts.isNewExpression(expr)) {
245777
+ if (!import_ts_morph9.ts.isNewExpression(expr)) {
245514
245778
  continue;
245515
245779
  }
245516
245780
  const key = this.routeKey((expr.arguments ?? [])[0]);
245517
245781
  if (key === void 0) {
245518
245782
  continue;
245519
245783
  }
245520
- routes[key] = expr;
245784
+ merged[key] = expr;
245521
245785
  }
245522
- return this.generateFile(directory, className, namespace, routes);
245786
+ return this.generateFile(directory, className, namespace, merged);
245523
245787
  }
245524
245788
  /**
245525
245789
  * Derive the map key for a route from its name argument.
@@ -245534,10 +245798,10 @@ var AstCliDataFileGenerator = class _AstCliDataFileGenerator extends AstFileGene
245534
245798
  if (nameArg === void 0) {
245535
245799
  return void 0;
245536
245800
  }
245537
- if (import_ts_morph10.ts.isStringLiteral(nameArg)) {
245801
+ if (import_ts_morph9.ts.isStringLiteral(nameArg)) {
245538
245802
  return nameArg.text;
245539
245803
  }
245540
- if (import_ts_morph10.ts.isPropertyAccessExpression(nameArg) && import_ts_morph10.ts.isIdentifier(nameArg.expression)) {
245804
+ if (import_ts_morph9.ts.isPropertyAccessExpression(nameArg) && import_ts_morph9.ts.isIdentifier(nameArg.expression)) {
245541
245805
  return `${nameArg.expression.text}::${nameArg.name.text}`;
245542
245806
  }
245543
245807
  return void 0;
@@ -245550,7 +245814,7 @@ var AstCliDataFileGenerator = class _AstCliDataFileGenerator extends AstFileGene
245550
245814
  const lines = [];
245551
245815
  for (const [key, valueExpr] of entries) {
245552
245816
  const printedValue = this.printExpr(valueExpr);
245553
- const formattedKey = key.includes("::") ? `[${this.printer.printNode(import_ts_morph10.ts.EmitHint.Unspecified, this.buildEnumCaseExpr(key), this.dummySourceFile)}]` : `['${key}']`;
245817
+ const formattedKey = key.includes("::") ? `[${this.printer.printNode(import_ts_morph9.ts.EmitHint.Unspecified, this.buildEnumCaseExpr(key), this.dummySourceFile)}]` : `['${key}']`;
245554
245818
  lines.push(` ${formattedKey}: (): RouteContract => ${printedValue},`);
245555
245819
  }
245556
245820
  return ["super({", ...lines, " });"].join("\n ");
@@ -245561,12 +245825,12 @@ var AstCliDataFileGenerator = class _AstCliDataFileGenerator extends AstFileGene
245561
245825
  * carry no source positions) are emitted through the printer.
245562
245826
  */
245563
245827
  printExpr(expr) {
245564
- return expr.pos >= 0 ? expr.getText() : this.printer.printNode(import_ts_morph10.ts.EmitHint.Unspecified, expr, this.dummySourceFile);
245828
+ return expr.pos >= 0 ? expr.getText() : this.printer.printNode(import_ts_morph9.ts.EmitHint.Unspecified, expr, this.dummySourceFile);
245565
245829
  }
245566
245830
  };
245567
245831
 
245568
245832
  // src/Sindri/Generator/Ast/Container/AstContainerDataFileGenerator.ts
245569
- var import_ts_morph11 = __toESM(require_ts_morph(), 1);
245833
+ var import_ts_morph10 = __toESM(require_ts_morph(), 1);
245570
245834
  var AstContainerDataFileGenerator = class _AstContainerDataFileGenerator extends AstFileGenerator {
245571
245835
  /** The framework class every generated container data file imports as a value. */
245572
245836
  static FRAMEWORK_IMPORTS = {
@@ -245577,13 +245841,13 @@ var AstContainerDataFileGenerator = class _AstContainerDataFileGenerator extends
245577
245841
  ContainerContract: "@valkyrjaio/valkyrja/Container/Manager/Contract/ContainerContract.ts"
245578
245842
  };
245579
245843
  classImportMap = {};
245580
- printer = import_ts_morph11.ts.createPrinter({ newLine: import_ts_morph11.ts.NewLineKind.LineFeed });
245581
- dummySourceFile = import_ts_morph11.ts.createSourceFile(
245844
+ printer = import_ts_morph10.ts.createPrinter({ newLine: import_ts_morph10.ts.NewLineKind.LineFeed });
245845
+ dummySourceFile = import_ts_morph10.ts.createSourceFile(
245582
245846
  "_dummy.ts",
245583
245847
  "",
245584
- import_ts_morph11.ts.ScriptTarget.ESNext,
245848
+ import_ts_morph10.ts.ScriptTarget.ESNext,
245585
245849
  false,
245586
- import_ts_morph11.ts.ScriptKind.TS
245850
+ import_ts_morph10.ts.ScriptKind.TS
245587
245851
  );
245588
245852
  generateFile(directory, className, namespace, publishers) {
245589
245853
  const userImportsBlock = this.buildUserImportsBlock(this.classImportMap, [
@@ -245614,7 +245878,7 @@ var AstContainerDataFileGenerator = class _AstContainerDataFileGenerator extends
245614
245878
  const lines = [];
245615
245879
  for (const [serviceId, [providerClass, methodName]] of entries) {
245616
245880
  const shortProviderClass = providerClass.slice(providerClass.lastIndexOf("\\") + 1);
245617
- const formattedKey = serviceId.includes("::") ? `[${this.printer.printNode(import_ts_morph11.ts.EmitHint.Unspecified, this.buildEnumCaseExpr(serviceId), this.dummySourceFile)}]` : `['${serviceId}']`;
245881
+ const formattedKey = serviceId.includes("::") ? `[${this.printer.printNode(import_ts_morph10.ts.EmitHint.Unspecified, this.buildEnumCaseExpr(serviceId), this.dummySourceFile)}]` : `['${serviceId}']`;
245618
245882
  lines.push(
245619
245883
  ` ${formattedKey}: (container: ContainerContract) => ${shortProviderClass}.${methodName}(container),`
245620
245884
  );
@@ -245626,7 +245890,7 @@ var AstContainerDataFileGenerator = class _AstContainerDataFileGenerator extends
245626
245890
  };
245627
245891
 
245628
245892
  // src/Sindri/Generator/Ast/Event/AstEventDataFileGenerator.ts
245629
- var import_ts_morph12 = __toESM(require_ts_morph(), 1);
245893
+ var import_ts_morph11 = __toESM(require_ts_morph(), 1);
245630
245894
  var AstEventDataFileGenerator = class _AstEventDataFileGenerator extends AstFileGenerator {
245631
245895
  /** The framework class every generated event data file imports as a value. */
245632
245896
  static FRAMEWORK_IMPORTS = {
@@ -245641,13 +245905,13 @@ var AstEventDataFileGenerator = class _AstEventDataFileGenerator extends AstFile
245641
245905
  ListenerContract: "@valkyrjaio/valkyrja/Event/Data/Contract/ListenerContract.ts"
245642
245906
  };
245643
245907
  classImportMap = {};
245644
- printer = import_ts_morph12.ts.createPrinter({ newLine: import_ts_morph12.ts.NewLineKind.LineFeed });
245645
- dummySourceFile = import_ts_morph12.ts.createSourceFile(
245908
+ printer = import_ts_morph11.ts.createPrinter({ newLine: import_ts_morph11.ts.NewLineKind.LineFeed });
245909
+ dummySourceFile = import_ts_morph11.ts.createSourceFile(
245646
245910
  "_dummy.ts",
245647
245911
  "",
245648
- import_ts_morph12.ts.ScriptTarget.ESNext,
245912
+ import_ts_morph11.ts.ScriptTarget.ESNext,
245649
245913
  false,
245650
- import_ts_morph12.ts.ScriptKind.TS
245914
+ import_ts_morph11.ts.ScriptKind.TS
245651
245915
  );
245652
245916
  generateFile(directory, className, namespace, listeners) {
245653
245917
  const userImportsBlock = this.buildUserImportsBlock(this.classImportMap, [
@@ -245682,7 +245946,7 @@ var AstEventDataFileGenerator = class _AstEventDataFileGenerator extends AstFile
245682
245946
  }
245683
245947
  const lines = [];
245684
245948
  for (const [key, valueExpr] of entries) {
245685
- const printed = this.printer.printNode(import_ts_morph12.ts.EmitHint.Unspecified, valueExpr, this.dummySourceFile);
245949
+ const printed = this.printer.printNode(import_ts_morph11.ts.EmitHint.Unspecified, valueExpr, this.dummySourceFile);
245686
245950
  lines.push(` ['${key}']: (): ListenerContract => ${printed},`);
245687
245951
  }
245688
245952
  return ["super(", " {},", " {", ...lines, " },", " );"].join(
@@ -245692,7 +245956,7 @@ var AstEventDataFileGenerator = class _AstEventDataFileGenerator extends AstFile
245692
245956
  };
245693
245957
 
245694
245958
  // src/Sindri/Generator/Ast/Http/AstHttpDataFileGenerator.ts
245695
- var import_ts_morph13 = __toESM(require_ts_morph(), 1);
245959
+ var import_ts_morph12 = __toESM(require_ts_morph(), 1);
245696
245960
 
245697
245961
  // node_modules/@valkyrjaio/valkyrja/src/Valkyrja/Http/Message/Enum/RequestMethod.ts
245698
245962
  function allRequestMethods() {
@@ -246064,6 +246328,8 @@ var Regex = class _Regex {
246064
246328
  static ALPHA_UPPERCASE = "[A-Z]+";
246065
246329
  static ALPHA_NUM = "[a-zA-Z0-9]+";
246066
246330
  static ALPHA_NUM_UNDERSCORE = "\\w+";
246331
+ // Anchors only — JavaScript's `RegExp` constructor takes a bare pattern, so
246332
+ // PHP-style `/.../` delimiters would be matched as literal characters.
246067
246333
  static START = "^";
246068
246334
  static END = "$";
246069
246335
  static START_CAPTURE_GROUP = "(";
@@ -246173,29 +246439,20 @@ var AstHttpDataFileGenerator = class _AstHttpDataFileGenerator extends AstFileGe
246173
246439
  };
246174
246440
  classImportMap = {};
246175
246441
  processor;
246176
- printer = import_ts_morph13.ts.createPrinter({ newLine: import_ts_morph13.ts.NewLineKind.LineFeed });
246177
- dummySourceFile = import_ts_morph13.ts.createSourceFile(
246442
+ printer = import_ts_morph12.ts.createPrinter({ newLine: import_ts_morph12.ts.NewLineKind.LineFeed });
246443
+ dummySourceFile = import_ts_morph12.ts.createSourceFile(
246178
246444
  "_dummy.ts",
246179
246445
  "",
246180
- import_ts_morph13.ts.ScriptTarget.ESNext,
246446
+ import_ts_morph12.ts.ScriptTarget.ESNext,
246181
246447
  false,
246182
- import_ts_morph13.ts.ScriptKind.TS
246448
+ import_ts_morph12.ts.ScriptKind.TS
246183
246449
  );
246184
246450
  constructor(processor = new Processor()) {
246185
246451
  super();
246186
246452
  this.processor = processor;
246187
246453
  }
246188
246454
  generateFile(directory, className, namespace, routes, routeData) {
246189
- const userImportsBlock = this.buildUserImportsBlock(this.classImportMap, [
246190
- ...Object.keys(_AstHttpDataFileGenerator.FRAMEWORK_IMPORTS),
246191
- ...Object.keys(_AstHttpDataFileGenerator.FRAMEWORK_TYPE_IMPORTS)
246192
- ]);
246193
- const routesContent = this.getRoutesAsContent(routes, routeData);
246194
- const paths = this.printNestedObject(this.buildPaths(routeData));
246195
- const dynamicPaths = this.printNestedObject(this.buildDynamicPaths(routeData));
246196
- const regexes = this.printNestedObject(this.buildRegexes(routeData));
246197
- const data = this.assembleFile(className, userImportsBlock, routesContent, paths, dynamicPaths, regexes);
246198
- return this.writeFile(directory, className, data);
246455
+ return this.generateMergedFile(directory, className, namespace, routes, routeData, []);
246199
246456
  }
246200
246457
  /**
246201
246458
  * Generate the routing data file from imperative `getRoutes()` route
@@ -246206,19 +246463,45 @@ var AstHttpDataFileGenerator = class _AstHttpDataFileGenerator extends AstFileGe
246206
246463
  * own arguments, mirroring the framework's runtime `RouteCollection`
246207
246464
  * (literal path and regex, request methods defaulting to `[HEAD, GET]`).
246208
246465
  */
246209
- generateFileFromRoutes(directory, className, _namespace, routeExprs) {
246466
+ generateFileFromRoutes(directory, className, namespace, routeExprs) {
246467
+ return this.generateMergedFile(directory, className, namespace, {}, {}, routeExprs);
246468
+ }
246469
+ /**
246470
+ * Generate the routing data file by MERGING attribute-scanned routes with
246471
+ * imperative `getRoutes()` route objects — the four lookup maps (routes,
246472
+ * paths, dynamicPaths, regexes) are built from both sources and combined.
246473
+ * Attribute entries are emitted first, then imperative ones.
246474
+ */
246475
+ generateMergedFile(directory, className, _namespace, routes, routeData, routeExprs) {
246210
246476
  const userImportsBlock = this.buildUserImportsBlock(this.classImportMap, [
246211
246477
  ...Object.keys(_AstHttpDataFileGenerator.FRAMEWORK_IMPORTS),
246212
246478
  ...Object.keys(_AstHttpDataFileGenerator.FRAMEWORK_TYPE_IMPORTS)
246213
246479
  ]);
246214
246480
  const metas = routeExprs.map((expr) => this.extractRouteMeta(expr)).filter((meta) => meta !== void 0);
246215
- const routesContent = this.getImperativeRoutesContent(metas);
246216
- const paths = this.printNestedObject(this.buildImperativePaths(metas, false));
246217
- const dynamicPaths = this.printNestedObject(this.buildImperativePaths(metas, true));
246218
- const regexes = this.printNestedObject(this.buildImperativeRegexes(metas));
246481
+ const routesContent = this.wrapRoutes([
246482
+ ...this.getRouteLines(routes, routeData),
246483
+ ...this.getImperativeRouteLines(metas)
246484
+ ]);
246485
+ const paths = this.printNestedObject(
246486
+ this.mergeNested(this.buildPaths(routeData), this.buildImperativePaths(metas, false))
246487
+ );
246488
+ const dynamicPaths = this.printNestedObject(
246489
+ this.mergeNested(this.buildDynamicPaths(routeData), this.buildImperativePaths(metas, true))
246490
+ );
246491
+ const regexes = this.printNestedObject(
246492
+ this.mergeNested(this.buildRegexes(routeData), this.buildImperativeRegexes(metas))
246493
+ );
246219
246494
  const data = this.assembleFile(className, userImportsBlock, routesContent, paths, dynamicPaths, regexes);
246220
246495
  return this.writeFile(directory, className, data);
246221
246496
  }
246497
+ /** Deep-merge two `method → key → name` maps; entries in `b` override `a`. */
246498
+ mergeNested(a, b) {
246499
+ const merged = {};
246500
+ for (const [outerKey, innerMap] of [...Object.entries(a), ...Object.entries(b)]) {
246501
+ merged[outerKey] = { ...merged[outerKey] ?? {}, ...innerMap };
246502
+ }
246503
+ return merged;
246504
+ }
246222
246505
  assembleFile(className, userImportsBlock, routesContent, paths, dynamicPaths, regexes) {
246223
246506
  return [
246224
246507
  "// This file was automatically generated by Sindri.",
@@ -246310,24 +246593,24 @@ var AstHttpDataFileGenerator = class _AstHttpDataFileGenerator extends AstFileGe
246310
246593
  const innerProps = [];
246311
246594
  for (const [innerKey, value] of Object.entries(innerMap)) {
246312
246595
  innerProps.push(
246313
- import_ts_morph13.ts.factory.createPropertyAssignment(
246314
- import_ts_morph13.ts.factory.createStringLiteral(innerKey),
246315
- import_ts_morph13.ts.factory.createStringLiteral(value)
246596
+ import_ts_morph12.ts.factory.createPropertyAssignment(
246597
+ import_ts_morph12.ts.factory.createStringLiteral(innerKey),
246598
+ import_ts_morph12.ts.factory.createStringLiteral(value)
246316
246599
  )
246317
246600
  );
246318
246601
  }
246319
246602
  outerProps.push(
246320
- import_ts_morph13.ts.factory.createPropertyAssignment(
246321
- import_ts_morph13.ts.factory.createStringLiteral(outerKey),
246322
- import_ts_morph13.ts.factory.createObjectLiteralExpression(innerProps, true)
246603
+ import_ts_morph12.ts.factory.createPropertyAssignment(
246604
+ import_ts_morph12.ts.factory.createStringLiteral(outerKey),
246605
+ import_ts_morph12.ts.factory.createObjectLiteralExpression(innerProps, true)
246323
246606
  )
246324
246607
  );
246325
246608
  }
246326
- return import_ts_morph13.ts.factory.createObjectLiteralExpression(outerProps, true);
246609
+ return import_ts_morph12.ts.factory.createObjectLiteralExpression(outerProps, true);
246327
246610
  }
246328
246611
  printNestedObject(data) {
246329
246612
  return this.printer.printNode(
246330
- import_ts_morph13.ts.EmitHint.Unspecified,
246613
+ import_ts_morph12.ts.EmitHint.Unspecified,
246331
246614
  this.buildNestedStringArrayExpr(data),
246332
246615
  this.dummySourceFile
246333
246616
  );
@@ -246378,24 +246661,41 @@ var AstHttpDataFileGenerator = class _AstHttpDataFileGenerator extends AstFileGe
246378
246661
  return methods;
246379
246662
  }
246380
246663
  getRoutesAsContent(routes, routeData) {
246381
- const entries = Object.entries(routes);
246382
- if (entries.length === 0) {
246664
+ return this.wrapRoutes(this.getRouteLines(routes, routeData));
246665
+ }
246666
+ /** Wrap route closure lines in the `{ ... }` object-literal body (or `{}`). */
246667
+ wrapRoutes(lines) {
246668
+ if (lines.length === 0) {
246383
246669
  return "{}";
246384
246670
  }
246671
+ return ["{", ...lines, " }"].join("\n ");
246672
+ }
246673
+ /** Build the per-route closure lines for attribute-scanned routes. */
246674
+ getRouteLines(routes, routeData) {
246385
246675
  const lines = [];
246386
- for (const [key, routeExpr] of entries) {
246676
+ for (const [key, routeExpr] of Object.entries(routes)) {
246387
246677
  const data = routeData[key] ?? null;
246388
- let expr = routeExpr;
246389
- if (data !== null && data.isDynamic && import_ts_morph13.ts.isNewExpression(expr)) {
246390
- const computedRegex = data.parameters.length > 0 ? this.computeRegex(data) : "";
246391
- const args = [...expr.arguments ?? [], import_ts_morph13.ts.factory.createStringLiteral(computedRegex)];
246392
- expr = import_ts_morph13.ts.factory.createNewExpression(expr.expression, expr.typeArguments, args);
246393
- }
246394
- const printedRoute = this.printer.printNode(import_ts_morph13.ts.EmitHint.Unspecified, expr, this.dummySourceFile);
246395
- const formattedKey = key.includes("::") ? `[${this.printer.printNode(import_ts_morph13.ts.EmitHint.Unspecified, this.buildEnumCaseExpr(key), this.dummySourceFile)}]` : `['${key}']`;
246678
+ const expr = data !== null ? this.injectRegex(routeExpr, data) : routeExpr;
246679
+ const printedRoute = this.printer.printNode(import_ts_morph12.ts.EmitHint.Unspecified, expr, this.dummySourceFile);
246680
+ const formattedKey = key.includes("::") ? `[${this.printer.printNode(import_ts_morph12.ts.EmitHint.Unspecified, this.buildEnumCaseExpr(key), this.dummySourceFile)}]` : `['${key}']`;
246396
246681
  lines.push(` ${formattedKey}: (): RouteContract => ${printedRoute},`);
246397
246682
  }
246398
- return ["{", ...lines, " }"].join("\n ");
246683
+ return lines;
246684
+ }
246685
+ /**
246686
+ * Replace the regex argument (slot 2) of a dynamic `new DynamicRoute(...)`
246687
+ * expression with the regex computed from the route's parameters. The reader
246688
+ * emits an empty-string placeholder there; the generator owns the routing
246689
+ * `Processor` and computes the real value here.
246690
+ */
246691
+ injectRegex(expr, data) {
246692
+ if (!(data.isDynamic && import_ts_morph12.ts.isNewExpression(expr))) {
246693
+ return expr;
246694
+ }
246695
+ const computedRegex = data.parameters.length > 0 ? this.computeRegex(data) : "";
246696
+ const original = expr.arguments ?? import_ts_morph12.ts.factory.createNodeArray();
246697
+ const args = original.map((arg, index) => index === 2 ? import_ts_morph12.ts.factory.createStringLiteral(computedRegex) : arg);
246698
+ return import_ts_morph12.ts.factory.createNewExpression(expr.expression, expr.typeArguments, args);
246399
246699
  }
246400
246700
  // -------------------------------------------------------------------------
246401
246701
  // Imperative `getRoutes()` support
@@ -246407,11 +246707,11 @@ var AstHttpDataFileGenerator = class _AstHttpDataFileGenerator extends AstFileGe
246407
246707
  * DynamicRoute(path, name, regex, parameters, handler, requestMethods?)
246408
246708
  */
246409
246709
  extractRouteMeta(expr) {
246410
- if (!import_ts_morph13.ts.isNewExpression(expr) || !import_ts_morph13.ts.isIdentifier(expr.expression)) {
246710
+ if (!import_ts_morph12.ts.isNewExpression(expr) || !import_ts_morph12.ts.isIdentifier(expr.expression)) {
246411
246711
  return void 0;
246412
246712
  }
246413
246713
  const isDynamic = expr.expression.text === "DynamicRoute";
246414
- const args = expr.arguments ?? import_ts_morph13.ts.factory.createNodeArray();
246714
+ const args = expr.arguments ?? import_ts_morph12.ts.factory.createNodeArray();
246415
246715
  const path6 = this.stringArg(args[0]);
246416
246716
  const name = this.stringArg(args[1]);
246417
246717
  if (name === void 0 || path6 === void 0) {
@@ -246423,7 +246723,7 @@ var AstHttpDataFileGenerator = class _AstHttpDataFileGenerator extends AstFileGe
246423
246723
  return { expr, name, path: path6, isDynamic, regex, methods };
246424
246724
  }
246425
246725
  stringArg(node) {
246426
- return node !== void 0 && import_ts_morph13.ts.isStringLiteral(node) ? node.text : void 0;
246726
+ return node !== void 0 && import_ts_morph12.ts.isStringLiteral(node) ? node.text : void 0;
246427
246727
  }
246428
246728
  /**
246429
246729
  * Resolve a `[RequestMethod.GET, ...]` argument to method-name strings,
@@ -246431,12 +246731,12 @@ var AstHttpDataFileGenerator = class _AstHttpDataFileGenerator extends AstFileGe
246431
246731
  * and expanding `RequestMethod.ANY` to every request method.
246432
246732
  */
246433
246733
  extractMethodNamesFromArg(node) {
246434
- if (node === void 0 || !import_ts_morph13.ts.isArrayLiteralExpression(node)) {
246734
+ if (node === void 0 || !import_ts_morph12.ts.isArrayLiteralExpression(node)) {
246435
246735
  return ["HEAD" /* HEAD */, "GET" /* GET */];
246436
246736
  }
246437
246737
  const methods = [];
246438
246738
  for (const element of node.elements) {
246439
- if (!import_ts_morph13.ts.isPropertyAccessExpression(element)) {
246739
+ if (!import_ts_morph12.ts.isPropertyAccessExpression(element)) {
246440
246740
  continue;
246441
246741
  }
246442
246742
  const method = element.name.text;
@@ -246447,15 +246747,9 @@ var AstHttpDataFileGenerator = class _AstHttpDataFileGenerator extends AstFileGe
246447
246747
  }
246448
246748
  return methods;
246449
246749
  }
246450
- getImperativeRoutesContent(metas) {
246451
- if (metas.length === 0) {
246452
- return "{}";
246453
- }
246454
- const lines = [];
246455
- for (const meta of metas) {
246456
- lines.push(` ['${meta.name}']: (): RouteContract => ${meta.expr.getText()},`);
246457
- }
246458
- return ["{", ...lines, " }"].join("\n ");
246750
+ /** Build the per-route closure lines for imperative `getRoutes()` routes. */
246751
+ getImperativeRouteLines(metas) {
246752
+ return metas.map((meta) => ` ['${meta.name}']: (): RouteContract => ${meta.expr.getText()},`);
246459
246753
  }
246460
246754
  buildImperativePaths(metas, dynamic) {
246461
246755
  const paths = {};
@@ -246698,15 +246992,17 @@ var GenerateDataFromAst = class extends GenerateFromAst {
246698
246992
  }
246699
246993
  const attrResult = this.cliRouteAttributeReader.readFile(controllerPath);
246700
246994
  Object.assign(allRoutes, attrResult.routes);
246995
+ this.mergeReaderImports(importMap, attrResult.importMap, config);
246701
246996
  }
246702
246997
  }
246703
246998
  this.cliGenerator.classImportMap = importMap;
246704
- const status = imperativeRoutes.length > 0 ? this.cliGenerator.generateFileFromRoutes(
246999
+ const status = this.cliGenerator.generateMergedFile(
246705
247000
  config.dataPath,
246706
247001
  "AppCliRoutingData",
246707
247002
  config.dataNamespace,
247003
+ allRoutes,
246708
247004
  imperativeRoutes
246709
- ) : this.cliGenerator.generateFile(config.dataPath, "AppCliRoutingData", config.dataNamespace, allRoutes);
247005
+ );
246710
247006
  return this.addMessagesForGenerateStatus(output, status).withAddedMessages(new NewLine()).writeMessages();
246711
247007
  }
246712
247008
  generateHttpData(httpRouteProviders, config, output) {
@@ -246736,23 +247032,33 @@ var GenerateDataFromAst = class extends GenerateFromAst {
246736
247032
  const attrResult = this.httpRouteAttributeReader.readFile(controllerPath);
246737
247033
  Object.assign(allRoutes, attrResult.routes);
246738
247034
  Object.assign(allRouteData, attrResult.routeData);
247035
+ this.mergeReaderImports(importMap, attrResult.importMap, config);
246739
247036
  }
246740
247037
  }
246741
247038
  this.httpGenerator.classImportMap = importMap;
246742
- const status = imperativeRoutes.length > 0 ? this.httpGenerator.generateFileFromRoutes(
246743
- config.dataPath,
246744
- "AppHttpRoutingData",
246745
- config.dataNamespace,
246746
- imperativeRoutes
246747
- ) : this.httpGenerator.generateFile(
247039
+ const status = this.httpGenerator.generateMergedFile(
246748
247040
  config.dataPath,
246749
247041
  "AppHttpRoutingData",
246750
247042
  config.dataNamespace,
246751
247043
  allRoutes,
246752
- allRouteData
247044
+ allRouteData,
247045
+ imperativeRoutes
246753
247046
  );
246754
247047
  return this.addMessagesForGenerateStatus(output, status).withAddedMessages(new NewLine()).writeMessages();
246755
247048
  }
247049
+ /**
247050
+ * Merge the class-name → absolute-file-path imports a route attribute reader
247051
+ * resolved (handler/help-text/middleware classes) into the generated data
247052
+ * file's import map, converting each absolute path to the specifier the
247053
+ * generated file imports it by — relative to the output directory for
247054
+ * application source, or the owning package's own specifier for a class an
247055
+ * installed package exposes.
247056
+ */
247057
+ mergeReaderImports(importMap, readerImportMap, config) {
247058
+ for (const [name, absolutePath] of Object.entries(readerImportMap)) {
247059
+ importMap[name] = this.importSpecifier(config.dataPath, absolutePath);
247060
+ }
247061
+ }
246756
247062
  addMessagesForGenerateStatus(output, status) {
246757
247063
  let text = "Failed";
246758
247064
  let formatter = new ErrorFormatter();