@valkyrjaio/sindri 26.5.1 → 26.6.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.
@@ -15,18 +15,22 @@ import { HttpRouteAttributeResult } from './Data/Result/HttpRouteAttributeResult
15
15
  import { HttpRouteMiddlewareReader } from './HttpRouteMiddlewareReader.ts';
16
16
  import { HttpRouteParameterReader } from './HttpRouteParameterReader.ts';
17
17
 
18
- import type { ClassDeclaration, Decorator, MethodDeclaration, Node } from 'ts-morph';
18
+ import type { ClassDeclaration, Decorator, MethodDeclaration } from 'ts-morph';
19
19
 
20
20
  import type { HttpRouteAttributeReaderContract } from './Contract/HttpRouteAttributeReaderContract.ts';
21
21
  import type { HttpRouteMiddlewareReaderContract } from './Contract/HttpRouteMiddlewareReaderContract.ts';
22
22
  import type { HttpRouteParameterReaderContract } from './Contract/HttpRouteParameterReaderContract.ts';
23
+ import type { HandlerData } from './Data/HandlerData.ts';
23
24
 
24
25
  /**
25
26
  * Scans an HTTP controller class file for @Route / @DynamicRoute and related
26
27
  * sub-decorators and returns TypeScript compiler API Expr nodes ready for the data cache generator.
27
28
  *
28
- * Mirrors the logic of the framework's runtime AttributeRouteCollector but operates
29
- * entirely on AST without executing any TypeScript code.
29
+ * The shipped decorators accept a single options object
30
+ * (`@Route({ path, name, ... })`), so this reader parses object-literal
31
+ * properties rather than positional arguments. It mirrors the logic of the
32
+ * framework's runtime AttributeRouteCollector but operates entirely on AST
33
+ * without executing any TypeScript code.
30
34
  */
