@valkyrjaio/sindri 26.3.0 → 26.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -193,7 +193,7 @@ var require_typescript = __commonJS({
193
193
  SymbolDisplayPartKind: () => SymbolDisplayPartKind,
194
194
  SymbolFlags: () => SymbolFlags,
195
195
  SymbolFormatFlags: () => SymbolFormatFlags,
196
- SyntaxKind: () => SyntaxKind2,
196
+ SyntaxKind: () => SyntaxKind,
197
197
  Ternary: () => Ternary,
198
198
  ThrottledCancellationToken: () => ThrottledCancellationToken,
199
199
  TokenClass: () => TokenClass,
@@ -4084,7 +4084,7 @@ Node ${formatSyntaxKind(node.kind)} was unexpected.`,
4084
4084
  function formatSyntaxKind(kind) {
4085
4085
  return formatEnum(
4086
4086
  kind,
4087
- SyntaxKind2,
4087
+ SyntaxKind,
4088
4088
  /*isFlags*/
4089
4089
  false
4090
4090
  );
@@ -5532,7 +5532,7 @@ ${lanes.join("\n")}
5532
5532
  })(tracingEnabled || (tracingEnabled = {}));
5533
5533
  var startTracing = tracingEnabled.startTracing;
5534
5534
  var dumpTracingLegend = tracingEnabled.dumpLegend;
5535
- var SyntaxKind2 = /* @__PURE__ */ ((SyntaxKind5) => {
5535
+ var SyntaxKind = /* @__PURE__ */ ((SyntaxKind5) => {
5536
5536
  SyntaxKind5[SyntaxKind5["Unknown"] = 0] = "Unknown";
5537
5537
  SyntaxKind5[SyntaxKind5["EndOfFileToken"] = 1] = "EndOfFileToken";
5538
5538
  SyntaxKind5[SyntaxKind5["SingleLineCommentTrivia"] = 2] = "SingleLineCommentTrivia";
@@ -6038,7 +6038,7 @@ ${lanes.join("\n")}
6038
6038
  /* LastKeyword */
6039
6039
  ] = "LastContextualKeyword";
6040
6040
  return SyntaxKind5;
6041
- })(SyntaxKind2 || {});
6041
+ })(SyntaxKind || {});
6042
6042
  var NodeFlags = /* @__PURE__ */ ((NodeFlags3) => {
6043
6043
  NodeFlags3[NodeFlags3["None"] = 0] = "None";
6044
6044
  NodeFlags3[NodeFlags3["Let"] = 1] = "Let";
@@ -196444,7 +196444,7 @@ ${options.prefix}` : "\n" : options.prefix
196444
196444
  SymbolDisplayPartKind: () => SymbolDisplayPartKind,
196445
196445
  SymbolFlags: () => SymbolFlags,
196446
196446
  SymbolFormatFlags: () => SymbolFormatFlags,
196447
- SyntaxKind: () => SyntaxKind2,
196447
+ SyntaxKind: () => SyntaxKind,
196448
196448
  Ternary: () => Ternary,
196449
196449
  ThrottledCancellationToken: () => ThrottledCancellationToken,
196450
196450
  TokenClass: () => TokenClass,
@@ -231501,12 +231501,12 @@ Node text: ${this.#forgottenText}`;
231501
231501
  }
231502
231502
  };
231503
231503
  }
