@bitsness/grapuco-cli 0.1.9 → 0.1.11

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.
Files changed (2) hide show
  1. package/dist/index.js +186 -4
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2043,7 +2043,7 @@ var init_signature_extractors = __esm({
2043
2043
  });
2044
2044
 
2045
2045
  // src/parser/ingestion/utils/call-extractors.ts
2046
- var CALL_ARGUMENT_LIST_TYPES, countCallArguments, MEMBER_ACCESS_NODE_TYPES, CONSTRUCTOR_CALL_NODE_TYPES, SCOPED_CALL_NODE_TYPES, inferCallForm, SIMPLE_RECEIVER_TYPES, extractReceiverName;
2046
+ var CALL_ARGUMENT_LIST_TYPES, countCallArguments, extractCallFirstStringArgument, MEMBER_ACCESS_NODE_TYPES, CONSTRUCTOR_CALL_NODE_TYPES, SCOPED_CALL_NODE_TYPES, inferCallForm, SIMPLE_RECEIVER_TYPES, extractReceiverName;
2047
2047
  var init_call_extractors = __esm({
2048
2048
  "src/parser/ingestion/utils/call-extractors.ts"() {
2049
2049
  "use strict";
@@ -2074,6 +2074,43 @@ var init_call_extractors = __esm({
2074
2074
  }
2075
2075
  return count;
2076
2076
  };
2077
+ extractCallFirstStringArgument = (callNode) => {
2078
+ if (!callNode) return void 0;
2079
+ let argsNode = callNode.childForFieldName("arguments") ?? callNode.children.find((child) => CALL_ARGUMENT_LIST_TYPES.has(child.type));
2080
+ if (!argsNode) {
2081
+ for (const child of callNode.children) {
2082
+ if (!child.isNamed) continue;
2083
+ const nested = child.children.find((gc) => CALL_ARGUMENT_LIST_TYPES.has(gc.type));
2084
+ if (nested) {
2085
+ argsNode = nested;
2086
+ break;
2087
+ }
2088
+ }
2089
+ }
2090
+ if (!argsNode) return void 0;
2091
+ let firstArg;
2092
+ for (const child of argsNode.children) {
2093
+ if (!child.isNamed || child.type === "comment") continue;
2094
+ firstArg = child;
2095
+ break;
2096
+ }
2097
+ if (!firstArg) return void 0;
2098
+ const stringTypes = /* @__PURE__ */ new Set([
2099
+ "string",
2100
+ "string_literal",
2101
+ "interpreted_string_literal",
2102
+ "raw_string_literal",
2103
+ "encapsed_string"
2104
+ ]);
2105
+ if (stringTypes.has(firstArg.type)) {
2106
+ let text = firstArg.text;
2107
+ if (text.startsWith('"') && text.endsWith('"') || text.startsWith("'") && text.endsWith("'") || text.startsWith("`") && text.endsWith("`")) {
2108
+ return text.substring(1, text.length - 1);
2109
+ }
2110
+ return text;
2111
+ }
2112
+ return void 0;
2113
+ };
2077
2114
  MEMBER_ACCESS_NODE_TYPES = /* @__PURE__ */ new Set([
2078
2115
  "member_expression",
2079
2116
  // TS/JS: obj.method()
@@ -2203,6 +2240,13 @@ var init_call_extractors = __esm({
2203
2240
  if (SIMPLE_RECEIVER_TYPES.has(receiver.type)) {
2204
2241
  return receiver.text;
2205
2242
  }
2243
+ if (receiver.type === "member_expression" || receiver.type === "attribute") {
2244
+ const obj = receiver.childForFieldName("object");
2245
+ const prop = receiver.childForFieldName("property") || receiver.childForFieldName("attribute");
2246
+ if (obj && prop && (obj.text === "this" || obj.text === "self")) {
2247
+ return prop.text;
2248
+ }
2249
+ }
2206
2250
  if (receiver.type === "call") {
2207
2251
  const func = receiver.childForFieldName("function");
2208
2252
  if (func?.text === "super") return "super";
@@ -5234,6 +5278,125 @@ var init_nestjs_routes = __esm({
5234
5278
  }
5235
5279
  });
5236
5280
 
5281
+ // src/parser/ingestion/workers/extractors/nestjs-events.ts
5282
+ function detectClassLevelSubscriber(classNode, className, filePath, events) {
5283
+ for (const child of classNode.children) {
5284
+ if (child.type !== "decorator") continue;
5285
+ const callExp = child.children.find((c) => c.type === "call_expression");
5286
+ if (!callExp) continue;
5287
+ const funcName = callExp.childForFieldName("function")?.text;
5288
+ if (!funcName || !CLASS_SUBSCRIBER_DECORATORS.has(funcName)) continue;
5289
+ const argsNode = callExp.childForFieldName("arguments");
5290
+ if (!argsNode) continue;
5291
+ let topicName = null;
5292
+ for (const arg of argsNode.children) {
5293
+ if (arg.type === "string") {
5294
+ topicName = arg.text.substring(1, arg.text.length - 1);
5295
+ break;
5296
+ }
5297
+ }
5298
+ if (!topicName) continue;
5299
+ const classBody = classNode.childForFieldName("body");
5300
+ if (!classBody) continue;
5301
+ let handlerMethodName = null;
5302
+ let handlerLine = classNode.startPosition.row;
5303
+ for (const member of classBody.children) {
5304
+ if (member.type === "method_definition") {
5305
+ const mName = member.childForFieldName("name")?.text;
5306
+ if (mName && HANDLER_METHOD_NAMES.has(mName)) {
5307
+ handlerMethodName = mName;
5308
+ handlerLine = member.startPosition.row;
5309
+ break;
5310
+ }
5311
+ }
5312
+ }
5313
+ events.push({
5314
+ filePath,
5315
+ topicName,
5316
+ className,
5317
+ methodName: handlerMethodName || className,
5318
+ framework: `bullmq-${funcName.toLowerCase()}`,
5319
+ lineNumber: handlerLine
5320
+ });
5321
+ }
5322
+ }
5323
+ var CLASS_SUBSCRIBER_DECORATORS, HANDLER_METHOD_NAMES, extractNestJSEvents;
5324
+ var init_nestjs_events = __esm({
5325
+ "src/parser/ingestion/workers/extractors/nestjs-events.ts"() {
5326
+ "use strict";
5327
+ CLASS_SUBSCRIBER_DECORATORS = /* @__PURE__ */ new Set([
5328
+ "Processor",
5329
+ // BullMQ @Processor('queueName')
5330
+ "WorkerHost",
5331
+ // BullMQ alternate pattern
5332
+ "Consumer",
5333
+ // KafkaJS / custom consumers
5334
+ "RabbitSubscribe"
5335
+ // @golevelup/nestjs-rabbitmq
5336
+ ]);
5337
+ HANDLER_METHOD_NAMES = /* @__PURE__ */ new Set([
5338
+ "process",
5339
+ // BullMQ WorkerHost.process()
5340
+ "handleMessage",
5341
+ // Generic consumer pattern
5342
+ "handle",
5343
+ // Generic handler
5344
+ "execute"
5345
+ // CQRS-style
5346
+ ]);
5347
+ extractNestJSEvents = (tree, filePath) => {
5348
+ const events = [];
5349
+ const rootNode = tree.rootNode;
5350
+ const walk = (node, currentClass = null) => {
5351
+ if (node.type === "class_declaration") {
5352
+ const nameNode = node.childForFieldName("name");
5353
+ currentClass = nameNode ? nameNode.text : null;
5354
+ detectClassLevelSubscriber(node, currentClass, filePath, events);
5355
+ }
5356
+ if (node.type === "method_definition") {
5357
+ const methodNameNode = node.childForFieldName("name");
5358
+ const methodName = methodNameNode ? methodNameNode.text : null;
5359
+ for (const child of node.children) {
5360
+ if (child.type === "decorator") {
5361
+ const callExp = child.children.find((c) => c.type === "call_expression");
5362
+ if (callExp) {
5363
+ const funcName = callExp.childForFieldName("function")?.text;
5364
+ if (funcName === "EventPattern" || funcName === "MessagePattern") {
5365
+ const argsNode = callExp.childForFieldName("arguments");
5366
+ if (argsNode) {
5367
+ let topicName = null;
5368
+ for (const arg of argsNode.children) {
5369
+ if (arg.type === "string") {
5370
+ topicName = arg.text.substring(1, arg.text.length - 1);
5371
+ break;
5372
+ }
5373
+ }
5374
+ if (topicName) {
5375
+ events.push({
5376
+ filePath,
5377
+ topicName,
5378
+ className: currentClass,
5379
+ methodName,
5380
+ framework: funcName === "EventPattern" ? "nestjs-event" : "nestjs-message",
5381
+ lineNumber: node.startPosition.row
5382
+ });
5383
+ }
5384
+ }
5385
+ }
5386
+ }
5387
+ }
5388
+ }
5389
+ }
5390
+ for (const child of node.children) {
5391
+ walk(child, currentClass);
5392
+ }
5393
+ };
5394
+ walk(rootNode);
5395
+ return events;
5396
+ };
5397
+ }
5398
+ });
5399
+
5237
5400
  // src/parser/ingestion/workers/extractors/php-eloquent.ts
5238
5401
  function findDescendant2(node, type) {
5239
5402
  if (node.type === type) return node;
@@ -5349,6 +5512,7 @@ var init_parse_worker = __esm({
5349
5512
  init_call_routing();
5350
5513
  init_laravel_routes();
5351
5514
  init_nestjs_routes();
5515
+ init_nestjs_events();
5352
5516
  init_php_eloquent();
5353
5517
  init_parser_loader();
5354
5518
  _require = (0, import_node_module.createRequire)(__filename);
@@ -5406,6 +5570,7 @@ var init_parse_worker = __esm({
5406
5570
  calls: [],
5407
5571
  heritage: [],
5408
5572
  routes: [],
5573
+ eventSubscribers: [],
5409
5574
  constructorBindings: [],
5410
5575
  fileCount: 0
5411
5576
  };
@@ -5613,6 +5778,18 @@ var init_parse_worker = __esm({
5613
5778
  const callForm = inferCallForm(callNode, callNameNode);
5614
5779
  const receiverName = callForm === "member" ? extractReceiverName(callNameNode) : void 0;
5615
5780
  const receiverTypeName = receiverName ? typeEnv.lookup(receiverName, callNode) : void 0;
5781
+ let channelArgument;
5782
+ const BROKER_CALL_METHODS = /* @__PURE__ */ new Set(["publish", "emit", "sendToQueue", "produce", "send", "broadcast", "dispatch"]);
5783
+ const AMBIGUOUS_BROKER_METHODS = /* @__PURE__ */ new Set(["add", "enqueue"]);
5784
+ if (BROKER_CALL_METHODS.has(calledName)) {
5785
+ channelArgument = extractCallFirstStringArgument(callNode);
5786
+ } else if (AMBIGUOUS_BROKER_METHODS.has(calledName)) {
5787
+ const recName = receiverName?.toLowerCase() || "";
5788
+ const recType = receiverTypeName?.toLowerCase() || "";
5789
+ if (recName.includes("queue") || recName.includes("job") || recName.includes("mq") || recName.includes("worker") || recType.includes("queue")) {
5790
+ channelArgument = extractCallFirstStringArgument(callNode);
5791
+ }
5792
+ }
5616
5793
  result.calls.push({
5617
5794
  filePath: file.path,
5618
5795
  calledName,
@@ -5620,7 +5797,8 @@ var init_parse_worker = __esm({
5620
5797
  argCount: countCallArguments(callNode),
5621
5798
  ...callForm !== void 0 ? { callForm } : {},
5622
5799
  ...receiverName !== void 0 ? { receiverName } : {},
5623
- ...receiverTypeName !== void 0 ? { receiverTypeName } : {}
5800
+ ...receiverTypeName !== void 0 ? { receiverTypeName } : {},
5801
+ ...channelArgument !== void 0 ? { channelArgument } : {}
5624
5802
  });
5625
5803
  }
5626
5804
  }
@@ -5779,6 +5957,8 @@ var init_parse_worker = __esm({
5779
5957
  if (language === "typescript" /* TypeScript */ && /\.controller\./.test(file.path)) {
5780
5958
  const nestRoutes = extractNestJSRoutes(tree, file.path);
5781
5959
  result.routes.push(...nestRoutes);
5960
+ const nestEvents = extractNestJSEvents(tree, file.path);
5961
+ result.eventSubscribers.push(...nestEvents);
5782
5962
  }
5783
5963
  try {
5784
5964
  if (tree && typeof tree.delete === "function") {
@@ -5796,6 +5976,7 @@ var init_parse_worker = __esm({
5796
5976
  calls: [],
5797
5977
  heritage: [],
5798
5978
  routes: [],
5979
+ eventSubscribers: [],
5799
5980
  constructorBindings: [],
5800
5981
  fileCount: 0
5801
5982
  };
@@ -5808,6 +5989,7 @@ var init_parse_worker = __esm({
5808
5989
  target.calls.push(...src.calls);
5809
5990
  target.heritage.push(...src.heritage);
5810
5991
  target.routes.push(...src.routes);
5992
+ target.eventSubscribers.push(...src.eventSubscribers);
5811
5993
  target.constructorBindings.push(...src.constructorBindings);
5812
5994
  target.fileCount += src.fileCount;
5813
5995
  };
@@ -5825,7 +6007,7 @@ var init_parse_worker = __esm({
5825
6007
  }
5826
6008
  if (msg && msg.type === "flush") {
5827
6009
  import_node_worker_threads.parentPort.postMessage({ type: "result", data: accumulated });
5828
- accumulated = { nodes: [], relationships: [], symbols: [], imports: [], calls: [], heritage: [], routes: [], constructorBindings: [], fileCount: 0 };
6010
+ accumulated = { nodes: [], relationships: [], symbols: [], imports: [], calls: [], heritage: [], routes: [], eventSubscribers: [], constructorBindings: [], fileCount: 0 };
5829
6011
  cumulativeProcessed = 0;
5830
6012
  return;
5831
6013
  }
@@ -6530,7 +6712,7 @@ var require_package = __commonJS({
6530
6712
  "package.json"(exports2, module2) {
6531
6713
  module2.exports = {
6532
6714
  name: "@bitsness/grapuco-cli",
6533
- version: "0.1.9",
6715
+ version: "0.1.11",
6534
6716
  description: "Grapuco CLI \u2014 Parse your code locally, sync architecture to cloud. Zero source code upload.",
6535
6717
  type: "commonjs",
6536
6718
  bin: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bitsness/grapuco-cli",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "description": "Grapuco CLI — Parse your code locally, sync architecture to cloud. Zero source code upload.",
5
5
  "type": "commonjs",
6
6
  "bin": {