sf-intelligence 0.1.26 → 0.2.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/demo-source/main/default/authproviders/Verdant_SSO_Provider.authprovider-meta.xml +11 -0
- package/demo-source/main/default/bots/Verdant_Support_Agent/Verdant_Support_Agent.bot-meta.xml +23 -0
- package/demo-source/main/default/bots/Verdant_Support_Agent/v1.botVersion-meta.xml +20 -0
- package/demo-source/main/default/certs/Verdant_Community.crt +3 -0
- package/demo-source/main/default/certs/Verdant_Community.crt-meta.xml +9 -0
- package/demo-source/main/default/connectedApps/Verdant_Marketing_Suite.connectedApp-meta.xml +12 -0
- package/demo-source/main/default/experiences/VerdantPortal1/views/home.json +4 -0
- package/demo-source/main/default/experiences/VerdantPortal1.site-meta.xml +6 -0
- package/demo-source/main/default/genAiPlannerBundles/Verdant_Support_Agent_v1/Verdant_Support_Agent_v1.genAiPlannerBundle-meta.xml +11 -0
- package/demo-source/main/default/globalValueSets/Solar_Equipment_Types.globalValueSet-meta.xml +23 -0
- package/demo-source/main/default/mutingpermissionsets/Sales_Muting.mutingpermissionset-meta.xml +21 -0
- package/demo-source/main/default/namedCredentials/Verdant_Permitting_API.namedCredential-meta.xml +11 -0
- package/demo-source/main/default/networkAccesses/Office_VPN.networkAccess-meta.xml +5 -0
- package/demo-source/main/default/networks/VerdantPortal.network-meta.xml +16 -0
- package/demo-source/main/default/platformEventChannelMembers/Verdant_Event_Member__chn.platformEventChannelMember-meta.xml +5 -0
- package/demo-source/main/default/platformEventChannels/Verdant_Event_Channel__chn.platformEventChannel-meta.xml +5 -0
- package/demo-source/main/default/samlssoconfigs/Verdant_Energy_SSO.samlssoconfig-meta.xml +8 -0
- package/demo-source/main/default/sites/VerdantPortal.site-meta.xml +9 -0
- package/demo-source/main/default/skills/Solar_Installation.skill-meta.xml +6 -0
- package/demo-source/main/default/standardValueSets/Status__c.standardValueSet-meta.xml +28 -0
- package/demo-source/main/default/timeSheetTemplates/Field_Crew_Weekly.timeSheetTemplate-meta.xml +10 -0
- package/demo-source/main/default/transactionSecurityPolicies/Block_Suspicious_Login.transactionSecurityPolicy-meta.xml +19 -0
- package/demo-source/main/default/wave/Ops_Overview.wdash-meta.xml +8 -0
- package/demo-source/main/default/wave/Project_Pipeline.xmd-meta.xml +21 -0
- package/dist/apex-ast-worker.js +402 -0
- package/dist/index.js +62162 -112947
- package/package.json +21 -19
- package/server.json +2 -2
- package/dist/data/embedding-index.json +0 -67943
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
// workers/apex-ast-worker.mjs
|
|
2
|
+
import { parentPort, workerData } from "node:worker_threads";
|
|
3
|
+
|
|
4
|
+
// ../parsers/dist/src/apex-ast-edges.js
|
|
5
|
+
import { ApexErrorListener, ApexParserFactory } from "@apexdevtools/apex-parser";
|
|
6
|
+
var SYSTEM_TYPES = /* @__PURE__ */ new Set([
|
|
7
|
+
"String",
|
|
8
|
+
"Integer",
|
|
9
|
+
"Decimal",
|
|
10
|
+
"Double",
|
|
11
|
+
"Long",
|
|
12
|
+
"Boolean",
|
|
13
|
+
"Id",
|
|
14
|
+
"Date",
|
|
15
|
+
"Datetime",
|
|
16
|
+
"Time",
|
|
17
|
+
"Blob",
|
|
18
|
+
"Object",
|
|
19
|
+
"List",
|
|
20
|
+
"Map",
|
|
21
|
+
"Set",
|
|
22
|
+
"Math",
|
|
23
|
+
"JSON",
|
|
24
|
+
"Test",
|
|
25
|
+
"Schema",
|
|
26
|
+
"UserInfo",
|
|
27
|
+
"Limits",
|
|
28
|
+
"EncodingUtil",
|
|
29
|
+
"Pattern",
|
|
30
|
+
"Matcher",
|
|
31
|
+
"Url",
|
|
32
|
+
"PageReference",
|
|
33
|
+
"Exception",
|
|
34
|
+
"AggregateResult",
|
|
35
|
+
"SObject",
|
|
36
|
+
"HttpRequest",
|
|
37
|
+
"HttpResponse",
|
|
38
|
+
"QueueableContext",
|
|
39
|
+
"SchedulableContext",
|
|
40
|
+
"Savepoint",
|
|
41
|
+
"System",
|
|
42
|
+
"Database",
|
|
43
|
+
"Type",
|
|
44
|
+
"Http",
|
|
45
|
+
"Trigger"
|
|
46
|
+
]);
|
|
47
|
+
var SYSTEM_CALL_ALLOW = {
|
|
48
|
+
Database: "*",
|
|
49
|
+
Type: /* @__PURE__ */ new Set(["forName"]),
|
|
50
|
+
System: /* @__PURE__ */ new Set(["enqueueJob", "schedule", "scheduleBatch"]),
|
|
51
|
+
Http: /* @__PURE__ */ new Set(["send"])
|
|
52
|
+
};
|
|
53
|
+
var kids = (n) => Array.from({ length: n.getChildCount?.() ?? 0 }, (_, i) => n.getChild(i));
|
|
54
|
+
var ctxName = (n) => String(n.constructor.name).replace(/Context$/, "");
|
|
55
|
+
var findAll = (n, want, out = []) => {
|
|
56
|
+
if (ctxName(n) === want)
|
|
57
|
+
out.push(n);
|
|
58
|
+
for (const c of kids(n))
|
|
59
|
+
findAll(c, want, out);
|
|
60
|
+
return out;
|
|
61
|
+
};
|
|
62
|
+
var ancestorWhere = (node, pred, stopAt = /* @__PURE__ */ new Set()) => {
|
|
63
|
+
let cur = node.parentCtx;
|
|
64
|
+
while (cur !== void 0 && cur !== null) {
|
|
65
|
+
if (pred(cur))
|
|
66
|
+
return cur;
|
|
67
|
+
if (stopAt.has(ctxName(cur)))
|
|
68
|
+
return null;
|
|
69
|
+
cur = cur.parentCtx;
|
|
70
|
+
}
|
|
71
|
+
return null;
|
|
72
|
+
};
|
|
73
|
+
var Collecting = class extends ApexErrorListener {
|
|
74
|
+
errors = [];
|
|
75
|
+
apexSyntaxError(line, column, message) {
|
|
76
|
+
if (this.errors.length < 3)
|
|
77
|
+
this.errors.push(`${line}:${column} ${message}`);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
var extractApexAstEdges = (source, className, options = {}) => {
|
|
81
|
+
const known = options.knownClasses ?? /* @__PURE__ */ new Set();
|
|
82
|
+
const listener = new Collecting();
|
|
83
|
+
let tree;
|
|
84
|
+
try {
|
|
85
|
+
const parser = ApexParserFactory.createParser(source);
|
|
86
|
+
parser.removeErrorListeners();
|
|
87
|
+
parser.addErrorListener(listener);
|
|
88
|
+
const kind = options.kind ?? (source.trimStart().toLowerCase().startsWith("trigger") ? "trigger" : "class");
|
|
89
|
+
tree = kind === "trigger" ? parser.triggerUnit() : parser.compilationUnit();
|
|
90
|
+
} catch (cause) {
|
|
91
|
+
return {
|
|
92
|
+
calls: [],
|
|
93
|
+
reads: [],
|
|
94
|
+
writes: [],
|
|
95
|
+
parseError: `parser runtime failure: ${cause instanceof Error ? cause.message : String(cause)}`
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
if (listener.errors.length > 0) {
|
|
99
|
+
return { calls: [], reads: [], writes: [], parseError: listener.errors[0] };
|
|
100
|
+
}
|
|
101
|
+
const fieldTypes = /* @__PURE__ */ new Map();
|
|
102
|
+
const methodLocals = /* @__PURE__ */ new Map();
|
|
103
|
+
const orphanLocals = /* @__PURE__ */ new Map();
|
|
104
|
+
const ownMethods = /* @__PURE__ */ new Set();
|
|
105
|
+
const methodReturnTypes = /* @__PURE__ */ new Map();
|
|
106
|
+
const innerClasses = /* @__PURE__ */ new Map();
|
|
107
|
+
let extendsType = null;
|
|
108
|
+
findAll(tree, "ClassDeclaration").forEach((cd, idx) => {
|
|
109
|
+
const cname = findAll(cd, "Id")[0]?.getText();
|
|
110
|
+
if (idx > 0 && cname !== void 0)
|
|
111
|
+
innerClasses.set(cname, `${className}.${cname}`);
|
|
112
|
+
const kidList = kids(cd);
|
|
113
|
+
const ext = kidList.findIndex((k) => k.getText?.() === "extends");
|
|
114
|
+
if (idx === 0 && ext >= 0)
|
|
115
|
+
extendsType = kidList[ext + 1]?.getText() ?? null;
|
|
116
|
+
});
|
|
117
|
+
const scopeFor = (node) => {
|
|
118
|
+
const md = ancestorWhere(node, (c) => ctxName(c) === "MethodDeclaration");
|
|
119
|
+
if (md === null)
|
|
120
|
+
return orphanLocals;
|
|
121
|
+
let scope = methodLocals.get(md);
|
|
122
|
+
if (scope === void 0) {
|
|
123
|
+
scope = /* @__PURE__ */ new Map();
|
|
124
|
+
methodLocals.set(md, scope);
|
|
125
|
+
}
|
|
126
|
+
return scope;
|
|
127
|
+
};
|
|
128
|
+
const declareVar = (typeText, varName, scope) => {
|
|
129
|
+
if (typeText === void 0 || varName === void 0 || typeText.length === 0)
|
|
130
|
+
return;
|
|
131
|
+
scope.set(varName.toLowerCase(), typeText.trim());
|
|
132
|
+
};
|
|
133
|
+
for (const md of findAll(tree, "MethodDeclaration")) {
|
|
134
|
+
const id = kids(md).find((k) => ctxName(k) === "Id")?.getText();
|
|
135
|
+
if (id === void 0)
|
|
136
|
+
continue;
|
|
137
|
+
ownMethods.add(id.toLowerCase());
|
|
138
|
+
const ret = kids(md).find((k) => ctxName(k) === "TypeRef")?.getText();
|
|
139
|
+
if (ret !== void 0 && ret.length > 0) {
|
|
140
|
+
methodReturnTypes.set(id.toLowerCase(), ret.trim());
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
for (const fd of findAll(tree, "FieldDeclaration")) {
|
|
144
|
+
const t = kids(fd)[0]?.getText();
|
|
145
|
+
for (const vd of findAll(fd, "VariableDeclarator")) {
|
|
146
|
+
declareVar(t, findAll(vd, "Id")[0]?.getText(), fieldTypes);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
for (const pd of findAll(tree, "PropertyDeclaration")) {
|
|
150
|
+
declareVar(kids(pd)[0]?.getText(), findAll(pd, "Id")[0]?.getText(), fieldTypes);
|
|
151
|
+
}
|
|
152
|
+
for (const lv of findAll(tree, "LocalVariableDeclaration")) {
|
|
153
|
+
const t = kids(lv).find((k) => ctxName(k) === "TypeRef")?.getText() ?? kids(lv)[0]?.getText();
|
|
154
|
+
for (const vd of findAll(lv, "VariableDeclarator")) {
|
|
155
|
+
declareVar(t, findAll(vd, "Id")[0]?.getText(), scopeFor(lv));
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
for (const fp of findAll(tree, "FormalParameter")) {
|
|
159
|
+
const typeText = kids(fp).find((k) => ctxName(k) === "TypeRef")?.getText() ?? kids(fp)[kids(fp).length - 2]?.getText();
|
|
160
|
+
const varName = kids(fp).find((k) => ctxName(k) === "Id")?.getText() ?? kids(fp)[kids(fp).length - 1]?.getText();
|
|
161
|
+
declareVar(typeText, varName, scopeFor(fp));
|
|
162
|
+
}
|
|
163
|
+
for (const fc of findAll(tree, "EnhancedForControl")) {
|
|
164
|
+
const ks = kids(fc);
|
|
165
|
+
declareVar(ks[0]?.getText(), ks[1]?.getText(), scopeFor(fc));
|
|
166
|
+
}
|
|
167
|
+
const resolveVarType = (name, at) => {
|
|
168
|
+
const lower = name.toLowerCase();
|
|
169
|
+
const md = ancestorWhere(at, (c) => ctxName(c) === "MethodDeclaration");
|
|
170
|
+
if (md !== null) {
|
|
171
|
+
const scope = methodLocals.get(md);
|
|
172
|
+
if (scope?.has(lower))
|
|
173
|
+
return scope.get(lower) ?? null;
|
|
174
|
+
} else if (orphanLocals.has(lower)) {
|
|
175
|
+
return orphanLocals.get(lower) ?? null;
|
|
176
|
+
}
|
|
177
|
+
return fieldTypes.get(lower) ?? null;
|
|
178
|
+
};
|
|
179
|
+
const calls = /* @__PURE__ */ new Set();
|
|
180
|
+
const reads = /* @__PURE__ */ new Set();
|
|
181
|
+
const writes = /* @__PURE__ */ new Set();
|
|
182
|
+
const callSites = [];
|
|
183
|
+
const recordCall = (callee, node) => {
|
|
184
|
+
calls.add(callee);
|
|
185
|
+
const md = ancestorWhere(node, (c) => ctxName(c) === "MethodDeclaration");
|
|
186
|
+
const cm = md === null ? void 0 : kids(md).find((k) => ctxName(k) === "Id")?.getText();
|
|
187
|
+
callSites.push({ callee, callerMethod: cm ?? "" });
|
|
188
|
+
};
|
|
189
|
+
const resolveType = (t) => innerClasses.get(t) ?? t;
|
|
190
|
+
const isSObjectish = (t) => t !== null && t !== void 0 && !SYSTEM_TYPES.has(t) && !innerClasses.has(t) && t !== className && !/[<>]/.test(t) && /^[A-Z]/.test(t) && !known.has(t);
|
|
191
|
+
const isUserClass = (t) => t === className || innerClasses.has(t) || t === extendsType || known.has(t);
|
|
192
|
+
const allowSystemCall = (cls, method) => {
|
|
193
|
+
const rule = SYSTEM_CALL_ALLOW[cls];
|
|
194
|
+
if (rule === void 0)
|
|
195
|
+
return false;
|
|
196
|
+
return rule === "*" || rule.has(method);
|
|
197
|
+
};
|
|
198
|
+
const FROM_OBJ = (scopeCtx) => {
|
|
199
|
+
const direct = scopeCtx.fromNameList?.();
|
|
200
|
+
const list = direct ?? findAll(scopeCtx, "FromNameList").find((fl) => (ancestorWhere(fl, (c) => ctxName(c) === "SubQuery", /* @__PURE__ */ new Set(["Query"])) ?? scopeCtx) === scopeCtx);
|
|
201
|
+
if (list === void 0 || list === null)
|
|
202
|
+
return void 0;
|
|
203
|
+
const txt = findAll(list, "FieldName")[0]?.getText();
|
|
204
|
+
return txt === void 0 || txt.length === 0 ? void 0 : txt;
|
|
205
|
+
};
|
|
206
|
+
const soqlFrom = (queryCtx) => {
|
|
207
|
+
const SCOPE = /* @__PURE__ */ new Set(["Query", "SubQuery"]);
|
|
208
|
+
for (const fn of findAll(queryCtx, "FieldName")) {
|
|
209
|
+
if (ancestorWhere(fn, (c) => ctxName(c) === "FromNameList", SCOPE) !== null)
|
|
210
|
+
continue;
|
|
211
|
+
if (ancestorWhere(fn, (c) => ctxName(c) === "TypeOf", SCOPE) !== null)
|
|
212
|
+
continue;
|
|
213
|
+
const scope = ancestorWhere(fn, (c) => SCOPE.has(ctxName(c))) ?? queryCtx;
|
|
214
|
+
if (ctxName(scope) === "SubQuery") {
|
|
215
|
+
const semiJoin = ancestorWhere(scope, (c) => ctxName(c) === "WhereClause", /* @__PURE__ */ new Set(["Query", "SubQuery"]));
|
|
216
|
+
if (semiJoin === null)
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
const obj = FROM_OBJ(scope);
|
|
220
|
+
if (obj === void 0)
|
|
221
|
+
continue;
|
|
222
|
+
reads.add(`${obj}.${fn.getText()}`);
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
for (const q of findAll(tree, "Query"))
|
|
226
|
+
soqlFrom(q);
|
|
227
|
+
for (const lit of findAll(tree, "LiteralPrimary")) {
|
|
228
|
+
const t = lit.getText();
|
|
229
|
+
if (/^'\s*select\s/i.test(t)) {
|
|
230
|
+
try {
|
|
231
|
+
const qp = ApexParserFactory.createParser(t.slice(1, -1));
|
|
232
|
+
qp.removeErrorListeners();
|
|
233
|
+
const c = new Collecting();
|
|
234
|
+
qp.addErrorListener(c);
|
|
235
|
+
const qt = qp.query();
|
|
236
|
+
if (c.errors.length === 0)
|
|
237
|
+
soqlFrom(qt);
|
|
238
|
+
} catch {
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
const dotSegments = (dot) => {
|
|
243
|
+
const path = [];
|
|
244
|
+
let cur = dot;
|
|
245
|
+
while (cur !== void 0 && ctxName(cur) === "DotExpression") {
|
|
246
|
+
const ks = kids(cur);
|
|
247
|
+
const tail = ks[ks.length - 1];
|
|
248
|
+
if (ctxName(tail) === "DotMethodCall") {
|
|
249
|
+
const id = findAll(tail, "AnyId")[0]?.getText() ?? findAll(tail, "Id")[0]?.getText();
|
|
250
|
+
path.unshift({ name: id ?? "", isCall: true });
|
|
251
|
+
} else {
|
|
252
|
+
path.unshift({ name: tail.getText(), isCall: false });
|
|
253
|
+
}
|
|
254
|
+
cur = ks[0];
|
|
255
|
+
}
|
|
256
|
+
if (cur === void 0)
|
|
257
|
+
return null;
|
|
258
|
+
return { root: cur.getText(), rootNode: cur, path };
|
|
259
|
+
};
|
|
260
|
+
const resolveRootType = (seg) => {
|
|
261
|
+
const rootLower = seg.root.toLowerCase();
|
|
262
|
+
if (/^new\s*/i.test(seg.root) || ctxName(seg.rootNode) === "NewExpression") {
|
|
263
|
+
return seg.root.replace(/^new\s*/i, "").replace(/\(.*\)$/, "").replace(/[<>].*$/, "");
|
|
264
|
+
}
|
|
265
|
+
if (rootLower === "this")
|
|
266
|
+
return className;
|
|
267
|
+
if (rootLower === "super")
|
|
268
|
+
return extendsType;
|
|
269
|
+
if (ctxName(seg.rootNode) === "MethodCallExpression") {
|
|
270
|
+
const mid = findAll(seg.rootNode, "Id")[0]?.getText();
|
|
271
|
+
if (mid === void 0 || !ownMethods.has(mid.toLowerCase()))
|
|
272
|
+
return null;
|
|
273
|
+
return methodReturnTypes.get(mid.toLowerCase()) ?? null;
|
|
274
|
+
}
|
|
275
|
+
return resolveVarType(seg.root, seg.rootNode);
|
|
276
|
+
};
|
|
277
|
+
const advanceCalls = (startType, path, at, record) => {
|
|
278
|
+
let type = startType;
|
|
279
|
+
let i = 0;
|
|
280
|
+
for (; i < path.length; i++) {
|
|
281
|
+
const seg = path[i];
|
|
282
|
+
if (seg === void 0 || !seg.isCall)
|
|
283
|
+
break;
|
|
284
|
+
const method = seg.name;
|
|
285
|
+
if (type === null)
|
|
286
|
+
break;
|
|
287
|
+
if (isUserClass(type)) {
|
|
288
|
+
if (record)
|
|
289
|
+
recordCall(`${resolveType(type)}.${method}`, at);
|
|
290
|
+
if (type === className || resolveType(type) === className) {
|
|
291
|
+
type = methodReturnTypes.get(method.toLowerCase()) ?? null;
|
|
292
|
+
} else {
|
|
293
|
+
type = null;
|
|
294
|
+
}
|
|
295
|
+
} else if (allowSystemCall(type, method)) {
|
|
296
|
+
if (record)
|
|
297
|
+
recordCall(`${type}.${method}`, at);
|
|
298
|
+
type = null;
|
|
299
|
+
} else {
|
|
300
|
+
type = null;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
return { type, fieldStart: i };
|
|
304
|
+
};
|
|
305
|
+
const peelOwnFields = (startType, path, from) => {
|
|
306
|
+
let type = startType;
|
|
307
|
+
let i = from;
|
|
308
|
+
while (i < path.length) {
|
|
309
|
+
const seg = path[i];
|
|
310
|
+
if (seg === void 0 || seg.isCall)
|
|
311
|
+
break;
|
|
312
|
+
if (type !== className)
|
|
313
|
+
break;
|
|
314
|
+
const ft = fieldTypes.get(seg.name.toLowerCase());
|
|
315
|
+
if (ft === void 0)
|
|
316
|
+
break;
|
|
317
|
+
type = ft;
|
|
318
|
+
i += 1;
|
|
319
|
+
}
|
|
320
|
+
return { type, fieldStart: i };
|
|
321
|
+
};
|
|
322
|
+
const resolveChain = (rootType, path, at, record) => {
|
|
323
|
+
const afterCalls = advanceCalls(rootType, path, at, record);
|
|
324
|
+
return peelOwnFields(afterCalls.type, path, afterCalls.fieldStart);
|
|
325
|
+
};
|
|
326
|
+
const writeRoots = /* @__PURE__ */ new Set();
|
|
327
|
+
for (const asg of findAll(tree, "AssignExpression")) {
|
|
328
|
+
const lhs = kids(asg)[0];
|
|
329
|
+
if (lhs !== void 0 && ctxName(lhs) === "DotExpression") {
|
|
330
|
+
writeRoots.add(lhs);
|
|
331
|
+
const seg = dotSegments(lhs);
|
|
332
|
+
if (seg !== null) {
|
|
333
|
+
const rootType = resolveRootType(seg);
|
|
334
|
+
const { type, fieldStart } = resolveChain(rootType, seg.path, lhs, false);
|
|
335
|
+
const fieldPath = seg.path.slice(fieldStart).filter((p) => !p.isCall).map((p) => p.name);
|
|
336
|
+
if (isSObjectish(type) && fieldPath.length > 0) {
|
|
337
|
+
writes.add(`${type}.${fieldPath.join(".")}`);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
const allDots = findAll(tree, "DotExpression");
|
|
343
|
+
const nestedDots = /* @__PURE__ */ new Set();
|
|
344
|
+
for (const d of allDots)
|
|
345
|
+
for (const c of kids(d))
|
|
346
|
+
if (ctxName(c) === "DotExpression")
|
|
347
|
+
nestedDots.add(c);
|
|
348
|
+
for (const dot of allDots) {
|
|
349
|
+
if (nestedDots.has(dot))
|
|
350
|
+
continue;
|
|
351
|
+
const seg = dotSegments(dot);
|
|
352
|
+
if (seg === null)
|
|
353
|
+
continue;
|
|
354
|
+
const rootType = resolveRootType(seg);
|
|
355
|
+
const tailIsCall = seg.path.length > 0 && seg.path[seg.path.length - 1]?.isCall === true;
|
|
356
|
+
if (rootType === null && resolveVarType(seg.root, seg.rootNode) === null && /^[A-Z]/.test(seg.root) && seg.path.length === 1 && tailIsCall) {
|
|
357
|
+
const last = seg.path[0]?.name ?? "";
|
|
358
|
+
if (allowSystemCall(seg.root, last))
|
|
359
|
+
recordCall(`${seg.root}.${last}`, dot);
|
|
360
|
+
else if (isUserClass(seg.root))
|
|
361
|
+
recordCall(`${resolveType(seg.root)}.${last}`, dot);
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
364
|
+
const { type, fieldStart } = resolveChain(rootType, seg.path, dot, true);
|
|
365
|
+
const fieldPath = seg.path.slice(fieldStart).filter((p) => !p.isCall).map((p) => p.name);
|
|
366
|
+
if (fieldPath.length > 0 && isSObjectish(type) && !writeRoots.has(dot)) {
|
|
367
|
+
reads.add(`${type}.${fieldPath.join(".")}`);
|
|
368
|
+
}
|
|
369
|
+
if (isSObjectish(rootType) && tailIsCall && seg.path.length > 1 && seg.path.slice(0, -1).every((p) => !p.isCall)) {
|
|
370
|
+
const before = seg.path.slice(0, -1).map((p) => p.name);
|
|
371
|
+
if (before.length > 0)
|
|
372
|
+
reads.add(`${rootType}.${before.join(".")}`);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
for (const mc of findAll(tree, "MethodCallExpression")) {
|
|
376
|
+
const id = findAll(mc, "Id")[0]?.getText();
|
|
377
|
+
if (id !== void 0 && ownMethods.has(id.toLowerCase()))
|
|
378
|
+
recordCall(`${className}.${id}`, mc);
|
|
379
|
+
}
|
|
380
|
+
return {
|
|
381
|
+
calls: [...calls].sort(),
|
|
382
|
+
reads: [...reads].sort(),
|
|
383
|
+
writes: [...writes].sort(),
|
|
384
|
+
innerTypes: [...innerClasses.keys()].sort(),
|
|
385
|
+
callSites: [...callSites].sort((a, b) => a.callee < b.callee ? -1 : a.callee > b.callee ? 1 : a.callerMethod < b.callerMethod ? -1 : a.callerMethod > b.callerMethod ? 1 : 0)
|
|
386
|
+
};
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
// workers/apex-ast-worker.mjs
|
|
390
|
+
if (parentPort === null) {
|
|
391
|
+
throw new Error("apex-ast-worker must run as a worker_thread");
|
|
392
|
+
}
|
|
393
|
+
var knownClasses = new Set(
|
|
394
|
+
Array.isArray(workerData?.knownClasses) ? workerData.knownClasses : []
|
|
395
|
+
);
|
|
396
|
+
parentPort.on("message", (job) => {
|
|
397
|
+
const result = extractApexAstEdges(job.source, job.apiName, {
|
|
398
|
+
knownClasses,
|
|
399
|
+
kind: job.kind
|
|
400
|
+
});
|
|
401
|
+
parentPort.postMessage({ index: job.index, result });
|
|
402
|
+
});
|