@igniter-js/cli 0.2.5 → 0.2.61
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +506 -455
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +501 -451
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
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,52 +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
|
-
controllers:
|
|
11489
|
+
const schemaResult = {
|
|
11490
|
+
controllers: introspectedControllers,
|
|
11491
|
+
docs: router?.config?.docs
|
|
11486
11492
|
};
|
|
11487
11493
|
return {
|
|
11488
|
-
schema,
|
|
11494
|
+
schema: schemaResult,
|
|
11489
11495
|
stats: {
|
|
11490
|
-
controllers: Object.keys(
|
|
11496
|
+
controllers: Object.keys(introspectedControllers).length,
|
|
11491
11497
|
actions: totalActions
|
|
11492
11498
|
}
|
|
11493
11499
|
};
|
|
11494
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
|
+
}
|
|
11495
11701
|
async function generateSchemaFromRouter(router, config) {
|
|
11496
11702
|
const logger6 = createChildLogger({ component: "generator" });
|
|
11497
11703
|
const startTime = performance.now();
|
|
@@ -11504,7 +11710,7 @@ async function generateSchemaFromRouter(router, config) {
|
|
|
11504
11710
|
extractSpinner = createDetachedSpinner("Extracting router schema...");
|
|
11505
11711
|
extractSpinner.start();
|
|
11506
11712
|
}
|
|
11507
|
-
const { schema, stats } =
|
|
11713
|
+
const { schema, stats } = introspectRouter(router);
|
|
11508
11714
|
if (isInteractiveMode2) {
|
|
11509
11715
|
logger6.success(`Schema extracted - ${stats.controllers} controllers, ${stats.actions} actions`);
|
|
11510
11716
|
} else if (extractSpinner) {
|
|
@@ -11519,7 +11725,7 @@ async function generateSchemaFromRouter(router, config) {
|
|
|
11519
11725
|
}
|
|
11520
11726
|
const outputDir = config.outputDir || "generated";
|
|
11521
11727
|
await ensureDirectoryExists(outputDir);
|
|
11522
|
-
const outputPath =
|
|
11728
|
+
const outputPath = path8.resolve(outputDir);
|
|
11523
11729
|
if (isInteractiveMode2) {
|
|
11524
11730
|
logger6.success(`Output directory ready ${outputPath}`);
|
|
11525
11731
|
} else if (dirSpinner) {
|
|
@@ -11549,8 +11755,8 @@ async function generateSchemaFromRouter(router, config) {
|
|
|
11549
11755
|
let totalSize = 0;
|
|
11550
11756
|
files.forEach((file) => {
|
|
11551
11757
|
const size = getFileSize(file.path);
|
|
11552
|
-
totalSize +=
|
|
11553
|
-
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) });
|
|
11554
11760
|
});
|
|
11555
11761
|
const duration = ((performance.now() - startTime) / 1e3).toFixed(2);
|
|
11556
11762
|
const totalSizeFormatted = totalSize < 1024 ? `${totalSize}b` : totalSize < 1024 * 1024 ? `${(totalSize / 1024).toFixed(1)}kb` : `${(totalSize / (1024 * 1024)).toFixed(1)}mb`;
|
|
@@ -11583,13 +11789,13 @@ export const AppRouterSchema = ${JSON.stringify(schema, null, 2)} as const
|
|
|
11583
11789
|
|
|
11584
11790
|
export type AppRouterSchemaType = typeof AppRouterSchema
|
|
11585
11791
|
`;
|
|
11586
|
-
const filePath =
|
|
11792
|
+
const filePath = path8.join(outputDir, "igniter.schema.ts");
|
|
11587
11793
|
await writeFileWithHeader(filePath, content, config);
|
|
11588
11794
|
return filePath;
|
|
11589
11795
|
}
|
|
11590
11796
|
async function generateClientFile(schema, outputDir, config) {
|
|
11591
|
-
const filePath =
|
|
11592
|
-
if (
|
|
11797
|
+
const filePath = path8.join(outputDir, "igniter.client.ts");
|
|
11798
|
+
if (fs7.existsSync(filePath)) {
|
|
11593
11799
|
const logger6 = createChildLogger({ component: "generator" });
|
|
11594
11800
|
logger6.info("Skipping client file generation, already exists", { path: filePath });
|
|
11595
11801
|
return filePath;
|
|
@@ -11649,7 +11855,7 @@ export const useQueryClient = useIgniterQueryClient<AppRouterType>;
|
|
|
11649
11855
|
async function writeFileWithHeader(filePath, content, config) {
|
|
11650
11856
|
const header = generateFileHeader(config);
|
|
11651
11857
|
const fullContent = header + "\n\n" + content;
|
|
11652
|
-
await
|
|
11858
|
+
await fs7.promises.writeFile(filePath, fullContent, "utf8");
|
|
11653
11859
|
}
|
|
11654
11860
|
function generateFileHeader(config) {
|
|
11655
11861
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -11673,19 +11879,146 @@ function generateFileHeader(config) {
|
|
|
11673
11879
|
}
|
|
11674
11880
|
async function ensureDirectoryExists(dirPath) {
|
|
11675
11881
|
try {
|
|
11676
|
-
await
|
|
11882
|
+
await fs7.promises.access(dirPath);
|
|
11677
11883
|
} catch (error) {
|
|
11678
|
-
await
|
|
11884
|
+
await fs7.promises.mkdir(dirPath, { recursive: true });
|
|
11679
11885
|
}
|
|
11680
11886
|
}
|
|
11681
|
-
var
|
|
11887
|
+
var fs7, path8;
|
|
11682
11888
|
var init_generator = __esm({
|
|
11683
11889
|
"src/adapters/build/generator.ts"() {
|
|
11684
11890
|
"use strict";
|
|
11685
|
-
|
|
11686
|
-
|
|
11891
|
+
fs7 = __toESM(require("fs"));
|
|
11892
|
+
path8 = __toESM(require("path"));
|
|
11687
11893
|
init_logger();
|
|
11688
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
|
+
};
|
|
11689
12022
|
}
|
|
11690
12023
|
});
|
|
11691
12024
|
|
|
@@ -11694,17 +12027,18 @@ var watcher_exports = {};
|
|
|
11694
12027
|
__export(watcher_exports, {
|
|
11695
12028
|
IgniterWatcher: () => IgniterWatcher
|
|
11696
12029
|
});
|
|
11697
|
-
var
|
|
12030
|
+
var fs8, path9, import_chokidar, IgniterWatcher;
|
|
11698
12031
|
var init_watcher = __esm({
|
|
11699
12032
|
"src/adapters/build/watcher.ts"() {
|
|
11700
12033
|
"use strict";
|
|
11701
|
-
|
|
11702
|
-
|
|
11703
|
-
|
|
12034
|
+
fs8 = __toESM(require("fs"));
|
|
12035
|
+
path9 = __toESM(require("path"));
|
|
12036
|
+
init_introspector();
|
|
11704
12037
|
init_generator();
|
|
11705
12038
|
init_logger();
|
|
11706
12039
|
init_spinner();
|
|
11707
12040
|
import_chokidar = __toESM(require("chokidar"));
|
|
12041
|
+
init_openapi_generator();
|
|
11708
12042
|
IgniterWatcher = class {
|
|
11709
12043
|
// Detect interactive mode
|
|
11710
12044
|
constructor(config) {
|
|
@@ -11724,6 +12058,8 @@ var init_watcher = __esm({
|
|
|
11724
12058
|
hotReload: true,
|
|
11725
12059
|
controllerPatterns: ["**/*.controller.{ts,js}"],
|
|
11726
12060
|
debug: false,
|
|
12061
|
+
generateDocs: false,
|
|
12062
|
+
docsOutputDir: "./src/docs",
|
|
11727
12063
|
...config
|
|
11728
12064
|
};
|
|
11729
12065
|
this.isInteractiveMode = !!(process.env.IGNITER_INTERACTIVE_MODE === "true" || process.argv.includes("--interactive"));
|
|
@@ -11785,380 +12121,6 @@ var init_watcher = __esm({
|
|
|
11785
12121
|
process.stdout.write("\n");
|
|
11786
12122
|
}
|
|
11787
12123
|
}
|
|
11788
|
-
/**
|
|
11789
|
-
* Load router from file with simplified approach
|
|
11790
|
-
*/
|
|
11791
|
-
async loadRouter(routerPath) {
|
|
11792
|
-
const logger6 = createChildLogger({ component: "router-loader" });
|
|
11793
|
-
const fullPath = path8.resolve(process.cwd(), routerPath);
|
|
11794
|
-
logger6.debug("Loading router", { path: routerPath });
|
|
11795
|
-
try {
|
|
11796
|
-
const module2 = await this.loadWithTypeScriptSupport(fullPath);
|
|
11797
|
-
if (module2) {
|
|
11798
|
-
if (module2?.config && module2?.controllers) {
|
|
11799
|
-
const controllerCount = Object.keys(module2.controllers || {}).length;
|
|
11800
|
-
return module2;
|
|
11801
|
-
}
|
|
11802
|
-
const router = module2?.AppRouter || module2?.default?.AppRouter || module2?.default || module2?.router;
|
|
11803
|
-
if (router && typeof router === "object") {
|
|
11804
|
-
const controllerCount = Object.keys(router.controllers || {}).length;
|
|
11805
|
-
return router;
|
|
11806
|
-
} else {
|
|
11807
|
-
logger6.debug("Available exports", {
|
|
11808
|
-
exports: Object.keys(module2 || {})
|
|
11809
|
-
});
|
|
11810
|
-
}
|
|
11811
|
-
} else {
|
|
11812
|
-
}
|
|
11813
|
-
const fallbackSpinner = createDetachedSpinner("Trying fallback loading method...");
|
|
11814
|
-
fallbackSpinner.start();
|
|
11815
|
-
const fallbackModule = await this.loadRouterWithIndexResolution(fullPath);
|
|
11816
|
-
if (fallbackModule) {
|
|
11817
|
-
if (fallbackModule?.config && fallbackModule?.controllers) {
|
|
11818
|
-
const controllerCount = Object.keys(fallbackModule.controllers || {}).length;
|
|
11819
|
-
fallbackSpinner.success(`Router loaded successfully - ${controllerCount} controllers`);
|
|
11820
|
-
return fallbackModule;
|
|
11821
|
-
}
|
|
11822
|
-
const router = fallbackModule?.AppRouter || fallbackModule?.default?.AppRouter || fallbackModule?.default || fallbackModule?.router;
|
|
11823
|
-
if (router && typeof router === "object") {
|
|
11824
|
-
const controllerCount = Object.keys(router.controllers || {}).length;
|
|
11825
|
-
fallbackSpinner.success(`Router loaded successfully - ${controllerCount} controllers`);
|
|
11826
|
-
return router;
|
|
11827
|
-
}
|
|
11828
|
-
}
|
|
11829
|
-
fallbackSpinner.error("Could not load router");
|
|
11830
|
-
} catch (error) {
|
|
11831
|
-
logger6.error("Failed to load router", { path: routerPath }, error);
|
|
11832
|
-
}
|
|
11833
|
-
return null;
|
|
11834
|
-
}
|
|
11835
|
-
/**
|
|
11836
|
-
* Load TypeScript files using TSX runtime loader
|
|
11837
|
-
* This is the NEW robust approach that replaces the problematic transpilation
|
|
11838
|
-
*/
|
|
11839
|
-
async loadWithTypeScriptSupport(filePath) {
|
|
11840
|
-
const logger6 = createChildLogger({ component: "tsx-loader" });
|
|
11841
|
-
const jsPath = filePath.replace(/\.ts$/, ".js");
|
|
11842
|
-
if (fs7.existsSync(jsPath)) {
|
|
11843
|
-
try {
|
|
11844
|
-
logger6.debug("Using compiled JS version");
|
|
11845
|
-
delete require.cache[jsPath];
|
|
11846
|
-
const module2 = require(jsPath);
|
|
11847
|
-
return module2;
|
|
11848
|
-
} catch (error) {
|
|
11849
|
-
logger6.debug("Compiled JS loading failed, trying TypeScript...");
|
|
11850
|
-
}
|
|
11851
|
-
}
|
|
11852
|
-
if (filePath.endsWith(".ts")) {
|
|
11853
|
-
try {
|
|
11854
|
-
logger6.debug("Using TSX runtime loader");
|
|
11855
|
-
const { spawn: spawn2 } = require("child_process");
|
|
11856
|
-
const tsxCheckResult = await new Promise((resolve4) => {
|
|
11857
|
-
const checkChild = spawn2("npx", ["tsx", "--version"], {
|
|
11858
|
-
stdio: "pipe",
|
|
11859
|
-
cwd: process.cwd()
|
|
11860
|
-
});
|
|
11861
|
-
checkChild.on("close", (code) => {
|
|
11862
|
-
resolve4(code === 0);
|
|
11863
|
-
});
|
|
11864
|
-
checkChild.on("error", () => {
|
|
11865
|
-
resolve4(false);
|
|
11866
|
-
});
|
|
11867
|
-
setTimeout(() => {
|
|
11868
|
-
checkChild.kill();
|
|
11869
|
-
resolve4(false);
|
|
11870
|
-
}, 5e3);
|
|
11871
|
-
});
|
|
11872
|
-
if (!tsxCheckResult) {
|
|
11873
|
-
throw new Error("TSX not available");
|
|
11874
|
-
}
|
|
11875
|
-
const result = await new Promise((resolve4, reject) => {
|
|
11876
|
-
const tsxScript = `
|
|
11877
|
-
async function loadRouter() {
|
|
11878
|
-
try {
|
|
11879
|
-
const module = await import('${(0, import_url.pathToFileURL)(filePath).href}');
|
|
11880
|
-
const router = module?.AppRouter || module?.default?.AppRouter || module?.default || module?.router;
|
|
11881
|
-
|
|
11882
|
-
if (router && typeof router === 'object') {
|
|
11883
|
-
// Extract safe metadata for CLI use
|
|
11884
|
-
const safeRouter = {
|
|
11885
|
-
config: {
|
|
11886
|
-
baseURL: router.config?.baseURL || '',
|
|
11887
|
-
basePATH: router.config?.basePATH || ''
|
|
11888
|
-
},
|
|
11889
|
-
controllers: {}
|
|
11890
|
-
};
|
|
11891
|
-
|
|
11892
|
-
// Extract controller metadata (no handlers/functions)
|
|
11893
|
-
if (router.controllers && typeof router.controllers === 'object') {
|
|
11894
|
-
for (const [controllerName, controller] of Object.entries(router.controllers)) {
|
|
11895
|
-
if (controller && typeof controller === 'object' && (controller as any).actions) {
|
|
11896
|
-
const safeActions: Record<string, any> = {};
|
|
11897
|
-
|
|
11898
|
-
for (const [actionName, action] of Object.entries((controller as any).actions)) {
|
|
11899
|
-
if (action && typeof action === 'object') {
|
|
11900
|
-
// Extract only metadata, no functions
|
|
11901
|
-
safeActions[actionName] = {
|
|
11902
|
-
path: (action as any).path || '',
|
|
11903
|
-
method: (action as any).method || 'GET',
|
|
11904
|
-
description: (action as any).description,
|
|
11905
|
-
// Keep type inference data if available
|
|
11906
|
-
$Infer: (action as any).$Infer
|
|
11907
|
-
};
|
|
11908
|
-
}
|
|
11909
|
-
}
|
|
11910
|
-
|
|
11911
|
-
safeRouter.controllers[controllerName] = {
|
|
11912
|
-
name: (controller as any).name || controllerName,
|
|
11913
|
-
path: (controller as any).path || '',
|
|
11914
|
-
actions: safeActions
|
|
11915
|
-
};
|
|
11916
|
-
}
|
|
11917
|
-
}
|
|
11918
|
-
}
|
|
11919
|
-
|
|
11920
|
-
console.log('__ROUTER_SUCCESS__' + JSON.stringify(safeRouter));
|
|
11921
|
-
process.exit(0); // Force exit after success
|
|
11922
|
-
} else {
|
|
11923
|
-
console.log('__ROUTER_ERROR__No router found in module');
|
|
11924
|
-
process.exit(1); // Force exit after error
|
|
11925
|
-
}
|
|
11926
|
-
} catch (error) {
|
|
11927
|
-
console.log('__ROUTER_ERROR__' + error.message);
|
|
11928
|
-
process.exit(1); // Force exit after error
|
|
11929
|
-
}
|
|
11930
|
-
}
|
|
11931
|
-
|
|
11932
|
-
loadRouter();
|
|
11933
|
-
`;
|
|
11934
|
-
const child = spawn2("npx", ["tsx", "-e", tsxScript], {
|
|
11935
|
-
stdio: "pipe",
|
|
11936
|
-
cwd: process.cwd(),
|
|
11937
|
-
env: { ...process.env, NODE_NO_WARNINGS: "1" }
|
|
11938
|
-
});
|
|
11939
|
-
let output = "";
|
|
11940
|
-
let errorOutput = "";
|
|
11941
|
-
child.stdout?.on("data", (data) => {
|
|
11942
|
-
output += data.toString();
|
|
11943
|
-
});
|
|
11944
|
-
child.stderr?.on("data", (data) => {
|
|
11945
|
-
errorOutput += data.toString();
|
|
11946
|
-
});
|
|
11947
|
-
child.on("close", (code) => {
|
|
11948
|
-
if (output.includes("__ROUTER_SUCCESS__")) {
|
|
11949
|
-
const resultLine = output.split("\n").find((line) => line.includes("__ROUTER_SUCCESS__"));
|
|
11950
|
-
if (resultLine) {
|
|
11951
|
-
try {
|
|
11952
|
-
const routerData = JSON.parse(resultLine.replace("__ROUTER_SUCCESS__", ""));
|
|
11953
|
-
resolve4(routerData);
|
|
11954
|
-
} catch (e) {
|
|
11955
|
-
reject(new Error("Failed to parse router data: " + e.message));
|
|
11956
|
-
}
|
|
11957
|
-
} else {
|
|
11958
|
-
reject(new Error("Router success marker found but no data"));
|
|
11959
|
-
}
|
|
11960
|
-
} else if (output.includes("__ROUTER_ERROR__")) {
|
|
11961
|
-
const errorLine = output.split("\n").find((line) => line.includes("__ROUTER_ERROR__"));
|
|
11962
|
-
const errorMsg = errorLine ? errorLine.replace("__ROUTER_ERROR__", "") : "Unknown error";
|
|
11963
|
-
reject(new Error("Router loading failed: " + errorMsg));
|
|
11964
|
-
} else {
|
|
11965
|
-
reject(new Error(`TSX execution failed with code ${code}: ${errorOutput || "No output"}`));
|
|
11966
|
-
}
|
|
11967
|
-
});
|
|
11968
|
-
child.on("error", (error) => {
|
|
11969
|
-
reject(new Error("Failed to spawn TSX process: " + error.message));
|
|
11970
|
-
});
|
|
11971
|
-
setTimeout(() => {
|
|
11972
|
-
child.kill();
|
|
11973
|
-
reject(new Error("Timeout loading TypeScript file with TSX"));
|
|
11974
|
-
}, 3e4);
|
|
11975
|
-
});
|
|
11976
|
-
return result;
|
|
11977
|
-
} catch (error) {
|
|
11978
|
-
logger6.debug("TSX runtime loader failed", {}, error);
|
|
11979
|
-
}
|
|
11980
|
-
}
|
|
11981
|
-
return null;
|
|
11982
|
-
}
|
|
11983
|
-
/**
|
|
11984
|
-
* Load router by resolving directory imports to index files
|
|
11985
|
-
*/
|
|
11986
|
-
async loadRouterWithIndexResolution(routerPath) {
|
|
11987
|
-
const logger6 = createChildLogger({ component: "index-resolver" });
|
|
11988
|
-
try {
|
|
11989
|
-
const routerContent = fs7.readFileSync(routerPath, "utf8");
|
|
11990
|
-
const importRegex = /from\s+['\"]([^'\"]+)['\"]/g;
|
|
11991
|
-
let resolvedContent = routerContent;
|
|
11992
|
-
const matches = Array.from(routerContent.matchAll(importRegex));
|
|
11993
|
-
for (const [fullMatch, importPath] of matches) {
|
|
11994
|
-
if (!importPath.startsWith(".") && !importPath.startsWith("@")) {
|
|
11995
|
-
continue;
|
|
11996
|
-
}
|
|
11997
|
-
const basePath = path8.dirname(routerPath);
|
|
11998
|
-
let resolvedPath;
|
|
11999
|
-
if (importPath.startsWith("@/")) {
|
|
12000
|
-
resolvedPath = path8.resolve(process.cwd(), "src", importPath.substring(2));
|
|
12001
|
-
} else if (importPath.startsWith("./")) {
|
|
12002
|
-
resolvedPath = path8.resolve(basePath, importPath);
|
|
12003
|
-
} else {
|
|
12004
|
-
resolvedPath = path8.resolve(basePath, importPath);
|
|
12005
|
-
}
|
|
12006
|
-
let finalPath = importPath;
|
|
12007
|
-
if (!importPath.match(/\\.(js|ts|tsx|jsx)$/)) {
|
|
12008
|
-
const possibleFiles = [
|
|
12009
|
-
resolvedPath + ".ts",
|
|
12010
|
-
resolvedPath + ".tsx",
|
|
12011
|
-
resolvedPath + ".js",
|
|
12012
|
-
resolvedPath + ".jsx"
|
|
12013
|
-
];
|
|
12014
|
-
let fileFound = false;
|
|
12015
|
-
for (const filePath of possibleFiles) {
|
|
12016
|
-
if (fs7.existsSync(filePath)) {
|
|
12017
|
-
const ext = path8.extname(filePath);
|
|
12018
|
-
finalPath = importPath + ext;
|
|
12019
|
-
fileFound = true;
|
|
12020
|
-
break;
|
|
12021
|
-
}
|
|
12022
|
-
}
|
|
12023
|
-
if (!fileFound) {
|
|
12024
|
-
const possibleIndexFiles = [
|
|
12025
|
-
path8.join(resolvedPath, "index.ts"),
|
|
12026
|
-
path8.join(resolvedPath, "index.tsx"),
|
|
12027
|
-
path8.join(resolvedPath, "index.js"),
|
|
12028
|
-
path8.join(resolvedPath, "index.jsx")
|
|
12029
|
-
];
|
|
12030
|
-
for (const indexFile of possibleIndexFiles) {
|
|
12031
|
-
if (fs7.existsSync(indexFile)) {
|
|
12032
|
-
const ext = path8.extname(indexFile);
|
|
12033
|
-
finalPath = importPath + "/index" + ext;
|
|
12034
|
-
break;
|
|
12035
|
-
}
|
|
12036
|
-
}
|
|
12037
|
-
}
|
|
12038
|
-
}
|
|
12039
|
-
const absolutePath = path8.resolve(basePath, finalPath);
|
|
12040
|
-
if (fs7.existsSync(absolutePath)) {
|
|
12041
|
-
const fileUrl = (0, import_url.pathToFileURL)(absolutePath).href;
|
|
12042
|
-
resolvedContent = resolvedContent.replace(fullMatch, `from '${fileUrl}'`);
|
|
12043
|
-
} else {
|
|
12044
|
-
resolvedContent = resolvedContent.replace(fullMatch, `from '${finalPath}'`);
|
|
12045
|
-
}
|
|
12046
|
-
}
|
|
12047
|
-
const tempFileName = `igniter-temp-${Date.now()}.ts`;
|
|
12048
|
-
const tempFilePath = path8.join(process.cwd(), tempFileName);
|
|
12049
|
-
try {
|
|
12050
|
-
fs7.writeFileSync(tempFilePath, resolvedContent);
|
|
12051
|
-
logger6.debug("Loading resolved module via TSX");
|
|
12052
|
-
const { spawn: spawn2 } = require("child_process");
|
|
12053
|
-
const result = await new Promise((resolve4, reject) => {
|
|
12054
|
-
const tsxScript = `
|
|
12055
|
-
async function loadResolvedRouter() {
|
|
12056
|
-
try {
|
|
12057
|
-
const module = await import('${(0, import_url.pathToFileURL)(tempFilePath).href}');
|
|
12058
|
-
const router = module?.AppRouter || module?.default?.AppRouter || module?.default || module?.router;
|
|
12059
|
-
|
|
12060
|
-
if (router && typeof router === 'object') {
|
|
12061
|
-
// Extract safe metadata
|
|
12062
|
-
const safeRouter = {
|
|
12063
|
-
config: {
|
|
12064
|
-
baseURL: router.config?.baseURL || '',
|
|
12065
|
-
basePATH: router.config?.basePATH || ''
|
|
12066
|
-
},
|
|
12067
|
-
controllers: {}
|
|
12068
|
-
};
|
|
12069
|
-
|
|
12070
|
-
if (router.controllers && typeof router.controllers === 'object') {
|
|
12071
|
-
for (const [controllerName, controller] of Object.entries(router.controllers)) {
|
|
12072
|
-
if (controller && typeof controller === 'object' && (controller as any).actions) {
|
|
12073
|
-
const safeActions: Record<string, any> = {};
|
|
12074
|
-
|
|
12075
|
-
for (const [actionName, action] of Object.entries((controller as any).actions)) {
|
|
12076
|
-
if (action && typeof action === 'object') {
|
|
12077
|
-
safeActions[actionName] = {
|
|
12078
|
-
path: (action as any).path || '',
|
|
12079
|
-
method: (action as any).method || 'GET',
|
|
12080
|
-
description: (action as any).description,
|
|
12081
|
-
$Infer: (action as any).$Infer
|
|
12082
|
-
};
|
|
12083
|
-
}
|
|
12084
|
-
}
|
|
12085
|
-
|
|
12086
|
-
safeRouter.controllers[controllerName] = {
|
|
12087
|
-
name: (controller as any).name || controllerName,
|
|
12088
|
-
path: (controller as any).path || '',
|
|
12089
|
-
actions: safeActions
|
|
12090
|
-
};
|
|
12091
|
-
}
|
|
12092
|
-
}
|
|
12093
|
-
}
|
|
12094
|
-
|
|
12095
|
-
console.log('__ROUTER_SUCCESS__' + JSON.stringify(safeRouter));
|
|
12096
|
-
process.exit(0); // Force exit after success
|
|
12097
|
-
} else {
|
|
12098
|
-
console.log('__ROUTER_ERROR__No router found in resolved module');
|
|
12099
|
-
process.exit(1); // Force exit after error
|
|
12100
|
-
}
|
|
12101
|
-
} catch (error) {
|
|
12102
|
-
console.log('__ROUTER_ERROR__' + error.message);
|
|
12103
|
-
process.exit(1); // Force exit after error
|
|
12104
|
-
}
|
|
12105
|
-
}
|
|
12106
|
-
|
|
12107
|
-
loadResolvedRouter();
|
|
12108
|
-
`;
|
|
12109
|
-
const child = spawn2("npx", ["tsx", "-e", tsxScript], {
|
|
12110
|
-
stdio: "pipe",
|
|
12111
|
-
cwd: process.cwd(),
|
|
12112
|
-
env: { ...process.env, NODE_NO_WARNINGS: "1" }
|
|
12113
|
-
});
|
|
12114
|
-
let output = "";
|
|
12115
|
-
let errorOutput = "";
|
|
12116
|
-
child.stdout?.on("data", (data) => {
|
|
12117
|
-
output += data.toString();
|
|
12118
|
-
});
|
|
12119
|
-
child.stderr?.on("data", (data) => {
|
|
12120
|
-
errorOutput += data.toString();
|
|
12121
|
-
});
|
|
12122
|
-
child.on("close", (code) => {
|
|
12123
|
-
if (output.includes("__ROUTER_SUCCESS__")) {
|
|
12124
|
-
const resultLine = output.split("\n").find((line) => line.includes("__ROUTER_SUCCESS__"));
|
|
12125
|
-
if (resultLine) {
|
|
12126
|
-
try {
|
|
12127
|
-
const routerData = JSON.parse(resultLine.replace("__ROUTER_SUCCESS__", ""));
|
|
12128
|
-
resolve4(routerData);
|
|
12129
|
-
} catch (e) {
|
|
12130
|
-
reject(new Error("Failed to parse resolved router data: " + e.message));
|
|
12131
|
-
}
|
|
12132
|
-
} else {
|
|
12133
|
-
reject(new Error("Router success marker found but no data"));
|
|
12134
|
-
}
|
|
12135
|
-
} else if (output.includes("__ROUTER_ERROR__")) {
|
|
12136
|
-
const errorLine = output.split("\n").find((line) => line.includes("__ROUTER_ERROR__"));
|
|
12137
|
-
const errorMsg = errorLine ? errorLine.replace("__ROUTER_ERROR__", "") : "Unknown error";
|
|
12138
|
-
reject(new Error("Router loading failed: " + errorMsg));
|
|
12139
|
-
} else {
|
|
12140
|
-
reject(new Error(`TSX execution failed with code ${code}: ${errorOutput || "No output"}`));
|
|
12141
|
-
}
|
|
12142
|
-
});
|
|
12143
|
-
child.on("error", (error) => {
|
|
12144
|
-
reject(new Error("Failed to spawn TSX process for resolved module: " + error.message));
|
|
12145
|
-
});
|
|
12146
|
-
setTimeout(() => {
|
|
12147
|
-
child.kill();
|
|
12148
|
-
reject(new Error("Timeout loading resolved TypeScript file with TSX"));
|
|
12149
|
-
}, 3e4);
|
|
12150
|
-
});
|
|
12151
|
-
return result;
|
|
12152
|
-
} finally {
|
|
12153
|
-
if (fs7.existsSync(tempFilePath)) {
|
|
12154
|
-
fs7.unlinkSync(tempFilePath);
|
|
12155
|
-
}
|
|
12156
|
-
}
|
|
12157
|
-
} catch (error) {
|
|
12158
|
-
logger6.error("Index resolution failed", { path: routerPath }, error);
|
|
12159
|
-
throw error;
|
|
12160
|
-
}
|
|
12161
|
-
}
|
|
12162
12124
|
/**
|
|
12163
12125
|
* Start watching controller files
|
|
12164
12126
|
*/
|
|
@@ -12173,7 +12135,7 @@ var init_watcher = __esm({
|
|
|
12173
12135
|
persistent: true,
|
|
12174
12136
|
ignoreInitial: false
|
|
12175
12137
|
});
|
|
12176
|
-
await new Promise((
|
|
12138
|
+
await new Promise((resolve5, reject) => {
|
|
12177
12139
|
this.watcher.on("add", this.handleFileChange.bind(this));
|
|
12178
12140
|
this.watcher.on("change", this.handleFileChange.bind(this));
|
|
12179
12141
|
this.watcher.on("unlink", this.handleFileChange.bind(this));
|
|
@@ -12181,7 +12143,7 @@ var init_watcher = __esm({
|
|
|
12181
12143
|
this.logger.success("File watcher is ready", {
|
|
12182
12144
|
watching: this.config.controllerPatterns?.join(", ")
|
|
12183
12145
|
});
|
|
12184
|
-
|
|
12146
|
+
resolve5();
|
|
12185
12147
|
});
|
|
12186
12148
|
this.watcher.on("error", (error) => {
|
|
12187
12149
|
this.logger.error("File watcher error", {}, error);
|
|
@@ -12254,8 +12216,8 @@ var init_watcher = __esm({
|
|
|
12254
12216
|
];
|
|
12255
12217
|
let router = null;
|
|
12256
12218
|
for (const routerPath of possibleRouterPaths) {
|
|
12257
|
-
if (
|
|
12258
|
-
router = await
|
|
12219
|
+
if (fs8.existsSync(routerPath)) {
|
|
12220
|
+
router = await loadRouter(routerPath);
|
|
12259
12221
|
if (router) {
|
|
12260
12222
|
break;
|
|
12261
12223
|
} else {
|
|
@@ -12270,20 +12232,43 @@ var init_watcher = __esm({
|
|
|
12270
12232
|
return;
|
|
12271
12233
|
}
|
|
12272
12234
|
await generateSchemaFromRouter(router, this.config);
|
|
12235
|
+
if (this.config.generateDocs) {
|
|
12236
|
+
await this.generateOpenAPISpec(router);
|
|
12237
|
+
}
|
|
12273
12238
|
} catch (error) {
|
|
12274
12239
|
this.logger.error("Schema generation failed", {}, error);
|
|
12275
12240
|
} finally {
|
|
12276
12241
|
this.isGenerating = false;
|
|
12277
12242
|
}
|
|
12278
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
|
+
}
|
|
12279
12264
|
};
|
|
12280
12265
|
}
|
|
12281
12266
|
});
|
|
12282
12267
|
|
|
12283
12268
|
// src/index.ts
|
|
12284
12269
|
var import_commander = require("commander");
|
|
12285
|
-
var
|
|
12286
|
-
var
|
|
12270
|
+
var fs9 = __toESM(require("fs"));
|
|
12271
|
+
var path10 = __toESM(require("path"));
|
|
12287
12272
|
|
|
12288
12273
|
// src/adapters/framework/framework-detector.ts
|
|
12289
12274
|
var fs2 = __toESM(require("fs"));
|
|
@@ -14467,13 +14452,13 @@ program.command("init").description("Create a new Igniter.js project with intera
|
|
|
14467
14452
|
process.exit(1);
|
|
14468
14453
|
}
|
|
14469
14454
|
}
|
|
14470
|
-
const targetDir = projectName === "." ? process.cwd() :
|
|
14471
|
-
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;
|
|
14472
14457
|
if (!options.force) {
|
|
14473
14458
|
try {
|
|
14474
|
-
const stats = await
|
|
14459
|
+
const stats = await fs9.promises.stat(targetDir);
|
|
14475
14460
|
if (stats.isDirectory()) {
|
|
14476
|
-
const files = await
|
|
14461
|
+
const files = await fs9.promises.readdir(targetDir);
|
|
14477
14462
|
const nonEmptyFiles = files.filter((file) => !file.startsWith("."));
|
|
14478
14463
|
if (nonEmptyFiles.length > 0 && !isExistingProject) {
|
|
14479
14464
|
const shouldOverwrite = await confirmOverwrite(`Directory '${projectName}' is not empty. Continue?`);
|
|
@@ -14502,7 +14487,7 @@ program.command("init").description("Create a new Igniter.js project with intera
|
|
|
14502
14487
|
process.exit(1);
|
|
14503
14488
|
}
|
|
14504
14489
|
});
|
|
14505
|
-
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) => {
|
|
14506
14491
|
const detectedFramework = detectFramework();
|
|
14507
14492
|
const framework = options.framework ? isFrameworkSupported(options.framework) ? options.framework : "generic" : detectedFramework;
|
|
14508
14493
|
const useInteractive = options.interactive !== false;
|
|
@@ -14531,9 +14516,10 @@ program.command("dev").description("Start development mode with framework and Ig
|
|
|
14531
14516
|
});
|
|
14532
14517
|
}
|
|
14533
14518
|
}
|
|
14519
|
+
const docsFlags = options.docs ? ` --docs --docs-output ${options.docsOutput}` : "";
|
|
14534
14520
|
processes.push({
|
|
14535
14521
|
name: "Igniter",
|
|
14536
|
-
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}`,
|
|
14537
14523
|
color: "blue",
|
|
14538
14524
|
cwd: process.cwd()
|
|
14539
14525
|
});
|
|
@@ -14544,7 +14530,7 @@ program.command("dev").description("Start development mode with framework and Ig
|
|
|
14544
14530
|
}
|
|
14545
14531
|
});
|
|
14546
14532
|
var generate = program.command("generate").description("Scaffold new features or generate client schema");
|
|
14547
|
-
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) => {
|
|
14548
14534
|
const startTime = performance.now();
|
|
14549
14535
|
const detectedFramework = detectFramework();
|
|
14550
14536
|
const framework = options.framework ? isFrameworkSupported(options.framework) ? options.framework : "generic" : detectedFramework;
|
|
@@ -14557,7 +14543,9 @@ generate.command("schema").description("Generate client schema from your Igniter
|
|
|
14557
14543
|
framework,
|
|
14558
14544
|
outputDir: options.output,
|
|
14559
14545
|
debug: options.debug,
|
|
14560
|
-
controllerPatterns: ["**/*.controller.{ts,js}"]
|
|
14546
|
+
controllerPatterns: ["**/*.controller.{ts,js}"],
|
|
14547
|
+
generateDocs: options.docs,
|
|
14548
|
+
docsOutputDir: options.docsOutput
|
|
14561
14549
|
});
|
|
14562
14550
|
watcherSpinner.success("Generator loaded");
|
|
14563
14551
|
if (options.watch) {
|
|
@@ -14570,6 +14558,69 @@ generate.command("schema").description("Generate client schema from your Igniter
|
|
|
14570
14558
|
process.exit(0);
|
|
14571
14559
|
}
|
|
14572
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
|
+
});
|
|
14573
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) => {
|
|
14574
14625
|
await handleGenerateFeature(name, options);
|
|
14575
14626
|
});
|