@igniter-js/cli 0.2.5 → 0.2.6
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 +506 -455
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +501 -451
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -5165,7 +5165,7 @@ var require_Command = __commonJS({
|
|
|
5165
5165
|
}
|
|
5166
5166
|
}
|
|
5167
5167
|
initPromise() {
|
|
5168
|
-
const promise = new Promise((
|
|
5168
|
+
const promise = new Promise((resolve5, reject) => {
|
|
5169
5169
|
if (!this.transformed) {
|
|
5170
5170
|
this.transformed = true;
|
|
5171
5171
|
const transformer = _Command._transformer.argument[this.name];
|
|
@@ -5174,7 +5174,7 @@ var require_Command = __commonJS({
|
|
|
5174
5174
|
}
|
|
5175
5175
|
this.stringifyArguments();
|
|
5176
5176
|
}
|
|
5177
|
-
this.resolve = this._convertValue(
|
|
5177
|
+
this.resolve = this._convertValue(resolve5);
|
|
5178
5178
|
if (this.errorStack) {
|
|
5179
5179
|
this.reject = (err) => {
|
|
5180
5180
|
reject((0, utils_1.optimizeErrorStack)(err, this.errorStack.stack, __dirname));
|
|
@@ -5204,7 +5204,7 @@ var require_Command = __commonJS({
|
|
|
5204
5204
|
/**
|
|
5205
5205
|
* Convert the value from buffer to the target encoding.
|
|
5206
5206
|
*/
|
|
5207
|
-
_convertValue(
|
|
5207
|
+
_convertValue(resolve5) {
|
|
5208
5208
|
return (value) => {
|
|
5209
5209
|
try {
|
|
5210
5210
|
const existingTimer = this._commandTimeoutTimer;
|
|
@@ -5212,7 +5212,7 @@ var require_Command = __commonJS({
|
|
|
5212
5212
|
clearTimeout(existingTimer);
|
|
5213
5213
|
delete this._commandTimeoutTimer;
|
|
5214
5214
|
}
|
|
5215
|
-
|
|
5215
|
+
resolve5(this.transformReply(value));
|
|
5216
5216
|
this.isResolved = true;
|
|
5217
5217
|
} catch (err) {
|
|
5218
5218
|
this.reject(err);
|
|
@@ -5467,13 +5467,13 @@ var require_autoPipelining = __commonJS({
|
|
|
5467
5467
|
if (client.isCluster && !client.slots.length) {
|
|
5468
5468
|
if (client.status === "wait")
|
|
5469
5469
|
client.connect().catch(lodash_1.noop);
|
|
5470
|
-
return (0, standard_as_callback_1.default)(new Promise(function(
|
|
5470
|
+
return (0, standard_as_callback_1.default)(new Promise(function(resolve5, reject) {
|
|
5471
5471
|
client.delayUntilReady((err) => {
|
|
5472
5472
|
if (err) {
|
|
5473
5473
|
reject(err);
|
|
5474
5474
|
return;
|
|
5475
5475
|
}
|
|
5476
|
-
executeWithAutoPipelining(client, functionName, commandName, args, null).then(
|
|
5476
|
+
executeWithAutoPipelining(client, functionName, commandName, args, null).then(resolve5, reject);
|
|
5477
5477
|
});
|
|
5478
5478
|
}), callback);
|
|
5479
5479
|
}
|
|
@@ -5490,13 +5490,13 @@ var require_autoPipelining = __commonJS({
|
|
|
5490
5490
|
pipeline[exports.kExec] = true;
|
|
5491
5491
|
setImmediate(executeAutoPipeline, client, slotKey);
|
|
5492
5492
|
}
|
|
5493
|
-
const autoPipelinePromise = new Promise(function(
|
|
5493
|
+
const autoPipelinePromise = new Promise(function(resolve5, reject) {
|
|
5494
5494
|
pipeline[exports.kCallbacks].push(function(err, value) {
|
|
5495
5495
|
if (err) {
|
|
5496
5496
|
reject(err);
|
|
5497
5497
|
return;
|
|
5498
5498
|
}
|
|
5499
|
-
|
|
5499
|
+
resolve5(value);
|
|
5500
5500
|
});
|
|
5501
5501
|
if (functionName === "call") {
|
|
5502
5502
|
args.unshift(commandName);
|
|
@@ -5734,8 +5734,8 @@ var require_Pipeline = __commonJS({
|
|
|
5734
5734
|
this[name] = redis[name];
|
|
5735
5735
|
this[name + "Buffer"] = redis[name + "Buffer"];
|
|
5736
5736
|
});
|
|
5737
|
-
this.promise = new Promise((
|
|
5738
|
-
this.resolve =
|
|
5737
|
+
this.promise = new Promise((resolve5, reject) => {
|
|
5738
|
+
this.resolve = resolve5;
|
|
5739
5739
|
this.reject = reject;
|
|
5740
5740
|
});
|
|
5741
5741
|
const _this = this;
|
|
@@ -6030,13 +6030,13 @@ var require_transaction = __commonJS({
|
|
|
6030
6030
|
if (this.isCluster && !this.redis.slots.length) {
|
|
6031
6031
|
if (this.redis.status === "wait")
|
|
6032
6032
|
this.redis.connect().catch(utils_1.noop);
|
|
6033
|
-
return (0, standard_as_callback_1.default)(new Promise((
|
|
6033
|
+
return (0, standard_as_callback_1.default)(new Promise((resolve5, reject) => {
|
|
6034
6034
|
this.redis.delayUntilReady((err) => {
|
|
6035
6035
|
if (err) {
|
|
6036
6036
|
reject(err);
|
|
6037
6037
|
return;
|
|
6038
6038
|
}
|
|
6039
|
-
this.exec(pipeline).then(
|
|
6039
|
+
this.exec(pipeline).then(resolve5, reject);
|
|
6040
6040
|
});
|
|
6041
6041
|
}), callback);
|
|
6042
6042
|
}
|
|
@@ -7212,7 +7212,7 @@ var require_cluster = __commonJS({
|
|
|
7212
7212
|
* Connect to a cluster
|
|
7213
7213
|
*/
|
|
7214
7214
|
connect() {
|
|
7215
|
-
return new Promise((
|
|
7215
|
+
return new Promise((resolve5, reject) => {
|
|
7216
7216
|
if (this.status === "connecting" || this.status === "connect" || this.status === "ready") {
|
|
7217
7217
|
reject(new Error("Redis is already connecting/connected"));
|
|
7218
7218
|
return;
|
|
@@ -7236,7 +7236,7 @@ var require_cluster = __commonJS({
|
|
|
7236
7236
|
this.retryAttempts = 0;
|
|
7237
7237
|
this.executeOfflineCommands();
|
|
7238
7238
|
this.resetNodesRefreshInterval();
|
|
7239
|
-
|
|
7239
|
+
resolve5();
|
|
7240
7240
|
};
|
|
7241
7241
|
let closeListener = void 0;
|
|
7242
7242
|
const refreshListener = () => {
|
|
@@ -7829,7 +7829,7 @@ var require_cluster = __commonJS({
|
|
|
7829
7829
|
});
|
|
7830
7830
|
}
|
|
7831
7831
|
resolveSrv(hostname) {
|
|
7832
|
-
return new Promise((
|
|
7832
|
+
return new Promise((resolve5, reject) => {
|
|
7833
7833
|
this.options.resolveSrv(hostname, (err, records) => {
|
|
7834
7834
|
if (err) {
|
|
7835
7835
|
return reject(err);
|
|
@@ -7843,7 +7843,7 @@ var require_cluster = __commonJS({
|
|
|
7843
7843
|
if (!group.records.length) {
|
|
7844
7844
|
sortedKeys.shift();
|
|
7845
7845
|
}
|
|
7846
|
-
self.dnsLookup(record.name).then((host) =>
|
|
7846
|
+
self.dnsLookup(record.name).then((host) => resolve5({
|
|
7847
7847
|
host,
|
|
7848
7848
|
port: record.port
|
|
7849
7849
|
}), tryFirstOne);
|
|
@@ -7853,14 +7853,14 @@ var require_cluster = __commonJS({
|
|
|
7853
7853
|
});
|
|
7854
7854
|
}
|
|
7855
7855
|
dnsLookup(hostname) {
|
|
7856
|
-
return new Promise((
|
|
7856
|
+
return new Promise((resolve5, reject) => {
|
|
7857
7857
|
this.options.dnsLookup(hostname, (err, address) => {
|
|
7858
7858
|
if (err) {
|
|
7859
7859
|
debug("failed to resolve hostname %s to IP: %s", hostname, err.message);
|
|
7860
7860
|
reject(err);
|
|
7861
7861
|
} else {
|
|
7862
7862
|
debug("resolved hostname %s to IP %s", hostname, address);
|
|
7863
|
-
|
|
7863
|
+
resolve5(address);
|
|
7864
7864
|
}
|
|
7865
7865
|
});
|
|
7866
7866
|
});
|
|
@@ -7978,7 +7978,7 @@ var require_StandaloneConnector = __commonJS({
|
|
|
7978
7978
|
if (options.tls) {
|
|
7979
7979
|
Object.assign(connectionOptions, options.tls);
|
|
7980
7980
|
}
|
|
7981
|
-
return new Promise((
|
|
7981
|
+
return new Promise((resolve5, reject) => {
|
|
7982
7982
|
process.nextTick(() => {
|
|
7983
7983
|
if (!this.connecting) {
|
|
7984
7984
|
reject(new Error(utils_1.CONNECTION_CLOSED_ERROR_MSG));
|
|
@@ -7997,7 +7997,7 @@ var require_StandaloneConnector = __commonJS({
|
|
|
7997
7997
|
this.stream.once("error", (err) => {
|
|
7998
7998
|
this.firstError = err;
|
|
7999
7999
|
});
|
|
8000
|
-
|
|
8000
|
+
resolve5(this.stream);
|
|
8001
8001
|
});
|
|
8002
8002
|
});
|
|
8003
8003
|
}
|
|
@@ -8156,7 +8156,7 @@ var require_SentinelConnector = __commonJS({
|
|
|
8156
8156
|
const error = new Error(errorMsg);
|
|
8157
8157
|
if (typeof retryDelay === "number") {
|
|
8158
8158
|
eventEmitter("error", error);
|
|
8159
|
-
await new Promise((
|
|
8159
|
+
await new Promise((resolve5) => setTimeout(resolve5, retryDelay));
|
|
8160
8160
|
return connectToNext();
|
|
8161
8161
|
} else {
|
|
8162
8162
|
throw error;
|
|
@@ -9462,7 +9462,7 @@ var require_Redis = __commonJS({
|
|
|
9462
9462
|
* be resolved when the connection status is ready.
|
|
9463
9463
|
*/
|
|
9464
9464
|
connect(callback) {
|
|
9465
|
-
const promise = new Promise((
|
|
9465
|
+
const promise = new Promise((resolve5, reject) => {
|
|
9466
9466
|
if (this.status === "connecting" || this.status === "connect" || this.status === "ready") {
|
|
9467
9467
|
reject(new Error("Redis is already connecting/connected"));
|
|
9468
9468
|
return;
|
|
@@ -9541,7 +9541,7 @@ var require_Redis = __commonJS({
|
|
|
9541
9541
|
}
|
|
9542
9542
|
const connectionReadyHandler = function() {
|
|
9543
9543
|
_this.removeListener("close", connectionCloseHandler);
|
|
9544
|
-
|
|
9544
|
+
resolve5();
|
|
9545
9545
|
};
|
|
9546
9546
|
var connectionCloseHandler = function() {
|
|
9547
9547
|
_this.removeListener("ready", connectionReadyHandler);
|
|
@@ -9635,10 +9635,10 @@ var require_Redis = __commonJS({
|
|
|
9635
9635
|
monitor: true,
|
|
9636
9636
|
lazyConnect: false
|
|
9637
9637
|
});
|
|
9638
|
-
return (0, standard_as_callback_1.default)(new Promise(function(
|
|
9638
|
+
return (0, standard_as_callback_1.default)(new Promise(function(resolve5, reject) {
|
|
9639
9639
|
monitorInstance.once("error", reject);
|
|
9640
9640
|
monitorInstance.once("monitoring", function() {
|
|
9641
|
-
|
|
9641
|
+
resolve5(monitorInstance);
|
|
9642
9642
|
});
|
|
9643
9643
|
}), callback);
|
|
9644
9644
|
}
|
|
@@ -11420,8 +11420,8 @@ ${ANSI_COLORS.bold}TOP OPERATIONS${ANSI_COLORS.reset}`);
|
|
|
11420
11420
|
}, 5e3);
|
|
11421
11421
|
this.startApiMonitoring();
|
|
11422
11422
|
await Promise.all(
|
|
11423
|
-
this.processes.map((proc) => new Promise((
|
|
11424
|
-
proc.on("exit",
|
|
11423
|
+
this.processes.map((proc) => new Promise((resolve5) => {
|
|
11424
|
+
proc.on("exit", resolve5);
|
|
11425
11425
|
}))
|
|
11426
11426
|
);
|
|
11427
11427
|
}
|
|
@@ -11451,54 +11451,259 @@ ${ANSI_COLORS.bold}TOP OPERATIONS${ANSI_COLORS.reset}`);
|
|
|
11451
11451
|
}
|
|
11452
11452
|
});
|
|
11453
11453
|
|
|
11454
|
-
// src/adapters/build/
|
|
11454
|
+
// src/adapters/build/introspector.ts
|
|
11455
|
+
var introspector_exports = {};
|
|
11456
|
+
__export(introspector_exports, {
|
|
11457
|
+
introspectRouter: () => introspectRouter,
|
|
11458
|
+
loadRouter: () => loadRouter
|
|
11459
|
+
});
|
|
11455
11460
|
import * as fs6 from "fs";
|
|
11456
11461
|
import * as path7 from "path";
|
|
11457
|
-
|
|
11458
|
-
|
|
11459
|
-
|
|
11460
|
-
|
|
11461
|
-
|
|
11462
|
-
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}kb`;
|
|
11463
|
-
return `${(bytes / (1024 * 1024)).toFixed(1)}mb`;
|
|
11464
|
-
} catch {
|
|
11465
|
-
return "0b";
|
|
11466
|
-
}
|
|
11467
|
-
}
|
|
11468
|
-
function extractRouterSchema(router) {
|
|
11469
|
-
const controllersSchema = {};
|
|
11462
|
+
import { pathToFileURL } from "url";
|
|
11463
|
+
function introspectRouter(router) {
|
|
11464
|
+
const logger6 = createChildLogger({ component: "router-introspector" });
|
|
11465
|
+
logger6.debug("Starting router introspection");
|
|
11466
|
+
const introspectedControllers = {};
|
|
11470
11467
|
let totalActions = 0;
|
|
11471
11468
|
for (const [controllerName, controller] of Object.entries(router.controllers)) {
|
|
11472
|
-
|
|
11469
|
+
logger6.debug(`Introspecting controller: ${controllerName}`);
|
|
11470
|
+
const introspectedActions = {};
|
|
11473
11471
|
if (controller && controller.actions) {
|
|
11474
11472
|
for (const [actionName, action] of Object.entries(controller.actions)) {
|
|
11475
|
-
|
|
11473
|
+
logger6.debug(`Introspecting action: ${controllerName}.${actionName}`, { path: action?.path, method: action?.method });
|
|
11474
|
+
const bodySchemaOutput = action.bodySchema !== void 0 ? action.bodySchema : void 0;
|
|
11475
|
+
const querySchemaOutput = action.querySchema !== void 0 ? action.querySchema : void 0;
|
|
11476
|
+
introspectedActions[actionName] = {
|
|
11476
11477
|
name: action?.name || actionName,
|
|
11477
11478
|
description: action?.description || "",
|
|
11478
11479
|
path: action?.path || "",
|
|
11479
|
-
method: action?.method || "GET"
|
|
11480
|
+
method: action?.method || "GET",
|
|
11481
|
+
isStream: action?.stream || false,
|
|
11482
|
+
...bodySchemaOutput !== void 0 ? { bodySchema: bodySchemaOutput } : {},
|
|
11483
|
+
...querySchemaOutput !== void 0 ? { querySchema: querySchemaOutput } : {}
|
|
11480
11484
|
};
|
|
11481
11485
|
totalActions++;
|
|
11482
11486
|
}
|
|
11487
|
+
} else {
|
|
11488
|
+
logger6.debug(`Controller ${controllerName} has no actions property or is invalid.`);
|
|
11483
11489
|
}
|
|
11484
|
-
|
|
11490
|
+
introspectedControllers[controllerName] = {
|
|
11485
11491
|
name: controller?.name || controllerName,
|
|
11486
11492
|
description: controller?.description || "",
|
|
11487
11493
|
path: controller?.path || "",
|
|
11488
|
-
actions:
|
|
11494
|
+
actions: introspectedActions
|
|
11489
11495
|
};
|
|
11490
11496
|
}
|
|
11491
|
-
const
|
|
11492
|
-
controllers:
|
|
11497
|
+
const schemaResult = {
|
|
11498
|
+
controllers: introspectedControllers,
|
|
11499
|
+
docs: router?.config?.docs
|
|
11493
11500
|
};
|
|
11494
11501
|
return {
|
|
11495
|
-
schema,
|
|
11502
|
+
schema: schemaResult,
|
|
11496
11503
|
stats: {
|
|
11497
|
-
controllers: Object.keys(
|
|
11504
|
+
controllers: Object.keys(introspectedControllers).length,
|
|
11498
11505
|
actions: totalActions
|
|
11499
11506
|
}
|
|
11500
11507
|
};
|
|
11501
11508
|
}
|
|
11509
|
+
async function loadRouter(routerPath) {
|
|
11510
|
+
const logger6 = createChildLogger({ component: "router-loader" });
|
|
11511
|
+
const fullPath = path7.resolve(process.cwd(), routerPath);
|
|
11512
|
+
logger6.debug("Loading router", { path: routerPath });
|
|
11513
|
+
try {
|
|
11514
|
+
const module = await loadWithTypeScriptSupport(fullPath);
|
|
11515
|
+
if (module) {
|
|
11516
|
+
if (module?.config && module?.controllers) {
|
|
11517
|
+
return module;
|
|
11518
|
+
}
|
|
11519
|
+
const router = module?.AppRouter || module?.default?.AppRouter || module?.default || module?.router;
|
|
11520
|
+
if (router && typeof router === "object") {
|
|
11521
|
+
return router;
|
|
11522
|
+
} else {
|
|
11523
|
+
logger6.debug("Module loaded but no router found", {
|
|
11524
|
+
exports: Object.keys(module || {})
|
|
11525
|
+
});
|
|
11526
|
+
}
|
|
11527
|
+
}
|
|
11528
|
+
const fallbackSpinner = createDetachedSpinner("Trying fallback loading method...");
|
|
11529
|
+
fallbackSpinner.start();
|
|
11530
|
+
const fallbackModule = await loadRouterWithIndexResolution(fullPath);
|
|
11531
|
+
if (fallbackModule) {
|
|
11532
|
+
if (fallbackModule?.config && fallbackModule?.controllers) {
|
|
11533
|
+
fallbackSpinner.success(`Router loaded successfully - ${Object.keys(fallbackModule.controllers).length} controllers`);
|
|
11534
|
+
return fallbackModule;
|
|
11535
|
+
}
|
|
11536
|
+
const router = fallbackModule?.AppRouter || fallbackModule?.default?.AppRouter || fallbackModule?.default || fallbackModule?.router;
|
|
11537
|
+
if (router && typeof router === "object") {
|
|
11538
|
+
fallbackSpinner.success(`Router loaded successfully - ${Object.keys(router.controllers).length} controllers`);
|
|
11539
|
+
return router;
|
|
11540
|
+
}
|
|
11541
|
+
}
|
|
11542
|
+
fallbackSpinner.error("Could not load router");
|
|
11543
|
+
} catch (error) {
|
|
11544
|
+
logger6.error("Failed to load router", { path: routerPath }, error);
|
|
11545
|
+
}
|
|
11546
|
+
return null;
|
|
11547
|
+
}
|
|
11548
|
+
async function loadWithTypeScriptSupport(filePath) {
|
|
11549
|
+
const logger6 = createChildLogger({ component: "tsx-loader" });
|
|
11550
|
+
const jsPath = filePath.replace(/\.ts$/, ".js");
|
|
11551
|
+
if (fs6.existsSync(jsPath)) {
|
|
11552
|
+
try {
|
|
11553
|
+
logger6.debug("Using compiled JS version");
|
|
11554
|
+
delete __require.cache[jsPath];
|
|
11555
|
+
return __require(jsPath);
|
|
11556
|
+
} catch (error) {
|
|
11557
|
+
logger6.debug("Compiled JS loading failed, trying TypeScript...");
|
|
11558
|
+
}
|
|
11559
|
+
}
|
|
11560
|
+
if (filePath.endsWith(".ts")) {
|
|
11561
|
+
try {
|
|
11562
|
+
logger6.debug("Using TSX runtime loader");
|
|
11563
|
+
const { spawn: spawn2 } = __require("child_process");
|
|
11564
|
+
const tsxCheckResult = await new Promise((resolve5) => {
|
|
11565
|
+
const checkChild = spawn2("npx", ["tsx", "--version"], {
|
|
11566
|
+
stdio: "pipe",
|
|
11567
|
+
cwd: process.cwd()
|
|
11568
|
+
});
|
|
11569
|
+
checkChild.on("close", (code) => resolve5(code === 0));
|
|
11570
|
+
checkChild.on("error", () => resolve5(false));
|
|
11571
|
+
setTimeout(() => {
|
|
11572
|
+
checkChild.kill();
|
|
11573
|
+
resolve5(false);
|
|
11574
|
+
}, 5e3);
|
|
11575
|
+
});
|
|
11576
|
+
if (!tsxCheckResult) {
|
|
11577
|
+
throw new Error("TSX not available");
|
|
11578
|
+
}
|
|
11579
|
+
return await new Promise((resolve5, reject) => {
|
|
11580
|
+
const tsxScript = `
|
|
11581
|
+
import { zodToJsonSchema } from 'zod-to-json-schema'; // Precisamos importar isso no script filho
|
|
11582
|
+
|
|
11583
|
+
async function loadRouter() {
|
|
11584
|
+
try {
|
|
11585
|
+
const module = await import('${pathToFileURL(filePath).href}');
|
|
11586
|
+
const router = module?.AppRouter || module?.default?.AppRouter || module?.default || module?.router;
|
|
11587
|
+
|
|
11588
|
+
if (router && typeof router === 'object') {
|
|
11589
|
+
const safeRouter = {
|
|
11590
|
+
config: {
|
|
11591
|
+
baseURL: router.config?.baseURL || '',
|
|
11592
|
+
basePATH: router.config?.basePATH || '',
|
|
11593
|
+
docs: router.config?.docs || undefined,
|
|
11594
|
+
},
|
|
11595
|
+
controllers: {}
|
|
11596
|
+
};
|
|
11597
|
+
|
|
11598
|
+
if (router.controllers && typeof router.controllers === 'object') {
|
|
11599
|
+
for (const [controllerName, controller] of Object.entries(router.controllers)) {
|
|
11600
|
+
if (controller && typeof controller === 'object' && (controller as any).actions) {
|
|
11601
|
+
const safeActions = {};
|
|
11602
|
+
for (const [actionName, action] of Object.entries((controller as any).actions)) {
|
|
11603
|
+
if (action && typeof action === 'object') {
|
|
11604
|
+
// MODIFICA\xC7\xC3O PRINCIPAL AQUI
|
|
11605
|
+
safeActions[actionName] = {
|
|
11606
|
+
name: (action as any).name,
|
|
11607
|
+
path: (action as any).path,
|
|
11608
|
+
method: (action as any).method,
|
|
11609
|
+
description: (action as any).description,
|
|
11610
|
+
// Convertemos o schema Zod para JSON Schema AQUI, antes do JSON.stringify
|
|
11611
|
+
bodySchema: (action as any).body ? zodToJsonSchema((action as any).body, { target: 'openApi3' }) : undefined,
|
|
11612
|
+
querySchema: (action as any).query ? zodToJsonSchema((action as any).query, { target: 'openApi3' }) : undefined,
|
|
11613
|
+
use: (action as any).use,
|
|
11614
|
+
stream: (action as any).stream,
|
|
11615
|
+
};
|
|
11616
|
+
}
|
|
11617
|
+
}
|
|
11618
|
+
safeRouter.controllers[controllerName] = {
|
|
11619
|
+
name: (controller as any).name || controllerName,
|
|
11620
|
+
path: (controller as any).path || '',
|
|
11621
|
+
description: (controller as any).description,
|
|
11622
|
+
actions: safeActions,
|
|
11623
|
+
};
|
|
11624
|
+
}
|
|
11625
|
+
}
|
|
11626
|
+
}
|
|
11627
|
+
console.log('__ROUTER_SUCCESS__' + JSON.stringify(safeRouter));
|
|
11628
|
+
process.exit(0);
|
|
11629
|
+
} else {
|
|
11630
|
+
console.log('__ROUTER_ERROR__No router found in module');
|
|
11631
|
+
process.exit(1);
|
|
11632
|
+
}
|
|
11633
|
+
} catch (error) {
|
|
11634
|
+
console.log('__ROUTER_ERROR__' + error.message);
|
|
11635
|
+
process.exit(1);
|
|
11636
|
+
}
|
|
11637
|
+
}
|
|
11638
|
+
loadRouter();
|
|
11639
|
+
`;
|
|
11640
|
+
const child = spawn2("npx", ["tsx", "-e", tsxScript], {
|
|
11641
|
+
stdio: "pipe",
|
|
11642
|
+
cwd: process.cwd(),
|
|
11643
|
+
env: { ...process.env, NODE_NO_WARNINGS: "1" }
|
|
11644
|
+
});
|
|
11645
|
+
let output = "";
|
|
11646
|
+
let errorOutput = "";
|
|
11647
|
+
child.stdout?.on("data", (data) => output += data.toString());
|
|
11648
|
+
child.stderr?.on("data", (data) => errorOutput += data.toString());
|
|
11649
|
+
child.on("close", (code) => {
|
|
11650
|
+
if (output.includes("__ROUTER_SUCCESS__")) {
|
|
11651
|
+
const resultLine = output.split("\n").find((line) => line.includes("__ROUTER_SUCCESS__"));
|
|
11652
|
+
if (resultLine) {
|
|
11653
|
+
try {
|
|
11654
|
+
const routerData = JSON.parse(resultLine.replace("__ROUTER_SUCCESS__", ""));
|
|
11655
|
+
resolve5(routerData);
|
|
11656
|
+
} catch (e) {
|
|
11657
|
+
reject(new Error("Failed to parse router data: " + e.message));
|
|
11658
|
+
}
|
|
11659
|
+
} else {
|
|
11660
|
+
reject(new Error("Router success marker found but no data"));
|
|
11661
|
+
}
|
|
11662
|
+
} else if (output.includes("__ROUTER_ERROR__")) {
|
|
11663
|
+
const errorLine = output.split("\n").find((line) => line.includes("__ROUTER_ERROR__"));
|
|
11664
|
+
const errorMsg = errorLine ? errorLine.replace("__ROUTER_ERROR__", "") : "Unknown error";
|
|
11665
|
+
reject(new Error("Router loading failed: " + errorMsg));
|
|
11666
|
+
} else {
|
|
11667
|
+
reject(new Error(`TSX execution failed with code ${code}: ${errorOutput || "No output"}`));
|
|
11668
|
+
}
|
|
11669
|
+
});
|
|
11670
|
+
child.on("error", (error) => reject(new Error("Failed to spawn TSX process: " + error.message)));
|
|
11671
|
+
setTimeout(() => {
|
|
11672
|
+
child.kill();
|
|
11673
|
+
reject(new Error("Timeout loading TypeScript file with TSX"));
|
|
11674
|
+
}, 3e4);
|
|
11675
|
+
});
|
|
11676
|
+
} catch (error) {
|
|
11677
|
+
logger6.debug("TSX runtime loader failed", {}, error);
|
|
11678
|
+
}
|
|
11679
|
+
}
|
|
11680
|
+
return null;
|
|
11681
|
+
}
|
|
11682
|
+
async function loadRouterWithIndexResolution(routerPath) {
|
|
11683
|
+
return null;
|
|
11684
|
+
}
|
|
11685
|
+
var init_introspector = __esm({
|
|
11686
|
+
"src/adapters/build/introspector.ts"() {
|
|
11687
|
+
"use strict";
|
|
11688
|
+
init_logger();
|
|
11689
|
+
init_spinner();
|
|
11690
|
+
}
|
|
11691
|
+
});
|
|
11692
|
+
|
|
11693
|
+
// src/adapters/build/generator.ts
|
|
11694
|
+
import * as fs7 from "fs";
|
|
11695
|
+
import * as path8 from "path";
|
|
11696
|
+
function getFileSize(filePath) {
|
|
11697
|
+
try {
|
|
11698
|
+
const stats = fs7.statSync(filePath);
|
|
11699
|
+
const bytes = stats.size;
|
|
11700
|
+
if (bytes < 1024) return `${bytes}b`;
|
|
11701
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}kb`;
|
|
11702
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)}mb`;
|
|
11703
|
+
} catch {
|
|
11704
|
+
return "0b";
|
|
11705
|
+
}
|
|
11706
|
+
}
|
|
11502
11707
|
async function generateSchemaFromRouter(router, config) {
|
|
11503
11708
|
const logger6 = createChildLogger({ component: "generator" });
|
|
11504
11709
|
const startTime = performance.now();
|
|
@@ -11511,7 +11716,7 @@ async function generateSchemaFromRouter(router, config) {
|
|
|
11511
11716
|
extractSpinner = createDetachedSpinner("Extracting router schema...");
|
|
11512
11717
|
extractSpinner.start();
|
|
11513
11718
|
}
|
|
11514
|
-
const { schema, stats } =
|
|
11719
|
+
const { schema, stats } = introspectRouter(router);
|
|
11515
11720
|
if (isInteractiveMode2) {
|
|
11516
11721
|
logger6.success(`Schema extracted - ${stats.controllers} controllers, ${stats.actions} actions`);
|
|
11517
11722
|
} else if (extractSpinner) {
|
|
@@ -11526,7 +11731,7 @@ async function generateSchemaFromRouter(router, config) {
|
|
|
11526
11731
|
}
|
|
11527
11732
|
const outputDir = config.outputDir || "generated";
|
|
11528
11733
|
await ensureDirectoryExists(outputDir);
|
|
11529
|
-
const outputPath =
|
|
11734
|
+
const outputPath = path8.resolve(outputDir);
|
|
11530
11735
|
if (isInteractiveMode2) {
|
|
11531
11736
|
logger6.success(`Output directory ready ${outputPath}`);
|
|
11532
11737
|
} else if (dirSpinner) {
|
|
@@ -11556,8 +11761,8 @@ async function generateSchemaFromRouter(router, config) {
|
|
|
11556
11761
|
let totalSize = 0;
|
|
11557
11762
|
files.forEach((file) => {
|
|
11558
11763
|
const size = getFileSize(file.path);
|
|
11559
|
-
totalSize +=
|
|
11560
|
-
logger6.info(`Generated ${file.name}`, { size, path:
|
|
11764
|
+
totalSize += fs7.statSync(file.path).size;
|
|
11765
|
+
logger6.info(`Generated ${file.name}`, { size, path: path8.relative(process.cwd(), file.path) });
|
|
11561
11766
|
});
|
|
11562
11767
|
const duration = ((performance.now() - startTime) / 1e3).toFixed(2);
|
|
11563
11768
|
const totalSizeFormatted = totalSize < 1024 ? `${totalSize}b` : totalSize < 1024 * 1024 ? `${(totalSize / 1024).toFixed(1)}kb` : `${(totalSize / (1024 * 1024)).toFixed(1)}mb`;
|
|
@@ -11590,13 +11795,13 @@ export const AppRouterSchema = ${JSON.stringify(schema, null, 2)} as const
|
|
|
11590
11795
|
|
|
11591
11796
|
export type AppRouterSchemaType = typeof AppRouterSchema
|
|
11592
11797
|
`;
|
|
11593
|
-
const filePath =
|
|
11798
|
+
const filePath = path8.join(outputDir, "igniter.schema.ts");
|
|
11594
11799
|
await writeFileWithHeader(filePath, content, config);
|
|
11595
11800
|
return filePath;
|
|
11596
11801
|
}
|
|
11597
11802
|
async function generateClientFile(schema, outputDir, config) {
|
|
11598
|
-
const filePath =
|
|
11599
|
-
if (
|
|
11803
|
+
const filePath = path8.join(outputDir, "igniter.client.ts");
|
|
11804
|
+
if (fs7.existsSync(filePath)) {
|
|
11600
11805
|
const logger6 = createChildLogger({ component: "generator" });
|
|
11601
11806
|
logger6.info("Skipping client file generation, already exists", { path: filePath });
|
|
11602
11807
|
return filePath;
|
|
@@ -11656,7 +11861,7 @@ export const useQueryClient = useIgniterQueryClient<AppRouterType>;
|
|
|
11656
11861
|
async function writeFileWithHeader(filePath, content, config) {
|
|
11657
11862
|
const header = generateFileHeader(config);
|
|
11658
11863
|
const fullContent = header + "\n\n" + content;
|
|
11659
|
-
await
|
|
11864
|
+
await fs7.promises.writeFile(filePath, fullContent, "utf8");
|
|
11660
11865
|
}
|
|
11661
11866
|
function generateFileHeader(config) {
|
|
11662
11867
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -11680,9 +11885,9 @@ function generateFileHeader(config) {
|
|
|
11680
11885
|
}
|
|
11681
11886
|
async function ensureDirectoryExists(dirPath) {
|
|
11682
11887
|
try {
|
|
11683
|
-
await
|
|
11888
|
+
await fs7.promises.access(dirPath);
|
|
11684
11889
|
} catch (error) {
|
|
11685
|
-
await
|
|
11890
|
+
await fs7.promises.mkdir(dirPath, { recursive: true });
|
|
11686
11891
|
}
|
|
11687
11892
|
}
|
|
11688
11893
|
var init_generator = __esm({
|
|
@@ -11690,6 +11895,133 @@ var init_generator = __esm({
|
|
|
11690
11895
|
"use strict";
|
|
11691
11896
|
init_logger();
|
|
11692
11897
|
init_spinner();
|
|
11898
|
+
init_introspector();
|
|
11899
|
+
}
|
|
11900
|
+
});
|
|
11901
|
+
|
|
11902
|
+
// src/adapters/docs/openapi-generator.ts
|
|
11903
|
+
var openapi_generator_exports = {};
|
|
11904
|
+
__export(openapi_generator_exports, {
|
|
11905
|
+
OpenAPIGenerator: () => OpenAPIGenerator
|
|
11906
|
+
});
|
|
11907
|
+
function toPascalCase2(str) {
|
|
11908
|
+
if (!str) return "";
|
|
11909
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
11910
|
+
}
|
|
11911
|
+
var OpenAPIGenerator;
|
|
11912
|
+
var init_openapi_generator = __esm({
|
|
11913
|
+
"src/adapters/docs/openapi-generator.ts"() {
|
|
11914
|
+
"use strict";
|
|
11915
|
+
OpenAPIGenerator = class {
|
|
11916
|
+
constructor(config) {
|
|
11917
|
+
this.schemas = {};
|
|
11918
|
+
this.docsConfig = config || {};
|
|
11919
|
+
}
|
|
11920
|
+
generate(router) {
|
|
11921
|
+
const servers = this.docsConfig.servers && this.docsConfig.servers.length > 0 ? this.docsConfig.servers : [{ url: "http://localhost:3000/api/v1", description: "Default server" }];
|
|
11922
|
+
const spec = {
|
|
11923
|
+
openapi: "3.0.0",
|
|
11924
|
+
info: this.docsConfig.info || { title: "Igniter API", version: "1.0.0" },
|
|
11925
|
+
servers,
|
|
11926
|
+
tags: this.buildTags(router),
|
|
11927
|
+
paths: this.buildPaths(router),
|
|
11928
|
+
components: {
|
|
11929
|
+
schemas: this.schemas,
|
|
11930
|
+
securitySchemes: this.docsConfig.securitySchemes || {}
|
|
11931
|
+
}
|
|
11932
|
+
};
|
|
11933
|
+
return spec;
|
|
11934
|
+
}
|
|
11935
|
+
buildTags(router) {
|
|
11936
|
+
const tags = [];
|
|
11937
|
+
for (const [controllerKey, controller] of Object.entries(router.controllers)) {
|
|
11938
|
+
const tag = {
|
|
11939
|
+
name: controller.name || controllerKey,
|
|
11940
|
+
description: controller.description
|
|
11941
|
+
};
|
|
11942
|
+
tags.push(tag);
|
|
11943
|
+
}
|
|
11944
|
+
return tags;
|
|
11945
|
+
}
|
|
11946
|
+
buildPaths(router) {
|
|
11947
|
+
const paths = {};
|
|
11948
|
+
for (const [controllerKey, controller] of Object.entries(router.controllers)) {
|
|
11949
|
+
for (const [actionKey, action] of Object.entries(controller.actions)) {
|
|
11950
|
+
const actionName = action.name || actionKey;
|
|
11951
|
+
let path11 = `/${controller.path}/${action.path}`;
|
|
11952
|
+
path11 = path11.replace(/\/{2,}/g, "/");
|
|
11953
|
+
if (path11.length > 1 && path11.endsWith("/")) {
|
|
11954
|
+
path11 = path11.slice(0, -1);
|
|
11955
|
+
}
|
|
11956
|
+
if (!paths[path11]) {
|
|
11957
|
+
paths[path11] = {};
|
|
11958
|
+
}
|
|
11959
|
+
const operation = {
|
|
11960
|
+
summary: action.description || actionName,
|
|
11961
|
+
operationId: actionName,
|
|
11962
|
+
tags: [controller.name || controllerKey],
|
|
11963
|
+
parameters: [],
|
|
11964
|
+
requestBody: void 0,
|
|
11965
|
+
responses: {
|
|
11966
|
+
"200": {
|
|
11967
|
+
description: "Success",
|
|
11968
|
+
content: {
|
|
11969
|
+
"application/json": {
|
|
11970
|
+
schema: {}
|
|
11971
|
+
}
|
|
11972
|
+
}
|
|
11973
|
+
}
|
|
11974
|
+
}
|
|
11975
|
+
};
|
|
11976
|
+
const pathParams = action.path.match(/:([a-zA-Z0-9_]+)/g);
|
|
11977
|
+
if (pathParams) {
|
|
11978
|
+
for (const param of pathParams) {
|
|
11979
|
+
const paramName = param.substring(1);
|
|
11980
|
+
operation.parameters.push({
|
|
11981
|
+
name: paramName,
|
|
11982
|
+
in: "path",
|
|
11983
|
+
required: true,
|
|
11984
|
+
schema: { type: "string" }
|
|
11985
|
+
});
|
|
11986
|
+
}
|
|
11987
|
+
}
|
|
11988
|
+
if (action.querySchema) {
|
|
11989
|
+
const querySchemaName = `${toPascalCase2(controller.name || controllerKey)}${toPascalCase2(actionName)}Query`;
|
|
11990
|
+
const queryJsonSchema = action.querySchema;
|
|
11991
|
+
this.schemas[querySchemaName] = queryJsonSchema;
|
|
11992
|
+
const properties = this.schemas[querySchemaName].properties || {};
|
|
11993
|
+
const requiredProps = this.schemas[querySchemaName].required || [];
|
|
11994
|
+
for (const propName of Object.keys(properties)) {
|
|
11995
|
+
operation.parameters.push({
|
|
11996
|
+
name: propName,
|
|
11997
|
+
in: "query",
|
|
11998
|
+
required: requiredProps.includes(propName),
|
|
11999
|
+
schema: properties[propName]
|
|
12000
|
+
});
|
|
12001
|
+
}
|
|
12002
|
+
}
|
|
12003
|
+
if (action.bodySchema) {
|
|
12004
|
+
const bodySchemaName = `${toPascalCase2(controller.name || controllerKey)}${toPascalCase2(actionName)}Body`;
|
|
12005
|
+
const bodyJsonSchema = action.bodySchema;
|
|
12006
|
+
this.schemas[bodySchemaName] = bodyJsonSchema;
|
|
12007
|
+
operation.requestBody = {
|
|
12008
|
+
required: true,
|
|
12009
|
+
content: {
|
|
12010
|
+
"application/json": {
|
|
12011
|
+
schema: { $ref: `#/components/schemas/${bodySchemaName}` }
|
|
12012
|
+
}
|
|
12013
|
+
}
|
|
12014
|
+
};
|
|
12015
|
+
}
|
|
12016
|
+
if (action.isStream) {
|
|
12017
|
+
operation.description = (operation.description ? operation.description + "\n\n" : "") + "This endpoint supports Server-Sent Events (SSE) for real-time updates. It functions as a standard GET request initially, then maintains an open connection for streaming data.";
|
|
12018
|
+
}
|
|
12019
|
+
paths[path11][action.method.toLowerCase()] = operation;
|
|
12020
|
+
}
|
|
12021
|
+
}
|
|
12022
|
+
return paths;
|
|
12023
|
+
}
|
|
12024
|
+
};
|
|
11693
12025
|
}
|
|
11694
12026
|
});
|
|
11695
12027
|
|
|
@@ -11698,17 +12030,18 @@ var watcher_exports = {};
|
|
|
11698
12030
|
__export(watcher_exports, {
|
|
11699
12031
|
IgniterWatcher: () => IgniterWatcher
|
|
11700
12032
|
});
|
|
11701
|
-
import * as
|
|
11702
|
-
import * as
|
|
11703
|
-
import { pathToFileURL } from "url";
|
|
12033
|
+
import * as fs8 from "fs";
|
|
12034
|
+
import * as path9 from "path";
|
|
11704
12035
|
import chokidar from "chokidar";
|
|
11705
12036
|
var IgniterWatcher;
|
|
11706
12037
|
var init_watcher = __esm({
|
|
11707
12038
|
"src/adapters/build/watcher.ts"() {
|
|
11708
12039
|
"use strict";
|
|
12040
|
+
init_introspector();
|
|
11709
12041
|
init_generator();
|
|
11710
12042
|
init_logger();
|
|
11711
12043
|
init_spinner();
|
|
12044
|
+
init_openapi_generator();
|
|
11712
12045
|
IgniterWatcher = class {
|
|
11713
12046
|
// Detect interactive mode
|
|
11714
12047
|
constructor(config) {
|
|
@@ -11728,6 +12061,8 @@ var init_watcher = __esm({
|
|
|
11728
12061
|
hotReload: true,
|
|
11729
12062
|
controllerPatterns: ["**/*.controller.{ts,js}"],
|
|
11730
12063
|
debug: false,
|
|
12064
|
+
generateDocs: false,
|
|
12065
|
+
docsOutputDir: "./src/docs",
|
|
11731
12066
|
...config
|
|
11732
12067
|
};
|
|
11733
12068
|
this.isInteractiveMode = !!(process.env.IGNITER_INTERACTIVE_MODE === "true" || process.argv.includes("--interactive"));
|
|
@@ -11789,380 +12124,6 @@ var init_watcher = __esm({
|
|
|
11789
12124
|
process.stdout.write("\n");
|
|
11790
12125
|
}
|
|
11791
12126
|
}
|
|
11792
|
-
/**
|
|
11793
|
-
* Load router from file with simplified approach
|
|
11794
|
-
*/
|
|
11795
|
-
async loadRouter(routerPath) {
|
|
11796
|
-
const logger6 = createChildLogger({ component: "router-loader" });
|
|
11797
|
-
const fullPath = path8.resolve(process.cwd(), routerPath);
|
|
11798
|
-
logger6.debug("Loading router", { path: routerPath });
|
|
11799
|
-
try {
|
|
11800
|
-
const module = await this.loadWithTypeScriptSupport(fullPath);
|
|
11801
|
-
if (module) {
|
|
11802
|
-
if (module?.config && module?.controllers) {
|
|
11803
|
-
const controllerCount = Object.keys(module.controllers || {}).length;
|
|
11804
|
-
return module;
|
|
11805
|
-
}
|
|
11806
|
-
const router = module?.AppRouter || module?.default?.AppRouter || module?.default || module?.router;
|
|
11807
|
-
if (router && typeof router === "object") {
|
|
11808
|
-
const controllerCount = Object.keys(router.controllers || {}).length;
|
|
11809
|
-
return router;
|
|
11810
|
-
} else {
|
|
11811
|
-
logger6.debug("Available exports", {
|
|
11812
|
-
exports: Object.keys(module || {})
|
|
11813
|
-
});
|
|
11814
|
-
}
|
|
11815
|
-
} else {
|
|
11816
|
-
}
|
|
11817
|
-
const fallbackSpinner = createDetachedSpinner("Trying fallback loading method...");
|
|
11818
|
-
fallbackSpinner.start();
|
|
11819
|
-
const fallbackModule = await this.loadRouterWithIndexResolution(fullPath);
|
|
11820
|
-
if (fallbackModule) {
|
|
11821
|
-
if (fallbackModule?.config && fallbackModule?.controllers) {
|
|
11822
|
-
const controllerCount = Object.keys(fallbackModule.controllers || {}).length;
|
|
11823
|
-
fallbackSpinner.success(`Router loaded successfully - ${controllerCount} controllers`);
|
|
11824
|
-
return fallbackModule;
|
|
11825
|
-
}
|
|
11826
|
-
const router = fallbackModule?.AppRouter || fallbackModule?.default?.AppRouter || fallbackModule?.default || fallbackModule?.router;
|
|
11827
|
-
if (router && typeof router === "object") {
|
|
11828
|
-
const controllerCount = Object.keys(router.controllers || {}).length;
|
|
11829
|
-
fallbackSpinner.success(`Router loaded successfully - ${controllerCount} controllers`);
|
|
11830
|
-
return router;
|
|
11831
|
-
}
|
|
11832
|
-
}
|
|
11833
|
-
fallbackSpinner.error("Could not load router");
|
|
11834
|
-
} catch (error) {
|
|
11835
|
-
logger6.error("Failed to load router", { path: routerPath }, error);
|
|
11836
|
-
}
|
|
11837
|
-
return null;
|
|
11838
|
-
}
|
|
11839
|
-
/**
|
|
11840
|
-
* Load TypeScript files using TSX runtime loader
|
|
11841
|
-
* This is the NEW robust approach that replaces the problematic transpilation
|
|
11842
|
-
*/
|
|
11843
|
-
async loadWithTypeScriptSupport(filePath) {
|
|
11844
|
-
const logger6 = createChildLogger({ component: "tsx-loader" });
|
|
11845
|
-
const jsPath = filePath.replace(/\.ts$/, ".js");
|
|
11846
|
-
if (fs7.existsSync(jsPath)) {
|
|
11847
|
-
try {
|
|
11848
|
-
logger6.debug("Using compiled JS version");
|
|
11849
|
-
delete __require.cache[jsPath];
|
|
11850
|
-
const module = __require(jsPath);
|
|
11851
|
-
return module;
|
|
11852
|
-
} catch (error) {
|
|
11853
|
-
logger6.debug("Compiled JS loading failed, trying TypeScript...");
|
|
11854
|
-
}
|
|
11855
|
-
}
|
|
11856
|
-
if (filePath.endsWith(".ts")) {
|
|
11857
|
-
try {
|
|
11858
|
-
logger6.debug("Using TSX runtime loader");
|
|
11859
|
-
const { spawn: spawn2 } = __require("child_process");
|
|
11860
|
-
const tsxCheckResult = await new Promise((resolve4) => {
|
|
11861
|
-
const checkChild = spawn2("npx", ["tsx", "--version"], {
|
|
11862
|
-
stdio: "pipe",
|
|
11863
|
-
cwd: process.cwd()
|
|
11864
|
-
});
|
|
11865
|
-
checkChild.on("close", (code) => {
|
|
11866
|
-
resolve4(code === 0);
|
|
11867
|
-
});
|
|
11868
|
-
checkChild.on("error", () => {
|
|
11869
|
-
resolve4(false);
|
|
11870
|
-
});
|
|
11871
|
-
setTimeout(() => {
|
|
11872
|
-
checkChild.kill();
|
|
11873
|
-
resolve4(false);
|
|
11874
|
-
}, 5e3);
|
|
11875
|
-
});
|
|
11876
|
-
if (!tsxCheckResult) {
|
|
11877
|
-
throw new Error("TSX not available");
|
|
11878
|
-
}
|
|
11879
|
-
const result = await new Promise((resolve4, reject) => {
|
|
11880
|
-
const tsxScript = `
|
|
11881
|
-
async function loadRouter() {
|
|
11882
|
-
try {
|
|
11883
|
-
const module = await import('${pathToFileURL(filePath).href}');
|
|
11884
|
-
const router = module?.AppRouter || module?.default?.AppRouter || module?.default || module?.router;
|
|
11885
|
-
|
|
11886
|
-
if (router && typeof router === 'object') {
|
|
11887
|
-
// Extract safe metadata for CLI use
|
|
11888
|
-
const safeRouter = {
|
|
11889
|
-
config: {
|
|
11890
|
-
baseURL: router.config?.baseURL || '',
|
|
11891
|
-
basePATH: router.config?.basePATH || ''
|
|
11892
|
-
},
|
|
11893
|
-
controllers: {}
|
|
11894
|
-
};
|
|
11895
|
-
|
|
11896
|
-
// Extract controller metadata (no handlers/functions)
|
|
11897
|
-
if (router.controllers && typeof router.controllers === 'object') {
|
|
11898
|
-
for (const [controllerName, controller] of Object.entries(router.controllers)) {
|
|
11899
|
-
if (controller && typeof controller === 'object' && (controller as any).actions) {
|
|
11900
|
-
const safeActions: Record<string, any> = {};
|
|
11901
|
-
|
|
11902
|
-
for (const [actionName, action] of Object.entries((controller as any).actions)) {
|
|
11903
|
-
if (action && typeof action === 'object') {
|
|
11904
|
-
// Extract only metadata, no functions
|
|
11905
|
-
safeActions[actionName] = {
|
|
11906
|
-
path: (action as any).path || '',
|
|
11907
|
-
method: (action as any).method || 'GET',
|
|
11908
|
-
description: (action as any).description,
|
|
11909
|
-
// Keep type inference data if available
|
|
11910
|
-
$Infer: (action as any).$Infer
|
|
11911
|
-
};
|
|
11912
|
-
}
|
|
11913
|
-
}
|
|
11914
|
-
|
|
11915
|
-
safeRouter.controllers[controllerName] = {
|
|
11916
|
-
name: (controller as any).name || controllerName,
|
|
11917
|
-
path: (controller as any).path || '',
|
|
11918
|
-
actions: safeActions
|
|
11919
|
-
};
|
|
11920
|
-
}
|
|
11921
|
-
}
|
|
11922
|
-
}
|
|
11923
|
-
|
|
11924
|
-
console.log('__ROUTER_SUCCESS__' + JSON.stringify(safeRouter));
|
|
11925
|
-
process.exit(0); // Force exit after success
|
|
11926
|
-
} else {
|
|
11927
|
-
console.log('__ROUTER_ERROR__No router found in module');
|
|
11928
|
-
process.exit(1); // Force exit after error
|
|
11929
|
-
}
|
|
11930
|
-
} catch (error) {
|
|
11931
|
-
console.log('__ROUTER_ERROR__' + error.message);
|
|
11932
|
-
process.exit(1); // Force exit after error
|
|
11933
|
-
}
|
|
11934
|
-
}
|
|
11935
|
-
|
|
11936
|
-
loadRouter();
|
|
11937
|
-
`;
|
|
11938
|
-
const child = spawn2("npx", ["tsx", "-e", tsxScript], {
|
|
11939
|
-
stdio: "pipe",
|
|
11940
|
-
cwd: process.cwd(),
|
|
11941
|
-
env: { ...process.env, NODE_NO_WARNINGS: "1" }
|
|
11942
|
-
});
|
|
11943
|
-
let output = "";
|
|
11944
|
-
let errorOutput = "";
|
|
11945
|
-
child.stdout?.on("data", (data) => {
|
|
11946
|
-
output += data.toString();
|
|
11947
|
-
});
|
|
11948
|
-
child.stderr?.on("data", (data) => {
|
|
11949
|
-
errorOutput += data.toString();
|
|
11950
|
-
});
|
|
11951
|
-
child.on("close", (code) => {
|
|
11952
|
-
if (output.includes("__ROUTER_SUCCESS__")) {
|
|
11953
|
-
const resultLine = output.split("\n").find((line) => line.includes("__ROUTER_SUCCESS__"));
|
|
11954
|
-
if (resultLine) {
|
|
11955
|
-
try {
|
|
11956
|
-
const routerData = JSON.parse(resultLine.replace("__ROUTER_SUCCESS__", ""));
|
|
11957
|
-
resolve4(routerData);
|
|
11958
|
-
} catch (e) {
|
|
11959
|
-
reject(new Error("Failed to parse router data: " + e.message));
|
|
11960
|
-
}
|
|
11961
|
-
} else {
|
|
11962
|
-
reject(new Error("Router success marker found but no data"));
|
|
11963
|
-
}
|
|
11964
|
-
} else if (output.includes("__ROUTER_ERROR__")) {
|
|
11965
|
-
const errorLine = output.split("\n").find((line) => line.includes("__ROUTER_ERROR__"));
|
|
11966
|
-
const errorMsg = errorLine ? errorLine.replace("__ROUTER_ERROR__", "") : "Unknown error";
|
|
11967
|
-
reject(new Error("Router loading failed: " + errorMsg));
|
|
11968
|
-
} else {
|
|
11969
|
-
reject(new Error(`TSX execution failed with code ${code}: ${errorOutput || "No output"}`));
|
|
11970
|
-
}
|
|
11971
|
-
});
|
|
11972
|
-
child.on("error", (error) => {
|
|
11973
|
-
reject(new Error("Failed to spawn TSX process: " + error.message));
|
|
11974
|
-
});
|
|
11975
|
-
setTimeout(() => {
|
|
11976
|
-
child.kill();
|
|
11977
|
-
reject(new Error("Timeout loading TypeScript file with TSX"));
|
|
11978
|
-
}, 3e4);
|
|
11979
|
-
});
|
|
11980
|
-
return result;
|
|
11981
|
-
} catch (error) {
|
|
11982
|
-
logger6.debug("TSX runtime loader failed", {}, error);
|
|
11983
|
-
}
|
|
11984
|
-
}
|
|
11985
|
-
return null;
|
|
11986
|
-
}
|
|
11987
|
-
/**
|
|
11988
|
-
* Load router by resolving directory imports to index files
|
|
11989
|
-
*/
|
|
11990
|
-
async loadRouterWithIndexResolution(routerPath) {
|
|
11991
|
-
const logger6 = createChildLogger({ component: "index-resolver" });
|
|
11992
|
-
try {
|
|
11993
|
-
const routerContent = fs7.readFileSync(routerPath, "utf8");
|
|
11994
|
-
const importRegex = /from\s+['\"]([^'\"]+)['\"]/g;
|
|
11995
|
-
let resolvedContent = routerContent;
|
|
11996
|
-
const matches = Array.from(routerContent.matchAll(importRegex));
|
|
11997
|
-
for (const [fullMatch, importPath] of matches) {
|
|
11998
|
-
if (!importPath.startsWith(".") && !importPath.startsWith("@")) {
|
|
11999
|
-
continue;
|
|
12000
|
-
}
|
|
12001
|
-
const basePath = path8.dirname(routerPath);
|
|
12002
|
-
let resolvedPath;
|
|
12003
|
-
if (importPath.startsWith("@/")) {
|
|
12004
|
-
resolvedPath = path8.resolve(process.cwd(), "src", importPath.substring(2));
|
|
12005
|
-
} else if (importPath.startsWith("./")) {
|
|
12006
|
-
resolvedPath = path8.resolve(basePath, importPath);
|
|
12007
|
-
} else {
|
|
12008
|
-
resolvedPath = path8.resolve(basePath, importPath);
|
|
12009
|
-
}
|
|
12010
|
-
let finalPath = importPath;
|
|
12011
|
-
if (!importPath.match(/\\.(js|ts|tsx|jsx)$/)) {
|
|
12012
|
-
const possibleFiles = [
|
|
12013
|
-
resolvedPath + ".ts",
|
|
12014
|
-
resolvedPath + ".tsx",
|
|
12015
|
-
resolvedPath + ".js",
|
|
12016
|
-
resolvedPath + ".jsx"
|
|
12017
|
-
];
|
|
12018
|
-
let fileFound = false;
|
|
12019
|
-
for (const filePath of possibleFiles) {
|
|
12020
|
-
if (fs7.existsSync(filePath)) {
|
|
12021
|
-
const ext = path8.extname(filePath);
|
|
12022
|
-
finalPath = importPath + ext;
|
|
12023
|
-
fileFound = true;
|
|
12024
|
-
break;
|
|
12025
|
-
}
|
|
12026
|
-
}
|
|
12027
|
-
if (!fileFound) {
|
|
12028
|
-
const possibleIndexFiles = [
|
|
12029
|
-
path8.join(resolvedPath, "index.ts"),
|
|
12030
|
-
path8.join(resolvedPath, "index.tsx"),
|
|
12031
|
-
path8.join(resolvedPath, "index.js"),
|
|
12032
|
-
path8.join(resolvedPath, "index.jsx")
|
|
12033
|
-
];
|
|
12034
|
-
for (const indexFile of possibleIndexFiles) {
|
|
12035
|
-
if (fs7.existsSync(indexFile)) {
|
|
12036
|
-
const ext = path8.extname(indexFile);
|
|
12037
|
-
finalPath = importPath + "/index" + ext;
|
|
12038
|
-
break;
|
|
12039
|
-
}
|
|
12040
|
-
}
|
|
12041
|
-
}
|
|
12042
|
-
}
|
|
12043
|
-
const absolutePath = path8.resolve(basePath, finalPath);
|
|
12044
|
-
if (fs7.existsSync(absolutePath)) {
|
|
12045
|
-
const fileUrl = pathToFileURL(absolutePath).href;
|
|
12046
|
-
resolvedContent = resolvedContent.replace(fullMatch, `from '${fileUrl}'`);
|
|
12047
|
-
} else {
|
|
12048
|
-
resolvedContent = resolvedContent.replace(fullMatch, `from '${finalPath}'`);
|
|
12049
|
-
}
|
|
12050
|
-
}
|
|
12051
|
-
const tempFileName = `igniter-temp-${Date.now()}.ts`;
|
|
12052
|
-
const tempFilePath = path8.join(process.cwd(), tempFileName);
|
|
12053
|
-
try {
|
|
12054
|
-
fs7.writeFileSync(tempFilePath, resolvedContent);
|
|
12055
|
-
logger6.debug("Loading resolved module via TSX");
|
|
12056
|
-
const { spawn: spawn2 } = __require("child_process");
|
|
12057
|
-
const result = await new Promise((resolve4, reject) => {
|
|
12058
|
-
const tsxScript = `
|
|
12059
|
-
async function loadResolvedRouter() {
|
|
12060
|
-
try {
|
|
12061
|
-
const module = await import('${pathToFileURL(tempFilePath).href}');
|
|
12062
|
-
const router = module?.AppRouter || module?.default?.AppRouter || module?.default || module?.router;
|
|
12063
|
-
|
|
12064
|
-
if (router && typeof router === 'object') {
|
|
12065
|
-
// Extract safe metadata
|
|
12066
|
-
const safeRouter = {
|
|
12067
|
-
config: {
|
|
12068
|
-
baseURL: router.config?.baseURL || '',
|
|
12069
|
-
basePATH: router.config?.basePATH || ''
|
|
12070
|
-
},
|
|
12071
|
-
controllers: {}
|
|
12072
|
-
};
|
|
12073
|
-
|
|
12074
|
-
if (router.controllers && typeof router.controllers === 'object') {
|
|
12075
|
-
for (const [controllerName, controller] of Object.entries(router.controllers)) {
|
|
12076
|
-
if (controller && typeof controller === 'object' && (controller as any).actions) {
|
|
12077
|
-
const safeActions: Record<string, any> = {};
|
|
12078
|
-
|
|
12079
|
-
for (const [actionName, action] of Object.entries((controller as any).actions)) {
|
|
12080
|
-
if (action && typeof action === 'object') {
|
|
12081
|
-
safeActions[actionName] = {
|
|
12082
|
-
path: (action as any).path || '',
|
|
12083
|
-
method: (action as any).method || 'GET',
|
|
12084
|
-
description: (action as any).description,
|
|
12085
|
-
$Infer: (action as any).$Infer
|
|
12086
|
-
};
|
|
12087
|
-
}
|
|
12088
|
-
}
|
|
12089
|
-
|
|
12090
|
-
safeRouter.controllers[controllerName] = {
|
|
12091
|
-
name: (controller as any).name || controllerName,
|
|
12092
|
-
path: (controller as any).path || '',
|
|
12093
|
-
actions: safeActions
|
|
12094
|
-
};
|
|
12095
|
-
}
|
|
12096
|
-
}
|
|
12097
|
-
}
|
|
12098
|
-
|
|
12099
|
-
console.log('__ROUTER_SUCCESS__' + JSON.stringify(safeRouter));
|
|
12100
|
-
process.exit(0); // Force exit after success
|
|
12101
|
-
} else {
|
|
12102
|
-
console.log('__ROUTER_ERROR__No router found in resolved module');
|
|
12103
|
-
process.exit(1); // Force exit after error
|
|
12104
|
-
}
|
|
12105
|
-
} catch (error) {
|
|
12106
|
-
console.log('__ROUTER_ERROR__' + error.message);
|
|
12107
|
-
process.exit(1); // Force exit after error
|
|
12108
|
-
}
|
|
12109
|
-
}
|
|
12110
|
-
|
|
12111
|
-
loadResolvedRouter();
|
|
12112
|
-
`;
|
|
12113
|
-
const child = spawn2("npx", ["tsx", "-e", tsxScript], {
|
|
12114
|
-
stdio: "pipe",
|
|
12115
|
-
cwd: process.cwd(),
|
|
12116
|
-
env: { ...process.env, NODE_NO_WARNINGS: "1" }
|
|
12117
|
-
});
|
|
12118
|
-
let output = "";
|
|
12119
|
-
let errorOutput = "";
|
|
12120
|
-
child.stdout?.on("data", (data) => {
|
|
12121
|
-
output += data.toString();
|
|
12122
|
-
});
|
|
12123
|
-
child.stderr?.on("data", (data) => {
|
|
12124
|
-
errorOutput += data.toString();
|
|
12125
|
-
});
|
|
12126
|
-
child.on("close", (code) => {
|
|
12127
|
-
if (output.includes("__ROUTER_SUCCESS__")) {
|
|
12128
|
-
const resultLine = output.split("\n").find((line) => line.includes("__ROUTER_SUCCESS__"));
|
|
12129
|
-
if (resultLine) {
|
|
12130
|
-
try {
|
|
12131
|
-
const routerData = JSON.parse(resultLine.replace("__ROUTER_SUCCESS__", ""));
|
|
12132
|
-
resolve4(routerData);
|
|
12133
|
-
} catch (e) {
|
|
12134
|
-
reject(new Error("Failed to parse resolved router data: " + e.message));
|
|
12135
|
-
}
|
|
12136
|
-
} else {
|
|
12137
|
-
reject(new Error("Router success marker found but no data"));
|
|
12138
|
-
}
|
|
12139
|
-
} else if (output.includes("__ROUTER_ERROR__")) {
|
|
12140
|
-
const errorLine = output.split("\n").find((line) => line.includes("__ROUTER_ERROR__"));
|
|
12141
|
-
const errorMsg = errorLine ? errorLine.replace("__ROUTER_ERROR__", "") : "Unknown error";
|
|
12142
|
-
reject(new Error("Router loading failed: " + errorMsg));
|
|
12143
|
-
} else {
|
|
12144
|
-
reject(new Error(`TSX execution failed with code ${code}: ${errorOutput || "No output"}`));
|
|
12145
|
-
}
|
|
12146
|
-
});
|
|
12147
|
-
child.on("error", (error) => {
|
|
12148
|
-
reject(new Error("Failed to spawn TSX process for resolved module: " + error.message));
|
|
12149
|
-
});
|
|
12150
|
-
setTimeout(() => {
|
|
12151
|
-
child.kill();
|
|
12152
|
-
reject(new Error("Timeout loading resolved TypeScript file with TSX"));
|
|
12153
|
-
}, 3e4);
|
|
12154
|
-
});
|
|
12155
|
-
return result;
|
|
12156
|
-
} finally {
|
|
12157
|
-
if (fs7.existsSync(tempFilePath)) {
|
|
12158
|
-
fs7.unlinkSync(tempFilePath);
|
|
12159
|
-
}
|
|
12160
|
-
}
|
|
12161
|
-
} catch (error) {
|
|
12162
|
-
logger6.error("Index resolution failed", { path: routerPath }, error);
|
|
12163
|
-
throw error;
|
|
12164
|
-
}
|
|
12165
|
-
}
|
|
12166
12127
|
/**
|
|
12167
12128
|
* Start watching controller files
|
|
12168
12129
|
*/
|
|
@@ -12177,7 +12138,7 @@ var init_watcher = __esm({
|
|
|
12177
12138
|
persistent: true,
|
|
12178
12139
|
ignoreInitial: false
|
|
12179
12140
|
});
|
|
12180
|
-
await new Promise((
|
|
12141
|
+
await new Promise((resolve5, reject) => {
|
|
12181
12142
|
this.watcher.on("add", this.handleFileChange.bind(this));
|
|
12182
12143
|
this.watcher.on("change", this.handleFileChange.bind(this));
|
|
12183
12144
|
this.watcher.on("unlink", this.handleFileChange.bind(this));
|
|
@@ -12185,7 +12146,7 @@ var init_watcher = __esm({
|
|
|
12185
12146
|
this.logger.success("File watcher is ready", {
|
|
12186
12147
|
watching: this.config.controllerPatterns?.join(", ")
|
|
12187
12148
|
});
|
|
12188
|
-
|
|
12149
|
+
resolve5();
|
|
12189
12150
|
});
|
|
12190
12151
|
this.watcher.on("error", (error) => {
|
|
12191
12152
|
this.logger.error("File watcher error", {}, error);
|
|
@@ -12258,8 +12219,8 @@ var init_watcher = __esm({
|
|
|
12258
12219
|
];
|
|
12259
12220
|
let router = null;
|
|
12260
12221
|
for (const routerPath of possibleRouterPaths) {
|
|
12261
|
-
if (
|
|
12262
|
-
router = await
|
|
12222
|
+
if (fs8.existsSync(routerPath)) {
|
|
12223
|
+
router = await loadRouter(routerPath);
|
|
12263
12224
|
if (router) {
|
|
12264
12225
|
break;
|
|
12265
12226
|
} else {
|
|
@@ -12274,20 +12235,43 @@ var init_watcher = __esm({
|
|
|
12274
12235
|
return;
|
|
12275
12236
|
}
|
|
12276
12237
|
await generateSchemaFromRouter(router, this.config);
|
|
12238
|
+
if (this.config.generateDocs) {
|
|
12239
|
+
await this.generateOpenAPISpec(router);
|
|
12240
|
+
}
|
|
12277
12241
|
} catch (error) {
|
|
12278
12242
|
this.logger.error("Schema generation failed", {}, error);
|
|
12279
12243
|
} finally {
|
|
12280
12244
|
this.isGenerating = false;
|
|
12281
12245
|
}
|
|
12282
12246
|
}
|
|
12247
|
+
/**
|
|
12248
|
+
* Generate OpenAPI specification from router
|
|
12249
|
+
*/
|
|
12250
|
+
async generateOpenAPISpec(router) {
|
|
12251
|
+
try {
|
|
12252
|
+
this.logger.info("Generating OpenAPI specification...");
|
|
12253
|
+
const introspected = introspectRouter(router);
|
|
12254
|
+
const generator = new OpenAPIGenerator(introspected.schema.docs || {});
|
|
12255
|
+
const openApiSpec = generator.generate(introspected.schema);
|
|
12256
|
+
const outputDir = path9.resolve(this.config.docsOutputDir);
|
|
12257
|
+
if (!fs8.existsSync(outputDir)) {
|
|
12258
|
+
fs8.mkdirSync(outputDir, { recursive: true });
|
|
12259
|
+
}
|
|
12260
|
+
const outputPath = path9.join(outputDir, "openapi.json");
|
|
12261
|
+
fs8.writeFileSync(outputPath, JSON.stringify(openApiSpec, null, 2), "utf8");
|
|
12262
|
+
this.logger.success(`OpenAPI specification updated at ${outputPath}`);
|
|
12263
|
+
} catch (error) {
|
|
12264
|
+
this.logger.error("Error generating OpenAPI specification:", error);
|
|
12265
|
+
}
|
|
12266
|
+
}
|
|
12283
12267
|
};
|
|
12284
12268
|
}
|
|
12285
12269
|
});
|
|
12286
12270
|
|
|
12287
12271
|
// src/index.ts
|
|
12288
12272
|
import { Command } from "commander";
|
|
12289
|
-
import * as
|
|
12290
|
-
import * as
|
|
12273
|
+
import * as fs9 from "fs";
|
|
12274
|
+
import * as path10 from "path";
|
|
12291
12275
|
|
|
12292
12276
|
// src/adapters/framework/framework-detector.ts
|
|
12293
12277
|
import * as fs2 from "fs";
|
|
@@ -14471,13 +14455,13 @@ program.command("init").description("Create a new Igniter.js project with intera
|
|
|
14471
14455
|
process.exit(1);
|
|
14472
14456
|
}
|
|
14473
14457
|
}
|
|
14474
|
-
const targetDir = projectName === "." ? process.cwd() :
|
|
14475
|
-
const isExistingProject = await
|
|
14458
|
+
const targetDir = projectName === "." ? process.cwd() : path10.resolve(projectName);
|
|
14459
|
+
const isExistingProject = await fs9.promises.stat(path10.join(targetDir, "package.json")).catch(() => null) !== null;
|
|
14476
14460
|
if (!options.force) {
|
|
14477
14461
|
try {
|
|
14478
|
-
const stats = await
|
|
14462
|
+
const stats = await fs9.promises.stat(targetDir);
|
|
14479
14463
|
if (stats.isDirectory()) {
|
|
14480
|
-
const files = await
|
|
14464
|
+
const files = await fs9.promises.readdir(targetDir);
|
|
14481
14465
|
const nonEmptyFiles = files.filter((file) => !file.startsWith("."));
|
|
14482
14466
|
if (nonEmptyFiles.length > 0 && !isExistingProject) {
|
|
14483
14467
|
const shouldOverwrite = await confirmOverwrite(`Directory '${projectName}' is not empty. Continue?`);
|
|
@@ -14506,7 +14490,7 @@ program.command("init").description("Create a new Igniter.js project with intera
|
|
|
14506
14490
|
process.exit(1);
|
|
14507
14491
|
}
|
|
14508
14492
|
});
|
|
14509
|
-
program.command("dev").description("Start development mode with framework and Igniter (interactive dashboard by default)").option("--framework <type>", `Framework type (${getFrameworkList()}, generic)`).option("--output <dir>", "Output directory for generated client files", "src/").option("--debug", "Enable debug mode").option("--port <number>", "Port for the dev server", "3000").option("--cmd <command>", "Custom command to start dev server").option("--no-framework", "Disable framework dev server (Igniter only)").option("--no-interactive", "Disable interactive mode (use regular concurrent mode)").action(async (options) => {
|
|
14493
|
+
program.command("dev").description("Start development mode with framework and Igniter (interactive dashboard by default)").option("--framework <type>", `Framework type (${getFrameworkList()}, generic)`).option("--output <dir>", "Output directory for generated client files", "src/").option("--debug", "Enable debug mode").option("--port <number>", "Port for the dev server", "3000").option("--cmd <command>", "Custom command to start dev server").option("--no-framework", "Disable framework dev server (Igniter only)").option("--no-interactive", "Disable interactive mode (use regular concurrent mode)").option("--docs", "Enable automatic OpenAPI documentation generation").option("--docs-output <dir>", "Output directory for OpenAPI docs", "./src/docs").action(async (options) => {
|
|
14510
14494
|
const detectedFramework = detectFramework();
|
|
14511
14495
|
const framework = options.framework ? isFrameworkSupported(options.framework) ? options.framework : "generic" : detectedFramework;
|
|
14512
14496
|
const useInteractive = options.interactive !== false;
|
|
@@ -14535,9 +14519,10 @@ program.command("dev").description("Start development mode with framework and Ig
|
|
|
14535
14519
|
});
|
|
14536
14520
|
}
|
|
14537
14521
|
}
|
|
14522
|
+
const docsFlags = options.docs ? ` --docs --docs-output ${options.docsOutput}` : "";
|
|
14538
14523
|
processes.push({
|
|
14539
14524
|
name: "Igniter",
|
|
14540
|
-
command: `igniter generate schema --watch --framework ${framework} --output ${options.output}${options.debug ? " --debug" : ""}`,
|
|
14525
|
+
command: `igniter generate schema --watch --framework ${framework} --output ${options.output}${options.debug ? " --debug" : ""}${docsFlags}`,
|
|
14541
14526
|
color: "blue",
|
|
14542
14527
|
cwd: process.cwd()
|
|
14543
14528
|
});
|
|
@@ -14548,7 +14533,7 @@ program.command("dev").description("Start development mode with framework and Ig
|
|
|
14548
14533
|
}
|
|
14549
14534
|
});
|
|
14550
14535
|
var generate = program.command("generate").description("Scaffold new features or generate client schema");
|
|
14551
|
-
generate.command("schema").description("Generate client schema from your Igniter router (for CI/CD or manual builds)").option("--framework <type>", `Framework type (${getFrameworkList()}, generic)`).option("--output <dir>", "Output directory", "src/").option("--debug", "Enable debug mode").option("--watch", "Watch for changes and regenerate automatically").action(async (options) => {
|
|
14536
|
+
generate.command("schema").description("Generate client schema from your Igniter router (for CI/CD or manual builds)").option("--framework <type>", `Framework type (${getFrameworkList()}, generic)`).option("--output <dir>", "Output directory", "src/").option("--debug", "Enable debug mode").option("--watch", "Watch for changes and regenerate automatically").option("--docs", "Enable automatic OpenAPI documentation generation").option("--docs-output <dir>", "Output directory for OpenAPI docs", "./src/docs").action(async (options) => {
|
|
14552
14537
|
const startTime = performance.now();
|
|
14553
14538
|
const detectedFramework = detectFramework();
|
|
14554
14539
|
const framework = options.framework ? isFrameworkSupported(options.framework) ? options.framework : "generic" : detectedFramework;
|
|
@@ -14561,7 +14546,9 @@ generate.command("schema").description("Generate client schema from your Igniter
|
|
|
14561
14546
|
framework,
|
|
14562
14547
|
outputDir: options.output,
|
|
14563
14548
|
debug: options.debug,
|
|
14564
|
-
controllerPatterns: ["**/*.controller.{ts,js}"]
|
|
14549
|
+
controllerPatterns: ["**/*.controller.{ts,js}"],
|
|
14550
|
+
generateDocs: options.docs,
|
|
14551
|
+
docsOutputDir: options.docsOutput
|
|
14565
14552
|
});
|
|
14566
14553
|
watcherSpinner.success("Generator loaded");
|
|
14567
14554
|
if (options.watch) {
|
|
@@ -14574,6 +14561,69 @@ generate.command("schema").description("Generate client schema from your Igniter
|
|
|
14574
14561
|
process.exit(0);
|
|
14575
14562
|
}
|
|
14576
14563
|
});
|
|
14564
|
+
generate.command("docs").description("Generate OpenAPI specification and/or interactive playground").option("--output <dir>", "Output directory for the OpenAPI spec", "./src").option("--ui", "Generate a self-contained HTML file with Scalar UI").action(async (options) => {
|
|
14565
|
+
const docsLogger = createChildLogger({ component: "generate-docs-command" });
|
|
14566
|
+
try {
|
|
14567
|
+
docsLogger.info("Starting OpenAPI documentation generation...");
|
|
14568
|
+
const { loadRouter: loadRouter2, introspectRouter: introspectRouter2 } = await Promise.resolve().then(() => (init_introspector(), introspector_exports));
|
|
14569
|
+
const { OpenAPIGenerator: OpenAPIGenerator2 } = await Promise.resolve().then(() => (init_openapi_generator(), openapi_generator_exports));
|
|
14570
|
+
const possibleRouterPaths = [
|
|
14571
|
+
"src/igniter.router.ts",
|
|
14572
|
+
"src/igniter.router.js",
|
|
14573
|
+
"src/router.ts",
|
|
14574
|
+
"src/router.js",
|
|
14575
|
+
"igniter.router.ts",
|
|
14576
|
+
"igniter.router.js",
|
|
14577
|
+
"router.ts",
|
|
14578
|
+
"router.js"
|
|
14579
|
+
];
|
|
14580
|
+
let router = null;
|
|
14581
|
+
for (const routerPath of possibleRouterPaths) {
|
|
14582
|
+
if (fs9.existsSync(routerPath)) {
|
|
14583
|
+
router = await loadRouter2(routerPath);
|
|
14584
|
+
if (router) {
|
|
14585
|
+
docsLogger.info(`Found router at: ${routerPath}`);
|
|
14586
|
+
break;
|
|
14587
|
+
}
|
|
14588
|
+
}
|
|
14589
|
+
}
|
|
14590
|
+
if (!router) {
|
|
14591
|
+
docsLogger.error("No Igniter router found in your project. Please ensure you have a router file (e.g., src/igniter.router.ts).");
|
|
14592
|
+
process.exit(1);
|
|
14593
|
+
}
|
|
14594
|
+
const introspected = introspectRouter2(router);
|
|
14595
|
+
const generator = new OpenAPIGenerator2(introspected.schema.docs || {});
|
|
14596
|
+
const openApiSpec = generator.generate(introspected.schema);
|
|
14597
|
+
const outputDir = path10.resolve(options.output, "docs");
|
|
14598
|
+
if (!fs9.existsSync(outputDir)) {
|
|
14599
|
+
fs9.mkdirSync(outputDir, { recursive: true });
|
|
14600
|
+
}
|
|
14601
|
+
const outputPath = path10.join(outputDir, "openapi.json");
|
|
14602
|
+
fs9.writeFileSync(outputPath, JSON.stringify(openApiSpec, null, 2), "utf8");
|
|
14603
|
+
docsLogger.success(`OpenAPI specification generated at ${outputPath}`);
|
|
14604
|
+
if (options.ui) {
|
|
14605
|
+
const scalarHtml = `<!doctype html>
|
|
14606
|
+
<html lang="en">
|
|
14607
|
+
<head>
|
|
14608
|
+
<meta charset="utf-8" />
|
|
14609
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
14610
|
+
<title>API Reference</title>
|
|
14611
|
+
</head>
|
|
14612
|
+
<body>
|
|
14613
|
+
<script id="api-reference" data-url="./openapi.json"></script>
|
|
14614
|
+
<script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
|
|
14615
|
+
</body>
|
|
14616
|
+
</html>`;
|
|
14617
|
+
const uiOutputPath = path10.join(outputDir, "index.html");
|
|
14618
|
+
fs9.writeFileSync(uiOutputPath, scalarHtml, "utf8");
|
|
14619
|
+
docsLogger.success(`Scalar UI generated at ${uiOutputPath}`);
|
|
14620
|
+
}
|
|
14621
|
+
} catch (error) {
|
|
14622
|
+
docsLogger.error("Failed to generate OpenAPI documentation:");
|
|
14623
|
+
console.error(error);
|
|
14624
|
+
process.exit(1);
|
|
14625
|
+
}
|
|
14626
|
+
});
|
|
14577
14627
|
generate.command("feature").description("Scaffold a new feature module").argument("<name>", "The name of the feature (e.g., 'user', 'products')").option("--schema <value>", "Generate from a schema provider (e.g., 'prisma:User')").action(async (name, options) => {
|
|
14578
14628
|
await handleGenerateFeature(name, options);
|
|
14579
14629
|
});
|