@kithinji/pod 1.0.42 → 1.0.47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ #!/usr/bin/env node
1
2
  var __defProp = Object.defineProperty;
2
3
  var __export = (target, all) => {
3
4
  for (var name in all)
@@ -499,7 +500,7 @@ function resolveImportFullPath(symbolName, sourceFile, compilerOptions) {
499
500
  };
500
501
  }
501
502
  function isNpmPackage(importPath) {
502
- return !importPath.startsWith(".") && !importPath.startsWith("/") && !path3.isAbsolute(importPath);
503
+ return !importPath.startsWith(".") && !importPath.startsWith("/") && !importPath.startsWith("@/") && !path3.isAbsolute(importPath);
503
504
  }
504
505
  function findVariableDeclarationInFile(variableName, sourceFile) {
505
506
  let found;
@@ -929,6 +930,7 @@ async function expandMacros(source, filePath, projectRoot = process.cwd()) {
929
930
 
930
931
  // src/plugins/generators/generate_controller.ts
931
932
  import * as path4 from "path";
933
+ import { printSync } from "@swc/core";
932
934
 
933
935
  // src/plugins/generators/utils.ts
934
936
  import {
@@ -963,8 +965,20 @@ function hasInjectableDecorator(decorators) {
963
965
  return expr.type === "Identifier" && expr.value === "Injectable" || expr.type === "CallExpression" && expr.callee.type === "Identifier" && expr.callee.value === "Injectable";
964
966
  });
965
967
  }
968
+ function isClassMethod(member) {
969
+ return member.type === "ClassMethod";
970
+ }
971
+ function isClassProperty(member) {
972
+ return member.type === "ClassProperty";
973
+ }
974
+ function isConstructor(member) {
975
+ return member.type === "Constructor";
976
+ }
966
977
  function isPublicMethod(member) {
967
- return member.type === "ClassMethod" && (member.accessibility === "public" || !member.accessibility);
978
+ return member.type === "ClassMethod" && (member.accessibility === "public" || member.accessibility === void 0);
979
+ }
980
+ function isPrivateMethod(member) {
981
+ return member.type === "ClassMethod" && member.accessibility === "private";
968
982
  }
969
983
  function getMethodName(method) {
970
984
  if (method.key.type === "Identifier") {
@@ -1026,23 +1040,36 @@ function analyzeReturnType(method) {
1026
1040
  isSubjectLike: false
1027
1041
  };
1028
1042
  }
1043
+ function stringifyEntityName(node) {
1044
+ if (!node) return "any";
1045
+ switch (node.type) {
1046
+ case "Identifier":
1047
+ return node.value;
1048
+ case "TsQualifiedName":
1049
+ return `${stringifyEntityName(node.left)}.${stringifyEntityName(
1050
+ node.right
1051
+ )}`;
1052
+ default:
1053
+ return "any";
1054
+ }
1055
+ }
1029
1056
  function stringifyType(node) {
1030
1057
  if (!node) return "any";
1031
1058
  switch (node.type) {
1032
1059
  case "TsKeywordType":
1033
1060
  return node.kind;
1034
- case "TsTypeReference":
1035
- if (node.typeName.type !== "Identifier") return "any";
1036
- const base = node.typeName.value;
1061
+ case "TsTypeReference": {
1062
+ const base = stringifyEntityName(node.typeName);
1037
1063
  const args = node.typeParams?.params ? `<${node.typeParams.params.map(stringifyType).join(", ")}>` : "";
1038
1064
  return base + args;
1065
+ }
1039
1066
  case "TsArrayType":
1040
1067
  return `${stringifyType(node.elemType)}[]`;
1041
1068
  case "TsUnionType":
1042
1069
  return node.types.map(stringifyType).join(" | ");
1043
1070
  case "TsIntersectionType":
1044
1071
  return node.types.map(stringifyType).join(" & ");
1045
- case "TsTypeLiteral":
1072
+ case "TsTypeLiteral": {
1046
1073
  const props = node.members.map((member) => {
1047
1074
  if (member.type === "TsPropertySignature") {
1048
1075
  const key = member.key.type === "Identifier" ? member.key.value : "";
@@ -1052,10 +1079,55 @@ function stringifyType(node) {
1052
1079
  return "";
1053
1080
  }).filter(Boolean);
1054
1081
  return `{ ${props.join("; ")} }`;
1082
+ }
1055
1083
  default:
1056
1084
  return "any";
1057
1085
  }
1058
1086
  }
1087
+ function stringifyDecorator(decorator) {
1088
+ const expr = decorator.expression;
1089
+ if (expr.type === "Identifier") {
1090
+ return `@${expr.value}`;
1091
+ }
1092
+ if (expr.type === "CallExpression" && expr.callee.type === "Identifier") {
1093
+ const args = expr.arguments.map((arg) => stringifyExpression(arg.expression)).join(", ");
1094
+ return args ? `@${expr.callee.value}(${args})` : `@${expr.callee.value}()`;
1095
+ }
1096
+ return "@Unknown";
1097
+ }
1098
+ function shouldLiftDecorator(decoratorString) {
1099
+ const match = decoratorString.match(/^@(_)?([A-Za-z0-9]+)/);
1100
+ if (!match) return false;
1101
+ const prefix = match[1];
1102
+ return !prefix;
1103
+ }
1104
+ function extractMethodDecorators(method) {
1105
+ const decorators = [];
1106
+ if (method.function.decorators) {
1107
+ for (const decorator of method.function.decorators) {
1108
+ decorators.push(stringifyDecorator(decorator));
1109
+ }
1110
+ }
1111
+ return decorators;
1112
+ }
1113
+ function extractParamDecorators(param) {
1114
+ const decorators = [];
1115
+ if (param.decorators) {
1116
+ for (const decorator of param.decorators) {
1117
+ decorators.push(stringifyDecorator(decorator));
1118
+ }
1119
+ }
1120
+ return decorators;
1121
+ }
1122
+ function extractPropertyDecorators(property) {
1123
+ const decorators = [];
1124
+ if (property.decorators) {
1125
+ for (const decorator of property.decorators) {
1126
+ decorators.push(stringifyDecorator(decorator));
1127
+ }
1128
+ }
1129
+ return decorators;
1130
+ }
1059
1131
  function extractMethodParams(params) {
1060
1132
  return params.map((p) => {
1061
1133
  const pat = p.pat;
@@ -1063,17 +1135,222 @@ function extractMethodParams(params) {
1063
1135
  return {
1064
1136
  name: "param",
1065
1137
  type: "any",
1066
- decorators: []
1138
+ decorators: extractParamDecorators(p)
1067
1139
  };
1068
1140
  }
1069
1141
  return {
1070
1142
  name: pat.value,
1071
1143
  type: pat.typeAnnotation ? stringifyType(pat.typeAnnotation.typeAnnotation) : "any",
1072
- decorators: []
1144
+ decorators: extractParamDecorators(p)
1073
1145
  };
1074
1146
  });
1075
1147
  }
1076
- function extractSignature(decorators, paramCount) {
1148
+ function extractIdentifiersFromQualifiedName(node) {
1149
+ const identifiers = [];
1150
+ if (node.type === "Identifier") {
1151
+ identifiers.push(node.value);
1152
+ } else if (node.type === "TsQualifiedName") {
1153
+ identifiers.push(...extractIdentifiersFromQualifiedName(node.left));
1154
+ identifiers.push(...extractIdentifiersFromQualifiedName(node.right));
1155
+ }
1156
+ return identifiers;
1157
+ }
1158
+ function extractIdentifiersFromType(node) {
1159
+ if (!node) return [];
1160
+ const identifiers = [];
1161
+ switch (node.type) {
1162
+ case "TsKeywordType":
1163
+ return [];
1164
+ case "TsTypeReference": {
1165
+ if (node.typeName.type === "Identifier") {
1166
+ identifiers.push(node.typeName.value);
1167
+ } else if (node.typeName.type === "TsQualifiedName") {
1168
+ identifiers.push(...extractIdentifiersFromQualifiedName(node.typeName));
1169
+ }
1170
+ if (node.typeParams?.params) {
1171
+ for (const param of node.typeParams.params) {
1172
+ identifiers.push(...extractIdentifiersFromType(param));
1173
+ }
1174
+ }
1175
+ break;
1176
+ }
1177
+ case "TsArrayType":
1178
+ identifiers.push(...extractIdentifiersFromType(node.elemType));
1179
+ break;
1180
+ case "TsUnionType":
1181
+ case "TsIntersectionType":
1182
+ for (const type of node.types) {
1183
+ identifiers.push(...extractIdentifiersFromType(type));
1184
+ }
1185
+ break;
1186
+ case "TsTypeLiteral":
1187
+ for (const member of node.members) {
1188
+ if (member.type === "TsPropertySignature" && member.typeAnnotation) {
1189
+ identifiers.push(
1190
+ ...extractIdentifiersFromType(member.typeAnnotation.typeAnnotation)
1191
+ );
1192
+ }
1193
+ }
1194
+ break;
1195
+ case "TsTupleType":
1196
+ if (node.elemTypes) {
1197
+ for (const elem of node.elemTypes) {
1198
+ identifiers.push(...extractIdentifiersFromType(elem));
1199
+ }
1200
+ }
1201
+ break;
1202
+ }
1203
+ return identifiers;
1204
+ }
1205
+ function extractIdentifiersFromDecorator(decorator) {
1206
+ const identifiers = [];
1207
+ const expr = decorator.expression;
1208
+ if (expr.type === "Identifier") {
1209
+ identifiers.push(expr.value);
1210
+ } else if (expr.type === "CallExpression") {
1211
+ if (expr.callee.type === "Identifier") {
1212
+ identifiers.push(expr.callee.value);
1213
+ }
1214
+ for (const arg of expr.arguments) {
1215
+ identifiers.push(...extractIdentifiersFromExpression(arg.expression));
1216
+ }
1217
+ }
1218
+ return identifiers;
1219
+ }
1220
+ function extractIdentifiersFromExpression(expr) {
1221
+ if (!expr) return [];
1222
+ const identifiers = [];
1223
+ switch (expr.type) {
1224
+ case "Identifier":
1225
+ identifiers.push(expr.value);
1226
+ break;
1227
+ case "MemberExpression":
1228
+ identifiers.push(...extractIdentifiersFromExpression(expr.object));
1229
+ if (expr.property.type === "Identifier") {
1230
+ identifiers.push(expr.property.value);
1231
+ }
1232
+ break;
1233
+ case "CallExpression":
1234
+ identifiers.push(...extractIdentifiersFromExpression(expr.callee));
1235
+ for (const arg of expr.arguments) {
1236
+ identifiers.push(...extractIdentifiersFromExpression(arg.expression));
1237
+ }
1238
+ break;
1239
+ }
1240
+ return identifiers;
1241
+ }
1242
+ function resolveImports(identifiers, importMap) {
1243
+ const imports = [];
1244
+ const seen = /* @__PURE__ */ new Set();
1245
+ for (const identifier of identifiers) {
1246
+ if (seen.has(identifier)) continue;
1247
+ const source = importMap[identifier];
1248
+ if (source) {
1249
+ imports.push({ identifier, source });
1250
+ seen.add(identifier);
1251
+ }
1252
+ }
1253
+ return imports;
1254
+ }
1255
+ function extractClassProperties(classDecl, importMap) {
1256
+ const properties = [];
1257
+ if (!classDecl.body || !Array.isArray(classDecl.body)) {
1258
+ return properties;
1259
+ }
1260
+ for (const member of classDecl.body) {
1261
+ if (!isClassProperty(member)) continue;
1262
+ const property = member;
1263
+ if (property.key.type !== "Identifier") continue;
1264
+ const propertyName = property.key.value;
1265
+ const propertyType = property.typeAnnotation ? stringifyType(property.typeAnnotation.typeAnnotation) : "any";
1266
+ const identifiers = [];
1267
+ if (property.typeAnnotation) {
1268
+ identifiers.push(
1269
+ ...extractIdentifiersFromType(property.typeAnnotation.typeAnnotation)
1270
+ );
1271
+ }
1272
+ if (property.decorators) {
1273
+ for (const decorator of property.decorators) {
1274
+ identifiers.push(...extractIdentifiersFromDecorator(decorator));
1275
+ }
1276
+ }
1277
+ const imports = resolveImports(identifiers, importMap);
1278
+ properties.push({
1279
+ name: propertyName,
1280
+ type: propertyType,
1281
+ isReadonly: property.readonly || false,
1282
+ isStatic: property.isStatic || false,
1283
+ accessibility: property.accessibility,
1284
+ decorators: extractPropertyDecorators(property),
1285
+ hasInitializer: !!property.value,
1286
+ imports
1287
+ });
1288
+ }
1289
+ return properties;
1290
+ }
1291
+ function extractConstructorParams(classDecl, importMap) {
1292
+ const params = [];
1293
+ if (!classDecl.body || !Array.isArray(classDecl.body)) {
1294
+ return params;
1295
+ }
1296
+ for (const member of classDecl.body) {
1297
+ if (!isConstructor(member)) continue;
1298
+ const constructor = member;
1299
+ for (const param of constructor.params) {
1300
+ if (param.type === "TsParameterProperty") {
1301
+ const tsParam = param;
1302
+ const pat = tsParam.param;
1303
+ if (pat.type !== "Identifier") continue;
1304
+ const identifiers = [];
1305
+ if (pat.typeAnnotation) {
1306
+ identifiers.push(
1307
+ ...extractIdentifiersFromType(pat.typeAnnotation.typeAnnotation)
1308
+ );
1309
+ }
1310
+ if (tsParam.decorators) {
1311
+ for (const decorator of tsParam.decorators) {
1312
+ identifiers.push(...extractIdentifiersFromDecorator(decorator));
1313
+ }
1314
+ }
1315
+ const imports = resolveImports(identifiers, importMap);
1316
+ params.push({
1317
+ name: pat.value,
1318
+ type: pat.typeAnnotation ? stringifyType(pat.typeAnnotation.typeAnnotation) : "any",
1319
+ accessibility: tsParam.accessibility,
1320
+ isReadonly: tsParam.readonly || false,
1321
+ decorators: extractParamDecorators(tsParam),
1322
+ imports
1323
+ });
1324
+ } else if (param.type === "Parameter") {
1325
+ const regularParam = param;
1326
+ const pat = regularParam.pat;
1327
+ if (pat.type !== "Identifier") continue;
1328
+ const identifiers = [];
1329
+ if (pat.typeAnnotation) {
1330
+ identifiers.push(
1331
+ ...extractIdentifiersFromType(pat.typeAnnotation.typeAnnotation)
1332
+ );
1333
+ }
1334
+ if (regularParam.decorators) {
1335
+ for (const decorator of regularParam.decorators) {
1336
+ identifiers.push(...extractIdentifiersFromDecorator(decorator));
1337
+ }
1338
+ }
1339
+ const imports = resolveImports(identifiers, importMap);
1340
+ params.push({
1341
+ name: pat.value,
1342
+ type: pat.typeAnnotation ? stringifyType(pat.typeAnnotation.typeAnnotation) : "any",
1343
+ accessibility: void 0,
1344
+ isReadonly: false,
1345
+ decorators: extractParamDecorators(regularParam),
1346
+ imports
1347
+ });
1348
+ }
1349
+ }
1350
+ }
1351
+ return params;
1352
+ }
1353
+ function extractSignature(decorators, paramCount, className, methodName, filePath) {
1077
1354
  if (!decorators) return { paramSchemas: [] };
1078
1355
  for (const decorator of decorators) {
1079
1356
  const expr = decorator.expression;
@@ -1083,6 +1360,14 @@ function extractSignature(decorators, paramCount) {
1083
1360
  const schemaStrings = args.map(
1084
1361
  (arg) => stringifyExpression(arg.expression)
1085
1362
  );
1363
+ if (paramCount === 0 && args.length === 1) {
1364
+ return { paramSchemas: [], returnSchema: schemaStrings[0] };
1365
+ }
1366
+ if (args.length !== paramCount + 1) {
1367
+ console.warn(
1368
+ `Warning: Method ${className}.${methodName} has ${paramCount} parameter(s) but @Signature has ${args.length} argument(s). Expected ${paramCount + 1} (${paramCount} param schema(s) + 1 return schema).`
1369
+ );
1370
+ }
1086
1371
  if (args.length === 1) {
1087
1372
  return { paramSchemas: [], returnSchema: schemaStrings[0] };
1088
1373
  }
@@ -1110,15 +1395,36 @@ function stringifyExpression(expr) {
1110
1395
  }
1111
1396
  return "any";
1112
1397
  }
1113
- function extractMethods(classDecl, filePath, includeSignatures = true) {
1398
+ function extractMethods(classDecl, filePath, includeSignatures = true, includeHeaders = false) {
1114
1399
  const methods = [];
1115
1400
  const className = classDecl.identifier?.value || "UnknownClass";
1401
+ if (!classDecl.body || !Array.isArray(classDecl.body)) {
1402
+ return methods;
1403
+ }
1116
1404
  for (const member of classDecl.body) {
1117
- if (!isPublicMethod(member)) continue;
1405
+ if (!isClassMethod(member)) continue;
1118
1406
  const method = member;
1119
1407
  const methodName = getMethodName(method);
1120
1408
  if (!methodName) continue;
1409
+ const endsWithHeaders = methodName.endsWith("Headers");
1121
1410
  const returnTypeInfo = analyzeReturnType(method);
1411
+ if (isPrivateMethod(member) && includeHeaders && endsWithHeaders) {
1412
+ methods.push({
1413
+ name: methodName,
1414
+ params: extractMethodParams(method.function.params),
1415
+ returnType: returnTypeInfo.type,
1416
+ isAsync: method.function.async,
1417
+ isStreamable: returnTypeInfo.isStreamable,
1418
+ streamType: returnTypeInfo.streamType,
1419
+ paramSchemas: [],
1420
+ returnSchema: void 0,
1421
+ decorators: void 0,
1422
+ lift: true,
1423
+ ast: method
1424
+ });
1425
+ continue;
1426
+ }
1427
+ if (!isPublicMethod(member)) continue;
1122
1428
  if (!returnTypeInfo.isStreamable && !method.function.async) {
1123
1429
  throw {
1124
1430
  type: "validation",
@@ -1131,7 +1437,10 @@ function extractMethods(classDecl, filePath, includeSignatures = true) {
1131
1437
  }
1132
1438
  const signatures = includeSignatures ? extractSignature(
1133
1439
  method.function.decorators,
1134
- method.function.params.length
1440
+ method.function.params.length,
1441
+ className,
1442
+ methodName,
1443
+ filePath
1135
1444
  ) : { paramSchemas: [] };
1136
1445
  methods.push({
1137
1446
  name: methodName,
@@ -1141,11 +1450,41 @@ function extractMethods(classDecl, filePath, includeSignatures = true) {
1141
1450
  isStreamable: returnTypeInfo.isStreamable,
1142
1451
  streamType: returnTypeInfo.streamType,
1143
1452
  paramSchemas: signatures.paramSchemas,
1144
- returnSchema: signatures.returnSchema
1453
+ returnSchema: signatures.returnSchema,
1454
+ decorators: extractMethodDecorators(method),
1455
+ lift: false,
1456
+ ast: method
1145
1457
  });
1146
1458
  }
1147
1459
  return methods;
1148
1460
  }
1461
+ function removeLiftedDecoratorsFromAST(ast) {
1462
+ const clonedAst = JSON.parse(JSON.stringify(ast));
1463
+ for (const item of clonedAst.body) {
1464
+ if (item.type === "ExportDeclaration" && item.declaration?.type === "ClassDeclaration") {
1465
+ const classDecl = item.declaration;
1466
+ if (hasInjectableDecorator(classDecl.decorators)) {
1467
+ for (const member of classDecl.body) {
1468
+ if (member.type === "ClassMethod" && (member.accessibility === "public" || member.accessibility === void 0)) {
1469
+ const method = member;
1470
+ if (method.function.decorators) {
1471
+ method.function.decorators = method.function.decorators.filter(
1472
+ (decorator) => {
1473
+ const decoratorStr = stringifyDecorator(decorator);
1474
+ return !shouldLiftDecorator(decoratorStr);
1475
+ }
1476
+ );
1477
+ if (method.function.decorators.length === 0) {
1478
+ delete method.function.decorators;
1479
+ }
1480
+ }
1481
+ }
1482
+ }
1483
+ }
1484
+ }
1485
+ }
1486
+ return clonedAst;
1487
+ }
1149
1488
  function serviceNameToPath(serviceName) {
1150
1489
  return serviceName.replace(/Service$/, "").replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
1151
1490
  }
@@ -1169,23 +1508,43 @@ function validateServiceInfo(serviceInfo, filePath) {
1169
1508
  );
1170
1509
  }
1171
1510
  serviceInfo.methods.forEach((method) => {
1172
- if (method.params.length > 0 && method.paramSchemas?.length === 0) {
1511
+ if (method.params.length > 0 && method.paramSchemas?.length === 0 && !method.returnSchema) {
1173
1512
  console.warn(
1174
- `Warning: Method ${serviceInfo.className}.${method.name} has parameters but no @Signature validation`
1513
+ `Warning: Method ${serviceInfo.className}.${method.name} has ${method.params.length} parameter(s) but no @Signature validation`
1175
1514
  );
1176
1515
  }
1177
1516
  });
1178
1517
  }
1179
- function extractServiceInfo(ast, filePath, includeSignatures = true) {
1518
+ function extractServiceInfo(ast, filePath, includeSignatures = true, includeHeaders = false, includeFields = false) {
1180
1519
  try {
1181
1520
  const serviceClass = findInjectableClass(ast);
1182
1521
  const importMap = extractImportMap(ast);
1183
1522
  if (!serviceClass?.identifier) {
1184
1523
  return null;
1185
1524
  }
1525
+ if (!serviceClass.body) {
1526
+ console.warn(
1527
+ `Warning: Service class ${serviceClass.identifier.value} has no body`
1528
+ );
1529
+ return {
1530
+ className: serviceClass.identifier.value,
1531
+ methods: [],
1532
+ properties: [],
1533
+ constructorParams: [],
1534
+ hasInjectable: true,
1535
+ importMap
1536
+ };
1537
+ }
1186
1538
  return {
1187
1539
  className: serviceClass.identifier.value,
1188
- methods: extractMethods(serviceClass, filePath, includeSignatures),
1540
+ methods: extractMethods(
1541
+ serviceClass,
1542
+ filePath,
1543
+ includeSignatures,
1544
+ includeHeaders
1545
+ ),
1546
+ properties: includeFields ? extractClassProperties(serviceClass, importMap) : [],
1547
+ constructorParams: includeFields ? extractConstructorParams(serviceClass, importMap) : [],
1189
1548
  hasInjectable: true,
1190
1549
  importMap
1191
1550
  };
@@ -1203,12 +1562,18 @@ function extractServiceInfo(ast, filePath, includeSignatures = true) {
1203
1562
  function generateController(filePath, code) {
1204
1563
  try {
1205
1564
  const ast = parseTypeScript(filePath, code);
1206
- const serviceInfo = extractServiceInfo(ast, filePath, true);
1565
+ const serviceInfo = extractServiceInfo(ast, filePath, true, false);
1207
1566
  if (!serviceInfo || !serviceInfo.hasInjectable) {
1208
1567
  return null;
1209
1568
  }
1210
1569
  validateServiceInfo(serviceInfo, filePath);
1211
- return generateControllerCode(serviceInfo, filePath);
1570
+ const transformedAst = removeLiftedDecoratorsFromAST(ast);
1571
+ const transformedServiceCode = printSync(transformedAst).code;
1572
+ const controllerCode = generateControllerCode(serviceInfo, filePath);
1573
+ return {
1574
+ controllerCode,
1575
+ transformedServiceCode
1576
+ };
1212
1577
  } catch (error) {
1213
1578
  if (error.type) {
1214
1579
  throw error;
@@ -1231,7 +1596,7 @@ function generateControllerCode(serviceInfo, filePath) {
1231
1596
  const serviceInstance = toInstanceName(serviceName);
1232
1597
  return `${imports}
1233
1598
 
1234
- @Controller("/${controllerPath}", {
1599
+ @Controller("/api/${controllerPath}", {
1235
1600
  providedIn: "root",
1236
1601
  })
1237
1602
  export class ${controllerName} {
@@ -1276,16 +1641,40 @@ function generateImports(serviceInfo, serviceName, serviceImportPath) {
1276
1641
  const hasStreamableWithParams = serviceInfo.methods.some(
1277
1642
  (m) => m.isStreamable && m.params.length > 0
1278
1643
  );
1279
- const decorators = ["Controller"];
1644
+ const hasFileUpload = serviceInfo.methods.some((m) => hasMulterFileParams(m));
1645
+ const hasSingleFileUpload = serviceInfo.methods.some(
1646
+ (m) => getFileParams(m).length === 1 && !isArrayFileParam(getFileParams(m)[0])
1647
+ );
1648
+ const hasMultipleFilesUpload = serviceInfo.methods.some(
1649
+ (m) => getFileParams(m).length === 1 && isArrayFileParam(getFileParams(m)[0])
1650
+ );
1651
+ const hasMultipleFileFields = serviceInfo.methods.some(
1652
+ (m) => getFileParams(m).length > 1
1653
+ );
1654
+ const decorators = ["Controller", "Req"];
1280
1655
  if (hasPost) decorators.push("Post");
1281
1656
  if (hasGet) decorators.push("Get");
1282
1657
  if (hasPost) decorators.push("Body");
1283
1658
  if (hasSse) decorators.push("Sse");
1284
1659
  if (hasStreamableWithParams) decorators.push("Query");
1660
+ if (hasFileUpload) decorators.push("UseInterceptors");
1661
+ if (hasSingleFileUpload) decorators.push("UploadedFile");
1662
+ if (hasMultipleFilesUpload || hasMultipleFileFields)
1663
+ decorators.push("UploadedFiles");
1285
1664
  let importStrings = `import { ${decorators.join(
1286
1665
  ", "
1287
1666
  )} } from "@kithinji/orca";
1288
1667
  `;
1668
+ if (hasFileUpload) {
1669
+ const interceptors = [];
1670
+ if (hasSingleFileUpload) interceptors.push("FileInterceptor");
1671
+ if (hasMultipleFilesUpload) interceptors.push("FilesInterceptor");
1672
+ if (hasMultipleFileFields) interceptors.push("FileFieldsInterceptor");
1673
+ importStrings += `import { ${interceptors.join(
1674
+ ", "
1675
+ )} } from "@kithinji/express";
1676
+ `;
1677
+ }
1289
1678
  importGroups.forEach((ids, source) => {
1290
1679
  const filteredIds = Array.from(ids).filter((id) => id !== serviceName);
1291
1680
  if (filteredIds.length > 0) {
@@ -1297,32 +1686,175 @@ function generateImports(serviceInfo, serviceName, serviceImportPath) {
1297
1686
  });
1298
1687
  return importStrings;
1299
1688
  }
1689
+ function hasMulterFileParams(method) {
1690
+ return method.params.some((param) => {
1691
+ return param.type === "Array<Express.Multer.File>" || param.type === "Express.Multer.File[]" || param.type === "Express.Multer.File";
1692
+ });
1693
+ }
1694
+ function getFileParams(method) {
1695
+ return method.params.filter(
1696
+ (p) => p.type === "Array<Express.Multer.File>" || p.type === "Express.Multer.File[]" || p.type === "Express.Multer.File"
1697
+ ).map((p) => ({
1698
+ name: p.name,
1699
+ type: p.type,
1700
+ isArray: p.type.includes("Array") || p.type.includes("[]")
1701
+ }));
1702
+ }
1703
+ function isArrayFileParam(param) {
1704
+ return param.isArray;
1705
+ }
1706
+ function getNonFileParams(method) {
1707
+ return method.params.filter(
1708
+ (p) => p.type !== "Array<Express.Multer.File>" && p.type !== "Express.Multer.File[]" && p.type !== "Express.Multer.File"
1709
+ );
1710
+ }
1300
1711
  function generateMethods(serviceInfo) {
1301
1712
  return serviceInfo.methods.map((m) => generateMethod(m, serviceInfo.className)).join("\n\n");
1302
1713
  }
1303
1714
  function generateMethod(method, serviceName) {
1304
1715
  const hasParams = method.params.length > 0;
1305
1716
  const serviceInstance = toInstanceName(serviceName);
1717
+ const fileParams = getFileParams(method);
1718
+ const hasFileParams = fileParams.length > 0;
1719
+ const liftedDecorators = method.decorators?.filter(shouldLiftDecorator).map((d) => ` ${d}`).join("\n") || "";
1306
1720
  if (method.isStreamable) {
1721
+ if (hasFileParams) {
1722
+ throw new Error(
1723
+ `Method '${method.name}' cannot have file parameters for streaming endpoints`
1724
+ );
1725
+ }
1726
+ const reqParam2 = `@Req() request: Request`;
1307
1727
  const queryParams = hasParams ? method.params.map((p) => `@Query('${p.name}') ${p.name}: ${p.type}`).join(", ") : "";
1728
+ const allParams2 = [reqParam2, queryParams].filter(Boolean).join(", ");
1308
1729
  const body2 = generateMethodBody(method, serviceInstance, false);
1309
1730
  const returnTypeName = method.streamType || "Observable";
1310
- return ` @Sse("${method.name}")
1311
- ${method.name}(${queryParams}): ${returnTypeName}<${method.returnType}> {
1731
+ const decoratorPrefix2 = liftedDecorators ? `${liftedDecorators}
1732
+ ` : "";
1733
+ return `${decoratorPrefix2} @Sse("${method.name}")
1734
+ ${method.name}(${allParams2}): ${returnTypeName}<${method.returnType}> {
1312
1735
  ${body2}
1313
1736
  }`;
1314
1737
  }
1738
+ if (hasFileParams) {
1739
+ return generateFileUploadMethod(
1740
+ method,
1741
+ serviceInstance,
1742
+ fileParams,
1743
+ liftedDecorators
1744
+ );
1745
+ }
1315
1746
  const decorator = hasParams ? "Post" : "Get";
1747
+ const reqParam = `@Req() request: Request`;
1316
1748
  const bodyParam = hasParams ? `@Body() body: any` : "";
1749
+ const allParams = [reqParam, bodyParam].filter(Boolean).join(", ");
1317
1750
  const body = generateMethodBody(method, serviceInstance, true);
1318
- return ` @${decorator}("${method.name}")
1319
- async ${method.name}(${bodyParam}): Promise<${method.returnType}> {
1751
+ const decoratorPrefix = liftedDecorators ? `${liftedDecorators}
1752
+ ` : "";
1753
+ return `${decoratorPrefix} @${decorator}("${method.name}")
1754
+ async ${method.name}(${allParams}): Promise<${method.returnType}> {
1320
1755
  ${body}
1321
1756
  }`;
1322
1757
  }
1758
+ function generateFileUploadMethod(method, serviceInstance, fileParams, liftedDecorators) {
1759
+ const nonFileParams = getNonFileParams(method);
1760
+ const interceptorDecorator = generateInterceptorDecorator(fileParams);
1761
+ const methodParams = generateFileMethodParams(fileParams, nonFileParams);
1762
+ const allParams = [`@Req() request: Request`, methodParams].filter(Boolean).join(", ");
1763
+ const body = generateFileMethodBody(
1764
+ method,
1765
+ serviceInstance,
1766
+ fileParams,
1767
+ nonFileParams
1768
+ );
1769
+ const decoratorPrefix = liftedDecorators ? `${liftedDecorators}
1770
+ ` : "";
1771
+ return `${decoratorPrefix}${interceptorDecorator} @Post("${method.name}")
1772
+ async ${method.name}(${allParams}): Promise<${method.returnType}> {
1773
+ ${body}
1774
+ }`;
1775
+ }
1776
+ function generateInterceptorDecorator(fileParams) {
1777
+ if (fileParams.length === 1) {
1778
+ const param = fileParams[0];
1779
+ if (param.isArray) {
1780
+ return ` @UseInterceptors(FilesInterceptor("${param.name}"))
1781
+ `;
1782
+ } else {
1783
+ return ` @UseInterceptors(FileInterceptor("${param.name}"))
1784
+ `;
1785
+ }
1786
+ } else {
1787
+ const fields = fileParams.map((p) => `{ name: "${p.name}" }`).join(", ");
1788
+ return ` @UseInterceptors(FileFieldsInterceptor([${fields}]))
1789
+ `;
1790
+ }
1791
+ }
1792
+ function generateFileMethodParams(fileParams, nonFileParams) {
1793
+ const params = [];
1794
+ if (fileParams.length === 1) {
1795
+ const param = fileParams[0];
1796
+ const decorator = param.isArray ? "@UploadedFiles()" : "@UploadedFile()";
1797
+ params.push(`${decorator} ${param.name}: ${param.type}`);
1798
+ } else if (fileParams.length > 1) {
1799
+ params.push(
1800
+ `@UploadedFiles() files: Record<string, Express.Multer.File[]>`
1801
+ );
1802
+ }
1803
+ if (nonFileParams.length > 0) {
1804
+ params.push(`@Body() body: any`);
1805
+ }
1806
+ return params.join(", ");
1807
+ }
1808
+ function generateFileMethodBody(method, serviceInstance, fileParams, nonFileParams) {
1809
+ const lines = [];
1810
+ lines.push(` this.${serviceInstance}.request = request;`);
1811
+ if (fileParams.length > 1) {
1812
+ fileParams.forEach((p) => {
1813
+ if (p.isArray) {
1814
+ lines.push(` const ${p.name} = files["${p.name}"] || [];`);
1815
+ } else {
1816
+ lines.push(
1817
+ ` const ${p.name} = files["${p.name}"]?.[0] || files["${p.name}"];`
1818
+ );
1819
+ }
1820
+ });
1821
+ }
1822
+ if (nonFileParams.length > 0) {
1823
+ const nonFileSchemas = method.paramSchemas.filter((_, i) => {
1824
+ const param = method.params[i];
1825
+ return !fileParams.some((fp) => fp.name === param.name);
1826
+ });
1827
+ if (nonFileSchemas.length > 0 && nonFileSchemas.some((s) => s)) {
1828
+ lines.push(
1829
+ ` const b = typeof body === 'object' && body !== null ? body : {};`
1830
+ );
1831
+ nonFileParams.forEach((p) => {
1832
+ const schemaIndex = method.params.findIndex((mp) => mp.name === p.name);
1833
+ if (method.paramSchemas[schemaIndex]) {
1834
+ lines.push(
1835
+ ` const ${p.name} = ${method.paramSchemas[schemaIndex]}.parse(b.${p.name});`
1836
+ );
1837
+ }
1838
+ });
1839
+ } else {
1840
+ const paramNames = nonFileParams.map((p) => p.name).join(", ");
1841
+ lines.push(` const { ${paramNames} } = body || {};`);
1842
+ }
1843
+ }
1844
+ const callArgs = method.params.map((p) => p.name).join(", ");
1845
+ const serviceCall = `${serviceInstance}.${method.name}(${callArgs})`;
1846
+ if (method.returnSchema) {
1847
+ lines.push(` const res = await this.${serviceCall};`);
1848
+ lines.push(` return ${method.returnSchema}.parse(res);`);
1849
+ } else {
1850
+ lines.push(` return this.${serviceCall};`);
1851
+ }
1852
+ return lines.join("\n");
1853
+ }
1323
1854
  function generateMethodBody(method, serviceInstance, isAsync) {
1324
1855
  const lines = [];
1325
1856
  const hasParams = method.params.length > 0;
1857
+ lines.push(` this.${serviceInstance}.request = request;`);
1326
1858
  if (hasParams && method.isStreamable && method.paramSchemas.length > 0) {
1327
1859
  method.params.forEach((p, i) => {
1328
1860
  lines.push(
@@ -1366,7 +1898,7 @@ function generateMethodBody(method, serviceInstance, isAsync) {
1366
1898
  // src/plugins/generators/tsx_server_stub.ts
1367
1899
  import * as path5 from "path";
1368
1900
  import { createHash } from "crypto";
1369
- import { parseSync as parseSync2, printSync } from "@swc/core";
1901
+ import { parseSync as parseSync2, printSync as printSync2 } from "@swc/core";
1370
1902
  function generateServerStub(filePath, code) {
1371
1903
  const hash = createHash("md5").update(filePath).digest("hex").slice(0, 8);
1372
1904
  const relativeFromSrc = filePath.split("/src/")[1];
@@ -1412,7 +1944,7 @@ function generateServerStub(filePath, code) {
1412
1944
  preservedNodes.push(item);
1413
1945
  }
1414
1946
  }
1415
- const preservedCode = preservedNodes.length > 0 ? printSync({
1947
+ const preservedCode = preservedNodes.length > 0 ? printSync2({
1416
1948
  type: "Module",
1417
1949
  span: ast.span,
1418
1950
  body: preservedNodes,
@@ -1445,7 +1977,7 @@ function extractClassStub(classDecl) {
1445
1977
  const constructorParams = [];
1446
1978
  if (classDecl.decorators) {
1447
1979
  for (const dec of classDecl.decorators) {
1448
- const str = stringifyDecorator(dec);
1980
+ const str = stringifyDecorator2(dec);
1449
1981
  if (str) decorators.push(str);
1450
1982
  }
1451
1983
  }
@@ -1468,8 +2000,8 @@ function extractClassStub(classDecl) {
1468
2000
  constructorParams
1469
2001
  };
1470
2002
  }
1471
- function stringifyDecorator(decorator) {
1472
- const exprCode = printSync({
2003
+ function stringifyDecorator2(decorator) {
2004
+ const exprCode = printSync2({
1473
2005
  type: "Module",
1474
2006
  span: { start: 0, end: 0, ctxt: 0 },
1475
2007
  body: [
@@ -1504,7 +2036,7 @@ function stringifyParam(param) {
1504
2036
  let decorators = [];
1505
2037
  if (param.decorators) {
1506
2038
  for (const d of param.decorators) {
1507
- const str = stringifyDecorator(d);
2039
+ const str = stringifyDecorator2(d);
1508
2040
  if (str) decorators.push(str);
1509
2041
  }
1510
2042
  }
@@ -1607,9 +2139,6 @@ var NodeTypeGuards = class {
1607
2139
  isSignalMember(expr) {
1608
2140
  return this.t.isMemberExpression(expr) && this.t.isIdentifier(expr.property, { name: "value" });
1609
2141
  }
1610
- isBehaviorSubjectMember(expr) {
1611
- return this.t.isMemberExpression(expr) && this.t.isIdentifier(expr.property, { name: "$value" });
1612
- }
1613
2142
  };
1614
2143
  var ASTUtilities = class {
1615
2144
  constructor(t, guards) {
@@ -1617,7 +2146,7 @@ var ASTUtilities = class {
1617
2146
  this.guards = guards;
1618
2147
  }
1619
2148
  getObject(expr) {
1620
- if (this.guards.isSignalMember(expr) || this.guards.isBehaviorSubjectMember(expr)) {
2149
+ if (this.guards.isSignalMember(expr)) {
1621
2150
  return expr.object;
1622
2151
  }
1623
2152
  return expr;
@@ -1655,24 +2184,12 @@ var ASTUtilities = class {
1655
2184
  }
1656
2185
  return expr;
1657
2186
  }
1658
- insertBeforeReturn(body, statements) {
1659
- const returnIndex = body.findIndex(
1660
- (stmt) => this.t.isReturnStatement(stmt)
1661
- );
1662
- if (returnIndex !== -1) {
1663
- body.splice(returnIndex, 0, ...statements);
1664
- } else {
1665
- body.push(...statements);
1666
- }
1667
- }
1668
2187
  addEffectCleanup(scope, effectCall) {
1669
2188
  const cleanupId = scope.generateUidIdentifier("cleanup");
1670
2189
  return [
1671
- // const _cleanup1 = $effect(...)
1672
2190
  this.t.variableDeclaration("const", [
1673
2191
  this.t.variableDeclarator(cleanupId, effectCall)
1674
2192
  ]),
1675
- // self.__cleanup = [...(self.__cleanup || []), _cleanup1]
1676
2193
  this.t.expressionStatement(
1677
2194
  this.t.assignmentExpression(
1678
2195
  "=",
@@ -1785,81 +2302,12 @@ var JSXUtilities = class {
1785
2302
  return svgTags.has(tag);
1786
2303
  }
1787
2304
  };
1788
- var ObservableManager = class {
1789
- constructor(t, guards) {
1790
- this.t = t;
1791
- this.guards = guards;
1792
- }
1793
- getObservableKey(expr) {
1794
- return this.stringifyNode(expr);
1795
- }
1796
- stringifyNode(node) {
1797
- if (!node) return "";
1798
- if (this.t.isThisExpression(node)) return "this";
1799
- if (this.t.isIdentifier(node)) return node.name;
1800
- if (this.t.isMemberExpression(node)) {
1801
- const obj = this.stringifyNode(node.object);
1802
- const prop = node.computed ? `[${this.stringifyNode(node.property)}]` : `.${node.property.name}`;
1803
- return obj + prop;
1804
- }
1805
- if (this.t.isCallExpression(node)) {
1806
- const callee = this.stringifyNode(node.callee);
1807
- const args = node.arguments.map((arg) => this.stringifyNode(arg)).join(",");
1808
- return `${callee}(${args})`;
1809
- }
1810
- if (this.t.isStringLiteral(node)) return `"${node.value}"`;
1811
- if (this.t.isNumericLiteral(node)) return String(node.value);
1812
- return node.type + JSON.stringify(node.name || node.value || "");
1813
- }
1814
- collectObservables(node, observables, astUtils) {
1815
- this.walkNode(node, (n) => {
1816
- if (this.guards.isBehaviorSubjectMember(n)) {
1817
- const observable = astUtils.replaceThisWithSelf(
1818
- n.object
1819
- );
1820
- const key = this.getObservableKey(observable);
1821
- if (!observables.has(key)) {
1822
- observables.set(key, observable);
1823
- }
1824
- }
1825
- });
1826
- }
1827
- replaceObservablesWithSignals(node, observableSignals, astUtils) {
1828
- const cloned = this.t.cloneNode(node, true);
1829
- this.walkNode(cloned, (n) => {
1830
- if (this.guards.isBehaviorSubjectMember(n)) {
1831
- const observable = astUtils.replaceThisWithSelf(n.object);
1832
- const key = this.getObservableKey(observable);
1833
- const signalId = observableSignals.get(key);
1834
- if (signalId) {
1835
- n.object = signalId;
1836
- n.property = this.t.identifier("value");
1837
- }
1838
- }
1839
- });
1840
- return cloned;
1841
- }
1842
- walkNode(node, callback) {
1843
- if (!node || typeof node !== "object") return;
1844
- callback(node);
1845
- for (const key in node) {
1846
- if (["loc", "start", "end", "extra"].includes(key)) continue;
1847
- const value = node[key];
1848
- if (Array.isArray(value)) {
1849
- value.forEach((item) => this.walkNode(item, callback));
1850
- } else if (value && typeof value === "object") {
1851
- this.walkNode(value, callback);
1852
- }
1853
- }
1854
- }
1855
- };
1856
2305
  var ElementTransformer = class {
1857
- constructor(t, guards, astUtils, jsxUtils, observableManager) {
2306
+ constructor(t, guards, astUtils, jsxUtils) {
1858
2307
  this.t = t;
1859
2308
  this.guards = guards;
1860
2309
  this.astUtils = astUtils;
1861
2310
  this.jsxUtils = jsxUtils;
1862
- this.observableManager = observableManager;
1863
2311
  }
1864
2312
  transformElement(path17, scope, context2) {
1865
2313
  if (this.t.isJSXFragment(path17.node)) {
@@ -2030,18 +2478,10 @@ var ElementTransformer = class {
2030
2478
  processComponentAttributes(attributes, props, context2) {
2031
2479
  for (const attr of attributes) {
2032
2480
  if (this.t.isJSXSpreadAttribute(attr)) {
2033
- this.observableManager.collectObservables(
2034
- attr.argument,
2035
- context2.observables,
2036
- this.astUtils
2037
- );
2038
- const replaced = this.observableManager.replaceObservablesWithSignals(
2039
- attr.argument,
2040
- context2.observableSignals,
2041
- this.astUtils
2042
- );
2043
2481
  props.push(
2044
- this.t.spreadElement(this.astUtils.replaceThisWithSelf(replaced))
2482
+ this.t.spreadElement(
2483
+ this.astUtils.replaceThisWithSelf(attr.argument)
2484
+ )
2045
2485
  );
2046
2486
  continue;
2047
2487
  }
@@ -2050,39 +2490,22 @@ var ElementTransformer = class {
2050
2490
  props.push(this.t.objectProperty(this.t.identifier(key), attr.value));
2051
2491
  } else if (this.t.isJSXExpressionContainer(attr.value)) {
2052
2492
  const expr = attr.value.expression;
2053
- this.observableManager.collectObservables(
2054
- expr,
2055
- context2.observables,
2056
- this.astUtils
2057
- );
2058
- if (this.guards.isSignalMember(expr) || this.guards.isBehaviorSubjectMember(expr)) {
2059
- const replaced = this.observableManager.replaceObservablesWithSignals(
2060
- expr,
2061
- context2.observableSignals,
2062
- this.astUtils
2063
- );
2493
+ if (this.guards.isSignalMember(expr)) {
2064
2494
  props.push(
2065
2495
  this.t.objectMethod(
2066
2496
  "get",
2067
2497
  this.t.identifier(key),
2068
2498
  [],
2069
2499
  this.t.blockStatement([
2070
- this.t.returnStatement(
2071
- this.astUtils.replaceThisWithSelf(replaced)
2072
- )
2500
+ this.t.returnStatement(this.astUtils.replaceThisWithSelf(expr))
2073
2501
  ])
2074
2502
  )
2075
2503
  );
2076
2504
  } else {
2077
- const replaced = this.observableManager.replaceObservablesWithSignals(
2078
- expr,
2079
- context2.observableSignals,
2080
- this.astUtils
2081
- );
2082
2505
  props.push(
2083
2506
  this.t.objectProperty(
2084
2507
  this.t.identifier(key),
2085
- this.astUtils.replaceThisWithSelf(replaced)
2508
+ this.astUtils.replaceThisWithSelf(expr)
2086
2509
  )
2087
2510
  );
2088
2511
  }
@@ -2105,22 +2528,12 @@ var ElementTransformer = class {
2105
2528
  let hrefValue = null;
2106
2529
  for (const attr of attributes) {
2107
2530
  if (this.t.isJSXSpreadAttribute(attr)) {
2108
- this.observableManager.collectObservables(
2109
- attr.argument,
2110
- context2.observables,
2111
- this.astUtils
2112
- );
2113
- const replaced = this.observableManager.replaceObservablesWithSignals(
2114
- attr.argument,
2115
- context2.observableSignals,
2116
- this.astUtils
2117
- );
2118
2531
  const effectCall = this.t.callExpression(this.t.identifier("$effect"), [
2119
2532
  this.t.arrowFunctionExpression(
2120
2533
  [],
2121
2534
  this.t.callExpression(this.t.identifier("$spread"), [
2122
2535
  elId,
2123
- this.astUtils.replaceThisWithSelf(replaced)
2536
+ this.astUtils.replaceThisWithSelf(attr.argument)
2124
2537
  ])
2125
2538
  )
2126
2539
  ]);
@@ -2135,34 +2548,18 @@ var ElementTransformer = class {
2135
2548
  if (key === "ref") {
2136
2549
  hasRef = true;
2137
2550
  if (this.t.isJSXExpressionContainer(attr.value)) {
2138
- this.observableManager.collectObservables(
2139
- attr.value.expression,
2140
- context2.observables,
2141
- this.astUtils
2142
- );
2143
- const replaced = this.observableManager.replaceObservablesWithSignals(
2144
- attr.value.expression,
2145
- context2.observableSignals,
2146
- this.astUtils
2551
+ refValue = this.astUtils.replaceThisWithSelf(
2552
+ attr.value.expression
2147
2553
  );
2148
- refValue = this.astUtils.replaceThisWithSelf(replaced);
2149
2554
  }
2150
2555
  continue;
2151
2556
  }
2152
2557
  if (key === "dangerouslySetInnerHTML") {
2153
2558
  hasDangerousHTML = true;
2154
2559
  if (this.t.isJSXExpressionContainer(attr.value)) {
2155
- this.observableManager.collectObservables(
2156
- attr.value.expression,
2157
- context2.observables,
2158
- this.astUtils
2560
+ dangerousHTMLValue = this.astUtils.replaceThisWithSelf(
2561
+ attr.value.expression
2159
2562
  );
2160
- const replaced = this.observableManager.replaceObservablesWithSignals(
2161
- attr.value.expression,
2162
- context2.observableSignals,
2163
- this.astUtils
2164
- );
2165
- dangerousHTMLValue = this.astUtils.replaceThisWithSelf(replaced);
2166
2563
  }
2167
2564
  continue;
2168
2565
  }
@@ -2180,9 +2577,9 @@ var ElementTransformer = class {
2180
2577
  this.processStyleAttribute(attr, elId, statements, scope, context2);
2181
2578
  continue;
2182
2579
  }
2183
- this.processRegularAttribute(key, attr, elId, statements, context2);
2580
+ this.processRegularAttribute(key, attr, elId, statements, scope, context2);
2184
2581
  }
2185
- if (tag === "a" && !hasClickHandler && hrefValue && this.isRelativeUrl(hrefValue)) {
2582
+ if (tag === "a" && !hasClickHandler) {
2186
2583
  statements.push(
2187
2584
  this.t.expressionStatement(
2188
2585
  this.t.callExpression(
@@ -2193,13 +2590,25 @@ var ElementTransformer = class {
2193
2590
  [
2194
2591
  this.t.stringLiteral("click"),
2195
2592
  this.t.arrowFunctionExpression(
2196
- [this.t.identifier("event")],
2593
+ [this.t.identifier("e")],
2197
2594
  this.t.callExpression(
2198
2595
  this.t.memberExpression(
2199
2596
  this.t.identifier("Orca"),
2200
2597
  this.t.identifier("navigate")
2201
2598
  ),
2202
- [this.t.identifier("event"), this.t.stringLiteral(hrefValue)]
2599
+ [
2600
+ this.t.identifier("e"),
2601
+ this.t.callExpression(
2602
+ this.t.memberExpression(
2603
+ this.t.memberExpression(
2604
+ this.t.identifier("e"),
2605
+ this.t.identifier("currentTarget")
2606
+ ),
2607
+ this.t.identifier("getAttribute")
2608
+ ),
2609
+ [this.t.stringLiteral("href")]
2610
+ )
2611
+ ]
2203
2612
  )
2204
2613
  )
2205
2614
  ]
@@ -2224,17 +2633,9 @@ var ElementTransformer = class {
2224
2633
  const eventName = key.slice(2).toLowerCase();
2225
2634
  let handler = this.t.nullLiteral();
2226
2635
  if (this.t.isJSXExpressionContainer(attr.value)) {
2227
- this.observableManager.collectObservables(
2228
- attr.value.expression,
2229
- context2.observables,
2230
- this.astUtils
2231
- );
2232
- const replaced = this.observableManager.replaceObservablesWithSignals(
2233
- attr.value.expression,
2234
- context2.observableSignals,
2235
- this.astUtils
2636
+ handler = this.astUtils.replaceThisWithSelf(
2637
+ attr.value.expression
2236
2638
  );
2237
- handler = this.astUtils.replaceThisWithSelf(replaced);
2238
2639
  }
2239
2640
  statements.push(
2240
2641
  this.t.expressionStatement(
@@ -2247,56 +2648,63 @@ var ElementTransformer = class {
2247
2648
  }
2248
2649
  processStyleAttribute(attr, elId, statements, scope, context2) {
2249
2650
  if (!this.t.isJSXExpressionContainer(attr.value)) return;
2250
- this.observableManager.collectObservables(
2251
- attr.value.expression,
2252
- context2.observables,
2253
- this.astUtils
2254
- );
2255
- const replaced = this.observableManager.replaceObservablesWithSignals(
2256
- attr.value.expression,
2257
- context2.observableSignals,
2258
- this.astUtils
2259
- );
2260
2651
  const effectCall = this.t.callExpression(this.t.identifier("$effect"), [
2261
2652
  this.t.arrowFunctionExpression(
2262
2653
  [],
2263
2654
  this.t.callExpression(this.t.identifier("$style"), [
2264
2655
  elId,
2265
- this.astUtils.replaceThisWithSelf(replaced)
2656
+ this.astUtils.replaceThisWithSelf(
2657
+ attr.value.expression
2658
+ )
2266
2659
  ])
2267
2660
  )
2268
2661
  ]);
2269
2662
  const cleanupStatements = this.astUtils.addEffectCleanup(scope, effectCall);
2270
2663
  statements.push(...cleanupStatements);
2271
2664
  }
2272
- processRegularAttribute(key, attr, elId, statements, context2) {
2665
+ processRegularAttribute(key, attr, elId, statements, scope, context2) {
2273
2666
  const attrName = key === "className" ? "class" : key;
2274
2667
  let value;
2668
+ let isReactive = false;
2275
2669
  if (this.t.isStringLiteral(attr.value)) {
2276
2670
  value = attr.value;
2277
2671
  } else if (this.t.isJSXExpressionContainer(attr.value)) {
2278
- this.observableManager.collectObservables(
2279
- attr.value.expression,
2280
- context2.observables,
2281
- this.astUtils
2672
+ const expr = this.astUtils.replaceThisWithSelf(
2673
+ attr.value.expression
2282
2674
  );
2283
- const replaced = this.observableManager.replaceObservablesWithSignals(
2284
- attr.value.expression,
2285
- context2.observableSignals,
2286
- this.astUtils
2675
+ isReactive = this.guards.isSignalMember(
2676
+ attr.value.expression
2287
2677
  );
2288
- value = this.astUtils.replaceThisWithSelf(replaced);
2678
+ value = expr;
2289
2679
  } else {
2290
2680
  value = this.t.booleanLiteral(true);
2291
2681
  }
2292
- statements.push(
2293
- this.t.expressionStatement(
2294
- this.t.callExpression(
2295
- this.t.memberExpression(elId, this.t.identifier("setAttribute")),
2296
- [this.t.stringLiteral(attrName), value]
2682
+ if (isReactive) {
2683
+ const effectCall = this.t.callExpression(this.t.identifier("$effect"), [
2684
+ this.t.arrowFunctionExpression(
2685
+ [],
2686
+ this.t.assignmentExpression(
2687
+ "=",
2688
+ this.t.memberExpression(elId, this.t.identifier(attrName)),
2689
+ value
2690
+ )
2297
2691
  )
2298
- )
2299
- );
2692
+ ]);
2693
+ const cleanupStatements = this.astUtils.addEffectCleanup(
2694
+ scope,
2695
+ effectCall
2696
+ );
2697
+ statements.push(...cleanupStatements);
2698
+ } else {
2699
+ statements.push(
2700
+ this.t.expressionStatement(
2701
+ this.t.callExpression(
2702
+ this.t.memberExpression(elId, this.t.identifier("setAttribute")),
2703
+ [this.t.stringLiteral(attrName), value]
2704
+ )
2705
+ )
2706
+ );
2707
+ }
2300
2708
  }
2301
2709
  processChildren(children, childExpressions, statements, scope, context2) {
2302
2710
  for (const child of children) {
@@ -2306,17 +2714,9 @@ var ElementTransformer = class {
2306
2714
  } else if (this.t.isJSXExpressionContainer(child)) {
2307
2715
  const expr = child.expression;
2308
2716
  if (!this.t.isJSXEmptyExpression(expr)) {
2309
- this.observableManager.collectObservables(
2310
- expr,
2311
- context2.observables,
2312
- this.astUtils
2717
+ childExpressions.push(
2718
+ this.astUtils.replaceThisWithSelf(expr)
2313
2719
  );
2314
- const replaced = this.observableManager.replaceObservablesWithSignals(
2315
- expr,
2316
- context2.observableSignals,
2317
- this.astUtils
2318
- );
2319
- childExpressions.push(this.astUtils.replaceThisWithSelf(replaced));
2320
2720
  }
2321
2721
  } else if (this.t.isJSXElement(child) || this.t.isJSXFragment(child)) {
2322
2722
  const childEl = this.transformElement({ node: child }, scope, context2);
@@ -2341,42 +2741,43 @@ var ElementTransformer = class {
2341
2741
  } else if (this.t.isJSXExpressionContainer(child)) {
2342
2742
  const expr = child.expression;
2343
2743
  if (this.t.isJSXEmptyExpression(expr)) continue;
2344
- this.observableManager.collectObservables(
2345
- expr,
2346
- context2.observables,
2347
- this.astUtils
2348
- );
2349
- let insertedValue;
2350
- if (this.guards.isSignalMember(expr)) {
2351
- insertedValue = this.astUtils.getObject(
2352
- expr
2353
- );
2354
- } else if (this.guards.isBehaviorSubjectMember(expr)) {
2355
- const replaced = this.observableManager.replaceObservablesWithSignals(
2744
+ if (this.t.isLogicalExpression(expr, { operator: "&&" })) {
2745
+ this.processConditionalAnd(
2356
2746
  expr,
2357
- context2.observableSignals,
2358
- this.astUtils
2747
+ parentId,
2748
+ statements,
2749
+ scope,
2750
+ context2
2359
2751
  );
2360
- insertedValue = this.astUtils.getObject(replaced);
2361
- } else {
2362
- const replaced = this.observableManager.replaceObservablesWithSignals(
2752
+ } else if (this.t.isConditionalExpression(expr)) {
2753
+ this.processConditionalTernary(
2363
2754
  expr,
2364
- context2.observableSignals,
2365
- this.astUtils
2755
+ parentId,
2756
+ statements,
2757
+ scope,
2758
+ context2
2366
2759
  );
2367
- insertedValue = this.t.arrowFunctionExpression(
2368
- [],
2369
- this.astUtils.replaceThisWithSelf(replaced)
2760
+ } else {
2761
+ let insertedValue;
2762
+ if (this.guards.isSignalMember(expr)) {
2763
+ insertedValue = this.astUtils.getObject(
2764
+ expr
2765
+ );
2766
+ } else {
2767
+ insertedValue = this.t.arrowFunctionExpression(
2768
+ [],
2769
+ this.astUtils.replaceThisWithSelf(expr)
2770
+ );
2771
+ }
2772
+ statements.push(
2773
+ this.t.expressionStatement(
2774
+ this.t.callExpression(this.t.identifier("$insert"), [
2775
+ parentId,
2776
+ insertedValue
2777
+ ])
2778
+ )
2370
2779
  );
2371
2780
  }
2372
- statements.push(
2373
- this.t.expressionStatement(
2374
- this.t.callExpression(this.t.identifier("$insert"), [
2375
- parentId,
2376
- insertedValue
2377
- ])
2378
- )
2379
- );
2380
2781
  } else if (this.t.isJSXElement(child) || this.t.isJSXFragment(child)) {
2381
2782
  const childEl = this.transformElement({ node: child }, scope, context2);
2382
2783
  statements.push(...childEl.statements);
@@ -2391,18 +2792,137 @@ var ElementTransformer = class {
2391
2792
  }
2392
2793
  }
2393
2794
  }
2795
+ processConditionalAnd(expr, parentId, statements, scope, context2) {
2796
+ const condition = expr.left;
2797
+ const consequent = expr.right;
2798
+ const computedId = scope.generateUidIdentifier("c");
2799
+ const branchExpr = this.transformExpressionToBranch(
2800
+ consequent,
2801
+ scope,
2802
+ context2
2803
+ );
2804
+ statements.push(
2805
+ this.t.expressionStatement(
2806
+ this.t.callExpression(this.t.identifier("$insert"), [
2807
+ parentId,
2808
+ this.t.callExpression(
2809
+ this.t.arrowFunctionExpression(
2810
+ [],
2811
+ this.t.blockStatement([
2812
+ this.t.variableDeclaration("var", [
2813
+ this.t.variableDeclarator(
2814
+ computedId,
2815
+ this.t.callExpression(this.t.identifier("$computed"), [
2816
+ this.t.arrowFunctionExpression(
2817
+ [],
2818
+ this.astUtils.replaceThisWithSelf(condition)
2819
+ )
2820
+ ])
2821
+ )
2822
+ ]),
2823
+ this.t.returnStatement(
2824
+ this.t.arrowFunctionExpression(
2825
+ [],
2826
+ this.t.logicalExpression(
2827
+ "&&",
2828
+ this.t.memberExpression(
2829
+ computedId,
2830
+ this.t.identifier("value")
2831
+ ),
2832
+ this.t.callExpression(branchExpr, [])
2833
+ )
2834
+ )
2835
+ )
2836
+ ])
2837
+ ),
2838
+ []
2839
+ )
2840
+ ])
2841
+ )
2842
+ );
2843
+ }
2844
+ processConditionalTernary(expr, parentId, statements, scope, context2) {
2845
+ const condition = expr.test;
2846
+ const consequent = expr.consequent;
2847
+ const alternate = expr.alternate;
2848
+ const computedId = scope.generateUidIdentifier("c");
2849
+ const trueBranchExpr = this.transformExpressionToBranch(
2850
+ consequent,
2851
+ scope,
2852
+ context2
2853
+ );
2854
+ const falseBranchExpr = this.transformExpressionToBranch(
2855
+ alternate,
2856
+ scope,
2857
+ context2
2858
+ );
2859
+ statements.push(
2860
+ this.t.expressionStatement(
2861
+ this.t.callExpression(this.t.identifier("$insert"), [
2862
+ parentId,
2863
+ this.t.callExpression(
2864
+ this.t.arrowFunctionExpression(
2865
+ [],
2866
+ this.t.blockStatement([
2867
+ this.t.variableDeclaration("var", [
2868
+ this.t.variableDeclarator(
2869
+ computedId,
2870
+ this.t.callExpression(this.t.identifier("$computed"), [
2871
+ this.t.arrowFunctionExpression(
2872
+ [],
2873
+ this.astUtils.replaceThisWithSelf(condition)
2874
+ )
2875
+ ])
2876
+ )
2877
+ ]),
2878
+ this.t.returnStatement(
2879
+ this.t.arrowFunctionExpression(
2880
+ [],
2881
+ this.t.conditionalExpression(
2882
+ this.t.memberExpression(
2883
+ computedId,
2884
+ this.t.identifier("value")
2885
+ ),
2886
+ this.t.callExpression(trueBranchExpr, []),
2887
+ this.t.callExpression(falseBranchExpr, [])
2888
+ )
2889
+ )
2890
+ )
2891
+ ])
2892
+ ),
2893
+ []
2894
+ )
2895
+ ])
2896
+ )
2897
+ );
2898
+ }
2899
+ transformExpressionToBranch(node, scope, context2) {
2900
+ if (this.t.isJSXElement(node) || this.t.isJSXFragment(node)) {
2901
+ const { id, statements } = this.transformElement(
2902
+ { node },
2903
+ scope,
2904
+ context2
2905
+ );
2906
+ return this.t.arrowFunctionExpression(
2907
+ [],
2908
+ this.t.blockStatement([...statements, this.t.returnStatement(id)])
2909
+ );
2910
+ }
2911
+ return this.t.arrowFunctionExpression(
2912
+ [],
2913
+ this.astUtils.replaceThisWithSelf(node)
2914
+ );
2915
+ }
2394
2916
  };
2395
2917
  function j2d({ types: t }) {
2396
2918
  const guards = new NodeTypeGuards(t);
2397
2919
  const astUtils = new ASTUtilities(t, guards);
2398
2920
  const jsxUtils = new JSXUtilities(t);
2399
- const observableManager = new ObservableManager(t, guards);
2400
2921
  const elementTransformer = new ElementTransformer(
2401
2922
  t,
2402
2923
  guards,
2403
2924
  astUtils,
2404
- jsxUtils,
2405
- observableManager
2925
+ jsxUtils
2406
2926
  );
2407
2927
  return {
2408
2928
  name: "jsx-to-dom",
@@ -2415,8 +2935,8 @@ function j2d({ types: t }) {
2415
2935
  { local: "$createComponent", imported: "createComponent" },
2416
2936
  { local: "$style", imported: "style" },
2417
2937
  { local: "$spread", imported: "spread" },
2418
- { local: "$toSignal", imported: "toSignal" },
2419
- { local: "$effect", imported: "effect" }
2938
+ { local: "$effect", imported: "effect" },
2939
+ { local: "$computed", imported: "computed" }
2420
2940
  ];
2421
2941
  for (const helper of helpers) {
2422
2942
  path17.unshiftContainer(
@@ -2450,49 +2970,12 @@ function j2d({ types: t }) {
2450
2970
  path17.setData("processed", true);
2451
2971
  const body = path17.node.body;
2452
2972
  if (!t.isBlockStatement(body)) return;
2453
- const observables = /* @__PURE__ */ new Map();
2454
- path17.traverse({
2455
- JSXElement(jsxPath) {
2456
- observableManager.collectObservables(
2457
- jsxPath.node,
2458
- observables,
2459
- astUtils
2460
- );
2461
- },
2462
- JSXFragment(jsxPath) {
2463
- observableManager.collectObservables(
2464
- jsxPath.node,
2465
- observables,
2466
- astUtils
2467
- );
2468
- }
2469
- });
2470
2973
  body.body.unshift(
2471
2974
  t.variableDeclaration("const", [
2472
2975
  t.variableDeclarator(t.identifier("self"), t.thisExpression())
2473
2976
  ])
2474
2977
  );
2475
- const observableSignals = /* @__PURE__ */ new Map();
2476
- const signalDeclarations = [];
2477
- for (const [key, observable] of observables) {
2478
- const signalId = path17.scope.generateUidIdentifier("sig");
2479
- observableSignals.set(key, signalId);
2480
- signalDeclarations.push(
2481
- t.variableDeclaration("const", [
2482
- t.variableDeclarator(
2483
- signalId,
2484
- t.callExpression(t.identifier("$toSignal"), [
2485
- observable,
2486
- t.identifier("self")
2487
- ])
2488
- )
2489
- ])
2490
- );
2491
- }
2492
- if (signalDeclarations.length > 0) {
2493
- astUtils.insertBeforeReturn(body.body, signalDeclarations);
2494
- }
2495
- const context2 = { observables, observableSignals };
2978
+ const context2 = { signals: /* @__PURE__ */ new Map() };
2496
2979
  path17.traverse({
2497
2980
  JSXElement(jsxPath) {
2498
2981
  if (jsxPath.getData("processed")) return;
@@ -2537,7 +3020,7 @@ function j2d({ types: t }) {
2537
3020
  }
2538
3021
 
2539
3022
  // src/plugins/generators/generate_server_component.ts
2540
- import { parseSync as parseSync3, printSync as printSync2 } from "@swc/core";
3023
+ import { parseSync as parseSync3, printSync as printSync3 } from "@swc/core";
2541
3024
  function generateServerComponent(filePath, code) {
2542
3025
  const ast = parseSync3(code, {
2543
3026
  syntax: "typescript",
@@ -2579,7 +3062,7 @@ function generateServerComponent(filePath, code) {
2579
3062
  preservedNodes.push(item);
2580
3063
  }
2581
3064
  }
2582
- const preservedCode = preservedNodes.length > 0 ? printSync2({
3065
+ const preservedCode = preservedNodes.length > 0 ? printSync3({
2583
3066
  type: "Module",
2584
3067
  span: ast.span,
2585
3068
  body: preservedNodes,
@@ -2614,7 +3097,7 @@ function extractClassStub2(classDecl) {
2614
3097
  const constructorParams = [];
2615
3098
  if (classDecl.decorators) {
2616
3099
  for (const dec of classDecl.decorators) {
2617
- const str = stringifyDecorator2(dec);
3100
+ const str = stringifyDecorator3(dec);
2618
3101
  if (str) decorators.push(str);
2619
3102
  }
2620
3103
  }
@@ -2639,8 +3122,8 @@ function extractClassStub2(classDecl) {
2639
3122
  methods
2640
3123
  };
2641
3124
  }
2642
- function stringifyDecorator2(decorator) {
2643
- const exprCode = printSync2({
3125
+ function stringifyDecorator3(decorator) {
3126
+ const exprCode = printSync3({
2644
3127
  type: "Module",
2645
3128
  span: { start: 0, end: 0, ctxt: 0 },
2646
3129
  body: [
@@ -2675,7 +3158,7 @@ function stringifyParam2(param) {
2675
3158
  let decorators = [];
2676
3159
  if (param.decorators) {
2677
3160
  for (const d of param.decorators) {
2678
- const str = stringifyDecorator2(d);
3161
+ const str = stringifyDecorator3(d);
2679
3162
  if (str) decorators.push(str);
2680
3163
  }
2681
3164
  }
@@ -2870,10 +3353,11 @@ ${decoratorsStr}export class ${className} extends _OrcaComponent {
2870
3353
  }
2871
3354
 
2872
3355
  // src/plugins/generators/generate_rpc.ts
3356
+ import { printSync as printSync4 } from "@swc/core";
2873
3357
  function generateRpcStub(filePath, code) {
2874
3358
  try {
2875
3359
  const ast = parseTypeScript(filePath, code);
2876
- const serviceInfo = extractServiceInfo(ast, filePath, false);
3360
+ const serviceInfo = extractServiceInfo(ast, filePath, false, true, true);
2877
3361
  if (!serviceInfo) {
2878
3362
  throw {
2879
3363
  type: "validation",
@@ -2881,7 +3365,6 @@ function generateRpcStub(filePath, code) {
2881
3365
  filePath
2882
3366
  };
2883
3367
  }
2884
- validateServiceInfo(serviceInfo, filePath);
2885
3368
  return generateStubCode2(serviceInfo);
2886
3369
  } catch (error) {
2887
3370
  if (error.type) {
@@ -2895,32 +3378,143 @@ function generateRpcStub(filePath, code) {
2895
3378
  };
2896
3379
  }
2897
3380
  }
3381
+ function hasMulterFileParams2(method) {
3382
+ return method.params.some((param) => {
3383
+ return param.type === "Array<Express.Multer.File>" || param.type === "Express.Multer.File[]" || param.type === "Express.Multer.File";
3384
+ });
3385
+ }
3386
+ function getFileParams2(method) {
3387
+ return method.params.filter(
3388
+ (p) => p.type === "Array<Express.Multer.File>" || p.type === "Express.Multer.File[]" || p.type === "Express.Multer.File"
3389
+ ).map((p) => ({
3390
+ name: p.name,
3391
+ type: p.type,
3392
+ isArray: p.type.includes("Array") || p.type.includes("[]")
3393
+ }));
3394
+ }
3395
+ function getNonFileParams2(method) {
3396
+ return method.params.filter(
3397
+ (p) => p.type !== "Array<Express.Multer.File>" && p.type !== "Express.Multer.File[]" && p.type !== "Express.Multer.File"
3398
+ );
3399
+ }
3400
+ function collectSharedImports(serviceInfo) {
3401
+ const allImports = [];
3402
+ const seen = /* @__PURE__ */ new Set();
3403
+ const sharedParams = serviceInfo.constructorParams.filter(
3404
+ (c) => c.decorators?.some((d) => d === "@Shared()")
3405
+ );
3406
+ for (const param of sharedParams) {
3407
+ if (param.imports) {
3408
+ for (const imp of param.imports) {
3409
+ const key = `${imp.identifier}:${imp.source}`;
3410
+ if (!seen.has(key)) {
3411
+ allImports.push(imp);
3412
+ seen.add(key);
3413
+ }
3414
+ }
3415
+ }
3416
+ }
3417
+ return allImports;
3418
+ }
3419
+ function generateImportStatements(imports) {
3420
+ const importsBySource = /* @__PURE__ */ new Map();
3421
+ for (const imp of imports) {
3422
+ if (!importsBySource.has(imp.source)) {
3423
+ importsBySource.set(imp.source, /* @__PURE__ */ new Set());
3424
+ }
3425
+ importsBySource.get(imp.source).add(imp.identifier);
3426
+ }
3427
+ const statements = [];
3428
+ for (const [source, identifiers] of importsBySource) {
3429
+ const identifierList = Array.from(identifiers).sort().join(", ");
3430
+ statements.push(`import { ${identifierList} } from "${source}";`);
3431
+ }
3432
+ return statements.sort().join("\n");
3433
+ }
2898
3434
  function generateStubCode2(serviceInfo) {
2899
3435
  const className = serviceInfo.className;
2900
3436
  const basePath = serviceNameToPath(className);
2901
3437
  const methods = serviceInfo.methods.map((method) => generateMethod2(method, basePath, className)).join("\n\n");
2902
3438
  const hasStreamable = serviceInfo.methods.some((m) => m.isStreamable);
2903
- const imports = generateImports2(hasStreamable);
3439
+ const sharedImports = collectSharedImports(serviceInfo);
3440
+ const imports = generateImports2(hasStreamable, sharedImports);
3441
+ let clientConstructorFields = serviceInfo.constructorParams.filter((c) => c.decorators?.some((d) => d === "@Shared()")).map((c) => {
3442
+ const filteredDecorators = c.decorators?.filter((d) => d !== "@Shared()") || [];
3443
+ const decoratorStr = filteredDecorators.length > 0 ? filteredDecorators.join("\n") + "\n" : "";
3444
+ return `${decoratorStr}${c.accessibility}${c.isReadonly ? " readonly " : " "}${c.name}: ${c.type},`;
3445
+ }).join("\n");
2904
3446
  return `${imports}
2905
3447
 
2906
3448
  @Injectable()
2907
3449
  export class ${className} {
3450
+ constructor(
3451
+ ${clientConstructorFields}
3452
+ ) {}
3453
+
2908
3454
  ${methods}
2909
3455
  }`;
2910
3456
  }
2911
- function generateImports2(hasStreamable) {
3457
+ function generateImports2(hasStreamable, sharedImports) {
2912
3458
  let imports = `import { Injectable } from "@kithinji/orca";
2913
3459
  `;
2914
3460
  if (hasStreamable) {
2915
- imports += `import { Observable } from "@kithinji/orca";
3461
+ imports += `import { Observable } from "rxjs";
2916
3462
  `;
2917
3463
  }
3464
+ if (sharedImports.length > 0) {
3465
+ const sharedImportStatements = generateImportStatements(sharedImports);
3466
+ if (sharedImportStatements) {
3467
+ imports += sharedImportStatements + "\n";
3468
+ }
3469
+ }
2918
3470
  return imports;
2919
3471
  }
2920
3472
  function generateMethod2(method, basePath, serviceName) {
3473
+ if (method.lift) {
3474
+ const classWrapper = {
3475
+ type: "Module",
3476
+ span: { start: 0, end: 0 },
3477
+ body: [
3478
+ {
3479
+ type: "ClassDeclaration",
3480
+ span: { start: 0, end: 0 },
3481
+ declare: false,
3482
+ ctxt: 0,
3483
+ identifier: {
3484
+ type: "Identifier",
3485
+ value: "",
3486
+ optional: false,
3487
+ span: { start: 0, end: 0 },
3488
+ ctxt: 0
3489
+ },
3490
+ body: [JSON.parse(JSON.stringify(method.ast))],
3491
+ decorators: [],
3492
+ superClass: null
3493
+ }
3494
+ ],
3495
+ shebang: null
3496
+ };
3497
+ let { code } = printSync4(classWrapper);
3498
+ const lines = code.split("\n");
3499
+ lines.shift();
3500
+ lines.pop();
3501
+ lines.pop();
3502
+ const methodCode = lines.join("\n");
3503
+ return methodCode;
3504
+ }
2921
3505
  if (method.isStreamable) {
3506
+ if (hasMulterFileParams2(method)) {
3507
+ throw new Error(
3508
+ `Method '${method.name}' cannot have file parameters for streaming endpoints`
3509
+ );
3510
+ }
2922
3511
  return generateSseMethod(method, basePath);
2923
3512
  }
3513
+ const fileParams = getFileParams2(method);
3514
+ const hasFileParams = fileParams.length > 0;
3515
+ if (hasFileParams) {
3516
+ return generateFileUploadMethod2(method, basePath, fileParams);
3517
+ }
2924
3518
  const params = method.params.map((p) => `${p.name}: ${p.type}`).join(", ");
2925
3519
  const hasParams = method.params.length > 0;
2926
3520
  if (!hasParams) {
@@ -2928,36 +3522,130 @@ function generateMethod2(method, basePath, serviceName) {
2928
3522
  }
2929
3523
  return generatePostMethod(method, basePath, params);
2930
3524
  }
3525
+ function generateFileUploadMethod2(method, basePath, fileParams) {
3526
+ const allParams = method.params.map((p) => `${p.name}: ${p.type}`).join(", ");
3527
+ const nonFileParams = getNonFileParams2(method);
3528
+ const returnType = `Promise<${method.returnType}>`;
3529
+ const lines = [];
3530
+ lines.push(` const formData = new FormData();`);
3531
+ lines.push(``);
3532
+ fileParams.forEach((fp) => {
3533
+ if (fp.isArray) {
3534
+ lines.push(` if (${fp.name} && Array.isArray(${fp.name})) {`);
3535
+ lines.push(` ${fp.name}.forEach((file) => {`);
3536
+ lines.push(` formData.append('${fp.name}', file);`);
3537
+ lines.push(` });`);
3538
+ lines.push(` }`);
3539
+ } else {
3540
+ lines.push(` if (${fp.name}) {`);
3541
+ lines.push(` formData.append('${fp.name}', ${fp.name});`);
3542
+ lines.push(` }`);
3543
+ }
3544
+ });
3545
+ if (nonFileParams.length > 0) {
3546
+ lines.push(``);
3547
+ nonFileParams.forEach((p) => {
3548
+ lines.push(` if (${p.name} !== undefined) {`);
3549
+ lines.push(
3550
+ ` formData.append('${p.name}', typeof ${p.name} === 'object' ? JSON.stringify(${p.name}) : String(${p.name}));`
3551
+ );
3552
+ lines.push(` }`);
3553
+ });
3554
+ }
3555
+ lines.push(``);
3556
+ lines.push(` let headers = this.${method.name}Headers?.() || {};`);
3557
+ lines.push(``);
3558
+ lines.push(` if(headers instanceof Promise) {`);
3559
+ lines.push(` headers = await headers;`);
3560
+ lines.push(` }`);
3561
+ lines.push(``);
3562
+ lines.push(
3563
+ ` const response = await fetch(\`/api/${basePath}/${method.name}\`, {`
3564
+ );
3565
+ lines.push(` method: 'POST',`);
3566
+ lines.push(` headers: {`);
3567
+ lines.push(` ...headers`);
3568
+ lines.push(` },`);
3569
+ lines.push(` body: formData,`);
3570
+ lines.push(` });`);
3571
+ lines.push(``);
3572
+ lines.push(` if (!response.ok) {`);
3573
+ lines.push(
3574
+ ` throw new Error(\`HTTP error! status: \${response.status}\`);`
3575
+ );
3576
+ lines.push(` }`);
3577
+ lines.push(``);
3578
+ lines.push(` return response.json();`);
3579
+ return ` async ${method.name}(${allParams}): ${returnType} {
3580
+ ${lines.join("\n")}
3581
+ }`;
3582
+ }
2931
3583
  function generateSseMethod(method, basePath) {
2932
3584
  const params = method.params.map((p) => `${p.name}: ${p.type}`).join(", ");
2933
3585
  const hasParams = method.params.length > 0;
2934
3586
  let urlBuilder;
3587
+ const headerCheck = `this.${method.name}Headers`;
2935
3588
  if (hasParams) {
2936
3589
  const queryParams = method.params.map((p) => `${p.name}=\${encodeURIComponent(${p.name})}`).join("&");
2937
- urlBuilder = `\`/${basePath}/${method.name}?${queryParams}\``;
3590
+ urlBuilder = `\`/api/${basePath}/${method.name}?${queryParams}\``;
2938
3591
  } else {
2939
- urlBuilder = `\`/${basePath}/${method.name}\``;
3592
+ urlBuilder = `\`/api/${basePath}/${method.name}\``;
2940
3593
  }
2941
3594
  return ` ${method.name}(${params}): Observable<${method.returnType}> {
2942
3595
  return new Observable((observer) => {
2943
- const eventSource = new EventSource(${urlBuilder});
3596
+ let url = ${urlBuilder};
2944
3597
 
2945
- eventSource.onmessage = (event) => {
2946
- try {
2947
- const data = JSON.parse(event.data);
2948
- observer.next(data);
2949
- } catch (error) {
2950
- observer.error?.(error);
3598
+ // Get headers and append them as query parameters for SSE
3599
+ const getHeaders = async () => {
3600
+ let headers = ${headerCheck}?.() || {};
3601
+
3602
+ if (headers instanceof Promise) {
3603
+ headers = await headers;
3604
+ }
3605
+
3606
+ // Convert headers to query parameters for EventSource
3607
+ const headerParams = new URLSearchParams();
3608
+ for (const [key, value] of Object.entries(headers)) {
3609
+ headerParams.append(key, String(value));
2951
3610
  }
3611
+
3612
+ const headerString = headerParams.toString();
3613
+ if (headerString) {
3614
+ url += ${hasParams ? "`&${headerString}`" : "`?${headerString}`"};
3615
+ }
3616
+
3617
+ const eventSource = new EventSource(url);
3618
+
3619
+ eventSource.onmessage = (event) => {
3620
+ try {
3621
+ const data = JSON.parse(event.data);
3622
+ observer.next(data);
3623
+ } catch (error) {
3624
+ observer.error?.(error);
3625
+ }
3626
+ };
3627
+
3628
+ eventSource.onerror = (error) => {
3629
+ observer.error?.(error);
3630
+ eventSource.close();
3631
+ };
3632
+
3633
+ return () => {
3634
+ eventSource.close();
3635
+ };
2952
3636
  };
2953
3637
 
2954
- eventSource.onerror = (error) => {
3638
+ getHeaders().then((cleanup) => {
3639
+ // Store cleanup function for unsubscribe
3640
+ if (cleanup) {
3641
+ observer.add(cleanup);
3642
+ }
3643
+ }).catch((error) => {
2955
3644
  observer.error?.(error);
2956
- eventSource.close();
2957
- };
3645
+ });
2958
3646
 
2959
3647
  return () => {
2960
- eventSource.close();
3648
+ // Cleanup handled in getHeaders
2961
3649
  };
2962
3650
  });
2963
3651
  }`;
@@ -2966,10 +3654,17 @@ function generateGetMethod(method, basePath) {
2966
3654
  const params = method.params.map((p) => `${p.name}: ${p.type}`).join(", ");
2967
3655
  const returnType = `Promise<${method.returnType}>`;
2968
3656
  return ` async ${method.name}(${params}): ${returnType} {
2969
- const response = await fetch(\`/${basePath}/${method.name}\`, {
3657
+ let headers = this.${method.name}Headers?.() || {};
3658
+
3659
+ if(headers instanceof Promise) {
3660
+ headers = await headers;
3661
+ }
3662
+
3663
+ const response = await fetch(\`/api/${basePath}/${method.name}\`, {
2970
3664
  method: 'GET',
2971
3665
  headers: {
2972
3666
  'Content-Type': 'application/json',
3667
+ ...headers
2973
3668
  }
2974
3669
  });
2975
3670
 
@@ -2984,10 +3679,17 @@ function generatePostMethod(method, basePath, params) {
2984
3679
  const paramNames = method.params.map((p) => p.name).join(", ");
2985
3680
  const returnType = `Promise<${method.returnType}>`;
2986
3681
  return ` async ${method.name}(${params}): ${returnType} {
2987
- const response = await fetch(\`/${basePath}/${method.name}\`, {
3682
+ let headers = this.${method.name}Headers?.() || {};
3683
+
3684
+ if(headers instanceof Promise) {
3685
+ headers = await headers;
3686
+ }
3687
+
3688
+ const response = await fetch(\`/api/${basePath}/${method.name}\`, {
2988
3689
  method: 'POST',
2989
3690
  headers: {
2990
3691
  'Content-Type': 'application/json',
3692
+ ...headers
2991
3693
  },
2992
3694
  body: JSON.stringify({ ${paramNames} }),
2993
3695
  });
@@ -3044,11 +3746,11 @@ function parseFileMetadata(source, path17) {
3044
3746
  }
3045
3747
  var ServerBuildTransformer = class {
3046
3748
  async transformPublicFile(source, path17) {
3047
- const controllerCode = generateController(path17, source);
3048
- if (controllerCode) {
3049
- source = `${source}
3749
+ const result = generateController(path17, source);
3750
+ if (result) {
3751
+ source = `${result.transformedServiceCode}
3050
3752
 
3051
- ${controllerCode}
3753
+ ${result.controllerCode}
3052
3754
  `;
3053
3755
  }
3054
3756
  return swcTransform(source, path17);
@@ -3183,7 +3885,48 @@ function useMyPlugin(options) {
3183
3885
  import { parseSync as parseSync4 } from "@swc/core";
3184
3886
  import * as fs3 from "fs";
3185
3887
  import * as path7 from "path";
3888
+ function findProjectRoot(startPath) {
3889
+ let currentDir = path7.dirname(startPath);
3890
+ while (currentDir !== path7.parse(currentDir).root) {
3891
+ const packageJsonPath = path7.join(currentDir, "package.json");
3892
+ if (fs3.existsSync(packageJsonPath)) {
3893
+ return currentDir;
3894
+ }
3895
+ currentDir = path7.dirname(currentDir);
3896
+ }
3897
+ return process.cwd();
3898
+ }
3899
+ function resolvePathAlias(importPath, projectRoot) {
3900
+ if (importPath.startsWith("@/")) {
3901
+ const possiblePaths = [
3902
+ path7.join(projectRoot, "src", importPath.slice(2)),
3903
+ path7.join(projectRoot, importPath.slice(2))
3904
+ ];
3905
+ for (const possiblePath of possiblePaths) {
3906
+ const extensions = ["", ".ts", ".tsx", ".js", ".jsx"];
3907
+ for (const ext of extensions) {
3908
+ const fullPath = possiblePath + ext;
3909
+ if (fs3.existsSync(fullPath) && fs3.statSync(fullPath).isFile()) {
3910
+ return fullPath;
3911
+ }
3912
+ }
3913
+ const indexFiles = ["/index.ts", "/index.tsx", "/index.js", "/index.jsx"];
3914
+ for (const indexFile of indexFiles) {
3915
+ const fullPath = possiblePath + indexFile;
3916
+ if (fs3.existsSync(fullPath) && fs3.statSync(fullPath).isFile()) {
3917
+ return fullPath;
3918
+ }
3919
+ }
3920
+ }
3921
+ }
3922
+ return null;
3923
+ }
3186
3924
  function resolveFilePath(fromFile, importPath) {
3925
+ if (importPath.startsWith("@/")) {
3926
+ const projectRoot = findProjectRoot(fromFile);
3927
+ const aliasResolved = resolvePathAlias(importPath, projectRoot);
3928
+ if (aliasResolved) return aliasResolved;
3929
+ }
3187
3930
  if (!importPath.startsWith(".")) return null;
3188
3931
  const dir = path7.dirname(fromFile);
3189
3932
  const basePath = path7.resolve(dir, importPath);
@@ -3606,7 +4349,7 @@ export async function navigate(event, url) {
3606
4349
  const injector = getCurrentInjector();
3607
4350
 
3608
4351
  if (injector) {
3609
- const navigate = injector.resolve(Navigate);
4352
+ const navigate = await injector.resolve(Navigate);
3610
4353
  navigate.go(url);
3611
4354
  } else {
3612
4355
  window.location.href = url;
@@ -3669,7 +4412,7 @@ var HotReloadManager = class {
3669
4412
  });
3670
4413
  });
3671
4414
  this.logger.info(
3672
- `Hot reload server listening on ws://localhost:${this.port}`
4415
+ `Hot reloads servers listening on ws://localhost:${this.port}`
3673
4416
  );
3674
4417
  }
3675
4418
  reload() {
@@ -4891,7 +5634,7 @@ function createPage(name) {
4891
5634
  import { ${listComponent} } from "./components/${name}-list.component";
4892
5635
 
4893
5636
  @Component({
4894
- deps: [${listComponent}]
5637
+ inject: [${listComponent}]
4895
5638
  })
4896
5639
  export class ${pageName} {
4897
5640
  build() {
@@ -4987,7 +5730,8 @@ function genPackageJson(name) {
4987
5730
  "reflect-metadata": "latest",
4988
5731
  zod: "^4.2.1",
4989
5732
  "@kithinji/orca": "latest",
4990
- "@kithinji/arcane": "latest"
5733
+ "@kithinji/arcane": "latest",
5734
+ rxjs: "latest"
4991
5735
  },
4992
5736
  devDependencies: {
4993
5737
  "@types/node": "^20.19.27",
@@ -5010,6 +5754,7 @@ function gentsconfig() {
5010
5754
  jsxImportSource: "@kithinji/orca",
5011
5755
  experimentalDecorators: true,
5012
5756
  emitDecoratorMetadata: true,
5757
+ strictPropertyInitialization: false,
5013
5758
  baseUrl: ".",
5014
5759
  paths: {
5015
5760
  "@/*": ["src/*"]
@@ -5056,7 +5801,7 @@ function genIndexHtml(name) {
5056
5801
  <meta charset="UTF-8" />
5057
5802
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
5058
5803
  <title>${name} app</title>
5059
- <link rel="stylesheet" href="index.css" />
5804
+ <link rel="stylesheet" href="/index.css" />
5060
5805
  </head>
5061
5806
  <body>
5062
5807
  <div id="root"></div>
@@ -5078,7 +5823,7 @@ import {
5078
5823
  } from "@kithinji/orca";
5079
5824
 
5080
5825
  @Component({
5081
- deps: [RouterOutlet],
5826
+ inject: [RouterOutlet],
5082
5827
  })
5083
5828
  class AppComponent {
5084
5829
  build() {
@@ -5093,8 +5838,8 @@ class AppComponent {
5093
5838
  })
5094
5839
  class AppModule {}
5095
5840
 
5096
- export function bootstrap() {
5097
- BrowserFactory.create(AppModule, document.getElementById("root")!);
5841
+ export async function bootstrap() {
5842
+ await BrowserFactory.create(AppModule, document.getElementById("root")!);
5098
5843
  }
5099
5844
 
5100
5845
  bootstrap();
@@ -6769,7 +7514,7 @@ async function compileFiles(entryPoints) {
6769
7514
 
6770
7515
  // src/index.ts
6771
7516
  var program = new Command();
6772
- program.name("pod").description("Pod cli tool").version("1.0.42");
7517
+ program.name("pod").description("Pod cli tool").version("1.0.46");
6773
7518
  program.command("new <name> [type]").description("Start a new Orca Project").action(async (name, type) => {
6774
7519
  const orca = async () => {
6775
7520
  await addNew(name);
@@ -6836,6 +7581,7 @@ program.command("deploy").description("Deploy to a target environment").argument
6836
7581
  process.exit(1);
6837
7582
  }
6838
7583
  });
7584
+ program.parse(process.argv);
6839
7585
  export {
6840
7586
  config_exports as config,
6841
7587
  expandMacros,
@@ -6846,7 +7592,6 @@ export {
6846
7592
  macros_exports as macros,
6847
7593
  mergeConfig,
6848
7594
  plugins_exports as plugins,
6849
- program,
6850
7595
  resetGlobalMacroGraph,
6851
7596
  store_exports as store,
6852
7597
  stylePlugin,