231504
- var Expression = class extends Node2 {
231504
+ var Expression2 = class extends Node2 {
231505
231505
  getContextualType() {
231506
231506
  return this._context.typeChecker.getContextualType(this);
231507
231507
  }
231508
231508
  };
231509
- var BinaryExpressionBase = Expression;
231509
+ var BinaryExpressionBase = Expression2;
231510
231510
  var BinaryExpression = class extends BinaryExpressionBase {
231511
231511
  getLeft() {
231512
231512
  return this._getNodeFromCompilerNode(this.compilerNode.left);
@@ -231530,7 +231530,7 @@ Node text: ${this.#forgottenText}`;
231530
231530
  return this._getNodeFromCompilerNode(this.compilerNode.left);
231531
231531
  }
231532
231532
  };
231533
- var UnaryExpression = class extends Expression {
231533
+ var UnaryExpression = class extends Expression2 {
231534
231534
  };
231535
231535
  var UpdateExpression = class extends UnaryExpression {
231536
231536
  };
@@ -231660,7 +231660,7 @@ Node text: ${this.#forgottenText}`;
231660
231660
  return BaseExpressionedNode(Base);
231661
231661
  }
231662
231662
  var createBase$E = (ctor) => TypedNode(ExpressionedNode(ctor));
231663
- var AsExpressionBase = createBase$E(Expression);
231663
+ var AsExpressionBase = createBase$E(Expression2);
231664
231664
  var AsExpression = class extends AsExpressionBase {
231665
231665
  };
231666
231666
  var AwaitExpressionBase = UnaryExpressionedNode(UnaryExpression);
@@ -231673,13 +231673,13 @@ Node text: ${this.#forgottenText}`;
231673
231673
  return this._context.typeChecker.getTypeAtLocation(this);
231674
231674
  }
231675
231675
  };
231676
- var CommaListExpressionBase = Expression;
231676
+ var CommaListExpressionBase = Expression2;
231677
231677
  var CommaListExpression = class extends CommaListExpressionBase {
231678
231678
  getElements() {
231679
231679
  return this.compilerNode.elements.map((e) => this._getNodeFromCompilerNode(e));
231680
231680
  }
231681
231681
  };
231682
- var ConditionalExpressionBase = Expression;
231682
+ var ConditionalExpressionBase = Expression2;
231683
231683
  var ConditionalExpression = class extends ConditionalExpressionBase {
231684
231684
  getCondition() {
231685
231685
  return this._getNodeFromCompilerNode(this.compilerNode.condition);
@@ -232010,13 +232010,13 @@ Node text: ${this.#forgottenText}`;
232010
232010
  });
232011
232011
  }
232012
232012
  };
232013
- var OmittedExpressionBase = Expression;
232013
+ var OmittedExpressionBase = Expression2;
232014
232014
  var OmittedExpression = class extends OmittedExpressionBase {
232015
232015
  };
232016
- var ParenthesizedExpressionBase = ExpressionedNode(Expression);
232016
+ var ParenthesizedExpressionBase = ExpressionedNode(Expression2);
232017
232017
  var ParenthesizedExpression = class extends ParenthesizedExpressionBase {
232018
232018
  };
232019
- var PartiallyEmittedExpressionBase = ExpressionedNode(Expression);
232019
+ var PartiallyEmittedExpressionBase = ExpressionedNode(Expression2);
232020
232020
  var PartiallyEmittedExpression = class extends PartiallyEmittedExpressionBase {
232021
232021
  };
232022
232022
  var PostfixUnaryExpressionBase = UnaryExpression;
@@ -232042,10 +232042,10 @@ Node text: ${this.#forgottenText}`;
232042
232042
  var PropertyAccessExpression = class extends PropertyAccessExpressionBase {
232043
232043
  };
232044
232044
  var createBase$x = (ctor) => TypedNode(ExpressionedNode(ctor));
232045
- var SatisfiesExpressionBase = createBase$x(Expression);
232045
+ var SatisfiesExpressionBase = createBase$x(Expression2);
232046
232046
  var SatisfiesExpression = class extends SatisfiesExpressionBase {
232047
232047
  };
232048
- var SpreadElementBase = ExpressionedNode(Expression);
232048
+ var SpreadElementBase = ExpressionedNode(Expression2);
232049
232049
  var SpreadElement = class extends SpreadElementBase {
232050
232050
  };
232051
232051
  var SuperElementAccessExpressionBase = SuperExpressionedNode(ElementAccessExpression);
@@ -232070,7 +232070,7 @@ Node text: ${this.#forgottenText}`;
232070
232070
  var VoidExpressionBase = UnaryExpressionedNode(UnaryExpression);
232071
232071
  var VoidExpression = class extends VoidExpressionBase {
232072
232072
  };
232073
- var YieldExpressionBase = ExpressionableNode(GeneratorableNode(Expression));
232073
+ var YieldExpressionBase = ExpressionableNode(GeneratorableNode(Expression2));
232074
232074
  var YieldExpression = class extends YieldExpressionBase {
232075
232075
  };
232076
232076
  var StatementBase = ChildOrderableNode(Node2);
@@ -234569,7 +234569,7 @@ Node text: ${this.#forgottenText}`;
234569
234569
  return JSDocableNode(TypeParameteredNode(SignaturedDeclaration(StatementedNode(ModifierableNode(Base)))));
234570
234570
  }
234571
234571
  var createBase$p = (ctor) => TextInsertableNode(BodiedNode(AsyncableNode(FunctionLikeDeclaration(ctor))));
234572
- var ArrowFunctionBase = createBase$p(Expression);
234572
+ var ArrowFunctionBase = createBase$p(Expression2);
234573
234573
  var ArrowFunction = class extends ArrowFunctionBase {
234574
234574
  getEqualsGreaterThan() {
234575
234575
  return this._getNodeFromCompilerNode(this.compilerNode.equalsGreaterThanToken);
@@ -237145,7 +237145,7 @@ Node text: ${this.#forgottenText}`;
237145
237145
  var JsxClosingElementBase = createBase$3(Node2);
237146
237146
  var JsxClosingElement = class extends JsxClosingElementBase {
237147
237147
  };
237148
- var JsxClosingFragment = class extends Expression {
237148
+ var JsxClosingFragment = class extends Expression2 {
237149
237149
  };
237150
237150
  var JsxElementBase = PrimaryExpression;
237151
237151
  var JsxElement = class extends JsxElementBase {
@@ -237217,7 +237217,7 @@ Node text: ${this.#forgottenText}`;
237217
237217
  }
237218
237218
  });
237219
237219
  }
237220
- var JsxExpressionBase = ExpressionableNode(DotDotDotTokenableNode(Expression));
237220
+ var JsxExpressionBase = ExpressionableNode(DotDotDotTokenableNode(Expression2));
237221
237221
  var JsxExpression = class extends JsxExpressionBase {
237222
237222
  };
237223
237223
  var JsxFragment = class extends PrimaryExpression {
@@ -237252,10 +237252,10 @@ Node text: ${this.#forgottenText}`;
237252
237252
  }
237253
237253
  };
237254
237254
  var createBase$2 = (ctor) => JsxAttributedNode(JsxTagNamedNode(ctor));
237255
- var JsxOpeningElementBase = createBase$2(Expression);
237255
+ var JsxOpeningElementBase = createBase$2(Expression2);
237256
237256
  var JsxOpeningElement = class extends JsxOpeningElementBase {
237257
237257
  };
237258
- var JsxOpeningFragment = class extends Expression {
237258
+ var JsxOpeningFragment = class extends Expression2 {
237259
237259
  };
237260
237260
  var createBase$1 = (ctor) => JsxAttributedNode(JsxTagNamedNode(ctor));
237261
237261
  var JsxSelfClosingElementBase = createBase$1(PrimaryExpression);
@@ -240078,21 +240078,21 @@ Node text: ${this.#forgottenText}`;
240078
240078
  [common.SyntaxKind.WithStatement]: WithStatement,
240079
240079
  [common.SyntaxKind.YieldExpression]: YieldExpression,
240080
240080
  [common.SyntaxKind.SemicolonToken]: Node2,
240081
- [common.SyntaxKind.AnyKeyword]: Expression,
240082
- [common.SyntaxKind.BooleanKeyword]: Expression,
240081
+ [common.SyntaxKind.AnyKeyword]: Expression2,
240082
+ [common.SyntaxKind.BooleanKeyword]: Expression2,
240083
240083
  [common.SyntaxKind.FalseKeyword]: FalseLiteral,
240084
240084
  [common.SyntaxKind.ImportKeyword]: ImportExpression,
240085
240085
  [common.SyntaxKind.InferKeyword]: Node2,
240086
240086
  [common.SyntaxKind.NeverKeyword]: Node2,
240087
240087
  [common.SyntaxKind.NullKeyword]: NullLiteral,
240088
- [common.SyntaxKind.NumberKeyword]: Expression,
240089
- [common.SyntaxKind.ObjectKeyword]: Expression,
240090
- [common.SyntaxKind.StringKeyword]: Expression,
240091
- [common.SyntaxKind.SymbolKeyword]: Expression,
240088
+ [common.SyntaxKind.NumberKeyword]: Expression2,
240089
+ [common.SyntaxKind.ObjectKeyword]: Expression2,
240090
+ [common.SyntaxKind.StringKeyword]: Expression2,
240091
+ [common.SyntaxKind.SymbolKeyword]: Expression2,
240092
240092
  [common.SyntaxKind.SuperKeyword]: SuperExpression,
240093
240093
  [common.SyntaxKind.ThisKeyword]: ThisExpression,
240094
240094
  [common.SyntaxKind.TrueKeyword]: TrueLiteral,
240095
- [common.SyntaxKind.UndefinedKeyword]: Expression,
240095
+ [common.SyntaxKind.UndefinedKeyword]: Expression2,
240096
240096
  [common.SyntaxKind.VoidExpression]: VoidExpression
240097
240097
  };
240098
240098
  var CompilerFactory = class {
@@ -241356,7 +241356,7 @@ Node text: ${this.#forgottenText}`;
241356
241356
  exports2.ExportSpecifier = ExportSpecifier;
241357
241357
  exports2.ExportSpecifierBase = ExportSpecifierBase;
241358
241358
  exports2.ExportableNode = ExportableNode;
241359
- exports2.Expression = Expression;
241359
+ exports2.Expression = Expression2;
241360
241360
  exports2.ExpressionStatement = ExpressionStatement;
241361
241361
  exports2.ExpressionStatementBase = ExpressionStatementBase;
241362
241362
  exports2.ExpressionWithTypeArguments = ExpressionWithTypeArguments;
@@ -242575,7 +242575,20 @@ var HandlerData = class {
242575
242575
  };
242576
242576
 
242577
242577
  // src/Sindri/Ast/Abstract/AstReader.ts
242578
- var AstReader = class {
242578
+ var AstReader = class _AstReader {
242579
+ /**
242580
+ * Module resolution settings for {@link resolveModuleSpecifierToFilePath}.
242581
+ *
242582
+ * NodeNext mirrors how the application itself resolves its imports, and
242583
+ * `allowImportingTsExtensions` matches the framework convention of writing
242584
+ * the `.ts` extension in every import specifier.
242585
+ */
242586
+ static MODULE_RESOLUTION_OPTIONS = {
242587
+ module: import_ts_morph.ts.ModuleKind.NodeNext,
242588
+ moduleResolution: import_ts_morph.ts.ModuleResolutionKind.NodeNext,
242589
+ allowImportingTsExtensions: true,
242590
+ noEmit: true
242591
+ };
242579
242592
  parseFileToSourceFile(filePath) {
242580
242593
  if (!fs.existsSync(filePath)) {
242581
242594
  throw new AstFileReadException(`Cannot read file '${filePath}'.`);
@@ -242623,12 +242636,39 @@ var AstReader = class {
242623
242636
  if (moduleSpecifier === void 0) {
242624
242637
  return "";
242625
242638
  }
242626
- if (!moduleSpecifier.startsWith(".")) {
242639
+ return this.resolveModuleSpecifierToFilePath(moduleSpecifier, currentFilePath);
242640
+ }
242641
+ /**
242642
+ * Resolve a module specifier to the absolute path of the TypeScript file it points at.
242643
+ *
242644
+ * Relative specifiers resolve against the importing file's directory. Bare
242645
+ * package specifiers (`@valkyrjaio/valkyrja/Cli/Routing/Data/Route.ts`) go
242646
+ * through the TypeScript compiler's NodeNext resolver, which honors the
242647
+ * package's `exports` map — so a provider tree that reaches into an
242648
+ * installed package (the framework's own component providers, reached from
242649
+ * the app config) is walked exactly like application source.
242650
+ *
242651
+ * Returns '' when the specifier cannot be resolved to an existing `.ts`
242652
+ * source file. A package shipping only compiled `.js` — or only `.d.ts`
242653
+ * declarations, which carry no `getRoutes()` body to read — has nothing for
242654
+ * the AST readers to walk.
242655
+ */
242656
+ resolveModuleSpecifierToFilePath(moduleSpecifier, currentFilePath) {
242657
+ if (moduleSpecifier.startsWith(".")) {
242658
+ const dir = path.dirname(currentFilePath);
242659
+ const resolved2 = path.resolve(dir, moduleSpecifier.replace(/\.js$/, ".ts"));
242660
+ return fs.existsSync(resolved2) ? resolved2 : "";
242661
+ }
242662
+ const resolved = import_ts_morph.ts.resolveModuleName(
242663
+ moduleSpecifier,
242664
+ currentFilePath,
242665
+ _AstReader.MODULE_RESOLUTION_OPTIONS,
242666
+ import_ts_morph.ts.sys
242667
+ ).resolvedModule?.resolvedFileName;
242668
+ if (resolved === void 0 || !resolved.endsWith(".ts") || resolved.endsWith(".d.ts")) {
242627
242669
  return "";
242628
242670
  }
242629
- const dir = path.dirname(currentFilePath);
242630
- const resolved = path.resolve(dir, moduleSpecifier.replace(/\.js$/, ".ts"));
242631
- return fs.existsSync(resolved) ? resolved : "";
242671
+ return resolved;
242632
242672
  }
242633
242673
  /**
242634
242674
  * Extract class names from a method returning [new A(), new B(), ...].
@@ -242705,6 +242745,35 @@ var AstReader = class {
242705
242745
  }
242706
242746
  return paths;
242707
242747
  }
242748
+ /**
242749
+ * Collect every identifier an expression references as a value.
242750
+ *
242751
+ * Expressions taken from a provider's `getRoutes()` body are emitted into
242752
+ * the generated data cache verbatim, so the cache must import everything
242753
+ * they name — not just the provider class, but the parameter/enum/constant
242754
+ * classes the route arguments reach for. Property *names* are skipped (the
242755
+ * `HELP` of `CommandName.HELP` is not an import) as are type positions,
242756
+ * which erase at runtime and must never become a value import.
242757
+ */
242758
+ collectReferencedIdentifiers(node, names = /* @__PURE__ */ new Set()) {
242759
+ if (import_ts_morph.ts.isTypeNode(node)) {
242760
+ return names;
242761
+ }
242762
+ if (import_ts_morph.ts.isIdentifier(node)) {
242763
+ names.add(node.text);
242764
+ return names;
242765
+ }
242766
+ if (import_ts_morph.ts.isPropertyAccessExpression(node)) {
242767
+ return this.collectReferencedIdentifiers(node.expression, names);
242768
+ }
242769
+ if (import_ts_morph.ts.isPropertyAssignment(node)) {
242770
+ return this.collectReferencedIdentifiers(node.initializer, names);
242771
+ }
242772
+ import_ts_morph.ts.forEachChild(node, (child) => {
242773
+ this.collectReferencedIdentifiers(child, names);
242774
+ });
242775
+ return names;
242776
+ }
242708
242777
  /**
242709
242778
  * Extract service ID strings from keys of an object-returning method.
242710
242779
  *
@@ -242806,7 +242875,7 @@ var AstReader = class {
242806
242875
  return node.text;
242807
242876
  }
242808
242877
  if (import_ts_morph.ts.isIdentifier(node)) {
242809
- return this.resolveStaticProperty(node.text, void 0, useMap, currentFilePath);
242878
+ return this.resolveConstant(node.text, useMap, currentFilePath);
242810
242879
  }
242811
242880
  if (import_ts_morph.ts.isPropertyAccessExpression(node)) {
242812
242881
  const objName = import_ts_morph.ts.isIdentifier(node.expression) ? node.expression.text : void 0;
@@ -242817,33 +242886,75 @@ var AstReader = class {
242817
242886
  return void 0;
242818
242887
  }
242819
242888
  /**
242820
- * Resolve a static property value by reading the imported source file.
242889
+ * Resolve a static property value by reading the file that declares its class.
242821
242890
  */
242822
242891
  resolveStaticProperty(className, propName, useMap, currentFilePath) {
242823
- const importedFilePath = this.resolveImportToFilePath(className, useMap, currentFilePath);
242824
- if (importedFilePath === "") {
242892
+ const declaringClass = this.parseDeclaringSourceFile(className, useMap, currentFilePath)?.getClass(className);
242893
+ if (declaringClass === void 0) {
242825
242894
  return void 0;
242826
242895
  }
242896
+ const prop = declaringClass.getStaticProperty(propName);
242897
+ if (prop === void 0 || !import_ts_morph.Node.isPropertyDeclaration(prop)) {
242898
+ return void 0;
242899
+ }
242900
+ return this.stringValueOf(prop.getInitializer());
242901
+ }
242902
+ /**
242903
+ * Resolve a module-level `const` to its string value.
242904
+ *
242905
+ * Not every binding key is a class static — some are plain exported
242906
+ * constants, e.g. `export const LoggerContractId = 'Valkyrja.Log.Logger.LoggerContract' as const;`
242907
+ */
242908
+ resolveConstant(name, useMap, currentFilePath) {
242909
+ const declaration = this.parseDeclaringSourceFile(name, useMap, currentFilePath)?.getVariableDeclaration(name);
242910
+ return this.stringValueOf(declaration?.getInitializer());
242911
+ }
242912
+ /**
242913
+ * Parse the file that declares a name: the module it is imported from, or
242914
+ * the current file when the name is not imported.
242915
+ *
242916
+ * A provider commonly declares the binding keys for its own services
242917
+ * alongside the publishers that use them, so `[Provider.SomeId]` names a
242918
+ * constant that no import map can resolve — it lives in the file being read.
242919
+ *
242920
+ * Returns undefined when the file cannot be read.
242921
+ */
242922
+ parseDeclaringSourceFile(name, useMap, currentFilePath) {
242923
+ const importedFilePath = this.resolveImportToFilePath(name, useMap, currentFilePath);
242924
+ const filePath = importedFilePath !== "" ? importedFilePath : currentFilePath;
242827
242925
  try {
242828
242926
  const project = new import_ts_morph.Project({ skipAddingFilesFromTsConfig: true, skipFileDependencyResolution: true });
242829
- const importedSource = project.addSourceFileAtPath(importedFilePath);
242830
- const importedClass = importedSource.getClass(className);
242831
- if (importedClass === void 0 || propName === void 0) {
242832
- return void 0;
242833
- }
242834
- const prop = importedClass.getStaticProperty(propName);
242835
- if (prop === void 0) {
242836
- return void 0;
242837
- }
242838
- if (import_ts_morph.Node.isPropertyDeclaration(prop)) {
242839
- const initializer = prop.getInitializer();
242840
- if (initializer?.isKind(import_ts_morph.SyntaxKind.StringLiteral) === true) {
242841
- return initializer.compilerNode.text;
242842
- }
242843
- }
242927
+ return project.addSourceFileAtPath(filePath);
242844
242928
  } catch {
242929
+ return void 0;
242845
242930
  }
242846
- return void 0;
242931
+ }
242932
+ /**
242933
+ * The string a declaration's initializer holds, once its type-only wrappers are stripped.
242934
+ */
242935
+ stringValueOf(initializer) {
242936
+ if (initializer === void 0) {
242937
+ return void 0;
242938
+ }
242939
+ const value = this.unwrapTypeAssertions(initializer.compilerNode);
242940
+ return import_ts_morph.ts.isStringLiteral(value) ? value.text : void 0;
242941
+ }
242942
+ /**
242943
+ * Strip the type-only wrappers an expression may be dressed in — `as`,
242944
+ * `satisfies` and parentheses — to reach the value node underneath.
242945
+ *
242946
+ * Binding-key constants are declared `static readonly Data = 'Some.Id' as
242947
+ * const;`, so the initializer node is an `AsExpression`, not the
242948
+ * `StringLiteral` holding the value. Without unwrapping, every such key
242949
+ * resolves to nothing and the publishers it names are dropped from the
242950
+ * generated cache.
242951
+ */
242952
+ unwrapTypeAssertions(node) {
242953
+ let current = node;
242954
+ while (import_ts_morph.ts.isAsExpression(current) || import_ts_morph.ts.isSatisfiesExpression(current) || import_ts_morph.ts.isParenthesizedExpression(current)) {
242955
+ current = current.expression;
242956
+ }
242957
+ return current;
242847
242958
  }
242848
242959
  /**
242849
242960
  * Resolve a short class name — returns the name as-is (class names in TS are local).
@@ -244271,16 +244382,24 @@ var import_ts_morph6 = __toESM(require_ts_morph(), 1);
244271
244382
 
244272
244383
  // src/Sindri/Ast/Data/Result/RouteProviderResult.ts
244273
244384
  var RouteProviderResult = class _RouteProviderResult {
244274
- constructor(controllerClasses = [], routes = []) {
244385
+ /**
244386
+ * @param routeImports Local name → absolute file path for every class the
244387
+ * imperative `routes` expressions reference, so the
244388
+ * generated data cache can import them.
244389
+ */
244390
+ constructor(controllerClasses = [], routes = [], routeImports = {}) {
244275
244391
  this.controllerClasses = controllerClasses;
244276
244392
  this.routes = routes;
244393
+ this.routeImports = routeImports;
244277
244394
  }
244278
244395
  controllerClasses;
244279
244396
  routes;
244397
+ routeImports;
244280
244398
  merge(other) {
244281
244399
  return new _RouteProviderResult(
244282
244400
  [.../* @__PURE__ */ new Set([...this.controllerClasses, ...other.controllerClasses])],
244283
- [...this.routes, ...other.routes]
244401
+ [...this.routes, ...other.routes],
244402
+ { ...this.routeImports, ...other.routeImports }
244284
244403
  );
244285
244404
  }
244286
244405
  };
@@ -244297,11 +244416,33 @@ var RouteProviderReader = class _RouteProviderReader extends AstReader {
244297
244416
  }
244298
244417
  const useMap = this.buildUseMap(sourceFile);
244299
244418
  const methods = this.indexMethods(classDecl);
244419
+ const routes = this.extractRoutes(methods[_RouteProviderReader.METHOD_ROUTES], useMap, filePath);
244300
244420
  return new RouteProviderResult(
244301
244421
  this.extractClassListFromValues(methods[_RouteProviderReader.METHOD_CONTROLLER_CLASSES], useMap, filePath),
244302
- this.extractRoutes(methods[_RouteProviderReader.METHOD_ROUTES], useMap, filePath)
244422
+ routes,
244423
+ this.extractRouteImports(routes, useMap, filePath)
244303
244424
  );
244304
244425
  }
244426
+ /**
244427
+ * Resolve every class the imperative route expressions reference to its
244428
+ * absolute file path, so the generated data cache can import them.
244429
+ *
244430
+ * Names that are not imported by the provider file — locals, globals, and
244431
+ * the provider's own class (which the generator imports separately) — have
244432
+ * no entry in the use map and are skipped.
244433
+ */
244434
+ extractRouteImports(routes, useMap, filePath) {
244435
+ const imports = {};
244436
+ for (const route2 of routes) {
244437
+ for (const name of this.collectReferencedIdentifiers(route2)) {
244438
+ const resolved = this.resolveImportToFilePath(name, useMap, filePath);
244439
+ if (resolved !== "") {
244440
+ imports[name] = resolved;
244441
+ }
244442
+ }
244443
+ }
244444
+ return imports;
244445
+ }
244305
244446
  /**
244306
244447
  * Extract the route objects returned by a provider's `getRoutes()` method.
244307
244448
  *
@@ -244443,12 +244584,23 @@ var AstFileGenerator = class {
244443
244584
  * Build the `import { X } from '...';` block for the provider/handler classes
244444
244585
  * a generated data cache references, followed by a trailing newline (or an
244445
244586
  * empty string when there are no such imports).
244587
+ *
244588
+ * Names the generated file's fixed framework header already binds are
244589
+ * dropped: those are the same framework classes under the same names, and
244590
+ * emitting them twice is a duplicate-identifier error.
244446
244591
  */
244447
- buildUserImportsBlock(classImportMap) {
244448
- const userImports = Object.entries(classImportMap).map(([name, specifier]) => `import { ${name} } from '${specifier}';`).join("\n");
244592
+ buildUserImportsBlock(classImportMap, reservedNames) {
244593
+ const userImports = Object.entries(classImportMap).filter(([name]) => !reservedNames.includes(name)).map(([name, specifier]) => `import { ${name} } from '${specifier}';`).join("\n");
244449
244594
  return userImports ? `${userImports}
244450
244595
  ` : "";
244451
244596
  }
244597
+ /**
244598
+ * Render the fixed framework imports a generated data file always carries.
244599
+ */
244600
+ buildFrameworkImportLines(imports, isType = false) {
244601
+ const keyword = isType ? "import type" : "import";
244602
+ return Object.entries(imports).map(([name, specifier]) => `${keyword} { ${name} } from '${specifier}';`);
244603
+ }
244452
244604
  writeFile(directory, className, data) {
244453
244605
  const filePath = directory.replace(/\/$/, "") + `/${className}.ts`;
244454
244606
  try {
@@ -244466,7 +244618,22 @@ var AstFileGenerator = class {
244466
244618
  };
244467
244619
 
244468
244620
  // src/Sindri/Generator/Ast/Cli/AstCliDataFileGenerator.ts
244469
- var AstCliDataFileGenerator = class extends AstFileGenerator {
244621
+ var AstCliDataFileGenerator = class _AstCliDataFileGenerator extends AstFileGenerator {
244622
+ /** The framework classes every generated CLI data file imports as values. */
244623
+ static FRAMEWORK_IMPORTS = {
244624
+ CliRoutingData: "@valkyrjaio/valkyrja/Cli/Routing/Data/CliRoutingData.ts",
244625
+ Route: "@valkyrjaio/valkyrja/Cli/Routing/Data/Route.ts",
244626
+ ArgumentParameter: "@valkyrjaio/valkyrja/Cli/Routing/Data/ArgumentParameter.ts",
244627
+ OptionParameter: "@valkyrjaio/valkyrja/Cli/Routing/Data/OptionParameter.ts",
244628
+ ArgumentMode: "@valkyrjaio/valkyrja/Cli/Routing/Enum/ArgumentMode.ts",
244629
+ ArgumentValueMode: "@valkyrjaio/valkyrja/Cli/Routing/Enum/ArgumentValueMode.ts",
244630
+ OptionMode: "@valkyrjaio/valkyrja/Cli/Routing/Enum/OptionMode.ts",
244631
+ OptionValueMode: "@valkyrjaio/valkyrja/Cli/Routing/Enum/OptionValueMode.ts"
244632
+ };
244633
+ /** The framework types every generated CLI data file imports. */
244634
+ static FRAMEWORK_TYPE_IMPORTS = {
244635
+ RouteContract: "@valkyrjaio/valkyrja/Cli/Routing/Data/Contract/RouteContract.ts"
244636
+ };
244470
244637
  classImportMap = {};
244471
244638
  printer = import_ts_morph9.ts.createPrinter({ newLine: import_ts_morph9.ts.NewLineKind.LineFeed });
244472
244639
  dummySourceFile = import_ts_morph9.ts.createSourceFile(
@@ -244477,20 +244644,16 @@ var AstCliDataFileGenerator = class extends AstFileGenerator {
244477
244644
  import_ts_morph9.ts.ScriptKind.TS
244478
244645
  );
244479
244646
  generateFile(directory, className, namespace, routes) {
244480
- const userImportsBlock = this.buildUserImportsBlock(this.classImportMap);
244647
+ const userImportsBlock = this.buildUserImportsBlock(this.classImportMap, [
244648
+ ...Object.keys(_AstCliDataFileGenerator.FRAMEWORK_IMPORTS),
244649
+ ...Object.keys(_AstCliDataFileGenerator.FRAMEWORK_TYPE_IMPORTS)
244650
+ ]);
244481
244651
  const contents = this.generateClassContents(routes);
244482
244652
  const data = [
244483
244653
  "// This file was automatically generated by Sindri.",
244484
244654
  "",
244485
- `import { CliRoutingData } from '@valkyrjaio/valkyrja/Cli/Routing/Data/CliRoutingData.ts';`,
244486
- `import { Route } from '@valkyrjaio/valkyrja/Cli/Routing/Data/Route.ts';`,
244487
- `import { ArgumentParameter } from '@valkyrjaio/valkyrja/Cli/Routing/Data/ArgumentParameter.ts';`,
244488
- `import { OptionParameter } from '@valkyrjaio/valkyrja/Cli/Routing/Data/OptionParameter.ts';`,
244489
- `import { ArgumentMode } from '@valkyrjaio/valkyrja/Cli/Routing/Enum/ArgumentMode.ts';`,
244490
- `import { ArgumentValueMode } from '@valkyrjaio/valkyrja/Cli/Routing/Enum/ArgumentValueMode.ts';`,
244491
- `import { OptionMode } from '@valkyrjaio/valkyrja/Cli/Routing/Enum/OptionMode.ts';`,
244492
- `import { OptionValueMode } from '@valkyrjaio/valkyrja/Cli/Routing/Enum/OptionValueMode.ts';`,
244493
- `import type { RouteContract } from '@valkyrjaio/valkyrja/Cli/Routing/Data/Contract/RouteContract.ts';`,
244655
+ ...this.buildFrameworkImportLines(_AstCliDataFileGenerator.FRAMEWORK_IMPORTS),
244656
+ ...this.buildFrameworkImportLines(_AstCliDataFileGenerator.FRAMEWORK_TYPE_IMPORTS, true),
244494
244657
  userImportsBlock,
244495
244658
  `export class ${className} extends CliRoutingData {`,
244496
244659
  ` constructor() {`,
@@ -244512,14 +244675,35 @@ var AstCliDataFileGenerator = class extends AstFileGenerator {
244512
244675
  if (!import_ts_morph9.ts.isNewExpression(expr)) {
244513
244676
  continue;
244514
244677
  }
244515
- const nameArg = (expr.arguments ?? [])[0];
244516
- if (nameArg === void 0 || !import_ts_morph9.ts.isStringLiteral(nameArg)) {
244678
+ const key = this.routeKey((expr.arguments ?? [])[0]);
244679
+ if (key === void 0) {
244517
244680
  continue;
244518
244681
  }
244519
- routes[nameArg.text] = expr;
244682
+ routes[key] = expr;
244520
244683
  }
244521
244684
  return this.generateFile(directory, className, namespace, routes);
244522
244685
  }
244686
+ /**
244687
+ * Derive the map key for a route from its name argument.
244688
+ *
244689
+ * A literal name keys the map by its own text. A constant reference
244690
+ * (`CliCommandName.LIST` — how the framework's own commands name
244691
+ * themselves) is kept as a `Class::CASE` marker, so the generated file
244692
+ * emits the constant reference as a computed key rather than baking in the
244693
+ * value it happens to hold today.
244694
+ */
244695
+ routeKey(nameArg) {
244696
+ if (nameArg === void 0) {
244697
+ return void 0;
244698
+ }
244699
+ if (import_ts_morph9.ts.isStringLiteral(nameArg)) {
244700
+ return nameArg.text;
244701
+ }
244702
+ if (import_ts_morph9.ts.isPropertyAccessExpression(nameArg) && import_ts_morph9.ts.isIdentifier(nameArg.expression)) {
244703
+ return `${nameArg.expression.text}::${nameArg.name.text}`;
244704
+ }
244705
+ return void 0;
244706
+ }
244523
244707
  generateClassContents(routes) {
244524
244708
  const entries = Object.entries(routes);
244525
244709
  if (entries.length === 0) {
@@ -244545,7 +244729,15 @@ var AstCliDataFileGenerator = class extends AstFileGenerator {
244545
244729
 
244546
244730
  // src/Sindri/Generator/Ast/Container/AstContainerDataFileGenerator.ts
244547
244731
  var import_ts_morph10 = __toESM(require_ts_morph(), 1);
244548
- var AstContainerDataFileGenerator = class extends AstFileGenerator {
244732
+ var AstContainerDataFileGenerator = class _AstContainerDataFileGenerator extends AstFileGenerator {
244733
+ /** The framework class every generated container data file imports as a value. */
244734
+ static FRAMEWORK_IMPORTS = {
244735
+ ContainerData: "@valkyrjaio/valkyrja/Container/Data/ContainerData.ts"
244736
+ };
244737
+ /** The framework type every generated container data file imports. */
244738
+ static FRAMEWORK_TYPE_IMPORTS = {
244739
+ ContainerContract: "@valkyrjaio/valkyrja/Container/Manager/Contract/ContainerContract.ts"
244740
+ };
244549
244741
  classImportMap = {};
244550
244742
  printer = import_ts_morph10.ts.createPrinter({ newLine: import_ts_morph10.ts.NewLineKind.LineFeed });
244551
244743
  dummySourceFile = import_ts_morph10.ts.createSourceFile(
@@ -244556,13 +244748,16 @@ var AstContainerDataFileGenerator = class extends AstFileGenerator {
244556
244748
  import_ts_morph10.ts.ScriptKind.TS
244557
244749
  );
244558
244750
  generateFile(directory, className, namespace, publishers) {
244559
- const userImportsBlock = this.buildUserImportsBlock(this.classImportMap);
244751
+ const userImportsBlock = this.buildUserImportsBlock(this.classImportMap, [
244752
+ ...Object.keys(_AstContainerDataFileGenerator.FRAMEWORK_IMPORTS),
244753
+ ...Object.keys(_AstContainerDataFileGenerator.FRAMEWORK_TYPE_IMPORTS)
244754
+ ]);
244560
244755
  const contents = this.generateClassContents(publishers);
244561
244756
  const data = [
244562
244757
  "// This file was automatically generated by Sindri.",
244563
244758
  "",
244564
- `import { ContainerData } from '@valkyrjaio/valkyrja/Container/Data/ContainerData.ts';`,
244565
- `import type { ContainerContract } from '@valkyrjaio/valkyrja/Container/Manager/Contract/ContainerContract.ts';`,
244759
+ ...this.buildFrameworkImportLines(_AstContainerDataFileGenerator.FRAMEWORK_IMPORTS),
244760
+ ...this.buildFrameworkImportLines(_AstContainerDataFileGenerator.FRAMEWORK_TYPE_IMPORTS, true),
244566
244761
  userImportsBlock,
244567
244762
  `export class ${className} extends ContainerData {`,
244568
244763
  ` constructor() {`,
@@ -244594,7 +244789,19 @@ var AstContainerDataFileGenerator = class extends AstFileGenerator {
244594
244789
 
244595
244790
  // src/Sindri/Generator/Ast/Event/AstEventDataFileGenerator.ts
244596
244791
  var import_ts_morph11 = __toESM(require_ts_morph(), 1);
244597
- var AstEventDataFileGenerator = class extends AstFileGenerator {
244792
+ var AstEventDataFileGenerator = class _AstEventDataFileGenerator extends AstFileGenerator {
244793
+ /** The framework class every generated event data file imports as a value. */
244794
+ static FRAMEWORK_IMPORTS = {
244795
+ EventData: "@valkyrjaio/valkyrja/Event/Data/EventData.ts"
244796
+ };
244797
+ /** The framework classes a generated event data file imports only when it holds listeners. */
244798
+ static LISTENER_IMPORTS = {
244799
+ Listener: "@valkyrjaio/valkyrja/Event/Data/Listener.ts"
244800
+ };
244801
+ /** The framework types a generated event data file imports only when it holds listeners. */
244802
+ static LISTENER_TYPE_IMPORTS = {
244803
+ ListenerContract: "@valkyrjaio/valkyrja/Event/Data/Contract/ListenerContract.ts"
244804
+ };
244598
244805
  classImportMap = {};
244599
244806
  printer = import_ts_morph11.ts.createPrinter({ newLine: import_ts_morph11.ts.NewLineKind.LineFeed });
244600
244807
  dummySourceFile = import_ts_morph11.ts.createSourceFile(
@@ -244605,16 +244812,20 @@ var AstEventDataFileGenerator = class extends AstFileGenerator {
244605
244812
  import_ts_morph11.ts.ScriptKind.TS
244606
244813
  );
244607
244814
  generateFile(directory, className, namespace, listeners) {
244608
- const userImportsBlock = this.buildUserImportsBlock(this.classImportMap);
244815
+ const userImportsBlock = this.buildUserImportsBlock(this.classImportMap, [
244816
+ ...Object.keys(_AstEventDataFileGenerator.FRAMEWORK_IMPORTS),
244817
+ ...Object.keys(_AstEventDataFileGenerator.LISTENER_IMPORTS),
244818
+ ...Object.keys(_AstEventDataFileGenerator.LISTENER_TYPE_IMPORTS)
244819
+ ]);
244609
244820
  const contents = this.generateClassContents(listeners);
244610
244821
  const listenerImports = Object.keys(listeners).length > 0 ? [
244611
- `import { Listener } from '@valkyrjaio/valkyrja/Event/Data/Listener.ts';`,
244612
- `import type { ListenerContract } from '@valkyrjaio/valkyrja/Event/Data/Contract/ListenerContract.ts';`
244822
+ ...this.buildFrameworkImportLines(_AstEventDataFileGenerator.LISTENER_IMPORTS),
244823
+ ...this.buildFrameworkImportLines(_AstEventDataFileGenerator.LISTENER_TYPE_IMPORTS, true)
244613
244824
  ] : [];
244614
244825
  const data = [
244615
244826
  "// This file was automatically generated by Sindri.",
244616
244827
  "",
244617
- `import { EventData } from '@valkyrjaio/valkyrja/Event/Data/EventData.ts';`,
244828
+ ...this.buildFrameworkImportLines(_AstEventDataFileGenerator.FRAMEWORK_IMPORTS),
244618
244829
  ...listenerImports,
244619
244830
  userImportsBlock,
244620
244831
  `export class ${className} extends EventData {`,
@@ -245015,8 +245226,8 @@ var Regex = class _Regex {
245015
245226
  static ALPHA_UPPERCASE = "[A-Z]+";
245016
245227
  static ALPHA_NUM = "[a-zA-Z0-9]+";
245017
245228
  static ALPHA_NUM_UNDERSCORE = "\\w+";
245018
- static START = "/^";
245019
- static END = "$/";
245229
+ static START = "^";
245230
+ static END = "$";
245020
245231
  static START_CAPTURE_GROUP = "(";
245021
245232
  static START_NON_CAPTURE_GROUP = "(?:";
245022
245233
  static END_CAPTURE_GROUP = ")";
@@ -245108,7 +245319,20 @@ var GeneratorUnreachableException = class extends GeneratorRuntimeException {
245108
245319
  };
245109
245320
 
245110
245321
  // src/Sindri/Generator/Ast/Http/AstHttpDataFileGenerator.ts
245111
- var AstHttpDataFileGenerator = class extends AstFileGenerator {
245322
+ var AstHttpDataFileGenerator = class _AstHttpDataFileGenerator extends AstFileGenerator {
245323
+ /** The framework classes every generated HTTP data file imports as values. */
245324
+ static FRAMEWORK_IMPORTS = {
245325
+ HttpRoutingData: "@valkyrjaio/valkyrja/Http/Routing/Data/HttpRoutingData.ts",
245326
+ DynamicRoute: "@valkyrjaio/valkyrja/Http/Routing/Data/DynamicRoute.ts",
245327
+ Route: "@valkyrjaio/valkyrja/Http/Routing/Data/Route.ts",
245328
+ Parameter: "@valkyrjaio/valkyrja/Http/Routing/Data/Parameter.ts",
245329
+ RequestMethod: "@valkyrjaio/valkyrja/Http/Message/Enum/RequestMethod.ts",
245330
+ Regex: "@valkyrjaio/valkyrja/Http/Routing/Constant/Regex.ts"
245331
+ };
245332
+ /** The framework types every generated HTTP data file imports. */
245333
+ static FRAMEWORK_TYPE_IMPORTS = {
245334
+ RouteContract: "@valkyrjaio/valkyrja/Http/Routing/Data/Contract/RouteContract.ts"
245335
+ };
245112
245336
  classImportMap = {};
245113
245337
  processor;
245114
245338
  printer = import_ts_morph12.ts.createPrinter({ newLine: import_ts_morph12.ts.NewLineKind.LineFeed });
@@ -245124,7 +245348,10 @@ var AstHttpDataFileGenerator = class extends AstFileGenerator {
245124
245348
  this.processor = processor;
245125
245349
  }
245126
245350
  generateFile(directory, className, namespace, routes, routeData) {
245127
- const userImportsBlock = this.buildUserImportsBlock(this.classImportMap);
245351
+ const userImportsBlock = this.buildUserImportsBlock(this.classImportMap, [
245352
+ ...Object.keys(_AstHttpDataFileGenerator.FRAMEWORK_IMPORTS),
245353
+ ...Object.keys(_AstHttpDataFileGenerator.FRAMEWORK_TYPE_IMPORTS)
245354
+ ]);
245128
245355
  const routesContent = this.getRoutesAsContent(routes, routeData);
245129
245356
  const paths = this.printNestedObject(this.buildPaths(routeData));
245130
245357
  const dynamicPaths = this.printNestedObject(this.buildDynamicPaths(routeData));
@@ -245142,7 +245369,10 @@ var AstHttpDataFileGenerator = class extends AstFileGenerator {
245142
245369
  * (literal path and regex, request methods defaulting to `[HEAD, GET]`).
245143
245370
  */
245144
245371
  generateFileFromRoutes(directory, className, _namespace, routeExprs) {
245145
- const userImportsBlock = this.buildUserImportsBlock(this.classImportMap);
245372
+ const userImportsBlock = this.buildUserImportsBlock(this.classImportMap, [
245373
+ ...Object.keys(_AstHttpDataFileGenerator.FRAMEWORK_IMPORTS),
245374
+ ...Object.keys(_AstHttpDataFileGenerator.FRAMEWORK_TYPE_IMPORTS)
245375
+ ]);
245146
245376
  const metas = routeExprs.map((expr) => this.extractRouteMeta(expr)).filter((meta) => meta !== void 0);
245147
245377
  const routesContent = this.getImperativeRoutesContent(metas);
245148
245378
  const paths = this.printNestedObject(this.buildImperativePaths(metas, false));
@@ -245155,13 +245385,8 @@ var AstHttpDataFileGenerator = class extends AstFileGenerator {
245155
245385
  return [
245156
245386
  "// This file was automatically generated by Sindri.",
245157
245387
  "",
245158
- `import { HttpRoutingData } from '@valkyrjaio/valkyrja/Http/Routing/Data/HttpRoutingData.ts';`,
245159
- `import { DynamicRoute } from '@valkyrjaio/valkyrja/Http/Routing/Data/DynamicRoute.ts';`,
245160
- `import { Route } from '@valkyrjaio/valkyrja/Http/Routing/Data/Route.ts';`,
245161
- `import { Parameter } from '@valkyrjaio/valkyrja/Http/Routing/Data/Parameter.ts';`,
245162
- `import { RequestMethod } from '@valkyrjaio/valkyrja/Http/Message/Enum/RequestMethod.ts';`,
245163
- `import { Regex } from '@valkyrjaio/valkyrja/Http/Routing/Constant/Regex.ts';`,
245164
- `import type { RouteContract } from '@valkyrjaio/valkyrja/Http/Routing/Data/Contract/RouteContract.ts';`,
245388
+ ...this.buildFrameworkImportLines(_AstHttpDataFileGenerator.FRAMEWORK_IMPORTS),
245389
+ ...this.buildFrameworkImportLines(_AstHttpDataFileGenerator.FRAMEWORK_TYPE_IMPORTS, true),
245165
245390
  userImportsBlock,
245166
245391
  `export class ${className} extends HttpRoutingData {`,
245167
245392
  ` constructor() {`,
@@ -245459,8 +245684,8 @@ var WarningFormatter = class extends Formatter {
245459
245684
 
245460
245685
  // node_modules/@valkyrjaio/valkyrja/src/Valkyrja/Application/Constant/ApplicationInfo.ts
245461
245686
  var ApplicationInfo = class {
245462
- static VERSION = "26.3.0";
245463
- static VERSION_BUILD_DATE_TIME = "July 25 2026 20:40:59 MST";
245687
+ static VERSION = "26.4.0";
245688
+ static VERSION_BUILD_DATE_TIME = "July 27 2026 22:05:07 MST";
245464
245689
  static ASCII = `
245465
245690
  _ _ _
245466
245691
  /\\ /\\__ _| | | ___ _ _ __(_) __ _
@@ -245569,8 +245794,8 @@ var Header = class extends Message {
245569
245794
 
245570
245795
  // src/Sindri/Constant/SindriInfo.ts
245571
245796
  var SindriInfo = class {
245572
- static VERSION = "26.3.0";
245573
- static VERSION_BUILD_DATE_TIME = "July 25 2026 21:11:03 MST";
245797
+ static VERSION = "26.4.0";
245798
+ static VERSION_BUILD_DATE_TIME = "July 27 2026 22:16:03 MST";
245574
245799
  static ICON = ` \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588
245575
245800
  \u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588
245576
245801
  \u2588\u2588\u2588\u2588
@@ -245725,6 +245950,117 @@ var GenerateDataFromAst = class {
245725
245950
  const relative2 = path3.relative(fromDir, toFile).split(path3.sep).join("/");
245726
245951
  return relative2.startsWith(".") ? relative2 : `./${relative2}`;
245727
245952
  }
245953
+ /**
245954
+ * Build the specifier a generated data file should import a class file by.
245955
+ *
245956
+ * Application source is imported by a path relative to the data directory;
245957
+ * a file belonging to an installed package is imported by that package's
245958
+ * own public specifier — so the framework classes the cache references are
245959
+ * written exactly as hand-written application code writes them
245960
+ * (`@valkyrjaio/valkyrja/Cli/Routing/Data/Route.ts`), never as a brittle
245961
+ * reach into `node_modules`.
245962
+ */
245963
+ importSpecifier(fromDir, toFile) {
245964
+ return this.packageSpecifier(toFile) ?? this.relativeSpecifier(fromDir, toFile);
245965
+ }
245966
+ /**
245967
+ * Map an absolute path inside an installed package back to the specifier
245968
+ * that package publicly exposes it as, or undefined when the file is not
245969
+ * part of one.
245970
+ *
245971
+ * The package's `exports` map is inverted: for `"./*.ts": "./src/*.ts"` a
245972
+ * file at `<package>/src/Foo.ts` maps back to `<name>/Foo.ts`. Packages
245973
+ * with no matching pattern fall back to the path as written under the
245974
+ * package root, which is what a package without an `exports` map exposes.
245975
+ */
245976
+ packageSpecifier(filePath) {
245977
+ const normalized = filePath.split(path3.sep).join("/");
245978
+ const marker = normalized.lastIndexOf("/node_modules/");
245979
+ if (marker === -1) {
245980
+ return void 0;
245981
+ }
245982
+ const subject = normalized.substring(marker + "/node_modules/".length);
245983
+ const segments = subject.split("/");
245984
+ const nameLength = subject.startsWith("@") ? 2 : 1;
245985
+ if (segments.length <= nameLength) {
245986
+ return void 0;
245987
+ }
245988
+ const packageName = segments.slice(0, nameLength).join("/");
245989
+ const packageDir = normalized.substring(0, marker + "/node_modules/".length) + packageName;
245990
+ const relative2 = segments.slice(nameLength).join("/");
245991
+ return `${packageName}/${this.exportedSubpath(packageDir, relative2) ?? relative2}`;
245992
+ }
245993
+ /**
245994
+ * Invert a package's `exports` patterns to find the subpath a file inside
245995
+ * it is exposed as, or undefined when no pattern matches it.
245996
+ */
245997
+ exportedSubpath(packageDir, relative2) {
245998
+ for (const [key, target] of Object.entries(this.readPackageExports(packageDir))) {
245999
+ const star = key.indexOf("*");
246000
+ const targetStar = target.indexOf("*");
246001
+ if (star === -1 || targetStar === -1) {
246002
+ continue;
246003
+ }
246004
+ const prefix = target.substring(0, targetStar).replace(/^\.\//, "");
246005
+ const suffix = target.substring(targetStar + 1);
246006
+ if (!relative2.startsWith(prefix) || !relative2.endsWith(suffix)) {
246007
+ continue;
246008
+ }
246009
+ const value = relative2.substring(prefix.length, relative2.length - suffix.length);
246010
+ return (key.substring(0, star) + value + key.substring(star + 1)).replace(/^\.\//, "");
246011
+ }
246012
+ return void 0;
246013
+ }
246014
+ /**
246015
+ * Read a package's `exports` map, flattened to subpath → file target.
246016
+ *
246017
+ * Conditional targets are collapsed to their first string value; every
246018
+ * condition a package declares for a subpath points at the same source
246019
+ * file in the layouts sindri generates for.
246020
+ */
246021
+ readPackageExports(packageDir) {
246022
+ const manifestPath = path3.join(packageDir, "package.json");
246023
+ if (!fs3.existsSync(manifestPath)) {
246024
+ return {};
246025
+ }
246026
+ let manifest;
246027
+ try {
246028
+ manifest = JSON.parse(fs3.readFileSync(manifestPath, "utf-8"));
246029
+ } catch {
246030
+ return {};
246031
+ }
246032
+ const exports2 = manifest.exports;
246033
+ if (typeof exports2 !== "object" || exports2 === null) {
246034
+ return {};
246035
+ }
246036
+ const flattened = {};
246037
+ for (const [subpath, target] of Object.entries(exports2)) {
246038
+ const resolved = this.firstStringTarget(target);
246039
+ if (resolved !== void 0) {
246040
+ flattened[subpath] = resolved;
246041
+ }
246042
+ }
246043
+ return flattened;
246044
+ }
246045
+ /**
246046
+ * Reduce an `exports` target — a string, or a nested map of conditions — to
246047
+ * the first file path it names.
246048
+ */
246049
+ firstStringTarget(target) {
246050
+ if (typeof target === "string") {
246051
+ return target;
246052
+ }
246053
+ if (typeof target !== "object" || target === null) {
246054
+ return void 0;
246055
+ }
246056
+ for (const value of Object.values(target)) {
246057
+ const resolved = this.firstStringTarget(value);
246058
+ if (resolved !== void 0) {
246059
+ return resolved;
246060
+ }
246061
+ }
246062
+ return void 0;
246063
+ }
245728
246064
  generateContainerData(serviceProviders, config, output) {
245729
246065
  output = output.withAddedMessages(new Message("Generating Container Data......................")).writeMessages();
245730
246066
  const publishers = {};
@@ -245736,7 +246072,7 @@ var GenerateDataFromAst = class {
245736
246072
  }
245737
246073
  const result = this.serviceProviderReader.readFile(filePath);
245738
246074
  if (Object.keys(result.publishers).length > 0) {
245739
- importMap[path3.basename(filePath, ".ts")] = this.relativeSpecifier(config.dataPath, filePath);
246075
+ importMap[path3.basename(filePath, ".ts")] = this.importSpecifier(config.dataPath, filePath);
245740
246076
  }
245741
246077
  Object.assign(publishers, result.publishers);
245742
246078
  }
@@ -245788,7 +246124,10 @@ var GenerateDataFromAst = class {
245788
246124
  const providerResult = this.routeProviderReader.readFile(filePath);
245789
246125
  if (providerResult.routes.length > 0) {
245790
246126
  imperativeRoutes.push(...providerResult.routes);
245791
- importMap[path3.basename(filePath, ".ts")] = this.relativeSpecifier(config.dataPath, filePath);
246127
+ importMap[path3.basename(filePath, ".ts")] = this.importSpecifier(config.dataPath, filePath);
246128
+ for (const [name, importPath] of Object.entries(providerResult.routeImports)) {
246129
+ importMap[name] = this.importSpecifier(config.dataPath, importPath);
246130
+ }
245792
246131
  }
245793
246132
  for (const controllerClass of providerResult.controllerClasses) {
245794
246133
  const controllerPath = this.fqnToFilePath(controllerClass, config.namespace, config.dir);
@@ -245822,7 +246161,10 @@ var GenerateDataFromAst = class {
245822
246161
  const providerResult = this.routeProviderReader.readFile(filePath);
245823
246162
  if (providerResult.routes.length > 0) {
245824
246163
  imperativeRoutes.push(...providerResult.routes);
245825
- importMap[path3.basename(filePath, ".ts")] = this.relativeSpecifier(config.dataPath, filePath);
246164
+ importMap[path3.basename(filePath, ".ts")] = this.importSpecifier(config.dataPath, filePath);
246165
+ for (const [name, importPath] of Object.entries(providerResult.routeImports)) {
246166
+ importMap[name] = this.importSpecifier(config.dataPath, importPath);
246167
+ }
245826
246168
  }
245827
246169
  for (const controllerClass of providerResult.controllerClasses) {
245828
246170
  const controllerPath = this.fqnToFilePath(controllerClass, config.namespace, config.dir);