@justmpm/ai-tool 0.6.0 → 0.7.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/README.md +53 -3
- package/dist/{chunk-UDT7TLSN.js → chunk-CRVOX3U4.js} +731 -25
- package/dist/cli.js +17 -6
- package/dist/index.d.ts +229 -4
- package/dist/index.js +5 -1
- package/dist/{server-VKLU25E2.js → server-SZHX26N6.js} +44 -4
- package/package.json +1 -1
- package/dist/chunk-M7JM3XRW.js +0 -219
|
@@ -47,6 +47,16 @@ function detectCategory(filePath) {
|
|
|
47
47
|
if (fileName.endsWith(".astro") && normalized.includes("/layouts/")) {
|
|
48
48
|
return "layout";
|
|
49
49
|
}
|
|
50
|
+
if (normalized.includes("functions/src/") && !normalized.includes("/__tests__/")) {
|
|
51
|
+
if (!fileName.includes("index") && !normalized.includes("/types/")) {
|
|
52
|
+
return "cloud-function";
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (normalized.includes("/triggers/") || normalized.includes("/callable/") || normalized.includes("/scheduled/")) {
|
|
56
|
+
if (normalized.includes("functions/") || normalized.includes("firebase/")) {
|
|
57
|
+
return "cloud-function";
|
|
58
|
+
}
|
|
59
|
+
}
|
|
50
60
|
if (normalized.includes("/api/") || normalized.includes("/server/") || fileNameNoExt.endsWith(".server") || fileNameNoExt === "+server") {
|
|
51
61
|
return "route";
|
|
52
62
|
}
|
|
@@ -106,6 +116,7 @@ var categoryIcons = {
|
|
|
106
116
|
type: "\u{1F4DD}",
|
|
107
117
|
config: "\u2699\uFE0F",
|
|
108
118
|
test: "\u{1F9EA}",
|
|
119
|
+
"cloud-function": "\u26A1",
|
|
109
120
|
other: "\u{1F4C1}"
|
|
110
121
|
};
|
|
111
122
|
function isEntryPoint(filePath) {
|
|
@@ -149,6 +160,7 @@ function formatMapSummary(result, areasInfo) {
|
|
|
149
160
|
"layout",
|
|
150
161
|
"route",
|
|
151
162
|
"store",
|
|
163
|
+
"cloud-function",
|
|
152
164
|
"other"
|
|
153
165
|
];
|
|
154
166
|
const catParts = [];
|
|
@@ -167,6 +179,16 @@ function formatMapSummary(result, areasInfo) {
|
|
|
167
179
|
}
|
|
168
180
|
out += `
|
|
169
181
|
`;
|
|
182
|
+
const cloudFunctionCount = result.summary.categories["cloud-function"] || 0;
|
|
183
|
+
if (cloudFunctionCount > 0) {
|
|
184
|
+
out += `\u{1F525} Firebase Cloud Functions detectado
|
|
185
|
+
`;
|
|
186
|
+
out += ` ${cloudFunctionCount} function(s) em functions/src/
|
|
187
|
+
`;
|
|
188
|
+
out += ` \u2192 Use 'ai-tool functions' para listar triggers
|
|
189
|
+
|
|
190
|
+
`;
|
|
191
|
+
}
|
|
170
192
|
if (result.circularDependencies.length > 0) {
|
|
171
193
|
out += `\u26A0\uFE0F ${result.circularDependencies.length} depend\xEAncia(s) circular(es) detectada(s)
|
|
172
194
|
`;
|
|
@@ -225,13 +247,14 @@ function formatMapText(result) {
|
|
|
225
247
|
"type",
|
|
226
248
|
"config",
|
|
227
249
|
"test",
|
|
250
|
+
"cloud-function",
|
|
228
251
|
"other"
|
|
229
252
|
];
|
|
230
253
|
for (const cat of catOrder) {
|
|
231
254
|
const count = result.summary.categories[cat];
|
|
232
255
|
if (count) {
|
|
233
256
|
const icon = categoryIcons[cat];
|
|
234
|
-
out += ` ${icon} ${cat.padEnd(
|
|
257
|
+
out += ` ${icon} ${cat.padEnd(14)} ${count}
|
|
235
258
|
`;
|
|
236
259
|
}
|
|
237
260
|
}
|
|
@@ -799,6 +822,7 @@ function formatAreaDetailText(result, options = {}) {
|
|
|
799
822
|
"type",
|
|
800
823
|
"config",
|
|
801
824
|
"test",
|
|
825
|
+
"cloud-function",
|
|
802
826
|
"other"
|
|
803
827
|
];
|
|
804
828
|
const catParts = [];
|
|
@@ -1057,6 +1081,26 @@ function formatAreaContextText(result) {
|
|
|
1057
1081
|
`;
|
|
1058
1082
|
out += ` state: ${st.state}
|
|
1059
1083
|
|
|
1084
|
+
`;
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
if (result.triggers && result.triggers.length > 0) {
|
|
1088
|
+
out += `\u26A1 TRIGGERS (${result.triggers.length})
|
|
1089
|
+
|
|
1090
|
+
`;
|
|
1091
|
+
for (const t of result.triggers) {
|
|
1092
|
+
const filePart = t.file.split("/").pop() || t.file;
|
|
1093
|
+
out += ` ${t.name}`.padEnd(35) + `${t.triggerType.padEnd(25)}[${filePart}:${t.line}]
|
|
1094
|
+
`;
|
|
1095
|
+
if (t.triggerPath) {
|
|
1096
|
+
out += ` path: ${t.triggerPath}
|
|
1097
|
+
`;
|
|
1098
|
+
}
|
|
1099
|
+
if (t.triggerSchedule) {
|
|
1100
|
+
out += ` schedule: ${t.triggerSchedule}
|
|
1101
|
+
`;
|
|
1102
|
+
}
|
|
1103
|
+
out += `
|
|
1060
1104
|
`;
|
|
1061
1105
|
}
|
|
1062
1106
|
}
|
|
@@ -1077,6 +1121,10 @@ function formatAreaContextText(result) {
|
|
|
1077
1121
|
`;
|
|
1078
1122
|
out += ` Stores: ${result.stores.length}
|
|
1079
1123
|
`;
|
|
1124
|
+
if (result.triggers && result.triggers.length > 0) {
|
|
1125
|
+
out += ` Triggers: ${result.triggers.length}
|
|
1126
|
+
`;
|
|
1127
|
+
}
|
|
1080
1128
|
return out;
|
|
1081
1129
|
}
|
|
1082
1130
|
|
|
@@ -1559,9 +1607,17 @@ var FOLDER_PATTERNS = [
|
|
|
1559
1607
|
{ pattern: /store\/.*[Aa]uth/, area: "auth", priority: 70 },
|
|
1560
1608
|
{ pattern: /store\/.*[Uu]ser/, area: "user", priority: 70 },
|
|
1561
1609
|
// ============================================================================
|
|
1562
|
-
// CLOUD FUNCTIONS
|
|
1610
|
+
// FIREBASE CLOUD FUNCTIONS (expandido)
|
|
1563
1611
|
// ============================================================================
|
|
1564
1612
|
{ pattern: /functions\/src\//, area: "cloud-functions", priority: 80 },
|
|
1613
|
+
{ pattern: /functions\/src\/triggers\//, area: "triggers", priority: 95 },
|
|
1614
|
+
{ pattern: /functions\/src\/callable\//, area: "callable", priority: 95 },
|
|
1615
|
+
{ pattern: /functions\/src\/scheduled\//, area: "scheduled", priority: 95 },
|
|
1616
|
+
{ pattern: /functions\/src\/firestore\//, area: "firestore-triggers", priority: 95 },
|
|
1617
|
+
{ pattern: /functions\/src\/auth\//, area: "auth-triggers", priority: 95 },
|
|
1618
|
+
{ pattern: /functions\/src\/storage\//, area: "storage-triggers", priority: 95 },
|
|
1619
|
+
{ pattern: /functions\/src\/pubsub\//, area: "pubsub", priority: 95 },
|
|
1620
|
+
{ pattern: /functions\/src\/https\//, area: "callable", priority: 95 },
|
|
1565
1621
|
// ============================================================================
|
|
1566
1622
|
// OUTROS
|
|
1567
1623
|
// ============================================================================
|
|
@@ -1611,7 +1667,17 @@ var KEYWORD_PATTERNS = [
|
|
|
1611
1667
|
{ keyword: /[Mm]anifest/, area: "pwa", priority: 55 },
|
|
1612
1668
|
// PDF (genérico)
|
|
1613
1669
|
{ keyword: /[Pp]df[Ee]xport/, area: "export", priority: 60 },
|
|
1614
|
-
{ keyword: /[Dd]ocx[Ee]xport/, area: "export", priority: 60 }
|
|
1670
|
+
{ keyword: /[Dd]ocx[Ee]xport/, area: "export", priority: 60 },
|
|
1671
|
+
// Firebase Cloud Functions triggers
|
|
1672
|
+
{ keyword: /[Oo]nCall/, area: "callable", priority: 70 },
|
|
1673
|
+
{ keyword: /[Oo]nRequest/, area: "callable", priority: 70 },
|
|
1674
|
+
{ keyword: /[Oo]nSchedule/, area: "scheduled", priority: 70 },
|
|
1675
|
+
{ keyword: /[Oo]n[A-Z].*Created/, area: "triggers", priority: 70 },
|
|
1676
|
+
{ keyword: /[Oo]n[A-Z].*Updated/, area: "triggers", priority: 70 },
|
|
1677
|
+
{ keyword: /[Oo]n[A-Z].*Deleted/, area: "triggers", priority: 70 },
|
|
1678
|
+
{ keyword: /[Oo]n[A-Z].*Written/, area: "triggers", priority: 70 },
|
|
1679
|
+
{ keyword: /[Oo]nObject/, area: "storage-triggers", priority: 70 },
|
|
1680
|
+
{ keyword: /[Oo]nMessage/, area: "pubsub", priority: 70 }
|
|
1615
1681
|
];
|
|
1616
1682
|
var AREA_NAMES = {
|
|
1617
1683
|
// Autenticação e usuário
|
|
@@ -1658,6 +1724,13 @@ var AREA_NAMES = {
|
|
|
1658
1724
|
layout: "Layout",
|
|
1659
1725
|
"shared-ui": "UI Compartilhada",
|
|
1660
1726
|
"cloud-functions": "Cloud Functions",
|
|
1727
|
+
triggers: "Triggers Firebase",
|
|
1728
|
+
callable: "Callable Functions",
|
|
1729
|
+
scheduled: "Scheduled Functions",
|
|
1730
|
+
"firestore-triggers": "Firestore Triggers",
|
|
1731
|
+
"auth-triggers": "Auth Triggers",
|
|
1732
|
+
"storage-triggers": "Storage Triggers",
|
|
1733
|
+
pubsub: "Pub/Sub",
|
|
1661
1734
|
assets: "Assets",
|
|
1662
1735
|
scripts: "Scripts",
|
|
1663
1736
|
// UI patterns
|
|
@@ -1711,7 +1784,14 @@ var AREA_DESCRIPTIONS = {
|
|
|
1711
1784
|
core: "Providers e configura\xE7\xE3o principal",
|
|
1712
1785
|
layout: "Layout e navega\xE7\xE3o",
|
|
1713
1786
|
"shared-ui": "Componentes de UI compartilhados",
|
|
1714
|
-
"cloud-functions": "Cloud Functions (serverless)",
|
|
1787
|
+
"cloud-functions": "Firebase Cloud Functions (serverless)",
|
|
1788
|
+
triggers: "Event-driven Firebase triggers",
|
|
1789
|
+
callable: "HTTPS callable functions (frontend SDK)",
|
|
1790
|
+
scheduled: "Scheduled/cron functions",
|
|
1791
|
+
"firestore-triggers": "Triggers para eventos do Firestore",
|
|
1792
|
+
"auth-triggers": "Triggers para eventos de autentica\xE7\xE3o",
|
|
1793
|
+
"storage-triggers": "Triggers para eventos do Storage",
|
|
1794
|
+
pubsub: "Triggers para mensagens Pub/Sub",
|
|
1715
1795
|
assets: "Assets p\xFAblicos",
|
|
1716
1796
|
scripts: "Scripts de automa\xE7\xE3o",
|
|
1717
1797
|
// UI patterns
|
|
@@ -2748,6 +2828,12 @@ function collectSuggestions(targetPath, graph, allFiles, limit) {
|
|
|
2748
2828
|
priority: "low"
|
|
2749
2829
|
});
|
|
2750
2830
|
}
|
|
2831
|
+
const cloudFunctionSuggestions = suggestFirebaseRules(targetPath, allFiles);
|
|
2832
|
+
for (const suggestion of cloudFunctionSuggestions) {
|
|
2833
|
+
if (addedPaths.has(suggestion.path)) continue;
|
|
2834
|
+
addedPaths.add(suggestion.path);
|
|
2835
|
+
suggestions.push(suggestion);
|
|
2836
|
+
}
|
|
2751
2837
|
const priorityOrder = {
|
|
2752
2838
|
critical: 0,
|
|
2753
2839
|
high: 1,
|
|
@@ -2806,6 +2892,63 @@ function findTargetFile2(target, allFiles) {
|
|
|
2806
2892
|
}
|
|
2807
2893
|
return null;
|
|
2808
2894
|
}
|
|
2895
|
+
function suggestFirebaseRules(targetPath, allFiles) {
|
|
2896
|
+
const suggestions = [];
|
|
2897
|
+
if (!targetPath.includes("functions/src/")) {
|
|
2898
|
+
return suggestions;
|
|
2899
|
+
}
|
|
2900
|
+
const isFirestoreTrigger = targetPath.toLowerCase().includes("firestore") || targetPath.toLowerCase().includes("document");
|
|
2901
|
+
const isStorageTrigger = targetPath.toLowerCase().includes("storage");
|
|
2902
|
+
for (const file of allFiles) {
|
|
2903
|
+
const fileName = file.split("/").pop()?.toLowerCase() || "";
|
|
2904
|
+
if (fileName === "firestore.rules" || file.endsWith("firestore.rules")) {
|
|
2905
|
+
if (isFirestoreTrigger) {
|
|
2906
|
+
suggestions.push({
|
|
2907
|
+
path: file,
|
|
2908
|
+
category: "config",
|
|
2909
|
+
reason: "Regras Firestore (trigger relacionado)",
|
|
2910
|
+
priority: "high"
|
|
2911
|
+
});
|
|
2912
|
+
} else {
|
|
2913
|
+
suggestions.push({
|
|
2914
|
+
path: file,
|
|
2915
|
+
category: "config",
|
|
2916
|
+
reason: "Regras Firestore (Cloud Function)",
|
|
2917
|
+
priority: "medium"
|
|
2918
|
+
});
|
|
2919
|
+
}
|
|
2920
|
+
}
|
|
2921
|
+
if (fileName === "storage.rules" || file.endsWith("storage.rules")) {
|
|
2922
|
+
if (isStorageTrigger) {
|
|
2923
|
+
suggestions.push({
|
|
2924
|
+
path: file,
|
|
2925
|
+
category: "config",
|
|
2926
|
+
reason: "Regras Storage (trigger relacionado)",
|
|
2927
|
+
priority: "high"
|
|
2928
|
+
});
|
|
2929
|
+
} else {
|
|
2930
|
+
suggestions.push({
|
|
2931
|
+
path: file,
|
|
2932
|
+
category: "config",
|
|
2933
|
+
reason: "Regras Storage (Cloud Function)",
|
|
2934
|
+
priority: "low"
|
|
2935
|
+
});
|
|
2936
|
+
}
|
|
2937
|
+
}
|
|
2938
|
+
}
|
|
2939
|
+
if (!targetPath.endsWith("index.ts")) {
|
|
2940
|
+
const indexFile = allFiles.find((f) => f.includes("functions/src/index"));
|
|
2941
|
+
if (indexFile) {
|
|
2942
|
+
suggestions.push({
|
|
2943
|
+
path: indexFile,
|
|
2944
|
+
category: "config",
|
|
2945
|
+
reason: "Exports de Cloud Functions",
|
|
2946
|
+
priority: "high"
|
|
2947
|
+
});
|
|
2948
|
+
}
|
|
2949
|
+
}
|
|
2950
|
+
return suggestions;
|
|
2951
|
+
}
|
|
2809
2952
|
function formatNotFound2(target, allFiles) {
|
|
2810
2953
|
return formatFileNotFound({ target, allFiles, command: "suggest" });
|
|
2811
2954
|
}
|
|
@@ -2881,11 +3024,11 @@ function simplifyType(typeText) {
|
|
|
2881
3024
|
return simplified;
|
|
2882
3025
|
}
|
|
2883
3026
|
function extractFunctions(sourceFile) {
|
|
2884
|
-
const
|
|
3027
|
+
const functions2 = [];
|
|
2885
3028
|
for (const func of sourceFile.getFunctions()) {
|
|
2886
3029
|
const name = func.getName();
|
|
2887
3030
|
if (!name) continue;
|
|
2888
|
-
|
|
3031
|
+
functions2.push({
|
|
2889
3032
|
name,
|
|
2890
3033
|
params: extractParams(func.getParameters()),
|
|
2891
3034
|
returnType: simplifyType(func.getReturnType().getText()),
|
|
@@ -2903,7 +3046,7 @@ function extractFunctions(sourceFile) {
|
|
|
2903
3046
|
if (init.getKind() === SyntaxKind.ArrowFunction) {
|
|
2904
3047
|
const arrowFunc = init.asKind(SyntaxKind.ArrowFunction);
|
|
2905
3048
|
if (!arrowFunc) continue;
|
|
2906
|
-
|
|
3049
|
+
functions2.push({
|
|
2907
3050
|
name: varDecl.getName(),
|
|
2908
3051
|
params: extractParams(arrowFunc.getParameters()),
|
|
2909
3052
|
returnType: simplifyType(arrowFunc.getReturnType().getText()),
|
|
@@ -2916,7 +3059,7 @@ function extractFunctions(sourceFile) {
|
|
|
2916
3059
|
if (init.getKind() === SyntaxKind.FunctionExpression) {
|
|
2917
3060
|
const funcExpr = init.asKind(SyntaxKind.FunctionExpression);
|
|
2918
3061
|
if (!funcExpr) continue;
|
|
2919
|
-
|
|
3062
|
+
functions2.push({
|
|
2920
3063
|
name: varDecl.getName(),
|
|
2921
3064
|
params: extractParams(funcExpr.getParameters()),
|
|
2922
3065
|
returnType: simplifyType(funcExpr.getReturnType().getText()),
|
|
@@ -2928,7 +3071,7 @@ function extractFunctions(sourceFile) {
|
|
|
2928
3071
|
}
|
|
2929
3072
|
}
|
|
2930
3073
|
}
|
|
2931
|
-
return
|
|
3074
|
+
return functions2;
|
|
2932
3075
|
}
|
|
2933
3076
|
function extractTypes(sourceFile) {
|
|
2934
3077
|
const types = [];
|
|
@@ -3035,6 +3178,62 @@ var IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
|
3035
3178
|
".vercel",
|
|
3036
3179
|
".analyze"
|
|
3037
3180
|
]);
|
|
3181
|
+
var FIREBASE_V2_TRIGGERS = /* @__PURE__ */ new Set([
|
|
3182
|
+
// HTTPS (firebase-functions/v2/https)
|
|
3183
|
+
"onCall",
|
|
3184
|
+
"onRequest",
|
|
3185
|
+
// Firestore (firebase-functions/v2/firestore)
|
|
3186
|
+
"onDocumentCreated",
|
|
3187
|
+
"onDocumentCreatedWithAuthContext",
|
|
3188
|
+
"onDocumentUpdated",
|
|
3189
|
+
"onDocumentUpdatedWithAuthContext",
|
|
3190
|
+
"onDocumentDeleted",
|
|
3191
|
+
"onDocumentDeletedWithAuthContext",
|
|
3192
|
+
"onDocumentWritten",
|
|
3193
|
+
"onDocumentWrittenWithAuthContext",
|
|
3194
|
+
// Realtime Database (firebase-functions/v2/database)
|
|
3195
|
+
"onValueCreated",
|
|
3196
|
+
"onValueUpdated",
|
|
3197
|
+
"onValueDeleted",
|
|
3198
|
+
"onValueWritten",
|
|
3199
|
+
// Scheduler (firebase-functions/v2/scheduler)
|
|
3200
|
+
"onSchedule",
|
|
3201
|
+
// Storage (firebase-functions/v2/storage)
|
|
3202
|
+
"onObjectFinalized",
|
|
3203
|
+
"onObjectArchived",
|
|
3204
|
+
"onObjectDeleted",
|
|
3205
|
+
"onMetadataUpdated",
|
|
3206
|
+
// Pub/Sub (firebase-functions/v2/pubsub)
|
|
3207
|
+
"onMessagePublished",
|
|
3208
|
+
// Identity (firebase-functions/v2/identity)
|
|
3209
|
+
"beforeUserCreated",
|
|
3210
|
+
"beforeUserSignedIn",
|
|
3211
|
+
"beforeEmailSent",
|
|
3212
|
+
"beforeSmsSent",
|
|
3213
|
+
// Alerts - Crashlytics (firebase-functions/v2/alerts/crashlytics)
|
|
3214
|
+
"onNewFatalIssuePublished",
|
|
3215
|
+
"onNewNonfatalIssuePublished",
|
|
3216
|
+
"onNewAnrIssuePublished",
|
|
3217
|
+
"onRegressionAlertPublished",
|
|
3218
|
+
"onStabilityDigestPublished",
|
|
3219
|
+
"onVelocityAlertPublished",
|
|
3220
|
+
// Alerts - App Distribution (firebase-functions/v2/alerts/appDistribution)
|
|
3221
|
+
"onNewTesterIosDevicePublished",
|
|
3222
|
+
"onInAppFeedbackPublished",
|
|
3223
|
+
// Alerts - Performance (firebase-functions/v2/alerts/performance)
|
|
3224
|
+
"onThresholdAlertPublished",
|
|
3225
|
+
// Alerts - Billing (firebase-functions/v2/alerts/billing)
|
|
3226
|
+
"onPlanUpdatePublished",
|
|
3227
|
+
"onPlanAutomatedUpdatePublished",
|
|
3228
|
+
// Remote Config (firebase-functions/v2/remoteConfig)
|
|
3229
|
+
"onConfigUpdated",
|
|
3230
|
+
// Eventarc (firebase-functions/v2/eventarc)
|
|
3231
|
+
"onCustomEventPublished",
|
|
3232
|
+
// Tasks (firebase-functions/v2/tasks)
|
|
3233
|
+
"onTaskDispatched",
|
|
3234
|
+
// Test Lab (firebase-functions/v2/testLab)
|
|
3235
|
+
"onTestMatrixCompleted"
|
|
3236
|
+
]);
|
|
3038
3237
|
function indexProject(cwd) {
|
|
3039
3238
|
const allFiles = getAllCodeFiles(cwd);
|
|
3040
3239
|
const project = createProject2(cwd);
|
|
@@ -3084,7 +3283,7 @@ function indexProject(cwd) {
|
|
|
3084
3283
|
if (!name) continue;
|
|
3085
3284
|
const isExported = func.isExported();
|
|
3086
3285
|
const params = func.getParameters().map((p) => p.getName());
|
|
3087
|
-
const returnType = simplifyType2(func
|
|
3286
|
+
const returnType = simplifyType2(safeGetReturnType(func));
|
|
3088
3287
|
const kind = inferSymbolKind(name, "function");
|
|
3089
3288
|
const symbol = {
|
|
3090
3289
|
name,
|
|
@@ -3116,7 +3315,7 @@ function indexProject(cwd) {
|
|
|
3116
3315
|
const funcLike = init.asKind(SyntaxKind2.ArrowFunction) || init.asKind(SyntaxKind2.FunctionExpression);
|
|
3117
3316
|
if (!funcLike) continue;
|
|
3118
3317
|
const params = funcLike.getParameters().map((p) => p.getName());
|
|
3119
|
-
const returnType = simplifyType2(funcLike
|
|
3318
|
+
const returnType = simplifyType2(safeGetReturnType(funcLike));
|
|
3120
3319
|
const kind = inferSymbolKind(name, "function");
|
|
3121
3320
|
const symbol = {
|
|
3122
3321
|
name,
|
|
@@ -3136,6 +3335,48 @@ function indexProject(cwd) {
|
|
|
3136
3335
|
if (isExported) {
|
|
3137
3336
|
exports.push(name);
|
|
3138
3337
|
}
|
|
3338
|
+
} else if (initKind === SyntaxKind2.CallExpression) {
|
|
3339
|
+
const triggerName = extractFirebaseTriggerName(init);
|
|
3340
|
+
if (triggerName && FIREBASE_V2_TRIGGERS.has(triggerName)) {
|
|
3341
|
+
const triggerInfo = extractTriggerInfo(init, triggerName);
|
|
3342
|
+
const symbol = {
|
|
3343
|
+
name,
|
|
3344
|
+
file: filePath,
|
|
3345
|
+
line: varDecl.getStartLineNumber(),
|
|
3346
|
+
kind: "trigger",
|
|
3347
|
+
signature: `${isExported ? "export " : ""}const ${name} = ${triggerName}(...)`,
|
|
3348
|
+
isExported,
|
|
3349
|
+
triggerInfo
|
|
3350
|
+
};
|
|
3351
|
+
symbols.push(symbol);
|
|
3352
|
+
if (!symbolsByName[name]) {
|
|
3353
|
+
symbolsByName[name] = [];
|
|
3354
|
+
}
|
|
3355
|
+
symbolsByName[name].push(symbol);
|
|
3356
|
+
if (isExported) {
|
|
3357
|
+
exports.push(name);
|
|
3358
|
+
}
|
|
3359
|
+
} else {
|
|
3360
|
+
const declKind = varStatement.getDeclarationKind();
|
|
3361
|
+
if (declKind.toString() === "const") {
|
|
3362
|
+
const symbol = {
|
|
3363
|
+
name,
|
|
3364
|
+
file: filePath,
|
|
3365
|
+
line: varDecl.getStartLineNumber(),
|
|
3366
|
+
kind: "const",
|
|
3367
|
+
signature: `${isExported ? "export " : ""}const ${name} = ${truncateCode(init.getText(), 40)}`,
|
|
3368
|
+
isExported
|
|
3369
|
+
};
|
|
3370
|
+
symbols.push(symbol);
|
|
3371
|
+
if (!symbolsByName[name]) {
|
|
3372
|
+
symbolsByName[name] = [];
|
|
3373
|
+
}
|
|
3374
|
+
symbolsByName[name].push(symbol);
|
|
3375
|
+
if (isExported) {
|
|
3376
|
+
exports.push(name);
|
|
3377
|
+
}
|
|
3378
|
+
}
|
|
3379
|
+
}
|
|
3139
3380
|
} else {
|
|
3140
3381
|
const declKind = varStatement.getDeclarationKind();
|
|
3141
3382
|
if (declKind.toString() !== "const") continue;
|
|
@@ -3189,7 +3430,7 @@ function indexProject(cwd) {
|
|
|
3189
3430
|
kind: "type",
|
|
3190
3431
|
signature: `${isExported ? "export " : ""}type ${name}`,
|
|
3191
3432
|
isExported,
|
|
3192
|
-
definition: simplifyType2(typeAlias.getType()
|
|
3433
|
+
definition: simplifyType2(safeGetTypeText(() => typeAlias.getType()))
|
|
3193
3434
|
};
|
|
3194
3435
|
symbols.push(symbol);
|
|
3195
3436
|
if (!symbolsByName[name]) {
|
|
@@ -3290,13 +3531,70 @@ function inferSymbolKind(name, context2) {
|
|
|
3290
3531
|
}
|
|
3291
3532
|
return context2 === "function" ? "function" : "const";
|
|
3292
3533
|
}
|
|
3534
|
+
function extractFirebaseTriggerName(init) {
|
|
3535
|
+
const text = init.getText();
|
|
3536
|
+
for (const trigger of FIREBASE_V2_TRIGGERS) {
|
|
3537
|
+
const pattern = new RegExp(`(?:^|\\.)${trigger}\\s*\\(`);
|
|
3538
|
+
if (pattern.test(text)) {
|
|
3539
|
+
return trigger;
|
|
3540
|
+
}
|
|
3541
|
+
}
|
|
3542
|
+
return null;
|
|
3543
|
+
}
|
|
3544
|
+
function extractTriggerInfo(init, triggerName) {
|
|
3545
|
+
const text = init.getText();
|
|
3546
|
+
const info = { triggerType: triggerName };
|
|
3547
|
+
if (triggerName.startsWith("onDocument") || triggerName.startsWith("onValue")) {
|
|
3548
|
+
const pathMatch = text.match(/\(\s*["'`]([^"'`]+)["'`]/);
|
|
3549
|
+
if (pathMatch) {
|
|
3550
|
+
info.triggerPath = pathMatch[1];
|
|
3551
|
+
}
|
|
3552
|
+
}
|
|
3553
|
+
if (triggerName === "onSchedule") {
|
|
3554
|
+
const scheduleMatch = text.match(/onSchedule\s*\(\s*["'`]([^"'`]+)["'`]/);
|
|
3555
|
+
if (scheduleMatch) {
|
|
3556
|
+
info.triggerSchedule = scheduleMatch[1];
|
|
3557
|
+
} else {
|
|
3558
|
+
const objectScheduleMatch = text.match(/schedule\s*:\s*["'`]([^"'`]+)["'`]/);
|
|
3559
|
+
if (objectScheduleMatch) {
|
|
3560
|
+
info.triggerSchedule = objectScheduleMatch[1];
|
|
3561
|
+
}
|
|
3562
|
+
}
|
|
3563
|
+
}
|
|
3564
|
+
if (triggerName.startsWith("onObject") || triggerName === "onMetadataUpdated") {
|
|
3565
|
+
const bucketMatch = text.match(/bucket\s*:\s*["'`]([^"'`]+)["'`]/);
|
|
3566
|
+
if (bucketMatch) {
|
|
3567
|
+
info.triggerPath = bucketMatch[1];
|
|
3568
|
+
}
|
|
3569
|
+
}
|
|
3570
|
+
return info;
|
|
3571
|
+
}
|
|
3293
3572
|
function simplifyType2(typeText) {
|
|
3573
|
+
if (!typeText) return "unknown";
|
|
3294
3574
|
let simplified = typeText.replace(/import\([^)]+\)\./g, "");
|
|
3295
3575
|
if (simplified.length > 80) {
|
|
3296
3576
|
simplified = simplified.slice(0, 77) + "...";
|
|
3297
3577
|
}
|
|
3298
3578
|
return simplified;
|
|
3299
3579
|
}
|
|
3580
|
+
function safeGetTypeText(getTypeFn) {
|
|
3581
|
+
try {
|
|
3582
|
+
const type = getTypeFn();
|
|
3583
|
+
if (!type) return "unknown";
|
|
3584
|
+
return type.getText();
|
|
3585
|
+
} catch {
|
|
3586
|
+
return "unknown";
|
|
3587
|
+
}
|
|
3588
|
+
}
|
|
3589
|
+
function safeGetReturnType(node) {
|
|
3590
|
+
try {
|
|
3591
|
+
const returnType = node.getReturnType();
|
|
3592
|
+
if (!returnType) return "unknown";
|
|
3593
|
+
return returnType.getText();
|
|
3594
|
+
} catch {
|
|
3595
|
+
return "unknown";
|
|
3596
|
+
}
|
|
3597
|
+
}
|
|
3300
3598
|
function truncateCode(code, maxLen) {
|
|
3301
3599
|
const oneLine = code.replace(/\s+/g, " ").trim();
|
|
3302
3600
|
if (oneLine.length <= maxLen) return oneLine;
|
|
@@ -3310,7 +3608,7 @@ function formatInterfaceDefinition2(iface) {
|
|
|
3310
3608
|
}
|
|
3311
3609
|
const props = iface.getProperties();
|
|
3312
3610
|
for (const prop of props.slice(0, 10)) {
|
|
3313
|
-
const propType = simplifyType2(prop.getType()
|
|
3611
|
+
const propType = simplifyType2(safeGetTypeText(() => prop.getType()));
|
|
3314
3612
|
parts.push(`${prop.getName()}: ${propType}`);
|
|
3315
3613
|
}
|
|
3316
3614
|
if (props.length > 10) {
|
|
@@ -3335,7 +3633,7 @@ async function context(target, options = {}) {
|
|
|
3335
3633
|
const absolutePath = resolve2(cwd, targetPath);
|
|
3336
3634
|
const sourceFile = addSourceFile(project, absolutePath);
|
|
3337
3635
|
const imports = extractImports(sourceFile);
|
|
3338
|
-
const
|
|
3636
|
+
const functions2 = extractFunctions(sourceFile);
|
|
3339
3637
|
const types = extractTypes(sourceFile);
|
|
3340
3638
|
const exports = extractExports(sourceFile);
|
|
3341
3639
|
const result = {
|
|
@@ -3345,7 +3643,7 @@ async function context(target, options = {}) {
|
|
|
3345
3643
|
category: detectCategory(targetPath),
|
|
3346
3644
|
imports,
|
|
3347
3645
|
exports,
|
|
3348
|
-
functions,
|
|
3646
|
+
functions: functions2,
|
|
3349
3647
|
types
|
|
3350
3648
|
};
|
|
3351
3649
|
if (format === "json") {
|
|
@@ -3478,10 +3776,11 @@ async function areaContext(areaName, options = {}) {
|
|
|
3478
3776
|
}
|
|
3479
3777
|
const types = [];
|
|
3480
3778
|
const hooks = [];
|
|
3481
|
-
const
|
|
3779
|
+
const functions2 = [];
|
|
3482
3780
|
const components = [];
|
|
3483
3781
|
const services = [];
|
|
3484
3782
|
const stores = [];
|
|
3783
|
+
const triggers = [];
|
|
3485
3784
|
for (const filePath of areaFiles) {
|
|
3486
3785
|
const fileData = index.files[filePath];
|
|
3487
3786
|
if (!fileData) continue;
|
|
@@ -3526,7 +3825,7 @@ async function areaContext(areaName, options = {}) {
|
|
|
3526
3825
|
returns: symbol.returnType || "unknown"
|
|
3527
3826
|
});
|
|
3528
3827
|
} else {
|
|
3529
|
-
|
|
3828
|
+
functions2.push({
|
|
3530
3829
|
name: symbol.name,
|
|
3531
3830
|
file: filePath,
|
|
3532
3831
|
line: symbol.line,
|
|
@@ -3545,6 +3844,16 @@ async function areaContext(areaName, options = {}) {
|
|
|
3545
3844
|
});
|
|
3546
3845
|
}
|
|
3547
3846
|
break;
|
|
3847
|
+
case "trigger":
|
|
3848
|
+
triggers.push({
|
|
3849
|
+
name: symbol.name,
|
|
3850
|
+
file: filePath,
|
|
3851
|
+
line: symbol.line,
|
|
3852
|
+
triggerType: symbol.triggerInfo?.triggerType || "unknown",
|
|
3853
|
+
triggerPath: symbol.triggerInfo?.triggerPath,
|
|
3854
|
+
triggerSchedule: symbol.triggerInfo?.triggerSchedule
|
|
3855
|
+
});
|
|
3856
|
+
break;
|
|
3548
3857
|
}
|
|
3549
3858
|
}
|
|
3550
3859
|
}
|
|
@@ -3560,10 +3869,11 @@ async function areaContext(areaName, options = {}) {
|
|
|
3560
3869
|
},
|
|
3561
3870
|
types,
|
|
3562
3871
|
hooks,
|
|
3563
|
-
functions,
|
|
3872
|
+
functions: functions2,
|
|
3564
3873
|
components,
|
|
3565
3874
|
services,
|
|
3566
|
-
stores
|
|
3875
|
+
stores,
|
|
3876
|
+
triggers
|
|
3567
3877
|
};
|
|
3568
3878
|
if (format === "json") {
|
|
3569
3879
|
return JSON.stringify(result, null, 2);
|
|
@@ -4012,6 +4322,406 @@ function getAllCodeFiles5(dir, files = [], baseDir = dir) {
|
|
|
4012
4322
|
return files;
|
|
4013
4323
|
}
|
|
4014
4324
|
|
|
4325
|
+
// src/commands/functions.ts
|
|
4326
|
+
async function functions(options = {}) {
|
|
4327
|
+
const cwd = options.cwd || process.cwd();
|
|
4328
|
+
const format = options.format || "text";
|
|
4329
|
+
const useCache = options.cache !== false;
|
|
4330
|
+
const filterTrigger = options.trigger;
|
|
4331
|
+
if (!isFirebaseProject(cwd)) {
|
|
4332
|
+
const errorMsg = "Este n\xE3o \xE9 um projeto Firebase.\nN\xE3o foi encontrado .firebaserc ou firebase.json";
|
|
4333
|
+
return format === "json" ? JSON.stringify({ error: errorMsg }) : `\u274C ${errorMsg}`;
|
|
4334
|
+
}
|
|
4335
|
+
if (!hasFirebaseFunctions(cwd)) {
|
|
4336
|
+
const errorMsg = "Projeto Firebase sem Cloud Functions.\nN\xE3o foi encontrado functions/src/index.ts";
|
|
4337
|
+
return format === "json" ? JSON.stringify({ error: errorMsg }) : `\u274C ${errorMsg}`;
|
|
4338
|
+
}
|
|
4339
|
+
try {
|
|
4340
|
+
let index;
|
|
4341
|
+
if (useCache && isCacheValid(cwd)) {
|
|
4342
|
+
const cached = getCachedSymbolsIndex(cwd);
|
|
4343
|
+
if (cached && cached.symbolsByName) {
|
|
4344
|
+
index = cached;
|
|
4345
|
+
} else {
|
|
4346
|
+
index = indexProject(cwd);
|
|
4347
|
+
cacheSymbolsIndex(cwd, index);
|
|
4348
|
+
updateCacheMeta(cwd);
|
|
4349
|
+
}
|
|
4350
|
+
} else {
|
|
4351
|
+
index = indexProject(cwd);
|
|
4352
|
+
if (useCache) {
|
|
4353
|
+
cacheSymbolsIndex(cwd, index);
|
|
4354
|
+
updateCacheMeta(cwd);
|
|
4355
|
+
}
|
|
4356
|
+
}
|
|
4357
|
+
const funcs = [];
|
|
4358
|
+
for (const fileData of Object.values(index.files)) {
|
|
4359
|
+
if (!fileData.path.includes("functions/src/")) continue;
|
|
4360
|
+
for (const symbol of fileData.symbols) {
|
|
4361
|
+
if (symbol.kind === "trigger") {
|
|
4362
|
+
funcs.push({
|
|
4363
|
+
name: symbol.name,
|
|
4364
|
+
file: symbol.file,
|
|
4365
|
+
line: symbol.line,
|
|
4366
|
+
triggerType: symbol.triggerInfo?.triggerType || "unknown",
|
|
4367
|
+
triggerPath: symbol.triggerInfo?.triggerPath,
|
|
4368
|
+
triggerSchedule: symbol.triggerInfo?.triggerSchedule,
|
|
4369
|
+
isExported: symbol.isExported
|
|
4370
|
+
});
|
|
4371
|
+
}
|
|
4372
|
+
}
|
|
4373
|
+
}
|
|
4374
|
+
const filtered = filterTrigger ? funcs.filter(
|
|
4375
|
+
(f) => f.triggerType.toLowerCase().includes(filterTrigger.toLowerCase())
|
|
4376
|
+
) : funcs;
|
|
4377
|
+
const byTrigger = {};
|
|
4378
|
+
const triggerCounts = {};
|
|
4379
|
+
for (const func of filtered) {
|
|
4380
|
+
if (!byTrigger[func.triggerType]) {
|
|
4381
|
+
byTrigger[func.triggerType] = [];
|
|
4382
|
+
triggerCounts[func.triggerType] = 0;
|
|
4383
|
+
}
|
|
4384
|
+
byTrigger[func.triggerType].push(func);
|
|
4385
|
+
triggerCounts[func.triggerType]++;
|
|
4386
|
+
}
|
|
4387
|
+
const result = {
|
|
4388
|
+
version: "1.0.0",
|
|
4389
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4390
|
+
cwd,
|
|
4391
|
+
isFirebaseProject: true,
|
|
4392
|
+
hasFunctions: true,
|
|
4393
|
+
functions: filtered,
|
|
4394
|
+
byTrigger,
|
|
4395
|
+
summary: {
|
|
4396
|
+
total: filtered.length,
|
|
4397
|
+
exported: filtered.filter((f) => f.isExported).length,
|
|
4398
|
+
byTrigger: triggerCounts
|
|
4399
|
+
}
|
|
4400
|
+
};
|
|
4401
|
+
if (format === "json") {
|
|
4402
|
+
return JSON.stringify(result, null, 2);
|
|
4403
|
+
}
|
|
4404
|
+
return formatFunctionsText(result);
|
|
4405
|
+
} catch (error) {
|
|
4406
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4407
|
+
throw new Error(`Erro ao executar functions: ${message}`);
|
|
4408
|
+
}
|
|
4409
|
+
}
|
|
4410
|
+
function formatFunctionsText(result) {
|
|
4411
|
+
let out = "";
|
|
4412
|
+
out += `
|
|
4413
|
+
`;
|
|
4414
|
+
out += `\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
|
|
4415
|
+
`;
|
|
4416
|
+
out += `\u2551 \u26A1 CLOUD FUNCTIONS \u2551
|
|
4417
|
+
`;
|
|
4418
|
+
out += `\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
|
|
4419
|
+
|
|
4420
|
+
`;
|
|
4421
|
+
out += `\u{1F4CA} RESUMO
|
|
4422
|
+
`;
|
|
4423
|
+
out += ` Total: ${result.summary.total} functions
|
|
4424
|
+
`;
|
|
4425
|
+
out += ` Exportadas: ${result.summary.exported}
|
|
4426
|
+
|
|
4427
|
+
`;
|
|
4428
|
+
if (result.summary.total === 0) {
|
|
4429
|
+
out += ` \u26A0\uFE0F Nenhuma Cloud Function detectada.
|
|
4430
|
+
`;
|
|
4431
|
+
out += ` Verifique se seus triggers usam padr\xE3o Firebase v2 (onCall, onDocumentCreated, etc).
|
|
4432
|
+
`;
|
|
4433
|
+
return out;
|
|
4434
|
+
}
|
|
4435
|
+
out += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
4436
|
+
|
|
4437
|
+
`;
|
|
4438
|
+
const triggerOrder = [
|
|
4439
|
+
// HTTPS
|
|
4440
|
+
"onCall",
|
|
4441
|
+
"onRequest",
|
|
4442
|
+
// Firestore
|
|
4443
|
+
"onDocumentCreated",
|
|
4444
|
+
"onDocumentCreatedWithAuthContext",
|
|
4445
|
+
"onDocumentUpdated",
|
|
4446
|
+
"onDocumentUpdatedWithAuthContext",
|
|
4447
|
+
"onDocumentDeleted",
|
|
4448
|
+
"onDocumentDeletedWithAuthContext",
|
|
4449
|
+
"onDocumentWritten",
|
|
4450
|
+
"onDocumentWrittenWithAuthContext",
|
|
4451
|
+
// Realtime Database
|
|
4452
|
+
"onValueCreated",
|
|
4453
|
+
"onValueUpdated",
|
|
4454
|
+
"onValueDeleted",
|
|
4455
|
+
"onValueWritten",
|
|
4456
|
+
// Scheduler
|
|
4457
|
+
"onSchedule",
|
|
4458
|
+
// Storage
|
|
4459
|
+
"onObjectFinalized",
|
|
4460
|
+
"onObjectArchived",
|
|
4461
|
+
"onObjectDeleted",
|
|
4462
|
+
"onMetadataUpdated",
|
|
4463
|
+
// Pub/Sub
|
|
4464
|
+
"onMessagePublished",
|
|
4465
|
+
// Identity
|
|
4466
|
+
"beforeUserCreated",
|
|
4467
|
+
"beforeUserSignedIn"
|
|
4468
|
+
];
|
|
4469
|
+
const sortedTriggers = Object.keys(result.byTrigger).sort((a, b) => {
|
|
4470
|
+
const aIdx = triggerOrder.indexOf(a);
|
|
4471
|
+
const bIdx = triggerOrder.indexOf(b);
|
|
4472
|
+
if (aIdx === -1 && bIdx === -1) return a.localeCompare(b);
|
|
4473
|
+
if (aIdx === -1) return 1;
|
|
4474
|
+
if (bIdx === -1) return -1;
|
|
4475
|
+
return aIdx - bIdx;
|
|
4476
|
+
});
|
|
4477
|
+
for (const trigger of sortedTriggers) {
|
|
4478
|
+
const funcs = result.byTrigger[trigger];
|
|
4479
|
+
const icon = getTriggerIcon(trigger);
|
|
4480
|
+
out += `${icon} ${trigger} (${funcs.length})
|
|
4481
|
+
|
|
4482
|
+
`;
|
|
4483
|
+
for (const func of funcs) {
|
|
4484
|
+
const exportTag = func.isExported ? "" : " [n\xE3o exportada]";
|
|
4485
|
+
out += ` ${func.name}${exportTag}
|
|
4486
|
+
`;
|
|
4487
|
+
out += ` \u{1F4C1} ${func.file}:${func.line}
|
|
4488
|
+
`;
|
|
4489
|
+
if (func.triggerPath) {
|
|
4490
|
+
out += ` \u{1F4CD} path: ${func.triggerPath}
|
|
4491
|
+
`;
|
|
4492
|
+
}
|
|
4493
|
+
if (func.triggerSchedule) {
|
|
4494
|
+
out += ` \u23F0 schedule: ${func.triggerSchedule}
|
|
4495
|
+
`;
|
|
4496
|
+
}
|
|
4497
|
+
}
|
|
4498
|
+
out += `
|
|
4499
|
+
`;
|
|
4500
|
+
}
|
|
4501
|
+
return out;
|
|
4502
|
+
}
|
|
4503
|
+
function getTriggerIcon(trigger) {
|
|
4504
|
+
if (trigger.includes("Call") || trigger.includes("Request")) return "\u{1F310}";
|
|
4505
|
+
if (trigger.includes("Document") || trigger.includes("Value")) return "\u{1F525}";
|
|
4506
|
+
if (trigger.includes("Schedule")) return "\u23F0";
|
|
4507
|
+
if (trigger.includes("Object") || trigger.includes("Metadata")) return "\u{1F4E6}";
|
|
4508
|
+
if (trigger.includes("Message") || trigger.includes("Pub")) return "\u{1F4E8}";
|
|
4509
|
+
if (trigger.includes("User") || trigger.includes("before")) return "\u{1F464}";
|
|
4510
|
+
if (trigger.includes("Alert") || trigger.includes("Issue")) return "\u{1F6A8}";
|
|
4511
|
+
if (trigger.includes("Config")) return "\u2699\uFE0F";
|
|
4512
|
+
if (trigger.includes("Task")) return "\u{1F4CB}";
|
|
4513
|
+
if (trigger.includes("Test")) return "\u{1F9EA}";
|
|
4514
|
+
return "\u26A1";
|
|
4515
|
+
}
|
|
4516
|
+
|
|
4517
|
+
// src/commands/find.ts
|
|
4518
|
+
async function find(query, options = {}) {
|
|
4519
|
+
const cwd = options.cwd || process.cwd();
|
|
4520
|
+
const format = options.format || "text";
|
|
4521
|
+
const filterType = options.type || "all";
|
|
4522
|
+
const filterArea = options.area;
|
|
4523
|
+
const defOnly = options.def ?? false;
|
|
4524
|
+
const refsOnly = options.refs ?? false;
|
|
4525
|
+
const useCache = options.cache !== false;
|
|
4526
|
+
if (!query || query.trim().length === 0) {
|
|
4527
|
+
throw new Error("Query \xE9 obrigat\xF3ria. Exemplo: ai-tool find useAuth");
|
|
4528
|
+
}
|
|
4529
|
+
try {
|
|
4530
|
+
let index;
|
|
4531
|
+
let fromCache = false;
|
|
4532
|
+
if (useCache && isCacheValid(cwd)) {
|
|
4533
|
+
const cached = getCachedSymbolsIndex(cwd);
|
|
4534
|
+
if (cached && cached.symbolsByName) {
|
|
4535
|
+
index = cached;
|
|
4536
|
+
fromCache = true;
|
|
4537
|
+
} else {
|
|
4538
|
+
index = indexProject(cwd);
|
|
4539
|
+
cacheSymbolsIndex(cwd, index);
|
|
4540
|
+
updateCacheMeta(cwd);
|
|
4541
|
+
}
|
|
4542
|
+
} else {
|
|
4543
|
+
index = indexProject(cwd);
|
|
4544
|
+
if (useCache) {
|
|
4545
|
+
cacheSymbolsIndex(cwd, index);
|
|
4546
|
+
updateCacheMeta(cwd);
|
|
4547
|
+
}
|
|
4548
|
+
}
|
|
4549
|
+
let allowedFiles = null;
|
|
4550
|
+
if (filterArea) {
|
|
4551
|
+
const config = readConfig(cwd);
|
|
4552
|
+
const areaLower = filterArea.toLowerCase();
|
|
4553
|
+
allowedFiles = /* @__PURE__ */ new Set();
|
|
4554
|
+
for (const filePath of Object.keys(index.files)) {
|
|
4555
|
+
if (isFileIgnored(filePath, config)) continue;
|
|
4556
|
+
const fileAreas = detectFileAreas(filePath, config);
|
|
4557
|
+
const belongsToArea = fileAreas.some(
|
|
4558
|
+
(a) => a.toLowerCase() === areaLower || a.toLowerCase().includes(areaLower)
|
|
4559
|
+
);
|
|
4560
|
+
if (belongsToArea) {
|
|
4561
|
+
allowedFiles.add(filePath);
|
|
4562
|
+
}
|
|
4563
|
+
}
|
|
4564
|
+
if (allowedFiles.size === 0) {
|
|
4565
|
+
return format === "json" ? JSON.stringify({ error: `Nenhum arquivo encontrado na \xE1rea "${filterArea}"` }) : `\u274C Nenhum arquivo encontrado na \xE1rea "${filterArea}"`;
|
|
4566
|
+
}
|
|
4567
|
+
}
|
|
4568
|
+
const matches = searchInIndex(index, query, filterType, allowedFiles);
|
|
4569
|
+
let definition = null;
|
|
4570
|
+
let references = [];
|
|
4571
|
+
for (const match of matches) {
|
|
4572
|
+
if (match.matchType === "definition") {
|
|
4573
|
+
if (!definition) {
|
|
4574
|
+
definition = match;
|
|
4575
|
+
}
|
|
4576
|
+
} else {
|
|
4577
|
+
references.push(match);
|
|
4578
|
+
}
|
|
4579
|
+
}
|
|
4580
|
+
if (defOnly) {
|
|
4581
|
+
references = [];
|
|
4582
|
+
}
|
|
4583
|
+
if (refsOnly) {
|
|
4584
|
+
definition = null;
|
|
4585
|
+
}
|
|
4586
|
+
const uniqueFiles = /* @__PURE__ */ new Set();
|
|
4587
|
+
if (definition) uniqueFiles.add(definition.file);
|
|
4588
|
+
for (const ref of references) {
|
|
4589
|
+
uniqueFiles.add(ref.file);
|
|
4590
|
+
}
|
|
4591
|
+
const result = {
|
|
4592
|
+
version: "1.0.0",
|
|
4593
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4594
|
+
query,
|
|
4595
|
+
filters: {
|
|
4596
|
+
type: filterType !== "all" ? filterType : void 0,
|
|
4597
|
+
area: filterArea,
|
|
4598
|
+
defOnly,
|
|
4599
|
+
refsOnly
|
|
4600
|
+
},
|
|
4601
|
+
definition,
|
|
4602
|
+
references,
|
|
4603
|
+
summary: {
|
|
4604
|
+
definitions: definition ? 1 : 0,
|
|
4605
|
+
references: references.length,
|
|
4606
|
+
files: uniqueFiles.size
|
|
4607
|
+
},
|
|
4608
|
+
fromCache
|
|
4609
|
+
};
|
|
4610
|
+
if (format === "json") {
|
|
4611
|
+
return JSON.stringify(result, null, 2);
|
|
4612
|
+
}
|
|
4613
|
+
return formatFindText(result);
|
|
4614
|
+
} catch (error) {
|
|
4615
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4616
|
+
throw new Error(`Erro ao executar find: ${message}`);
|
|
4617
|
+
}
|
|
4618
|
+
}
|
|
4619
|
+
function searchInIndex(index, query, filterType, allowedFiles) {
|
|
4620
|
+
const matches = [];
|
|
4621
|
+
const queryLower = query.toLowerCase();
|
|
4622
|
+
const processedSymbols = /* @__PURE__ */ new Set();
|
|
4623
|
+
for (const [name, symbols] of Object.entries(index.symbolsByName)) {
|
|
4624
|
+
const nameLower = name.toLowerCase();
|
|
4625
|
+
if (nameLower === queryLower || nameLower.includes(queryLower)) {
|
|
4626
|
+
for (const symbol of symbols) {
|
|
4627
|
+
if (allowedFiles && !allowedFiles.has(symbol.file)) continue;
|
|
4628
|
+
if (!matchesType(symbol.kind, filterType)) continue;
|
|
4629
|
+
const key = `${symbol.file}:${symbol.line}:${symbol.name}`;
|
|
4630
|
+
if (processedSymbols.has(key)) continue;
|
|
4631
|
+
processedSymbols.add(key);
|
|
4632
|
+
const fileData = index.files[symbol.file];
|
|
4633
|
+
matches.push({
|
|
4634
|
+
file: symbol.file,
|
|
4635
|
+
line: symbol.line,
|
|
4636
|
+
column: 0,
|
|
4637
|
+
code: symbol.signature,
|
|
4638
|
+
matchType: "definition",
|
|
4639
|
+
symbolType: mapKindToSymbolType(symbol.kind),
|
|
4640
|
+
category: fileData?.category || "other"
|
|
4641
|
+
});
|
|
4642
|
+
}
|
|
4643
|
+
}
|
|
4644
|
+
}
|
|
4645
|
+
for (const [filePath, fileData] of Object.entries(index.files)) {
|
|
4646
|
+
if (allowedFiles && !allowedFiles.has(filePath)) continue;
|
|
4647
|
+
for (const imp of fileData.imports) {
|
|
4648
|
+
for (const spec of imp.specifiers) {
|
|
4649
|
+
const specLower = spec.toLowerCase();
|
|
4650
|
+
if (specLower === queryLower || specLower.includes(queryLower)) {
|
|
4651
|
+
const key = `import:${filePath}:${spec}:${imp.source}`;
|
|
4652
|
+
if (processedSymbols.has(key)) continue;
|
|
4653
|
+
processedSymbols.add(key);
|
|
4654
|
+
matches.push({
|
|
4655
|
+
file: filePath,
|
|
4656
|
+
line: 1,
|
|
4657
|
+
// Imports geralmente estão no topo
|
|
4658
|
+
column: 0,
|
|
4659
|
+
code: `import { ${spec} } from '${imp.source}'`,
|
|
4660
|
+
matchType: "import",
|
|
4661
|
+
symbolType: inferSymbolTypeFromName(spec),
|
|
4662
|
+
category: fileData.category
|
|
4663
|
+
});
|
|
4664
|
+
}
|
|
4665
|
+
}
|
|
4666
|
+
}
|
|
4667
|
+
}
|
|
4668
|
+
matches.sort((a, b) => {
|
|
4669
|
+
const order = { definition: 0, import: 1, usage: 2 };
|
|
4670
|
+
const orderDiff = order[a.matchType] - order[b.matchType];
|
|
4671
|
+
if (orderDiff !== 0) return orderDiff;
|
|
4672
|
+
return a.file.localeCompare(b.file) || a.line - b.line;
|
|
4673
|
+
});
|
|
4674
|
+
return matches;
|
|
4675
|
+
}
|
|
4676
|
+
function matchesType(kind, filter) {
|
|
4677
|
+
if (filter === "all") return true;
|
|
4678
|
+
switch (filter) {
|
|
4679
|
+
case "function":
|
|
4680
|
+
return kind === "function" || kind === "trigger";
|
|
4681
|
+
case "type":
|
|
4682
|
+
return kind === "type" || kind === "interface" || kind === "enum";
|
|
4683
|
+
case "const":
|
|
4684
|
+
return kind === "const";
|
|
4685
|
+
case "component":
|
|
4686
|
+
return kind === "component";
|
|
4687
|
+
case "hook":
|
|
4688
|
+
return kind === "hook";
|
|
4689
|
+
case "trigger":
|
|
4690
|
+
return kind === "trigger";
|
|
4691
|
+
default:
|
|
4692
|
+
return true;
|
|
4693
|
+
}
|
|
4694
|
+
}
|
|
4695
|
+
function mapKindToSymbolType(kind) {
|
|
4696
|
+
switch (kind) {
|
|
4697
|
+
case "function":
|
|
4698
|
+
return "function";
|
|
4699
|
+
case "hook":
|
|
4700
|
+
return "hook";
|
|
4701
|
+
case "component":
|
|
4702
|
+
return "component";
|
|
4703
|
+
case "type":
|
|
4704
|
+
case "interface":
|
|
4705
|
+
case "enum":
|
|
4706
|
+
return "type";
|
|
4707
|
+
case "const":
|
|
4708
|
+
return "const";
|
|
4709
|
+
case "trigger":
|
|
4710
|
+
return "trigger";
|
|
4711
|
+
default:
|
|
4712
|
+
return "function";
|
|
4713
|
+
}
|
|
4714
|
+
}
|
|
4715
|
+
function inferSymbolTypeFromName(name) {
|
|
4716
|
+
if (name.startsWith("use") && name.length > 3 && name[3] === name[3].toUpperCase()) {
|
|
4717
|
+
return "hook";
|
|
4718
|
+
}
|
|
4719
|
+
if (name[0] === name[0].toUpperCase()) {
|
|
4720
|
+
return "type";
|
|
4721
|
+
}
|
|
4722
|
+
return "function";
|
|
4723
|
+
}
|
|
4724
|
+
|
|
4015
4725
|
// src/index.ts
|
|
4016
4726
|
import { createRequire } from "module";
|
|
4017
4727
|
var require2 = createRequire(import.meta.url);
|
|
@@ -4023,7 +4733,6 @@ export {
|
|
|
4023
4733
|
categoryIcons,
|
|
4024
4734
|
isEntryPoint,
|
|
4025
4735
|
isCodeFile,
|
|
4026
|
-
formatFindText,
|
|
4027
4736
|
isFirebaseProject,
|
|
4028
4737
|
hasFirebaseFunctions,
|
|
4029
4738
|
isExportedCloudFunction,
|
|
@@ -4031,10 +4740,7 @@ export {
|
|
|
4031
4740
|
clearFirebaseCache,
|
|
4032
4741
|
getCacheDir,
|
|
4033
4742
|
isCacheValid,
|
|
4034
|
-
updateCacheMeta,
|
|
4035
4743
|
invalidateCache,
|
|
4036
|
-
cacheSymbolsIndex,
|
|
4037
|
-
getCachedSymbolsIndex,
|
|
4038
4744
|
configExists,
|
|
4039
4745
|
readConfig,
|
|
4040
4746
|
writeConfig,
|
|
@@ -4046,7 +4752,6 @@ export {
|
|
|
4046
4752
|
KEYWORD_PATTERNS,
|
|
4047
4753
|
AREA_NAMES,
|
|
4048
4754
|
AREA_DESCRIPTIONS,
|
|
4049
|
-
isFileIgnored,
|
|
4050
4755
|
detectFileAreas,
|
|
4051
4756
|
getAreaName,
|
|
4052
4757
|
getAreaDescription,
|
|
@@ -4065,11 +4770,12 @@ export {
|
|
|
4065
4770
|
formatInvalidCommand,
|
|
4066
4771
|
impact,
|
|
4067
4772
|
suggest,
|
|
4068
|
-
indexProject,
|
|
4069
4773
|
context,
|
|
4070
4774
|
areaContext,
|
|
4071
4775
|
areas,
|
|
4072
4776
|
area,
|
|
4073
4777
|
areasInit,
|
|
4778
|
+
find,
|
|
4779
|
+
functions,
|
|
4074
4780
|
VERSION
|
|
4075
4781
|
};
|