31
35
  export class HttpRouteAttributeReader extends RouteAttributeReader implements HttpRouteAttributeReaderContract {
32
36
  constructor(
@@ -112,7 +116,7 @@ export class HttpRouteAttributeReader extends RouteAttributeReader implements Ht
112
116
 
113
117
  for (const method of classDecl.getMethods()) {
114
118
  for (const decorator of this.findDecoratorsOnNode(method, 'Route', useMap, currentFilePath)) {
115
- const data = this.buildRouteData(
119
+ this.collectRoute(
116
120
  decorator,
117
121
  method,
118
122
  useMap,
@@ -121,16 +125,13 @@ export class HttpRouteAttributeReader extends RouteAttributeReader implements Ht
121
125
  classPathPrefix,
122
126
  classNamePrefix,
123
127
  false,
128
+ routes,
129
+ routeData,
124
130
  );
125
-
126
- if (data !== null) {
127
- routes[data.name] = this.buildRouteExpr(data);
128
- routeData[data.name] = data;
129
- }
130
131
  }
131
132
 
132
133
  for (const decorator of this.findDecoratorsOnNode(method, 'DynamicRoute', useMap, currentFilePath)) {
133
- const data = this.buildRouteData(
134
+ this.collectRoute(
134
135
  decorator,
135
136
  method,
136
137
  useMap,
@@ -139,16 +140,44 @@ export class HttpRouteAttributeReader extends RouteAttributeReader implements Ht
139
140
  classPathPrefix,
140
141
  classNamePrefix,
141
142
  true,
143
+ routes,
144
+ routeData,
142
145
  );
143
-
144
- if (data !== null) {
145
- routes[data.name] = this.buildRouteExpr(data);
146
- routeData[data.name] = data;
147
- }
148
146
  }
149
147
  }
150
148
 
151
- return new HttpRouteAttributeResult(routes, routeData);
149
+ const importMap = this.buildImportMap(routeData, useMap, currentFilePath, currentClass);
150
+
151
+ return new HttpRouteAttributeResult(routes, routeData, importMap);
152
+ }
153
+
154
+ protected collectRoute(
155
+ decorator: Decorator,
156
+ method: MethodDeclaration,
157
+ useMap: Record<string, string>,
158
+ currentFilePath: string,
159
+ currentClass: string,
160
+ classPathPrefix: string,
161
+ classNamePrefix: string,
162
+ isDynamic: boolean,
163
+ routes: Record<string, ts.Expression>,
164
+ routeData: Record<string, HttpRouteData>,
165
+ ): void {
166
+ const data = this.buildRouteData(
167
+ decorator,
168
+ method,
169
+ useMap,
170
+ currentFilePath,
171
+ currentClass,
172
+ classPathPrefix,
173
+ classNamePrefix,
174
+ isDynamic,
175
+ );
176
+
177
+ if (data !== null) {
178
+ routes[data.name] = this.buildRouteExpr(data);
179
+ routeData[data.name] = data;
180
+ }
152
181
  }
153
182
 
154
183
  protected buildRouteData(
@@ -161,8 +190,14 @@ export class HttpRouteAttributeReader extends RouteAttributeReader implements Ht
161
190
  classNamePrefix: string,
162
191
  isDynamic: boolean,
163
192
  ): HttpRouteData | null {
164
- const path = this.extractStringArg(decorator, 0, useMap, currentFilePath, currentClass);
165
- const name = this.extractStringArg(decorator, 1, useMap, currentFilePath, currentClass);
193
+ const obj = this.getDecoratorObjectArg(decorator);
194
+
195
+ if (obj === undefined) {
196
+ return null;
197
+ }
198
+
199
+ const path = this.getObjectStringProp(obj, 'path', useMap, currentFilePath, currentClass);
200
+ const name = this.getObjectStringProp(obj, 'name', useMap, currentFilePath, currentClass);
166
201
 
167
202
  if (path === '' || name === '') {
168
203
  return null;
@@ -173,10 +208,8 @@ export class HttpRouteAttributeReader extends RouteAttributeReader implements Ht
173
208
 
174
209
  const resolvedIsDynamic = isDynamic || updatedPath.includes('{');
175
210
 
176
- const decoratorArgs = decorator.getArguments().map((a: Node) => a.compilerNode as ts.Expression);
177
-
178
211
  const requestMethods = this.middlewareReader.updateRequestMethods(
179
- this.middlewareReader.extractInlineRequestMethods(decoratorArgs, useMap, currentFilePath, currentClass),
212
+ this.middlewareReader.extractObjectRequestMethods(obj, useMap, currentFilePath, currentClass),
180
213
  method,
181
214
  useMap,
182
215
  currentFilePath,
@@ -194,32 +227,84 @@ export class HttpRouteAttributeReader extends RouteAttributeReader implements Ht
194
227
  useMap,
195
228
  currentFilePath,
196
229
  currentClass,
197
- this.extractClassListArgFromDecorator(decorator, 5, useMap, currentFilePath, currentClass),
198
- this.extractClassListArgFromDecorator(decorator, 6, useMap, currentFilePath, currentClass),
199
- this.extractClassListArgFromDecorator(decorator, 7, useMap, currentFilePath, currentClass),
200
- this.extractClassListArgFromDecorator(decorator, 8, useMap, currentFilePath, currentClass),
201
- this.extractClassListArgFromDecorator(decorator, 9, useMap, currentFilePath, currentClass),
230
+ this.getObjectClassListProp(obj, 'middleware', useMap, currentFilePath, currentClass),
202
231
  );
203
232
 
204
233
  return new HttpRouteData(
205
234
  updatedPath,
206
235
  updatedName,
207
- this.updateHandler(method, useMap, currentFilePath, currentClass),
236
+ this.resolveHandler(obj, method, useMap, currentFilePath, currentClass),
208
237
  requestMethods,
209
238
  routeMatchedMiddleware,
210
239
  routeDispatchedMiddleware,
211
240
  throwableCaughtMiddleware,
212
241
  sendingResponseMiddleware,
213
242
  responseSentMiddleware,
214
- this.middlewareReader.updateRequestStruct(method, useMap, currentFilePath, currentClass),
215
- this.middlewareReader.updateResponseStruct(method, useMap, currentFilePath, currentClass),
243
+ this.resolveRequestStruct(obj, method, useMap, currentFilePath, currentClass),
244
+ this.resolveResponseStruct(obj, method, useMap, currentFilePath, currentClass),
216
245
  resolvedIsDynamic,
217
- resolvedIsDynamic
218
- ? this.updateParameters(decoratorArgs, method, useMap, currentFilePath, currentClass)
219
- : [],
246
+ resolvedIsDynamic ? this.parameterReader.updateParameters(obj, useMap, currentFilePath, currentClass) : [],
220
247
  );
221
248
  }
222
249
 
250
+ protected resolveHandler(
251
+ obj: ts.ObjectLiteralExpression,
252
+ method: MethodDeclaration,
253
+ useMap: Record<string, string>,
254
+ currentFilePath: string,
255
+ currentClass: string,
256
+ ): HandlerData {
257
+ const fromObj = this.getObjectHandlerProp(obj, 'handler', useMap, currentFilePath, currentClass);
258
+
259
+ if (fromObj !== undefined) {
260
+ return fromObj;
261
+ }
262
+
263
+ return this.updateHandler(method, useMap, currentFilePath, currentClass);
264
+ }
265
+
266
+ protected resolveRequestStruct(
267
+ obj: ts.ObjectLiteralExpression,
268
+ method: MethodDeclaration,
269
+ useMap: Record<string, string>,
270
+ currentFilePath: string,
271
+ currentClass: string,
272
+ ): string | null {
273
+ const value = this.extractExprValue(
274
+ this.getObjectProp(obj, 'requestStruct'),
275
+ useMap,
276
+ currentFilePath,
277
+ currentClass,
278
+ );
279
+
280
+ if (typeof value === 'string' && value !== '') {
281
+ return value;
282
+ }
283
+
284
+ return this.middlewareReader.updateRequestStruct(method, useMap, currentFilePath, currentClass);
285
+ }
286
+
287
+ protected resolveResponseStruct(
288
+ obj: ts.ObjectLiteralExpression,
289
+ method: MethodDeclaration,
290
+ useMap: Record<string, string>,
291
+ currentFilePath: string,
292
+ currentClass: string,
293
+ ): string | null {
294
+ const value = this.extractExprValue(
295
+ this.getObjectProp(obj, 'responseStruct'),
296
+ useMap,
297
+ currentFilePath,
298
+ currentClass,
299
+ );
300
+
301
+ if (typeof value === 'string' && value !== '') {
302
+ return value;
303
+ }
304
+
305
+ return this.middlewareReader.updateResponseStruct(method, useMap, currentFilePath, currentClass);
306
+ }
307
+
223
308
  protected updatePath(
224
309
  path: string,
225
310
  classPathPrefix: string,
@@ -276,23 +361,27 @@ export class HttpRouteAttributeReader extends RouteAttributeReader implements Ht
276
361
  return name;
277
362
  }
278
363
 
364
+ /**
365
+ * Emit `new DynamicRoute(path, name, regex, parameters, handler,
366
+ * requestMethods, ...5 middleware, requestStruct, responseStruct)` or
367
+ * `new Route(path, name, handler, requestMethods, ...5 middleware,
368
+ * requestStruct, responseStruct)`, matching the framework constructor order.
369
+ *
370
+ * For dynamic routes the regex slot is emitted as an empty-string
371
+ * placeholder; the generator computes and injects the real regex (it owns
372
+ * the routing `Processor`), and the parameters array is always emitted (`[]`
373
+ * when empty).
374
+ */
279
375
  protected buildRouteExpr(data: HttpRouteData): ts.Expression {
280
376
  const args: ts.Expression[] = [this.buildEnumCaseExpr(data.path), this.buildEnumCaseExpr(data.name)];
281
377
 
282
- if (data.isDynamic && data.parameters.length > 0) {
378
+ if (data.isDynamic) {
379
+ args.push(this.buildStringExpr(''));
283
380
  args.push(this.parameterReader.buildParameterListExpr([...data.parameters]));
284
381
  }
285
382
 
286
- if (data.handler !== null) {
287
- args.push(this.buildHandlerExpr(data.handler));
288
- } else {
289
- args.push(this.buildNullExpr());
290
- }
291
-
292
- if (data.requestMethods.length > 0) {
293
- args.push(this.buildEnumCaseArrayExpr(data.requestMethods));
294
- }
295
-
383
+ args.push(data.handler !== null ? this.buildHandlerExpr(data.handler) : this.buildNullExpr());
384
+ args.push(this.buildEnumCaseArrayExpr(data.requestMethods));
296
385
  args.push(...this.middlewareReader.buildRouteMiddlewareArgs(data));
297
386
  args.push(...this.middlewareReader.buildRouteStructArgs(data));
298
387
 
@@ -303,13 +392,54 @@ export class HttpRouteAttributeReader extends RouteAttributeReader implements Ht
303
392
  return this.buildNewExpr(targetClass, args);
304
393
  }
305
394
 
306
- protected updateParameters(
307
- decoratorArgs: ts.Expression[],
308
- method: MethodDeclaration,
395
+ /**
396
+ * Build the class-name → absolute-file-path map for every handler and
397
+ * middleware class the generated data cache references, so the generator can
398
+ * emit the matching import statements.
399
+ */
400
+ protected buildImportMap(
401
+ routeData: Record<string, HttpRouteData>,
309
402
  useMap: Record<string, string>,
310
403
  currentFilePath: string,
311
404
  currentClass: string,
312
- ): import('./Data/HttpParameterData.ts').HttpParameterData[] {
313
- return this.parameterReader.updateParameters(decoratorArgs, method, useMap, currentFilePath, currentClass);
405
+ ): Record<string, string> {
406
+ const importMap: Record<string, string> = {};
407
+
408
+ for (const data of Object.values(routeData)) {
409
+ if (data.handler !== null) {
410
+ this.addClassImport(importMap, data.handler.class, useMap, currentFilePath, currentClass);
411
+ }
412
+
413
+ for (const middleware of [
414
+ ...data.routeMatchedMiddleware,
415
+ ...data.routeDispatchedMiddleware,
416
+ ...data.throwableCaughtMiddleware,
417
+ ...data.sendingResponseMiddleware,
418
+ ...data.responseSentMiddleware,
419
+ ]) {
420
+ this.addClassImport(importMap, middleware, useMap, currentFilePath, currentClass);
421
+ }
422
+ }
423
+
424
+ return importMap;
425
+ }
426
+
427
+ protected addClassImport(
428
+ importMap: Record<string, string>,
429
+ className: string,
430
+ useMap: Record<string, string>,
431
+ currentFilePath: string,
432
+ currentClass: string,
433
+ ): void {
434
+ const shortName = className.slice(className.lastIndexOf('\\') + 1);
435
+
436
+ const filePath =
437
+ shortName === currentClass
438
+ ? currentFilePath
439
+ : this.resolveImportToFilePath(shortName, useMap, currentFilePath);
440
+
441
+ if (filePath !== '') {
442
+ importMap[shortName] = filePath;
443
+ }
314
444
  }
315
445
  }
@@ -24,19 +24,13 @@ import type { HttpRouteMiddlewareReaderContract } from './Contract/HttpRouteMidd
24
24
  * complexity threshold; injected as a constructor argument.
25
25
  */
26
26
  export class HttpRouteMiddlewareReader extends AstReader implements HttpRouteMiddlewareReaderContract {
27
- extractInlineRequestMethods(
28
- decoratorArgs: ts.NodeArray<ts.Expression> | ts.Expression[],
27
+ extractObjectRequestMethods(
28
+ obj: ts.ObjectLiteralExpression,
29
29
  useMap: Record<string, string>,
30
30
  namespace: string,
31
31
  currentClass: string,
32
32
  ): string[] {
33
- const requestMethodsArg = decoratorArgs[3];
34
-
35
- if (requestMethodsArg === undefined || !ts.isArrayLiteralExpression(requestMethodsArg)) {
36
- return [];
37
- }
38
-
39
- return this.extractClassListFromArrayExpr(requestMethodsArg, useMap, namespace, currentClass);
33
+ return this.getObjectClassListProp(obj, 'requestMethods', useMap, namespace, currentClass);
40
34
  }
41
35
 
42
36
  updateRequestMethods(
@@ -71,14 +65,41 @@ export class HttpRouteMiddlewareReader extends AstReader implements HttpRouteMid
71
65
  useMap: Record<string, string>,
72
66
  namespace: string,
73
67
  currentClass: string,
74
- routeMatchedMiddleware: string[],
75
- routeDispatchedMiddleware: string[],
76
- throwableCaughtMiddleware: string[],
77
- sendingResponseMiddleware: string[],
78
- responseSentMiddleware: string[],
68
+ middleware: string[],
79
69
  ): [string[], string[], string[], string[], string[]] {
70
+ let routeMatchedMiddleware: string[] = [];
71
+ let routeDispatchedMiddleware: string[] = [];
72
+ let throwableCaughtMiddleware: string[] = [];
73
+ let sendingResponseMiddleware: string[] = [];
74
+ let responseSentMiddleware: string[] = [];
75
+
76
+ for (const mwName of middleware) {
77
+ [
78
+ routeMatchedMiddleware,
79
+ routeDispatchedMiddleware,
80
+ throwableCaughtMiddleware,
81
+ sendingResponseMiddleware,
82
+ responseSentMiddleware,
83
+ ] = this.classifyMiddleware(
84
+ mwName,
85
+ useMap,
86
+ namespace,
87
+ routeMatchedMiddleware,
88
+ routeDispatchedMiddleware,
89
+ throwableCaughtMiddleware,
90
+ sendingResponseMiddleware,
91
+ responseSentMiddleware,
92
+ );
93
+ }
94
+
80
95
  for (const decorator of this.findDecoratorsOnNode(method, 'Middleware', useMap, namespace)) {
81
- const mwName = this.extractExprValue(this.getDecoratorArg(decorator, 0), useMap, namespace, currentClass);
96
+ // `@Middleware` takes a thunked class reference (`() => AuthMiddleware`).
97
+ const mwName = this.extractExprValue(
98
+ this.unwrapClassThunk(this.getDecoratorArg(decorator, 0)),
99
+ useMap,
100
+ namespace,
101
+ currentClass,
102
+ );
82
103
 
83
104
  if (typeof mwName !== 'string' || mwName === '') {
84
105
  continue;
@@ -12,75 +12,41 @@ import { ts } from 'ts-morph';
12
12
  import { AstReader } from './Abstract/AstReader.ts';
13
13
  import { HttpParameterData } from './Data/HttpParameterData.ts';
14
14
 
15
- import type { Decorator, MethodDeclaration } from 'ts-morph';
16
-
17
15
  import type { HttpRouteParameterReaderContract } from './Contract/HttpRouteParameterReaderContract.ts';
18
16
 
19
17
  /**
20
18
  * Reads and builds AST expressions for HTTP dynamic route parameters.
21
19
  *
20
+ * The shipped `@DynamicRoute` decorator folds parameter definitions into its
21
+ * options object as `parameters: [{ name, regex, cast?, isOptional?,
22
+ * shouldCapture?, default? }]` (TC39 Stage-3 has no parameter decorators), so
23
+ * this reader parses that array of object literals rather than a separate
24
+ * `@Parameter` decorator.
25
+ *
22
26
  * Extracted from HttpRouteAttributeReader to keep each class under the
23
27
  * complexity threshold; injected as a constructor argument.
24
28
  */
25
29
  export class HttpRouteParameterReader extends AstReader implements HttpRouteParameterReaderContract {
26
30
  updateParameters(
27
- decoratorArgs: ts.NodeArray<ts.Expression> | ts.Expression[],
28
- method: MethodDeclaration,
31
+ obj: ts.ObjectLiteralExpression,
29
32
  useMap: Record<string, string>,
30
33
  namespace: string,
31
34
  currentClass: string,
32
35
  ): HttpParameterData[] {
33
- return [
34
- ...this.collectInlineParameters(decoratorArgs, useMap, namespace, currentClass),
35
- ...this.collectAttributeParameters(method, useMap, namespace, currentClass),
36
- ...this.collectMethodParamParameters(method, useMap, namespace, currentClass),
37
- ];
38
- }
36
+ const node = this.getObjectProp(obj, 'parameters');
39
37
 
40
- buildParameterListExpr(parameters: HttpParameterData[]): ts.ArrayLiteralExpression {
41
- const elements = parameters.map((p) => this.buildParameterExpr(p));
42
- return ts.factory.createArrayLiteralExpression(elements);
43
- }
44
-
45
- protected collectInlineParameters(
46
- decoratorArgs: ts.NodeArray<ts.Expression> | ts.Expression[],
47
- useMap: Record<string, string>,
48
- currentFilePath: string,
49
- currentClass: string,
50
- ): HttpParameterData[] {
51
- const inlineArg = decoratorArgs[2];
52
-
53
- if (inlineArg === undefined || !ts.isArrayLiteralExpression(inlineArg)) {
38
+ if (node === undefined || !ts.isArrayLiteralExpression(node)) {
54
39
  return [];
55
40
  }
56
41
 
57
42
  const parameters: HttpParameterData[] = [];
58
43
 
59
- for (const element of inlineArg.elements) {
60
- if (!ts.isNewExpression(element) || element.arguments === undefined) {
44
+ for (const element of node.elements) {
45
+ if (!ts.isObjectLiteralExpression(element)) {
61
46
  continue;
62
47
  }
63
48
 
64
- const param = this.buildParameterDataFromTsArgs(element.arguments, useMap, currentFilePath, currentClass);
65
-
66
- if (param !== null) {
67
- parameters.push(param);
68
- }
69
- }
70
-
71
- return parameters;
72
- }
73
-
74
- protected collectAttributeParameters(
75
- method: MethodDeclaration,
76
- useMap: Record<string, string>,
77
- currentFilePath: string,
78
- currentClass: string,
79
- ): HttpParameterData[] {
80
- const parameters: HttpParameterData[] = [];
81
-
82
- for (const decorator of this.findDecoratorsOnNode(method, 'Parameter', useMap, currentFilePath)) {
83
- const param = this.buildParameterDataFromDecorator(decorator, useMap, currentFilePath, currentClass);
49
+ const param = this.buildParameterData(element, useMap, namespace, currentClass);
84
50
 
85
51
  if (param !== null) {
86
52
  parameters.push(param);
@@ -90,89 +56,49 @@ export class HttpRouteParameterReader extends AstReader implements HttpRoutePara
90
56
  return parameters;
91
57
  }
92
58
 
93
- protected collectMethodParamParameters(
94
- method: MethodDeclaration,
95
- useMap: Record<string, string>,
96
- currentFilePath: string,
97
- currentClass: string,
98
- ): HttpParameterData[] {
99
- const parameters: HttpParameterData[] = [];
100
-
101
- for (const methodParam of method.getParameters()) {
102
- for (const decorator of this.findDecoratorsOnNode(methodParam, 'Parameter', useMap, currentFilePath)) {
103
- const param = this.buildParameterDataFromDecorator(decorator, useMap, currentFilePath, currentClass);
104
-
105
- if (param !== null) {
106
- parameters.push(param);
107
- }
108
- }
109
- }
110
-
111
- return parameters;
59
+ buildParameterListExpr(parameters: HttpParameterData[]): ts.ArrayLiteralExpression {
60
+ const elements = parameters.map((p) => this.buildParameterExpr(p));
61
+ return ts.factory.createArrayLiteralExpression(elements);
112
62
  }
113
63
 
114
- protected buildParameterDataFromDecorator(
115
- decorator: Decorator,
64
+ protected buildParameterData(
65
+ obj: ts.ObjectLiteralExpression,
116
66
  useMap: Record<string, string>,
117
67
  currentFilePath: string,
118
68
  currentClass: string,
119
69
  ): HttpParameterData | null {
120
- const name = this.extractStringArg(decorator, 0, useMap, currentFilePath, currentClass);
121
- const regex = this.extractStringArg(decorator, 1, useMap, currentFilePath, currentClass);
70
+ const name = this.getObjectStringProp(obj, 'name', useMap, currentFilePath, currentClass);
71
+ const regex = this.getObjectStringProp(obj, 'regex', useMap, currentFilePath, currentClass);
122
72
 
123
73
  if (name === '' || regex === '') {
124
74
  return null;
125
75
  }
126
76
 
77
+ const cast = this.getObjectStringProp(obj, 'cast', useMap, currentFilePath, currentClass) || null;
78
+
127
79
  return new HttpParameterData(
128
80
  name,
129
81
  regex,
130
- this.extractStringArg(decorator, 2, useMap, currentFilePath, currentClass) || null,
131
- this.extractBoolArg(decorator, 3, useMap, currentFilePath, currentClass, false),
132
- this.extractBoolArg(decorator, 4, useMap, currentFilePath, currentClass, true),
82
+ cast,
83
+ this.getObjectBoolProp(obj, 'isOptional', useMap, currentFilePath, currentClass, false),
84
+ this.getObjectBoolProp(obj, 'shouldCapture', useMap, currentFilePath, currentClass, true),
85
+ this.extractDefault(obj, useMap, currentFilePath, currentClass),
133
86
  );
134
87
  }
135
88
 
136
- protected buildParameterDataFromTsArgs(
137
- args: ts.NodeArray<ts.Expression>,
89
+ protected extractDefault(
90
+ obj: ts.ObjectLiteralExpression,
138
91
  useMap: Record<string, string>,
139
92
  currentFilePath: string,
140
93
  currentClass: string,
141
- ): HttpParameterData | null {
142
- const nameNode = args[0];
143
- const regexNode = args[1];
94
+ ): string | number | boolean | null {
95
+ const value = this.extractExprValue(this.getObjectProp(obj, 'default'), useMap, currentFilePath, currentClass);
144
96
 
145
- if (nameNode === undefined || regexNode === undefined) {
146
- return null;
97
+ if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
98
+ return value;
147
99
  }
148
100
 
149
- const name = this.extractExprValue(nameNode, useMap, currentFilePath, currentClass);
150
- const regex = this.extractExprValue(regexNode, useMap, currentFilePath, currentClass);
151
-
152
- if (typeof name !== 'string' || name === '' || typeof regex !== 'string' || regex === '') {
153
- return null;
154
- }
155
-
156
- const castNode = args[2];
157
- const castRaw =
158
- castNode !== undefined ? this.extractExprValue(castNode, useMap, currentFilePath, currentClass) : null;
159
- const cast = typeof castRaw === 'string' && castRaw !== '' ? castRaw : null;
160
-
161
- const isOptionalNode = args[3];
162
- const isOptionalRaw =
163
- isOptionalNode !== undefined
164
- ? this.extractExprValue(isOptionalNode, useMap, currentFilePath, currentClass)
165
- : false;
166
- const isOptional = typeof isOptionalRaw === 'boolean' ? isOptionalRaw : false;
167
-
168
- const shouldCaptureNode = args[4];
169
- const shouldCaptureRaw =
170
- shouldCaptureNode !== undefined
171
- ? this.extractExprValue(shouldCaptureNode, useMap, currentFilePath, currentClass)
172
- : true;
173
- const shouldCapture = typeof shouldCaptureRaw === 'boolean' ? shouldCaptureRaw : true;
174
-
175
- return new HttpParameterData(name, regex, cast, isOptional, shouldCapture);
101
+ return null;
176
102
  }
177
103
 
178
104
  protected buildParameterExpr(data: HttpParameterData): ts.Expression {
@@ -188,6 +114,22 @@ export class HttpRouteParameterReader extends AstReader implements HttpRoutePara
188
114
  this.buildBoolExpr(data.shouldCapture),
189
115
  ];
190
116
 
117
+ if (data.defaultValue !== null) {
118
+ args.push(this.buildScalarExpr(data.defaultValue));
119
+ }
120
+
191
121
  return this.buildNewExpr('Valkyrja\\Http\\Routing\\Data\\Parameter', args);
192
122
  }
123
+
124
+ protected buildScalarExpr(value: string | number | boolean): ts.Expression {
125
+ if (typeof value === 'string') {
126
+ return this.buildStringExpr(value);
127
+ }
128
+
129
+ if (typeof value === 'boolean') {
130
+ return this.buildBoolExpr(value);
131
+ }
132
+
133
+ return ts.factory.createNumericLiteral(value);
134
+ }
193
135
  }
@@ -8,8 +8,8 @@
8
8
  */
9
9
 
10
10
  export class SindriInfo {
11
- static readonly VERSION = '26.5.1' as const;
12
- static readonly VERSION_BUILD_DATE_TIME = 'August 1 2026 14:12:56 MST' as const;
11
+ static readonly VERSION = '26.6.0' as const;
12
+ static readonly VERSION_BUILD_DATE_TIME = 'August 1 2026 18:38:34 MST' as const;
13
13
  static readonly ICON = `\
14
14
  ██████████
15
15
  ████ ████