@kithinji/pod 1.0.24 → 1.0.26

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/main.js CHANGED
@@ -11,7 +11,8 @@ import { Command } from "commander";
11
11
  // src/dev/server.ts
12
12
  import * as esbuild2 from "esbuild";
13
13
  import { spawn } from "child_process";
14
- import * as fs5 from "fs/promises";
14
+ import * as fs6 from "fs/promises";
15
+ import { WebSocketServer, WebSocket } from "ws";
15
16
 
16
17
  // src/config/config.ts
17
18
  import * as path from "path";
@@ -926,72 +927,32 @@ async function expandMacros(source, filePath, projectRoot = process.cwd()) {
926
927
 
927
928
  // src/plugins/generators/generate_controller.ts
928
929
  import * as path4 from "path";
929
- import { parseSync } from "@swc/core";
930
- function generateController(filePath, code) {
931
- try {
932
- const ast = parseSync(code, {
933
- syntax: "typescript",
934
- tsx: filePath.endsWith("x") || filePath.endsWith(".tsx"),
935
- decorators: true
936
- });
937
- const serviceInfo = extractServiceInfo(ast, filePath);
938
- if (!serviceInfo || !serviceInfo.hasInjectable) {
939
- return null;
940
- }
941
- validateServiceInfo(serviceInfo, filePath);
942
- return generateControllerCode(serviceInfo, filePath);
943
- } catch (error) {
944
- if (error.type) {
945
- throw error;
946
- }
947
- throw {
948
- type: "parse",
949
- message: `Failed to parse TypeScript file: ${error.message}`,
950
- filePath,
951
- details: error
952
- };
953
- }
954
- }
955
- function extractServiceInfo(ast, filePath) {
956
- let serviceClass = null;
957
- let hasInjectable = false;
958
- const importMap = {};
959
- try {
960
- for (const item of ast.body) {
961
- if (item.type === "ImportDeclaration") {
962
- const decl = item;
963
- const source = decl.source.value;
964
- decl.specifiers.forEach((spec) => {
965
- if (spec.type === "ImportSpecifier" || spec.type === "ImportDefaultSpecifier" || spec.type === "ImportNamespaceSpecifier") {
966
- importMap[spec.local.value] = source;
967
- }
968
- });
969
- }
970
- if (item.type === "ExportDeclaration" && item.declaration?.type === "ClassDeclaration") {
971
- const classDecl = item.declaration;
972
- if (hasInjectableDecorator(classDecl.decorators)) {
973
- serviceClass = classDecl;
974
- hasInjectable = true;
975
- }
976
- }
977
- }
978
- if (!serviceClass?.identifier) {
979
- return null;
980
- }
981
- return {
982
- className: serviceClass.identifier.value,
983
- methods: extractMethods(serviceClass, filePath),
984
- hasInjectable,
985
- importMap
986
- };
987
- } catch (error) {
988
- throw {
989
- type: "parse",
990
- message: `Failed to extract service info: ${error.message}`,
991
- filePath,
992
- details: error
993
- };
930
+
931
+ // src/plugins/generators/utils.ts
932
+ import {
933
+ parseSync
934
+ } from "@swc/core";
935
+ var RETURN_TYPE_CONFIGS = [
936
+ {
937
+ typeName: "Observable",
938
+ isStreamable: true,
939
+ streamType: "Observable" /* Observable */,
940
+ decoratorName: "Sse",
941
+ isSubjectLike: false
942
+ },
943
+ {
944
+ typeName: "Promise",
945
+ isStreamable: false,
946
+ decoratorName: "Post",
947
+ isSubjectLike: false
994
948
  }
949
+ ];
950
+ function parseTypeScript(filePath, code) {
951
+ return parseSync(code, {
952
+ syntax: "typescript",
953
+ tsx: filePath.endsWith("x") || filePath.endsWith(".tsx"),
954
+ decorators: true
955
+ });
995
956
  }
996
957
  function hasInjectableDecorator(decorators) {
997
958
  if (!decorators) return false;
@@ -1000,39 +961,6 @@ function hasInjectableDecorator(decorators) {
1000
961
  return expr.type === "Identifier" && expr.value === "Injectable" || expr.type === "CallExpression" && expr.callee.type === "Identifier" && expr.callee.value === "Injectable";
1001
962
  });
1002
963
  }
1003
- function extractMethods(classDecl, filePath) {
1004
- const methods = [];
1005
- const className = classDecl.identifier?.value || "UnknownClass";
1006
- for (const member of classDecl.body) {
1007
- if (!isPublicMethod(member)) continue;
1008
- const method = member;
1009
- const methodName = getMethodName(method);
1010
- if (!methodName) continue;
1011
- const returnTypeInfo = analyzeReturnType(method);
1012
- if (!returnTypeInfo.isObservable && !method.function.async) {
1013
- throw {
1014
- type: "validation",
1015
- message: `Method ${className}.${methodName} must be async or return an Observable`,
1016
- filePath,
1017
- details: { className, methodName }
1018
- };
1019
- }
1020
- const { paramSchemas, returnSchema } = extractSignature(
1021
- method.function.decorators,
1022
- method.function.params.length
1023
- );
1024
- methods.push({
1025
- name: methodName,
1026
- params: extractMethodParams(method.function.params),
1027
- returnType: returnTypeInfo.type,
1028
- isAsync: method.function.async,
1029
- isObservable: returnTypeInfo.isObservable,
1030
- paramSchemas,
1031
- returnSchema
1032
- });
1033
- }
1034
- return methods;
1035
- }
1036
964
  function isPublicMethod(member) {
1037
965
  return member.type === "ClassMethod" && (member.accessibility === "public" || !member.accessibility);
1038
966
  }
@@ -1042,30 +970,107 @@ function getMethodName(method) {
1042
970
  }
1043
971
  return null;
1044
972
  }
973
+ function extractImportMap(ast) {
974
+ const importMap = {};
975
+ for (const item of ast.body) {
976
+ if (item.type === "ImportDeclaration") {
977
+ const decl = item;
978
+ const source = decl.source.value;
979
+ decl.specifiers.forEach((spec) => {
980
+ if (spec.type === "ImportSpecifier" || spec.type === "ImportDefaultSpecifier" || spec.type === "ImportNamespaceSpecifier") {
981
+ importMap[spec.local.value] = source;
982
+ }
983
+ });
984
+ }
985
+ }
986
+ return importMap;
987
+ }
988
+ function findInjectableClass(ast) {
989
+ for (const item of ast.body) {
990
+ if (item.type === "ExportDeclaration" && item.declaration?.type === "ClassDeclaration") {
991
+ const classDecl = item.declaration;
992
+ if (hasInjectableDecorator(classDecl.decorators)) {
993
+ return classDecl;
994
+ }
995
+ }
996
+ }
997
+ return null;
998
+ }
1045
999
  function analyzeReturnType(method) {
1046
1000
  const returnType = method.function.returnType?.typeAnnotation;
1047
1001
  if (!returnType) {
1048
- return { type: "any", isObservable: false };
1049
- }
1050
- if (returnType.type === "TsTypeReference" && returnType.typeName.type === "Identifier" && returnType.typeName.value === "Observable") {
1051
- const innerType = returnType.typeParams?.params[0];
1052
1002
  return {
1053
- type: innerType ? stringifyType(innerType) : "any",
1054
- isObservable: true
1003
+ type: "any",
1004
+ isStreamable: false,
1005
+ isSubjectLike: false
1055
1006
  };
1056
1007
  }
1057
- if (returnType.type === "TsTypeReference" && returnType.typeName.type === "Identifier" && returnType.typeName.value === "Promise") {
1058
- const innerType = returnType.typeParams?.params[0];
1059
- return {
1060
- type: innerType ? stringifyType(innerType) : "any",
1061
- isObservable: false
1062
- };
1008
+ if (returnType.type === "TsTypeReference" && returnType.typeName.type === "Identifier") {
1009
+ const typeName = returnType.typeName.value;
1010
+ const config = RETURN_TYPE_CONFIGS.find((c) => c.typeName === typeName);
1011
+ if (config) {
1012
+ const innerType = returnType.typeParams?.params[0];
1013
+ return {
1014
+ type: innerType ? stringifyType(innerType) : "any",
1015
+ isStreamable: config.isStreamable,
1016
+ streamType: config.streamType,
1017
+ isSubjectLike: config.isSubjectLike
1018
+ };
1019
+ }
1063
1020
  }
1064
1021
  return {
1065
1022
  type: stringifyType(returnType),
1066
- isObservable: false
1023
+ isStreamable: false,
1024
+ isSubjectLike: false
1067
1025
  };
1068
1026
  }
1027
+ function stringifyType(node) {
1028
+ if (!node) return "any";
1029
+ switch (node.type) {
1030
+ case "TsKeywordType":
1031
+ return node.kind;
1032
+ case "TsTypeReference":
1033
+ if (node.typeName.type !== "Identifier") return "any";
1034
+ const base = node.typeName.value;
1035
+ const args = node.typeParams?.params ? `<${node.typeParams.params.map(stringifyType).join(", ")}>` : "";
1036
+ return base + args;
1037
+ case "TsArrayType":
1038
+ return `${stringifyType(node.elemType)}[]`;
1039
+ case "TsUnionType":
1040
+ return node.types.map(stringifyType).join(" | ");
1041
+ case "TsIntersectionType":
1042
+ return node.types.map(stringifyType).join(" & ");
1043
+ case "TsTypeLiteral":
1044
+ const props = node.members.map((member) => {
1045
+ if (member.type === "TsPropertySignature") {
1046
+ const key = member.key.type === "Identifier" ? member.key.value : "";
1047
+ const type = member.typeAnnotation ? stringifyType(member.typeAnnotation.typeAnnotation) : "any";
1048
+ return `${key}: ${type}`;
1049
+ }
1050
+ return "";
1051
+ }).filter(Boolean);
1052
+ return `{ ${props.join("; ")} }`;
1053
+ default:
1054
+ return "any";
1055
+ }
1056
+ }
1057
+ function extractMethodParams(params) {
1058
+ return params.map((p) => {
1059
+ const pat = p.pat;
1060
+ if (pat.type !== "Identifier") {
1061
+ return {
1062
+ name: "param",
1063
+ type: "any",
1064
+ decorators: []
1065
+ };
1066
+ }
1067
+ return {
1068
+ name: pat.value,
1069
+ type: pat.typeAnnotation ? stringifyType(pat.typeAnnotation.typeAnnotation) : "any",
1070
+ decorators: []
1071
+ };
1072
+ });
1073
+ }
1069
1074
  function extractSignature(decorators, paramCount) {
1070
1075
  if (!decorators) return { paramSchemas: [] };
1071
1076
  for (const decorator of decorators) {
@@ -1103,42 +1108,50 @@ function stringifyExpression(expr) {
1103
1108
  }
1104
1109
  return "any";
1105
1110
  }
1106
- function extractMethodParams(params) {
1107
- return params.map((p) => {
1108
- const pat = p.pat;
1109
- if (pat.type !== "Identifier") {
1110
- return {
1111
- name: "param",
1112
- type: "any",
1113
- decorators: []
1111
+ function extractMethods(classDecl, filePath, includeSignatures = true) {
1112
+ const methods = [];
1113
+ const className = classDecl.identifier?.value || "UnknownClass";
1114
+ for (const member of classDecl.body) {
1115
+ if (!isPublicMethod(member)) continue;
1116
+ const method = member;
1117
+ const methodName = getMethodName(method);
1118
+ if (!methodName) continue;
1119
+ const returnTypeInfo = analyzeReturnType(method);
1120
+ if (!returnTypeInfo.isStreamable && !method.function.async) {
1121
+ throw {
1122
+ type: "validation",
1123
+ message: `Method ${className}.${methodName} must be async or return a streamable type (${RETURN_TYPE_CONFIGS.filter(
1124
+ (c) => c.isStreamable
1125
+ ).map((c) => c.typeName).join(", ")})`,
1126
+ filePath,
1127
+ details: { className, methodName }
1114
1128
  };
1115
1129
  }
1116
- return {
1117
- name: pat.value,
1118
- type: pat.typeAnnotation ? stringifyType(pat.typeAnnotation.typeAnnotation) : "any",
1119
- decorators: []
1120
- };
1121
- });
1122
- }
1123
- function stringifyType(node) {
1124
- if (!node) return "any";
1125
- switch (node.type) {
1126
- case "TsKeywordType":
1127
- return node.kind;
1128
- case "TsTypeReference":
1129
- if (node.typeName.type !== "Identifier") return "any";
1130
- const base = node.typeName.value;
1131
- const args = node.typeParams?.params ? `<${node.typeParams.params.map(stringifyType).join(", ")}>` : "";
1132
- return base + args;
1133
- case "TsArrayType":
1134
- return `${stringifyType(node.elemType)}[]`;
1135
- case "TsUnionType":
1136
- return node.types.map(stringifyType).join(" | ");
1137
- case "TsIntersectionType":
1138
- return node.types.map(stringifyType).join(" & ");
1139
- default:
1140
- return "any";
1130
+ const signatures = includeSignatures ? extractSignature(
1131
+ method.function.decorators,
1132
+ method.function.params.length
1133
+ ) : { paramSchemas: [] };
1134
+ methods.push({
1135
+ name: methodName,
1136
+ params: extractMethodParams(method.function.params),
1137
+ returnType: returnTypeInfo.type,
1138
+ isAsync: method.function.async,
1139
+ isStreamable: returnTypeInfo.isStreamable,
1140
+ streamType: returnTypeInfo.streamType,
1141
+ paramSchemas: signatures.paramSchemas,
1142
+ returnSchema: signatures.returnSchema
1143
+ });
1141
1144
  }
1145
+ return methods;
1146
+ }
1147
+ function serviceNameToPath(serviceName) {
1148
+ return serviceName.replace(/Service$/, "").replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
1149
+ }
1150
+ function toInstanceName(className) {
1151
+ return className.charAt(0).toLowerCase() + className.slice(1);
1152
+ }
1153
+ function capitalize(str) {
1154
+ return str.charAt(0).toUpperCase() + str.slice(1);
1142
1155
  }
1143
1156
  function validateServiceInfo(serviceInfo, filePath) {
1144
1157
  if (!serviceInfo.className) {
@@ -1154,13 +1167,58 @@ function validateServiceInfo(serviceInfo, filePath) {
1154
1167
  );
1155
1168
  }
1156
1169
  serviceInfo.methods.forEach((method) => {
1157
- if (method.params.length > 0 && method.paramSchemas.length === 0) {
1170
+ if (method.params.length > 0 && method.paramSchemas?.length === 0) {
1158
1171
  console.warn(
1159
1172
  `Warning: Method ${serviceInfo.className}.${method.name} has parameters but no @Signature validation`
1160
1173
  );
1161
1174
  }
1162
1175
  });
1163
1176
  }
1177
+ function extractServiceInfo(ast, filePath, includeSignatures = true) {
1178
+ try {
1179
+ const serviceClass = findInjectableClass(ast);
1180
+ const importMap = extractImportMap(ast);
1181
+ if (!serviceClass?.identifier) {
1182
+ return null;
1183
+ }
1184
+ return {
1185
+ className: serviceClass.identifier.value,
1186
+ methods: extractMethods(serviceClass, filePath, includeSignatures),
1187
+ hasInjectable: true,
1188
+ importMap
1189
+ };
1190
+ } catch (error) {
1191
+ throw {
1192
+ type: "parse",
1193
+ message: `Failed to extract service info: ${error.message}`,
1194
+ filePath,
1195
+ details: error
1196
+ };
1197
+ }
1198
+ }
1199
+
1200
+ // src/plugins/generators/generate_controller.ts
1201
+ function generateController(filePath, code) {
1202
+ try {
1203
+ const ast = parseTypeScript(filePath, code);
1204
+ const serviceInfo = extractServiceInfo(ast, filePath, true);
1205
+ if (!serviceInfo || !serviceInfo.hasInjectable) {
1206
+ return null;
1207
+ }
1208
+ validateServiceInfo(serviceInfo, filePath);
1209
+ return generateControllerCode(serviceInfo, filePath);
1210
+ } catch (error) {
1211
+ if (error.type) {
1212
+ throw error;
1213
+ }
1214
+ throw {
1215
+ type: "parse",
1216
+ message: `Failed to parse TypeScript file: ${error.message}`,
1217
+ filePath,
1218
+ details: error
1219
+ };
1220
+ }
1221
+ }
1164
1222
  function generateControllerCode(serviceInfo, filePath) {
1165
1223
  const serviceName = serviceInfo.className;
1166
1224
  const controllerName = serviceName.replace(/Service$/, "GenController");
@@ -1175,7 +1233,9 @@ function generateControllerCode(serviceInfo, filePath) {
1175
1233
  providedIn: "root",
1176
1234
  })
1177
1235
  export class ${controllerName} {
1178
- constructor(private readonly ${serviceInstance}: ${serviceName}) {}
1236
+ constructor(
1237
+ private readonly ${serviceInstance}: ${serviceName}
1238
+ ) {}
1179
1239
 
1180
1240
  ${methods}
1181
1241
  }`;
@@ -1184,12 +1244,6 @@ function getImportPath(filePath) {
1184
1244
  const basename3 = path4.basename(filePath);
1185
1245
  return `./${basename3.replace(/\.tsx?$/, "")}`;
1186
1246
  }
1187
- function serviceNameToPath(serviceName) {
1188
- return serviceName.replace(/Service$/, "").replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
1189
- }
1190
- function toInstanceName(className) {
1191
- return className.charAt(0).toLowerCase() + className.slice(1);
1192
- }
1193
1247
  function generateImports(serviceInfo, serviceName, serviceImportPath) {
1194
1248
  const importGroups = /* @__PURE__ */ new Map();
1195
1249
  const registerIdentifier = (id) => {
@@ -1209,21 +1263,23 @@ function generateImports(serviceInfo, serviceName, serviceImportPath) {
1209
1263
  });
1210
1264
  });
1211
1265
  const hasPost = serviceInfo.methods.some(
1212
- (m) => !m.isObservable && m.params.length > 0
1266
+ (m) => !m.isStreamable && m.params.length > 0
1213
1267
  );
1214
1268
  const hasGet = serviceInfo.methods.some(
1215
- (m) => !m.isObservable && m.params.length === 0
1269
+ (m) => !m.isStreamable && m.params.length === 0
1270
+ );
1271
+ const hasSse = serviceInfo.methods.some(
1272
+ (m) => m.isStreamable && m.streamType == "Observable" /* Observable */
1216
1273
  );
1217
- const hasSse = serviceInfo.methods.some((m) => m.isObservable);
1218
- const hasSseWithParams = serviceInfo.methods.some(
1219
- (m) => m.isObservable && m.params.length > 0
1274
+ const hasStreamableWithParams = serviceInfo.methods.some(
1275
+ (m) => m.isStreamable && m.params.length > 0
1220
1276
  );
1221
1277
  const decorators = ["Controller"];
1222
1278
  if (hasPost) decorators.push("Post");
1223
1279
  if (hasGet) decorators.push("Get");
1224
1280
  if (hasPost) decorators.push("Body");
1225
1281
  if (hasSse) decorators.push("Sse");
1226
- if (hasSseWithParams) decorators.push("Query");
1282
+ if (hasStreamableWithParams) decorators.push("Query");
1227
1283
  let importStrings = `import { ${decorators.join(
1228
1284
  ", "
1229
1285
  )} } from "@kithinji/orca";
@@ -1245,11 +1301,12 @@ function generateMethods(serviceInfo) {
1245
1301
  function generateMethod(method, serviceName) {
1246
1302
  const hasParams = method.params.length > 0;
1247
1303
  const serviceInstance = toInstanceName(serviceName);
1248
- if (method.isObservable) {
1304
+ if (method.isStreamable) {
1249
1305
  const queryParams = hasParams ? method.params.map((p) => `@Query('${p.name}') ${p.name}: ${p.type}`).join(", ") : "";
1250
1306
  const body2 = generateMethodBody(method, serviceInstance, false);
1307
+ const returnTypeName = method.streamType || "Observable";
1251
1308
  return ` @Sse("${method.name}")
1252
- ${method.name}(${queryParams}): Observable<${method.returnType}> {
1309
+ ${method.name}(${queryParams}): ${returnTypeName}<${method.returnType}> {
1253
1310
  ${body2}
1254
1311
  }`;
1255
1312
  }
@@ -1264,14 +1321,14 @@ ${body}
1264
1321
  function generateMethodBody(method, serviceInstance, isAsync) {
1265
1322
  const lines = [];
1266
1323
  const hasParams = method.params.length > 0;
1267
- if (hasParams && method.isObservable && method.paramSchemas.length > 0) {
1324
+ if (hasParams && method.isStreamable && method.paramSchemas.length > 0) {
1268
1325
  method.params.forEach((p, i) => {
1269
1326
  lines.push(
1270
1327
  ` const validated${capitalize(p.name)} = ${method.paramSchemas[i]}.parse(${p.name});`
1271
1328
  );
1272
1329
  });
1273
1330
  }
1274
- if (hasParams && !method.isObservable) {
1331
+ if (hasParams && !method.isStreamable) {
1275
1332
  if (method.paramSchemas.length > 0) {
1276
1333
  lines.push(
1277
1334
  ` const b = typeof body === 'object' && body !== null ? body : {};`
@@ -1287,7 +1344,7 @@ function generateMethodBody(method, serviceInstance, isAsync) {
1287
1344
  }
1288
1345
  }
1289
1346
  let callArgs;
1290
- if (hasParams && method.isObservable && method.paramSchemas.length > 0) {
1347
+ if (hasParams && method.isStreamable && method.paramSchemas.length > 0) {
1291
1348
  callArgs = method.params.map((p) => `validated${capitalize(p.name)}`).join(", ");
1292
1349
  } else {
1293
1350
  callArgs = method.params.map((p) => p.name).join(", ");
@@ -1303,9 +1360,6 @@ function generateMethodBody(method, serviceInstance, isAsync) {
1303
1360
  }
1304
1361
  return lines.join("\n");
1305
1362
  }
1306
- function capitalize(str) {
1307
- return str.charAt(0).toUpperCase() + str.slice(1);
1308
- }
1309
1363
 
1310
1364
  // src/plugins/generators/tsx_server_stub.ts
1311
1365
  import * as path5 from "path";
@@ -1551,7 +1605,7 @@ var NodeTypeGuards = class {
1551
1605
  isSignalMember(expr) {
1552
1606
  return this.t.isMemberExpression(expr) && this.t.isIdentifier(expr.property, { name: "value" });
1553
1607
  }
1554
- isObservableMember(expr) {
1608
+ isBehaviorSubjectMember(expr) {
1555
1609
  return this.t.isMemberExpression(expr) && this.t.isIdentifier(expr.property, { name: "$value" });
1556
1610
  }
1557
1611
  };
@@ -1561,7 +1615,7 @@ var ASTUtilities = class {
1561
1615
  this.guards = guards;
1562
1616
  }
1563
1617
  getObject(expr) {
1564
- if (this.guards.isSignalMember(expr) || this.guards.isObservableMember(expr)) {
1618
+ if (this.guards.isSignalMember(expr) || this.guards.isBehaviorSubjectMember(expr)) {
1565
1619
  return expr.object;
1566
1620
  }
1567
1621
  return expr;
@@ -1696,7 +1750,7 @@ var ObservableManager = class {
1696
1750
  }
1697
1751
  collectObservables(node, observables, astUtils) {
1698
1752
  this.walkNode(node, (n) => {
1699
- if (this.guards.isObservableMember(n)) {
1753
+ if (this.guards.isBehaviorSubjectMember(n)) {
1700
1754
  const observable = astUtils.replaceThisWithSelf(
1701
1755
  n.object
1702
1756
  );
@@ -1710,7 +1764,7 @@ var ObservableManager = class {
1710
1764
  replaceObservablesWithSignals(node, observableSignals, astUtils) {
1711
1765
  const cloned = this.t.cloneNode(node, true);
1712
1766
  this.walkNode(cloned, (n) => {
1713
- if (this.guards.isObservableMember(n)) {
1767
+ if (this.guards.isBehaviorSubjectMember(n)) {
1714
1768
  const observable = astUtils.replaceThisWithSelf(n.object);
1715
1769
  const key = this.getObservableKey(observable);
1716
1770
  const signalId = observableSignals.get(key);
@@ -1833,7 +1887,8 @@ var ElementTransformer = class {
1833
1887
  elId,
1834
1888
  statements,
1835
1889
  scope,
1836
- context2
1890
+ context2,
1891
+ tag
1837
1892
  );
1838
1893
  if (hasRef && refValue) {
1839
1894
  statements.push(
@@ -1927,7 +1982,7 @@ var ElementTransformer = class {
1927
1982
  context2.observables,
1928
1983
  this.astUtils
1929
1984
  );
1930
- if (this.guards.isSignalMember(expr) || this.guards.isObservableMember(expr)) {
1985
+ if (this.guards.isSignalMember(expr) || this.guards.isBehaviorSubjectMember(expr)) {
1931
1986
  const replaced = this.observableManager.replaceObservablesWithSignals(
1932
1987
  expr,
1933
1988
  context2.observableSignals,
@@ -1968,11 +2023,13 @@ var ElementTransformer = class {
1968
2023
  }
1969
2024
  }
1970
2025
  }
1971
- processDOMAttributes(attributes, elId, statements, scope, context2) {
2026
+ processDOMAttributes(attributes, elId, statements, scope, context2, tag) {
1972
2027
  let hasRef = false;
1973
2028
  let refValue = null;
1974
2029
  let hasDangerousHTML = false;
1975
2030
  let dangerousHTMLValue = null;
2031
+ let hasClickHandler = false;
2032
+ let hrefValue = null;
1976
2033
  for (const attr of attributes) {
1977
2034
  if (this.t.isJSXSpreadAttribute(attr)) {
1978
2035
  this.observableManager.collectObservables(
@@ -2037,17 +2094,59 @@ var ElementTransformer = class {
2037
2094
  continue;
2038
2095
  }
2039
2096
  if (/^on[A-Z]/.test(key)) {
2097
+ if (key === "onClick") {
2098
+ hasClickHandler = true;
2099
+ }
2040
2100
  this.processEventListener(key, attr, elId, statements, context2);
2041
2101
  continue;
2042
2102
  }
2103
+ if (key === "href" && this.t.isStringLiteral(attr.value)) {
2104
+ hrefValue = attr.value.value;
2105
+ }
2043
2106
  if (key === "style" && this.t.isJSXExpressionContainer(attr.value)) {
2044
2107
  this.processStyleAttribute(attr, elId, statements, scope, context2);
2045
2108
  continue;
2046
2109
  }
2047
2110
  this.processRegularAttribute(key, attr, elId, statements, context2);
2048
2111
  }
2112
+ if (tag === "a" && !hasClickHandler && hrefValue && this.isRelativeUrl(hrefValue)) {
2113
+ statements.push(
2114
+ this.t.expressionStatement(
2115
+ this.t.callExpression(
2116
+ this.t.memberExpression(
2117
+ elId,
2118
+ this.t.identifier("addEventListener")
2119
+ ),
2120
+ [
2121
+ this.t.stringLiteral("click"),
2122
+ this.t.arrowFunctionExpression(
2123
+ [this.t.identifier("event")],
2124
+ this.t.callExpression(
2125
+ this.t.memberExpression(
2126
+ this.t.identifier("Orca"),
2127
+ this.t.identifier("navigate")
2128
+ ),
2129
+ [this.t.identifier("event"), this.t.stringLiteral(hrefValue)]
2130
+ )
2131
+ )
2132
+ ]
2133
+ )
2134
+ )
2135
+ );
2136
+ }
2049
2137
  return { hasRef, refValue, hasDangerousHTML, dangerousHTMLValue };
2050
2138
  }
2139
+ isRelativeUrl(url) {
2140
+ try {
2141
+ new URL(url);
2142
+ return false;
2143
+ } catch {
2144
+ if (url.startsWith("#") || url.startsWith("mailto:") || url.startsWith("tel:")) {
2145
+ return false;
2146
+ }
2147
+ return true;
2148
+ }
2149
+ }
2051
2150
  processEventListener(key, attr, elId, statements, context2) {
2052
2151
  const eventName = key.slice(2).toLowerCase();
2053
2152
  let handler = this.t.nullLiteral();
@@ -2179,7 +2278,7 @@ var ElementTransformer = class {
2179
2278
  insertedValue = this.astUtils.getObject(
2180
2279
  expr
2181
2280
  );
2182
- } else if (this.guards.isObservableMember(expr)) {
2281
+ } else if (this.guards.isBehaviorSubjectMember(expr)) {
2183
2282
  const replaced = this.observableManager.replaceObservablesWithSignals(
2184
2283
  expr,
2185
2284
  context2.observableSignals,
@@ -2698,224 +2797,55 @@ ${decoratorsStr}export class ${className} extends _OrcaComponent {
2698
2797
  }
2699
2798
 
2700
2799
  // src/plugins/generators/generate_rpc.ts
2701
- import { parseSync as parseSync4 } from "@swc/core";
2702
2800
  function generateRpcStub(filePath, code) {
2703
2801
  try {
2704
- const ast = parseSync4(code, {
2705
- syntax: "typescript",
2706
- tsx: filePath.endsWith("x") || filePath.endsWith(".tsx"),
2707
- decorators: true
2708
- });
2709
- const serviceInfo = extractServiceInfo2(ast, filePath);
2710
- validateServiceInfo2(serviceInfo, filePath);
2711
- return generateStubCode2(serviceInfo);
2712
- } catch (error) {
2713
- if (error.type) {
2714
- throw error;
2715
- }
2716
- throw {
2717
- type: "parse",
2718
- message: `Failed to parse TypeScript file: ${error.message}`,
2719
- filePath,
2720
- details: error
2721
- };
2722
- }
2723
- }
2724
- function extractServiceInfo2(ast, filePath) {
2725
- let serviceClass = null;
2726
- try {
2727
- for (const item of ast.body) {
2728
- if (item.type === "ExportDeclaration" && item.declaration?.type === "ClassDeclaration") {
2729
- const classDecl = item.declaration;
2730
- if (hasInjectableDecorator2(classDecl.decorators)) {
2731
- serviceClass = classDecl;
2732
- break;
2733
- }
2734
- }
2735
- }
2736
- if (!serviceClass?.identifier) {
2802
+ const ast = parseTypeScript(filePath, code);
2803
+ const serviceInfo = extractServiceInfo(ast, filePath, false);
2804
+ if (!serviceInfo) {
2737
2805
  throw {
2738
2806
  type: "validation",
2739
2807
  message: "No exported class with @Injectable decorator found",
2740
2808
  filePath
2741
2809
  };
2742
2810
  }
2743
- const className = serviceClass.identifier.value;
2744
- const methods = extractMethods3(serviceClass, className, filePath);
2745
- return {
2746
- className,
2747
- methods
2748
- };
2811
+ validateServiceInfo(serviceInfo, filePath);
2812
+ return generateStubCode2(serviceInfo);
2749
2813
  } catch (error) {
2750
2814
  if (error.type) {
2751
2815
  throw error;
2752
2816
  }
2753
2817
  throw {
2754
2818
  type: "parse",
2755
- message: `Failed to extract service info: ${error.message}`,
2819
+ message: `Failed to parse TypeScript file: ${error.message}`,
2756
2820
  filePath,
2757
2821
  details: error
2758
2822
  };
2759
2823
  }
2760
2824
  }
2761
- function hasInjectableDecorator2(decorators) {
2762
- if (!decorators) return false;
2763
- return decorators.some((decorator) => {
2764
- const expr = decorator.expression;
2765
- if (expr.type === "CallExpression") {
2766
- if (expr.callee.type === "Identifier" && expr.callee.value === "Injectable") {
2767
- return true;
2768
- }
2769
- }
2770
- if (expr.type === "Identifier" && expr.value === "Injectable") {
2771
- return true;
2772
- }
2773
- return false;
2774
- });
2775
- }
2776
- function extractMethods3(classDecl, className, filePath) {
2777
- const methods = [];
2778
- for (const member of classDecl.body) {
2779
- if (!isPublicMethod2(member)) continue;
2780
- const method = member;
2781
- const methodName = getMethodName2(method);
2782
- if (!methodName) continue;
2783
- const returnTypeInfo = analyzeReturnType2(method);
2784
- if (!returnTypeInfo.isObservable && !method.function.async) {
2785
- throw {
2786
- type: "validation",
2787
- message: `Method ${className}.${methodName} must be async or return an Observable`,
2788
- filePath,
2789
- details: { className, methodName }
2790
- };
2791
- }
2792
- const params = extractMethodParams3(method.function.params || []);
2793
- methods.push({
2794
- name: methodName,
2795
- params,
2796
- returnType: returnTypeInfo.type,
2797
- isAsync: method.function.async,
2798
- isObservable: returnTypeInfo.isObservable
2799
- });
2800
- }
2801
- return methods;
2802
- }
2803
- function isPublicMethod2(member) {
2804
- return member.type === "ClassMethod" && (member.accessibility === "public" || !member.accessibility);
2805
- }
2806
- function getMethodName2(method) {
2807
- if (method.key.type === "Identifier") {
2808
- return method.key.value;
2809
- }
2810
- return null;
2811
- }
2812
- function analyzeReturnType2(method) {
2813
- const returnType = method.function.returnType?.typeAnnotation;
2814
- if (!returnType) {
2815
- return { type: "any", isObservable: false };
2816
- }
2817
- if (returnType.type === "TsTypeReference" && returnType.typeName.type === "Identifier" && returnType.typeName.value === "Observable") {
2818
- const innerType = returnType.typeParams?.params[0];
2819
- return {
2820
- type: innerType ? stringifyType4(innerType) : "any",
2821
- isObservable: true
2822
- };
2823
- }
2824
- if (returnType.type === "TsTypeReference" && returnType.typeName.type === "Identifier" && returnType.typeName.value === "Promise") {
2825
- const innerType = returnType.typeParams?.params[0];
2826
- return {
2827
- type: innerType ? stringifyType4(innerType) : "any",
2828
- isObservable: false
2829
- };
2830
- }
2831
- return {
2832
- type: stringifyType4(returnType),
2833
- isObservable: false
2834
- };
2835
- }
2836
- function extractMethodParams3(params) {
2837
- const result = [];
2838
- for (const param of params) {
2839
- const pat = param.pat;
2840
- if (pat.type === "Identifier") {
2841
- const name = pat.value;
2842
- const type = pat.typeAnnotation?.typeAnnotation ? stringifyType4(pat.typeAnnotation.typeAnnotation) : "any";
2843
- result.push({
2844
- name,
2845
- type
2846
- });
2847
- }
2848
- }
2849
- return result;
2850
- }
2851
- function stringifyType4(typeNode) {
2852
- if (!typeNode) return "any";
2853
- switch (typeNode.type) {
2854
- case "TsKeywordType":
2855
- return typeNode.kind;
2856
- case "TsTypeReference":
2857
- if (typeNode.typeName.type === "Identifier") {
2858
- const baseName = typeNode.typeName.value;
2859
- if (typeNode.typeParams?.params.length) {
2860
- const params = typeNode.typeParams.params.map(stringifyType4).join(", ");
2861
- return `${baseName}<${params}>`;
2862
- }
2863
- return baseName;
2864
- }
2865
- return "any";
2866
- case "TsArrayType":
2867
- return `${stringifyType4(typeNode.elemType)}[]`;
2868
- case "TsUnionType":
2869
- return typeNode.types.map(stringifyType4).join(" | ");
2870
- case "TsIntersectionType":
2871
- return typeNode.types.map(stringifyType4).join(" & ");
2872
- case "TsTypeLiteral":
2873
- const props = typeNode.members.map((member) => {
2874
- if (member.type === "TsPropertySignature") {
2875
- const key = member.key.type === "Identifier" ? member.key.value : "";
2876
- const type = member.typeAnnotation ? stringifyType4(member.typeAnnotation.typeAnnotation) : "any";
2877
- return `${key}: ${type}`;
2878
- }
2879
- return "";
2880
- }).filter(Boolean);
2881
- return `{ ${props.join("; ")} }`;
2882
- default:
2883
- return "any";
2884
- }
2885
- }
2886
- function validateServiceInfo2(serviceInfo, filePath) {
2887
- if (!serviceInfo.className) {
2888
- throw {
2889
- type: "validation",
2890
- message: "Service class must have a valid name",
2891
- filePath
2892
- };
2893
- }
2894
- if (serviceInfo.methods.length === 0) {
2895
- console.warn(
2896
- `Warning: Service ${serviceInfo.className} has no public methods`
2897
- );
2898
- }
2899
- }
2900
2825
  function generateStubCode2(serviceInfo) {
2901
2826
  const className = serviceInfo.className;
2902
- const basePath = serviceNameToPath2(className);
2903
- const methods = serviceInfo.methods.map((method) => generateMethod2(method, basePath)).join("\n\n");
2904
- const hasObservable = serviceInfo.methods.some((m) => m.isObservable);
2905
- const observableImport = hasObservable ? `import { Observable } from "@kithinji/orca";
2906
- ` : "";
2907
- return `${observableImport}import { Injectable } from "@kithinji/orca";
2827
+ const basePath = serviceNameToPath(className);
2828
+ const methods = serviceInfo.methods.map((method) => generateMethod2(method, basePath, className)).join("\n\n");
2829
+ const hasStreamable = serviceInfo.methods.some((m) => m.isStreamable);
2830
+ const imports = generateImports2(hasStreamable);
2831
+ return `${imports}
2908
2832
 
2909
2833
  @Injectable()
2910
2834
  export class ${className} {
2911
2835
  ${methods}
2912
2836
  }`;
2913
2837
  }
2914
- function serviceNameToPath2(serviceName) {
2915
- return serviceName.replace(/Service$/, "").replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
2838
+ function generateImports2(hasStreamable) {
2839
+ let imports = `import { Injectable } from "@kithinji/orca";
2840
+ `;
2841
+ if (hasStreamable) {
2842
+ imports += `import { Observable } from "@kithinji/orca";
2843
+ `;
2844
+ }
2845
+ return imports;
2916
2846
  }
2917
- function generateMethod2(method, basePath) {
2918
- if (method.isObservable) {
2847
+ function generateMethod2(method, basePath, serviceName) {
2848
+ if (method.isStreamable) {
2919
2849
  return generateSseMethod(method, basePath);
2920
2850
  }
2921
2851
  const params = method.params.map((p) => `${p.name}: ${p.type}`).join(", ");
@@ -3177,7 +3107,7 @@ function useMyPlugin(options) {
3177
3107
  }
3178
3108
 
3179
3109
  // src/plugins/analyzers/graph.ts
3180
- import { parseSync as parseSync5 } from "@swc/core";
3110
+ import { parseSync as parseSync4 } from "@swc/core";
3181
3111
  import * as fs3 from "fs";
3182
3112
  import * as path7 from "path";
3183
3113
  function resolveFilePath(fromFile, importPath) {
@@ -3325,7 +3255,7 @@ function buildGraph(entryPoints) {
3325
3255
  const store = Store.getInstance();
3326
3256
  const newCode = store.get(filePath);
3327
3257
  const content = newCode ? newCode[0] : fs3.readFileSync(filePath, "utf-8");
3328
- const ast = parseSync5(content, {
3258
+ const ast = parseSync4(content, {
3329
3259
  syntax: "typescript",
3330
3260
  tsx: isTsx,
3331
3261
  decorators: true
@@ -3393,30 +3323,247 @@ function stylePlugin(store) {
3393
3323
  };
3394
3324
  }
3395
3325
 
3326
+ // src/html/index.ts
3327
+ import * as fs5 from "fs/promises";
3328
+ var HtmlPreprocessor = class {
3329
+ constructor(options = {}) {
3330
+ this.options = options;
3331
+ }
3332
+ async processFile(inputPath, outputPath) {
3333
+ const html = await fs5.readFile(inputPath, "utf-8");
3334
+ const processed = await this.process(html);
3335
+ await fs5.writeFile(outputPath, processed, "utf-8");
3336
+ }
3337
+ async process(html) {
3338
+ let result = html;
3339
+ if (this.options.transformers) {
3340
+ for (const transformer of this.options.transformers) {
3341
+ result = await transformer(result);
3342
+ }
3343
+ }
3344
+ if (this.options.injectScripts && this.options.injectScripts.length > 0) {
3345
+ result = this.injectScripts(result, this.options.injectScripts);
3346
+ }
3347
+ if (this.options.injectStyles && this.options.injectStyles.length > 0) {
3348
+ result = this.injectStyles(result, this.options.injectStyles);
3349
+ }
3350
+ if (this.options.replaceVariables) {
3351
+ result = this.replaceVariables(result, this.options.replaceVariables);
3352
+ }
3353
+ if (this.options.minify) {
3354
+ result = this.minify(result);
3355
+ }
3356
+ return result;
3357
+ }
3358
+ injectScripts(html, scripts) {
3359
+ const scriptTags = scripts.map((src) => ` <script src="${src}"></script>`).join("\n");
3360
+ if (html.includes("</body>")) {
3361
+ return html.replace("</body>", `${scriptTags}
3362
+ </body>`);
3363
+ } else if (html.includes("</head>")) {
3364
+ return html.replace("</head>", `${scriptTags}
3365
+ </head>`);
3366
+ } else {
3367
+ return html + `
3368
+ ${scriptTags}`;
3369
+ }
3370
+ }
3371
+ injectStyles(html, styles) {
3372
+ const styleTags = styles.map((href) => ` <link rel="stylesheet" href="${href}">`).join("\n");
3373
+ if (html.includes("</head>")) {
3374
+ return html.replace("</head>", `${styleTags}
3375
+ </head>`);
3376
+ } else if (html.includes("<head>")) {
3377
+ return html.replace("<head>", `<head>
3378
+ ${styleTags}`);
3379
+ } else {
3380
+ return styleTags + "\n" + html;
3381
+ }
3382
+ }
3383
+ replaceVariables(html, variables) {
3384
+ let result = html;
3385
+ for (const [key, value] of Object.entries(variables)) {
3386
+ result = result.replace(new RegExp(`\\{\\{${key}\\}\\}`, "g"), value);
3387
+ result = result.replace(new RegExp(`\\$\\{${key}\\}`, "g"), value);
3388
+ }
3389
+ return result;
3390
+ }
3391
+ minify(html) {
3392
+ return html.replace(/<!--[\s\S]*?-->/g, "").replace(/\s+/g, " ").replace(/>\s+</g, "><").trim();
3393
+ }
3394
+ };
3395
+ function createHotReloadTransformer(port) {
3396
+ return (html) => {
3397
+ const hotReloadScript = `
3398
+ <script>
3399
+ (function() {
3400
+ let ws;
3401
+ let reconnectAttempts = 0;
3402
+ const maxReconnectAttempts = 10;
3403
+
3404
+ function connect() {
3405
+ ws = new WebSocket('ws://localhost:${port}');
3406
+
3407
+ ws.onopen = () => {
3408
+ reconnectAttempts = 0;
3409
+ };
3410
+
3411
+ ws.onmessage = (event) => {
3412
+ if (event.data === 'reload') {
3413
+ window.location.reload();
3414
+ }
3415
+ };
3416
+
3417
+ ws.onclose = () => {
3418
+ if (reconnectAttempts < maxReconnectAttempts) {
3419
+ reconnectAttempts++;
3420
+ console.log(\`\u{1F504} Reconnecting... (attempt \${reconnectAttempts}/\${maxReconnectAttempts})\`);
3421
+ setTimeout(connect, 1000 * reconnectAttempts);
3422
+ } else {
3423
+ console.log('\u274C Max reconnection attempts reached');
3424
+ }
3425
+ };
3426
+
3427
+ ws.onerror = (error) => {
3428
+ console.error('\u{1F525} Hot reload error:', error);
3429
+ };
3430
+ }
3431
+
3432
+ connect();
3433
+ })();
3434
+ </script>`;
3435
+ if (html.includes("</body>")) {
3436
+ return html.replace("</body>", `${hotReloadScript}
3437
+ </body>`);
3438
+ }
3439
+ return html + hotReloadScript;
3440
+ };
3441
+ }
3442
+
3396
3443
  // src/dev/server.ts
3397
- async function copyFile2() {
3444
+ var virtualClientFiles = {
3445
+ "virtual:navigate": {
3446
+ output: "navigate",
3447
+ code: `
3448
+ export async function navigate(event, url) {
3449
+ event.preventDefault();
3450
+
3451
+ try {
3452
+ const { Navigate, getCurrentInjector } = await import("./src/client/client.js");
3453
+ const injector = getCurrentInjector();
3454
+
3455
+ if (injector) {
3456
+ const navigate = injector.resolve(Navigate);
3457
+ navigate.go(url);
3458
+ } else {
3459
+ window.location.href = url;
3460
+ }
3461
+ } catch (error) {
3462
+ console.error("Navigation error:", error);
3463
+ window.location.href = url;
3464
+ }
3465
+ }
3466
+ `.trim()
3467
+ }
3468
+ };
3469
+ function createVirtualModulePlugin(virtualFiles) {
3470
+ return {
3471
+ name: "virtual-module",
3472
+ setup(build) {
3473
+ build.onResolve({ filter: /^virtual:/ }, (args) => {
3474
+ if (virtualFiles[args.path]) {
3475
+ return {
3476
+ path: args.path,
3477
+ namespace: "virtual"
3478
+ };
3479
+ }
3480
+ });
3481
+ build.onLoad({ filter: /.*/, namespace: "virtual" }, (args) => {
3482
+ const virtualFile = virtualFiles[args.path];
3483
+ if (virtualFile) {
3484
+ return {
3485
+ contents: virtualFile.code,
3486
+ loader: "js"
3487
+ };
3488
+ }
3489
+ });
3490
+ }
3491
+ };
3492
+ }
3493
+ var HotReloadManager = class {
3494
+ constructor(port = 3001) {
3495
+ this.wss = null;
3496
+ this.clients = /* @__PURE__ */ new Set();
3497
+ this.port = port;
3498
+ }
3499
+ start() {
3500
+ this.wss = new WebSocketServer({ port: this.port });
3501
+ this.wss.on("connection", (ws) => {
3502
+ this.clients.add(ws);
3503
+ ws.on("close", () => {
3504
+ this.clients.delete(ws);
3505
+ });
3506
+ ws.on("error", (error) => {
3507
+ console.error("WebSocket error:", error);
3508
+ this.clients.delete(ws);
3509
+ });
3510
+ });
3511
+ console.log(`Hot reload server listening on ws://localhost:${this.port}`);
3512
+ }
3513
+ reload() {
3514
+ const activeClients = Array.from(this.clients).filter(
3515
+ (client) => client.readyState === WebSocket.OPEN
3516
+ );
3517
+ if (activeClients.length === 0) {
3518
+ return;
3519
+ }
3520
+ activeClients.forEach((client) => {
3521
+ try {
3522
+ client.send("reload");
3523
+ } catch (error) {
3524
+ console.error("Failed to send reload signal:", error);
3525
+ this.clients.delete(client);
3526
+ }
3527
+ });
3528
+ }
3529
+ close() {
3530
+ if (this.wss) {
3531
+ this.clients.forEach((client) => client.close());
3532
+ this.wss.close();
3533
+ }
3534
+ }
3535
+ };
3536
+ async function copyAndProcessHtml(hotReloadPort, preprocessorOptions) {
3398
3537
  try {
3399
- await fs5.mkdir("public", { recursive: true });
3400
- await fs5.copyFile("./src/client/index.html", "./public/index.html");
3538
+ await fs6.mkdir("public", { recursive: true });
3539
+ const preprocessor = new HtmlPreprocessor({
3540
+ transformers: [createHotReloadTransformer(hotReloadPort)],
3541
+ injectScripts: ["./navigate.js"],
3542
+ ...preprocessorOptions
3543
+ });
3544
+ await preprocessor.processFile(
3545
+ "./src/client/index.html",
3546
+ "./public/index.html"
3547
+ );
3401
3548
  } catch (error) {
3402
- console.error("\u274C Failed to copy index.html:", error);
3549
+ console.error("Failed to copy and process index.html:", error);
3403
3550
  throw error;
3404
3551
  }
3405
3552
  }
3406
3553
  async function cleanDirectories() {
3407
3554
  await Promise.all([
3408
- fs5.rm("dist", { recursive: true, force: true }),
3409
- fs5.rm("public", { recursive: true, force: true })
3555
+ fs6.rm("dist", { recursive: true, force: true }),
3556
+ fs6.rm("public", { recursive: true, force: true })
3410
3557
  ]);
3411
3558
  }
3412
- function createRestartServerPlugin(serverProcess, onServerBuildComplete) {
3559
+ function createRestartServerPlugin(serverProcess, onServerBuildComplete, hotReloadManager) {
3413
3560
  return {
3414
3561
  name: "restart-server",
3415
3562
  setup(build) {
3416
3563
  build.onEnd((result) => {
3417
3564
  if (result.errors.length > 0) {
3418
3565
  console.error(
3419
- `\u274C Server build failed with ${result.errors.length} error(s)`
3566
+ `Server build failed with ${result.errors.length} error(s)`
3420
3567
  );
3421
3568
  return;
3422
3569
  }
@@ -3427,8 +3574,11 @@ function createRestartServerPlugin(serverProcess, onServerBuildComplete) {
3427
3574
  stdio: "inherit"
3428
3575
  });
3429
3576
  serverProcess.current.on("error", (err) => {
3430
- console.error("\u274C Server process error:", err);
3577
+ console.error("Server process error:", err);
3431
3578
  });
3579
+ setTimeout(() => {
3580
+ hotReloadManager.reload();
3581
+ }, 500);
3432
3582
  onServerBuildComplete();
3433
3583
  });
3434
3584
  }
@@ -3438,15 +3588,48 @@ async function startDevServer() {
3438
3588
  const store = Store.getInstance();
3439
3589
  const userConfig = await loadConfig();
3440
3590
  const config = mergeConfig(getDefaultConfig(), userConfig);
3591
+ const HOT_RELOAD_PORT = 3001;
3592
+ const hotReloadManager = new HotReloadManager(HOT_RELOAD_PORT);
3441
3593
  await cleanDirectories();
3442
- await copyFile2();
3594
+ await copyAndProcessHtml(HOT_RELOAD_PORT, config.htmlPreprocessor);
3595
+ hotReloadManager.start();
3443
3596
  const entryPoints = ["src/main.ts"];
3444
3597
  const clientFiles = /* @__PURE__ */ new Set(["src/client/client.tsx"]);
3445
3598
  const serverProcessRef = { current: null };
3446
3599
  let clientCtx = null;
3600
+ let virtualCtx = null;
3447
3601
  let isShuttingDown = false;
3448
3602
  let pendingClientFiles = /* @__PURE__ */ new Set();
3449
3603
  let needsClientRebuild = false;
3604
+ async function buildVirtualFiles() {
3605
+ if (isShuttingDown) return;
3606
+ try {
3607
+ if (virtualCtx) {
3608
+ await virtualCtx.dispose();
3609
+ virtualCtx = null;
3610
+ }
3611
+ const virtualEntryPoints = {};
3612
+ Object.entries(virtualClientFiles).forEach(([key, value]) => {
3613
+ virtualEntryPoints[value.output] = key;
3614
+ });
3615
+ virtualCtx = await esbuild2.context({
3616
+ entryPoints: virtualEntryPoints,
3617
+ bundle: true,
3618
+ outdir: "public",
3619
+ platform: "browser",
3620
+ format: "iife",
3621
+ globalName: "Orca",
3622
+ sourcemap: config.build?.sourcemap ?? true,
3623
+ minify: config.build?.minify ?? false,
3624
+ plugins: [createVirtualModulePlugin(virtualClientFiles)],
3625
+ write: true
3626
+ });
3627
+ await virtualCtx.rebuild();
3628
+ } catch (error) {
3629
+ console.error("Failed to build virtual files:", error);
3630
+ throw error;
3631
+ }
3632
+ }
3450
3633
  async function rebuildClient() {
3451
3634
  if (isShuttingDown) return;
3452
3635
  try {
@@ -3466,7 +3649,7 @@ async function startDevServer() {
3466
3649
  format: "esm",
3467
3650
  sourcemap: config.build?.sourcemap ?? true,
3468
3651
  splitting: true,
3469
- minify: config.build?.minify ?? true,
3652
+ minify: config.build?.minify ?? false,
3470
3653
  plugins: [
3471
3654
  ...config.plugins?.map((cb) => cb(store)) || [],
3472
3655
  ...config.client_plugins?.map((cb) => cb(store)) || [],
@@ -3482,8 +3665,11 @@ async function startDevServer() {
3482
3665
  build.onEnd((result) => {
3483
3666
  if (result.errors.length > 0) {
3484
3667
  console.error(
3485
- `\u274C Client build failed with ${result.errors.length} error(s)`
3668
+ `Client build failed with ${result.errors.length} error(s)`
3486
3669
  );
3670
+ } else {
3671
+ console.log("Client build completed");
3672
+ hotReloadManager.reload();
3487
3673
  }
3488
3674
  });
3489
3675
  }
@@ -3495,7 +3681,7 @@ async function startDevServer() {
3495
3681
  pendingClientFiles.clear();
3496
3682
  needsClientRebuild = false;
3497
3683
  } catch (error) {
3498
- console.error("\u274C Failed to rebuild client:", error);
3684
+ console.error("Failed to rebuild client:", error);
3499
3685
  throw error;
3500
3686
  }
3501
3687
  }
@@ -3526,13 +3712,18 @@ async function startDevServer() {
3526
3712
  }
3527
3713
  }
3528
3714
  }),
3529
- createRestartServerPlugin(serverProcessRef, onServerBuildComplete)
3715
+ createRestartServerPlugin(
3716
+ serverProcessRef,
3717
+ onServerBuildComplete,
3718
+ hotReloadManager
3719
+ )
3530
3720
  ],
3531
3721
  write: true
3532
3722
  });
3533
3723
  async function shutdown() {
3534
3724
  if (isShuttingDown) return;
3535
3725
  isShuttingDown = true;
3726
+ console.log("\nShutting down dev server...");
3536
3727
  try {
3537
3728
  if (serverProcessRef.current) {
3538
3729
  serverProcessRef.current.kill("SIGTERM");
@@ -3540,14 +3731,19 @@ async function startDevServer() {
3540
3731
  }
3541
3732
  await serverCtx.dispose();
3542
3733
  if (clientCtx) await clientCtx.dispose();
3734
+ if (virtualCtx) await virtualCtx.dispose();
3735
+ hotReloadManager.close();
3736
+ console.log("Dev server shut down successfully");
3543
3737
  process.exit(0);
3544
3738
  } catch (error) {
3545
- console.error("\u274C Error during shutdown:", error);
3739
+ console.error("Error during shutdown:", error);
3546
3740
  process.exit(1);
3547
3741
  }
3548
3742
  }
3549
3743
  process.on("SIGINT", shutdown);
3550
3744
  process.on("SIGTERM", shutdown);
3745
+ console.log("Starting dev server...");
3746
+ await buildVirtualFiles();
3551
3747
  await serverCtx.watch();
3552
3748
  }
3553
3749
 
@@ -3560,7 +3756,7 @@ __export(config_exports, {
3560
3756
  });
3561
3757
 
3562
3758
  // src/add/component/component.ts
3563
- import * as fs6 from "fs";
3759
+ import * as fs7 from "fs";
3564
3760
  import * as path8 from "path";
3565
3761
  import * as ts2 from "typescript";
3566
3762
  var ComponentDefinition = class {
@@ -3737,30 +3933,30 @@ Processing dependencies for "${name}": [${component.dependencies.join(
3737
3933
  );
3738
3934
  const componentDir = path8.dirname(componentPath);
3739
3935
  const appModulePath = path8.join(process.cwd(), "src/app/app.module.ts");
3740
- if (!fs6.existsSync(componentModulePath)) {
3936
+ if (!fs7.existsSync(componentModulePath)) {
3741
3937
  const moduleDir = path8.dirname(componentModulePath);
3742
- if (!fs6.existsSync(moduleDir)) {
3743
- fs6.mkdirSync(moduleDir, { recursive: true });
3938
+ if (!fs7.existsSync(moduleDir)) {
3939
+ fs7.mkdirSync(moduleDir, { recursive: true });
3744
3940
  }
3745
- fs6.writeFileSync(componentModulePath, createModule(), "utf-8");
3941
+ fs7.writeFileSync(componentModulePath, createModule(), "utf-8");
3746
3942
  }
3747
- if (!fs6.existsSync(componentDir)) {
3748
- fs6.mkdirSync(componentDir, { recursive: true });
3943
+ if (!fs7.existsSync(componentDir)) {
3944
+ fs7.mkdirSync(componentDir, { recursive: true });
3749
3945
  }
3750
- if (!fs6.existsSync(componentPath)) {
3751
- fs6.writeFileSync(componentPath, component.generate(), "utf-8");
3946
+ if (!fs7.existsSync(componentPath)) {
3947
+ fs7.writeFileSync(componentPath, component.generate(), "utf-8");
3752
3948
  console.log(`Created ${name}.component.tsx`);
3753
3949
  } else {
3754
3950
  console.log(`${name}.component.tsx already exists, skipping file creation`);
3755
3951
  }
3756
- const moduleContent = fs6.readFileSync(componentModulePath, "utf-8");
3952
+ const moduleContent = fs7.readFileSync(componentModulePath, "utf-8");
3757
3953
  const updatedModule = updateModuleWithComponent(moduleContent, name);
3758
- fs6.writeFileSync(componentModulePath, updatedModule, "utf-8");
3759
- if (fs6.existsSync(appModulePath)) {
3760
- const appModuleContent = fs6.readFileSync(appModulePath, "utf-8");
3954
+ fs7.writeFileSync(componentModulePath, updatedModule, "utf-8");
3955
+ if (fs7.existsSync(appModulePath)) {
3956
+ const appModuleContent = fs7.readFileSync(appModulePath, "utf-8");
3761
3957
  const updatedAppModule = ensureComponentModuleImported(appModuleContent);
3762
3958
  if (updatedAppModule !== appModuleContent) {
3763
- fs6.writeFileSync(appModulePath, updatedAppModule, "utf-8");
3959
+ fs7.writeFileSync(appModulePath, updatedAppModule, "utf-8");
3764
3960
  }
3765
3961
  }
3766
3962
  }
@@ -3946,12 +4142,12 @@ function toPascalCase(str) {
3946
4142
  }
3947
4143
 
3948
4144
  // src/utils/create.ts
3949
- import * as fs7 from "fs";
4145
+ import * as fs8 from "fs";
3950
4146
  import * as path9 from "path";
3951
4147
  function createStructure(basePath, entry) {
3952
- fs7.mkdirSync(basePath, { recursive: true });
4148
+ fs8.mkdirSync(basePath, { recursive: true });
3953
4149
  entry.files?.forEach((file) => {
3954
- fs7.writeFileSync(path9.join(basePath, file.name), file.content);
4150
+ fs8.writeFileSync(path9.join(basePath, file.name), file.content);
3955
4151
  });
3956
4152
  entry.dirs?.forEach((dir) => {
3957
4153
  const dirPath = path9.join(basePath, dir.name || "");
@@ -3964,7 +4160,7 @@ import path11 from "path";
3964
4160
 
3965
4161
  // src/add/module/module.ts
3966
4162
  import * as path10 from "path";
3967
- import * as fs8 from "fs";
4163
+ import * as fs9 from "fs";
3968
4164
  import * as ts3 from "typescript";
3969
4165
  function addFeature(name) {
3970
4166
  const featureDir = path10.join(process.cwd(), "src", "features", name);
@@ -4027,8 +4223,8 @@ function updateFeaturesIndex(featureName) {
4027
4223
  );
4028
4224
  const moduleName = toPascalCase(featureName + "_Module");
4029
4225
  const importPath = `./${featureName}/${featureName}.module`;
4030
- if (fs8.existsSync(featuresIndexPath)) {
4031
- let content = fs8.readFileSync(featuresIndexPath, "utf-8");
4226
+ if (fs9.existsSync(featuresIndexPath)) {
4227
+ let content = fs9.readFileSync(featuresIndexPath, "utf-8");
4032
4228
  const sourceFile = ts3.createSourceFile(
4033
4229
  "index.ts",
4034
4230
  content,
@@ -4054,24 +4250,24 @@ function updateFeaturesIndex(featureName) {
4054
4250
  }
4055
4251
  const exportStatement = `export { ${moduleName} } from "${importPath}";
4056
4252
  `;
4057
- fs8.appendFileSync(featuresIndexPath, exportStatement);
4253
+ fs9.appendFileSync(featuresIndexPath, exportStatement);
4058
4254
  } else {
4059
4255
  const featuresDir = path10.dirname(featuresIndexPath);
4060
- if (!fs8.existsSync(featuresDir)) {
4061
- fs8.mkdirSync(featuresDir, { recursive: true });
4256
+ if (!fs9.existsSync(featuresDir)) {
4257
+ fs9.mkdirSync(featuresDir, { recursive: true });
4062
4258
  }
4063
4259
  const exportStatement = `export { ${moduleName} } from "${importPath}";
4064
4260
  `;
4065
- fs8.writeFileSync(featuresIndexPath, exportStatement, "utf-8");
4261
+ fs9.writeFileSync(featuresIndexPath, exportStatement, "utf-8");
4066
4262
  }
4067
4263
  }
4068
4264
  function updateAppModule(featureName) {
4069
4265
  const appModulePath = path10.join(process.cwd(), "src", "app", "app.module.ts");
4070
- if (!fs8.existsSync(appModulePath)) {
4266
+ if (!fs9.existsSync(appModulePath)) {
4071
4267
  return;
4072
4268
  }
4073
4269
  const moduleName = toPascalCase(featureName + "_Module");
4074
- let content = fs8.readFileSync(appModulePath, "utf-8");
4270
+ let content = fs9.readFileSync(appModulePath, "utf-8");
4075
4271
  const sourceFile = ts3.createSourceFile(
4076
4272
  "app.module.ts",
4077
4273
  content,
@@ -4095,7 +4291,7 @@ function updateAppModule(featureName) {
4095
4291
  });
4096
4292
  if (hasImport) {
4097
4293
  content = addToModuleImportsArray(content, sourceFile, moduleName);
4098
- fs8.writeFileSync(appModulePath, content, "utf-8");
4294
+ fs9.writeFileSync(appModulePath, content, "utf-8");
4099
4295
  return;
4100
4296
  }
4101
4297
  let lastImportEnd = 0;
@@ -4114,7 +4310,7 @@ function updateAppModule(featureName) {
4114
4310
  true
4115
4311
  );
4116
4312
  content = addToModuleImportsArray(content, newSourceFile, moduleName);
4117
- fs8.writeFileSync(appModulePath, content, "utf-8");
4313
+ fs9.writeFileSync(appModulePath, content, "utf-8");
4118
4314
  }
4119
4315
  function addToModuleImportsArray(content, sourceFile, moduleName) {
4120
4316
  let decoratorNode;
@@ -4388,24 +4584,19 @@ export class ${serviceName} {
4388
4584
  }
4389
4585
  function createPage(name) {
4390
4586
  const pageName = toPascalCase(name + "_Page");
4391
- const serviceName = toPascalCase(name + "_Service");
4392
- const serviceVar = toCamelCase(name + "_Service");
4393
4587
  const listComponent = toPascalCase(name + "_List");
4394
4588
  return `import { Component } from "@kithinji/orca";
4395
- import { ${serviceName} } from "./${name}.service";
4396
4589
  import { ${listComponent} } from "./components/${name}-list.component";
4397
4590
 
4398
- @Component()
4591
+ @Component({
4592
+ deps: [${listComponent}]
4593
+ })
4399
4594
  export class ${pageName} {
4400
- constructor(
4401
- public ${serviceVar}: ${serviceName}
4402
- ) {}
4403
-
4404
4595
  build() {
4405
4596
  return (
4406
4597
  <div>
4407
4598
  <h1>${toPascalCase(name)} Management</h1>
4408
- <${listComponent} service={this.${serviceVar}} />
4599
+ <${listComponent} name="hello" />
4409
4600
  </div>
4410
4601
  );
4411
4602
  }
@@ -4415,6 +4606,7 @@ export class ${pageName} {
4415
4606
  function createListComponent(name) {
4416
4607
  const componentName = toPascalCase(name + "_List");
4417
4608
  const serviceName = toPascalCase(name + "_Service");
4609
+ const serviceVar = toCamelCase(name + "_Service");
4418
4610
  return `"use interactive";
4419
4611
 
4420
4612
  import { Component } from "@kithinji/orca";
@@ -4423,9 +4615,13 @@ import { ${serviceName} } from "../${name}.service";
4423
4615
  @Component()
4424
4616
  export class ${componentName} {
4425
4617
  props!: {
4426
- service: ${serviceName};
4618
+ name: string;
4427
4619
  };
4428
4620
 
4621
+ constructor(
4622
+ public readonly ${serviceVar}: ${serviceName}
4623
+ ) {}
4624
+
4429
4625
  build() {
4430
4626
  return (
4431
4627
  <div>
@@ -4599,6 +4795,9 @@ export function bootstrap() {
4599
4795
  }
4600
4796
 
4601
4797
  bootstrap();
4798
+
4799
+ /* Don't modify */
4800
+ export { Navigate, getCurrentInjector } from "@kithinji/orca";
4602
4801
  `;
4603
4802
  }
4604
4803
  function genMainTs() {
@@ -4621,17 +4820,17 @@ import path14 from "path";
4621
4820
  import { execSync } from "child_process";
4622
4821
 
4623
4822
  // src/docker/docker.ts
4624
- import fs9 from "fs-extra";
4823
+ import fs10 from "fs-extra";
4625
4824
  import path12 from "path";
4626
4825
  import prompts from "prompts";
4627
4826
  import yaml from "js-yaml";
4628
4827
  async function dockerize(env = "prod") {
4629
4828
  const cwd = process.cwd();
4630
4829
  const packageJsonPath = path12.join(cwd, "package.json");
4631
- if (!fs9.existsSync(packageJsonPath)) {
4830
+ if (!fs10.existsSync(packageJsonPath)) {
4632
4831
  throw new Error("package.json not found. Are you in a Pod project?");
4633
4832
  }
4634
- const packageJson = await fs9.readJSON(packageJsonPath);
4833
+ const packageJson = await fs10.readJSON(packageJsonPath);
4635
4834
  const projectName = packageJson.name;
4636
4835
  const detectedServices = detectServices(packageJson);
4637
4836
  const selectedServices = await selectServices(detectedServices);
@@ -4672,30 +4871,30 @@ async function selectServices(detected) {
4672
4871
  }
4673
4872
  async function restructureProject(cwd, projectName) {
4674
4873
  const nestedDir = path12.join(cwd, projectName);
4675
- if (fs9.existsSync(nestedDir)) {
4874
+ if (fs10.existsSync(nestedDir)) {
4676
4875
  console.log("\u26A0\uFE0F Project already restructured, skipping...");
4677
4876
  return;
4678
4877
  }
4679
- await fs9.ensureDir(nestedDir);
4680
- const items = await fs9.readdir(cwd);
4878
+ await fs10.ensureDir(nestedDir);
4879
+ const items = await fs10.readdir(cwd);
4681
4880
  const toMove = items.filter((item) => item !== projectName);
4682
4881
  for (const item of toMove) {
4683
4882
  const src = path12.join(cwd, item);
4684
4883
  const dest = path12.join(nestedDir, item);
4685
- await fs9.move(src, dest, { overwrite: true });
4884
+ await fs10.move(src, dest, { overwrite: true });
4686
4885
  }
4687
4886
  const envSrc = path12.join(nestedDir, ".env");
4688
4887
  const envDest = path12.join(cwd, ".env");
4689
- if (fs9.existsSync(envSrc)) {
4690
- await fs9.move(envSrc, envDest, { overwrite: true });
4888
+ if (fs10.existsSync(envSrc)) {
4889
+ await fs10.move(envSrc, envDest, { overwrite: true });
4691
4890
  }
4692
4891
  }
4693
4892
  async function writeEnvVars(cwd, services, env) {
4694
4893
  const envPath = path12.join(cwd, ".env");
4695
4894
  let existingEnv = {};
4696
4895
  let existingContent = "";
4697
- if (fs9.existsSync(envPath)) {
4698
- existingContent = await fs9.readFile(envPath, "utf8");
4896
+ if (fs10.existsSync(envPath)) {
4897
+ existingContent = await fs10.readFile(envPath, "utf8");
4699
4898
  existingEnv = parseEnvFile(existingContent);
4700
4899
  }
4701
4900
  const newVars = [];
@@ -4724,7 +4923,7 @@ async function writeEnvVars(cwd, services, env) {
4724
4923
  if (newVars.length > 0) {
4725
4924
  const separator = existingContent && !existingContent.endsWith("\n") ? "\n" : "";
4726
4925
  const newContent = existingContent + separator + (existingContent ? "\n" : "") + newVars.join("\n") + "\n";
4727
- await fs9.writeFile(envPath, newContent);
4926
+ await fs10.writeFile(envPath, newContent);
4728
4927
  console.log(
4729
4928
  `\u2705 Added ${newVars.length} new environment variable(s) to .env`
4730
4929
  );
@@ -4820,8 +5019,8 @@ docker-compose*.yml
4820
5019
  tmp
4821
5020
  temp
4822
5021
  `;
4823
- await fs9.writeFile(dockerfilePath, dockerfile);
4824
- await fs9.writeFile(dockerignorePath, dockerignore);
5022
+ await fs10.writeFile(dockerfilePath, dockerfile);
5023
+ await fs10.writeFile(dockerignorePath, dockerignore);
4825
5024
  }
4826
5025
  async function createDeployfile(cwd, projectName) {
4827
5026
  const deployFile = `name: ${projectName}
@@ -4946,7 +5145,7 @@ targets:
4946
5145
  docker image prune -af --filter "until=24h"
4947
5146
  `;
4948
5147
  const deployFilePath = path12.join(cwd, "pod.deploy.yml");
4949
- await fs9.writeFile(deployFilePath, deployFile);
5148
+ await fs10.writeFile(deployFilePath, deployFile);
4950
5149
  }
4951
5150
  async function setupProduction(cwd, projectName, services) {
4952
5151
  const compose = {
@@ -5017,7 +5216,7 @@ async function setupProduction(cwd, projectName, services) {
5017
5216
  compose.services[projectName].depends_on.push(service.name);
5018
5217
  }
5019
5218
  const composePath = path12.join(cwd, "docker-compose.yml");
5020
- await fs9.writeFile(
5219
+ await fs10.writeFile(
5021
5220
  composePath,
5022
5221
  yaml.dump(compose, { indent: 2, lineWidth: -1 })
5023
5222
  );
@@ -5025,8 +5224,8 @@ async function setupProduction(cwd, projectName, services) {
5025
5224
  async function setupDevelopment(cwd, projectName, services) {
5026
5225
  const existingCompose = path12.join(cwd, "docker-compose.yml");
5027
5226
  let existingServices = [];
5028
- if (fs9.existsSync(existingCompose)) {
5029
- const content = await fs9.readFile(existingCompose, "utf8");
5227
+ if (fs10.existsSync(existingCompose)) {
5228
+ const content = await fs10.readFile(existingCompose, "utf8");
5030
5229
  const existing = yaml.load(content);
5031
5230
  if (existing.services) {
5032
5231
  existingServices = Object.keys(existing.services).filter((s) => ["postgres", "mysql", "redis", "mongodb"].includes(s)).map((name) => ({ name }));
@@ -5096,14 +5295,14 @@ async function setupDevelopment(cwd, projectName, services) {
5096
5295
  compose.services[projectName].depends_on.push(tunnelName);
5097
5296
  }
5098
5297
  const devComposePath = path12.join(cwd, "docker-compose.dev.yml");
5099
- await fs9.writeFile(
5298
+ await fs10.writeFile(
5100
5299
  devComposePath,
5101
5300
  yaml.dump(compose, { indent: 2, lineWidth: -1 })
5102
5301
  );
5103
5302
  }
5104
5303
  async function createTunnelService(projectDir, serviceName) {
5105
5304
  const tunnelDir = path12.join(projectDir, `${serviceName}-tunnel`);
5106
- await fs9.ensureDir(tunnelDir);
5305
+ await fs10.ensureDir(tunnelDir);
5107
5306
  const dockerfile = `FROM alpine:latest
5108
5307
 
5109
5308
  RUN apk add --no-cache openssh-client
@@ -5131,8 +5330,8 @@ ssh -i $SSH_KEY \\
5131
5330
  -o ServerAliveInterval=60 \\
5132
5331
  $REMOTE_HOST
5133
5332
  `;
5134
- await fs9.writeFile(path12.join(tunnelDir, "Dockerfile"), dockerfile);
5135
- await fs9.writeFile(path12.join(tunnelDir, "tunnel.sh"), tunnelScript);
5333
+ await fs10.writeFile(path12.join(tunnelDir, "Dockerfile"), dockerfile);
5334
+ await fs10.writeFile(path12.join(tunnelDir, "tunnel.sh"), tunnelScript);
5136
5335
  }
5137
5336
  function getServiceConfig(serviceName) {
5138
5337
  const configs = {
@@ -5242,7 +5441,7 @@ function printNextSteps(projectName, env, services) {
5242
5441
  }
5243
5442
 
5244
5443
  // src/deploy/deploy.ts
5245
- import fs10 from "fs-extra";
5444
+ import fs11 from "fs-extra";
5246
5445
  import yaml2 from "js-yaml";
5247
5446
  import path13 from "path";
5248
5447
  import os from "os";
@@ -5499,12 +5698,12 @@ STDERR: ${result.stderr}`);
5499
5698
  }
5500
5699
  async uploadContent(remotePath, content) {
5501
5700
  const localTmp = path13.join(os.tmpdir(), `pod_tmp_${Date.now()}`);
5502
- fs10.writeFileSync(localTmp, content);
5701
+ fs11.writeFileSync(localTmp, content);
5503
5702
  try {
5504
5703
  await this.ssh.execCommand(`mkdir -p $(dirname ${remotePath})`);
5505
5704
  await this.ssh.putFile(localTmp, remotePath);
5506
5705
  } finally {
5507
- if (fs10.existsSync(localTmp)) fs10.unlinkSync(localTmp);
5706
+ if (fs11.existsSync(localTmp)) fs11.unlinkSync(localTmp);
5508
5707
  }
5509
5708
  }
5510
5709
  async readJson(remotePath) {
@@ -5578,25 +5777,25 @@ STDERR: ${err.stderr || err.message}`
5578
5777
  os.tmpdir(),
5579
5778
  `pod_script_${name}_${Date.now()}.sh`
5580
5779
  );
5581
- fs10.writeFileSync(scriptPath, interpolated);
5582
- fs10.chmodSync(scriptPath, "755");
5780
+ fs11.writeFileSync(scriptPath, interpolated);
5781
+ fs11.chmodSync(scriptPath, "755");
5583
5782
  try {
5584
5783
  await this.runCommand(scriptPath);
5585
5784
  } finally {
5586
- if (fs10.existsSync(scriptPath)) fs10.unlinkSync(scriptPath);
5785
+ if (fs11.existsSync(scriptPath)) fs11.unlinkSync(scriptPath);
5587
5786
  }
5588
5787
  }
5589
5788
  async uploadContent(localPath, content) {
5590
5789
  const dir = path13.dirname(localPath);
5591
- if (!fs10.existsSync(dir)) {
5592
- fs10.mkdirSync(dir, { recursive: true });
5790
+ if (!fs11.existsSync(dir)) {
5791
+ fs11.mkdirSync(dir, { recursive: true });
5593
5792
  }
5594
- fs10.writeFileSync(localPath, content);
5793
+ fs11.writeFileSync(localPath, content);
5595
5794
  }
5596
5795
  async readJson(localPath) {
5597
5796
  try {
5598
- if (!fs10.existsSync(localPath)) return null;
5599
- const content = fs10.readFileSync(localPath, "utf8");
5797
+ if (!fs11.existsSync(localPath)) return null;
5798
+ const content = fs11.readFileSync(localPath, "utf8");
5600
5799
  return JSON.parse(content);
5601
5800
  } catch {
5602
5801
  return null;
@@ -5604,8 +5803,8 @@ STDERR: ${err.stderr || err.message}`
5604
5803
  }
5605
5804
  async syncDirectory(source, destination, exclude) {
5606
5805
  console.log(chalk.gray(` Copying ${source} \u2192 ${destination}`));
5607
- if (!fs10.existsSync(destination)) {
5608
- fs10.mkdirSync(destination, { recursive: true });
5806
+ if (!fs11.existsSync(destination)) {
5807
+ fs11.mkdirSync(destination, { recursive: true });
5609
5808
  }
5610
5809
  const shouldExclude = (relativePath) => {
5611
5810
  if (!exclude?.length) return false;
@@ -5622,19 +5821,19 @@ STDERR: ${err.stderr || err.message}`
5622
5821
  });
5623
5822
  };
5624
5823
  const copyRecursive = (src, dest) => {
5625
- const entries = fs10.readdirSync(src, { withFileTypes: true });
5824
+ const entries = fs11.readdirSync(src, { withFileTypes: true });
5626
5825
  for (const entry of entries) {
5627
5826
  const srcPath = path13.join(src, entry.name);
5628
5827
  const destPath = path13.join(dest, entry.name);
5629
5828
  const relativePath = path13.relative(source, srcPath);
5630
5829
  if (shouldExclude(relativePath)) continue;
5631
5830
  if (entry.isDirectory()) {
5632
- if (!fs10.existsSync(destPath)) {
5633
- fs10.mkdirSync(destPath, { recursive: true });
5831
+ if (!fs11.existsSync(destPath)) {
5832
+ fs11.mkdirSync(destPath, { recursive: true });
5634
5833
  }
5635
5834
  copyRecursive(srcPath, destPath);
5636
5835
  } else {
5637
- fs10.copyFileSync(srcPath, destPath);
5836
+ fs11.copyFileSync(srcPath, destPath);
5638
5837
  }
5639
5838
  }
5640
5839
  };
@@ -5797,7 +5996,7 @@ var OperationHandler = class {
5797
5996
  async function deploy(targetName, options) {
5798
5997
  const cwd = process.cwd();
5799
5998
  const rawConfig = yaml2.load(
5800
- fs10.readFileSync(path13.join(cwd, "pod.deploy.yml"), "utf8"),
5999
+ fs11.readFileSync(path13.join(cwd, "pod.deploy.yml"), "utf8"),
5801
6000
  { schema: yaml2.DEFAULT_SCHEMA }
5802
6001
  );
5803
6002
  const rawTarget = rawConfig.targets?.[targetName];
@@ -5859,7 +6058,7 @@ Deployment Failed: ${err.message}`));
5859
6058
  // src/main.ts
5860
6059
  import chalk2 from "chalk";
5861
6060
  var program = new Command();
5862
- program.name("pod").description("Pod cli tool").version("1.0.24");
6061
+ program.name("pod").description("Pod cli tool").version("1.0.26");
5863
6062
  program.command("new <name>").description("Start a new Orca Project").action(async (name) => {
5864
6063
  await addNew(name);
5865
6064
  const appDir = path14.resolve(process.cwd());