@atlashub/smartstack-cli 3.53.0 → 3.54.0
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/mcp-entry.mjs +657 -140
- package/dist/mcp-entry.mjs.map +1 -1
- package/package.json +1 -1
- package/templates/skills/apex/references/core-seed-data.md +26 -0
- package/templates/skills/apex/references/smartstack-api.md +34 -0
- package/templates/skills/application/references/backend-table-prefix-mapping.md +1 -0
- package/templates/skills/business-analyse/patterns/suggestion-catalog.md +2 -0
package/dist/mcp-entry.mjs
CHANGED
|
@@ -471,8 +471,8 @@ var init_parseUtil = __esm({
|
|
|
471
471
|
init_errors();
|
|
472
472
|
init_en();
|
|
473
473
|
makeIssue = (params) => {
|
|
474
|
-
const { data, path:
|
|
475
|
-
const fullPath = [...
|
|
474
|
+
const { data, path: path31, errorMaps, issueData } = params;
|
|
475
|
+
const fullPath = [...path31, ...issueData.path || []];
|
|
476
476
|
const fullIssue = {
|
|
477
477
|
...issueData,
|
|
478
478
|
path: fullPath
|
|
@@ -786,11 +786,11 @@ var init_types = __esm({
|
|
|
786
786
|
init_parseUtil();
|
|
787
787
|
init_util();
|
|
788
788
|
ParseInputLazyPath = class {
|
|
789
|
-
constructor(parent, value,
|
|
789
|
+
constructor(parent, value, path31, key) {
|
|
790
790
|
this._cachedPath = [];
|
|
791
791
|
this.parent = parent;
|
|
792
792
|
this.data = value;
|
|
793
|
-
this._path =
|
|
793
|
+
this._path = path31;
|
|
794
794
|
this._key = key;
|
|
795
795
|
}
|
|
796
796
|
get path() {
|
|
@@ -4367,10 +4367,10 @@ function assignProp(target, prop, value) {
|
|
|
4367
4367
|
configurable: true
|
|
4368
4368
|
});
|
|
4369
4369
|
}
|
|
4370
|
-
function getElementAtPath(obj,
|
|
4371
|
-
if (!
|
|
4370
|
+
function getElementAtPath(obj, path31) {
|
|
4371
|
+
if (!path31)
|
|
4372
4372
|
return obj;
|
|
4373
|
-
return
|
|
4373
|
+
return path31.reduce((acc, key) => acc?.[key], obj);
|
|
4374
4374
|
}
|
|
4375
4375
|
function promiseAllObject(promisesObj) {
|
|
4376
4376
|
const keys = Object.keys(promisesObj);
|
|
@@ -4619,11 +4619,11 @@ function aborted(x, startIndex = 0) {
|
|
|
4619
4619
|
}
|
|
4620
4620
|
return false;
|
|
4621
4621
|
}
|
|
4622
|
-
function prefixIssues(
|
|
4622
|
+
function prefixIssues(path31, issues) {
|
|
4623
4623
|
return issues.map((iss) => {
|
|
4624
4624
|
var _a;
|
|
4625
4625
|
(_a = iss).path ?? (_a.path = []);
|
|
4626
|
-
iss.path.unshift(
|
|
4626
|
+
iss.path.unshift(path31);
|
|
4627
4627
|
return iss;
|
|
4628
4628
|
});
|
|
4629
4629
|
}
|
|
@@ -14447,8 +14447,8 @@ var require_utils = __commonJS({
|
|
|
14447
14447
|
}
|
|
14448
14448
|
return ind;
|
|
14449
14449
|
}
|
|
14450
|
-
function removeDotSegments(
|
|
14451
|
-
let input =
|
|
14450
|
+
function removeDotSegments(path31) {
|
|
14451
|
+
let input = path31;
|
|
14452
14452
|
const output = [];
|
|
14453
14453
|
let nextSlash = -1;
|
|
14454
14454
|
let len = 0;
|
|
@@ -14648,8 +14648,8 @@ var require_schemes = __commonJS({
|
|
|
14648
14648
|
wsComponent.secure = void 0;
|
|
14649
14649
|
}
|
|
14650
14650
|
if (wsComponent.resourceName) {
|
|
14651
|
-
const [
|
|
14652
|
-
wsComponent.path =
|
|
14651
|
+
const [path31, query] = wsComponent.resourceName.split("?");
|
|
14652
|
+
wsComponent.path = path31 && path31 !== "/" ? path31 : void 0;
|
|
14653
14653
|
wsComponent.query = query;
|
|
14654
14654
|
wsComponent.resourceName = void 0;
|
|
14655
14655
|
}
|
|
@@ -23041,12 +23041,12 @@ var init_esm7 = __esm({
|
|
|
23041
23041
|
/**
|
|
23042
23042
|
* Get the Path object referenced by the string path, resolved from this Path
|
|
23043
23043
|
*/
|
|
23044
|
-
resolve(
|
|
23045
|
-
if (!
|
|
23044
|
+
resolve(path31) {
|
|
23045
|
+
if (!path31) {
|
|
23046
23046
|
return this;
|
|
23047
23047
|
}
|
|
23048
|
-
const rootPath = this.getRootString(
|
|
23049
|
-
const dir =
|
|
23048
|
+
const rootPath = this.getRootString(path31);
|
|
23049
|
+
const dir = path31.substring(rootPath.length);
|
|
23050
23050
|
const dirParts = dir.split(this.splitSep);
|
|
23051
23051
|
const result = rootPath ? this.getRoot(rootPath).#resolveParts(dirParts) : this.#resolveParts(dirParts);
|
|
23052
23052
|
return result;
|
|
@@ -23798,8 +23798,8 @@ var init_esm7 = __esm({
|
|
|
23798
23798
|
/**
|
|
23799
23799
|
* @internal
|
|
23800
23800
|
*/
|
|
23801
|
-
getRootString(
|
|
23802
|
-
return win32.parse(
|
|
23801
|
+
getRootString(path31) {
|
|
23802
|
+
return win32.parse(path31).root;
|
|
23803
23803
|
}
|
|
23804
23804
|
/**
|
|
23805
23805
|
* @internal
|
|
@@ -23845,8 +23845,8 @@ var init_esm7 = __esm({
|
|
|
23845
23845
|
/**
|
|
23846
23846
|
* @internal
|
|
23847
23847
|
*/
|
|
23848
|
-
getRootString(
|
|
23849
|
-
return
|
|
23848
|
+
getRootString(path31) {
|
|
23849
|
+
return path31.startsWith("/") ? "/" : "";
|
|
23850
23850
|
}
|
|
23851
23851
|
/**
|
|
23852
23852
|
* @internal
|
|
@@ -23935,11 +23935,11 @@ var init_esm7 = __esm({
|
|
|
23935
23935
|
/**
|
|
23936
23936
|
* Get the depth of a provided path, string, or the cwd
|
|
23937
23937
|
*/
|
|
23938
|
-
depth(
|
|
23939
|
-
if (typeof
|
|
23940
|
-
|
|
23938
|
+
depth(path31 = this.cwd) {
|
|
23939
|
+
if (typeof path31 === "string") {
|
|
23940
|
+
path31 = this.cwd.resolve(path31);
|
|
23941
23941
|
}
|
|
23942
|
-
return
|
|
23942
|
+
return path31.depth();
|
|
23943
23943
|
}
|
|
23944
23944
|
/**
|
|
23945
23945
|
* Return the cache of child entries. Exposed so subclasses can create
|
|
@@ -24426,9 +24426,9 @@ var init_esm7 = __esm({
|
|
|
24426
24426
|
process3();
|
|
24427
24427
|
return results;
|
|
24428
24428
|
}
|
|
24429
|
-
chdir(
|
|
24429
|
+
chdir(path31 = this.cwd) {
|
|
24430
24430
|
const oldCwd = this.cwd;
|
|
24431
|
-
this.cwd = typeof
|
|
24431
|
+
this.cwd = typeof path31 === "string" ? this.cwd.resolve(path31) : path31;
|
|
24432
24432
|
this.cwd[setAsCwd](oldCwd);
|
|
24433
24433
|
}
|
|
24434
24434
|
};
|
|
@@ -24809,8 +24809,8 @@ var init_processor = __esm({
|
|
|
24809
24809
|
}
|
|
24810
24810
|
// match, absolute, ifdir
|
|
24811
24811
|
entries() {
|
|
24812
|
-
return [...this.store.entries()].map(([
|
|
24813
|
-
|
|
24812
|
+
return [...this.store.entries()].map(([path31, n]) => [
|
|
24813
|
+
path31,
|
|
24814
24814
|
!!(n & 2),
|
|
24815
24815
|
!!(n & 1)
|
|
24816
24816
|
]);
|
|
@@ -25025,9 +25025,9 @@ var init_walker = __esm({
|
|
|
25025
25025
|
signal;
|
|
25026
25026
|
maxDepth;
|
|
25027
25027
|
includeChildMatches;
|
|
25028
|
-
constructor(patterns,
|
|
25028
|
+
constructor(patterns, path31, opts) {
|
|
25029
25029
|
this.patterns = patterns;
|
|
25030
|
-
this.path =
|
|
25030
|
+
this.path = path31;
|
|
25031
25031
|
this.opts = opts;
|
|
25032
25032
|
this.#sep = !opts.posix && opts.platform === "win32" ? "\\" : "/";
|
|
25033
25033
|
this.includeChildMatches = opts.includeChildMatches !== false;
|
|
@@ -25046,11 +25046,11 @@ var init_walker = __esm({
|
|
|
25046
25046
|
});
|
|
25047
25047
|
}
|
|
25048
25048
|
}
|
|
25049
|
-
#ignored(
|
|
25050
|
-
return this.seen.has(
|
|
25049
|
+
#ignored(path31) {
|
|
25050
|
+
return this.seen.has(path31) || !!this.#ignore?.ignored?.(path31);
|
|
25051
25051
|
}
|
|
25052
|
-
#childrenIgnored(
|
|
25053
|
-
return !!this.#ignore?.childrenIgnored?.(
|
|
25052
|
+
#childrenIgnored(path31) {
|
|
25053
|
+
return !!this.#ignore?.childrenIgnored?.(path31);
|
|
25054
25054
|
}
|
|
25055
25055
|
// backpressure mechanism
|
|
25056
25056
|
pause() {
|
|
@@ -25265,8 +25265,8 @@ var init_walker = __esm({
|
|
|
25265
25265
|
};
|
|
25266
25266
|
GlobWalker = class extends GlobUtil {
|
|
25267
25267
|
matches = /* @__PURE__ */ new Set();
|
|
25268
|
-
constructor(patterns,
|
|
25269
|
-
super(patterns,
|
|
25268
|
+
constructor(patterns, path31, opts) {
|
|
25269
|
+
super(patterns, path31, opts);
|
|
25270
25270
|
}
|
|
25271
25271
|
matchEmit(e) {
|
|
25272
25272
|
this.matches.add(e);
|
|
@@ -25303,8 +25303,8 @@ var init_walker = __esm({
|
|
|
25303
25303
|
};
|
|
25304
25304
|
GlobStream = class extends GlobUtil {
|
|
25305
25305
|
results;
|
|
25306
|
-
constructor(patterns,
|
|
25307
|
-
super(patterns,
|
|
25306
|
+
constructor(patterns, path31, opts) {
|
|
25307
|
+
super(patterns, path31, opts);
|
|
25308
25308
|
this.results = new Minipass({
|
|
25309
25309
|
signal: this.signal,
|
|
25310
25310
|
objectMode: true
|
|
@@ -25741,10 +25741,10 @@ var init_fs = __esm({
|
|
|
25741
25741
|
init_esm_shims();
|
|
25742
25742
|
init_esm8();
|
|
25743
25743
|
FileSystemError = class extends Error {
|
|
25744
|
-
constructor(message, operation,
|
|
25744
|
+
constructor(message, operation, path31, cause) {
|
|
25745
25745
|
super(message);
|
|
25746
25746
|
this.operation = operation;
|
|
25747
|
-
this.path =
|
|
25747
|
+
this.path = path31;
|
|
25748
25748
|
this.cause = cause;
|
|
25749
25749
|
this.name = "FileSystemError";
|
|
25750
25750
|
}
|
|
@@ -25787,7 +25787,7 @@ function tenantModeToTemplateFlags(mode) {
|
|
|
25787
25787
|
tenantMode: mode
|
|
25788
25788
|
};
|
|
25789
25789
|
}
|
|
25790
|
-
var ProjectConfigSchema, SmartStackConfigSchema, ConventionsConfigSchema, EfCoreContextSchema, EfCoreConfigSchema, ScaffoldingConfigSchema, ConfigSchema, TenantModeSchema, ValidateConventionsInputSchema, CheckMigrationsInputSchema, EntityPropertySchema, ScaffoldExtensionInputSchema, ApiDocsInputSchema, SuggestMigrationInputSchema, GeneratePermissionsInputSchema, TestTypeSchema, TestTargetSchema, ScaffoldTestsInputSchema, AnalyzeTestCoverageInputSchema, ValidateTestConventionsInputSchema, SuggestTestScenariosInputSchema, SecurityCheckSchema, ValidateSecurityInputSchema, QualityMetricSchema, AnalyzeCodeQualityInputSchema, ScaffoldApiClientInputSchema, ScaffoldRoutesInputSchema, ValidateFrontendRoutesInputSchema, SlotDefinitionSchema, ScaffoldFrontendExtensionInputSchema, AnalyzeExtensionPointsInputSchema, AnalyzeHierarchyPatternsInputSchema, ReviewCodeCheckSchema, ReviewCodeInputSchema;
|
|
25790
|
+
var ProjectConfigSchema, SmartStackConfigSchema, ConventionsConfigSchema, EfCoreContextSchema, EfCoreConfigSchema, ScaffoldingConfigSchema, ConfigSchema, TenantModeSchema, ValidateConventionsInputSchema, CheckMigrationsInputSchema, EntityPropertySchema, ScaffoldExtensionInputSchema, ApiDocsInputSchema, SuggestMigrationInputSchema, GeneratePermissionsInputSchema, TestTypeSchema, TestTargetSchema, ScaffoldTestsInputSchema, AnalyzeTestCoverageInputSchema, ValidateTestConventionsInputSchema, SuggestTestScenariosInputSchema, SecurityCheckSchema, ValidateSecurityInputSchema, QualityMetricSchema, AnalyzeCodeQualityInputSchema, ScaffoldApiClientInputSchema, ScaffoldRoutesInputSchema, ValidateFrontendRoutesInputSchema, SlotDefinitionSchema, ScaffoldFrontendExtensionInputSchema, AnalyzeExtensionPointsInputSchema, ScaffoldDataExportInputSchema, AnalyzeHierarchyPatternsInputSchema, ReviewCodeCheckSchema, ReviewCodeInputSchema;
|
|
25791
25791
|
var init_types3 = __esm({
|
|
25792
25792
|
"src/mcp/types/index.ts"() {
|
|
25793
25793
|
"use strict";
|
|
@@ -26067,6 +26067,18 @@ var init_types3 = __esm({
|
|
|
26067
26067
|
target: external_exports.enum(["pages", "components", "forms", "tables", "all"]).default("all").describe("Type of components to analyze"),
|
|
26068
26068
|
filter: external_exports.string().optional().describe('Filter by file name pattern (e.g., "User*")')
|
|
26069
26069
|
});
|
|
26070
|
+
ScaffoldDataExportInputSchema = external_exports.object({
|
|
26071
|
+
name: external_exports.string().min(1).describe('Entity name in PascalCase (e.g., "Product", "Order")'),
|
|
26072
|
+
navRoute: external_exports.string().min(1).describe('NavRoute of the source entity (e.g., "business.sales.products") \u2014 used for permission derivation'),
|
|
26073
|
+
options: external_exports.object({
|
|
26074
|
+
entityProperties: external_exports.array(EntityPropertySchema).optional().describe("Properties for the export DTO"),
|
|
26075
|
+
rateLimitPerMinute: external_exports.number().int().positive().default(60).describe("Rate limit per minute (default: 60)"),
|
|
26076
|
+
maxPageSize: external_exports.number().int().positive().default(1e3).describe("Max page size (default: 1000)"),
|
|
26077
|
+
includeAuditFields: external_exports.boolean().default(true).describe("Include CreatedAt/UpdatedAt in DTO (default: true)"),
|
|
26078
|
+
includeFrontendClient: external_exports.boolean().default(false).describe("Generate TypeScript API client (default: false)"),
|
|
26079
|
+
dryRun: external_exports.boolean().default(false).describe("Preview without writing files (default: false)")
|
|
26080
|
+
}).optional()
|
|
26081
|
+
});
|
|
26070
26082
|
AnalyzeHierarchyPatternsInputSchema = external_exports.object({
|
|
26071
26083
|
path: external_exports.string().optional().describe("Project path to analyze (default: SmartStack.app path)"),
|
|
26072
26084
|
entityFilter: external_exports.string().optional().describe('Filter entities by name pattern (e.g., "User*", "*Group*")'),
|
|
@@ -26394,7 +26406,8 @@ var init_config = __esm({
|
|
|
26394
26406
|
"ref_",
|
|
26395
26407
|
"loc_",
|
|
26396
26408
|
"lic_",
|
|
26397
|
-
"tenant_"
|
|
26409
|
+
"tenant_",
|
|
26410
|
+
"ext_"
|
|
26398
26411
|
],
|
|
26399
26412
|
customTablePrefixes: [],
|
|
26400
26413
|
scopeTypes: ["Core", "Extension", "Partner", "Community"],
|
|
@@ -29863,13 +29876,13 @@ var require_ast = __commonJS({
|
|
|
29863
29876
|
helperExpression: function helperExpression(node) {
|
|
29864
29877
|
return node.type === "SubExpression" || (node.type === "MustacheStatement" || node.type === "BlockStatement") && !!(node.params && node.params.length || node.hash);
|
|
29865
29878
|
},
|
|
29866
|
-
scopedId: function scopedId(
|
|
29867
|
-
return /^\.|this\b/.test(
|
|
29879
|
+
scopedId: function scopedId(path31) {
|
|
29880
|
+
return /^\.|this\b/.test(path31.original);
|
|
29868
29881
|
},
|
|
29869
29882
|
// an ID is simple if it only has one part, and that part is not
|
|
29870
29883
|
// `..` or `this`.
|
|
29871
|
-
simpleId: function simpleId(
|
|
29872
|
-
return
|
|
29884
|
+
simpleId: function simpleId(path31) {
|
|
29885
|
+
return path31.parts.length === 1 && !AST2.helpers.scopedId(path31) && !path31.depth;
|
|
29873
29886
|
}
|
|
29874
29887
|
}
|
|
29875
29888
|
};
|
|
@@ -30943,12 +30956,12 @@ var require_helpers2 = __commonJS({
|
|
|
30943
30956
|
loc
|
|
30944
30957
|
};
|
|
30945
30958
|
}
|
|
30946
|
-
function prepareMustache(
|
|
30959
|
+
function prepareMustache(path31, params, hash, open, strip, locInfo) {
|
|
30947
30960
|
var escapeFlag = open.charAt(3) || open.charAt(2), escaped = escapeFlag !== "{" && escapeFlag !== "&";
|
|
30948
30961
|
var decorator = /\*/.test(open);
|
|
30949
30962
|
return {
|
|
30950
30963
|
type: decorator ? "Decorator" : "MustacheStatement",
|
|
30951
|
-
path:
|
|
30964
|
+
path: path31,
|
|
30952
30965
|
params,
|
|
30953
30966
|
hash,
|
|
30954
30967
|
escaped,
|
|
@@ -31220,9 +31233,9 @@ var require_compiler = __commonJS({
|
|
|
31220
31233
|
},
|
|
31221
31234
|
DecoratorBlock: function DecoratorBlock(decorator) {
|
|
31222
31235
|
var program = decorator.program && this.compileProgram(decorator.program);
|
|
31223
|
-
var params = this.setupFullMustacheParams(decorator, program, void 0),
|
|
31236
|
+
var params = this.setupFullMustacheParams(decorator, program, void 0), path31 = decorator.path;
|
|
31224
31237
|
this.useDecorators = true;
|
|
31225
|
-
this.opcode("registerDecorator", params.length,
|
|
31238
|
+
this.opcode("registerDecorator", params.length, path31.original);
|
|
31226
31239
|
},
|
|
31227
31240
|
PartialStatement: function PartialStatement(partial2) {
|
|
31228
31241
|
this.usePartial = true;
|
|
@@ -31286,46 +31299,46 @@ var require_compiler = __commonJS({
|
|
|
31286
31299
|
}
|
|
31287
31300
|
},
|
|
31288
31301
|
ambiguousSexpr: function ambiguousSexpr(sexpr, program, inverse) {
|
|
31289
|
-
var
|
|
31290
|
-
this.opcode("getContext",
|
|
31302
|
+
var path31 = sexpr.path, name = path31.parts[0], isBlock = program != null || inverse != null;
|
|
31303
|
+
this.opcode("getContext", path31.depth);
|
|
31291
31304
|
this.opcode("pushProgram", program);
|
|
31292
31305
|
this.opcode("pushProgram", inverse);
|
|
31293
|
-
|
|
31294
|
-
this.accept(
|
|
31306
|
+
path31.strict = true;
|
|
31307
|
+
this.accept(path31);
|
|
31295
31308
|
this.opcode("invokeAmbiguous", name, isBlock);
|
|
31296
31309
|
},
|
|
31297
31310
|
simpleSexpr: function simpleSexpr(sexpr) {
|
|
31298
|
-
var
|
|
31299
|
-
|
|
31300
|
-
this.accept(
|
|
31311
|
+
var path31 = sexpr.path;
|
|
31312
|
+
path31.strict = true;
|
|
31313
|
+
this.accept(path31);
|
|
31301
31314
|
this.opcode("resolvePossibleLambda");
|
|
31302
31315
|
},
|
|
31303
31316
|
helperSexpr: function helperSexpr(sexpr, program, inverse) {
|
|
31304
|
-
var params = this.setupFullMustacheParams(sexpr, program, inverse),
|
|
31317
|
+
var params = this.setupFullMustacheParams(sexpr, program, inverse), path31 = sexpr.path, name = path31.parts[0];
|
|
31305
31318
|
if (this.options.knownHelpers[name]) {
|
|
31306
31319
|
this.opcode("invokeKnownHelper", params.length, name);
|
|
31307
31320
|
} else if (this.options.knownHelpersOnly) {
|
|
31308
31321
|
throw new _exception2["default"]("You specified knownHelpersOnly, but used the unknown helper " + name, sexpr);
|
|
31309
31322
|
} else {
|
|
31310
|
-
|
|
31311
|
-
|
|
31312
|
-
this.accept(
|
|
31313
|
-
this.opcode("invokeHelper", params.length,
|
|
31323
|
+
path31.strict = true;
|
|
31324
|
+
path31.falsy = true;
|
|
31325
|
+
this.accept(path31);
|
|
31326
|
+
this.opcode("invokeHelper", params.length, path31.original, _ast2["default"].helpers.simpleId(path31));
|
|
31314
31327
|
}
|
|
31315
31328
|
},
|
|
31316
|
-
PathExpression: function PathExpression(
|
|
31317
|
-
this.addDepth(
|
|
31318
|
-
this.opcode("getContext",
|
|
31319
|
-
var name =
|
|
31329
|
+
PathExpression: function PathExpression(path31) {
|
|
31330
|
+
this.addDepth(path31.depth);
|
|
31331
|
+
this.opcode("getContext", path31.depth);
|
|
31332
|
+
var name = path31.parts[0], scoped = _ast2["default"].helpers.scopedId(path31), blockParamId = !path31.depth && !scoped && this.blockParamIndex(name);
|
|
31320
31333
|
if (blockParamId) {
|
|
31321
|
-
this.opcode("lookupBlockParam", blockParamId,
|
|
31334
|
+
this.opcode("lookupBlockParam", blockParamId, path31.parts);
|
|
31322
31335
|
} else if (!name) {
|
|
31323
31336
|
this.opcode("pushContext");
|
|
31324
|
-
} else if (
|
|
31337
|
+
} else if (path31.data) {
|
|
31325
31338
|
this.options.data = true;
|
|
31326
|
-
this.opcode("lookupData",
|
|
31339
|
+
this.opcode("lookupData", path31.depth, path31.parts, path31.strict);
|
|
31327
31340
|
} else {
|
|
31328
|
-
this.opcode("lookupOnContext",
|
|
31341
|
+
this.opcode("lookupOnContext", path31.parts, path31.falsy, path31.strict, scoped);
|
|
31329
31342
|
}
|
|
31330
31343
|
},
|
|
31331
31344
|
StringLiteral: function StringLiteral(string3) {
|
|
@@ -31681,16 +31694,16 @@ var require_util2 = __commonJS({
|
|
|
31681
31694
|
}
|
|
31682
31695
|
exports.urlGenerate = urlGenerate;
|
|
31683
31696
|
function normalize2(aPath) {
|
|
31684
|
-
var
|
|
31697
|
+
var path31 = aPath;
|
|
31685
31698
|
var url2 = urlParse(aPath);
|
|
31686
31699
|
if (url2) {
|
|
31687
31700
|
if (!url2.path) {
|
|
31688
31701
|
return aPath;
|
|
31689
31702
|
}
|
|
31690
|
-
|
|
31703
|
+
path31 = url2.path;
|
|
31691
31704
|
}
|
|
31692
|
-
var isAbsolute = exports.isAbsolute(
|
|
31693
|
-
var parts =
|
|
31705
|
+
var isAbsolute = exports.isAbsolute(path31);
|
|
31706
|
+
var parts = path31.split(/\/+/);
|
|
31694
31707
|
for (var part, up = 0, i = parts.length - 1; i >= 0; i--) {
|
|
31695
31708
|
part = parts[i];
|
|
31696
31709
|
if (part === ".") {
|
|
@@ -31707,15 +31720,15 @@ var require_util2 = __commonJS({
|
|
|
31707
31720
|
}
|
|
31708
31721
|
}
|
|
31709
31722
|
}
|
|
31710
|
-
|
|
31711
|
-
if (
|
|
31712
|
-
|
|
31723
|
+
path31 = parts.join("/");
|
|
31724
|
+
if (path31 === "") {
|
|
31725
|
+
path31 = isAbsolute ? "/" : ".";
|
|
31713
31726
|
}
|
|
31714
31727
|
if (url2) {
|
|
31715
|
-
url2.path =
|
|
31728
|
+
url2.path = path31;
|
|
31716
31729
|
return urlGenerate(url2);
|
|
31717
31730
|
}
|
|
31718
|
-
return
|
|
31731
|
+
return path31;
|
|
31719
31732
|
}
|
|
31720
31733
|
exports.normalize = normalize2;
|
|
31721
31734
|
function join2(aRoot, aPath) {
|
|
@@ -34516,8 +34529,8 @@ var require_printer = __commonJS({
|
|
|
34516
34529
|
return this.accept(sexpr.path) + " " + params + hash;
|
|
34517
34530
|
};
|
|
34518
34531
|
PrintVisitor.prototype.PathExpression = function(id) {
|
|
34519
|
-
var
|
|
34520
|
-
return (id.data ? "@" : "") + "PATH:" +
|
|
34532
|
+
var path31 = id.parts.join("/");
|
|
34533
|
+
return (id.data ? "@" : "") + "PATH:" + path31;
|
|
34521
34534
|
};
|
|
34522
34535
|
PrintVisitor.prototype.StringLiteral = function(string3) {
|
|
34523
34536
|
return '"' + string3.value + '"';
|
|
@@ -46114,11 +46127,11 @@ var require_mime_types = __commonJS({
|
|
|
46114
46127
|
}
|
|
46115
46128
|
return exts[0];
|
|
46116
46129
|
}
|
|
46117
|
-
function lookup(
|
|
46118
|
-
if (!
|
|
46130
|
+
function lookup(path31) {
|
|
46131
|
+
if (!path31 || typeof path31 !== "string") {
|
|
46119
46132
|
return false;
|
|
46120
46133
|
}
|
|
46121
|
-
var extension2 = extname("x." +
|
|
46134
|
+
var extension2 = extname("x." + path31).toLowerCase().substr(1);
|
|
46122
46135
|
if (!extension2) {
|
|
46123
46136
|
return false;
|
|
46124
46137
|
}
|
|
@@ -47281,7 +47294,7 @@ var require_form_data = __commonJS({
|
|
|
47281
47294
|
init_esm_shims();
|
|
47282
47295
|
var CombinedStream = require_combined_stream();
|
|
47283
47296
|
var util4 = __require("util");
|
|
47284
|
-
var
|
|
47297
|
+
var path31 = __require("path");
|
|
47285
47298
|
var http3 = __require("http");
|
|
47286
47299
|
var https2 = __require("https");
|
|
47287
47300
|
var parseUrl = __require("url").parse;
|
|
@@ -47409,11 +47422,11 @@ var require_form_data = __commonJS({
|
|
|
47409
47422
|
FormData3.prototype._getContentDisposition = function(value, options) {
|
|
47410
47423
|
var filename;
|
|
47411
47424
|
if (typeof options.filepath === "string") {
|
|
47412
|
-
filename =
|
|
47425
|
+
filename = path31.normalize(options.filepath).replace(/\\/g, "/");
|
|
47413
47426
|
} else if (options.filename || value && (value.name || value.path)) {
|
|
47414
|
-
filename =
|
|
47427
|
+
filename = path31.basename(options.filename || value && (value.name || value.path));
|
|
47415
47428
|
} else if (value && value.readable && hasOwn(value, "httpVersion")) {
|
|
47416
|
-
filename =
|
|
47429
|
+
filename = path31.basename(value.client._httpMessage.path || "");
|
|
47417
47430
|
}
|
|
47418
47431
|
if (filename) {
|
|
47419
47432
|
return 'filename="' + filename + '"';
|
|
@@ -47612,9 +47625,9 @@ function isVisitable(thing) {
|
|
|
47612
47625
|
function removeBrackets(key) {
|
|
47613
47626
|
return utils_default.endsWith(key, "[]") ? key.slice(0, -2) : key;
|
|
47614
47627
|
}
|
|
47615
|
-
function renderKey(
|
|
47616
|
-
if (!
|
|
47617
|
-
return
|
|
47628
|
+
function renderKey(path31, key, dots) {
|
|
47629
|
+
if (!path31) return key;
|
|
47630
|
+
return path31.concat(key).map(function each(token, i) {
|
|
47618
47631
|
token = removeBrackets(token);
|
|
47619
47632
|
return !dots && i ? "[" + token + "]" : token;
|
|
47620
47633
|
}).join(dots ? "." : "");
|
|
@@ -47659,9 +47672,9 @@ function toFormData(obj, formData, options) {
|
|
|
47659
47672
|
}
|
|
47660
47673
|
return value;
|
|
47661
47674
|
}
|
|
47662
|
-
function defaultVisitor(value, key,
|
|
47675
|
+
function defaultVisitor(value, key, path31) {
|
|
47663
47676
|
let arr = value;
|
|
47664
|
-
if (value && !
|
|
47677
|
+
if (value && !path31 && typeof value === "object") {
|
|
47665
47678
|
if (utils_default.endsWith(key, "{}")) {
|
|
47666
47679
|
key = metaTokens ? key : key.slice(0, -2);
|
|
47667
47680
|
value = JSON.stringify(value);
|
|
@@ -47680,7 +47693,7 @@ function toFormData(obj, formData, options) {
|
|
|
47680
47693
|
if (isVisitable(value)) {
|
|
47681
47694
|
return true;
|
|
47682
47695
|
}
|
|
47683
|
-
formData.append(renderKey(
|
|
47696
|
+
formData.append(renderKey(path31, key, dots), convertValue(value));
|
|
47684
47697
|
return false;
|
|
47685
47698
|
}
|
|
47686
47699
|
const stack = [];
|
|
@@ -47689,10 +47702,10 @@ function toFormData(obj, formData, options) {
|
|
|
47689
47702
|
convertValue,
|
|
47690
47703
|
isVisitable
|
|
47691
47704
|
});
|
|
47692
|
-
function build(value,
|
|
47705
|
+
function build(value, path31) {
|
|
47693
47706
|
if (utils_default.isUndefined(value)) return;
|
|
47694
47707
|
if (stack.indexOf(value) !== -1) {
|
|
47695
|
-
throw Error("Circular reference detected in " +
|
|
47708
|
+
throw Error("Circular reference detected in " + path31.join("."));
|
|
47696
47709
|
}
|
|
47697
47710
|
stack.push(value);
|
|
47698
47711
|
utils_default.forEach(value, function each(el, key) {
|
|
@@ -47700,11 +47713,11 @@ function toFormData(obj, formData, options) {
|
|
|
47700
47713
|
formData,
|
|
47701
47714
|
el,
|
|
47702
47715
|
utils_default.isString(key) ? key.trim() : key,
|
|
47703
|
-
|
|
47716
|
+
path31,
|
|
47704
47717
|
exposedHelpers
|
|
47705
47718
|
);
|
|
47706
47719
|
if (result === true) {
|
|
47707
|
-
build(el,
|
|
47720
|
+
build(el, path31 ? path31.concat(key) : [key]);
|
|
47708
47721
|
}
|
|
47709
47722
|
});
|
|
47710
47723
|
stack.pop();
|
|
@@ -47990,7 +48003,7 @@ var init_platform = __esm({
|
|
|
47990
48003
|
// node_modules/axios/lib/helpers/toURLEncodedForm.js
|
|
47991
48004
|
function toURLEncodedForm(data, options) {
|
|
47992
48005
|
return toFormData_default(data, new platform_default.classes.URLSearchParams(), {
|
|
47993
|
-
visitor: function(value, key,
|
|
48006
|
+
visitor: function(value, key, path31, helpers) {
|
|
47994
48007
|
if (platform_default.isNode && utils_default.isBuffer(value)) {
|
|
47995
48008
|
this.append(key, value.toString("base64"));
|
|
47996
48009
|
return false;
|
|
@@ -48029,11 +48042,11 @@ function arrayToObject(arr) {
|
|
|
48029
48042
|
return obj;
|
|
48030
48043
|
}
|
|
48031
48044
|
function formDataToJSON(formData) {
|
|
48032
|
-
function buildPath(
|
|
48033
|
-
let name =
|
|
48045
|
+
function buildPath(path31, value, target, index) {
|
|
48046
|
+
let name = path31[index++];
|
|
48034
48047
|
if (name === "__proto__") return true;
|
|
48035
48048
|
const isNumericKey = Number.isFinite(+name);
|
|
48036
|
-
const isLast = index >=
|
|
48049
|
+
const isLast = index >= path31.length;
|
|
48037
48050
|
name = !name && utils_default.isArray(target) ? target.length : name;
|
|
48038
48051
|
if (isLast) {
|
|
48039
48052
|
if (utils_default.hasOwnProp(target, name)) {
|
|
@@ -48046,7 +48059,7 @@ function formDataToJSON(formData) {
|
|
|
48046
48059
|
if (!target[name] || !utils_default.isObject(target[name])) {
|
|
48047
48060
|
target[name] = [];
|
|
48048
48061
|
}
|
|
48049
|
-
const result = buildPath(
|
|
48062
|
+
const result = buildPath(path31, value, target[name], index);
|
|
48050
48063
|
if (result && utils_default.isArray(target[name])) {
|
|
48051
48064
|
target[name] = arrayToObject(target[name]);
|
|
48052
48065
|
}
|
|
@@ -50946,9 +50959,9 @@ var init_http = __esm({
|
|
|
50946
50959
|
auth = urlUsername + ":" + urlPassword;
|
|
50947
50960
|
}
|
|
50948
50961
|
auth && headers.delete("authorization");
|
|
50949
|
-
let
|
|
50962
|
+
let path31;
|
|
50950
50963
|
try {
|
|
50951
|
-
|
|
50964
|
+
path31 = buildURL(
|
|
50952
50965
|
parsed.pathname + parsed.search,
|
|
50953
50966
|
config2.params,
|
|
50954
50967
|
config2.paramsSerializer
|
|
@@ -50966,7 +50979,7 @@ var init_http = __esm({
|
|
|
50966
50979
|
false
|
|
50967
50980
|
);
|
|
50968
50981
|
const options = {
|
|
50969
|
-
path:
|
|
50982
|
+
path: path31,
|
|
50970
50983
|
method,
|
|
50971
50984
|
headers: headers.toJSON(),
|
|
50972
50985
|
agents: { http: config2.httpAgent, https: config2.httpsAgent },
|
|
@@ -51219,14 +51232,14 @@ var init_cookies = __esm({
|
|
|
51219
51232
|
cookies_default = platform_default.hasStandardBrowserEnv ? (
|
|
51220
51233
|
// Standard browser envs support document.cookie
|
|
51221
51234
|
{
|
|
51222
|
-
write(name, value, expires,
|
|
51235
|
+
write(name, value, expires, path31, domain, secure, sameSite) {
|
|
51223
51236
|
if (typeof document === "undefined") return;
|
|
51224
51237
|
const cookie = [`${name}=${encodeURIComponent(value)}`];
|
|
51225
51238
|
if (utils_default.isNumber(expires)) {
|
|
51226
51239
|
cookie.push(`expires=${new Date(expires).toUTCString()}`);
|
|
51227
51240
|
}
|
|
51228
|
-
if (utils_default.isString(
|
|
51229
|
-
cookie.push(`path=${
|
|
51241
|
+
if (utils_default.isString(path31)) {
|
|
51242
|
+
cookie.push(`path=${path31}`);
|
|
51230
51243
|
}
|
|
51231
51244
|
if (utils_default.isString(domain)) {
|
|
51232
51245
|
cookie.push(`domain=${domain}`);
|
|
@@ -62091,8 +62104,505 @@ var init_analyze_code_quality = __esm({
|
|
|
62091
62104
|
}
|
|
62092
62105
|
});
|
|
62093
62106
|
|
|
62094
|
-
// src/mcp/tools/
|
|
62107
|
+
// src/mcp/tools/scaffold-data-export.ts
|
|
62095
62108
|
import path25 from "path";
|
|
62109
|
+
async function handleScaffoldDataExport(args, config2) {
|
|
62110
|
+
const input = ScaffoldDataExportInputSchema.parse(args);
|
|
62111
|
+
logger.info("Scaffolding Data Export", { name: input.name, navRoute: input.navRoute });
|
|
62112
|
+
const result = await scaffoldDataExport(input, config2);
|
|
62113
|
+
return formatResult9(result, input);
|
|
62114
|
+
}
|
|
62115
|
+
async function scaffoldDataExport(input, config2) {
|
|
62116
|
+
const result = {
|
|
62117
|
+
success: true,
|
|
62118
|
+
files: [],
|
|
62119
|
+
instructions: []
|
|
62120
|
+
};
|
|
62121
|
+
const { name, navRoute, options } = input;
|
|
62122
|
+
const dryRun = options?.dryRun ?? false;
|
|
62123
|
+
const includeAuditFields = options?.includeAuditFields ?? true;
|
|
62124
|
+
const includeFrontendClient = options?.includeFrontendClient ?? false;
|
|
62125
|
+
const rateLimitPerMinute = options?.rateLimitPerMinute ?? 60;
|
|
62126
|
+
const maxPageSize = options?.maxPageSize ?? 1e3;
|
|
62127
|
+
const entityProperties = options?.entityProperties ?? [];
|
|
62128
|
+
const nameLower = name.charAt(0).toLowerCase() + name.slice(1);
|
|
62129
|
+
const namePlural = pluralize(nameLower);
|
|
62130
|
+
const segments = navRoute.split(".");
|
|
62131
|
+
const permissionPath = segments.join(".");
|
|
62132
|
+
const baseNamespace = config2.conventions.namespaces.api.replace(".Api", "");
|
|
62133
|
+
const appSegment = segments.length >= 2 ? toPascalCase2(segments[1]) : "Default";
|
|
62134
|
+
const moduleSegment = segments.length >= 3 ? toPascalCase2(segments[2]) : name;
|
|
62135
|
+
const projectRoot = config2.smartstack.projectPath;
|
|
62136
|
+
const structure = await findSmartStackStructure(projectRoot);
|
|
62137
|
+
const apiPath = structure.api || path25.join(projectRoot, "Api");
|
|
62138
|
+
const applicationPath = structure.application || path25.join(projectRoot, "Application");
|
|
62139
|
+
const infrastructurePath = structure.infrastructure || path25.join(projectRoot, "Infrastructure");
|
|
62140
|
+
const webPath = structure.web || path25.join(projectRoot, "web");
|
|
62141
|
+
const controllerContent = generateExportController(name, namePlural, permissionPath, baseNamespace, maxPageSize);
|
|
62142
|
+
const controllerFile = path25.join(apiPath, "Controllers", "DataExport", "v1", `Export${name}Controller.cs`);
|
|
62143
|
+
if (!dryRun) {
|
|
62144
|
+
await ensureDirectory(path25.dirname(controllerFile));
|
|
62145
|
+
await writeText(controllerFile, controllerContent);
|
|
62146
|
+
}
|
|
62147
|
+
result.files.push({ path: controllerFile, content: controllerContent, type: "created" });
|
|
62148
|
+
const interfaceContent = generateExportServiceInterface(name, baseNamespace);
|
|
62149
|
+
const interfaceFile = path25.join(applicationPath, "Common", "Interfaces", `IExport${name}Service.cs`);
|
|
62150
|
+
if (!dryRun) {
|
|
62151
|
+
await ensureDirectory(path25.dirname(interfaceFile));
|
|
62152
|
+
await writeText(interfaceFile, interfaceContent);
|
|
62153
|
+
}
|
|
62154
|
+
result.files.push({ path: interfaceFile, content: interfaceContent, type: "created" });
|
|
62155
|
+
const serviceContent = generateExportService(name, namePlural, baseNamespace, entityProperties, includeAuditFields);
|
|
62156
|
+
const serviceFile = path25.join(infrastructurePath, "Services", "DataExport", `Export${name}Service.cs`);
|
|
62157
|
+
if (!dryRun) {
|
|
62158
|
+
await ensureDirectory(path25.dirname(serviceFile));
|
|
62159
|
+
await writeText(serviceFile, serviceContent);
|
|
62160
|
+
}
|
|
62161
|
+
result.files.push({ path: serviceFile, content: serviceContent, type: "created" });
|
|
62162
|
+
const dtoContent = generateExportDto(name, baseNamespace, entityProperties, includeAuditFields);
|
|
62163
|
+
const dtoFile = path25.join(applicationPath, "DataExport", "Dtos", `Export${name}Dto.cs`);
|
|
62164
|
+
if (!dryRun) {
|
|
62165
|
+
await ensureDirectory(path25.dirname(dtoFile));
|
|
62166
|
+
await writeText(dtoFile, dtoContent);
|
|
62167
|
+
}
|
|
62168
|
+
result.files.push({ path: dtoFile, content: dtoContent, type: "created" });
|
|
62169
|
+
const seedContent = generateSeedDataFragment(name, namePlural, permissionPath, appSegment, moduleSegment, rateLimitPerMinute, maxPageSize);
|
|
62170
|
+
const seedFile = path25.join(infrastructurePath, "Persistence", "Seeding", "Data", "DataExport", `Export${name}EndpointSeedData.cs`);
|
|
62171
|
+
if (!dryRun) {
|
|
62172
|
+
await ensureDirectory(path25.dirname(seedFile));
|
|
62173
|
+
await writeText(seedFile, seedContent);
|
|
62174
|
+
}
|
|
62175
|
+
result.files.push({ path: seedFile, content: seedContent, type: "created" });
|
|
62176
|
+
if (includeFrontendClient) {
|
|
62177
|
+
const clientContent = generateFrontendClient(name, nameLower, namePlural);
|
|
62178
|
+
const clientFile = path25.join(webPath, "src", "services", "api", `export${name}.ts`);
|
|
62179
|
+
if (!dryRun) {
|
|
62180
|
+
await ensureDirectory(path25.dirname(clientFile));
|
|
62181
|
+
await writeText(clientFile, clientContent);
|
|
62182
|
+
}
|
|
62183
|
+
result.files.push({ path: clientFile, content: clientContent, type: "created" });
|
|
62184
|
+
}
|
|
62185
|
+
result.instructions.push(`Register IExport${name}Service in DI container (Program.cs or DependencyInjection.cs)`);
|
|
62186
|
+
result.instructions.push(`Add the export permission "${permissionPath}.export" to PermissionConfiguration.cs`);
|
|
62187
|
+
result.instructions.push(`Add the seed data to DataExportEndpointConfiguration or call the fragment from your seeder`);
|
|
62188
|
+
result.instructions.push(`Create an EF Core migration to apply the new seed data`);
|
|
62189
|
+
if (includeFrontendClient) {
|
|
62190
|
+
result.instructions.push(`Import the client: import { export${name}Api } from './services/api/export${name}';`);
|
|
62191
|
+
}
|
|
62192
|
+
return result;
|
|
62193
|
+
}
|
|
62194
|
+
function generateExportController(name, namePlural, permissionPath, baseNamespace, maxPageSize) {
|
|
62195
|
+
return `using Microsoft.AspNetCore.Authorization;
|
|
62196
|
+
using Microsoft.AspNetCore.Mvc;
|
|
62197
|
+
using Microsoft.AspNetCore.RateLimiting;
|
|
62198
|
+
using ${baseNamespace}.Application.Common.Models;
|
|
62199
|
+
using ${baseNamespace}.Application.DataExport.Dtos;
|
|
62200
|
+
using ${baseNamespace}.Application.Common.Interfaces;
|
|
62201
|
+
using ${baseNamespace}.Api.Authorization;
|
|
62202
|
+
|
|
62203
|
+
namespace ${baseNamespace}.Api.Controllers.DataExport.v1;
|
|
62204
|
+
|
|
62205
|
+
/// <summary>
|
|
62206
|
+
/// Data Export endpoint for ${name} entities.
|
|
62207
|
+
/// Security: JWT authentication + RequirePermission + Rate limiting + DataExportAccessMiddleware
|
|
62208
|
+
/// </summary>
|
|
62209
|
+
[ApiController]
|
|
62210
|
+
[Route("api/v1/export")]
|
|
62211
|
+
[Authorize]
|
|
62212
|
+
[EnableRateLimiting("ExternalApp")]
|
|
62213
|
+
public class Export${name}Controller : ControllerBase
|
|
62214
|
+
{
|
|
62215
|
+
private readonly IExport${name}Service _exportService;
|
|
62216
|
+
|
|
62217
|
+
public Export${name}Controller(IExport${name}Service exportService)
|
|
62218
|
+
{
|
|
62219
|
+
_exportService = exportService;
|
|
62220
|
+
}
|
|
62221
|
+
|
|
62222
|
+
/// <summary>
|
|
62223
|
+
/// Export ${name} data with pagination and optional modified-since filter.
|
|
62224
|
+
/// </summary>
|
|
62225
|
+
[HttpGet("${namePlural}")]
|
|
62226
|
+
[RequirePermission(Permissions.${permissionPath.split(".").map((s) => toPascalCase2(s)).join(".")}.Export)]
|
|
62227
|
+
public async Task<ActionResult<PaginatedResult<Export${name}Dto>>> Export(
|
|
62228
|
+
[FromQuery] int page = 1,
|
|
62229
|
+
[FromQuery] int pageSize = 100,
|
|
62230
|
+
[FromQuery] DateTime? modifiedSince = null,
|
|
62231
|
+
CancellationToken ct = default)
|
|
62232
|
+
{
|
|
62233
|
+
pageSize = Math.Clamp(pageSize, 1, ${maxPageSize});
|
|
62234
|
+
page = Math.Max(1, page);
|
|
62235
|
+
|
|
62236
|
+
var result = await _exportService.ExportAsync(page, pageSize, modifiedSince, ct);
|
|
62237
|
+
return Ok(result);
|
|
62238
|
+
}
|
|
62239
|
+
}
|
|
62240
|
+
`;
|
|
62241
|
+
}
|
|
62242
|
+
function generateExportServiceInterface(name, baseNamespace) {
|
|
62243
|
+
return `using ${baseNamespace}.Application.Common.Models;
|
|
62244
|
+
using ${baseNamespace}.Application.DataExport.Dtos;
|
|
62245
|
+
|
|
62246
|
+
namespace ${baseNamespace}.Application.Common.Interfaces;
|
|
62247
|
+
|
|
62248
|
+
/// <summary>
|
|
62249
|
+
/// Service interface for ${name} data export.
|
|
62250
|
+
/// </summary>
|
|
62251
|
+
public interface IExport${name}Service
|
|
62252
|
+
{
|
|
62253
|
+
/// <summary>
|
|
62254
|
+
/// Export ${name} data with pagination and optional modified-since filter.
|
|
62255
|
+
/// </summary>
|
|
62256
|
+
Task<PaginatedResult<Export${name}Dto>> ExportAsync(
|
|
62257
|
+
int page = 1,
|
|
62258
|
+
int pageSize = 100,
|
|
62259
|
+
DateTime? modifiedSince = null,
|
|
62260
|
+
CancellationToken ct = default);
|
|
62261
|
+
}
|
|
62262
|
+
`;
|
|
62263
|
+
}
|
|
62264
|
+
function generateExportService(name, namePlural, baseNamespace, entityProperties, includeAuditFields) {
|
|
62265
|
+
const projectionFields = [" Id = x.Id"];
|
|
62266
|
+
for (const prop of entityProperties) {
|
|
62267
|
+
projectionFields.push(` ${prop.name} = x.${prop.name}`);
|
|
62268
|
+
}
|
|
62269
|
+
if (includeAuditFields) {
|
|
62270
|
+
projectionFields.push(" CreatedAt = x.CreatedAt");
|
|
62271
|
+
projectionFields.push(" UpdatedAt = x.UpdatedAt");
|
|
62272
|
+
}
|
|
62273
|
+
const projectionBlock = projectionFields.join(",\n");
|
|
62274
|
+
return `using Microsoft.EntityFrameworkCore;
|
|
62275
|
+
using Microsoft.Extensions.Logging;
|
|
62276
|
+
using ${baseNamespace}.Application.Common.Interfaces;
|
|
62277
|
+
using ${baseNamespace}.Application.Common.Models;
|
|
62278
|
+
using ${baseNamespace}.Application.DataExport.Dtos;
|
|
62279
|
+
using ${baseNamespace}.Infrastructure.Persistence;
|
|
62280
|
+
|
|
62281
|
+
namespace ${baseNamespace}.Infrastructure.Services.DataExport;
|
|
62282
|
+
|
|
62283
|
+
/// <summary>
|
|
62284
|
+
/// Data export service for ${name} entities.
|
|
62285
|
+
/// Enforces tenant isolation via ICurrentTenantService.
|
|
62286
|
+
/// </summary>
|
|
62287
|
+
public class Export${name}Service : IExport${name}Service
|
|
62288
|
+
{
|
|
62289
|
+
private readonly ApplicationDbContext _db;
|
|
62290
|
+
private readonly ICurrentTenantService _currentTenant;
|
|
62291
|
+
private readonly ICurrentUserService _currentUser;
|
|
62292
|
+
private readonly ILogger<Export${name}Service> _logger;
|
|
62293
|
+
|
|
62294
|
+
public Export${name}Service(
|
|
62295
|
+
ApplicationDbContext db,
|
|
62296
|
+
ICurrentTenantService currentTenant,
|
|
62297
|
+
ICurrentUserService currentUser,
|
|
62298
|
+
ILogger<Export${name}Service> logger)
|
|
62299
|
+
{
|
|
62300
|
+
_db = db;
|
|
62301
|
+
_currentTenant = currentTenant;
|
|
62302
|
+
_currentUser = currentUser;
|
|
62303
|
+
_logger = logger;
|
|
62304
|
+
}
|
|
62305
|
+
|
|
62306
|
+
public async Task<PaginatedResult<Export${name}Dto>> ExportAsync(
|
|
62307
|
+
int page = 1,
|
|
62308
|
+
int pageSize = 100,
|
|
62309
|
+
DateTime? modifiedSince = null,
|
|
62310
|
+
CancellationToken ct = default)
|
|
62311
|
+
{
|
|
62312
|
+
var tenantId = _currentTenant.TenantId
|
|
62313
|
+
?? throw new UnauthorizedAccessException("Tenant context is required");
|
|
62314
|
+
|
|
62315
|
+
_logger.LogInformation("Data export requested for ${name} by {UserId}, tenant {TenantId}, page {Page}",
|
|
62316
|
+
_currentUser.UserId, tenantId, page);
|
|
62317
|
+
|
|
62318
|
+
var query = _db.${toPascalCase2(namePlural)}
|
|
62319
|
+
.Where(x => x.TenantId == tenantId)
|
|
62320
|
+
.AsNoTracking();
|
|
62321
|
+
|
|
62322
|
+
if (modifiedSince.HasValue)
|
|
62323
|
+
{
|
|
62324
|
+
query = query.Where(x => x.UpdatedAt >= modifiedSince.Value
|
|
62325
|
+
|| x.CreatedAt >= modifiedSince.Value);
|
|
62326
|
+
}
|
|
62327
|
+
|
|
62328
|
+
var totalCount = await query.CountAsync(ct);
|
|
62329
|
+
|
|
62330
|
+
var items = await query
|
|
62331
|
+
.OrderBy(x => x.Id)
|
|
62332
|
+
.Skip((page - 1) * pageSize)
|
|
62333
|
+
.Take(pageSize)
|
|
62334
|
+
.Select(x => new Export${name}Dto
|
|
62335
|
+
{
|
|
62336
|
+
${projectionBlock}
|
|
62337
|
+
})
|
|
62338
|
+
.ToListAsync(ct);
|
|
62339
|
+
|
|
62340
|
+
return new PaginatedResult<Export${name}Dto>(items, totalCount, page, pageSize);
|
|
62341
|
+
}
|
|
62342
|
+
}
|
|
62343
|
+
`;
|
|
62344
|
+
}
|
|
62345
|
+
function generateExportDto(name, baseNamespace, entityProperties, includeAuditFields) {
|
|
62346
|
+
const properties = [" public Guid Id { get; init; }"];
|
|
62347
|
+
for (const prop of entityProperties) {
|
|
62348
|
+
const nullableSuffix = prop.required ? "" : "?";
|
|
62349
|
+
properties.push(` public ${prop.type}${nullableSuffix} ${prop.name} { get; init; }`);
|
|
62350
|
+
}
|
|
62351
|
+
if (includeAuditFields) {
|
|
62352
|
+
properties.push(" public DateTime CreatedAt { get; init; }");
|
|
62353
|
+
properties.push(" public DateTime? UpdatedAt { get; init; }");
|
|
62354
|
+
}
|
|
62355
|
+
return `namespace ${baseNamespace}.Application.DataExport.Dtos;
|
|
62356
|
+
|
|
62357
|
+
/// <summary>
|
|
62358
|
+
/// Flat DTO for ${name} data export (external consumption).
|
|
62359
|
+
/// Auto-generated by SmartStack MCP \u2014 customize as needed.
|
|
62360
|
+
/// </summary>
|
|
62361
|
+
public class Export${name}Dto
|
|
62362
|
+
{
|
|
62363
|
+
${properties.join("\n")}
|
|
62364
|
+
}
|
|
62365
|
+
`;
|
|
62366
|
+
}
|
|
62367
|
+
function generateSeedDataFragment(name, namePlural, permissionPath, appSegment, moduleSegment, rateLimitPerMinute, maxPageSize) {
|
|
62368
|
+
return `// ============================================================================
|
|
62369
|
+
// Data Export Endpoint Seed Data \u2014 ${name}
|
|
62370
|
+
// Auto-generated by SmartStack MCP \u2014 add to DataExportEndpointConfiguration.cs
|
|
62371
|
+
// ============================================================================
|
|
62372
|
+
//
|
|
62373
|
+
// Copy the HasData() block below into your DataExportEndpointConfiguration
|
|
62374
|
+
// or IClientSeedDataProvider to register this export endpoint.
|
|
62375
|
+
//
|
|
62376
|
+
// builder.HasData(new
|
|
62377
|
+
// {
|
|
62378
|
+
// Id = DeterministicGuid.Create("data-export:${namePlural}"),
|
|
62379
|
+
// Code = "${namePlural}",
|
|
62380
|
+
// Name = "${name} Export",
|
|
62381
|
+
// RouteTemplate = "/api/v1/export/${namePlural}",
|
|
62382
|
+
// RequiredPermission = "${permissionPath}.export",
|
|
62383
|
+
// EntityType = "${name}",
|
|
62384
|
+
// IsActive = true,
|
|
62385
|
+
// DefaultRateLimitPerMinute = ${rateLimitPerMinute},
|
|
62386
|
+
// DefaultMaxPageSize = ${maxPageSize},
|
|
62387
|
+
// CreatedAt = seedDate
|
|
62388
|
+
// });
|
|
62389
|
+
//
|
|
62390
|
+
// IMPORTANT: NavigationApplicationId and NavigationModuleId must reference
|
|
62391
|
+
// actual seed data IDs from NavigationApplicationSeedData and
|
|
62392
|
+
// NavigationModuleSeedData respectively.
|
|
62393
|
+
//
|
|
62394
|
+
// Permission to add to PermissionConfiguration.cs:
|
|
62395
|
+
// "${permissionPath}.export" \u2014 Export ${name} data via M2M API
|
|
62396
|
+
`;
|
|
62397
|
+
}
|
|
62398
|
+
function generateFrontendClient(name, nameLower, namePlural) {
|
|
62399
|
+
return `/**
|
|
62400
|
+
* ${name} Data Export API Client
|
|
62401
|
+
*
|
|
62402
|
+
* Auto-generated by SmartStack MCP - DO NOT EDIT MANUALLY
|
|
62403
|
+
* Endpoint: /api/v1/export/${namePlural}
|
|
62404
|
+
*/
|
|
62405
|
+
|
|
62406
|
+
import { apiClient } from '../lib/apiClient';
|
|
62407
|
+
|
|
62408
|
+
export interface Export${name}Dto {
|
|
62409
|
+
id: string;
|
|
62410
|
+
[key: string]: unknown;
|
|
62411
|
+
createdAt: string;
|
|
62412
|
+
updatedAt?: string;
|
|
62413
|
+
}
|
|
62414
|
+
|
|
62415
|
+
export interface ExportParams {
|
|
62416
|
+
page?: number;
|
|
62417
|
+
pageSize?: number;
|
|
62418
|
+
modifiedSince?: string;
|
|
62419
|
+
}
|
|
62420
|
+
|
|
62421
|
+
export interface PaginatedExportResult<T> {
|
|
62422
|
+
items: T[];
|
|
62423
|
+
totalCount: number;
|
|
62424
|
+
page: number;
|
|
62425
|
+
pageSize: number;
|
|
62426
|
+
totalPages: number;
|
|
62427
|
+
hasPreviousPage: boolean;
|
|
62428
|
+
hasNextPage: boolean;
|
|
62429
|
+
}
|
|
62430
|
+
|
|
62431
|
+
const EXPORT_ENDPOINT = '/api/v1/export/${namePlural}';
|
|
62432
|
+
|
|
62433
|
+
export const export${name}Api = {
|
|
62434
|
+
/**
|
|
62435
|
+
* Fetch paginated export data
|
|
62436
|
+
*/
|
|
62437
|
+
async getPage(params?: ExportParams): Promise<PaginatedExportResult<Export${name}Dto>> {
|
|
62438
|
+
const response = await apiClient.get<PaginatedExportResult<Export${name}Dto>>(EXPORT_ENDPOINT, { params });
|
|
62439
|
+
return response.data;
|
|
62440
|
+
},
|
|
62441
|
+
|
|
62442
|
+
/**
|
|
62443
|
+
* Download all pages as a blob (CSV/JSON)
|
|
62444
|
+
*/
|
|
62445
|
+
async downloadAll(format: 'json' | 'csv' = 'json'): Promise<Blob> {
|
|
62446
|
+
const response = await apiClient.get(EXPORT_ENDPOINT, {
|
|
62447
|
+
params: { pageSize: 1000, format },
|
|
62448
|
+
responseType: 'blob',
|
|
62449
|
+
});
|
|
62450
|
+
return response.data;
|
|
62451
|
+
},
|
|
62452
|
+
};
|
|
62453
|
+
|
|
62454
|
+
export default export${name}Api;
|
|
62455
|
+
`;
|
|
62456
|
+
}
|
|
62457
|
+
function formatResult9(result, input) {
|
|
62458
|
+
const lines = [];
|
|
62459
|
+
const namePlural = pluralize(input.name.charAt(0).toLowerCase() + input.name.slice(1));
|
|
62460
|
+
lines.push(`# Scaffold Data Export: ${input.name}`);
|
|
62461
|
+
lines.push("");
|
|
62462
|
+
if (input.options?.dryRun) {
|
|
62463
|
+
lines.push("> **DRY RUN** - No files were written");
|
|
62464
|
+
lines.push("");
|
|
62465
|
+
}
|
|
62466
|
+
lines.push("## Export Endpoint");
|
|
62467
|
+
lines.push("");
|
|
62468
|
+
lines.push(`- **Entity**: \`${input.name}\``);
|
|
62469
|
+
lines.push(`- **NavRoute**: \`${input.navRoute}\``);
|
|
62470
|
+
lines.push(`- **Permission**: \`${input.navRoute}.export\``);
|
|
62471
|
+
lines.push(`- **Endpoint**: \`GET /api/v1/export/${namePlural}\``);
|
|
62472
|
+
lines.push(`- **Rate Limit**: ${input.options?.rateLimitPerMinute ?? 60}/min`);
|
|
62473
|
+
lines.push(`- **Max Page Size**: ${input.options?.maxPageSize ?? 1e3}`);
|
|
62474
|
+
lines.push("");
|
|
62475
|
+
lines.push("## Security Layers");
|
|
62476
|
+
lines.push("");
|
|
62477
|
+
lines.push("1. **Authentication** \u2014 `[Authorize]` + JWT assertion from ExternalApplicationAuthService");
|
|
62478
|
+
lines.push(`2. **Authorization** \u2014 \`[RequirePermission("${input.navRoute}.export")]\``);
|
|
62479
|
+
lines.push("3. **Access Control** \u2014 DataExportAccessMiddleware verifies per-app endpoint access");
|
|
62480
|
+
lines.push(`4. **Rate Limiting** \u2014 \`[EnableRateLimiting("ExternalApp")]\` (${input.options?.rateLimitPerMinute ?? 60}/min)`);
|
|
62481
|
+
lines.push("");
|
|
62482
|
+
lines.push("## Generated Files");
|
|
62483
|
+
lines.push("");
|
|
62484
|
+
for (const file of result.files) {
|
|
62485
|
+
const relativePath = file.path.replace(/\\/g, "/");
|
|
62486
|
+
const parts = relativePath.split("/");
|
|
62487
|
+
const srcIdx = parts.findIndex((p) => ["Controllers", "Application", "Infrastructure", "services", "Persistence"].includes(p));
|
|
62488
|
+
const displayPath = srcIdx >= 0 ? parts.slice(srcIdx).join("/") : parts.slice(-3).join("/");
|
|
62489
|
+
lines.push(`### ${displayPath}`);
|
|
62490
|
+
lines.push("");
|
|
62491
|
+
const ext2 = file.path.endsWith(".ts") ? "typescript" : "csharp";
|
|
62492
|
+
const truncated = file.content.length > 2e3 ? file.content.substring(0, 2e3) + "\n// ... (truncated)" : file.content;
|
|
62493
|
+
lines.push(`\`\`\`${ext2}`);
|
|
62494
|
+
lines.push(truncated);
|
|
62495
|
+
lines.push("```");
|
|
62496
|
+
lines.push("");
|
|
62497
|
+
}
|
|
62498
|
+
lines.push("## Next Steps");
|
|
62499
|
+
lines.push("");
|
|
62500
|
+
for (const instruction of result.instructions) {
|
|
62501
|
+
lines.push(`- ${instruction}`);
|
|
62502
|
+
}
|
|
62503
|
+
return lines.join("\n");
|
|
62504
|
+
}
|
|
62505
|
+
function toPascalCase2(s) {
|
|
62506
|
+
return s.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
62507
|
+
}
|
|
62508
|
+
function pluralize(s) {
|
|
62509
|
+
if (s.endsWith("y") && !["ay", "ey", "oy", "uy"].some((v) => s.endsWith(v))) {
|
|
62510
|
+
return s.slice(0, -1) + "ies";
|
|
62511
|
+
}
|
|
62512
|
+
if (s.endsWith("s") || s.endsWith("x") || s.endsWith("z") || s.endsWith("ch") || s.endsWith("sh")) {
|
|
62513
|
+
return s + "es";
|
|
62514
|
+
}
|
|
62515
|
+
return s + "s";
|
|
62516
|
+
}
|
|
62517
|
+
var scaffoldDataExportTool;
|
|
62518
|
+
var init_scaffold_data_export = __esm({
|
|
62519
|
+
"src/mcp/tools/scaffold-data-export.ts"() {
|
|
62520
|
+
"use strict";
|
|
62521
|
+
init_esm_shims();
|
|
62522
|
+
init_types3();
|
|
62523
|
+
init_logger();
|
|
62524
|
+
init_detector();
|
|
62525
|
+
init_fs();
|
|
62526
|
+
scaffoldDataExportTool = {
|
|
62527
|
+
name: "scaffold_data_export",
|
|
62528
|
+
description: `Generate Data Export API for a SmartStack entity.
|
|
62529
|
+
|
|
62530
|
+
Creates all code needed to expose an entity via the M2M Data Export API with 3-layer security:
|
|
62531
|
+
- Export Controller (versioned, rate-limited, permission-protected)
|
|
62532
|
+
- Export Service (interface + implementation with tenant isolation)
|
|
62533
|
+
- Export DTO (flat projection for external consumption)
|
|
62534
|
+
- Seed data fragment (DataExportEndpointConfiguration HasData)
|
|
62535
|
+
- Frontend client (optional, TypeScript blob download)
|
|
62536
|
+
|
|
62537
|
+
Example:
|
|
62538
|
+
scaffold_data_export name="Product" navRoute="business.sales.products"
|
|
62539
|
+
|
|
62540
|
+
The generated code follows the External Application & Data Export pattern
|
|
62541
|
+
documented in smartstack-api.md.`,
|
|
62542
|
+
inputSchema: {
|
|
62543
|
+
type: "object",
|
|
62544
|
+
properties: {
|
|
62545
|
+
name: {
|
|
62546
|
+
type: "string",
|
|
62547
|
+
description: 'Entity name in PascalCase (e.g., "Product", "Order")'
|
|
62548
|
+
},
|
|
62549
|
+
navRoute: {
|
|
62550
|
+
type: "string",
|
|
62551
|
+
description: 'NavRoute of the source entity (e.g., "business.sales.products") \u2014 used for permission derivation'
|
|
62552
|
+
},
|
|
62553
|
+
options: {
|
|
62554
|
+
type: "object",
|
|
62555
|
+
properties: {
|
|
62556
|
+
entityProperties: {
|
|
62557
|
+
type: "array",
|
|
62558
|
+
items: {
|
|
62559
|
+
type: "object",
|
|
62560
|
+
properties: {
|
|
62561
|
+
name: { type: "string" },
|
|
62562
|
+
type: { type: "string" },
|
|
62563
|
+
required: { type: "boolean" },
|
|
62564
|
+
maxLength: { type: "number" }
|
|
62565
|
+
},
|
|
62566
|
+
required: ["name", "type"]
|
|
62567
|
+
},
|
|
62568
|
+
description: "Properties for the export DTO"
|
|
62569
|
+
},
|
|
62570
|
+
rateLimitPerMinute: {
|
|
62571
|
+
type: "number",
|
|
62572
|
+
default: 60,
|
|
62573
|
+
description: "Rate limit per minute (default: 60)"
|
|
62574
|
+
},
|
|
62575
|
+
maxPageSize: {
|
|
62576
|
+
type: "number",
|
|
62577
|
+
default: 1e3,
|
|
62578
|
+
description: "Max page size (default: 1000)"
|
|
62579
|
+
},
|
|
62580
|
+
includeAuditFields: {
|
|
62581
|
+
type: "boolean",
|
|
62582
|
+
default: true,
|
|
62583
|
+
description: "Include CreatedAt/UpdatedAt in DTO (default: true)"
|
|
62584
|
+
},
|
|
62585
|
+
includeFrontendClient: {
|
|
62586
|
+
type: "boolean",
|
|
62587
|
+
default: false,
|
|
62588
|
+
description: "Generate TypeScript API client (default: false)"
|
|
62589
|
+
},
|
|
62590
|
+
dryRun: {
|
|
62591
|
+
type: "boolean",
|
|
62592
|
+
default: false,
|
|
62593
|
+
description: "Preview without writing files (default: false)"
|
|
62594
|
+
}
|
|
62595
|
+
}
|
|
62596
|
+
}
|
|
62597
|
+
},
|
|
62598
|
+
required: ["name", "navRoute"]
|
|
62599
|
+
}
|
|
62600
|
+
};
|
|
62601
|
+
}
|
|
62602
|
+
});
|
|
62603
|
+
|
|
62604
|
+
// src/mcp/tools/analyze-hierarchy-patterns.ts
|
|
62605
|
+
import path26 from "path";
|
|
62096
62606
|
async function handleAnalyzeHierarchyPatterns(args, config2) {
|
|
62097
62607
|
const input = AnalyzeHierarchyPatternsInputSchema.parse(args);
|
|
62098
62608
|
const projectPath = input.path || config2.smartstack.projectPath;
|
|
@@ -62115,7 +62625,7 @@ async function handleAnalyzeHierarchyPatterns(args, config2) {
|
|
|
62115
62625
|
recommendations,
|
|
62116
62626
|
circularDependencies: detectCircularDependencies(entityGraph)
|
|
62117
62627
|
};
|
|
62118
|
-
return
|
|
62628
|
+
return formatResult10(result, outputFormat, structure.root);
|
|
62119
62629
|
}
|
|
62120
62630
|
async function buildEntityGraph(domainPath, filter3) {
|
|
62121
62631
|
const entityFiles = await findFiles("**/*.cs", { cwd: domainPath });
|
|
@@ -62171,7 +62681,7 @@ async function buildEntityGraph(domainPath, filter3) {
|
|
|
62171
62681
|
return entityMap;
|
|
62172
62682
|
}
|
|
62173
62683
|
function parseEntity(content, file, domainPath) {
|
|
62174
|
-
const fileName =
|
|
62684
|
+
const fileName = path26.basename(file, ".cs");
|
|
62175
62685
|
if (fileName.endsWith("Dto") || fileName.endsWith("Command") || fileName.endsWith("Query") || fileName.endsWith("Handler") || fileName.endsWith("Validator") || fileName.endsWith("Exception") || fileName.startsWith("I")) {
|
|
62176
62686
|
return null;
|
|
62177
62687
|
}
|
|
@@ -62215,7 +62725,7 @@ function parseEntity(content, file, domainPath) {
|
|
|
62215
62725
|
const parentEntity = foreignKeys.length > 0 ? foreignKeys[0].referencedEntity : void 0;
|
|
62216
62726
|
return {
|
|
62217
62727
|
name: entityName,
|
|
62218
|
-
file:
|
|
62728
|
+
file: path26.relative(domainPath, file),
|
|
62219
62729
|
baseClass: hasSystemEntity ? "SystemEntity" : "BaseEntity",
|
|
62220
62730
|
isTenantAware: hasITenantEntity,
|
|
62221
62731
|
isSystemEntity: hasSystemEntity,
|
|
@@ -62246,24 +62756,24 @@ function detectCircularDependencies(entityMap) {
|
|
|
62246
62756
|
const cycles = [];
|
|
62247
62757
|
for (const [name] of entityMap) {
|
|
62248
62758
|
let dfs2 = function(current) {
|
|
62249
|
-
if (
|
|
62250
|
-
const cycleStart =
|
|
62251
|
-
cycles.push([...
|
|
62759
|
+
if (path31.includes(current)) {
|
|
62760
|
+
const cycleStart = path31.indexOf(current);
|
|
62761
|
+
cycles.push([...path31.slice(cycleStart), current]);
|
|
62252
62762
|
return true;
|
|
62253
62763
|
}
|
|
62254
62764
|
if (visited.has(current)) return false;
|
|
62255
62765
|
visited.add(current);
|
|
62256
|
-
|
|
62766
|
+
path31.push(current);
|
|
62257
62767
|
const node = entityMap.get(current);
|
|
62258
62768
|
if (node?.parent) {
|
|
62259
62769
|
dfs2(node.parent);
|
|
62260
62770
|
}
|
|
62261
|
-
|
|
62771
|
+
path31.pop();
|
|
62262
62772
|
return false;
|
|
62263
62773
|
};
|
|
62264
62774
|
var dfs = dfs2;
|
|
62265
62775
|
const visited = /* @__PURE__ */ new Set();
|
|
62266
|
-
const
|
|
62776
|
+
const path31 = [];
|
|
62267
62777
|
dfs2(name);
|
|
62268
62778
|
}
|
|
62269
62779
|
const unique = /* @__PURE__ */ new Map();
|
|
@@ -62470,7 +62980,7 @@ function getImplementationSteps(pattern, node) {
|
|
|
62470
62980
|
return [];
|
|
62471
62981
|
}
|
|
62472
62982
|
}
|
|
62473
|
-
function
|
|
62983
|
+
function formatResult10(result, format, _rootPath) {
|
|
62474
62984
|
if (format === "json") {
|
|
62475
62985
|
return JSON.stringify(result, null, 2);
|
|
62476
62986
|
}
|
|
@@ -66412,7 +66922,7 @@ var init_conventions = __esm({
|
|
|
66412
66922
|
});
|
|
66413
66923
|
|
|
66414
66924
|
// src/mcp/resources/project-info.ts
|
|
66415
|
-
import
|
|
66925
|
+
import path27 from "path";
|
|
66416
66926
|
async function getProjectInfoResource(config2) {
|
|
66417
66927
|
const projectPath = config2.smartstack.projectPath;
|
|
66418
66928
|
const projectInfo = await detectProject(projectPath);
|
|
@@ -66443,16 +66953,16 @@ async function getProjectInfoResource(config2) {
|
|
|
66443
66953
|
lines.push("```");
|
|
66444
66954
|
lines.push(`${projectInfo.name}/`);
|
|
66445
66955
|
if (structure.domain) {
|
|
66446
|
-
lines.push(`\u251C\u2500\u2500 ${
|
|
66956
|
+
lines.push(`\u251C\u2500\u2500 ${path27.basename(structure.domain)}/ # Domain layer (entities)`);
|
|
66447
66957
|
}
|
|
66448
66958
|
if (structure.application) {
|
|
66449
|
-
lines.push(`\u251C\u2500\u2500 ${
|
|
66959
|
+
lines.push(`\u251C\u2500\u2500 ${path27.basename(structure.application)}/ # Application layer (services)`);
|
|
66450
66960
|
}
|
|
66451
66961
|
if (structure.infrastructure) {
|
|
66452
|
-
lines.push(`\u251C\u2500\u2500 ${
|
|
66962
|
+
lines.push(`\u251C\u2500\u2500 ${path27.basename(structure.infrastructure)}/ # Infrastructure (EF Core)`);
|
|
66453
66963
|
}
|
|
66454
66964
|
if (structure.api) {
|
|
66455
|
-
lines.push(`\u251C\u2500\u2500 ${
|
|
66965
|
+
lines.push(`\u251C\u2500\u2500 ${path27.basename(structure.api)}/ # API layer (controllers)`);
|
|
66456
66966
|
}
|
|
66457
66967
|
if (structure.web) {
|
|
66458
66968
|
lines.push(`\u2514\u2500\u2500 web/smartstack-web/ # React frontend`);
|
|
@@ -66465,8 +66975,8 @@ async function getProjectInfoResource(config2) {
|
|
|
66465
66975
|
lines.push("| Project | Path |");
|
|
66466
66976
|
lines.push("|---------|------|");
|
|
66467
66977
|
for (const csproj of projectInfo.csprojFiles) {
|
|
66468
|
-
const name =
|
|
66469
|
-
const relativePath =
|
|
66978
|
+
const name = path27.basename(csproj, ".csproj");
|
|
66979
|
+
const relativePath = path27.relative(projectPath, csproj);
|
|
66470
66980
|
lines.push(`| ${name} | \`${relativePath}\` |`);
|
|
66471
66981
|
}
|
|
66472
66982
|
lines.push("");
|
|
@@ -66476,10 +66986,10 @@ async function getProjectInfoResource(config2) {
|
|
|
66476
66986
|
cwd: structure.migrations,
|
|
66477
66987
|
ignore: ["*.Designer.cs"]
|
|
66478
66988
|
});
|
|
66479
|
-
const migrations = migrationFiles.map((f) =>
|
|
66989
|
+
const migrations = migrationFiles.map((f) => path27.basename(f)).filter((f) => !f.includes("ModelSnapshot") && !f.includes(".Designer.")).sort();
|
|
66480
66990
|
lines.push("## EF Core Migrations");
|
|
66481
66991
|
lines.push("");
|
|
66482
|
-
lines.push(`**Location**: \`${
|
|
66992
|
+
lines.push(`**Location**: \`${path27.relative(projectPath, structure.migrations)}\``);
|
|
66483
66993
|
lines.push(`**Total Migrations**: ${migrations.length}`);
|
|
66484
66994
|
lines.push("");
|
|
66485
66995
|
if (migrations.length > 0) {
|
|
@@ -66514,11 +67024,11 @@ async function getProjectInfoResource(config2) {
|
|
|
66514
67024
|
lines.push("dotnet build");
|
|
66515
67025
|
lines.push("");
|
|
66516
67026
|
lines.push("# Run API");
|
|
66517
|
-
lines.push(`cd ${structure.api ?
|
|
67027
|
+
lines.push(`cd ${structure.api ? path27.relative(projectPath, structure.api) : "src/Api"}`);
|
|
66518
67028
|
lines.push("dotnet run");
|
|
66519
67029
|
lines.push("");
|
|
66520
67030
|
lines.push("# Run frontend");
|
|
66521
|
-
lines.push(`cd ${structure.web ?
|
|
67031
|
+
lines.push(`cd ${structure.web ? path27.relative(projectPath, structure.web) : "web"}`);
|
|
66522
67032
|
lines.push("npm run dev");
|
|
66523
67033
|
lines.push("");
|
|
66524
67034
|
lines.push("# Create migration");
|
|
@@ -66556,7 +67066,7 @@ var init_project_info = __esm({
|
|
|
66556
67066
|
});
|
|
66557
67067
|
|
|
66558
67068
|
// src/mcp/resources/api-endpoints.ts
|
|
66559
|
-
import
|
|
67069
|
+
import path28 from "path";
|
|
66560
67070
|
async function getApiEndpointsResource(config2, endpointFilter) {
|
|
66561
67071
|
const structure = await findSmartStackStructure(config2.smartstack.projectPath);
|
|
66562
67072
|
if (!structure.api) {
|
|
@@ -66575,7 +67085,7 @@ async function getApiEndpointsResource(config2, endpointFilter) {
|
|
|
66575
67085
|
}
|
|
66576
67086
|
async function parseController(filePath, _rootPath) {
|
|
66577
67087
|
const content = await readText(filePath);
|
|
66578
|
-
const fileName =
|
|
67088
|
+
const fileName = path28.basename(filePath, ".cs");
|
|
66579
67089
|
const controllerName = fileName.replace("Controller", "");
|
|
66580
67090
|
const endpoints = [];
|
|
66581
67091
|
const routeMatch = content.match(/\[Route\s*\(\s*"([^"]+)"\s*\)\]/);
|
|
@@ -66737,7 +67247,7 @@ var init_api_endpoints = __esm({
|
|
|
66737
67247
|
});
|
|
66738
67248
|
|
|
66739
67249
|
// src/mcp/resources/db-schema.ts
|
|
66740
|
-
import
|
|
67250
|
+
import path29 from "path";
|
|
66741
67251
|
async function getDbSchemaResource(config2, tableFilter) {
|
|
66742
67252
|
const structure = await findSmartStackStructure(config2.smartstack.projectPath);
|
|
66743
67253
|
if (!structure.domain && !structure.infrastructure) {
|
|
@@ -66821,7 +67331,7 @@ async function parseEntity2(filePath, rootPath, _config) {
|
|
|
66821
67331
|
tableName,
|
|
66822
67332
|
properties,
|
|
66823
67333
|
relationships,
|
|
66824
|
-
file:
|
|
67334
|
+
file: path29.relative(rootPath, filePath)
|
|
66825
67335
|
};
|
|
66826
67336
|
}
|
|
66827
67337
|
async function enrichFromConfigurations(entities, infrastructurePath, _config) {
|
|
@@ -66982,7 +67492,7 @@ var init_db_schema = __esm({
|
|
|
66982
67492
|
});
|
|
66983
67493
|
|
|
66984
67494
|
// src/mcp/resources/entities.ts
|
|
66985
|
-
import
|
|
67495
|
+
import path30 from "path";
|
|
66986
67496
|
async function getEntitiesResource(config2, entityFilter) {
|
|
66987
67497
|
const structure = await findSmartStackStructure(config2.smartstack.projectPath);
|
|
66988
67498
|
if (!structure.domain) {
|
|
@@ -67036,7 +67546,7 @@ async function parseEntitySummary(filePath, rootPath, config2) {
|
|
|
67036
67546
|
hasSoftDelete,
|
|
67037
67547
|
hasRowVersion,
|
|
67038
67548
|
file: filePath,
|
|
67039
|
-
relativePath:
|
|
67549
|
+
relativePath: path30.relative(rootPath, filePath)
|
|
67040
67550
|
};
|
|
67041
67551
|
}
|
|
67042
67552
|
function inferTableInfo(entityName, config2) {
|
|
@@ -67094,11 +67604,11 @@ function inferTableInfo(entityName, config2) {
|
|
|
67094
67604
|
if (!validPrefixes.includes(tablePrefix)) {
|
|
67095
67605
|
tablePrefix = "ref_";
|
|
67096
67606
|
}
|
|
67097
|
-
const tableName = `${tablePrefix}${
|
|
67607
|
+
const tableName = `${tablePrefix}${pluralize2(entityName)}`;
|
|
67098
67608
|
const schema = config2.conventions.schemas.platform;
|
|
67099
67609
|
return { tableName, tablePrefix, schema };
|
|
67100
67610
|
}
|
|
67101
|
-
function
|
|
67611
|
+
function pluralize2(name) {
|
|
67102
67612
|
if (name.endsWith("y") && !/[aeiou]y$/i.test(name)) {
|
|
67103
67613
|
return name.slice(0, -1) + "ies";
|
|
67104
67614
|
}
|
|
@@ -67254,6 +67764,8 @@ async function createServer() {
|
|
|
67254
67764
|
// Frontend Extension Tools
|
|
67255
67765
|
scaffoldFrontendExtensionTool,
|
|
67256
67766
|
analyzeExtensionPointsTool,
|
|
67767
|
+
// Data Export Tool
|
|
67768
|
+
scaffoldDataExportTool,
|
|
67257
67769
|
// Security & Code Quality Tools
|
|
67258
67770
|
validateSecurityTool,
|
|
67259
67771
|
analyzeCodeQualityTool,
|
|
@@ -67323,6 +67835,10 @@ async function createServer() {
|
|
|
67323
67835
|
case "analyze_extension_points":
|
|
67324
67836
|
result = await handleAnalyzeExtensionPoints(args ?? {}, config2);
|
|
67325
67837
|
break;
|
|
67838
|
+
// Data Export Tool
|
|
67839
|
+
case "scaffold_data_export":
|
|
67840
|
+
result = await handleScaffoldDataExport(args ?? {}, config2);
|
|
67841
|
+
break;
|
|
67326
67842
|
// Security & Code Quality Tools
|
|
67327
67843
|
case "validate_security":
|
|
67328
67844
|
result = await handleValidateSecurity(args ?? {}, config2);
|
|
@@ -67451,6 +67967,7 @@ var init_server3 = __esm({
|
|
|
67451
67967
|
init_scaffold_frontend_tests();
|
|
67452
67968
|
init_validate_security();
|
|
67453
67969
|
init_analyze_code_quality();
|
|
67970
|
+
init_scaffold_data_export();
|
|
67454
67971
|
init_analyze_hierarchy_patterns();
|
|
67455
67972
|
init_review_code();
|
|
67456
67973
|
init_conventions();
|