@agentuity/cli 1.0.38 → 1.0.40
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/cmd/build/app-router-detector.d.ts +42 -0
- package/dist/cmd/build/app-router-detector.d.ts.map +1 -0
- package/dist/cmd/build/app-router-detector.js +253 -0
- package/dist/cmd/build/app-router-detector.js.map +1 -0
- package/dist/cmd/build/ast.d.ts +11 -1
- package/dist/cmd/build/ast.d.ts.map +1 -1
- package/dist/cmd/build/ast.js +273 -29
- package/dist/cmd/build/ast.js.map +1 -1
- package/dist/cmd/build/entry-generator.d.ts.map +1 -1
- package/dist/cmd/build/entry-generator.js +23 -16
- package/dist/cmd/build/entry-generator.js.map +1 -1
- package/dist/cmd/build/vite/bundle-files.d.ts +12 -0
- package/dist/cmd/build/vite/bundle-files.d.ts.map +1 -0
- package/dist/cmd/build/vite/bundle-files.js +107 -0
- package/dist/cmd/build/vite/bundle-files.js.map +1 -0
- package/dist/cmd/build/vite/registry-generator.d.ts.map +1 -1
- package/dist/cmd/build/vite/registry-generator.js +37 -13
- package/dist/cmd/build/vite/registry-generator.js.map +1 -1
- package/dist/cmd/build/vite/route-discovery.d.ts +17 -3
- package/dist/cmd/build/vite/route-discovery.d.ts.map +1 -1
- package/dist/cmd/build/vite/route-discovery.js +91 -3
- package/dist/cmd/build/vite/route-discovery.js.map +1 -1
- package/dist/cmd/build/vite/vite-builder.d.ts.map +1 -1
- package/dist/cmd/build/vite/vite-builder.js +9 -0
- package/dist/cmd/build/vite/vite-builder.js.map +1 -1
- package/dist/cmd/build/vite-bundler.d.ts.map +1 -1
- package/dist/cmd/build/vite-bundler.js +3 -0
- package/dist/cmd/build/vite-bundler.js.map +1 -1
- package/dist/cmd/dev/index.d.ts.map +1 -1
- package/dist/cmd/dev/index.js +30 -0
- package/dist/cmd/dev/index.js.map +1 -1
- package/dist/types.d.ts +9 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/route-migration.d.ts +61 -0
- package/dist/utils/route-migration.d.ts.map +1 -0
- package/dist/utils/route-migration.js +662 -0
- package/dist/utils/route-migration.js.map +1 -0
- package/package.json +6 -6
- package/src/cmd/build/app-router-detector.ts +350 -0
- package/src/cmd/build/ast.ts +339 -36
- package/src/cmd/build/entry-generator.ts +23 -16
- package/src/cmd/build/vite/bundle-files.ts +135 -0
- package/src/cmd/build/vite/registry-generator.ts +38 -13
- package/src/cmd/build/vite/route-discovery.ts +151 -3
- package/src/cmd/build/vite/vite-builder.ts +11 -0
- package/src/cmd/build/vite-bundler.ts +4 -0
- package/src/cmd/dev/index.ts +34 -0
- package/src/types.ts +9 -0
- package/src/utils/route-migration.ts +793 -0
package/dist/cmd/build/ast.js
CHANGED
|
@@ -1068,9 +1068,96 @@ function resolveImportPath(fromDir, importPath) {
|
|
|
1068
1068
|
}
|
|
1069
1069
|
return null;
|
|
1070
1070
|
}
|
|
1071
|
-
|
|
1071
|
+
/**
|
|
1072
|
+
* Check if a CallExpression is a chained router initializer.
|
|
1073
|
+
* Walks up the callee chain looking for createRouter() or new Hono() at the root.
|
|
1074
|
+
*
|
|
1075
|
+
* Example AST for `createRouter().get('/foo', handler).post('/bar', handler)`:
|
|
1076
|
+
* ```
|
|
1077
|
+
* CallExpression (.post)
|
|
1078
|
+
* callee: MemberExpression
|
|
1079
|
+
* object: CallExpression (.get)
|
|
1080
|
+
* callee: MemberExpression
|
|
1081
|
+
* object: CallExpression (createRouter)
|
|
1082
|
+
* property: "post"
|
|
1083
|
+
* ```
|
|
1084
|
+
*/
|
|
1085
|
+
function isChainedRouterInit(node) {
|
|
1086
|
+
let current = node;
|
|
1087
|
+
// Walk down the chain: each link is CallExpression → MemberExpression → CallExpression
|
|
1088
|
+
while (current.type === 'CallExpression') {
|
|
1089
|
+
const callee = current.callee;
|
|
1090
|
+
if (!callee)
|
|
1091
|
+
return false;
|
|
1092
|
+
// Direct createRouter()
|
|
1093
|
+
if (callee.type === 'Identifier' && callee.name === 'createRouter')
|
|
1094
|
+
return true;
|
|
1095
|
+
// Chained: .method() → MemberExpression
|
|
1096
|
+
if (callee.type === 'MemberExpression' && callee.object) {
|
|
1097
|
+
current = callee.object;
|
|
1098
|
+
continue;
|
|
1099
|
+
}
|
|
1100
|
+
break;
|
|
1101
|
+
}
|
|
1102
|
+
// Check if we landed on createRouter() or new Hono()
|
|
1103
|
+
if (current.type === 'CallExpression') {
|
|
1104
|
+
const callee = current.callee;
|
|
1105
|
+
if (callee?.type === 'Identifier' && callee.name === 'createRouter')
|
|
1106
|
+
return true;
|
|
1107
|
+
}
|
|
1108
|
+
if (current.type === 'NewExpression') {
|
|
1109
|
+
const callee = current.callee;
|
|
1110
|
+
if (callee?.type === 'Identifier' && callee.name === 'Hono')
|
|
1111
|
+
return true;
|
|
1112
|
+
}
|
|
1113
|
+
return false;
|
|
1114
|
+
}
|
|
1115
|
+
/**
|
|
1116
|
+
* Flatten a chained call expression into individual method calls.
|
|
1117
|
+
*
|
|
1118
|
+
* Given `createRouter().get('/a', h1).post('/b', h2).route('/c', sub)`, returns:
|
|
1119
|
+
* ```
|
|
1120
|
+
* [
|
|
1121
|
+
* { method: 'get', arguments: ['/a', h1] },
|
|
1122
|
+
* { method: 'post', arguments: ['/b', h2] },
|
|
1123
|
+
* { method: 'route', arguments: ['/c', sub] },
|
|
1124
|
+
* ]
|
|
1125
|
+
* ```
|
|
1126
|
+
*/
|
|
1127
|
+
function flattenChainedCalls(node) {
|
|
1128
|
+
const calls = [];
|
|
1129
|
+
let current = node;
|
|
1130
|
+
while (current.type === 'CallExpression') {
|
|
1131
|
+
const callee = current.callee;
|
|
1132
|
+
if (!callee)
|
|
1133
|
+
break;
|
|
1134
|
+
if (callee.type === 'MemberExpression' &&
|
|
1135
|
+
callee.property?.type === 'Identifier') {
|
|
1136
|
+
calls.unshift({
|
|
1137
|
+
method: callee.property.name,
|
|
1138
|
+
arguments: current.arguments || [],
|
|
1139
|
+
});
|
|
1140
|
+
current = callee.object;
|
|
1141
|
+
continue;
|
|
1142
|
+
}
|
|
1143
|
+
break; // Reached the root (createRouter() / new Hono())
|
|
1144
|
+
}
|
|
1145
|
+
return calls;
|
|
1146
|
+
}
|
|
1147
|
+
export async function parseRoute(rootDir, filename, projectId, deploymentId, visitedFilesOrOptions, mountedSubrouters) {
|
|
1148
|
+
// Support both old positional args and new options object
|
|
1149
|
+
let options;
|
|
1150
|
+
if (visitedFilesOrOptions instanceof Set) {
|
|
1151
|
+
options = { visitedFiles: visitedFilesOrOptions, mountedSubrouters };
|
|
1152
|
+
}
|
|
1153
|
+
else if (visitedFilesOrOptions && typeof visitedFilesOrOptions === 'object') {
|
|
1154
|
+
options = visitedFilesOrOptions;
|
|
1155
|
+
}
|
|
1156
|
+
else {
|
|
1157
|
+
options = { mountedSubrouters };
|
|
1158
|
+
}
|
|
1072
1159
|
// Track visited files to prevent infinite recursion
|
|
1073
|
-
const visited = visitedFiles ?? new Set();
|
|
1160
|
+
const visited = options.visitedFiles ?? new Set();
|
|
1074
1161
|
const resolvedFilename = resolve(filename);
|
|
1075
1162
|
if (visited.has(resolvedFilename)) {
|
|
1076
1163
|
return []; // Already parsed this file, avoid infinite loop
|
|
@@ -1159,6 +1246,9 @@ export async function parseRoute(rootDir, filename, projectId, deploymentId, vis
|
|
|
1159
1246
|
message: `could not find default export for ${filename} using ${rootDir}`,
|
|
1160
1247
|
});
|
|
1161
1248
|
}
|
|
1249
|
+
// Track the chained init expression (e.g., createRouter().get(...).post(...))
|
|
1250
|
+
// so we can extract routes from it later
|
|
1251
|
+
let chainedInitExpr;
|
|
1162
1252
|
for (const body of ast.body) {
|
|
1163
1253
|
if (body.type === 'VariableDeclaration') {
|
|
1164
1254
|
for (const vardecl of body.declarations) {
|
|
@@ -1172,6 +1262,14 @@ export async function parseRoute(rootDir, filename, projectId, deploymentId, vis
|
|
|
1172
1262
|
variableName = identifier.name;
|
|
1173
1263
|
break;
|
|
1174
1264
|
}
|
|
1265
|
+
// Support chained calls: createRouter().get(...).post(...)
|
|
1266
|
+
// The init is a CallExpression whose callee is a MemberExpression chain
|
|
1267
|
+
// that eventually roots at createRouter() or new Hono()
|
|
1268
|
+
if (isChainedRouterInit(call)) {
|
|
1269
|
+
variableName = identifier.name;
|
|
1270
|
+
chainedInitExpr = call;
|
|
1271
|
+
break;
|
|
1272
|
+
}
|
|
1175
1273
|
}
|
|
1176
1274
|
else if (vardecl.init?.type === 'NewExpression') {
|
|
1177
1275
|
const newExpr = vardecl.init;
|
|
@@ -1193,16 +1291,19 @@ export async function parseRoute(rootDir, filename, projectId, deploymentId, vis
|
|
|
1193
1291
|
});
|
|
1194
1292
|
}
|
|
1195
1293
|
const rel = toForwardSlash(relative(rootDir, filename));
|
|
1196
|
-
//
|
|
1197
|
-
//
|
|
1198
|
-
//
|
|
1199
|
-
//
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1294
|
+
// Determine the base mount path for routes in this file.
|
|
1295
|
+
// When mountPrefix is provided (explicit routing via createApp({ router })),
|
|
1296
|
+
// use it directly — the mount path comes from the code, not the filesystem.
|
|
1297
|
+
// Otherwise, derive it from the file's position in src/api/ (file-based routing).
|
|
1298
|
+
let basePath;
|
|
1299
|
+
if (options.mountPrefix !== undefined) {
|
|
1300
|
+
basePath = options.mountPrefix;
|
|
1301
|
+
}
|
|
1302
|
+
else {
|
|
1303
|
+
const srcDir = join(rootDir, 'src');
|
|
1304
|
+
const relativeApiPath = extractRelativeApiPath(filename, srcDir);
|
|
1305
|
+
basePath = computeApiMountPath(relativeApiPath);
|
|
1306
|
+
}
|
|
1206
1307
|
const routes = [];
|
|
1207
1308
|
try {
|
|
1208
1309
|
for (const body of ast.body) {
|
|
@@ -1268,31 +1369,38 @@ export async function parseRoute(rootDir, filename, projectId, deploymentId, vis
|
|
|
1268
1369
|
continue;
|
|
1269
1370
|
}
|
|
1270
1371
|
try {
|
|
1271
|
-
//
|
|
1272
|
-
const
|
|
1372
|
+
// The combined mount point for this sub-router
|
|
1373
|
+
const combinedBase = joinMountAndRoute(basePath, mountPath);
|
|
1374
|
+
// Parse sub-router's routes with the code-derived mount prefix.
|
|
1375
|
+
// This ensures the sub-router's routes are prefixed correctly
|
|
1376
|
+
// regardless of where the file lives on disk.
|
|
1377
|
+
const subRoutes = await parseRoute(rootDir, resolvedFile, projectId, deploymentId, {
|
|
1378
|
+
visitedFiles: visited,
|
|
1379
|
+
mountedSubrouters: options.mountedSubrouters,
|
|
1380
|
+
mountPrefix: combinedBase,
|
|
1381
|
+
});
|
|
1273
1382
|
// Track this file as a mounted sub-router
|
|
1274
|
-
if (mountedSubrouters) {
|
|
1275
|
-
mountedSubrouters.add(resolve(resolvedFile));
|
|
1383
|
+
if (options.mountedSubrouters) {
|
|
1384
|
+
options.mountedSubrouters.add(resolve(resolvedFile));
|
|
1276
1385
|
}
|
|
1277
|
-
// Compute the sub-router's own basePath so we can strip it
|
|
1278
|
-
const subSrcDir = join(rootDir, 'src');
|
|
1279
|
-
const subRelativeApiPath = extractRelativeApiPath(resolvedFile, subSrcDir);
|
|
1280
|
-
const subBasePath = computeApiMountPath(subRelativeApiPath);
|
|
1281
|
-
// The combined mount point for sub-routes
|
|
1282
|
-
const combinedBase = joinMountAndRoute(basePath, mountPath);
|
|
1283
1386
|
for (const subRoute of subRoutes) {
|
|
1284
|
-
//
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
routeSuffix = routeSuffix.slice(subBasePath.length) || '/';
|
|
1288
|
-
}
|
|
1289
|
-
const fullPath = joinMountAndRoute(combinedBase, routeSuffix);
|
|
1387
|
+
// Sub-routes already have the correct full path
|
|
1388
|
+
// (the recursive call used combinedBase as mountPrefix)
|
|
1389
|
+
const fullPath = subRoute.path;
|
|
1290
1390
|
const id = generateRouteId(projectId, deploymentId, subRoute.type, subRoute.method, rel, fullPath, subRoute.version);
|
|
1391
|
+
// Preserve the sub-route's original filename for schema
|
|
1392
|
+
// import resolution. The registry generator needs to know
|
|
1393
|
+
// which file actually defines/exports the schema variable.
|
|
1394
|
+
const config = { ...subRoute.config };
|
|
1395
|
+
if (subRoute.filename && subRoute.filename !== rel) {
|
|
1396
|
+
config.schemaSourceFile = subRoute.filename;
|
|
1397
|
+
}
|
|
1291
1398
|
routes.push({
|
|
1292
1399
|
...subRoute,
|
|
1293
1400
|
id,
|
|
1294
1401
|
path: fullPath,
|
|
1295
|
-
filename: rel,
|
|
1402
|
+
filename: rel,
|
|
1403
|
+
config: Object.keys(config).length > 0 ? config : undefined,
|
|
1296
1404
|
});
|
|
1297
1405
|
}
|
|
1298
1406
|
}
|
|
@@ -1672,6 +1780,142 @@ export async function parseRoute(rootDir, filename, projectId, deploymentId, vis
|
|
|
1672
1780
|
}
|
|
1673
1781
|
}
|
|
1674
1782
|
}
|
|
1783
|
+
// Process routes from chained initialization expressions
|
|
1784
|
+
// e.g., const router = createRouter().get('/foo', handler).post('/bar', handler)
|
|
1785
|
+
if (chainedInitExpr) {
|
|
1786
|
+
const chainedCalls = flattenChainedCalls(chainedInitExpr);
|
|
1787
|
+
for (const chainedCall of chainedCalls) {
|
|
1788
|
+
const { method: chainMethod, arguments: chainArgs } = chainedCall;
|
|
1789
|
+
// Skip non-route methods
|
|
1790
|
+
if (chainMethod === 'use' ||
|
|
1791
|
+
chainMethod === 'onError' ||
|
|
1792
|
+
chainMethod === 'notFound' ||
|
|
1793
|
+
chainMethod === 'basePath' ||
|
|
1794
|
+
chainMethod === 'mount') {
|
|
1795
|
+
continue;
|
|
1796
|
+
}
|
|
1797
|
+
// Handle .route() for sub-router mounting (same as the ExpressionStatement case)
|
|
1798
|
+
if (chainMethod === 'route') {
|
|
1799
|
+
const mountPathArg = chainArgs[0];
|
|
1800
|
+
const subRouterArg = chainArgs[1];
|
|
1801
|
+
if (mountPathArg &&
|
|
1802
|
+
mountPathArg.type === 'Literal' &&
|
|
1803
|
+
subRouterArg &&
|
|
1804
|
+
subRouterArg.type === 'Identifier') {
|
|
1805
|
+
const mountPath = String(mountPathArg.value);
|
|
1806
|
+
const subRouterName = subRouterArg.name;
|
|
1807
|
+
const subRouterImportPath = importMap.get(subRouterName);
|
|
1808
|
+
if (subRouterImportPath) {
|
|
1809
|
+
const resolvedFile = resolveImportPath(dirname(filename), subRouterImportPath);
|
|
1810
|
+
if (resolvedFile && !visited.has(resolve(resolvedFile))) {
|
|
1811
|
+
try {
|
|
1812
|
+
const combinedBase = joinMountAndRoute(basePath, mountPath);
|
|
1813
|
+
const subRoutes = await parseRoute(rootDir, resolvedFile, projectId, deploymentId, {
|
|
1814
|
+
visitedFiles: visited,
|
|
1815
|
+
mountedSubrouters: options.mountedSubrouters,
|
|
1816
|
+
mountPrefix: combinedBase,
|
|
1817
|
+
});
|
|
1818
|
+
if (options.mountedSubrouters) {
|
|
1819
|
+
options.mountedSubrouters.add(resolve(resolvedFile));
|
|
1820
|
+
}
|
|
1821
|
+
for (const subRoute of subRoutes) {
|
|
1822
|
+
const fullPath = subRoute.path;
|
|
1823
|
+
const id = generateRouteId(projectId, deploymentId, subRoute.type, subRoute.method, rel, fullPath, subRoute.version);
|
|
1824
|
+
const config = { ...subRoute.config };
|
|
1825
|
+
if (subRoute.filename && subRoute.filename !== rel) {
|
|
1826
|
+
config.schemaSourceFile = subRoute.filename;
|
|
1827
|
+
}
|
|
1828
|
+
routes.push({
|
|
1829
|
+
...subRoute,
|
|
1830
|
+
id,
|
|
1831
|
+
path: fullPath,
|
|
1832
|
+
filename: rel,
|
|
1833
|
+
config: Object.keys(config).length > 0 ? config : undefined,
|
|
1834
|
+
});
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1837
|
+
catch {
|
|
1838
|
+
// Sub-router parse failure — skip
|
|
1839
|
+
}
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
continue;
|
|
1844
|
+
}
|
|
1845
|
+
// Handle HTTP methods: get, post, put, patch, delete
|
|
1846
|
+
const CHAINED_HTTP_METHODS = ['get', 'post', 'put', 'delete', 'patch'];
|
|
1847
|
+
if (CHAINED_HTTP_METHODS.includes(chainMethod.toLowerCase())) {
|
|
1848
|
+
const pathArg = chainArgs[0];
|
|
1849
|
+
if (!pathArg || pathArg.type !== 'Literal')
|
|
1850
|
+
continue;
|
|
1851
|
+
const suffix = String(pathArg.value);
|
|
1852
|
+
let type = 'api';
|
|
1853
|
+
// Check for websocket/sse/stream wrappers in handler args
|
|
1854
|
+
for (const arg of chainArgs.slice(1)) {
|
|
1855
|
+
if (arg?.type === 'CallExpression') {
|
|
1856
|
+
const callExpr = arg;
|
|
1857
|
+
if (callExpr.callee?.type === 'Identifier') {
|
|
1858
|
+
const calleeName = callExpr.callee.name;
|
|
1859
|
+
if (calleeName === 'websocket' ||
|
|
1860
|
+
calleeName === 'sse' ||
|
|
1861
|
+
calleeName === 'stream') {
|
|
1862
|
+
type = calleeName;
|
|
1863
|
+
break;
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
const thepath = joinMountAndRoute(basePath, suffix);
|
|
1869
|
+
const id = generateRouteId(projectId, deploymentId, type, chainMethod, rel, thepath, version);
|
|
1870
|
+
// Check for validators in chained args
|
|
1871
|
+
const validatorInfo = hasValidatorCall(chainArgs);
|
|
1872
|
+
const routeConfig = {};
|
|
1873
|
+
if (validatorInfo.hasValidator) {
|
|
1874
|
+
routeConfig.hasValidator = true;
|
|
1875
|
+
if (validatorInfo.agentVariable) {
|
|
1876
|
+
routeConfig.agentVariable = validatorInfo.agentVariable;
|
|
1877
|
+
const agentImportPath = importMap.get(validatorInfo.agentVariable);
|
|
1878
|
+
if (agentImportPath)
|
|
1879
|
+
routeConfig.agentImportPath = agentImportPath;
|
|
1880
|
+
}
|
|
1881
|
+
if (validatorInfo.inputSchemaVariable) {
|
|
1882
|
+
routeConfig.inputSchemaVariable = validatorInfo.inputSchemaVariable;
|
|
1883
|
+
const inputImportInfo = importInfoMap.get(validatorInfo.inputSchemaVariable);
|
|
1884
|
+
if (inputImportInfo) {
|
|
1885
|
+
routeConfig.inputSchemaImportPath = inputImportInfo.modulePath;
|
|
1886
|
+
routeConfig.inputSchemaImportedName = inputImportInfo.importedName;
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
if (validatorInfo.outputSchemaVariable) {
|
|
1890
|
+
routeConfig.outputSchemaVariable = validatorInfo.outputSchemaVariable;
|
|
1891
|
+
const outputImportInfo = importInfoMap.get(validatorInfo.outputSchemaVariable);
|
|
1892
|
+
if (outputImportInfo) {
|
|
1893
|
+
routeConfig.outputSchemaImportPath = outputImportInfo.modulePath;
|
|
1894
|
+
routeConfig.outputSchemaImportedName = outputImportInfo.importedName;
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
if (validatorInfo.stream !== undefined)
|
|
1898
|
+
routeConfig.stream = validatorInfo.stream;
|
|
1899
|
+
}
|
|
1900
|
+
// Fall back to exported schemas
|
|
1901
|
+
if (!routeConfig.inputSchemaVariable && exportedInputSchemaName) {
|
|
1902
|
+
routeConfig.inputSchemaVariable = exportedInputSchemaName;
|
|
1903
|
+
}
|
|
1904
|
+
if (!routeConfig.outputSchemaVariable && exportedOutputSchemaName) {
|
|
1905
|
+
routeConfig.outputSchemaVariable = exportedOutputSchemaName;
|
|
1906
|
+
}
|
|
1907
|
+
routes.push({
|
|
1908
|
+
id,
|
|
1909
|
+
method: chainMethod,
|
|
1910
|
+
type: type,
|
|
1911
|
+
filename: rel,
|
|
1912
|
+
path: thepath,
|
|
1913
|
+
version,
|
|
1914
|
+
config: Object.keys(routeConfig).length > 0 ? routeConfig : undefined,
|
|
1915
|
+
});
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1675
1919
|
}
|
|
1676
1920
|
catch (error) {
|
|
1677
1921
|
if (error instanceof InvalidRouterConfigError || error instanceof SchemaNotExportedError) {
|