@igniter-js/cli 0.2.4 → 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/README.md +2 -2
- package/dist/index.js +541 -487
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +536 -483
- 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,63 +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
|
-
|
|
11493
|
-
|
|
11494
|
-
basePATH: router.config?.basePATH || ""
|
|
11495
|
-
},
|
|
11496
|
-
controllers: controllersSchema,
|
|
11497
|
-
processor: {},
|
|
11498
|
-
handler: {},
|
|
11499
|
-
$context: {},
|
|
11500
|
-
$plugins: {},
|
|
11501
|
-
$caller: {}
|
|
11497
|
+
const schemaResult = {
|
|
11498
|
+
controllers: introspectedControllers,
|
|
11499
|
+
docs: router?.config?.docs
|
|
11502
11500
|
};
|
|
11503
11501
|
return {
|
|
11504
|
-
schema,
|
|
11502
|
+
schema: schemaResult,
|
|
11505
11503
|
stats: {
|
|
11506
|
-
controllers: Object.keys(
|
|
11504
|
+
controllers: Object.keys(introspectedControllers).length,
|
|
11507
11505
|
actions: totalActions
|
|
11508
11506
|
}
|
|
11509
11507
|
};
|
|
11510
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
|
+
}
|
|
11511
11707
|
async function generateSchemaFromRouter(router, config) {
|
|
11512
11708
|
const logger6 = createChildLogger({ component: "generator" });
|
|
11513
11709
|
const startTime = performance.now();
|
|
@@ -11520,7 +11716,7 @@ async function generateSchemaFromRouter(router, config) {
|
|
|
11520
11716
|
extractSpinner = createDetachedSpinner("Extracting router schema...");
|
|
11521
11717
|
extractSpinner.start();
|
|
11522
11718
|
}
|
|
11523
|
-
const { schema, stats } =
|
|
11719
|
+
const { schema, stats } = introspectRouter(router);
|
|
11524
11720
|
if (isInteractiveMode2) {
|
|
11525
11721
|
logger6.success(`Schema extracted - ${stats.controllers} controllers, ${stats.actions} actions`);
|
|
11526
11722
|
} else if (extractSpinner) {
|
|
@@ -11535,7 +11731,7 @@ async function generateSchemaFromRouter(router, config) {
|
|
|
11535
11731
|
}
|
|
11536
11732
|
const outputDir = config.outputDir || "generated";
|
|
11537
11733
|
await ensureDirectoryExists(outputDir);
|
|
11538
|
-
const outputPath =
|
|
11734
|
+
const outputPath = path8.resolve(outputDir);
|
|
11539
11735
|
if (isInteractiveMode2) {
|
|
11540
11736
|
logger6.success(`Output directory ready ${outputPath}`);
|
|
11541
11737
|
} else if (dirSpinner) {
|
|
@@ -11565,8 +11761,8 @@ async function generateSchemaFromRouter(router, config) {
|
|
|
11565
11761
|
let totalSize = 0;
|
|
11566
11762
|
files.forEach((file) => {
|
|
11567
11763
|
const size = getFileSize(file.path);
|
|
11568
|
-
totalSize +=
|
|
11569
|
-
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) });
|
|
11570
11766
|
});
|
|
11571
11767
|
const duration = ((performance.now() - startTime) / 1e3).toFixed(2);
|
|
11572
11768
|
const totalSizeFormatted = totalSize < 1024 ? `${totalSize}b` : totalSize < 1024 * 1024 ? `${(totalSize / 1024).toFixed(1)}kb` : `${(totalSize / (1024 * 1024)).toFixed(1)}mb`;
|
|
@@ -11599,33 +11795,45 @@ export const AppRouterSchema = ${JSON.stringify(schema, null, 2)} as const
|
|
|
11599
11795
|
|
|
11600
11796
|
export type AppRouterSchemaType = typeof AppRouterSchema
|
|
11601
11797
|
`;
|
|
11602
|
-
const filePath =
|
|
11798
|
+
const filePath = path8.join(outputDir, "igniter.schema.ts");
|
|
11603
11799
|
await writeFileWithHeader(filePath, content, config);
|
|
11604
11800
|
return filePath;
|
|
11605
11801
|
}
|
|
11606
11802
|
async function generateClientFile(schema, outputDir, config) {
|
|
11607
|
-
const
|
|
11803
|
+
const filePath = path8.join(outputDir, "igniter.client.ts");
|
|
11804
|
+
if (fs7.existsSync(filePath)) {
|
|
11805
|
+
const logger6 = createChildLogger({ component: "generator" });
|
|
11806
|
+
logger6.info("Skipping client file generation, already exists", { path: filePath });
|
|
11807
|
+
return filePath;
|
|
11808
|
+
}
|
|
11809
|
+
const content = `* eslint-disable */
|
|
11810
|
+
/* prettier-ignore */
|
|
11811
|
+
|
|
11812
|
+
import { createIgniterClient, useIgniterQueryClient } from '@igniter-js/core/client'
|
|
11608
11813
|
import type { AppRouterType } from './igniter.router'
|
|
11609
11814
|
|
|
11610
11815
|
/**
|
|
11611
|
-
|
|
11612
|
-
|
|
11613
|
-
|
|
11614
|
-
|
|
11615
|
-
|
|
11616
|
-
|
|
11617
|
-
|
|
11618
|
-
|
|
11816
|
+
* Type-safe API client generated from your Igniter router
|
|
11817
|
+
*
|
|
11818
|
+
* Usage in Server Components:
|
|
11819
|
+
* const users = await api.users.list.query()
|
|
11820
|
+
*
|
|
11821
|
+
* Usage in Client Components:
|
|
11822
|
+
* const { data } = api.users.list.useQuery()
|
|
11823
|
+
*
|
|
11824
|
+
* Note: Adjust environment variable prefixes (e.g., NEXT_PUBLIC_, BUN_PUBLIC_, DENO_PUBLIC_, REACT_APP_)
|
|
11825
|
+
* according to your project's framework/runtime (Next.js, Bun, Deno, React/Vite, etc.).
|
|
11826
|
+
*/
|
|
11619
11827
|
export const api = createIgniterClient<AppRouterType>({
|
|
11620
|
-
|
|
11621
|
-
|
|
11622
|
-
|
|
11623
|
-
|
|
11624
|
-
|
|
11625
|
-
|
|
11828
|
+
baseURL: process.env.NEXT_PUBLIC_IGNITER_API_URL, // Adapt for your needs
|
|
11829
|
+
basePath: process.env.NEXT_PUBLIC_IGNITER_API_BASE_PATH,
|
|
11830
|
+
router: () => {
|
|
11831
|
+
if (typeof window === 'undefined') {
|
|
11832
|
+
return require('./igniter.router').AppRouter
|
|
11833
|
+
}
|
|
11626
11834
|
|
|
11627
|
-
|
|
11628
|
-
|
|
11835
|
+
return require('./igniter.schema').AppRouterSchema
|
|
11836
|
+
},
|
|
11629
11837
|
})
|
|
11630
11838
|
|
|
11631
11839
|
/**
|
|
@@ -11647,18 +11855,18 @@ export type ApiClient = typeof api
|
|
|
11647
11855
|
*/
|
|
11648
11856
|
export const useQueryClient = useIgniterQueryClient<AppRouterType>;
|
|
11649
11857
|
`;
|
|
11650
|
-
const filePath = path7.join(outputDir, "igniter.client.ts");
|
|
11651
11858
|
await writeFileWithHeader(filePath, content, config);
|
|
11652
11859
|
return filePath;
|
|
11653
11860
|
}
|
|
11654
11861
|
async function writeFileWithHeader(filePath, content, config) {
|
|
11655
11862
|
const header = generateFileHeader(config);
|
|
11656
11863
|
const fullContent = header + "\n\n" + content;
|
|
11657
|
-
await
|
|
11864
|
+
await fs7.promises.writeFile(filePath, fullContent, "utf8");
|
|
11658
11865
|
}
|
|
11659
11866
|
function generateFileHeader(config) {
|
|
11660
11867
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
11661
|
-
return `/* eslint-disable
|
|
11868
|
+
return `/* eslint-disable */
|
|
11869
|
+
/* prettier-ignore */
|
|
11662
11870
|
|
|
11663
11871
|
/**
|
|
11664
11872
|
* Generated by @igniter-js/cli
|
|
@@ -11677,9 +11885,9 @@ function generateFileHeader(config) {
|
|
|
11677
11885
|
}
|
|
11678
11886
|
async function ensureDirectoryExists(dirPath) {
|
|
11679
11887
|
try {
|
|
11680
|
-
await
|
|
11888
|
+
await fs7.promises.access(dirPath);
|
|
11681
11889
|
} catch (error) {
|
|
11682
|
-
await
|
|
11890
|
+
await fs7.promises.mkdir(dirPath, { recursive: true });
|
|
11683
11891
|
}
|
|
11684
11892
|
}
|
|
11685
11893
|
var init_generator = __esm({
|
|
@@ -11687,6 +11895,133 @@ var init_generator = __esm({
|
|
|
11687
11895
|
"use strict";
|
|
11688
11896
|
init_logger();
|
|
11689
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
|
+
};
|
|
11690
12025
|
}
|
|
11691
12026
|
});
|
|
11692
12027
|
|
|
@@ -11695,17 +12030,18 @@ var watcher_exports = {};
|
|
|
11695
12030
|
__export(watcher_exports, {
|
|
11696
12031
|
IgniterWatcher: () => IgniterWatcher
|
|
11697
12032
|
});
|
|
11698
|
-
import * as
|
|
11699
|
-
import * as
|
|
11700
|
-
import { pathToFileURL } from "url";
|
|
12033
|
+
import * as fs8 from "fs";
|
|
12034
|
+
import * as path9 from "path";
|
|
11701
12035
|
import chokidar from "chokidar";
|
|
11702
12036
|
var IgniterWatcher;
|
|
11703
12037
|
var init_watcher = __esm({
|
|
11704
12038
|
"src/adapters/build/watcher.ts"() {
|
|
11705
12039
|
"use strict";
|
|
12040
|
+
init_introspector();
|
|
11706
12041
|
init_generator();
|
|
11707
12042
|
init_logger();
|
|
11708
12043
|
init_spinner();
|
|
12044
|
+
init_openapi_generator();
|
|
11709
12045
|
IgniterWatcher = class {
|
|
11710
12046
|
// Detect interactive mode
|
|
11711
12047
|
constructor(config) {
|
|
@@ -11725,6 +12061,8 @@ var init_watcher = __esm({
|
|
|
11725
12061
|
hotReload: true,
|
|
11726
12062
|
controllerPatterns: ["**/*.controller.{ts,js}"],
|
|
11727
12063
|
debug: false,
|
|
12064
|
+
generateDocs: false,
|
|
12065
|
+
docsOutputDir: "./src/docs",
|
|
11728
12066
|
...config
|
|
11729
12067
|
};
|
|
11730
12068
|
this.isInteractiveMode = !!(process.env.IGNITER_INTERACTIVE_MODE === "true" || process.argv.includes("--interactive"));
|
|
@@ -11786,380 +12124,6 @@ var init_watcher = __esm({
|
|
|
11786
12124
|
process.stdout.write("\n");
|
|
11787
12125
|
}
|
|
11788
12126
|
}
|
|
11789
|
-
/**
|
|
11790
|
-
* Load router from file with simplified approach
|
|
11791
|
-
*/
|
|
11792
|
-
async loadRouter(routerPath) {
|
|
11793
|
-
const logger6 = createChildLogger({ component: "router-loader" });
|
|
11794
|
-
const fullPath = path8.resolve(process.cwd(), routerPath);
|
|
11795
|
-
logger6.debug("Loading router", { path: routerPath });
|
|
11796
|
-
try {
|
|
11797
|
-
const module = await this.loadWithTypeScriptSupport(fullPath);
|
|
11798
|
-
if (module) {
|
|
11799
|
-
if (module?.config && module?.controllers) {
|
|
11800
|
-
const controllerCount = Object.keys(module.controllers || {}).length;
|
|
11801
|
-
return module;
|
|
11802
|
-
}
|
|
11803
|
-
const router = module?.AppRouter || module?.default?.AppRouter || module?.default || module?.router;
|
|
11804
|
-
if (router && typeof router === "object") {
|
|
11805
|
-
const controllerCount = Object.keys(router.controllers || {}).length;
|
|
11806
|
-
return router;
|
|
11807
|
-
} else {
|
|
11808
|
-
logger6.debug("Available exports", {
|
|
11809
|
-
exports: Object.keys(module || {})
|
|
11810
|
-
});
|
|
11811
|
-
}
|
|
11812
|
-
} else {
|
|
11813
|
-
}
|
|
11814
|
-
const fallbackSpinner = createDetachedSpinner("Trying fallback loading method...");
|
|
11815
|
-
fallbackSpinner.start();
|
|
11816
|
-
const fallbackModule = await this.loadRouterWithIndexResolution(fullPath);
|
|
11817
|
-
if (fallbackModule) {
|
|
11818
|
-
if (fallbackModule?.config && fallbackModule?.controllers) {
|
|
11819
|
-
const controllerCount = Object.keys(fallbackModule.controllers || {}).length;
|
|
11820
|
-
fallbackSpinner.success(`Router loaded successfully - ${controllerCount} controllers`);
|
|
11821
|
-
return fallbackModule;
|
|
11822
|
-
}
|
|
11823
|
-
const router = fallbackModule?.AppRouter || fallbackModule?.default?.AppRouter || fallbackModule?.default || fallbackModule?.router;
|
|
11824
|
-
if (router && typeof router === "object") {
|
|
11825
|
-
const controllerCount = Object.keys(router.controllers || {}).length;
|
|
11826
|
-
fallbackSpinner.success(`Router loaded successfully - ${controllerCount} controllers`);
|
|
11827
|
-
return router;
|
|
11828
|
-
}
|
|
11829
|
-
}
|
|
11830
|
-
fallbackSpinner.error("Could not load router");
|
|
11831
|
-
} catch (error) {
|
|
11832
|
-
logger6.error("Failed to load router", { path: routerPath }, error);
|
|
11833
|
-
}
|
|
11834
|
-
return null;
|
|
11835
|
-
}
|
|
11836
|
-
/**
|
|
11837
|
-
* Load TypeScript files using TSX runtime loader
|
|
11838
|
-
* This is the NEW robust approach that replaces the problematic transpilation
|
|
11839
|
-
*/
|
|
11840
|
-
async loadWithTypeScriptSupport(filePath) {
|
|
11841
|
-
const logger6 = createChildLogger({ component: "tsx-loader" });
|
|
11842
|
-
const jsPath = filePath.replace(/\.ts$/, ".js");
|
|
11843
|
-
if (fs7.existsSync(jsPath)) {
|
|
11844
|
-
try {
|
|
11845
|
-
logger6.debug("Using compiled JS version");
|
|
11846
|
-
delete __require.cache[jsPath];
|
|
11847
|
-
const module = __require(jsPath);
|
|
11848
|
-
return module;
|
|
11849
|
-
} catch (error) {
|
|
11850
|
-
logger6.debug("Compiled JS loading failed, trying TypeScript...");
|
|
11851
|
-
}
|
|
11852
|
-
}
|
|
11853
|
-
if (filePath.endsWith(".ts")) {
|
|
11854
|
-
try {
|
|
11855
|
-
logger6.debug("Using TSX runtime loader");
|
|
11856
|
-
const { spawn: spawn2 } = __require("child_process");
|
|
11857
|
-
const tsxCheckResult = await new Promise((resolve4) => {
|
|
11858
|
-
const checkChild = spawn2("npx", ["tsx", "--version"], {
|
|
11859
|
-
stdio: "pipe",
|
|
11860
|
-
cwd: process.cwd()
|
|
11861
|
-
});
|
|
11862
|
-
checkChild.on("close", (code) => {
|
|
11863
|
-
resolve4(code === 0);
|
|
11864
|
-
});
|
|
11865
|
-
checkChild.on("error", () => {
|
|
11866
|
-
resolve4(false);
|
|
11867
|
-
});
|
|
11868
|
-
setTimeout(() => {
|
|
11869
|
-
checkChild.kill();
|
|
11870
|
-
resolve4(false);
|
|
11871
|
-
}, 5e3);
|
|
11872
|
-
});
|
|
11873
|
-
if (!tsxCheckResult) {
|
|
11874
|
-
throw new Error("TSX not available");
|
|
11875
|
-
}
|
|
11876
|
-
const result = await new Promise((resolve4, reject) => {
|
|
11877
|
-
const tsxScript = `
|
|
11878
|
-
async function loadRouter() {
|
|
11879
|
-
try {
|
|
11880
|
-
const module = await import('${pathToFileURL(filePath).href}');
|
|
11881
|
-
const router = module?.AppRouter || module?.default?.AppRouter || module?.default || module?.router;
|
|
11882
|
-
|
|
11883
|
-
if (router && typeof router === 'object') {
|
|
11884
|
-
// Extract safe metadata for CLI use
|
|
11885
|
-
const safeRouter = {
|
|
11886
|
-
config: {
|
|
11887
|
-
baseURL: router.config?.baseURL || '',
|
|
11888
|
-
basePATH: router.config?.basePATH || ''
|
|
11889
|
-
},
|
|
11890
|
-
controllers: {}
|
|
11891
|
-
};
|
|
11892
|
-
|
|
11893
|
-
// Extract controller metadata (no handlers/functions)
|
|
11894
|
-
if (router.controllers && typeof router.controllers === 'object') {
|
|
11895
|
-
for (const [controllerName, controller] of Object.entries(router.controllers)) {
|
|
11896
|
-
if (controller && typeof controller === 'object' && (controller as any).actions) {
|
|
11897
|
-
const safeActions: Record<string, any> = {};
|
|
11898
|
-
|
|
11899
|
-
for (const [actionName, action] of Object.entries((controller as any).actions)) {
|
|
11900
|
-
if (action && typeof action === 'object') {
|
|
11901
|
-
// Extract only metadata, no functions
|
|
11902
|
-
safeActions[actionName] = {
|
|
11903
|
-
path: (action as any).path || '',
|
|
11904
|
-
method: (action as any).method || 'GET',
|
|
11905
|
-
description: (action as any).description,
|
|
11906
|
-
// Keep type inference data if available
|
|
11907
|
-
$Infer: (action as any).$Infer
|
|
11908
|
-
};
|
|
11909
|
-
}
|
|
11910
|
-
}
|
|
11911
|
-
|
|
11912
|
-
safeRouter.controllers[controllerName] = {
|
|
11913
|
-
name: (controller as any).name || controllerName,
|
|
11914
|
-
path: (controller as any).path || '',
|
|
11915
|
-
actions: safeActions
|
|
11916
|
-
};
|
|
11917
|
-
}
|
|
11918
|
-
}
|
|
11919
|
-
}
|
|
11920
|
-
|
|
11921
|
-
console.log('__ROUTER_SUCCESS__' + JSON.stringify(safeRouter));
|
|
11922
|
-
process.exit(0); // Force exit after success
|
|
11923
|
-
} else {
|
|
11924
|
-
console.log('__ROUTER_ERROR__No router found in module');
|
|
11925
|
-
process.exit(1); // Force exit after error
|
|
11926
|
-
}
|
|
11927
|
-
} catch (error) {
|
|
11928
|
-
console.log('__ROUTER_ERROR__' + error.message);
|
|
11929
|
-
process.exit(1); // Force exit after error
|
|
11930
|
-
}
|
|
11931
|
-
}
|
|
11932
|
-
|
|
11933
|
-
loadRouter();
|
|
11934
|
-
`;
|
|
11935
|
-
const child = spawn2("npx", ["tsx", "-e", tsxScript], {
|
|
11936
|
-
stdio: "pipe",
|
|
11937
|
-
cwd: process.cwd(),
|
|
11938
|
-
env: { ...process.env, NODE_NO_WARNINGS: "1" }
|
|
11939
|
-
});
|
|
11940
|
-
let output = "";
|
|
11941
|
-
let errorOutput = "";
|
|
11942
|
-
child.stdout?.on("data", (data) => {
|
|
11943
|
-
output += data.toString();
|
|
11944
|
-
});
|
|
11945
|
-
child.stderr?.on("data", (data) => {
|
|
11946
|
-
errorOutput += data.toString();
|
|
11947
|
-
});
|
|
11948
|
-
child.on("close", (code) => {
|
|
11949
|
-
if (output.includes("__ROUTER_SUCCESS__")) {
|
|
11950
|
-
const resultLine = output.split("\n").find((line) => line.includes("__ROUTER_SUCCESS__"));
|
|
11951
|
-
if (resultLine) {
|
|
11952
|
-
try {
|
|
11953
|
-
const routerData = JSON.parse(resultLine.replace("__ROUTER_SUCCESS__", ""));
|
|
11954
|
-
resolve4(routerData);
|
|
11955
|
-
} catch (e) {
|
|
11956
|
-
reject(new Error("Failed to parse router data: " + e.message));
|
|
11957
|
-
}
|
|
11958
|
-
} else {
|
|
11959
|
-
reject(new Error("Router success marker found but no data"));
|
|
11960
|
-
}
|
|
11961
|
-
} else if (output.includes("__ROUTER_ERROR__")) {
|
|
11962
|
-
const errorLine = output.split("\n").find((line) => line.includes("__ROUTER_ERROR__"));
|
|
11963
|
-
const errorMsg = errorLine ? errorLine.replace("__ROUTER_ERROR__", "") : "Unknown error";
|
|
11964
|
-
reject(new Error("Router loading failed: " + errorMsg));
|
|
11965
|
-
} else {
|
|
11966
|
-
reject(new Error(`TSX execution failed with code ${code}: ${errorOutput || "No output"}`));
|
|
11967
|
-
}
|
|
11968
|
-
});
|
|
11969
|
-
child.on("error", (error) => {
|
|
11970
|
-
reject(new Error("Failed to spawn TSX process: " + error.message));
|
|
11971
|
-
});
|
|
11972
|
-
setTimeout(() => {
|
|
11973
|
-
child.kill();
|
|
11974
|
-
reject(new Error("Timeout loading TypeScript file with TSX"));
|
|
11975
|
-
}, 3e4);
|
|
11976
|
-
});
|
|
11977
|
-
return result;
|
|
11978
|
-
} catch (error) {
|
|
11979
|
-
logger6.debug("TSX runtime loader failed", {}, error);
|
|
11980
|
-
}
|
|
11981
|
-
}
|
|
11982
|
-
return null;
|
|
11983
|
-
}
|
|
11984
|
-
/**
|
|
11985
|
-
* Load router by resolving directory imports to index files
|
|
11986
|
-
*/
|
|
11987
|
-
async loadRouterWithIndexResolution(routerPath) {
|
|
11988
|
-
const logger6 = createChildLogger({ component: "index-resolver" });
|
|
11989
|
-
try {
|
|
11990
|
-
const routerContent = fs7.readFileSync(routerPath, "utf8");
|
|
11991
|
-
const importRegex = /from\s+['\"]([^'\"]+)['\"]/g;
|
|
11992
|
-
let resolvedContent = routerContent;
|
|
11993
|
-
const matches = Array.from(routerContent.matchAll(importRegex));
|
|
11994
|
-
for (const [fullMatch, importPath] of matches) {
|
|
11995
|
-
if (!importPath.startsWith(".") && !importPath.startsWith("@")) {
|
|
11996
|
-
continue;
|
|
11997
|
-
}
|
|
11998
|
-
const basePath = path8.dirname(routerPath);
|
|
11999
|
-
let resolvedPath;
|
|
12000
|
-
if (importPath.startsWith("@/")) {
|
|
12001
|
-
resolvedPath = path8.resolve(process.cwd(), "src", importPath.substring(2));
|
|
12002
|
-
} else if (importPath.startsWith("./")) {
|
|
12003
|
-
resolvedPath = path8.resolve(basePath, importPath);
|
|
12004
|
-
} else {
|
|
12005
|
-
resolvedPath = path8.resolve(basePath, importPath);
|
|
12006
|
-
}
|
|
12007
|
-
let finalPath = importPath;
|
|
12008
|
-
if (!importPath.match(/\\.(js|ts|tsx|jsx)$/)) {
|
|
12009
|
-
const possibleFiles = [
|
|
12010
|
-
resolvedPath + ".ts",
|
|
12011
|
-
resolvedPath + ".tsx",
|
|
12012
|
-
resolvedPath + ".js",
|
|
12013
|
-
resolvedPath + ".jsx"
|
|
12014
|
-
];
|
|
12015
|
-
let fileFound = false;
|
|
12016
|
-
for (const filePath of possibleFiles) {
|
|
12017
|
-
if (fs7.existsSync(filePath)) {
|
|
12018
|
-
const ext = path8.extname(filePath);
|
|
12019
|
-
finalPath = importPath + ext;
|
|
12020
|
-
fileFound = true;
|
|
12021
|
-
break;
|
|
12022
|
-
}
|
|
12023
|
-
}
|
|
12024
|
-
if (!fileFound) {
|
|
12025
|
-
const possibleIndexFiles = [
|
|
12026
|
-
path8.join(resolvedPath, "index.ts"),
|
|
12027
|
-
path8.join(resolvedPath, "index.tsx"),
|
|
12028
|
-
path8.join(resolvedPath, "index.js"),
|
|
12029
|
-
path8.join(resolvedPath, "index.jsx")
|
|
12030
|
-
];
|
|
12031
|
-
for (const indexFile of possibleIndexFiles) {
|
|
12032
|
-
if (fs7.existsSync(indexFile)) {
|
|
12033
|
-
const ext = path8.extname(indexFile);
|
|
12034
|
-
finalPath = importPath + "/index" + ext;
|
|
12035
|
-
break;
|
|
12036
|
-
}
|
|
12037
|
-
}
|
|
12038
|
-
}
|
|
12039
|
-
}
|
|
12040
|
-
const absolutePath = path8.resolve(basePath, finalPath);
|
|
12041
|
-
if (fs7.existsSync(absolutePath)) {
|
|
12042
|
-
const fileUrl = pathToFileURL(absolutePath).href;
|
|
12043
|
-
resolvedContent = resolvedContent.replace(fullMatch, `from '${fileUrl}'`);
|
|
12044
|
-
} else {
|
|
12045
|
-
resolvedContent = resolvedContent.replace(fullMatch, `from '${finalPath}'`);
|
|
12046
|
-
}
|
|
12047
|
-
}
|
|
12048
|
-
const tempFileName = `igniter-temp-${Date.now()}.ts`;
|
|
12049
|
-
const tempFilePath = path8.join(process.cwd(), tempFileName);
|
|
12050
|
-
try {
|
|
12051
|
-
fs7.writeFileSync(tempFilePath, resolvedContent);
|
|
12052
|
-
logger6.debug("Loading resolved module via TSX");
|
|
12053
|
-
const { spawn: spawn2 } = __require("child_process");
|
|
12054
|
-
const result = await new Promise((resolve4, reject) => {
|
|
12055
|
-
const tsxScript = `
|
|
12056
|
-
async function loadResolvedRouter() {
|
|
12057
|
-
try {
|
|
12058
|
-
const module = await import('${pathToFileURL(tempFilePath).href}');
|
|
12059
|
-
const router = module?.AppRouter || module?.default?.AppRouter || module?.default || module?.router;
|
|
12060
|
-
|
|
12061
|
-
if (router && typeof router === 'object') {
|
|
12062
|
-
// Extract safe metadata
|
|
12063
|
-
const safeRouter = {
|
|
12064
|
-
config: {
|
|
12065
|
-
baseURL: router.config?.baseURL || '',
|
|
12066
|
-
basePATH: router.config?.basePATH || ''
|
|
12067
|
-
},
|
|
12068
|
-
controllers: {}
|
|
12069
|
-
};
|
|
12070
|
-
|
|
12071
|
-
if (router.controllers && typeof router.controllers === 'object') {
|
|
12072
|
-
for (const [controllerName, controller] of Object.entries(router.controllers)) {
|
|
12073
|
-
if (controller && typeof controller === 'object' && (controller as any).actions) {
|
|
12074
|
-
const safeActions: Record<string, any> = {};
|
|
12075
|
-
|
|
12076
|
-
for (const [actionName, action] of Object.entries((controller as any).actions)) {
|
|
12077
|
-
if (action && typeof action === 'object') {
|
|
12078
|
-
safeActions[actionName] = {
|
|
12079
|
-
path: (action as any).path || '',
|
|
12080
|
-
method: (action as any).method || 'GET',
|
|
12081
|
-
description: (action as any).description,
|
|
12082
|
-
$Infer: (action as any).$Infer
|
|
12083
|
-
};
|
|
12084
|
-
}
|
|
12085
|
-
}
|
|
12086
|
-
|
|
12087
|
-
safeRouter.controllers[controllerName] = {
|
|
12088
|
-
name: (controller as any).name || controllerName,
|
|
12089
|
-
path: (controller as any).path || '',
|
|
12090
|
-
actions: safeActions
|
|
12091
|
-
};
|
|
12092
|
-
}
|
|
12093
|
-
}
|
|
12094
|
-
}
|
|
12095
|
-
|
|
12096
|
-
console.log('__ROUTER_SUCCESS__' + JSON.stringify(safeRouter));
|
|
12097
|
-
process.exit(0); // Force exit after success
|
|
12098
|
-
} else {
|
|
12099
|
-
console.log('__ROUTER_ERROR__No router found in resolved module');
|
|
12100
|
-
process.exit(1); // Force exit after error
|
|
12101
|
-
}
|
|
12102
|
-
} catch (error) {
|
|
12103
|
-
console.log('__ROUTER_ERROR__' + error.message);
|
|
12104
|
-
process.exit(1); // Force exit after error
|
|
12105
|
-
}
|
|
12106
|
-
}
|
|
12107
|
-
|
|
12108
|
-
loadResolvedRouter();
|
|
12109
|
-
`;
|
|
12110
|
-
const child = spawn2("npx", ["tsx", "-e", tsxScript], {
|
|
12111
|
-
stdio: "pipe",
|
|
12112
|
-
cwd: process.cwd(),
|
|
12113
|
-
env: { ...process.env, NODE_NO_WARNINGS: "1" }
|
|
12114
|
-
});
|
|
12115
|
-
let output = "";
|
|
12116
|
-
let errorOutput = "";
|
|
12117
|
-
child.stdout?.on("data", (data) => {
|
|
12118
|
-
output += data.toString();
|
|
12119
|
-
});
|
|
12120
|
-
child.stderr?.on("data", (data) => {
|
|
12121
|
-
errorOutput += data.toString();
|
|
12122
|
-
});
|
|
12123
|
-
child.on("close", (code) => {
|
|
12124
|
-
if (output.includes("__ROUTER_SUCCESS__")) {
|
|
12125
|
-
const resultLine = output.split("\n").find((line) => line.includes("__ROUTER_SUCCESS__"));
|
|
12126
|
-
if (resultLine) {
|
|
12127
|
-
try {
|
|
12128
|
-
const routerData = JSON.parse(resultLine.replace("__ROUTER_SUCCESS__", ""));
|
|
12129
|
-
resolve4(routerData);
|
|
12130
|
-
} catch (e) {
|
|
12131
|
-
reject(new Error("Failed to parse resolved router data: " + e.message));
|
|
12132
|
-
}
|
|
12133
|
-
} else {
|
|
12134
|
-
reject(new Error("Router success marker found but no data"));
|
|
12135
|
-
}
|
|
12136
|
-
} else if (output.includes("__ROUTER_ERROR__")) {
|
|
12137
|
-
const errorLine = output.split("\n").find((line) => line.includes("__ROUTER_ERROR__"));
|
|
12138
|
-
const errorMsg = errorLine ? errorLine.replace("__ROUTER_ERROR__", "") : "Unknown error";
|
|
12139
|
-
reject(new Error("Router loading failed: " + errorMsg));
|
|
12140
|
-
} else {
|
|
12141
|
-
reject(new Error(`TSX execution failed with code ${code}: ${errorOutput || "No output"}`));
|
|
12142
|
-
}
|
|
12143
|
-
});
|
|
12144
|
-
child.on("error", (error) => {
|
|
12145
|
-
reject(new Error("Failed to spawn TSX process for resolved module: " + error.message));
|
|
12146
|
-
});
|
|
12147
|
-
setTimeout(() => {
|
|
12148
|
-
child.kill();
|
|
12149
|
-
reject(new Error("Timeout loading resolved TypeScript file with TSX"));
|
|
12150
|
-
}, 3e4);
|
|
12151
|
-
});
|
|
12152
|
-
return result;
|
|
12153
|
-
} finally {
|
|
12154
|
-
if (fs7.existsSync(tempFilePath)) {
|
|
12155
|
-
fs7.unlinkSync(tempFilePath);
|
|
12156
|
-
}
|
|
12157
|
-
}
|
|
12158
|
-
} catch (error) {
|
|
12159
|
-
logger6.error("Index resolution failed", { path: routerPath }, error);
|
|
12160
|
-
throw error;
|
|
12161
|
-
}
|
|
12162
|
-
}
|
|
12163
12127
|
/**
|
|
12164
12128
|
* Start watching controller files
|
|
12165
12129
|
*/
|
|
@@ -12174,7 +12138,7 @@ var init_watcher = __esm({
|
|
|
12174
12138
|
persistent: true,
|
|
12175
12139
|
ignoreInitial: false
|
|
12176
12140
|
});
|
|
12177
|
-
await new Promise((
|
|
12141
|
+
await new Promise((resolve5, reject) => {
|
|
12178
12142
|
this.watcher.on("add", this.handleFileChange.bind(this));
|
|
12179
12143
|
this.watcher.on("change", this.handleFileChange.bind(this));
|
|
12180
12144
|
this.watcher.on("unlink", this.handleFileChange.bind(this));
|
|
@@ -12182,7 +12146,7 @@ var init_watcher = __esm({
|
|
|
12182
12146
|
this.logger.success("File watcher is ready", {
|
|
12183
12147
|
watching: this.config.controllerPatterns?.join(", ")
|
|
12184
12148
|
});
|
|
12185
|
-
|
|
12149
|
+
resolve5();
|
|
12186
12150
|
});
|
|
12187
12151
|
this.watcher.on("error", (error) => {
|
|
12188
12152
|
this.logger.error("File watcher error", {}, error);
|
|
@@ -12255,8 +12219,8 @@ var init_watcher = __esm({
|
|
|
12255
12219
|
];
|
|
12256
12220
|
let router = null;
|
|
12257
12221
|
for (const routerPath of possibleRouterPaths) {
|
|
12258
|
-
if (
|
|
12259
|
-
router = await
|
|
12222
|
+
if (fs8.existsSync(routerPath)) {
|
|
12223
|
+
router = await loadRouter(routerPath);
|
|
12260
12224
|
if (router) {
|
|
12261
12225
|
break;
|
|
12262
12226
|
} else {
|
|
@@ -12271,20 +12235,43 @@ var init_watcher = __esm({
|
|
|
12271
12235
|
return;
|
|
12272
12236
|
}
|
|
12273
12237
|
await generateSchemaFromRouter(router, this.config);
|
|
12238
|
+
if (this.config.generateDocs) {
|
|
12239
|
+
await this.generateOpenAPISpec(router);
|
|
12240
|
+
}
|
|
12274
12241
|
} catch (error) {
|
|
12275
12242
|
this.logger.error("Schema generation failed", {}, error);
|
|
12276
12243
|
} finally {
|
|
12277
12244
|
this.isGenerating = false;
|
|
12278
12245
|
}
|
|
12279
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
|
+
}
|
|
12280
12267
|
};
|
|
12281
12268
|
}
|
|
12282
12269
|
});
|
|
12283
12270
|
|
|
12284
12271
|
// src/index.ts
|
|
12285
12272
|
import { Command } from "commander";
|
|
12286
|
-
import * as
|
|
12287
|
-
import * as
|
|
12273
|
+
import * as fs9 from "fs";
|
|
12274
|
+
import * as path10 from "path";
|
|
12288
12275
|
|
|
12289
12276
|
// src/adapters/framework/framework-detector.ts
|
|
12290
12277
|
import * as fs2 from "fs";
|
|
@@ -12736,7 +12723,7 @@ var IGNITER_FEATURES = {
|
|
|
12736
12723
|
name: "Redis Store",
|
|
12737
12724
|
description: "Caching, sessions, and pub/sub messaging",
|
|
12738
12725
|
dependencies: [
|
|
12739
|
-
{ name: "@igniter-js/adapter-redis", version: "
|
|
12726
|
+
{ name: "@igniter-js/adapter-redis", version: "latest" },
|
|
12740
12727
|
{ name: "ioredis", version: "^5.6.1" }
|
|
12741
12728
|
],
|
|
12742
12729
|
devDependencies: [
|
|
@@ -12764,8 +12751,8 @@ var IGNITER_FEATURES = {
|
|
|
12764
12751
|
name: "BullMQ Jobs",
|
|
12765
12752
|
description: "Background task processing and job queues",
|
|
12766
12753
|
dependencies: [
|
|
12767
|
-
{ name: "@igniter-js/adapter-redis", version: "
|
|
12768
|
-
{ name: "@igniter-js/adapter-bullmq", version: "
|
|
12754
|
+
{ name: "@igniter-js/adapter-redis", version: "latest" },
|
|
12755
|
+
{ name: "@igniter-js/adapter-bullmq", version: "latest" },
|
|
12769
12756
|
{ name: "bullmq", version: "^4.0.0" },
|
|
12770
12757
|
{ name: "ioredis", version: "^5.6.1" }
|
|
12771
12758
|
],
|
|
@@ -12793,7 +12780,7 @@ var IGNITER_FEATURES = {
|
|
|
12793
12780
|
name: "MCP Server",
|
|
12794
12781
|
description: "Easy expose your API as a MCP server for AI assistants like Cursor, Claude, etc.",
|
|
12795
12782
|
dependencies: [
|
|
12796
|
-
{ name: "@igniter-js/adapter-mcp", version: "
|
|
12783
|
+
{ name: "@igniter-js/adapter-mcp", version: "latest" },
|
|
12797
12784
|
{ name: "@vercel/mcp-adapter", version: "^0.2.0" },
|
|
12798
12785
|
{ name: "@modelcontextprotocol/sdk", version: "^1.10.2" },
|
|
12799
12786
|
{ name: "ioredis", version: "^5.6.1" }
|
|
@@ -12825,7 +12812,7 @@ var IGNITER_FEATURES = {
|
|
|
12825
12812
|
name: "Enhanced Logging",
|
|
12826
12813
|
description: "Advanced console logging with structured output",
|
|
12827
12814
|
dependencies: [
|
|
12828
|
-
{ name: "@igniter-js/core", version: "
|
|
12815
|
+
{ name: "@igniter-js/core", version: "latest" }
|
|
12829
12816
|
],
|
|
12830
12817
|
envVars: [
|
|
12831
12818
|
{ key: "IGNITER_LOG_LEVEL", value: "info", description: "Logging level (debug, info, warn, error)" }
|
|
@@ -12836,7 +12823,7 @@ var IGNITER_FEATURES = {
|
|
|
12836
12823
|
name: "Telemetry",
|
|
12837
12824
|
description: "Telemetry for tracking requests and errors",
|
|
12838
12825
|
dependencies: [
|
|
12839
|
-
{ name: "@igniter-js/core", version: "
|
|
12826
|
+
{ name: "@igniter-js/core", version: "latest" }
|
|
12840
12827
|
],
|
|
12841
12828
|
envVars: [
|
|
12842
12829
|
{ key: "IGNITER_TELEMETRY_ENABLE_TRACING", value: "true", description: "Enable telemetry tracing" },
|
|
@@ -14468,13 +14455,13 @@ program.command("init").description("Create a new Igniter.js project with intera
|
|
|
14468
14455
|
process.exit(1);
|
|
14469
14456
|
}
|
|
14470
14457
|
}
|
|
14471
|
-
const targetDir = projectName === "." ? process.cwd() :
|
|
14472
|
-
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;
|
|
14473
14460
|
if (!options.force) {
|
|
14474
14461
|
try {
|
|
14475
|
-
const stats = await
|
|
14462
|
+
const stats = await fs9.promises.stat(targetDir);
|
|
14476
14463
|
if (stats.isDirectory()) {
|
|
14477
|
-
const files = await
|
|
14464
|
+
const files = await fs9.promises.readdir(targetDir);
|
|
14478
14465
|
const nonEmptyFiles = files.filter((file) => !file.startsWith("."));
|
|
14479
14466
|
if (nonEmptyFiles.length > 0 && !isExistingProject) {
|
|
14480
14467
|
const shouldOverwrite = await confirmOverwrite(`Directory '${projectName}' is not empty. Continue?`);
|
|
@@ -14503,7 +14490,7 @@ program.command("init").description("Create a new Igniter.js project with intera
|
|
|
14503
14490
|
process.exit(1);
|
|
14504
14491
|
}
|
|
14505
14492
|
});
|
|
14506
|
-
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) => {
|
|
14507
14494
|
const detectedFramework = detectFramework();
|
|
14508
14495
|
const framework = options.framework ? isFrameworkSupported(options.framework) ? options.framework : "generic" : detectedFramework;
|
|
14509
14496
|
const useInteractive = options.interactive !== false;
|
|
@@ -14532,9 +14519,10 @@ program.command("dev").description("Start development mode with framework and Ig
|
|
|
14532
14519
|
});
|
|
14533
14520
|
}
|
|
14534
14521
|
}
|
|
14522
|
+
const docsFlags = options.docs ? ` --docs --docs-output ${options.docsOutput}` : "";
|
|
14535
14523
|
processes.push({
|
|
14536
14524
|
name: "Igniter",
|
|
14537
|
-
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}`,
|
|
14538
14526
|
color: "blue",
|
|
14539
14527
|
cwd: process.cwd()
|
|
14540
14528
|
});
|
|
@@ -14545,7 +14533,7 @@ program.command("dev").description("Start development mode with framework and Ig
|
|
|
14545
14533
|
}
|
|
14546
14534
|
});
|
|
14547
14535
|
var generate = program.command("generate").description("Scaffold new features or generate client schema");
|
|
14548
|
-
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) => {
|
|
14549
14537
|
const startTime = performance.now();
|
|
14550
14538
|
const detectedFramework = detectFramework();
|
|
14551
14539
|
const framework = options.framework ? isFrameworkSupported(options.framework) ? options.framework : "generic" : detectedFramework;
|
|
@@ -14558,7 +14546,9 @@ generate.command("schema").description("Generate client schema from your Igniter
|
|
|
14558
14546
|
framework,
|
|
14559
14547
|
outputDir: options.output,
|
|
14560
14548
|
debug: options.debug,
|
|
14561
|
-
controllerPatterns: ["**/*.controller.{ts,js}"]
|
|
14549
|
+
controllerPatterns: ["**/*.controller.{ts,js}"],
|
|
14550
|
+
generateDocs: options.docs,
|
|
14551
|
+
docsOutputDir: options.docsOutput
|
|
14562
14552
|
});
|
|
14563
14553
|
watcherSpinner.success("Generator loaded");
|
|
14564
14554
|
if (options.watch) {
|
|
@@ -14571,6 +14561,69 @@ generate.command("schema").description("Generate client schema from your Igniter
|
|
|
14571
14561
|
process.exit(0);
|
|
14572
14562
|
}
|
|
14573
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
|
+
});
|
|
14574
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) => {
|
|
14575
14628
|
await handleGenerateFeature(name, options);
|
|
14576
14629
|
});
|