@kithinji/pod 1.0.43 → 1.0.48

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
@@ -500,7 +500,7 @@ function resolveImportFullPath(symbolName, sourceFile, compilerOptions) {
500
500
  };
501
501
  }
502
502
  function isNpmPackage(importPath) {
503
- return !importPath.startsWith(".") && !importPath.startsWith("/") && !path3.isAbsolute(importPath);
503
+ return !importPath.startsWith(".") && !importPath.startsWith("/") && !importPath.startsWith("@/") && !path3.isAbsolute(importPath);
504
504
  }
505
505
  function findVariableDeclarationInFile(variableName, sourceFile) {
506
506
  let found;
@@ -930,6 +930,7 @@ async function expandMacros(source, filePath, projectRoot = process.cwd()) {
930
930
 
931
931
  // src/plugins/generators/generate_controller.ts
932
932
  import * as path4 from "path";
933
+ import { printSync } from "@swc/core";
933
934
 
934
935
  // src/plugins/generators/utils.ts
935
936
  import {
@@ -964,8 +965,20 @@ function hasInjectableDecorator(decorators) {
964
965
  return expr.type === "Identifier" && expr.value === "Injectable" || expr.type === "CallExpression" && expr.callee.type === "Identifier" && expr.callee.value === "Injectable";
965
966
  });
966
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
+ }
967
977
  function isPublicMethod(member) {
968
- 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";
969
982
  }
970
983
  function getMethodName(method) {
971
984
  if (method.key.type === "Identifier") {
@@ -1027,23 +1040,36 @@ function analyzeReturnType(method) {
1027
1040
  isSubjectLike: false
1028
1041
  };
1029
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
+ }
1030
1056
  function stringifyType(node) {
1031
1057
  if (!node) return "any";
1032
1058
  switch (node.type) {
1033
1059
  case "TsKeywordType":
1034
1060
  return node.kind;
1035
- case "TsTypeReference":
1036
- if (node.typeName.type !== "Identifier") return "any";
1037
- const base = node.typeName.value;
1061
+ case "TsTypeReference": {
1062
+ const base = stringifyEntityName(node.typeName);
1038
1063
  const args = node.typeParams?.params ? `<${node.typeParams.params.map(stringifyType).join(", ")}>` : "";
1039
1064
  return base + args;
1065
+ }
1040
1066
  case "TsArrayType":
1041
1067
  return `${stringifyType(node.elemType)}[]`;
1042
1068
  case "TsUnionType":
1043
1069
  return node.types.map(stringifyType).join(" | ");
1044
1070
  case "TsIntersectionType":
1045
1071
  return node.types.map(stringifyType).join(" & ");
1046
- case "TsTypeLiteral":
1072
+ case "TsTypeLiteral": {
1047
1073
  const props = node.members.map((member) => {
1048
1074
  if (member.type === "TsPropertySignature") {
1049
1075
  const key = member.key.type === "Identifier" ? member.key.value : "";
@@ -1053,10 +1079,55 @@ function stringifyType(node) {
1053
1079
  return "";
1054
1080
  }).filter(Boolean);
1055
1081
  return `{ ${props.join("; ")} }`;
1082
+ }
1056
1083
  default:
1057
1084
  return "any";
1058
1085
  }
1059
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
+ }
1060
1131
  function extractMethodParams(params) {
1061
1132
  return params.map((p) => {
1062
1133
  const pat = p.pat;
@@ -1064,17 +1135,222 @@ function extractMethodParams(params) {
1064
1135
  return {
1065
1136
  name: "param",
1066
1137
  type: "any",
1067
- decorators: []
1138
+ decorators: extractParamDecorators(p)
1068
1139
  };
1069
1140
  }
1070
1141
  return {
1071
1142
  name: pat.value,
1072
1143
  type: pat.typeAnnotation ? stringifyType(pat.typeAnnotation.typeAnnotation) : "any",
1073
- decorators: []
1144
+ decorators: extractParamDecorators(p)
1074
1145
  };
1075
1146
  });
1076
1147
  }
1077
- 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) {
1078
1354
  if (!decorators) return { paramSchemas: [] };
1079
1355
  for (const decorator of decorators) {
1080
1356
  const expr = decorator.expression;
@@ -1084,6 +1360,14 @@ function extractSignature(decorators, paramCount) {
1084
1360
  const schemaStrings = args.map(
1085
1361
  (arg) => stringifyExpression(arg.expression)
1086
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
+ }
1087
1371
  if (args.length === 1) {
1088
1372
  return { paramSchemas: [], returnSchema: schemaStrings[0] };
1089
1373
  }
@@ -1111,15 +1395,36 @@ function stringifyExpression(expr) {
1111
1395
  }
1112
1396
  return "any";
1113
1397
  }
1114
- function extractMethods(classDecl, filePath, includeSignatures = true) {
1398
+ function extractMethods(classDecl, filePath, includeSignatures = true, includeHeaders = false) {
1115
1399
  const methods = [];
1116
1400
  const className = classDecl.identifier?.value || "UnknownClass";
1401
+ if (!classDecl.body || !Array.isArray(classDecl.body)) {
1402
+ return methods;
1403
+ }
1117
1404
  for (const member of classDecl.body) {
1118
- if (!isPublicMethod(member)) continue;
1405
+ if (!isClassMethod(member)) continue;
1119
1406
  const method = member;
1120
1407
  const methodName = getMethodName(method);
1121
1408
  if (!methodName) continue;
1409
+ const endsWithHeaders = methodName.endsWith("Headers");
1122
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;
1123
1428
  if (!returnTypeInfo.isStreamable && !method.function.async) {
1124
1429
  throw {
1125
1430
  type: "validation",
@@ -1132,7 +1437,10 @@ function extractMethods(classDecl, filePath, includeSignatures = true) {
1132
1437
  }
1133
1438
  const signatures = includeSignatures ? extractSignature(
1134
1439
  method.function.decorators,
1135
- method.function.params.length
1440
+ method.function.params.length,
1441
+ className,
1442
+ methodName,
1443
+ filePath
1136
1444
  ) : { paramSchemas: [] };
1137
1445
  methods.push({
1138
1446
  name: methodName,
@@ -1142,11 +1450,41 @@ function extractMethods(classDecl, filePath, includeSignatures = true) {
1142
1450
  isStreamable: returnTypeInfo.isStreamable,
1143
1451
  streamType: returnTypeInfo.streamType,
1144
1452
  paramSchemas: signatures.paramSchemas,
1145
- returnSchema: signatures.returnSchema
1453
+ returnSchema: signatures.returnSchema,
1454
+ decorators: extractMethodDecorators(method),
1455
+ lift: false,
1456
+ ast: method
1146
1457
  });
1147
1458
  }
1148
1459
  return methods;
1149
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
+ }
1150
1488
  function serviceNameToPath(serviceName) {
1151
1489
  return serviceName.replace(/Service$/, "").replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
1152
1490
  }
@@ -1170,23 +1508,43 @@ function validateServiceInfo(serviceInfo, filePath) {
1170
1508
  );
1171
1509
  }
1172
1510
  serviceInfo.methods.forEach((method) => {
1173
- if (method.params.length > 0 && method.paramSchemas?.length === 0) {
1511
+ if (method.params.length > 0 && method.paramSchemas?.length === 0 && !method.returnSchema) {
1174
1512
  console.warn(
1175
- `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`
1176
1514
  );
1177
1515
  }
1178
1516
  });
1179
1517
  }
1180
- function extractServiceInfo(ast, filePath, includeSignatures = true) {
1518
+ function extractServiceInfo(ast, filePath, includeSignatures = true, includeHeaders = false, includeFields = false) {
1181
1519
  try {
1182
1520
  const serviceClass = findInjectableClass(ast);
1183
1521
  const importMap = extractImportMap(ast);
1184
1522
  if (!serviceClass?.identifier) {
1185
1523
  return null;
1186
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
+ }
1187
1538
  return {
1188
1539
  className: serviceClass.identifier.value,
1189
- 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) : [],
1190
1548
  hasInjectable: true,
1191
1549
  importMap
1192
1550
  };
@@ -1204,12 +1562,18 @@ function extractServiceInfo(ast, filePath, includeSignatures = true) {
1204
1562
  function generateController(filePath, code) {
1205
1563
  try {
1206
1564
  const ast = parseTypeScript(filePath, code);
1207
- const serviceInfo = extractServiceInfo(ast, filePath, true);
1565
+ const serviceInfo = extractServiceInfo(ast, filePath, true, false);
1208
1566
  if (!serviceInfo || !serviceInfo.hasInjectable) {
1209
1567
  return null;
1210
1568
  }
1211
1569
  validateServiceInfo(serviceInfo, filePath);
1212
- 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
+ };
1213
1577
  } catch (error) {
1214
1578
  if (error.type) {
1215
1579
  throw error;
@@ -1232,7 +1596,7 @@ function generateControllerCode(serviceInfo, filePath) {
1232
1596
  const serviceInstance = toInstanceName(serviceName);
1233
1597
  return `${imports}
1234
1598
 
1235
- @Controller("/${controllerPath}", {
1599
+ @Controller("/api/${controllerPath}", {
1236
1600
  providedIn: "root",
1237
1601
  })
1238
1602
  export class ${controllerName} {
@@ -1277,16 +1641,40 @@ function generateImports(serviceInfo, serviceName, serviceImportPath) {
1277
1641
  const hasStreamableWithParams = serviceInfo.methods.some(
1278
1642
  (m) => m.isStreamable && m.params.length > 0
1279
1643
  );
1280
- 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"];
1281
1655
  if (hasPost) decorators.push("Post");
1282
1656
  if (hasGet) decorators.push("Get");
1283
1657
  if (hasPost) decorators.push("Body");
1284
1658
  if (hasSse) decorators.push("Sse");
1285
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");
1286
1664
  let importStrings = `import { ${decorators.join(
1287
1665
  ", "
1288
1666
  )} } from "@kithinji/orca";
1289
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
+ }
1290
1678
  importGroups.forEach((ids, source) => {
1291
1679
  const filteredIds = Array.from(ids).filter((id) => id !== serviceName);
1292
1680
  if (filteredIds.length > 0) {
@@ -1298,32 +1686,175 @@ function generateImports(serviceInfo, serviceName, serviceImportPath) {
1298
1686
  });
1299
1687
  return importStrings;
1300
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
+ }
1301
1711
  function generateMethods(serviceInfo) {
1302
1712
  return serviceInfo.methods.map((m) => generateMethod(m, serviceInfo.className)).join("\n\n");
1303
1713
  }
1304
1714
  function generateMethod(method, serviceName) {
1305
1715
  const hasParams = method.params.length > 0;
1306
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") || "";
1307
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`;
1308
1727
  const queryParams = hasParams ? method.params.map((p) => `@Query('${p.name}') ${p.name}: ${p.type}`).join(", ") : "";
1728
+ const allParams2 = [reqParam2, queryParams].filter(Boolean).join(", ");
1309
1729
  const body2 = generateMethodBody(method, serviceInstance, false);
1310
1730
  const returnTypeName = method.streamType || "Observable";
1311
- return ` @Sse("${method.name}")
1312
- ${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}> {
1313
1735
  ${body2}
1314
1736
  }`;
1315
1737
  }
1738
+ if (hasFileParams) {
1739
+ return generateFileUploadMethod(
1740
+ method,
1741
+ serviceInstance,
1742
+ fileParams,
1743
+ liftedDecorators
1744
+ );
1745
+ }
1316
1746
  const decorator = hasParams ? "Post" : "Get";
1747
+ const reqParam = `@Req() request: Request`;
1317
1748
  const bodyParam = hasParams ? `@Body() body: any` : "";
1749
+ const allParams = [reqParam, bodyParam].filter(Boolean).join(", ");
1318
1750
  const body = generateMethodBody(method, serviceInstance, true);
1319
- return ` @${decorator}("${method.name}")
1320
- 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}> {
1321
1755
  ${body}
1322
1756
  }`;
1323
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
+ }
1324
1854
  function generateMethodBody(method, serviceInstance, isAsync) {
1325
1855
  const lines = [];
1326
1856
  const hasParams = method.params.length > 0;
1857
+ lines.push(` this.${serviceInstance}.request = request;`);
1327
1858
  if (hasParams && method.isStreamable && method.paramSchemas.length > 0) {
1328
1859
  method.params.forEach((p, i) => {
1329
1860
  lines.push(
@@ -1367,7 +1898,7 @@ function generateMethodBody(method, serviceInstance, isAsync) {
1367
1898
  // src/plugins/generators/tsx_server_stub.ts
1368
1899
  import * as path5 from "path";
1369
1900
  import { createHash } from "crypto";
1370
- import { parseSync as parseSync2, printSync } from "@swc/core";
1901
+ import { parseSync as parseSync2, printSync as printSync2 } from "@swc/core";
1371
1902
  function generateServerStub(filePath, code) {
1372
1903
  const hash = createHash("md5").update(filePath).digest("hex").slice(0, 8);
1373
1904
  const relativeFromSrc = filePath.split("/src/")[1];
@@ -1413,7 +1944,7 @@ function generateServerStub(filePath, code) {
1413
1944
  preservedNodes.push(item);
1414
1945
  }
1415
1946
  }
1416
- const preservedCode = preservedNodes.length > 0 ? printSync({
1947
+ const preservedCode = preservedNodes.length > 0 ? printSync2({
1417
1948
  type: "Module",
1418
1949
  span: ast.span,
1419
1950
  body: preservedNodes,
@@ -1446,7 +1977,7 @@ function extractClassStub(classDecl) {
1446
1977
  const constructorParams = [];
1447
1978
  if (classDecl.decorators) {
1448
1979
  for (const dec of classDecl.decorators) {
1449
- const str = stringifyDecorator(dec);
1980
+ const str = stringifyDecorator2(dec);
1450
1981
  if (str) decorators.push(str);
1451
1982
  }
1452
1983
  }
@@ -1469,8 +2000,8 @@ function extractClassStub(classDecl) {
1469
2000
  constructorParams
1470
2001
  };
1471
2002
  }
1472
- function stringifyDecorator(decorator) {
1473
- const exprCode = printSync({
2003
+ function stringifyDecorator2(decorator) {
2004
+ const exprCode = printSync2({
1474
2005
  type: "Module",
1475
2006
  span: { start: 0, end: 0, ctxt: 0 },
1476
2007
  body: [
@@ -1505,7 +2036,7 @@ function stringifyParam(param) {
1505
2036
  let decorators = [];
1506
2037
  if (param.decorators) {
1507
2038
  for (const d of param.decorators) {
1508
- const str = stringifyDecorator(d);
2039
+ const str = stringifyDecorator2(d);
1509
2040
  if (str) decorators.push(str);
1510
2041
  }
1511
2042
  }
@@ -1608,9 +2139,6 @@ var NodeTypeGuards = class {
1608
2139
  isSignalMember(expr) {
1609
2140
  return this.t.isMemberExpression(expr) && this.t.isIdentifier(expr.property, { name: "value" });
1610
2141
  }
1611
- isBehaviorSubjectMember(expr) {
1612
- return this.t.isMemberExpression(expr) && this.t.isIdentifier(expr.property, { name: "$value" });
1613
- }
1614
2142
  };
1615
2143
  var ASTUtilities = class {
1616
2144
  constructor(t, guards) {
@@ -1618,7 +2146,7 @@ var ASTUtilities = class {
1618
2146
  this.guards = guards;
1619
2147
  }
1620
2148
  getObject(expr) {
1621
- if (this.guards.isSignalMember(expr) || this.guards.isBehaviorSubjectMember(expr)) {
2149
+ if (this.guards.isSignalMember(expr)) {
1622
2150
  return expr.object;
1623
2151
  }
1624
2152
  return expr;
@@ -1656,24 +2184,12 @@ var ASTUtilities = class {
1656
2184
  }
1657
2185
  return expr;
1658
2186
  }
1659
- insertBeforeReturn(body, statements) {
1660
- const returnIndex = body.findIndex(
1661
- (stmt) => this.t.isReturnStatement(stmt)
1662
- );
1663
- if (returnIndex !== -1) {
1664
- body.splice(returnIndex, 0, ...statements);
1665
- } else {
1666
- body.push(...statements);
1667
- }
1668
- }
1669
2187
  addEffectCleanup(scope, effectCall) {
1670
2188
  const cleanupId = scope.generateUidIdentifier("cleanup");
1671
2189
  return [
1672
- // const _cleanup1 = $effect(...)
1673
2190
  this.t.variableDeclaration("const", [
1674
2191
  this.t.variableDeclarator(cleanupId, effectCall)
1675
2192
  ]),
1676
- // self.__cleanup = [...(self.__cleanup || []), _cleanup1]
1677
2193
  this.t.expressionStatement(
1678
2194
  this.t.assignmentExpression(
1679
2195
  "=",
@@ -1786,81 +2302,12 @@ var JSXUtilities = class {
1786
2302
  return svgTags.has(tag);
1787
2303
  }
1788
2304
  };
1789
- var ObservableManager = class {
1790
- constructor(t, guards) {
1791
- this.t = t;
1792
- this.guards = guards;
1793
- }
1794
- getObservableKey(expr) {
1795
- return this.stringifyNode(expr);
1796
- }
1797
- stringifyNode(node) {
1798
- if (!node) return "";
1799
- if (this.t.isThisExpression(node)) return "this";
1800
- if (this.t.isIdentifier(node)) return node.name;
1801
- if (this.t.isMemberExpression(node)) {
1802
- const obj = this.stringifyNode(node.object);
1803
- const prop = node.computed ? `[${this.stringifyNode(node.property)}]` : `.${node.property.name}`;
1804
- return obj + prop;
1805
- }
1806
- if (this.t.isCallExpression(node)) {
1807
- const callee = this.stringifyNode(node.callee);
1808
- const args = node.arguments.map((arg) => this.stringifyNode(arg)).join(",");
1809
- return `${callee}(${args})`;
1810
- }
1811
- if (this.t.isStringLiteral(node)) return `"${node.value}"`;
1812
- if (this.t.isNumericLiteral(node)) return String(node.value);
1813
- return node.type + JSON.stringify(node.name || node.value || "");
1814
- }
1815
- collectObservables(node, observables, astUtils) {
1816
- this.walkNode(node, (n) => {
1817
- if (this.guards.isBehaviorSubjectMember(n)) {
1818
- const observable = astUtils.replaceThisWithSelf(
1819
- n.object
1820
- );
1821
- const key = this.getObservableKey(observable);
1822
- if (!observables.has(key)) {
1823
- observables.set(key, observable);
1824
- }
1825
- }
1826
- });
1827
- }
1828
- replaceObservablesWithSignals(node, observableSignals, astUtils) {
1829
- const cloned = this.t.cloneNode(node, true);
1830
- this.walkNode(cloned, (n) => {
1831
- if (this.guards.isBehaviorSubjectMember(n)) {
1832
- const observable = astUtils.replaceThisWithSelf(n.object);
1833
- const key = this.getObservableKey(observable);
1834
- const signalId = observableSignals.get(key);
1835
- if (signalId) {
1836
- n.object = signalId;
1837
- n.property = this.t.identifier("value");
1838
- }
1839
- }
1840
- });
1841
- return cloned;
1842
- }
1843
- walkNode(node, callback) {
1844
- if (!node || typeof node !== "object") return;
1845
- callback(node);
1846
- for (const key in node) {
1847
- if (["loc", "start", "end", "extra"].includes(key)) continue;
1848
- const value = node[key];
1849
- if (Array.isArray(value)) {
1850
- value.forEach((item) => this.walkNode(item, callback));
1851
- } else if (value && typeof value === "object") {
1852
- this.walkNode(value, callback);
1853
- }
1854
- }
1855
- }
1856
- };
1857
2305
  var ElementTransformer = class {
1858
- constructor(t, guards, astUtils, jsxUtils, observableManager) {
2306
+ constructor(t, guards, astUtils, jsxUtils) {
1859
2307
  this.t = t;
1860
2308
  this.guards = guards;
1861
2309
  this.astUtils = astUtils;
1862
2310
  this.jsxUtils = jsxUtils;
1863
- this.observableManager = observableManager;
1864
2311
  }
1865
2312
  transformElement(path17, scope, context2) {
1866
2313
  if (this.t.isJSXFragment(path17.node)) {
@@ -2031,18 +2478,10 @@ var ElementTransformer = class {
2031
2478
  processComponentAttributes(attributes, props, context2) {
2032
2479
  for (const attr of attributes) {
2033
2480
  if (this.t.isJSXSpreadAttribute(attr)) {
2034
- this.observableManager.collectObservables(
2035
- attr.argument,
2036
- context2.observables,
2037
- this.astUtils
2038
- );
2039
- const replaced = this.observableManager.replaceObservablesWithSignals(
2040
- attr.argument,
2041
- context2.observableSignals,
2042
- this.astUtils
2043
- );
2044
2481
  props.push(
2045
- this.t.spreadElement(this.astUtils.replaceThisWithSelf(replaced))
2482
+ this.t.spreadElement(
2483
+ this.astUtils.replaceThisWithSelf(attr.argument)
2484
+ )
2046
2485
  );
2047
2486
  continue;
2048
2487
  }
@@ -2051,39 +2490,22 @@ var ElementTransformer = class {
2051
2490
  props.push(this.t.objectProperty(this.t.identifier(key), attr.value));
2052
2491
  } else if (this.t.isJSXExpressionContainer(attr.value)) {
2053
2492
  const expr = attr.value.expression;
2054
- this.observableManager.collectObservables(
2055
- expr,
2056
- context2.observables,
2057
- this.astUtils
2058
- );
2059
- if (this.guards.isSignalMember(expr) || this.guards.isBehaviorSubjectMember(expr)) {
2060
- const replaced = this.observableManager.replaceObservablesWithSignals(
2061
- expr,
2062
- context2.observableSignals,
2063
- this.astUtils
2064
- );
2493
+ if (this.guards.isSignalMember(expr)) {
2065
2494
  props.push(
2066
2495
  this.t.objectMethod(
2067
2496
  "get",
2068
2497
  this.t.identifier(key),
2069
2498
  [],
2070
2499
  this.t.blockStatement([
2071
- this.t.returnStatement(
2072
- this.astUtils.replaceThisWithSelf(replaced)
2073
- )
2500
+ this.t.returnStatement(this.astUtils.replaceThisWithSelf(expr))
2074
2501
  ])
2075
2502
  )
2076
2503
  );
2077
2504
  } else {
2078
- const replaced = this.observableManager.replaceObservablesWithSignals(
2079
- expr,
2080
- context2.observableSignals,
2081
- this.astUtils
2082
- );
2083
2505
  props.push(
2084
2506
  this.t.objectProperty(
2085
2507
  this.t.identifier(key),
2086
- this.astUtils.replaceThisWithSelf(replaced)
2508
+ this.astUtils.replaceThisWithSelf(expr)
2087
2509
  )
2088
2510
  );
2089
2511
  }
@@ -2106,22 +2528,12 @@ var ElementTransformer = class {
2106
2528
  let hrefValue = null;
2107
2529
  for (const attr of attributes) {
2108
2530
  if (this.t.isJSXSpreadAttribute(attr)) {
2109
- this.observableManager.collectObservables(
2110
- attr.argument,
2111
- context2.observables,
2112
- this.astUtils
2113
- );
2114
- const replaced = this.observableManager.replaceObservablesWithSignals(
2115
- attr.argument,
2116
- context2.observableSignals,
2117
- this.astUtils
2118
- );
2119
2531
  const effectCall = this.t.callExpression(this.t.identifier("$effect"), [
2120
2532
  this.t.arrowFunctionExpression(
2121
2533
  [],
2122
2534
  this.t.callExpression(this.t.identifier("$spread"), [
2123
2535
  elId,
2124
- this.astUtils.replaceThisWithSelf(replaced)
2536
+ this.astUtils.replaceThisWithSelf(attr.argument)
2125
2537
  ])
2126
2538
  )
2127
2539
  ]);
@@ -2136,34 +2548,18 @@ var ElementTransformer = class {
2136
2548
  if (key === "ref") {
2137
2549
  hasRef = true;
2138
2550
  if (this.t.isJSXExpressionContainer(attr.value)) {
2139
- this.observableManager.collectObservables(
2140
- attr.value.expression,
2141
- context2.observables,
2142
- this.astUtils
2143
- );
2144
- const replaced = this.observableManager.replaceObservablesWithSignals(
2145
- attr.value.expression,
2146
- context2.observableSignals,
2147
- this.astUtils
2551
+ refValue = this.astUtils.replaceThisWithSelf(
2552
+ attr.value.expression
2148
2553
  );
2149
- refValue = this.astUtils.replaceThisWithSelf(replaced);
2150
2554
  }
2151
2555
  continue;
2152
2556
  }
2153
2557
  if (key === "dangerouslySetInnerHTML") {
2154
2558
  hasDangerousHTML = true;
2155
2559
  if (this.t.isJSXExpressionContainer(attr.value)) {
2156
- this.observableManager.collectObservables(
2157
- attr.value.expression,
2158
- context2.observables,
2159
- this.astUtils
2560
+ dangerousHTMLValue = this.astUtils.replaceThisWithSelf(
2561
+ attr.value.expression
2160
2562
  );
2161
- const replaced = this.observableManager.replaceObservablesWithSignals(
2162
- attr.value.expression,
2163
- context2.observableSignals,
2164
- this.astUtils
2165
- );
2166
- dangerousHTMLValue = this.astUtils.replaceThisWithSelf(replaced);
2167
2563
  }
2168
2564
  continue;
2169
2565
  }
@@ -2181,9 +2577,9 @@ var ElementTransformer = class {
2181
2577
  this.processStyleAttribute(attr, elId, statements, scope, context2);
2182
2578
  continue;
2183
2579
  }
2184
- this.processRegularAttribute(key, attr, elId, statements, context2);
2580
+ this.processRegularAttribute(key, attr, elId, statements, scope, context2);
2185
2581
  }
2186
- if (tag === "a" && !hasClickHandler && hrefValue && this.isRelativeUrl(hrefValue)) {
2582
+ if (tag === "a" && !hasClickHandler) {
2187
2583
  statements.push(
2188
2584
  this.t.expressionStatement(
2189
2585
  this.t.callExpression(
@@ -2194,13 +2590,25 @@ var ElementTransformer = class {
2194
2590
  [
2195
2591
  this.t.stringLiteral("click"),
2196
2592
  this.t.arrowFunctionExpression(
2197
- [this.t.identifier("event")],
2593
+ [this.t.identifier("e")],
2198
2594
  this.t.callExpression(
2199
2595
  this.t.memberExpression(
2200
2596
  this.t.identifier("Orca"),
2201
2597
  this.t.identifier("navigate")
2202
2598
  ),
2203
- [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
+ ]
2204
2612
  )
2205
2613
  )
2206
2614
  ]
@@ -2225,17 +2633,9 @@ var ElementTransformer = class {
2225
2633
  const eventName = key.slice(2).toLowerCase();
2226
2634
  let handler = this.t.nullLiteral();
2227
2635
  if (this.t.isJSXExpressionContainer(attr.value)) {
2228
- this.observableManager.collectObservables(
2229
- attr.value.expression,
2230
- context2.observables,
2231
- this.astUtils
2232
- );
2233
- const replaced = this.observableManager.replaceObservablesWithSignals(
2234
- attr.value.expression,
2235
- context2.observableSignals,
2236
- this.astUtils
2636
+ handler = this.astUtils.replaceThisWithSelf(
2637
+ attr.value.expression
2237
2638
  );
2238
- handler = this.astUtils.replaceThisWithSelf(replaced);
2239
2639
  }
2240
2640
  statements.push(
2241
2641
  this.t.expressionStatement(
@@ -2248,56 +2648,63 @@ var ElementTransformer = class {
2248
2648
  }
2249
2649
  processStyleAttribute(attr, elId, statements, scope, context2) {
2250
2650
  if (!this.t.isJSXExpressionContainer(attr.value)) return;
2251
- this.observableManager.collectObservables(
2252
- attr.value.expression,
2253
- context2.observables,
2254
- this.astUtils
2255
- );
2256
- const replaced = this.observableManager.replaceObservablesWithSignals(
2257
- attr.value.expression,
2258
- context2.observableSignals,
2259
- this.astUtils
2260
- );
2261
2651
  const effectCall = this.t.callExpression(this.t.identifier("$effect"), [
2262
2652
  this.t.arrowFunctionExpression(
2263
2653
  [],
2264
2654
  this.t.callExpression(this.t.identifier("$style"), [
2265
2655
  elId,
2266
- this.astUtils.replaceThisWithSelf(replaced)
2656
+ this.astUtils.replaceThisWithSelf(
2657
+ attr.value.expression
2658
+ )
2267
2659
  ])
2268
2660
  )
2269
2661
  ]);
2270
2662
  const cleanupStatements = this.astUtils.addEffectCleanup(scope, effectCall);
2271
2663
  statements.push(...cleanupStatements);
2272
2664
  }
2273
- processRegularAttribute(key, attr, elId, statements, context2) {
2665
+ processRegularAttribute(key, attr, elId, statements, scope, context2) {
2274
2666
  const attrName = key === "className" ? "class" : key;
2275
2667
  let value;
2668
+ let isReactive = false;
2276
2669
  if (this.t.isStringLiteral(attr.value)) {
2277
2670
  value = attr.value;
2278
2671
  } else if (this.t.isJSXExpressionContainer(attr.value)) {
2279
- this.observableManager.collectObservables(
2280
- attr.value.expression,
2281
- context2.observables,
2282
- this.astUtils
2672
+ const expr = this.astUtils.replaceThisWithSelf(
2673
+ attr.value.expression
2283
2674
  );
2284
- const replaced = this.observableManager.replaceObservablesWithSignals(
2285
- attr.value.expression,
2286
- context2.observableSignals,
2287
- this.astUtils
2675
+ isReactive = this.guards.isSignalMember(
2676
+ attr.value.expression
2288
2677
  );
2289
- value = this.astUtils.replaceThisWithSelf(replaced);
2678
+ value = expr;
2290
2679
  } else {
2291
2680
  value = this.t.booleanLiteral(true);
2292
2681
  }
2293
- statements.push(
2294
- this.t.expressionStatement(
2295
- this.t.callExpression(
2296
- this.t.memberExpression(elId, this.t.identifier("setAttribute")),
2297
- [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
+ )
2298
2691
  )
2299
- )
2300
- );
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
+ }
2301
2708
  }
2302
2709
  processChildren(children, childExpressions, statements, scope, context2) {
2303
2710
  for (const child of children) {
@@ -2307,17 +2714,9 @@ var ElementTransformer = class {
2307
2714
  } else if (this.t.isJSXExpressionContainer(child)) {
2308
2715
  const expr = child.expression;
2309
2716
  if (!this.t.isJSXEmptyExpression(expr)) {
2310
- this.observableManager.collectObservables(
2311
- expr,
2312
- context2.observables,
2313
- this.astUtils
2717
+ childExpressions.push(
2718
+ this.astUtils.replaceThisWithSelf(expr)
2314
2719
  );
2315
- const replaced = this.observableManager.replaceObservablesWithSignals(
2316
- expr,
2317
- context2.observableSignals,
2318
- this.astUtils
2319
- );
2320
- childExpressions.push(this.astUtils.replaceThisWithSelf(replaced));
2321
2720
  }
2322
2721
  } else if (this.t.isJSXElement(child) || this.t.isJSXFragment(child)) {
2323
2722
  const childEl = this.transformElement({ node: child }, scope, context2);
@@ -2342,42 +2741,43 @@ var ElementTransformer = class {
2342
2741
  } else if (this.t.isJSXExpressionContainer(child)) {
2343
2742
  const expr = child.expression;
2344
2743
  if (this.t.isJSXEmptyExpression(expr)) continue;
2345
- this.observableManager.collectObservables(
2346
- expr,
2347
- context2.observables,
2348
- this.astUtils
2349
- );
2350
- let insertedValue;
2351
- if (this.guards.isSignalMember(expr)) {
2352
- insertedValue = this.astUtils.getObject(
2353
- expr
2354
- );
2355
- } else if (this.guards.isBehaviorSubjectMember(expr)) {
2356
- const replaced = this.observableManager.replaceObservablesWithSignals(
2744
+ if (this.t.isLogicalExpression(expr, { operator: "&&" })) {
2745
+ this.processConditionalAnd(
2357
2746
  expr,
2358
- context2.observableSignals,
2359
- this.astUtils
2747
+ parentId,
2748
+ statements,
2749
+ scope,
2750
+ context2
2360
2751
  );
2361
- insertedValue = this.astUtils.getObject(replaced);
2362
- } else {
2363
- const replaced = this.observableManager.replaceObservablesWithSignals(
2752
+ } else if (this.t.isConditionalExpression(expr)) {
2753
+ this.processConditionalTernary(
2364
2754
  expr,
2365
- context2.observableSignals,
2366
- this.astUtils
2755
+ parentId,
2756
+ statements,
2757
+ scope,
2758
+ context2
2367
2759
  );
2368
- insertedValue = this.t.arrowFunctionExpression(
2369
- [],
2370
- 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
+ )
2371
2779
  );
2372
2780
  }
2373
- statements.push(
2374
- this.t.expressionStatement(
2375
- this.t.callExpression(this.t.identifier("$insert"), [
2376
- parentId,
2377
- insertedValue
2378
- ])
2379
- )
2380
- );
2381
2781
  } else if (this.t.isJSXElement(child) || this.t.isJSXFragment(child)) {
2382
2782
  const childEl = this.transformElement({ node: child }, scope, context2);
2383
2783
  statements.push(...childEl.statements);
@@ -2392,18 +2792,137 @@ var ElementTransformer = class {
2392
2792
  }
2393
2793
  }
2394
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
+ }
2395
2916
  };
2396
2917
  function j2d({ types: t }) {
2397
2918
  const guards = new NodeTypeGuards(t);
2398
2919
  const astUtils = new ASTUtilities(t, guards);
2399
2920
  const jsxUtils = new JSXUtilities(t);
2400
- const observableManager = new ObservableManager(t, guards);
2401
2921
  const elementTransformer = new ElementTransformer(
2402
2922
  t,
2403
2923
  guards,
2404
2924
  astUtils,
2405
- jsxUtils,
2406
- observableManager
2925
+ jsxUtils
2407
2926
  );
2408
2927
  return {
2409
2928
  name: "jsx-to-dom",
@@ -2416,8 +2935,8 @@ function j2d({ types: t }) {
2416
2935
  { local: "$createComponent", imported: "createComponent" },
2417
2936
  { local: "$style", imported: "style" },
2418
2937
  { local: "$spread", imported: "spread" },
2419
- { local: "$toSignal", imported: "toSignal" },
2420
- { local: "$effect", imported: "effect" }
2938
+ { local: "$effect", imported: "effect" },
2939
+ { local: "$computed", imported: "computed" }
2421
2940
  ];
2422
2941
  for (const helper of helpers) {
2423
2942
  path17.unshiftContainer(
@@ -2451,49 +2970,12 @@ function j2d({ types: t }) {
2451
2970
  path17.setData("processed", true);
2452
2971
  const body = path17.node.body;
2453
2972
  if (!t.isBlockStatement(body)) return;
2454
- const observables = /* @__PURE__ */ new Map();
2455
- path17.traverse({
2456
- JSXElement(jsxPath) {
2457
- observableManager.collectObservables(
2458
- jsxPath.node,
2459
- observables,
2460
- astUtils
2461
- );
2462
- },
2463
- JSXFragment(jsxPath) {
2464
- observableManager.collectObservables(
2465
- jsxPath.node,
2466
- observables,
2467
- astUtils
2468
- );
2469
- }
2470
- });
2471
2973
  body.body.unshift(
2472
2974
  t.variableDeclaration("const", [
2473
2975
  t.variableDeclarator(t.identifier("self"), t.thisExpression())
2474
2976
  ])
2475
2977
  );
2476
- const observableSignals = /* @__PURE__ */ new Map();
2477
- const signalDeclarations = [];
2478
- for (const [key, observable] of observables) {
2479
- const signalId = path17.scope.generateUidIdentifier("sig");
2480
- observableSignals.set(key, signalId);
2481
- signalDeclarations.push(
2482
- t.variableDeclaration("const", [
2483
- t.variableDeclarator(
2484
- signalId,
2485
- t.callExpression(t.identifier("$toSignal"), [
2486
- observable,
2487
- t.identifier("self")
2488
- ])
2489
- )
2490
- ])
2491
- );
2492
- }
2493
- if (signalDeclarations.length > 0) {
2494
- astUtils.insertBeforeReturn(body.body, signalDeclarations);
2495
- }
2496
- const context2 = { observables, observableSignals };
2978
+ const context2 = { signals: /* @__PURE__ */ new Map() };
2497
2979
  path17.traverse({
2498
2980
  JSXElement(jsxPath) {
2499
2981
  if (jsxPath.getData("processed")) return;
@@ -2538,7 +3020,7 @@ function j2d({ types: t }) {
2538
3020
  }
2539
3021
 
2540
3022
  // src/plugins/generators/generate_server_component.ts
2541
- import { parseSync as parseSync3, printSync as printSync2 } from "@swc/core";
3023
+ import { parseSync as parseSync3, printSync as printSync3 } from "@swc/core";
2542
3024
  function generateServerComponent(filePath, code) {
2543
3025
  const ast = parseSync3(code, {
2544
3026
  syntax: "typescript",
@@ -2580,7 +3062,7 @@ function generateServerComponent(filePath, code) {
2580
3062
  preservedNodes.push(item);
2581
3063
  }
2582
3064
  }
2583
- const preservedCode = preservedNodes.length > 0 ? printSync2({
3065
+ const preservedCode = preservedNodes.length > 0 ? printSync3({
2584
3066
  type: "Module",
2585
3067
  span: ast.span,
2586
3068
  body: preservedNodes,
@@ -2615,7 +3097,7 @@ function extractClassStub2(classDecl) {
2615
3097
  const constructorParams = [];
2616
3098
  if (classDecl.decorators) {
2617
3099
  for (const dec of classDecl.decorators) {
2618
- const str = stringifyDecorator2(dec);
3100
+ const str = stringifyDecorator3(dec);
2619
3101
  if (str) decorators.push(str);
2620
3102
  }
2621
3103
  }
@@ -2640,8 +3122,8 @@ function extractClassStub2(classDecl) {
2640
3122
  methods
2641
3123
  };
2642
3124
  }
2643
- function stringifyDecorator2(decorator) {
2644
- const exprCode = printSync2({
3125
+ function stringifyDecorator3(decorator) {
3126
+ const exprCode = printSync3({
2645
3127
  type: "Module",
2646
3128
  span: { start: 0, end: 0, ctxt: 0 },
2647
3129
  body: [
@@ -2676,7 +3158,7 @@ function stringifyParam2(param) {
2676
3158
  let decorators = [];
2677
3159
  if (param.decorators) {
2678
3160
  for (const d of param.decorators) {
2679
- const str = stringifyDecorator2(d);
3161
+ const str = stringifyDecorator3(d);
2680
3162
  if (str) decorators.push(str);
2681
3163
  }
2682
3164
  }
@@ -2871,10 +3353,11 @@ ${decoratorsStr}export class ${className} extends _OrcaComponent {
2871
3353
  }
2872
3354
 
2873
3355
  // src/plugins/generators/generate_rpc.ts
3356
+ import { printSync as printSync4 } from "@swc/core";
2874
3357
  function generateRpcStub(filePath, code) {
2875
3358
  try {
2876
3359
  const ast = parseTypeScript(filePath, code);
2877
- const serviceInfo = extractServiceInfo(ast, filePath, false);
3360
+ const serviceInfo = extractServiceInfo(ast, filePath, false, true, true);
2878
3361
  if (!serviceInfo) {
2879
3362
  throw {
2880
3363
  type: "validation",
@@ -2882,7 +3365,6 @@ function generateRpcStub(filePath, code) {
2882
3365
  filePath
2883
3366
  };
2884
3367
  }
2885
- validateServiceInfo(serviceInfo, filePath);
2886
3368
  return generateStubCode2(serviceInfo);
2887
3369
  } catch (error) {
2888
3370
  if (error.type) {
@@ -2896,32 +3378,143 @@ function generateRpcStub(filePath, code) {
2896
3378
  };
2897
3379
  }
2898
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
+ }
2899
3434
  function generateStubCode2(serviceInfo) {
2900
3435
  const className = serviceInfo.className;
2901
3436
  const basePath = serviceNameToPath(className);
2902
3437
  const methods = serviceInfo.methods.map((method) => generateMethod2(method, basePath, className)).join("\n\n");
2903
3438
  const hasStreamable = serviceInfo.methods.some((m) => m.isStreamable);
2904
- 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");
2905
3446
  return `${imports}
2906
3447
 
2907
3448
  @Injectable()
2908
3449
  export class ${className} {
3450
+ constructor(
3451
+ ${clientConstructorFields}
3452
+ ) {}
3453
+
2909
3454
  ${methods}
2910
3455
  }`;
2911
3456
  }
2912
- function generateImports2(hasStreamable) {
3457
+ function generateImports2(hasStreamable, sharedImports) {
2913
3458
  let imports = `import { Injectable } from "@kithinji/orca";
2914
3459
  `;
2915
3460
  if (hasStreamable) {
2916
- imports += `import { Observable } from "@kithinji/orca";
3461
+ imports += `import { Observable } from "rxjs";
2917
3462
  `;
2918
3463
  }
3464
+ if (sharedImports.length > 0) {
3465
+ const sharedImportStatements = generateImportStatements(sharedImports);
3466
+ if (sharedImportStatements) {
3467
+ imports += sharedImportStatements + "\n";
3468
+ }
3469
+ }
2919
3470
  return imports;
2920
3471
  }
2921
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
+ }
2922
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
+ }
2923
3511
  return generateSseMethod(method, basePath);
2924
3512
  }
3513
+ const fileParams = getFileParams2(method);
3514
+ const hasFileParams = fileParams.length > 0;
3515
+ if (hasFileParams) {
3516
+ return generateFileUploadMethod2(method, basePath, fileParams);
3517
+ }
2925
3518
  const params = method.params.map((p) => `${p.name}: ${p.type}`).join(", ");
2926
3519
  const hasParams = method.params.length > 0;
2927
3520
  if (!hasParams) {
@@ -2929,36 +3522,130 @@ function generateMethod2(method, basePath, serviceName) {
2929
3522
  }
2930
3523
  return generatePostMethod(method, basePath, params);
2931
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
+ }
2932
3583
  function generateSseMethod(method, basePath) {
2933
3584
  const params = method.params.map((p) => `${p.name}: ${p.type}`).join(", ");
2934
3585
  const hasParams = method.params.length > 0;
2935
3586
  let urlBuilder;
3587
+ const headerCheck = `this.${method.name}Headers`;
2936
3588
  if (hasParams) {
2937
3589
  const queryParams = method.params.map((p) => `${p.name}=\${encodeURIComponent(${p.name})}`).join("&");
2938
- urlBuilder = `\`/${basePath}/${method.name}?${queryParams}\``;
3590
+ urlBuilder = `\`/api/${basePath}/${method.name}?${queryParams}\``;
2939
3591
  } else {
2940
- urlBuilder = `\`/${basePath}/${method.name}\``;
3592
+ urlBuilder = `\`/api/${basePath}/${method.name}\``;
2941
3593
  }
2942
3594
  return ` ${method.name}(${params}): Observable<${method.returnType}> {
2943
3595
  return new Observable((observer) => {
2944
- const eventSource = new EventSource(${urlBuilder});
3596
+ let url = ${urlBuilder};
2945
3597
 
2946
- eventSource.onmessage = (event) => {
2947
- try {
2948
- const data = JSON.parse(event.data);
2949
- observer.next(data);
2950
- } catch (error) {
2951
- 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));
2952
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
+ };
2953
3636
  };
2954
3637
 
2955
- eventSource.onerror = (error) => {
3638
+ getHeaders().then((cleanup) => {
3639
+ // Store cleanup function for unsubscribe
3640
+ if (cleanup) {
3641
+ observer.add(cleanup);
3642
+ }
3643
+ }).catch((error) => {
2956
3644
  observer.error?.(error);
2957
- eventSource.close();
2958
- };
3645
+ });
2959
3646
 
2960
3647
  return () => {
2961
- eventSource.close();
3648
+ // Cleanup handled in getHeaders
2962
3649
  };
2963
3650
  });
2964
3651
  }`;
@@ -2967,10 +3654,17 @@ function generateGetMethod(method, basePath) {
2967
3654
  const params = method.params.map((p) => `${p.name}: ${p.type}`).join(", ");
2968
3655
  const returnType = `Promise<${method.returnType}>`;
2969
3656
  return ` async ${method.name}(${params}): ${returnType} {
2970
- 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}\`, {
2971
3664
  method: 'GET',
2972
3665
  headers: {
2973
3666
  'Content-Type': 'application/json',
3667
+ ...headers
2974
3668
  }
2975
3669
  });
2976
3670
 
@@ -2985,10 +3679,17 @@ function generatePostMethod(method, basePath, params) {
2985
3679
  const paramNames = method.params.map((p) => p.name).join(", ");
2986
3680
  const returnType = `Promise<${method.returnType}>`;
2987
3681
  return ` async ${method.name}(${params}): ${returnType} {
2988
- 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}\`, {
2989
3689
  method: 'POST',
2990
3690
  headers: {
2991
3691
  'Content-Type': 'application/json',
3692
+ ...headers
2992
3693
  },
2993
3694
  body: JSON.stringify({ ${paramNames} }),
2994
3695
  });
@@ -3045,11 +3746,11 @@ function parseFileMetadata(source, path17) {
3045
3746
  }
3046
3747
  var ServerBuildTransformer = class {
3047
3748
  async transformPublicFile(source, path17) {
3048
- const controllerCode = generateController(path17, source);
3049
- if (controllerCode) {
3050
- source = `${source}
3749
+ const result = generateController(path17, source);
3750
+ if (result) {
3751
+ source = `${result.transformedServiceCode}
3051
3752
 
3052
- ${controllerCode}
3753
+ ${result.controllerCode}
3053
3754
  `;
3054
3755
  }
3055
3756
  return swcTransform(source, path17);
@@ -3184,7 +3885,48 @@ function useMyPlugin(options) {
3184
3885
  import { parseSync as parseSync4 } from "@swc/core";
3185
3886
  import * as fs3 from "fs";
3186
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
+ }
3187
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
+ }
3188
3930
  if (!importPath.startsWith(".")) return null;
3189
3931
  const dir = path7.dirname(fromFile);
3190
3932
  const basePath = path7.resolve(dir, importPath);
@@ -3607,7 +4349,7 @@ export async function navigate(event, url) {
3607
4349
  const injector = getCurrentInjector();
3608
4350
 
3609
4351
  if (injector) {
3610
- const navigate = injector.resolve(Navigate);
4352
+ const navigate = await injector.resolve(Navigate);
3611
4353
  navigate.go(url);
3612
4354
  } else {
3613
4355
  window.location.href = url;
@@ -3670,7 +4412,7 @@ var HotReloadManager = class {
3670
4412
  });
3671
4413
  });
3672
4414
  this.logger.info(
3673
- `Hot reload server listening on ws://localhost:${this.port}`
4415
+ `Hot reloads servers listening on ws://localhost:${this.port}`
3674
4416
  );
3675
4417
  }
3676
4418
  reload() {
@@ -4892,7 +5634,7 @@ function createPage(name) {
4892
5634
  import { ${listComponent} } from "./components/${name}-list.component";
4893
5635
 
4894
5636
  @Component({
4895
- deps: [${listComponent}]
5637
+ inject: [${listComponent}]
4896
5638
  })
4897
5639
  export class ${pageName} {
4898
5640
  build() {
@@ -4988,7 +5730,8 @@ function genPackageJson(name) {
4988
5730
  "reflect-metadata": "latest",
4989
5731
  zod: "^4.2.1",
4990
5732
  "@kithinji/orca": "latest",
4991
- "@kithinji/arcane": "latest"
5733
+ "@kithinji/arcane": "latest",
5734
+ rxjs: "latest"
4992
5735
  },
4993
5736
  devDependencies: {
4994
5737
  "@types/node": "^20.19.27",
@@ -5011,6 +5754,7 @@ function gentsconfig() {
5011
5754
  jsxImportSource: "@kithinji/orca",
5012
5755
  experimentalDecorators: true,
5013
5756
  emitDecoratorMetadata: true,
5757
+ strictPropertyInitialization: false,
5014
5758
  baseUrl: ".",
5015
5759
  paths: {
5016
5760
  "@/*": ["src/*"]
@@ -5057,7 +5801,7 @@ function genIndexHtml(name) {
5057
5801
  <meta charset="UTF-8" />
5058
5802
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
5059
5803
  <title>${name} app</title>
5060
- <link rel="stylesheet" href="index.css" />
5804
+ <link rel="stylesheet" href="/index.css" />
5061
5805
  </head>
5062
5806
  <body>
5063
5807
  <div id="root"></div>
@@ -5079,7 +5823,7 @@ import {
5079
5823
  } from "@kithinji/orca";
5080
5824
 
5081
5825
  @Component({
5082
- deps: [RouterOutlet],
5826
+ inject: [RouterOutlet],
5083
5827
  })
5084
5828
  class AppComponent {
5085
5829
  build() {
@@ -5094,8 +5838,8 @@ class AppComponent {
5094
5838
  })
5095
5839
  class AppModule {}
5096
5840
 
5097
- export function bootstrap() {
5098
- BrowserFactory.create(AppModule, document.getElementById("root")!);
5841
+ export async function bootstrap() {
5842
+ await BrowserFactory.create(AppModule, document.getElementById("root")!);
5099
5843
  }
5100
5844
 
5101
5845
  bootstrap();
@@ -6770,7 +7514,7 @@ async function compileFiles(entryPoints) {
6770
7514
 
6771
7515
  // src/index.ts
6772
7516
  var program = new Command();
6773
- program.name("pod").description("Pod cli tool").version("1.0.42");
7517
+ program.name("pod").description("Pod cli tool").version("1.0.46");
6774
7518
  program.command("new <name> [type]").description("Start a new Orca Project").action(async (name, type) => {
6775
7519
  const orca = async () => {
6776
7520
  await addNew(name);