@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.js
CHANGED
|
@@ -5160,7 +5160,7 @@ var require_Command = __commonJS({
|
|
|
5160
5160
|
}
|
|
5161
5161
|
}
|
|
5162
5162
|
initPromise() {
|
|
5163
|
-
const promise = new Promise((
|
|
5163
|
+
const promise = new Promise((resolve5, reject) => {
|
|
5164
5164
|
if (!this.transformed) {
|
|
5165
5165
|
this.transformed = true;
|
|
5166
5166
|
const transformer = _Command._transformer.argument[this.name];
|
|
@@ -5169,7 +5169,7 @@ var require_Command = __commonJS({
|
|
|
5169
5169
|
}
|
|
5170
5170
|
this.stringifyArguments();
|
|
5171
5171
|
}
|
|
5172
|
-
this.resolve = this._convertValue(
|
|
5172
|
+
this.resolve = this._convertValue(resolve5);
|
|
5173
5173
|
if (this.errorStack) {
|
|
5174
5174
|
this.reject = (err) => {
|
|
5175
5175
|
reject((0, utils_1.optimizeErrorStack)(err, this.errorStack.stack, __dirname));
|
|
@@ -5199,7 +5199,7 @@ var require_Command = __commonJS({
|
|
|
5199
5199
|
/**
|
|
5200
5200
|
* Convert the value from buffer to the target encoding.
|
|
5201
5201
|
*/
|
|
5202
|
-
_convertValue(
|
|
5202
|
+
_convertValue(resolve5) {
|
|
5203
5203
|
return (value) => {
|
|
5204
5204
|
try {
|
|
5205
5205
|
const existingTimer = this._commandTimeoutTimer;
|
|
@@ -5207,7 +5207,7 @@ var require_Command = __commonJS({
|
|
|
5207
5207
|
clearTimeout(existingTimer);
|
|
5208
5208
|
delete this._commandTimeoutTimer;
|
|
5209
5209
|
}
|
|
5210
|
-
|
|
5210
|
+
resolve5(this.transformReply(value));
|
|
5211
5211
|
this.isResolved = true;
|
|
5212
5212
|
} catch (err) {
|
|
5213
5213
|
this.reject(err);
|
|
@@ -5462,13 +5462,13 @@ var require_autoPipelining = __commonJS({
|
|
|
5462
5462
|
if (client.isCluster && !client.slots.length) {
|
|
5463
5463
|
if (client.status === "wait")
|
|
5464
5464
|
client.connect().catch(lodash_1.noop);
|
|
5465
|
-
return (0, standard_as_callback_1.default)(new Promise(function(
|
|
5465
|
+
return (0, standard_as_callback_1.default)(new Promise(function(resolve5, reject) {
|
|
5466
5466
|
client.delayUntilReady((err) => {
|
|
5467
5467
|
if (err) {
|
|
5468
5468
|
reject(err);
|
|
5469
5469
|
return;
|
|
5470
5470
|
}
|
|
5471
|
-
executeWithAutoPipelining(client, functionName, commandName, args, null).then(
|
|
5471
|
+
executeWithAutoPipelining(client, functionName, commandName, args, null).then(resolve5, reject);
|
|
5472
5472
|
});
|
|
5473
5473
|
}), callback);
|
|
5474
5474
|
}
|
|
@@ -5485,13 +5485,13 @@ var require_autoPipelining = __commonJS({
|
|
|
5485
5485
|
pipeline[exports2.kExec] = true;
|
|
5486
5486
|
setImmediate(executeAutoPipeline, client, slotKey);
|
|
5487
5487
|
}
|
|
5488
|
-
const autoPipelinePromise = new Promise(function(
|
|
5488
|
+
const autoPipelinePromise = new Promise(function(resolve5, reject) {
|
|
5489
5489
|
pipeline[exports2.kCallbacks].push(function(err, value) {
|
|
5490
5490
|
if (err) {
|
|
5491
5491
|
reject(err);
|
|
5492
5492
|
return;
|
|
5493
5493
|
}
|
|
5494
|
-
|
|
5494
|
+
resolve5(value);
|
|
5495
5495
|
});
|
|
5496
5496
|
if (functionName === "call") {
|
|
5497
5497
|
args.unshift(commandName);
|
|
@@ -5729,8 +5729,8 @@ var require_Pipeline = __commonJS({
|
|
|
5729
5729
|
this[name] = redis[name];
|
|
5730
5730
|
this[name + "Buffer"] = redis[name + "Buffer"];
|
|
5731
5731
|
});
|
|
5732
|
-
this.promise = new Promise((
|
|
5733
|
-
this.resolve =
|
|
5732
|
+
this.promise = new Promise((resolve5, reject) => {
|
|
5733
|
+
this.resolve = resolve5;
|
|
5734
5734
|
this.reject = reject;
|
|
5735
5735
|
});
|
|
5736
5736
|
const _this = this;
|
|
@@ -6025,13 +6025,13 @@ var require_transaction = __commonJS({
|
|
|
6025
6025
|
if (this.isCluster && !this.redis.slots.length) {
|
|
6026
6026
|
if (this.redis.status === "wait")
|
|
6027
6027
|
this.redis.connect().catch(utils_1.noop);
|
|
6028
|
-
return (0, standard_as_callback_1.default)(new Promise((
|
|
6028
|
+
return (0, standard_as_callback_1.default)(new Promise((resolve5, reject) => {
|
|
6029
6029
|
this.redis.delayUntilReady((err) => {
|
|
6030
6030
|
if (err) {
|
|
6031
6031
|
reject(err);
|
|
6032
6032
|
return;
|
|
6033
6033
|
}
|
|
6034
|
-
this.exec(pipeline).then(
|
|
6034
|
+
this.exec(pipeline).then(resolve5, reject);
|
|
6035
6035
|
});
|
|
6036
6036
|
}), callback);
|
|
6037
6037
|
}
|
|
@@ -7207,7 +7207,7 @@ var require_cluster = __commonJS({
|
|
|
7207
7207
|
* Connect to a cluster
|
|
7208
7208
|
*/
|
|
7209
7209
|
connect() {
|
|
7210
|
-
return new Promise((
|
|
7210
|
+
return new Promise((resolve5, reject) => {
|
|
7211
7211
|
if (this.status === "connecting" || this.status === "connect" || this.status === "ready") {
|
|
7212
7212
|
reject(new Error("Redis is already connecting/connected"));
|
|
7213
7213
|
return;
|
|
@@ -7231,7 +7231,7 @@ var require_cluster = __commonJS({
|
|
|
7231
7231
|
this.retryAttempts = 0;
|
|
7232
7232
|
this.executeOfflineCommands();
|
|
7233
7233
|
this.resetNodesRefreshInterval();
|
|
7234
|
-
|
|
7234
|
+
resolve5();
|
|
7235
7235
|
};
|
|
7236
7236
|
let closeListener = void 0;
|
|
7237
7237
|
const refreshListener = () => {
|
|
@@ -7824,7 +7824,7 @@ var require_cluster = __commonJS({
|
|
|
7824
7824
|
});
|
|
7825
7825
|
}
|
|
7826
7826
|
resolveSrv(hostname) {
|
|
7827
|
-
return new Promise((
|
|
7827
|
+
return new Promise((resolve5, reject) => {
|
|
7828
7828
|
this.options.resolveSrv(hostname, (err, records) => {
|
|
7829
7829
|
if (err) {
|
|
7830
7830
|
return reject(err);
|
|
@@ -7838,7 +7838,7 @@ var require_cluster = __commonJS({
|
|
|
7838
7838
|
if (!group.records.length) {
|
|
7839
7839
|
sortedKeys.shift();
|
|
7840
7840
|
}
|
|
7841
|
-
self.dnsLookup(record.name).then((host) =>
|
|
7841
|
+
self.dnsLookup(record.name).then((host) => resolve5({
|
|
7842
7842
|
host,
|
|
7843
7843
|
port: record.port
|
|
7844
7844
|
}), tryFirstOne);
|
|
@@ -7848,14 +7848,14 @@ var require_cluster = __commonJS({
|
|
|
7848
7848
|
});
|
|
7849
7849
|
}
|
|
7850
7850
|
dnsLookup(hostname) {
|
|
7851
|
-
return new Promise((
|
|
7851
|
+
return new Promise((resolve5, reject) => {
|
|
7852
7852
|
this.options.dnsLookup(hostname, (err, address) => {
|
|
7853
7853
|
if (err) {
|
|
7854
7854
|
debug("failed to resolve hostname %s to IP: %s", hostname, err.message);
|
|
7855
7855
|
reject(err);
|
|
7856
7856
|
} else {
|
|
7857
7857
|
debug("resolved hostname %s to IP %s", hostname, address);
|
|
7858
|
-
|
|
7858
|
+
resolve5(address);
|
|
7859
7859
|
}
|
|
7860
7860
|
});
|
|
7861
7861
|
});
|
|
@@ -7973,7 +7973,7 @@ var require_StandaloneConnector = __commonJS({
|
|
|
7973
7973
|
if (options.tls) {
|
|
7974
7974
|
Object.assign(connectionOptions, options.tls);
|
|
7975
7975
|
}
|
|
7976
|
-
return new Promise((
|
|
7976
|
+
return new Promise((resolve5, reject) => {
|
|
7977
7977
|
process.nextTick(() => {
|
|
7978
7978
|
if (!this.connecting) {
|
|
7979
7979
|
reject(new Error(utils_1.CONNECTION_CLOSED_ERROR_MSG));
|
|
@@ -7992,7 +7992,7 @@ var require_StandaloneConnector = __commonJS({
|
|
|
7992
7992
|
this.stream.once("error", (err) => {
|
|
7993
7993
|
this.firstError = err;
|
|
7994
7994
|
});
|
|
7995
|
-
|
|
7995
|
+
resolve5(this.stream);
|
|
7996
7996
|
});
|
|
7997
7997
|
});
|
|
7998
7998
|
}
|
|
@@ -8151,7 +8151,7 @@ var require_SentinelConnector = __commonJS({
|
|
|
8151
8151
|
const error = new Error(errorMsg);
|
|
8152
8152
|
if (typeof retryDelay === "number") {
|
|
8153
8153
|
eventEmitter("error", error);
|
|
8154
|
-
await new Promise((
|
|
8154
|
+
await new Promise((resolve5) => setTimeout(resolve5, retryDelay));
|
|
8155
8155
|
return connectToNext();
|
|
8156
8156
|
} else {
|
|
8157
8157
|
throw error;
|
|
@@ -9457,7 +9457,7 @@ var require_Redis = __commonJS({
|
|
|
9457
9457
|
* be resolved when the connection status is ready.
|
|
9458
9458
|
*/
|
|
9459
9459
|
connect(callback) {
|
|
9460
|
-
const promise = new Promise((
|
|
9460
|
+
const promise = new Promise((resolve5, reject) => {
|
|
9461
9461
|
if (this.status === "connecting" || this.status === "connect" || this.status === "ready") {
|
|
9462
9462
|
reject(new Error("Redis is already connecting/connected"));
|
|
9463
9463
|
return;
|
|
@@ -9536,7 +9536,7 @@ var require_Redis = __commonJS({
|
|
|
9536
9536
|
}
|
|
9537
9537
|
const connectionReadyHandler = function() {
|
|
9538
9538
|
_this.removeListener("close", connectionCloseHandler);
|
|
9539
|
-
|
|
9539
|
+
resolve5();
|
|
9540
9540
|
};
|
|
9541
9541
|
var connectionCloseHandler = function() {
|
|
9542
9542
|
_this.removeListener("ready", connectionReadyHandler);
|
|
@@ -9630,10 +9630,10 @@ var require_Redis = __commonJS({
|
|
|
9630
9630
|
monitor: true,
|
|
9631
9631
|
lazyConnect: false
|
|
9632
9632
|
});
|
|
9633
|
-
return (0, standard_as_callback_1.default)(new Promise(function(
|
|
9633
|
+
return (0, standard_as_callback_1.default)(new Promise(function(resolve5, reject) {
|
|
9634
9634
|
monitorInstance.once("error", reject);
|
|
9635
9635
|
monitorInstance.once("monitoring", function() {
|
|
9636
|
-
|
|
9636
|
+
resolve5(monitorInstance);
|
|
9637
9637
|
});
|
|
9638
9638
|
}), callback);
|
|
9639
9639
|
}
|
|
@@ -11415,8 +11415,8 @@ ${ANSI_COLORS.bold}TOP OPERATIONS${ANSI_COLORS.reset}`);
|
|
|
11415
11415
|
}, 5e3);
|
|
11416
11416
|
this.startApiMonitoring();
|
|
11417
11417
|
await Promise.all(
|
|
11418
|
-
this.processes.map((proc) => new Promise((
|
|
11419
|
-
proc.on("exit",
|
|
11418
|
+
this.processes.map((proc) => new Promise((resolve5) => {
|
|
11419
|
+
proc.on("exit", resolve5);
|
|
11420
11420
|
}))
|
|
11421
11421
|
);
|
|
11422
11422
|
}
|
|
@@ -11446,61 +11446,258 @@ ${ANSI_COLORS.bold}TOP OPERATIONS${ANSI_COLORS.reset}`);
|
|
|
11446
11446
|
}
|
|
11447
11447
|
});
|
|
11448
11448
|
|
|
11449
|
-
// src/adapters/build/
|
|
11450
|
-
|
|
11451
|
-
|
|
11452
|
-
|
|
11453
|
-
|
|
11454
|
-
|
|
11455
|
-
|
|
11456
|
-
|
|
11457
|
-
|
|
11458
|
-
|
|
11459
|
-
}
|
|
11460
|
-
}
|
|
11461
|
-
function extractRouterSchema(router) {
|
|
11462
|
-
const controllersSchema = {};
|
|
11449
|
+
// src/adapters/build/introspector.ts
|
|
11450
|
+
var introspector_exports = {};
|
|
11451
|
+
__export(introspector_exports, {
|
|
11452
|
+
introspectRouter: () => introspectRouter,
|
|
11453
|
+
loadRouter: () => loadRouter
|
|
11454
|
+
});
|
|
11455
|
+
function introspectRouter(router) {
|
|
11456
|
+
const logger6 = createChildLogger({ component: "router-introspector" });
|
|
11457
|
+
logger6.debug("Starting router introspection");
|
|
11458
|
+
const introspectedControllers = {};
|
|
11463
11459
|
let totalActions = 0;
|
|
11464
11460
|
for (const [controllerName, controller] of Object.entries(router.controllers)) {
|
|
11465
|
-
|
|
11461
|
+
logger6.debug(`Introspecting controller: ${controllerName}`);
|
|
11462
|
+
const introspectedActions = {};
|
|
11466
11463
|
if (controller && controller.actions) {
|
|
11467
11464
|
for (const [actionName, action] of Object.entries(controller.actions)) {
|
|
11468
|
-
|
|
11465
|
+
logger6.debug(`Introspecting action: ${controllerName}.${actionName}`, { path: action?.path, method: action?.method });
|
|
11466
|
+
const bodySchemaOutput = action.bodySchema !== void 0 ? action.bodySchema : void 0;
|
|
11467
|
+
const querySchemaOutput = action.querySchema !== void 0 ? action.querySchema : void 0;
|
|
11468
|
+
introspectedActions[actionName] = {
|
|
11469
11469
|
name: action?.name || actionName,
|
|
11470
11470
|
description: action?.description || "",
|
|
11471
11471
|
path: action?.path || "",
|
|
11472
|
-
method: action?.method || "GET"
|
|
11472
|
+
method: action?.method || "GET",
|
|
11473
|
+
isStream: action?.stream || false,
|
|
11474
|
+
...bodySchemaOutput !== void 0 ? { bodySchema: bodySchemaOutput } : {},
|
|
11475
|
+
...querySchemaOutput !== void 0 ? { querySchema: querySchemaOutput } : {}
|
|
11473
11476
|
};
|
|
11474
11477
|
totalActions++;
|
|
11475
11478
|
}
|
|
11479
|
+
} else {
|
|
11480
|
+
logger6.debug(`Controller ${controllerName} has no actions property or is invalid.`);
|
|
11476
11481
|
}
|
|
11477
|
-
|
|
11482
|
+
introspectedControllers[controllerName] = {
|
|
11478
11483
|
name: controller?.name || controllerName,
|
|
11479
11484
|
description: controller?.description || "",
|
|
11480
11485
|
path: controller?.path || "",
|
|
11481
|
-
actions:
|
|
11486
|
+
actions: introspectedActions
|
|
11482
11487
|
};
|
|
11483
11488
|
}
|
|
11484
|
-
const
|
|
11485
|
-
|
|
11486
|
-
|
|
11487
|
-
basePATH: router.config?.basePATH || ""
|
|
11488
|
-
},
|
|
11489
|
-
controllers: controllersSchema,
|
|
11490
|
-
processor: {},
|
|
11491
|
-
handler: {},
|
|
11492
|
-
$context: {},
|
|
11493
|
-
$plugins: {},
|
|
11494
|
-
$caller: {}
|
|
11489
|
+
const schemaResult = {
|
|
11490
|
+
controllers: introspectedControllers,
|
|
11491
|
+
docs: router?.config?.docs
|
|
11495
11492
|
};
|
|
11496
11493
|
return {
|
|
11497
|
-
schema,
|
|
11494
|
+
schema: schemaResult,
|
|
11498
11495
|
stats: {
|
|
11499
|
-
controllers: Object.keys(
|
|
11496
|
+
controllers: Object.keys(introspectedControllers).length,
|
|
11500
11497
|
actions: totalActions
|
|
11501
11498
|
}
|
|
11502
11499
|
};
|
|
11503
11500
|
}
|
|
11501
|
+
async function loadRouter(routerPath) {
|
|
11502
|
+
const logger6 = createChildLogger({ component: "router-loader" });
|
|
11503
|
+
const fullPath = path7.resolve(process.cwd(), routerPath);
|
|
11504
|
+
logger6.debug("Loading router", { path: routerPath });
|
|
11505
|
+
try {
|
|
11506
|
+
const module2 = await loadWithTypeScriptSupport(fullPath);
|
|
11507
|
+
if (module2) {
|
|
11508
|
+
if (module2?.config && module2?.controllers) {
|
|
11509
|
+
return module2;
|
|
11510
|
+
}
|
|
11511
|
+
const router = module2?.AppRouter || module2?.default?.AppRouter || module2?.default || module2?.router;
|
|
11512
|
+
if (router && typeof router === "object") {
|
|
11513
|
+
return router;
|
|
11514
|
+
} else {
|
|
11515
|
+
logger6.debug("Module loaded but no router found", {
|
|
11516
|
+
exports: Object.keys(module2 || {})
|
|
11517
|
+
});
|
|
11518
|
+
}
|
|
11519
|
+
}
|
|
11520
|
+
const fallbackSpinner = createDetachedSpinner("Trying fallback loading method...");
|
|
11521
|
+
fallbackSpinner.start();
|
|
11522
|
+
const fallbackModule = await loadRouterWithIndexResolution(fullPath);
|
|
11523
|
+
if (fallbackModule) {
|
|
11524
|
+
if (fallbackModule?.config && fallbackModule?.controllers) {
|
|
11525
|
+
fallbackSpinner.success(`Router loaded successfully - ${Object.keys(fallbackModule.controllers).length} controllers`);
|
|
11526
|
+
return fallbackModule;
|
|
11527
|
+
}
|
|
11528
|
+
const router = fallbackModule?.AppRouter || fallbackModule?.default?.AppRouter || fallbackModule?.default || fallbackModule?.router;
|
|
11529
|
+
if (router && typeof router === "object") {
|
|
11530
|
+
fallbackSpinner.success(`Router loaded successfully - ${Object.keys(router.controllers).length} controllers`);
|
|
11531
|
+
return router;
|
|
11532
|
+
}
|
|
11533
|
+
}
|
|
11534
|
+
fallbackSpinner.error("Could not load router");
|
|
11535
|
+
} catch (error) {
|
|
11536
|
+
logger6.error("Failed to load router", { path: routerPath }, error);
|
|
11537
|
+
}
|
|
11538
|
+
return null;
|
|
11539
|
+
}
|
|
11540
|
+
async function loadWithTypeScriptSupport(filePath) {
|
|
11541
|
+
const logger6 = createChildLogger({ component: "tsx-loader" });
|
|
11542
|
+
const jsPath = filePath.replace(/\.ts$/, ".js");
|
|
11543
|
+
if (fs6.existsSync(jsPath)) {
|
|
11544
|
+
try {
|
|
11545
|
+
logger6.debug("Using compiled JS version");
|
|
11546
|
+
delete require.cache[jsPath];
|
|
11547
|
+
return require(jsPath);
|
|
11548
|
+
} catch (error) {
|
|
11549
|
+
logger6.debug("Compiled JS loading failed, trying TypeScript...");
|
|
11550
|
+
}
|
|
11551
|
+
}
|
|
11552
|
+
if (filePath.endsWith(".ts")) {
|
|
11553
|
+
try {
|
|
11554
|
+
logger6.debug("Using TSX runtime loader");
|
|
11555
|
+
const { spawn: spawn2 } = require("child_process");
|
|
11556
|
+
const tsxCheckResult = await new Promise((resolve5) => {
|
|
11557
|
+
const checkChild = spawn2("npx", ["tsx", "--version"], {
|
|
11558
|
+
stdio: "pipe",
|
|
11559
|
+
cwd: process.cwd()
|
|
11560
|
+
});
|
|
11561
|
+
checkChild.on("close", (code) => resolve5(code === 0));
|
|
11562
|
+
checkChild.on("error", () => resolve5(false));
|
|
11563
|
+
setTimeout(() => {
|
|
11564
|
+
checkChild.kill();
|
|
11565
|
+
resolve5(false);
|
|
11566
|
+
}, 5e3);
|
|
11567
|
+
});
|
|
11568
|
+
if (!tsxCheckResult) {
|
|
11569
|
+
throw new Error("TSX not available");
|
|
11570
|
+
}
|
|
11571
|
+
return await new Promise((resolve5, reject) => {
|
|
11572
|
+
const tsxScript = `
|
|
11573
|
+
import { zodToJsonSchema } from 'zod-to-json-schema'; // Precisamos importar isso no script filho
|
|
11574
|
+
|
|
11575
|
+
async function loadRouter() {
|
|
11576
|
+
try {
|
|
11577
|
+
const module = await import('${(0, import_url.pathToFileURL)(filePath).href}');
|
|
11578
|
+
const router = module?.AppRouter || module?.default?.AppRouter || module?.default || module?.router;
|
|
11579
|
+
|
|
11580
|
+
if (router && typeof router === 'object') {
|
|
11581
|
+
const safeRouter = {
|
|
11582
|
+
config: {
|
|
11583
|
+
baseURL: router.config?.baseURL || '',
|
|
11584
|
+
basePATH: router.config?.basePATH || '',
|
|
11585
|
+
docs: router.config?.docs || undefined,
|
|
11586
|
+
},
|
|
11587
|
+
controllers: {}
|
|
11588
|
+
};
|
|
11589
|
+
|
|
11590
|
+
if (router.controllers && typeof router.controllers === 'object') {
|
|
11591
|
+
for (const [controllerName, controller] of Object.entries(router.controllers)) {
|
|
11592
|
+
if (controller && typeof controller === 'object' && (controller as any).actions) {
|
|
11593
|
+
const safeActions = {};
|
|
11594
|
+
for (const [actionName, action] of Object.entries((controller as any).actions)) {
|
|
11595
|
+
if (action && typeof action === 'object') {
|
|
11596
|
+
// MODIFICA\xC7\xC3O PRINCIPAL AQUI
|
|
11597
|
+
safeActions[actionName] = {
|
|
11598
|
+
name: (action as any).name,
|
|
11599
|
+
path: (action as any).path,
|
|
11600
|
+
method: (action as any).method,
|
|
11601
|
+
description: (action as any).description,
|
|
11602
|
+
// Convertemos o schema Zod para JSON Schema AQUI, antes do JSON.stringify
|
|
11603
|
+
bodySchema: (action as any).body ? zodToJsonSchema((action as any).body, { target: 'openApi3' }) : undefined,
|
|
11604
|
+
querySchema: (action as any).query ? zodToJsonSchema((action as any).query, { target: 'openApi3' }) : undefined,
|
|
11605
|
+
use: (action as any).use,
|
|
11606
|
+
stream: (action as any).stream,
|
|
11607
|
+
};
|
|
11608
|
+
}
|
|
11609
|
+
}
|
|
11610
|
+
safeRouter.controllers[controllerName] = {
|
|
11611
|
+
name: (controller as any).name || controllerName,
|
|
11612
|
+
path: (controller as any).path || '',
|
|
11613
|
+
description: (controller as any).description,
|
|
11614
|
+
actions: safeActions,
|
|
11615
|
+
};
|
|
11616
|
+
}
|
|
11617
|
+
}
|
|
11618
|
+
}
|
|
11619
|
+
console.log('__ROUTER_SUCCESS__' + JSON.stringify(safeRouter));
|
|
11620
|
+
process.exit(0);
|
|
11621
|
+
} else {
|
|
11622
|
+
console.log('__ROUTER_ERROR__No router found in module');
|
|
11623
|
+
process.exit(1);
|
|
11624
|
+
}
|
|
11625
|
+
} catch (error) {
|
|
11626
|
+
console.log('__ROUTER_ERROR__' + error.message);
|
|
11627
|
+
process.exit(1);
|
|
11628
|
+
}
|
|
11629
|
+
}
|
|
11630
|
+
loadRouter();
|
|
11631
|
+
`;
|
|
11632
|
+
const child = spawn2("npx", ["tsx", "-e", tsxScript], {
|
|
11633
|
+
stdio: "pipe",
|
|
11634
|
+
cwd: process.cwd(),
|
|
11635
|
+
env: { ...process.env, NODE_NO_WARNINGS: "1" }
|
|
11636
|
+
});
|
|
11637
|
+
let output = "";
|
|
11638
|
+
let errorOutput = "";
|
|
11639
|
+
child.stdout?.on("data", (data) => output += data.toString());
|
|
11640
|
+
child.stderr?.on("data", (data) => errorOutput += data.toString());
|
|
11641
|
+
child.on("close", (code) => {
|
|
11642
|
+
if (output.includes("__ROUTER_SUCCESS__")) {
|
|
11643
|
+
const resultLine = output.split("\n").find((line) => line.includes("__ROUTER_SUCCESS__"));
|
|
11644
|
+
if (resultLine) {
|
|
11645
|
+
try {
|
|
11646
|
+
const routerData = JSON.parse(resultLine.replace("__ROUTER_SUCCESS__", ""));
|
|
11647
|
+
resolve5(routerData);
|
|
11648
|
+
} catch (e) {
|
|
11649
|
+
reject(new Error("Failed to parse router data: " + e.message));
|
|
11650
|
+
}
|
|
11651
|
+
} else {
|
|
11652
|
+
reject(new Error("Router success marker found but no data"));
|
|
11653
|
+
}
|
|
11654
|
+
} else if (output.includes("__ROUTER_ERROR__")) {
|
|
11655
|
+
const errorLine = output.split("\n").find((line) => line.includes("__ROUTER_ERROR__"));
|
|
11656
|
+
const errorMsg = errorLine ? errorLine.replace("__ROUTER_ERROR__", "") : "Unknown error";
|
|
11657
|
+
reject(new Error("Router loading failed: " + errorMsg));
|
|
11658
|
+
} else {
|
|
11659
|
+
reject(new Error(`TSX execution failed with code ${code}: ${errorOutput || "No output"}`));
|
|
11660
|
+
}
|
|
11661
|
+
});
|
|
11662
|
+
child.on("error", (error) => reject(new Error("Failed to spawn TSX process: " + error.message)));
|
|
11663
|
+
setTimeout(() => {
|
|
11664
|
+
child.kill();
|
|
11665
|
+
reject(new Error("Timeout loading TypeScript file with TSX"));
|
|
11666
|
+
}, 3e4);
|
|
11667
|
+
});
|
|
11668
|
+
} catch (error) {
|
|
11669
|
+
logger6.debug("TSX runtime loader failed", {}, error);
|
|
11670
|
+
}
|
|
11671
|
+
}
|
|
11672
|
+
return null;
|
|
11673
|
+
}
|
|
11674
|
+
async function loadRouterWithIndexResolution(routerPath) {
|
|
11675
|
+
return null;
|
|
11676
|
+
}
|
|
11677
|
+
var fs6, path7, import_url;
|
|
11678
|
+
var init_introspector = __esm({
|
|
11679
|
+
"src/adapters/build/introspector.ts"() {
|
|
11680
|
+
"use strict";
|
|
11681
|
+
fs6 = __toESM(require("fs"));
|
|
11682
|
+
path7 = __toESM(require("path"));
|
|
11683
|
+
import_url = require("url");
|
|
11684
|
+
init_logger();
|
|
11685
|
+
init_spinner();
|
|
11686
|
+
}
|
|
11687
|
+
});
|
|
11688
|
+
|
|
11689
|
+
// src/adapters/build/generator.ts
|
|
11690
|
+
function getFileSize(filePath) {
|
|
11691
|
+
try {
|
|
11692
|
+
const stats = fs7.statSync(filePath);
|
|
11693
|
+
const bytes = stats.size;
|
|
11694
|
+
if (bytes < 1024) return `${bytes}b`;
|
|
11695
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}kb`;
|
|
11696
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)}mb`;
|
|
11697
|
+
} catch {
|
|
11698
|
+
return "0b";
|
|
11699
|
+
}
|
|
11700
|
+
}
|
|
11504
11701
|
async function generateSchemaFromRouter(router, config) {
|
|
11505
11702
|
const logger6 = createChildLogger({ component: "generator" });
|
|
11506
11703
|
const startTime = performance.now();
|
|
@@ -11513,7 +11710,7 @@ async function generateSchemaFromRouter(router, config) {
|
|
|
11513
11710
|
extractSpinner = createDetachedSpinner("Extracting router schema...");
|
|
11514
11711
|
extractSpinner.start();
|
|
11515
11712
|
}
|
|
11516
|
-
const { schema, stats } =
|
|
11713
|
+
const { schema, stats } = introspectRouter(router);
|
|
11517
11714
|
if (isInteractiveMode2) {
|
|
11518
11715
|
logger6.success(`Schema extracted - ${stats.controllers} controllers, ${stats.actions} actions`);
|
|
11519
11716
|
} else if (extractSpinner) {
|
|
@@ -11528,7 +11725,7 @@ async function generateSchemaFromRouter(router, config) {
|
|
|
11528
11725
|
}
|
|
11529
11726
|
const outputDir = config.outputDir || "generated";
|
|
11530
11727
|
await ensureDirectoryExists(outputDir);
|
|
11531
|
-
const outputPath =
|
|
11728
|
+
const outputPath = path8.resolve(outputDir);
|
|
11532
11729
|
if (isInteractiveMode2) {
|
|
11533
11730
|
logger6.success(`Output directory ready ${outputPath}`);
|
|
11534
11731
|
} else if (dirSpinner) {
|
|
@@ -11558,8 +11755,8 @@ async function generateSchemaFromRouter(router, config) {
|
|
|
11558
11755
|
let totalSize = 0;
|
|
11559
11756
|
files.forEach((file) => {
|
|
11560
11757
|
const size = getFileSize(file.path);
|
|
11561
|
-
totalSize +=
|
|
11562
|
-
logger6.info(`Generated ${file.name}`, { size, path:
|
|
11758
|
+
totalSize += fs7.statSync(file.path).size;
|
|
11759
|
+
logger6.info(`Generated ${file.name}`, { size, path: path8.relative(process.cwd(), file.path) });
|
|
11563
11760
|
});
|
|
11564
11761
|
const duration = ((performance.now() - startTime) / 1e3).toFixed(2);
|
|
11565
11762
|
const totalSizeFormatted = totalSize < 1024 ? `${totalSize}b` : totalSize < 1024 * 1024 ? `${(totalSize / 1024).toFixed(1)}kb` : `${(totalSize / (1024 * 1024)).toFixed(1)}mb`;
|
|
@@ -11592,33 +11789,45 @@ export const AppRouterSchema = ${JSON.stringify(schema, null, 2)} as const
|
|
|
11592
11789
|
|
|
11593
11790
|
export type AppRouterSchemaType = typeof AppRouterSchema
|
|
11594
11791
|
`;
|
|
11595
|
-
const filePath =
|
|
11792
|
+
const filePath = path8.join(outputDir, "igniter.schema.ts");
|
|
11596
11793
|
await writeFileWithHeader(filePath, content, config);
|
|
11597
11794
|
return filePath;
|
|
11598
11795
|
}
|
|
11599
11796
|
async function generateClientFile(schema, outputDir, config) {
|
|
11600
|
-
const
|
|
11797
|
+
const filePath = path8.join(outputDir, "igniter.client.ts");
|
|
11798
|
+
if (fs7.existsSync(filePath)) {
|
|
11799
|
+
const logger6 = createChildLogger({ component: "generator" });
|
|
11800
|
+
logger6.info("Skipping client file generation, already exists", { path: filePath });
|
|
11801
|
+
return filePath;
|
|
11802
|
+
}
|
|
11803
|
+
const content = `* eslint-disable */
|
|
11804
|
+
/* prettier-ignore */
|
|
11805
|
+
|
|
11806
|
+
import { createIgniterClient, useIgniterQueryClient } from '@igniter-js/core/client'
|
|
11601
11807
|
import type { AppRouterType } from './igniter.router'
|
|
11602
11808
|
|
|
11603
11809
|
/**
|
|
11604
|
-
|
|
11605
|
-
|
|
11606
|
-
|
|
11607
|
-
|
|
11608
|
-
|
|
11609
|
-
|
|
11610
|
-
|
|
11611
|
-
|
|
11810
|
+
* Type-safe API client generated from your Igniter router
|
|
11811
|
+
*
|
|
11812
|
+
* Usage in Server Components:
|
|
11813
|
+
* const users = await api.users.list.query()
|
|
11814
|
+
*
|
|
11815
|
+
* Usage in Client Components:
|
|
11816
|
+
* const { data } = api.users.list.useQuery()
|
|
11817
|
+
*
|
|
11818
|
+
* Note: Adjust environment variable prefixes (e.g., NEXT_PUBLIC_, BUN_PUBLIC_, DENO_PUBLIC_, REACT_APP_)
|
|
11819
|
+
* according to your project's framework/runtime (Next.js, Bun, Deno, React/Vite, etc.).
|
|
11820
|
+
*/
|
|
11612
11821
|
export const api = createIgniterClient<AppRouterType>({
|
|
11613
|
-
|
|
11614
|
-
|
|
11615
|
-
|
|
11616
|
-
|
|
11617
|
-
|
|
11618
|
-
|
|
11822
|
+
baseURL: process.env.NEXT_PUBLIC_IGNITER_API_URL, // Adapt for your needs
|
|
11823
|
+
basePath: process.env.NEXT_PUBLIC_IGNITER_API_BASE_PATH,
|
|
11824
|
+
router: () => {
|
|
11825
|
+
if (typeof window === 'undefined') {
|
|
11826
|
+
return require('./igniter.router').AppRouter
|
|
11827
|
+
}
|
|
11619
11828
|
|
|
11620
|
-
|
|
11621
|
-
|
|
11829
|
+
return require('./igniter.schema').AppRouterSchema
|
|
11830
|
+
},
|
|
11622
11831
|
})
|
|
11623
11832
|
|
|
11624
11833
|
/**
|
|
@@ -11640,18 +11849,18 @@ export type ApiClient = typeof api
|
|
|
11640
11849
|
*/
|
|
11641
11850
|
export const useQueryClient = useIgniterQueryClient<AppRouterType>;
|
|
11642
11851
|
`;
|
|
11643
|
-
const filePath = path7.join(outputDir, "igniter.client.ts");
|
|
11644
11852
|
await writeFileWithHeader(filePath, content, config);
|
|
11645
11853
|
return filePath;
|
|
11646
11854
|
}
|
|
11647
11855
|
async function writeFileWithHeader(filePath, content, config) {
|
|
11648
11856
|
const header = generateFileHeader(config);
|
|
11649
11857
|
const fullContent = header + "\n\n" + content;
|
|
11650
|
-
await
|
|
11858
|
+
await fs7.promises.writeFile(filePath, fullContent, "utf8");
|
|
11651
11859
|
}
|
|
11652
11860
|
function generateFileHeader(config) {
|
|
11653
11861
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
11654
|
-
return `/* eslint-disable
|
|
11862
|
+
return `/* eslint-disable */
|
|
11863
|
+
/* prettier-ignore */
|
|
11655
11864
|
|
|
11656
11865
|
/**
|
|
11657
11866
|
* Generated by @igniter-js/cli
|
|
@@ -11670,19 +11879,146 @@ function generateFileHeader(config) {
|
|
|
11670
11879
|
}
|
|
11671
11880
|
async function ensureDirectoryExists(dirPath) {
|
|
11672
11881
|
try {
|
|
11673
|
-
await
|
|
11882
|
+
await fs7.promises.access(dirPath);
|
|
11674
11883
|
} catch (error) {
|
|
11675
|
-
await
|
|
11884
|
+
await fs7.promises.mkdir(dirPath, { recursive: true });
|
|
11676
11885
|
}
|
|
11677
11886
|
}
|
|
11678
|
-
var
|
|
11887
|
+
var fs7, path8;
|
|
11679
11888
|
var init_generator = __esm({
|
|
11680
11889
|
"src/adapters/build/generator.ts"() {
|
|
11681
11890
|
"use strict";
|
|
11682
|
-
|
|
11683
|
-
|
|
11891
|
+
fs7 = __toESM(require("fs"));
|
|
11892
|
+
path8 = __toESM(require("path"));
|
|
11684
11893
|
init_logger();
|
|
11685
11894
|
init_spinner();
|
|
11895
|
+
init_introspector();
|
|
11896
|
+
}
|
|
11897
|
+
});
|
|
11898
|
+
|
|
11899
|
+
// src/adapters/docs/openapi-generator.ts
|
|
11900
|
+
var openapi_generator_exports = {};
|
|
11901
|
+
__export(openapi_generator_exports, {
|
|
11902
|
+
OpenAPIGenerator: () => OpenAPIGenerator
|
|
11903
|
+
});
|
|
11904
|
+
function toPascalCase2(str) {
|
|
11905
|
+
if (!str) return "";
|
|
11906
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
11907
|
+
}
|
|
11908
|
+
var OpenAPIGenerator;
|
|
11909
|
+
var init_openapi_generator = __esm({
|
|
11910
|
+
"src/adapters/docs/openapi-generator.ts"() {
|
|
11911
|
+
"use strict";
|
|
11912
|
+
OpenAPIGenerator = class {
|
|
11913
|
+
constructor(config) {
|
|
11914
|
+
this.schemas = {};
|
|
11915
|
+
this.docsConfig = config || {};
|
|
11916
|
+
}
|
|
11917
|
+
generate(router) {
|
|
11918
|
+
const servers = this.docsConfig.servers && this.docsConfig.servers.length > 0 ? this.docsConfig.servers : [{ url: "http://localhost:3000/api/v1", description: "Default server" }];
|
|
11919
|
+
const spec = {
|
|
11920
|
+
openapi: "3.0.0",
|
|
11921
|
+
info: this.docsConfig.info || { title: "Igniter API", version: "1.0.0" },
|
|
11922
|
+
servers,
|
|
11923
|
+
tags: this.buildTags(router),
|
|
11924
|
+
paths: this.buildPaths(router),
|
|
11925
|
+
components: {
|
|
11926
|
+
schemas: this.schemas,
|
|
11927
|
+
securitySchemes: this.docsConfig.securitySchemes || {}
|
|
11928
|
+
}
|
|
11929
|
+
};
|
|
11930
|
+
return spec;
|
|
11931
|
+
}
|
|
11932
|
+
buildTags(router) {
|
|
11933
|
+
const tags = [];
|
|
11934
|
+
for (const [controllerKey, controller] of Object.entries(router.controllers)) {
|
|
11935
|
+
const tag = {
|
|
11936
|
+
name: controller.name || controllerKey,
|
|
11937
|
+
description: controller.description
|
|
11938
|
+
};
|
|
11939
|
+
tags.push(tag);
|
|
11940
|
+
}
|
|
11941
|
+
return tags;
|
|
11942
|
+
}
|
|
11943
|
+
buildPaths(router) {
|
|
11944
|
+
const paths = {};
|
|
11945
|
+
for (const [controllerKey, controller] of Object.entries(router.controllers)) {
|
|
11946
|
+
for (const [actionKey, action] of Object.entries(controller.actions)) {
|
|
11947
|
+
const actionName = action.name || actionKey;
|
|
11948
|
+
let path11 = `/${controller.path}/${action.path}`;
|
|
11949
|
+
path11 = path11.replace(/\/{2,}/g, "/");
|
|
11950
|
+
if (path11.length > 1 && path11.endsWith("/")) {
|
|
11951
|
+
path11 = path11.slice(0, -1);
|
|
11952
|
+
}
|
|
11953
|
+
if (!paths[path11]) {
|
|
11954
|
+
paths[path11] = {};
|
|
11955
|
+
}
|
|
11956
|
+
const operation = {
|
|
11957
|
+
summary: action.description || actionName,
|
|
11958
|
+
operationId: actionName,
|
|
11959
|
+
tags: [controller.name || controllerKey],
|
|
11960
|
+
parameters: [],
|
|
11961
|
+
requestBody: void 0,
|
|
11962
|
+
responses: {
|
|
11963
|
+
"200": {
|
|
11964
|
+
description: "Success",
|
|
11965
|
+
content: {
|
|
11966
|
+
"application/json": {
|
|
11967
|
+
schema: {}
|
|
11968
|
+
}
|
|
11969
|
+
}
|
|
11970
|
+
}
|
|
11971
|
+
}
|
|
11972
|
+
};
|
|
11973
|
+
const pathParams = action.path.match(/:([a-zA-Z0-9_]+)/g);
|
|
11974
|
+
if (pathParams) {
|
|
11975
|
+
for (const param of pathParams) {
|
|
11976
|
+
const paramName = param.substring(1);
|
|
11977
|
+
operation.parameters.push({
|
|
11978
|
+
name: paramName,
|
|
11979
|
+
in: "path",
|
|
11980
|
+
required: true,
|
|
11981
|
+
schema: { type: "string" }
|
|
11982
|
+
});
|
|
11983
|
+
}
|
|
11984
|
+
}
|
|
11985
|
+
if (action.querySchema) {
|
|
11986
|
+
const querySchemaName = `${toPascalCase2(controller.name || controllerKey)}${toPascalCase2(actionName)}Query`;
|
|
11987
|
+
const queryJsonSchema = action.querySchema;
|
|
11988
|
+
this.schemas[querySchemaName] = queryJsonSchema;
|
|
11989
|
+
const properties = this.schemas[querySchemaName].properties || {};
|
|
11990
|
+
const requiredProps = this.schemas[querySchemaName].required || [];
|
|
11991
|
+
for (const propName of Object.keys(properties)) {
|
|
11992
|
+
operation.parameters.push({
|
|
11993
|
+
name: propName,
|
|
11994
|
+
in: "query",
|
|
11995
|
+
required: requiredProps.includes(propName),
|
|
11996
|
+
schema: properties[propName]
|
|
11997
|
+
});
|
|
11998
|
+
}
|
|
11999
|
+
}
|
|
12000
|
+
if (action.bodySchema) {
|
|
12001
|
+
const bodySchemaName = `${toPascalCase2(controller.name || controllerKey)}${toPascalCase2(actionName)}Body`;
|
|
12002
|
+
const bodyJsonSchema = action.bodySchema;
|
|
12003
|
+
this.schemas[bodySchemaName] = bodyJsonSchema;
|
|
12004
|
+
operation.requestBody = {
|
|
12005
|
+
required: true,
|
|
12006
|
+
content: {
|
|
12007
|
+
"application/json": {
|
|
12008
|
+
schema: { $ref: `#/components/schemas/${bodySchemaName}` }
|
|
12009
|
+
}
|
|
12010
|
+
}
|
|
12011
|
+
};
|
|
12012
|
+
}
|
|
12013
|
+
if (action.isStream) {
|
|
12014
|
+
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.";
|
|
12015
|
+
}
|
|
12016
|
+
paths[path11][action.method.toLowerCase()] = operation;
|
|
12017
|
+
}
|
|
12018
|
+
}
|
|
12019
|
+
return paths;
|
|
12020
|
+
}
|
|
12021
|
+
};
|
|
11686
12022
|
}
|
|
11687
12023
|
});
|
|
11688
12024
|
|
|
@@ -11691,17 +12027,18 @@ var watcher_exports = {};
|
|
|
11691
12027
|
__export(watcher_exports, {
|
|
11692
12028
|
IgniterWatcher: () => IgniterWatcher
|
|
11693
12029
|
});
|
|
11694
|
-
var
|
|
12030
|
+
var fs8, path9, import_chokidar, IgniterWatcher;
|
|
11695
12031
|
var init_watcher = __esm({
|
|
11696
12032
|
"src/adapters/build/watcher.ts"() {
|
|
11697
12033
|
"use strict";
|
|
11698
|
-
|
|
11699
|
-
|
|
11700
|
-
|
|
12034
|
+
fs8 = __toESM(require("fs"));
|
|
12035
|
+
path9 = __toESM(require("path"));
|
|
12036
|
+
init_introspector();
|
|
11701
12037
|
init_generator();
|
|
11702
12038
|
init_logger();
|
|
11703
12039
|
init_spinner();
|
|
11704
12040
|
import_chokidar = __toESM(require("chokidar"));
|
|
12041
|
+
init_openapi_generator();
|
|
11705
12042
|
IgniterWatcher = class {
|
|
11706
12043
|
// Detect interactive mode
|
|
11707
12044
|
constructor(config) {
|
|
@@ -11721,6 +12058,8 @@ var init_watcher = __esm({
|
|
|
11721
12058
|
hotReload: true,
|
|
11722
12059
|
controllerPatterns: ["**/*.controller.{ts,js}"],
|
|
11723
12060
|
debug: false,
|
|
12061
|
+
generateDocs: false,
|
|
12062
|
+
docsOutputDir: "./src/docs",
|
|
11724
12063
|
...config
|
|
11725
12064
|
};
|
|
11726
12065
|
this.isInteractiveMode = !!(process.env.IGNITER_INTERACTIVE_MODE === "true" || process.argv.includes("--interactive"));
|
|
@@ -11782,380 +12121,6 @@ var init_watcher = __esm({
|
|
|
11782
12121
|
process.stdout.write("\n");
|
|
11783
12122
|
}
|
|
11784
12123
|
}
|
|
11785
|
-
/**
|
|
11786
|
-
* Load router from file with simplified approach
|
|
11787
|
-
*/
|
|
11788
|
-
async loadRouter(routerPath) {
|
|
11789
|
-
const logger6 = createChildLogger({ component: "router-loader" });
|
|
11790
|
-
const fullPath = path8.resolve(process.cwd(), routerPath);
|
|
11791
|
-
logger6.debug("Loading router", { path: routerPath });
|
|
11792
|
-
try {
|
|
11793
|
-
const module2 = await this.loadWithTypeScriptSupport(fullPath);
|
|
11794
|
-
if (module2) {
|
|
11795
|
-
if (module2?.config && module2?.controllers) {
|
|
11796
|
-
const controllerCount = Object.keys(module2.controllers || {}).length;
|
|
11797
|
-
return module2;
|
|
11798
|
-
}
|
|
11799
|
-
const router = module2?.AppRouter || module2?.default?.AppRouter || module2?.default || module2?.router;
|
|
11800
|
-
if (router && typeof router === "object") {
|
|
11801
|
-
const controllerCount = Object.keys(router.controllers || {}).length;
|
|
11802
|
-
return router;
|
|
11803
|
-
} else {
|
|
11804
|
-
logger6.debug("Available exports", {
|
|
11805
|
-
exports: Object.keys(module2 || {})
|
|
11806
|
-
});
|
|
11807
|
-
}
|
|
11808
|
-
} else {
|
|
11809
|
-
}
|
|
11810
|
-
const fallbackSpinner = createDetachedSpinner("Trying fallback loading method...");
|
|
11811
|
-
fallbackSpinner.start();
|
|
11812
|
-
const fallbackModule = await this.loadRouterWithIndexResolution(fullPath);
|
|
11813
|
-
if (fallbackModule) {
|
|
11814
|
-
if (fallbackModule?.config && fallbackModule?.controllers) {
|
|
11815
|
-
const controllerCount = Object.keys(fallbackModule.controllers || {}).length;
|
|
11816
|
-
fallbackSpinner.success(`Router loaded successfully - ${controllerCount} controllers`);
|
|
11817
|
-
return fallbackModule;
|
|
11818
|
-
}
|
|
11819
|
-
const router = fallbackModule?.AppRouter || fallbackModule?.default?.AppRouter || fallbackModule?.default || fallbackModule?.router;
|
|
11820
|
-
if (router && typeof router === "object") {
|
|
11821
|
-
const controllerCount = Object.keys(router.controllers || {}).length;
|
|
11822
|
-
fallbackSpinner.success(`Router loaded successfully - ${controllerCount} controllers`);
|
|
11823
|
-
return router;
|
|
11824
|
-
}
|
|
11825
|
-
}
|
|
11826
|
-
fallbackSpinner.error("Could not load router");
|
|
11827
|
-
} catch (error) {
|
|
11828
|
-
logger6.error("Failed to load router", { path: routerPath }, error);
|
|
11829
|
-
}
|
|
11830
|
-
return null;
|
|
11831
|
-
}
|
|
11832
|
-
/**
|
|
11833
|
-
* Load TypeScript files using TSX runtime loader
|
|
11834
|
-
* This is the NEW robust approach that replaces the problematic transpilation
|
|
11835
|
-
*/
|
|
11836
|
-
async loadWithTypeScriptSupport(filePath) {
|
|
11837
|
-
const logger6 = createChildLogger({ component: "tsx-loader" });
|
|
11838
|
-
const jsPath = filePath.replace(/\.ts$/, ".js");
|
|
11839
|
-
if (fs7.existsSync(jsPath)) {
|
|
11840
|
-
try {
|
|
11841
|
-
logger6.debug("Using compiled JS version");
|
|
11842
|
-
delete require.cache[jsPath];
|
|
11843
|
-
const module2 = require(jsPath);
|
|
11844
|
-
return module2;
|
|
11845
|
-
} catch (error) {
|
|
11846
|
-
logger6.debug("Compiled JS loading failed, trying TypeScript...");
|
|
11847
|
-
}
|
|
11848
|
-
}
|
|
11849
|
-
if (filePath.endsWith(".ts")) {
|
|
11850
|
-
try {
|
|
11851
|
-
logger6.debug("Using TSX runtime loader");
|
|
11852
|
-
const { spawn: spawn2 } = require("child_process");
|
|
11853
|
-
const tsxCheckResult = await new Promise((resolve4) => {
|
|
11854
|
-
const checkChild = spawn2("npx", ["tsx", "--version"], {
|
|
11855
|
-
stdio: "pipe",
|
|
11856
|
-
cwd: process.cwd()
|
|
11857
|
-
});
|
|
11858
|
-
checkChild.on("close", (code) => {
|
|
11859
|
-
resolve4(code === 0);
|
|
11860
|
-
});
|
|
11861
|
-
checkChild.on("error", () => {
|
|
11862
|
-
resolve4(false);
|
|
11863
|
-
});
|
|
11864
|
-
setTimeout(() => {
|
|
11865
|
-
checkChild.kill();
|
|
11866
|
-
resolve4(false);
|
|
11867
|
-
}, 5e3);
|
|
11868
|
-
});
|
|
11869
|
-
if (!tsxCheckResult) {
|
|
11870
|
-
throw new Error("TSX not available");
|
|
11871
|
-
}
|
|
11872
|
-
const result = await new Promise((resolve4, reject) => {
|
|
11873
|
-
const tsxScript = `
|
|
11874
|
-
async function loadRouter() {
|
|
11875
|
-
try {
|
|
11876
|
-
const module = await import('${(0, import_url.pathToFileURL)(filePath).href}');
|
|
11877
|
-
const router = module?.AppRouter || module?.default?.AppRouter || module?.default || module?.router;
|
|
11878
|
-
|
|
11879
|
-
if (router && typeof router === 'object') {
|
|
11880
|
-
// Extract safe metadata for CLI use
|
|
11881
|
-
const safeRouter = {
|
|
11882
|
-
config: {
|
|
11883
|
-
baseURL: router.config?.baseURL || '',
|
|
11884
|
-
basePATH: router.config?.basePATH || ''
|
|
11885
|
-
},
|
|
11886
|
-
controllers: {}
|
|
11887
|
-
};
|
|
11888
|
-
|
|
11889
|
-
// Extract controller metadata (no handlers/functions)
|
|
11890
|
-
if (router.controllers && typeof router.controllers === 'object') {
|
|
11891
|
-
for (const [controllerName, controller] of Object.entries(router.controllers)) {
|
|
11892
|
-
if (controller && typeof controller === 'object' && (controller as any).actions) {
|
|
11893
|
-
const safeActions: Record<string, any> = {};
|
|
11894
|
-
|
|
11895
|
-
for (const [actionName, action] of Object.entries((controller as any).actions)) {
|
|
11896
|
-
if (action && typeof action === 'object') {
|
|
11897
|
-
// Extract only metadata, no functions
|
|
11898
|
-
safeActions[actionName] = {
|
|
11899
|
-
path: (action as any).path || '',
|
|
11900
|
-
method: (action as any).method || 'GET',
|
|
11901
|
-
description: (action as any).description,
|
|
11902
|
-
// Keep type inference data if available
|
|
11903
|
-
$Infer: (action as any).$Infer
|
|
11904
|
-
};
|
|
11905
|
-
}
|
|
11906
|
-
}
|
|
11907
|
-
|
|
11908
|
-
safeRouter.controllers[controllerName] = {
|
|
11909
|
-
name: (controller as any).name || controllerName,
|
|
11910
|
-
path: (controller as any).path || '',
|
|
11911
|
-
actions: safeActions
|
|
11912
|
-
};
|
|
11913
|
-
}
|
|
11914
|
-
}
|
|
11915
|
-
}
|
|
11916
|
-
|
|
11917
|
-
console.log('__ROUTER_SUCCESS__' + JSON.stringify(safeRouter));
|
|
11918
|
-
process.exit(0); // Force exit after success
|
|
11919
|
-
} else {
|
|
11920
|
-
console.log('__ROUTER_ERROR__No router found in module');
|
|
11921
|
-
process.exit(1); // Force exit after error
|
|
11922
|
-
}
|
|
11923
|
-
} catch (error) {
|
|
11924
|
-
console.log('__ROUTER_ERROR__' + error.message);
|
|
11925
|
-
process.exit(1); // Force exit after error
|
|
11926
|
-
}
|
|
11927
|
-
}
|
|
11928
|
-
|
|
11929
|
-
loadRouter();
|
|
11930
|
-
`;
|
|
11931
|
-
const child = spawn2("npx", ["tsx", "-e", tsxScript], {
|
|
11932
|
-
stdio: "pipe",
|
|
11933
|
-
cwd: process.cwd(),
|
|
11934
|
-
env: { ...process.env, NODE_NO_WARNINGS: "1" }
|
|
11935
|
-
});
|
|
11936
|
-
let output = "";
|
|
11937
|
-
let errorOutput = "";
|
|
11938
|
-
child.stdout?.on("data", (data) => {
|
|
11939
|
-
output += data.toString();
|
|
11940
|
-
});
|
|
11941
|
-
child.stderr?.on("data", (data) => {
|
|
11942
|
-
errorOutput += data.toString();
|
|
11943
|
-
});
|
|
11944
|
-
child.on("close", (code) => {
|
|
11945
|
-
if (output.includes("__ROUTER_SUCCESS__")) {
|
|
11946
|
-
const resultLine = output.split("\n").find((line) => line.includes("__ROUTER_SUCCESS__"));
|
|
11947
|
-
if (resultLine) {
|
|
11948
|
-
try {
|
|
11949
|
-
const routerData = JSON.parse(resultLine.replace("__ROUTER_SUCCESS__", ""));
|
|
11950
|
-
resolve4(routerData);
|
|
11951
|
-
} catch (e) {
|
|
11952
|
-
reject(new Error("Failed to parse router data: " + e.message));
|
|
11953
|
-
}
|
|
11954
|
-
} else {
|
|
11955
|
-
reject(new Error("Router success marker found but no data"));
|
|
11956
|
-
}
|
|
11957
|
-
} else if (output.includes("__ROUTER_ERROR__")) {
|
|
11958
|
-
const errorLine = output.split("\n").find((line) => line.includes("__ROUTER_ERROR__"));
|
|
11959
|
-
const errorMsg = errorLine ? errorLine.replace("__ROUTER_ERROR__", "") : "Unknown error";
|
|
11960
|
-
reject(new Error("Router loading failed: " + errorMsg));
|
|
11961
|
-
} else {
|
|
11962
|
-
reject(new Error(`TSX execution failed with code ${code}: ${errorOutput || "No output"}`));
|
|
11963
|
-
}
|
|
11964
|
-
});
|
|
11965
|
-
child.on("error", (error) => {
|
|
11966
|
-
reject(new Error("Failed to spawn TSX process: " + error.message));
|
|
11967
|
-
});
|
|
11968
|
-
setTimeout(() => {
|
|
11969
|
-
child.kill();
|
|
11970
|
-
reject(new Error("Timeout loading TypeScript file with TSX"));
|
|
11971
|
-
}, 3e4);
|
|
11972
|
-
});
|
|
11973
|
-
return result;
|
|
11974
|
-
} catch (error) {
|
|
11975
|
-
logger6.debug("TSX runtime loader failed", {}, error);
|
|
11976
|
-
}
|
|
11977
|
-
}
|
|
11978
|
-
return null;
|
|
11979
|
-
}
|
|
11980
|
-
/**
|
|
11981
|
-
* Load router by resolving directory imports to index files
|
|
11982
|
-
*/
|
|
11983
|
-
async loadRouterWithIndexResolution(routerPath) {
|
|
11984
|
-
const logger6 = createChildLogger({ component: "index-resolver" });
|
|
11985
|
-
try {
|
|
11986
|
-
const routerContent = fs7.readFileSync(routerPath, "utf8");
|
|
11987
|
-
const importRegex = /from\s+['\"]([^'\"]+)['\"]/g;
|
|
11988
|
-
let resolvedContent = routerContent;
|
|
11989
|
-
const matches = Array.from(routerContent.matchAll(importRegex));
|
|
11990
|
-
for (const [fullMatch, importPath] of matches) {
|
|
11991
|
-
if (!importPath.startsWith(".") && !importPath.startsWith("@")) {
|
|
11992
|
-
continue;
|
|
11993
|
-
}
|
|
11994
|
-
const basePath = path8.dirname(routerPath);
|
|
11995
|
-
let resolvedPath;
|
|
11996
|
-
if (importPath.startsWith("@/")) {
|
|
11997
|
-
resolvedPath = path8.resolve(process.cwd(), "src", importPath.substring(2));
|
|
11998
|
-
} else if (importPath.startsWith("./")) {
|
|
11999
|
-
resolvedPath = path8.resolve(basePath, importPath);
|
|
12000
|
-
} else {
|
|
12001
|
-
resolvedPath = path8.resolve(basePath, importPath);
|
|
12002
|
-
}
|
|
12003
|
-
let finalPath = importPath;
|
|
12004
|
-
if (!importPath.match(/\\.(js|ts|tsx|jsx)$/)) {
|
|
12005
|
-
const possibleFiles = [
|
|
12006
|
-
resolvedPath + ".ts",
|
|
12007
|
-
resolvedPath + ".tsx",
|
|
12008
|
-
resolvedPath + ".js",
|
|
12009
|
-
resolvedPath + ".jsx"
|
|
12010
|
-
];
|
|
12011
|
-
let fileFound = false;
|
|
12012
|
-
for (const filePath of possibleFiles) {
|
|
12013
|
-
if (fs7.existsSync(filePath)) {
|
|
12014
|
-
const ext = path8.extname(filePath);
|
|
12015
|
-
finalPath = importPath + ext;
|
|
12016
|
-
fileFound = true;
|
|
12017
|
-
break;
|
|
12018
|
-
}
|
|
12019
|
-
}
|
|
12020
|
-
if (!fileFound) {
|
|
12021
|
-
const possibleIndexFiles = [
|
|
12022
|
-
path8.join(resolvedPath, "index.ts"),
|
|
12023
|
-
path8.join(resolvedPath, "index.tsx"),
|
|
12024
|
-
path8.join(resolvedPath, "index.js"),
|
|
12025
|
-
path8.join(resolvedPath, "index.jsx")
|
|
12026
|
-
];
|
|
12027
|
-
for (const indexFile of possibleIndexFiles) {
|
|
12028
|
-
if (fs7.existsSync(indexFile)) {
|
|
12029
|
-
const ext = path8.extname(indexFile);
|
|
12030
|
-
finalPath = importPath + "/index" + ext;
|
|
12031
|
-
break;
|
|
12032
|
-
}
|
|
12033
|
-
}
|
|
12034
|
-
}
|
|
12035
|
-
}
|
|
12036
|
-
const absolutePath = path8.resolve(basePath, finalPath);
|
|
12037
|
-
if (fs7.existsSync(absolutePath)) {
|
|
12038
|
-
const fileUrl = (0, import_url.pathToFileURL)(absolutePath).href;
|
|
12039
|
-
resolvedContent = resolvedContent.replace(fullMatch, `from '${fileUrl}'`);
|
|
12040
|
-
} else {
|
|
12041
|
-
resolvedContent = resolvedContent.replace(fullMatch, `from '${finalPath}'`);
|
|
12042
|
-
}
|
|
12043
|
-
}
|
|
12044
|
-
const tempFileName = `igniter-temp-${Date.now()}.ts`;
|
|
12045
|
-
const tempFilePath = path8.join(process.cwd(), tempFileName);
|
|
12046
|
-
try {
|
|
12047
|
-
fs7.writeFileSync(tempFilePath, resolvedContent);
|
|
12048
|
-
logger6.debug("Loading resolved module via TSX");
|
|
12049
|
-
const { spawn: spawn2 } = require("child_process");
|
|
12050
|
-
const result = await new Promise((resolve4, reject) => {
|
|
12051
|
-
const tsxScript = `
|
|
12052
|
-
async function loadResolvedRouter() {
|
|
12053
|
-
try {
|
|
12054
|
-
const module = await import('${(0, import_url.pathToFileURL)(tempFilePath).href}');
|
|
12055
|
-
const router = module?.AppRouter || module?.default?.AppRouter || module?.default || module?.router;
|
|
12056
|
-
|
|
12057
|
-
if (router && typeof router === 'object') {
|
|
12058
|
-
// Extract safe metadata
|
|
12059
|
-
const safeRouter = {
|
|
12060
|
-
config: {
|
|
12061
|
-
baseURL: router.config?.baseURL || '',
|
|
12062
|
-
basePATH: router.config?.basePATH || ''
|
|
12063
|
-
},
|
|
12064
|
-
controllers: {}
|
|
12065
|
-
};
|
|
12066
|
-
|
|
12067
|
-
if (router.controllers && typeof router.controllers === 'object') {
|
|
12068
|
-
for (const [controllerName, controller] of Object.entries(router.controllers)) {
|
|
12069
|
-
if (controller && typeof controller === 'object' && (controller as any).actions) {
|
|
12070
|
-
const safeActions: Record<string, any> = {};
|
|
12071
|
-
|
|
12072
|
-
for (const [actionName, action] of Object.entries((controller as any).actions)) {
|
|
12073
|
-
if (action && typeof action === 'object') {
|
|
12074
|
-
safeActions[actionName] = {
|
|
12075
|
-
path: (action as any).path || '',
|
|
12076
|
-
method: (action as any).method || 'GET',
|
|
12077
|
-
description: (action as any).description,
|
|
12078
|
-
$Infer: (action as any).$Infer
|
|
12079
|
-
};
|
|
12080
|
-
}
|
|
12081
|
-
}
|
|
12082
|
-
|
|
12083
|
-
safeRouter.controllers[controllerName] = {
|
|
12084
|
-
name: (controller as any).name || controllerName,
|
|
12085
|
-
path: (controller as any).path || '',
|
|
12086
|
-
actions: safeActions
|
|
12087
|
-
};
|
|
12088
|
-
}
|
|
12089
|
-
}
|
|
12090
|
-
}
|
|
12091
|
-
|
|
12092
|
-
console.log('__ROUTER_SUCCESS__' + JSON.stringify(safeRouter));
|
|
12093
|
-
process.exit(0); // Force exit after success
|
|
12094
|
-
} else {
|
|
12095
|
-
console.log('__ROUTER_ERROR__No router found in resolved module');
|
|
12096
|
-
process.exit(1); // Force exit after error
|
|
12097
|
-
}
|
|
12098
|
-
} catch (error) {
|
|
12099
|
-
console.log('__ROUTER_ERROR__' + error.message);
|
|
12100
|
-
process.exit(1); // Force exit after error
|
|
12101
|
-
}
|
|
12102
|
-
}
|
|
12103
|
-
|
|
12104
|
-
loadResolvedRouter();
|
|
12105
|
-
`;
|
|
12106
|
-
const child = spawn2("npx", ["tsx", "-e", tsxScript], {
|
|
12107
|
-
stdio: "pipe",
|
|
12108
|
-
cwd: process.cwd(),
|
|
12109
|
-
env: { ...process.env, NODE_NO_WARNINGS: "1" }
|
|
12110
|
-
});
|
|
12111
|
-
let output = "";
|
|
12112
|
-
let errorOutput = "";
|
|
12113
|
-
child.stdout?.on("data", (data) => {
|
|
12114
|
-
output += data.toString();
|
|
12115
|
-
});
|
|
12116
|
-
child.stderr?.on("data", (data) => {
|
|
12117
|
-
errorOutput += data.toString();
|
|
12118
|
-
});
|
|
12119
|
-
child.on("close", (code) => {
|
|
12120
|
-
if (output.includes("__ROUTER_SUCCESS__")) {
|
|
12121
|
-
const resultLine = output.split("\n").find((line) => line.includes("__ROUTER_SUCCESS__"));
|
|
12122
|
-
if (resultLine) {
|
|
12123
|
-
try {
|
|
12124
|
-
const routerData = JSON.parse(resultLine.replace("__ROUTER_SUCCESS__", ""));
|
|
12125
|
-
resolve4(routerData);
|
|
12126
|
-
} catch (e) {
|
|
12127
|
-
reject(new Error("Failed to parse resolved router data: " + e.message));
|
|
12128
|
-
}
|
|
12129
|
-
} else {
|
|
12130
|
-
reject(new Error("Router success marker found but no data"));
|
|
12131
|
-
}
|
|
12132
|
-
} else if (output.includes("__ROUTER_ERROR__")) {
|
|
12133
|
-
const errorLine = output.split("\n").find((line) => line.includes("__ROUTER_ERROR__"));
|
|
12134
|
-
const errorMsg = errorLine ? errorLine.replace("__ROUTER_ERROR__", "") : "Unknown error";
|
|
12135
|
-
reject(new Error("Router loading failed: " + errorMsg));
|
|
12136
|
-
} else {
|
|
12137
|
-
reject(new Error(`TSX execution failed with code ${code}: ${errorOutput || "No output"}`));
|
|
12138
|
-
}
|
|
12139
|
-
});
|
|
12140
|
-
child.on("error", (error) => {
|
|
12141
|
-
reject(new Error("Failed to spawn TSX process for resolved module: " + error.message));
|
|
12142
|
-
});
|
|
12143
|
-
setTimeout(() => {
|
|
12144
|
-
child.kill();
|
|
12145
|
-
reject(new Error("Timeout loading resolved TypeScript file with TSX"));
|
|
12146
|
-
}, 3e4);
|
|
12147
|
-
});
|
|
12148
|
-
return result;
|
|
12149
|
-
} finally {
|
|
12150
|
-
if (fs7.existsSync(tempFilePath)) {
|
|
12151
|
-
fs7.unlinkSync(tempFilePath);
|
|
12152
|
-
}
|
|
12153
|
-
}
|
|
12154
|
-
} catch (error) {
|
|
12155
|
-
logger6.error("Index resolution failed", { path: routerPath }, error);
|
|
12156
|
-
throw error;
|
|
12157
|
-
}
|
|
12158
|
-
}
|
|
12159
12124
|
/**
|
|
12160
12125
|
* Start watching controller files
|
|
12161
12126
|
*/
|
|
@@ -12170,7 +12135,7 @@ var init_watcher = __esm({
|
|
|
12170
12135
|
persistent: true,
|
|
12171
12136
|
ignoreInitial: false
|
|
12172
12137
|
});
|
|
12173
|
-
await new Promise((
|
|
12138
|
+
await new Promise((resolve5, reject) => {
|
|
12174
12139
|
this.watcher.on("add", this.handleFileChange.bind(this));
|
|
12175
12140
|
this.watcher.on("change", this.handleFileChange.bind(this));
|
|
12176
12141
|
this.watcher.on("unlink", this.handleFileChange.bind(this));
|
|
@@ -12178,7 +12143,7 @@ var init_watcher = __esm({
|
|
|
12178
12143
|
this.logger.success("File watcher is ready", {
|
|
12179
12144
|
watching: this.config.controllerPatterns?.join(", ")
|
|
12180
12145
|
});
|
|
12181
|
-
|
|
12146
|
+
resolve5();
|
|
12182
12147
|
});
|
|
12183
12148
|
this.watcher.on("error", (error) => {
|
|
12184
12149
|
this.logger.error("File watcher error", {}, error);
|
|
@@ -12251,8 +12216,8 @@ var init_watcher = __esm({
|
|
|
12251
12216
|
];
|
|
12252
12217
|
let router = null;
|
|
12253
12218
|
for (const routerPath of possibleRouterPaths) {
|
|
12254
|
-
if (
|
|
12255
|
-
router = await
|
|
12219
|
+
if (fs8.existsSync(routerPath)) {
|
|
12220
|
+
router = await loadRouter(routerPath);
|
|
12256
12221
|
if (router) {
|
|
12257
12222
|
break;
|
|
12258
12223
|
} else {
|
|
@@ -12267,20 +12232,43 @@ var init_watcher = __esm({
|
|
|
12267
12232
|
return;
|
|
12268
12233
|
}
|
|
12269
12234
|
await generateSchemaFromRouter(router, this.config);
|
|
12235
|
+
if (this.config.generateDocs) {
|
|
12236
|
+
await this.generateOpenAPISpec(router);
|
|
12237
|
+
}
|
|
12270
12238
|
} catch (error) {
|
|
12271
12239
|
this.logger.error("Schema generation failed", {}, error);
|
|
12272
12240
|
} finally {
|
|
12273
12241
|
this.isGenerating = false;
|
|
12274
12242
|
}
|
|
12275
12243
|
}
|
|
12244
|
+
/**
|
|
12245
|
+
* Generate OpenAPI specification from router
|
|
12246
|
+
*/
|
|
12247
|
+
async generateOpenAPISpec(router) {
|
|
12248
|
+
try {
|
|
12249
|
+
this.logger.info("Generating OpenAPI specification...");
|
|
12250
|
+
const introspected = introspectRouter(router);
|
|
12251
|
+
const generator = new OpenAPIGenerator(introspected.schema.docs || {});
|
|
12252
|
+
const openApiSpec = generator.generate(introspected.schema);
|
|
12253
|
+
const outputDir = path9.resolve(this.config.docsOutputDir);
|
|
12254
|
+
if (!fs8.existsSync(outputDir)) {
|
|
12255
|
+
fs8.mkdirSync(outputDir, { recursive: true });
|
|
12256
|
+
}
|
|
12257
|
+
const outputPath = path9.join(outputDir, "openapi.json");
|
|
12258
|
+
fs8.writeFileSync(outputPath, JSON.stringify(openApiSpec, null, 2), "utf8");
|
|
12259
|
+
this.logger.success(`OpenAPI specification updated at ${outputPath}`);
|
|
12260
|
+
} catch (error) {
|
|
12261
|
+
this.logger.error("Error generating OpenAPI specification:", error);
|
|
12262
|
+
}
|
|
12263
|
+
}
|
|
12276
12264
|
};
|
|
12277
12265
|
}
|
|
12278
12266
|
});
|
|
12279
12267
|
|
|
12280
12268
|
// src/index.ts
|
|
12281
12269
|
var import_commander = require("commander");
|
|
12282
|
-
var
|
|
12283
|
-
var
|
|
12270
|
+
var fs9 = __toESM(require("fs"));
|
|
12271
|
+
var path10 = __toESM(require("path"));
|
|
12284
12272
|
|
|
12285
12273
|
// src/adapters/framework/framework-detector.ts
|
|
12286
12274
|
var fs2 = __toESM(require("fs"));
|
|
@@ -12732,7 +12720,7 @@ var IGNITER_FEATURES = {
|
|
|
12732
12720
|
name: "Redis Store",
|
|
12733
12721
|
description: "Caching, sessions, and pub/sub messaging",
|
|
12734
12722
|
dependencies: [
|
|
12735
|
-
{ name: "@igniter-js/adapter-redis", version: "
|
|
12723
|
+
{ name: "@igniter-js/adapter-redis", version: "latest" },
|
|
12736
12724
|
{ name: "ioredis", version: "^5.6.1" }
|
|
12737
12725
|
],
|
|
12738
12726
|
devDependencies: [
|
|
@@ -12760,8 +12748,8 @@ var IGNITER_FEATURES = {
|
|
|
12760
12748
|
name: "BullMQ Jobs",
|
|
12761
12749
|
description: "Background task processing and job queues",
|
|
12762
12750
|
dependencies: [
|
|
12763
|
-
{ name: "@igniter-js/adapter-redis", version: "
|
|
12764
|
-
{ name: "@igniter-js/adapter-bullmq", version: "
|
|
12751
|
+
{ name: "@igniter-js/adapter-redis", version: "latest" },
|
|
12752
|
+
{ name: "@igniter-js/adapter-bullmq", version: "latest" },
|
|
12765
12753
|
{ name: "bullmq", version: "^4.0.0" },
|
|
12766
12754
|
{ name: "ioredis", version: "^5.6.1" }
|
|
12767
12755
|
],
|
|
@@ -12789,7 +12777,7 @@ var IGNITER_FEATURES = {
|
|
|
12789
12777
|
name: "MCP Server",
|
|
12790
12778
|
description: "Easy expose your API as a MCP server for AI assistants like Cursor, Claude, etc.",
|
|
12791
12779
|
dependencies: [
|
|
12792
|
-
{ name: "@igniter-js/adapter-mcp", version: "
|
|
12780
|
+
{ name: "@igniter-js/adapter-mcp", version: "latest" },
|
|
12793
12781
|
{ name: "@vercel/mcp-adapter", version: "^0.2.0" },
|
|
12794
12782
|
{ name: "@modelcontextprotocol/sdk", version: "^1.10.2" },
|
|
12795
12783
|
{ name: "ioredis", version: "^5.6.1" }
|
|
@@ -12821,7 +12809,7 @@ var IGNITER_FEATURES = {
|
|
|
12821
12809
|
name: "Enhanced Logging",
|
|
12822
12810
|
description: "Advanced console logging with structured output",
|
|
12823
12811
|
dependencies: [
|
|
12824
|
-
{ name: "@igniter-js/core", version: "
|
|
12812
|
+
{ name: "@igniter-js/core", version: "latest" }
|
|
12825
12813
|
],
|
|
12826
12814
|
envVars: [
|
|
12827
12815
|
{ key: "IGNITER_LOG_LEVEL", value: "info", description: "Logging level (debug, info, warn, error)" }
|
|
@@ -12832,7 +12820,7 @@ var IGNITER_FEATURES = {
|
|
|
12832
12820
|
name: "Telemetry",
|
|
12833
12821
|
description: "Telemetry for tracking requests and errors",
|
|
12834
12822
|
dependencies: [
|
|
12835
|
-
{ name: "@igniter-js/core", version: "
|
|
12823
|
+
{ name: "@igniter-js/core", version: "latest" }
|
|
12836
12824
|
],
|
|
12837
12825
|
envVars: [
|
|
12838
12826
|
{ key: "IGNITER_TELEMETRY_ENABLE_TRACING", value: "true", description: "Enable telemetry tracing" },
|
|
@@ -14464,13 +14452,13 @@ program.command("init").description("Create a new Igniter.js project with intera
|
|
|
14464
14452
|
process.exit(1);
|
|
14465
14453
|
}
|
|
14466
14454
|
}
|
|
14467
|
-
const targetDir = projectName === "." ? process.cwd() :
|
|
14468
|
-
const isExistingProject = await
|
|
14455
|
+
const targetDir = projectName === "." ? process.cwd() : path10.resolve(projectName);
|
|
14456
|
+
const isExistingProject = await fs9.promises.stat(path10.join(targetDir, "package.json")).catch(() => null) !== null;
|
|
14469
14457
|
if (!options.force) {
|
|
14470
14458
|
try {
|
|
14471
|
-
const stats = await
|
|
14459
|
+
const stats = await fs9.promises.stat(targetDir);
|
|
14472
14460
|
if (stats.isDirectory()) {
|
|
14473
|
-
const files = await
|
|
14461
|
+
const files = await fs9.promises.readdir(targetDir);
|
|
14474
14462
|
const nonEmptyFiles = files.filter((file) => !file.startsWith("."));
|
|
14475
14463
|
if (nonEmptyFiles.length > 0 && !isExistingProject) {
|
|
14476
14464
|
const shouldOverwrite = await confirmOverwrite(`Directory '${projectName}' is not empty. Continue?`);
|
|
@@ -14499,7 +14487,7 @@ program.command("init").description("Create a new Igniter.js project with intera
|
|
|
14499
14487
|
process.exit(1);
|
|
14500
14488
|
}
|
|
14501
14489
|
});
|
|
14502
|
-
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) => {
|
|
14490
|
+
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) => {
|
|
14503
14491
|
const detectedFramework = detectFramework();
|
|
14504
14492
|
const framework = options.framework ? isFrameworkSupported(options.framework) ? options.framework : "generic" : detectedFramework;
|
|
14505
14493
|
const useInteractive = options.interactive !== false;
|
|
@@ -14528,9 +14516,10 @@ program.command("dev").description("Start development mode with framework and Ig
|
|
|
14528
14516
|
});
|
|
14529
14517
|
}
|
|
14530
14518
|
}
|
|
14519
|
+
const docsFlags = options.docs ? ` --docs --docs-output ${options.docsOutput}` : "";
|
|
14531
14520
|
processes.push({
|
|
14532
14521
|
name: "Igniter",
|
|
14533
|
-
command: `igniter generate schema --watch --framework ${framework} --output ${options.output}${options.debug ? " --debug" : ""}`,
|
|
14522
|
+
command: `igniter generate schema --watch --framework ${framework} --output ${options.output}${options.debug ? " --debug" : ""}${docsFlags}`,
|
|
14534
14523
|
color: "blue",
|
|
14535
14524
|
cwd: process.cwd()
|
|
14536
14525
|
});
|
|
@@ -14541,7 +14530,7 @@ program.command("dev").description("Start development mode with framework and Ig
|
|
|
14541
14530
|
}
|
|
14542
14531
|
});
|
|
14543
14532
|
var generate = program.command("generate").description("Scaffold new features or generate client schema");
|
|
14544
|
-
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) => {
|
|
14533
|
+
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) => {
|
|
14545
14534
|
const startTime = performance.now();
|
|
14546
14535
|
const detectedFramework = detectFramework();
|
|
14547
14536
|
const framework = options.framework ? isFrameworkSupported(options.framework) ? options.framework : "generic" : detectedFramework;
|
|
@@ -14554,7 +14543,9 @@ generate.command("schema").description("Generate client schema from your Igniter
|
|
|
14554
14543
|
framework,
|
|
14555
14544
|
outputDir: options.output,
|
|
14556
14545
|
debug: options.debug,
|
|
14557
|
-
controllerPatterns: ["**/*.controller.{ts,js}"]
|
|
14546
|
+
controllerPatterns: ["**/*.controller.{ts,js}"],
|
|
14547
|
+
generateDocs: options.docs,
|
|
14548
|
+
docsOutputDir: options.docsOutput
|
|
14558
14549
|
});
|
|
14559
14550
|
watcherSpinner.success("Generator loaded");
|
|
14560
14551
|
if (options.watch) {
|
|
@@ -14567,6 +14558,69 @@ generate.command("schema").description("Generate client schema from your Igniter
|
|
|
14567
14558
|
process.exit(0);
|
|
14568
14559
|
}
|
|
14569
14560
|
});
|
|
14561
|
+
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) => {
|
|
14562
|
+
const docsLogger = createChildLogger({ component: "generate-docs-command" });
|
|
14563
|
+
try {
|
|
14564
|
+
docsLogger.info("Starting OpenAPI documentation generation...");
|
|
14565
|
+
const { loadRouter: loadRouter2, introspectRouter: introspectRouter2 } = await Promise.resolve().then(() => (init_introspector(), introspector_exports));
|
|
14566
|
+
const { OpenAPIGenerator: OpenAPIGenerator2 } = await Promise.resolve().then(() => (init_openapi_generator(), openapi_generator_exports));
|
|
14567
|
+
const possibleRouterPaths = [
|
|
14568
|
+
"src/igniter.router.ts",
|
|
14569
|
+
"src/igniter.router.js",
|
|
14570
|
+
"src/router.ts",
|
|
14571
|
+
"src/router.js",
|
|
14572
|
+
"igniter.router.ts",
|
|
14573
|
+
"igniter.router.js",
|
|
14574
|
+
"router.ts",
|
|
14575
|
+
"router.js"
|
|
14576
|
+
];
|
|
14577
|
+
let router = null;
|
|
14578
|
+
for (const routerPath of possibleRouterPaths) {
|
|
14579
|
+
if (fs9.existsSync(routerPath)) {
|
|
14580
|
+
router = await loadRouter2(routerPath);
|
|
14581
|
+
if (router) {
|
|
14582
|
+
docsLogger.info(`Found router at: ${routerPath}`);
|
|
14583
|
+
break;
|
|
14584
|
+
}
|
|
14585
|
+
}
|
|
14586
|
+
}
|
|
14587
|
+
if (!router) {
|
|
14588
|
+
docsLogger.error("No Igniter router found in your project. Please ensure you have a router file (e.g., src/igniter.router.ts).");
|
|
14589
|
+
process.exit(1);
|
|
14590
|
+
}
|
|
14591
|
+
const introspected = introspectRouter2(router);
|
|
14592
|
+
const generator = new OpenAPIGenerator2(introspected.schema.docs || {});
|
|
14593
|
+
const openApiSpec = generator.generate(introspected.schema);
|
|
14594
|
+
const outputDir = path10.resolve(options.output, "docs");
|
|
14595
|
+
if (!fs9.existsSync(outputDir)) {
|
|
14596
|
+
fs9.mkdirSync(outputDir, { recursive: true });
|
|
14597
|
+
}
|
|
14598
|
+
const outputPath = path10.join(outputDir, "openapi.json");
|
|
14599
|
+
fs9.writeFileSync(outputPath, JSON.stringify(openApiSpec, null, 2), "utf8");
|
|
14600
|
+
docsLogger.success(`OpenAPI specification generated at ${outputPath}`);
|
|
14601
|
+
if (options.ui) {
|
|
14602
|
+
const scalarHtml = `<!doctype html>
|
|
14603
|
+
<html lang="en">
|
|
14604
|
+
<head>
|
|
14605
|
+
<meta charset="utf-8" />
|
|
14606
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
14607
|
+
<title>API Reference</title>
|
|
14608
|
+
</head>
|
|
14609
|
+
<body>
|
|
14610
|
+
<script id="api-reference" data-url="./openapi.json"></script>
|
|
14611
|
+
<script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
|
|
14612
|
+
</body>
|
|
14613
|
+
</html>`;
|
|
14614
|
+
const uiOutputPath = path10.join(outputDir, "index.html");
|
|
14615
|
+
fs9.writeFileSync(uiOutputPath, scalarHtml, "utf8");
|
|
14616
|
+
docsLogger.success(`Scalar UI generated at ${uiOutputPath}`);
|
|
14617
|
+
}
|
|
14618
|
+
} catch (error) {
|
|
14619
|
+
docsLogger.error("Failed to generate OpenAPI documentation:");
|
|
14620
|
+
console.error(error);
|
|
14621
|
+
process.exit(1);
|
|
14622
|
+
}
|
|
14623
|
+
});
|
|
14570
14624
|
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) => {
|
|
14571
14625
|
await handleGenerateFeature(name, options);
|
|
14572
14626
|
});
|